From 205af2854f36c489b4cfc441981e7b5e67d3ec3f Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 9 May 2015 15:39:40 -0700 Subject: [PATCH 0001/1028] Xcode project --- .gitignore | 6 + Rex.xcodeproj/project.pbxproj | 415 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + Rex/Info.plist | 28 ++ Rex/Rex.h | 19 + RexTests/Info.plist | 24 + RexTests/RexTests.swift | 36 ++ 7 files changed, 535 insertions(+) create mode 100644 .gitignore create mode 100644 Rex.xcodeproj/project.pbxproj create mode 100644 Rex.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 Rex/Info.plist create mode 100644 Rex/Rex.h create mode 100644 RexTests/Info.plist create mode 100644 RexTests/RexTests.swift diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..8060b61ebc --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.DS_Store +xcuserdata +*.xcuserdatad +*.xccheckout +*.mode* +*.pbxuser diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..e0691dc8c7 --- /dev/null +++ b/Rex.xcodeproj/project.pbxproj @@ -0,0 +1,415 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + D8003E941AFEC3D400D7D3C5 /* Rex.h in Headers */ = {isa = PBXBuildFile; fileRef = D8003E931AFEC3D400D7D3C5 /* Rex.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8003E9A1AFEC3D400D7D3C5 /* Rex.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003E8E1AFEC3D400D7D3C5 /* Rex.framework */; }; + D8003EA11AFEC3D400D7D3C5 /* RexTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EA01AFEC3D400D7D3C5 /* RexTests.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + D8003E9B1AFEC3D400D7D3C5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D8003E851AFEC3D400D7D3C5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = D8003E8D1AFEC3D400D7D3C5; + remoteInfo = Rex; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + D8003E8E1AFEC3D400D7D3C5 /* Rex.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Rex.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D8003E921AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D8003E931AFEC3D400D7D3C5 /* Rex.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Rex.h; sourceTree = ""; }; + D8003E991AFEC3D400D7D3C5 /* RexTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RexTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + D8003E9F1AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D8003EA01AFEC3D400D7D3C5 /* RexTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RexTests.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + D8003E8A1AFEC3D400D7D3C5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D8003E961AFEC3D400D7D3C5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D8003E9A1AFEC3D400D7D3C5 /* Rex.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + D8003E841AFEC3D400D7D3C5 = { + isa = PBXGroup; + children = ( + D8003E901AFEC3D400D7D3C5 /* Rex */, + D8003E9D1AFEC3D400D7D3C5 /* RexTests */, + D8003E8F1AFEC3D400D7D3C5 /* Products */, + ); + sourceTree = ""; + }; + D8003E8F1AFEC3D400D7D3C5 /* Products */ = { + isa = PBXGroup; + children = ( + D8003E8E1AFEC3D400D7D3C5 /* Rex.framework */, + D8003E991AFEC3D400D7D3C5 /* RexTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + D8003E901AFEC3D400D7D3C5 /* Rex */ = { + isa = PBXGroup; + children = ( + D8003E931AFEC3D400D7D3C5 /* Rex.h */, + D8003E911AFEC3D400D7D3C5 /* Supporting Files */, + ); + path = Rex; + sourceTree = ""; + }; + D8003E911AFEC3D400D7D3C5 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + D8003E921AFEC3D400D7D3C5 /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + D8003E9D1AFEC3D400D7D3C5 /* RexTests */ = { + isa = PBXGroup; + children = ( + D8003EA01AFEC3D400D7D3C5 /* RexTests.swift */, + D8003E9E1AFEC3D400D7D3C5 /* Supporting Files */, + ); + path = RexTests; + sourceTree = ""; + }; + D8003E9E1AFEC3D400D7D3C5 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + D8003E9F1AFEC3D400D7D3C5 /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + D8003E8B1AFEC3D400D7D3C5 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + D8003E941AFEC3D400D7D3C5 /* Rex.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + D8003E8D1AFEC3D400D7D3C5 /* Rex */ = { + isa = PBXNativeTarget; + buildConfigurationList = D8003EA41AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "Rex" */; + buildPhases = ( + D8003E891AFEC3D400D7D3C5 /* Sources */, + D8003E8A1AFEC3D400D7D3C5 /* Frameworks */, + D8003E8B1AFEC3D400D7D3C5 /* Headers */, + D8003E8C1AFEC3D400D7D3C5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Rex; + productName = Rex; + productReference = D8003E8E1AFEC3D400D7D3C5 /* Rex.framework */; + productType = "com.apple.product-type.framework"; + }; + D8003E981AFEC3D400D7D3C5 /* RexTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = D8003EA71AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "RexTests" */; + buildPhases = ( + D8003E951AFEC3D400D7D3C5 /* Sources */, + D8003E961AFEC3D400D7D3C5 /* Frameworks */, + D8003E971AFEC3D400D7D3C5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + D8003E9C1AFEC3D400D7D3C5 /* PBXTargetDependency */, + ); + name = RexTests; + productName = RexTests; + productReference = D8003E991AFEC3D400D7D3C5 /* RexTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + D8003E851AFEC3D400D7D3C5 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0630; + ORGANIZATIONNAME = "Neil Pankey"; + TargetAttributes = { + D8003E8D1AFEC3D400D7D3C5 = { + CreatedOnToolsVersion = 6.3; + }; + D8003E981AFEC3D400D7D3C5 = { + CreatedOnToolsVersion = 6.3; + }; + }; + }; + buildConfigurationList = D8003E881AFEC3D400D7D3C5 /* Build configuration list for PBXProject "Rex" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = D8003E841AFEC3D400D7D3C5; + productRefGroup = D8003E8F1AFEC3D400D7D3C5 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + D8003E8D1AFEC3D400D7D3C5 /* Rex */, + D8003E981AFEC3D400D7D3C5 /* RexTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + D8003E8C1AFEC3D400D7D3C5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D8003E971AFEC3D400D7D3C5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + D8003E891AFEC3D400D7D3C5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D8003E951AFEC3D400D7D3C5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D8003EA11AFEC3D400D7D3C5 /* RexTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + D8003E9C1AFEC3D400D7D3C5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D8003E8D1AFEC3D400D7D3C5 /* Rex */; + targetProxy = D8003E9B1AFEC3D400D7D3C5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + D8003EA21AFEC3D400D7D3C5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.10; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + D8003EA31AFEC3D400D7D3C5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.10; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + D8003EA51AFEC3D400D7D3C5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = Rex/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + D8003EA61AFEC3D400D7D3C5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = Rex/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; + D8003EA81AFEC3D400D7D3C5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = RexTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + D8003EA91AFEC3D400D7D3C5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + INFOPLIST_FILE = RexTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + D8003E881AFEC3D400D7D3C5 /* Build configuration list for PBXProject "Rex" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D8003EA21AFEC3D400D7D3C5 /* Debug */, + D8003EA31AFEC3D400D7D3C5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D8003EA41AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "Rex" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D8003EA51AFEC3D400D7D3C5 /* Debug */, + D8003EA61AFEC3D400D7D3C5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + }; + D8003EA71AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "RexTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D8003EA81AFEC3D400D7D3C5 /* Debug */, + D8003EA91AFEC3D400D7D3C5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + }; +/* End XCConfigurationList section */ + }; + rootObject = D8003E851AFEC3D400D7D3C5 /* Project object */; +} diff --git a/Rex.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Rex.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..9e10bd191a --- /dev/null +++ b/Rex.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Rex/Info.plist b/Rex/Info.plist new file mode 100644 index 0000000000..2ebeb6a976 --- /dev/null +++ b/Rex/Info.plist @@ -0,0 +1,28 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + me.neilpa.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 0.0.1 + CFBundleSignature + ???? + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSHumanReadableCopyright + Copyright © 2015 Neil Pankey. All rights reserved. + NSPrincipalClass + + + diff --git a/Rex/Rex.h b/Rex/Rex.h new file mode 100644 index 0000000000..5aeccb98c1 --- /dev/null +++ b/Rex/Rex.h @@ -0,0 +1,19 @@ +// +// Rex.h +// Rex +// +// Created by Neil Pankey on 5/9/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +#import + +//! Project version number for Rex. +FOUNDATION_EXPORT double RexVersionNumber; + +//! Project version string for Rex. +FOUNDATION_EXPORT const unsigned char RexVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/RexTests/Info.plist b/RexTests/Info.plist new file mode 100644 index 0000000000..14a51c6942 --- /dev/null +++ b/RexTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + me.neilpa.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/RexTests/RexTests.swift b/RexTests/RexTests.swift new file mode 100644 index 0000000000..9ce57bb77a --- /dev/null +++ b/RexTests/RexTests.swift @@ -0,0 +1,36 @@ +// +// RexTests.swift +// RexTests +// +// Created by Neil Pankey on 5/9/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import Cocoa +import XCTest + +class RexTests: XCTestCase { + + override func setUp() { + super.setUp() + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } + + func testExample() { + // This is an example of a functional test case. + XCTAssert(true, "Pass") + } + + func testPerformanceExample() { + // This is an example of a performance test case. + self.measureBlock() { + // Put the code you want to measure the time of here. + } + } + +} From 981b291ebbe407489c2b26f8e653abf5e0dba68c Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 9 May 2015 15:42:25 -0700 Subject: [PATCH 0002/1028] Organizing --- Rex.xcodeproj/project.pbxproj | 20 ++++++++++---------- Rex.xcworkspace/contents.xcworkspacedata | 7 +++++++ {Rex => Source}/Info.plist | 0 {Rex => Source}/Rex.h | 0 {RexTests => Tests}/Info.plist | 0 {RexTests => Tests}/RexTests.swift | 0 6 files changed, 17 insertions(+), 10 deletions(-) create mode 100644 Rex.xcworkspace/contents.xcworkspacedata rename {Rex => Source}/Info.plist (100%) rename {Rex => Source}/Rex.h (100%) rename {RexTests => Tests}/Info.plist (100%) rename {RexTests => Tests}/RexTests.swift (100%) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index e0691dc8c7..ac6892ca0e 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -53,8 +53,8 @@ D8003E841AFEC3D400D7D3C5 = { isa = PBXGroup; children = ( - D8003E901AFEC3D400D7D3C5 /* Rex */, - D8003E9D1AFEC3D400D7D3C5 /* RexTests */, + D8003E901AFEC3D400D7D3C5 /* Source */, + D8003E9D1AFEC3D400D7D3C5 /* Tests */, D8003E8F1AFEC3D400D7D3C5 /* Products */, ); sourceTree = ""; @@ -68,13 +68,13 @@ name = Products; sourceTree = ""; }; - D8003E901AFEC3D400D7D3C5 /* Rex */ = { + D8003E901AFEC3D400D7D3C5 /* Source */ = { isa = PBXGroup; children = ( D8003E931AFEC3D400D7D3C5 /* Rex.h */, D8003E911AFEC3D400D7D3C5 /* Supporting Files */, ); - path = Rex; + path = Source; sourceTree = ""; }; D8003E911AFEC3D400D7D3C5 /* Supporting Files */ = { @@ -85,13 +85,13 @@ name = "Supporting Files"; sourceTree = ""; }; - D8003E9D1AFEC3D400D7D3C5 /* RexTests */ = { + D8003E9D1AFEC3D400D7D3C5 /* Tests */ = { isa = PBXGroup; children = ( D8003EA01AFEC3D400D7D3C5 /* RexTests.swift */, D8003E9E1AFEC3D400D7D3C5 /* Supporting Files */, ); - path = RexTests; + path = Tests; sourceTree = ""; }; D8003E9E1AFEC3D400D7D3C5 /* Supporting Files */ = { @@ -324,7 +324,7 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_VERSION = A; - INFOPLIST_FILE = Rex/Info.plist; + INFOPLIST_FILE = Source/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -341,7 +341,7 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_VERSION = A; - INFOPLIST_FILE = Rex/Info.plist; + INFOPLIST_FILE = Source/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -361,7 +361,7 @@ "DEBUG=1", "$(inherited)", ); - INFOPLIST_FILE = RexTests/Info.plist; + INFOPLIST_FILE = Tests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -375,7 +375,7 @@ "$(DEVELOPER_FRAMEWORKS_DIR)", "$(inherited)", ); - INFOPLIST_FILE = RexTests/Info.plist; + INFOPLIST_FILE = Tests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_NAME = "$(TARGET_NAME)"; }; diff --git a/Rex.xcworkspace/contents.xcworkspacedata b/Rex.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..c40667845e --- /dev/null +++ b/Rex.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Rex/Info.plist b/Source/Info.plist similarity index 100% rename from Rex/Info.plist rename to Source/Info.plist diff --git a/Rex/Rex.h b/Source/Rex.h similarity index 100% rename from Rex/Rex.h rename to Source/Rex.h diff --git a/RexTests/Info.plist b/Tests/Info.plist similarity index 100% rename from RexTests/Info.plist rename to Tests/Info.plist diff --git a/RexTests/RexTests.swift b/Tests/RexTests.swift similarity index 100% rename from RexTests/RexTests.swift rename to Tests/RexTests.swift From 4bc526e7ddebca13f794fce95721f8de5b9f8bf4 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 9 May 2015 15:50:45 -0700 Subject: [PATCH 0003/1028] Dependencies --- .gitmodules | 3 ++ Cartfile | 1 + Cartfile.resolved | 3 ++ Carthage/Checkouts/ReactiveCocoa | 1 + Rex.xcodeproj/project.pbxproj | 60 ++++++++++++++++++++++++++++++++ 5 files changed, 68 insertions(+) create mode 100644 .gitmodules create mode 100644 Cartfile create mode 100644 Cartfile.resolved create mode 160000 Carthage/Checkouts/ReactiveCocoa diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..a87e043fc4 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Carthage/Checkouts/ReactiveCocoa"] + path = Carthage/Checkouts/ReactiveCocoa + url = https://github.com/ReactiveCocoa/ReactiveCocoa.git diff --git a/Cartfile b/Cartfile new file mode 100644 index 0000000000..f0f99fad61 --- /dev/null +++ b/Cartfile @@ -0,0 +1 @@ +github "ReactiveCocoa/ReactiveCocoa" ~> 3.0 diff --git a/Cartfile.resolved b/Cartfile.resolved new file mode 100644 index 0000000000..75f5fd8120 --- /dev/null +++ b/Cartfile.resolved @@ -0,0 +1,3 @@ +github "robrix/Box" "1.2.2" +github "antitypical/Result" "0.4.1" +github "ReactiveCocoa/ReactiveCocoa" "v3.0-beta.4" diff --git a/Carthage/Checkouts/ReactiveCocoa b/Carthage/Checkouts/ReactiveCocoa new file mode 160000 index 0000000000..1c8b18751c --- /dev/null +++ b/Carthage/Checkouts/ReactiveCocoa @@ -0,0 +1 @@ +Subproject commit 1c8b18751c4af1f3c6003077ff57bed80e184c3a diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index ac6892ca0e..6dbf29dcd2 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -10,6 +10,12 @@ D8003E941AFEC3D400D7D3C5 /* Rex.h in Headers */ = {isa = PBXBuildFile; fileRef = D8003E931AFEC3D400D7D3C5 /* Rex.h */; settings = {ATTRIBUTES = (Public, ); }; }; D8003E9A1AFEC3D400D7D3C5 /* Rex.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003E8E1AFEC3D400D7D3C5 /* Rex.framework */; }; D8003EA11AFEC3D400D7D3C5 /* RexTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EA01AFEC3D400D7D3C5 /* RexTests.swift */; }; + D8003EAF1AFEC68A00D7D3C5 /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAC1AFEC68A00D7D3C5 /* Box.framework */; }; + D8003EB01AFEC68A00D7D3C5 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; }; + D8003EB11AFEC68A00D7D3C5 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAE1AFEC68A00D7D3C5 /* Result.framework */; }; + D8003EB31AFEC6B000D7D3C5 /* Box.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003EAC1AFEC68A00D7D3C5 /* Box.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 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, ); }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -22,6 +28,21 @@ }; /* End PBXContainerItemProxy section */ +/* Begin PBXCopyFilesBuildPhase section */ + D8003EB21AFEC6A800D7D3C5 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 16; + files = ( + D8003EB31AFEC6B000D7D3C5 /* Box.framework in CopyFiles */, + D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in CopyFiles */, + D8003EB51AFEC6B000D7D3C5 /* Result.framework in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ D8003E8E1AFEC3D400D7D3C5 /* Rex.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Rex.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D8003E921AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -29,6 +50,9 @@ D8003E991AFEC3D400D7D3C5 /* RexTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RexTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; D8003E9F1AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D8003EA01AFEC3D400D7D3C5 /* RexTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RexTests.swift; sourceTree = ""; }; + D8003EAC1AFEC68A00D7D3C5 /* Box.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Box.framework; sourceTree = ""; }; + D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ReactiveCocoa.framework; sourceTree = ""; }; + D8003EAE1AFEC68A00D7D3C5 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -36,6 +60,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + D8003EB11AFEC68A00D7D3C5 /* Result.framework in Frameworks */, + D8003EAF1AFEC68A00D7D3C5 /* Box.framework in Frameworks */, + D8003EB01AFEC68A00D7D3C5 /* ReactiveCocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -56,6 +83,7 @@ D8003E901AFEC3D400D7D3C5 /* Source */, D8003E9D1AFEC3D400D7D3C5 /* Tests */, D8003E8F1AFEC3D400D7D3C5 /* Products */, + D8003EAA1AFEC57200D7D3C5 /* External */, ); sourceTree = ""; }; @@ -102,6 +130,25 @@ name = "Supporting Files"; sourceTree = ""; }; + D8003EAA1AFEC57200D7D3C5 /* External */ = { + isa = PBXGroup; + children = ( + D8003EAB1AFEC67700D7D3C5 /* Mac */, + ); + name = External; + path = Carthage/Build; + sourceTree = ""; + }; + D8003EAB1AFEC67700D7D3C5 /* Mac */ = { + isa = PBXGroup; + children = ( + D8003EAC1AFEC68A00D7D3C5 /* Box.framework */, + D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */, + D8003EAE1AFEC68A00D7D3C5 /* Result.framework */, + ); + path = Mac; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -141,6 +188,7 @@ D8003E951AFEC3D400D7D3C5 /* Sources */, D8003E961AFEC3D400D7D3C5 /* Frameworks */, D8003E971AFEC3D400D7D3C5 /* Resources */, + D8003EB21AFEC6A800D7D3C5 /* CopyFiles */, ); buildRules = ( ); @@ -318,11 +366,16 @@ D8003EA51AFEC3D400D7D3C5 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + APPLICATION_EXTENSION_API_ONLY = NO; COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/Mac", + ); FRAMEWORK_VERSION = A; INFOPLIST_FILE = Source/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -335,11 +388,16 @@ D8003EA61AFEC3D400D7D3C5 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + APPLICATION_EXTENSION_API_ONLY = NO; COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/Mac", + ); FRAMEWORK_VERSION = A; INFOPLIST_FILE = Source/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -400,6 +458,7 @@ D8003EA61AFEC3D400D7D3C5 /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; D8003EA71AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "RexTests" */ = { isa = XCConfigurationList; @@ -408,6 +467,7 @@ D8003EA91AFEC3D400D7D3C5 /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; From d326d694365062a865d3d580e3024e5ea7a05baf Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 9 May 2015 15:51:02 -0700 Subject: [PATCH 0004/1028] App extension API --- Rex.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 6dbf29dcd2..8aef9ca5e2 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -366,7 +366,7 @@ D8003EA51AFEC3D400D7D3C5 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - APPLICATION_EXTENSION_API_ONLY = NO; + APPLICATION_EXTENSION_API_ONLY = YES; COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; @@ -388,7 +388,7 @@ D8003EA61AFEC3D400D7D3C5 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - APPLICATION_EXTENSION_API_ONLY = NO; + APPLICATION_EXTENSION_API_ONLY = YES; COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; From 1877ae218b2e25c88b1a7ae0521b133a46e3ef2c Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 9 May 2015 15:53:41 -0700 Subject: [PATCH 0005/1028] Port Atomic from RAC --- Rex.xcodeproj/project.pbxproj | 9 ++++- Source/Atomic.swift | 74 +++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 Source/Atomic.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 8aef9ca5e2..e04359c55b 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ D8003EB31AFEC6B000D7D3C5 /* Box.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003EAC1AFEC68A00D7D3C5 /* Box.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 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, ); }; }; + D8003EB71AFEC6F400D7D3C5 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EB61AFEC6F400D7D3C5 /* Atomic.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -53,6 +54,7 @@ D8003EAC1AFEC68A00D7D3C5 /* Box.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Box.framework; sourceTree = ""; }; D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ReactiveCocoa.framework; sourceTree = ""; }; D8003EAE1AFEC68A00D7D3C5 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = ""; }; + D8003EB61AFEC6F400D7D3C5 /* Atomic.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Atomic.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -99,7 +101,7 @@ D8003E901AFEC3D400D7D3C5 /* Source */ = { isa = PBXGroup; children = ( - D8003E931AFEC3D400D7D3C5 /* Rex.h */, + D8003EB61AFEC6F400D7D3C5 /* Atomic.swift */, D8003E911AFEC3D400D7D3C5 /* Supporting Files */, ); path = Source; @@ -108,6 +110,7 @@ D8003E911AFEC3D400D7D3C5 /* Supporting Files */ = { isa = PBXGroup; children = ( + D8003E931AFEC3D400D7D3C5 /* Rex.h */, D8003E921AFEC3D400D7D3C5 /* Info.plist */, ); name = "Supporting Files"; @@ -257,6 +260,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + D8003EB71AFEC6F400D7D3C5 /* Atomic.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -367,6 +371,7 @@ isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_ENABLE_MODULES = YES; COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; @@ -382,6 +387,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; @@ -389,6 +395,7 @@ isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_ENABLE_MODULES = YES; COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; diff --git a/Source/Atomic.swift b/Source/Atomic.swift new file mode 100644 index 0000000000..58baaaf878 --- /dev/null +++ b/Source/Atomic.swift @@ -0,0 +1,74 @@ +// +// Atomic.swift +// ReactiveCocoa +// +// Created by Justin Spahr-Summers on 2014-06-10. +// Copyright (c) 2014 GitHub. All rights reserved. +// + +/// An atomic variable. +internal final class Atomic { + private var spinlock = OS_SPINLOCK_INIT + private var _value: T + + /// Atomically gets or sets the value of the variable. + var value: T { + get { + lock() + let v = _value + unlock() + + return v + } + + set(newValue) { + lock() + _value = newValue + unlock() + } + } + + /// Initializes the variable with the given initial value. + init(_ value: T) { + _value = value + } + + private func lock() { + withUnsafeMutablePointer(&spinlock, OSSpinLockLock) + } + + private func unlock() { + withUnsafeMutablePointer(&spinlock, OSSpinLockUnlock) + } + + /// Atomically replaces the contents of the variable. + /// + /// Returns the old value. + func swap(newValue: T) -> T { + return modify { _ in newValue } + } + + /// Atomically modifies the variable. + /// + /// Returns the old value. + func modify(@noescape action: T -> T) -> T { + lock() + let oldValue = _value + _value = action(_value) + unlock() + + return oldValue + } + + /// Atomically performs an arbitrary action using the current value of the + /// variable. + /// + /// Returns the result of the action. + func withValue(@noescape action: T -> U) -> U { + lock() + let result = action(_value) + unlock() + + return result + } +} \ No newline at end of file From 8c077436d0a593324a834bbc09e0838b504f18a9 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 9 May 2015 15:57:44 -0700 Subject: [PATCH 0006/1028] Implement groupBy --- Rex.xcodeproj/project.pbxproj | 8 +++++-- Source/SignalProducer.swift | 44 +++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 Source/SignalProducer.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index e04359c55b..07d6eef340 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 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, ); }; }; D8003EB71AFEC6F400D7D3C5 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EB61AFEC6F400D7D3C5 /* Atomic.swift */; }; + D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -55,6 +56,7 @@ D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ReactiveCocoa.framework; sourceTree = ""; }; D8003EAE1AFEC68A00D7D3C5 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = ""; }; D8003EB61AFEC6F400D7D3C5 /* Atomic.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Atomic.swift; sourceTree = ""; }; + D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalProducer.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -102,6 +104,7 @@ isa = PBXGroup; children = ( D8003EB61AFEC6F400D7D3C5 /* Atomic.swift */, + D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */, D8003E911AFEC3D400D7D3C5 /* Supporting Files */, ); path = Source; @@ -261,6 +264,7 @@ buildActionMask = 2147483647; files = ( D8003EB71AFEC6F400D7D3C5 /* Atomic.swift in Sources */, + D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -370,7 +374,7 @@ D8003EA51AFEC3D400D7D3C5 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - APPLICATION_EXTENSION_API_ONLY = YES; + APPLICATION_EXTENSION_API_ONLY = NO; CLANG_ENABLE_MODULES = YES; COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; @@ -394,7 +398,7 @@ D8003EA61AFEC3D400D7D3C5 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - APPLICATION_EXTENSION_API_ONLY = YES; + APPLICATION_EXTENSION_API_ONLY = NO; CLANG_ENABLE_MODULES = YES; COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift new file mode 100644 index 0000000000..8420e34e5e --- /dev/null +++ b/Source/SignalProducer.swift @@ -0,0 +1,44 @@ +// +// SignalProducer.swift +// Rex +// +// Created by Neil Pankey on 5/9/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa + +/// 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. +public func groupBy(grouping: T -> K)(producer: SignalProducer) -> SignalProducer<(K, SignalProducer), E> { + return SignalProducer { observer, disposable in + var groups: Dictionary.Observer> = [:] + + producer.start(next: { value in + let key = grouping(value) + var group = groups[key] + + if group == nil { + let (producer, sink) = SignalProducer.buffer() + groups[key] = sink + group = sink + + sendNext(observer, (key, producer)) + } + sendNext(group!, value) + + }, error: { error in + sendError(observer, error) + groups.values.map { sendError($0, error) } + + }, completed: { _ in + sendCompleted(observer) + groups.values.map { sendCompleted($0) } + + }, interrupted: { _ in + groups.values.map { sendInterrupted($0) } + sendInterrupted(observer) + }) + } +} From b1be41f97ddc6fa1bd1834160c9f9b1a74518871 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 9 May 2015 16:28:13 -0700 Subject: [PATCH 0007/1028] Lock when updating groupings --- Source/SignalProducer.swift | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index 8420e34e5e..e88a9c9615 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -12,33 +12,40 @@ import ReactiveCocoa /// from `grouping`. Termination events on the original signal are /// also forwarded to each producer group. public func groupBy(grouping: T -> K)(producer: SignalProducer) -> SignalProducer<(K, SignalProducer), E> { + return SignalProducer { observer, disposable in - var groups: Dictionary.Observer> = [:] + var groups: [K: Signal.Observer] = [:] + + let lock = NSRecursiveLock() + lock.name = "me.neilpa.rex.groupBy" producer.start(next: { value in let key = grouping(value) - var group = groups[key] + lock.lock() + var group = groups[key] if group == nil { let (producer, sink) = SignalProducer.buffer() + sendNext(observer, (key, producer)) + groups[key] = sink group = sink - - sendNext(observer, (key, producer)) } + lock.unlock() + sendNext(group!, value) - }, error: { error in - sendError(observer, error) - groups.values.map { sendError($0, error) } + }, error: { error in + sendError(observer, error) + groups.values.map { sendError($0, error) } - }, completed: { _ in - sendCompleted(observer) - groups.values.map { sendCompleted($0) } + }, completed: { _ in + sendCompleted(observer) + groups.values.map { sendCompleted($0) } - }, interrupted: { _ in - groups.values.map { sendInterrupted($0) } - sendInterrupted(observer) + }, interrupted: { _ in + sendInterrupted(observer) + groups.values.map { sendInterrupted($0) } }) } } From fd23c6b232dd65c0f4ed2b6826f0d9be294045f7 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 9 May 2015 16:30:12 -0700 Subject: [PATCH 0008/1028] Delete Atomic for now --- Rex.xcodeproj/project.pbxproj | 4 -- Source/Atomic.swift | 74 ----------------------------------- 2 files changed, 78 deletions(-) delete mode 100644 Source/Atomic.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 07d6eef340..98f36de373 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -16,7 +16,6 @@ D8003EB31AFEC6B000D7D3C5 /* Box.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003EAC1AFEC68A00D7D3C5 /* Box.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 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, ); }; }; - D8003EB71AFEC6F400D7D3C5 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EB61AFEC6F400D7D3C5 /* Atomic.swift */; }; D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */; }; /* End PBXBuildFile section */ @@ -55,7 +54,6 @@ D8003EAC1AFEC68A00D7D3C5 /* Box.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Box.framework; sourceTree = ""; }; D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ReactiveCocoa.framework; sourceTree = ""; }; D8003EAE1AFEC68A00D7D3C5 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = ""; }; - D8003EB61AFEC6F400D7D3C5 /* Atomic.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Atomic.swift; sourceTree = ""; }; D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalProducer.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -103,7 +101,6 @@ D8003E901AFEC3D400D7D3C5 /* Source */ = { isa = PBXGroup; children = ( - D8003EB61AFEC6F400D7D3C5 /* Atomic.swift */, D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */, D8003E911AFEC3D400D7D3C5 /* Supporting Files */, ); @@ -263,7 +260,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D8003EB71AFEC6F400D7D3C5 /* Atomic.swift in Sources */, D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Source/Atomic.swift b/Source/Atomic.swift deleted file mode 100644 index 58baaaf878..0000000000 --- a/Source/Atomic.swift +++ /dev/null @@ -1,74 +0,0 @@ -// -// Atomic.swift -// ReactiveCocoa -// -// Created by Justin Spahr-Summers on 2014-06-10. -// Copyright (c) 2014 GitHub. All rights reserved. -// - -/// An atomic variable. -internal final class Atomic { - private var spinlock = OS_SPINLOCK_INIT - private var _value: T - - /// Atomically gets or sets the value of the variable. - var value: T { - get { - lock() - let v = _value - unlock() - - return v - } - - set(newValue) { - lock() - _value = newValue - unlock() - } - } - - /// Initializes the variable with the given initial value. - init(_ value: T) { - _value = value - } - - private func lock() { - withUnsafeMutablePointer(&spinlock, OSSpinLockLock) - } - - private func unlock() { - withUnsafeMutablePointer(&spinlock, OSSpinLockUnlock) - } - - /// Atomically replaces the contents of the variable. - /// - /// Returns the old value. - func swap(newValue: T) -> T { - return modify { _ in newValue } - } - - /// Atomically modifies the variable. - /// - /// Returns the old value. - func modify(@noescape action: T -> T) -> T { - lock() - let oldValue = _value - _value = action(_value) - unlock() - - return oldValue - } - - /// Atomically performs an arbitrary action using the current value of the - /// variable. - /// - /// Returns the result of the action. - func withValue(@noescape action: T -> U) -> U { - lock() - let result = action(_value) - unlock() - - return result - } -} \ No newline at end of file From 56ee238a09e55fa42b2add556f6adac05c05ac3a Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 9 May 2015 16:42:11 -0700 Subject: [PATCH 0009/1028] Implement filterMap --- Rex.xcodeproj/project.pbxproj | 4 ++++ Source/Signal.swift | 25 +++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 Source/Signal.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 98f36de373..b98d2b0e81 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 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, ); }; }; D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */; }; + D8003EBD1AFED01000D7D3C5 /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBC1AFED01000D7D3C5 /* Signal.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -55,6 +56,7 @@ D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ReactiveCocoa.framework; sourceTree = ""; }; D8003EAE1AFEC68A00D7D3C5 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = ""; }; D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalProducer.swift; sourceTree = ""; }; + D8003EBC1AFED01000D7D3C5 /* Signal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Signal.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -101,6 +103,7 @@ D8003E901AFEC3D400D7D3C5 /* Source */ = { isa = PBXGroup; children = ( + D8003EBC1AFED01000D7D3C5 /* Signal.swift */, D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */, D8003E911AFEC3D400D7D3C5 /* Supporting Files */, ); @@ -261,6 +264,7 @@ buildActionMask = 2147483647; files = ( D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */, + D8003EBD1AFED01000D7D3C5 /* Signal.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Source/Signal.swift b/Source/Signal.swift new file mode 100644 index 0000000000..408451b7f4 --- /dev/null +++ b/Source/Signal.swift @@ -0,0 +1,25 @@ +// +// Signal.swift +// Rex +// +// Created by Neil Pankey on 5/9/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa + +public func filterMap(transform: T -> U?)(signal: Signal) -> Signal { + return Signal { observer in + signal.observe(next: { value in + if let val = transform(value) { + sendNext(observer, val) + } + }, error: { error in + sendError(observer, error) + }, completed: { + sendCompleted(observer) + }, interrupted: { + sendInterrupted(observer) + }) + } +} From 290032db78f4b0a862c93d164cf35c216738945e Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 9 May 2015 17:03:32 -0700 Subject: [PATCH 0010/1028] Test groupBy --- Rex.xcodeproj/project.pbxproj | 20 ++++++++++++---- Tests/RexTests.swift | 36 ---------------------------- Tests/SignalProducerTests.swift | 42 +++++++++++++++++++++++++++++++++ Tests/SignalTests.swift | 17 +++++++++++++ 4 files changed, 75 insertions(+), 40 deletions(-) delete mode 100644 Tests/RexTests.swift create mode 100644 Tests/SignalProducerTests.swift create mode 100644 Tests/SignalTests.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index b98d2b0e81..5257e1f368 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -9,7 +9,6 @@ /* Begin PBXBuildFile section */ D8003E941AFEC3D400D7D3C5 /* Rex.h in Headers */ = {isa = PBXBuildFile; fileRef = D8003E931AFEC3D400D7D3C5 /* Rex.h */; settings = {ATTRIBUTES = (Public, ); }; }; D8003E9A1AFEC3D400D7D3C5 /* Rex.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003E8E1AFEC3D400D7D3C5 /* Rex.framework */; }; - D8003EA11AFEC3D400D7D3C5 /* RexTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EA01AFEC3D400D7D3C5 /* RexTests.swift */; }; D8003EAF1AFEC68A00D7D3C5 /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAC1AFEC68A00D7D3C5 /* Box.framework */; }; D8003EB01AFEC68A00D7D3C5 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; }; D8003EB11AFEC68A00D7D3C5 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAE1AFEC68A00D7D3C5 /* Result.framework */; }; @@ -18,6 +17,11 @@ D8003EB51AFEC6B000D7D3C5 /* Result.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003EAE1AFEC68A00D7D3C5 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */; }; D8003EBD1AFED01000D7D3C5 /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBC1AFED01000D7D3C5 /* Signal.swift */; }; + D8003EC21AFED30F00D7D3C5 /* SignalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */; }; + D8003EC31AFED30F00D7D3C5 /* SignalProducerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EC01AFED30100D7D3C5 /* SignalProducerTests.swift */; }; + D8003EC41AFED36F00D7D3C5 /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAC1AFEC68A00D7D3C5 /* Box.framework */; }; + D8003EC51AFED36F00D7D3C5 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; }; + D8003EC61AFED36F00D7D3C5 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAE1AFEC68A00D7D3C5 /* Result.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -51,12 +55,13 @@ D8003E931AFEC3D400D7D3C5 /* Rex.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Rex.h; sourceTree = ""; }; D8003E991AFEC3D400D7D3C5 /* RexTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RexTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; D8003E9F1AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - D8003EA01AFEC3D400D7D3C5 /* RexTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RexTests.swift; sourceTree = ""; }; D8003EAC1AFEC68A00D7D3C5 /* Box.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Box.framework; sourceTree = ""; }; D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ReactiveCocoa.framework; sourceTree = ""; }; D8003EAE1AFEC68A00D7D3C5 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = ""; }; D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalProducer.swift; sourceTree = ""; }; D8003EBC1AFED01000D7D3C5 /* Signal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Signal.swift; sourceTree = ""; }; + D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalTests.swift; sourceTree = ""; }; + D8003EC01AFED30100D7D3C5 /* SignalProducerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalProducerTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -74,6 +79,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + D8003EC41AFED36F00D7D3C5 /* Box.framework in Frameworks */, + D8003EC51AFED36F00D7D3C5 /* ReactiveCocoa.framework in Frameworks */, + D8003EC61AFED36F00D7D3C5 /* Result.framework in Frameworks */, D8003E9A1AFEC3D400D7D3C5 /* Rex.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -122,7 +130,8 @@ D8003E9D1AFEC3D400D7D3C5 /* Tests */ = { isa = PBXGroup; children = ( - D8003EA01AFEC3D400D7D3C5 /* RexTests.swift */, + D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */, + D8003EC01AFED30100D7D3C5 /* SignalProducerTests.swift */, D8003E9E1AFEC3D400D7D3C5 /* Supporting Files */, ); path = Tests; @@ -272,7 +281,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D8003EA11AFEC3D400D7D3C5 /* RexTests.swift in Sources */, + D8003EC21AFED30F00D7D3C5 /* SignalTests.swift in Sources */, + D8003EC31AFED30F00D7D3C5 /* SignalProducerTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -425,6 +435,7 @@ FRAMEWORK_SEARCH_PATHS = ( "$(DEVELOPER_FRAMEWORKS_DIR)", "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/Mac", ); GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", @@ -443,6 +454,7 @@ FRAMEWORK_SEARCH_PATHS = ( "$(DEVELOPER_FRAMEWORKS_DIR)", "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/Mac", ); INFOPLIST_FILE = Tests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; diff --git a/Tests/RexTests.swift b/Tests/RexTests.swift deleted file mode 100644 index 9ce57bb77a..0000000000 --- a/Tests/RexTests.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// RexTests.swift -// RexTests -// -// Created by Neil Pankey on 5/9/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - -import Cocoa -import XCTest - -class RexTests: XCTestCase { - - override func setUp() { - super.setUp() - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() - } - - func testExample() { - // This is an example of a functional test case. - XCTAssert(true, "Pass") - } - - func testPerformanceExample() { - // This is an example of a performance test case. - self.measureBlock() { - // Put the code you want to measure the time of here. - } - } - -} diff --git a/Tests/SignalProducerTests.swift b/Tests/SignalProducerTests.swift new file mode 100644 index 0000000000..9f6d064b6c --- /dev/null +++ b/Tests/SignalProducerTests.swift @@ -0,0 +1,42 @@ +// +// SignalProducerTests.swift +// Rex +// +// Created by Neil Pankey on 5/9/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import Rex +import ReactiveCocoa +import XCTest + +final class SignalProducerTests: XCTestCase { + + func testGroupBy() { + let (producer, sink) = SignalProducer.buffer() + var evens: [Int] = [] + var odds: [Int] = [] + + producer + |> groupBy { $0 % 2 == 0 } + |> start(next: { key, group in + if key { + group |> start(next: evens.append) + } else { + group |> start(next: odds.append) + } + }) + + sendNext(sink, 1) + XCTAssert(evens == []) + XCTAssert(odds == [1]) + + sendNext(sink, 2) + XCTAssert(evens == [2]) + XCTAssert(odds == [1]) + + sendNext(sink, 3) + XCTAssert(evens == [2]) + XCTAssert(odds == [1, 3]) + } +} \ No newline at end of file diff --git a/Tests/SignalTests.swift b/Tests/SignalTests.swift new file mode 100644 index 0000000000..645d269a9f --- /dev/null +++ b/Tests/SignalTests.swift @@ -0,0 +1,17 @@ +// +// SignalTests.swift +// Rex +// +// Created by Neil Pankey on 5/9/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import Rex +import XCTest + +final class SignalTests: XCTestCase { + + func testFilterMap() { + + } +} \ No newline at end of file From b5fccaad8bfec968fa35ec48cd8565480ed6e36d Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 9 May 2015 17:10:27 -0700 Subject: [PATCH 0011/1028] Test filterMap --- Tests/SignalTests.swift | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Tests/SignalTests.swift b/Tests/SignalTests.swift index 645d269a9f..ce32fb2253 100644 --- a/Tests/SignalTests.swift +++ b/Tests/SignalTests.swift @@ -7,11 +7,31 @@ // import Rex +import ReactiveCocoa import XCTest final class SignalTests: XCTestCase { func testFilterMap() { + let (signal, sink) = Signal.pipe() + var values: [String] = [] + signal + |> filterMap { + return $0 % 2 == 0 ? toString($0) : nil + } + |> observe(next: values.append) + + sendNext(sink, 1) + XCTAssert(values == []) + + sendNext(sink, 2) + XCTAssert(values == ["2"]) + + sendNext(sink, 3) + XCTAssert(values == ["2"]) + + sendNext(sink, 6) + XCTAssert(values == ["2", "6"]) } } \ No newline at end of file From d534cfa83214e8a9627f284f8abcafc2982287a7 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 9 May 2015 17:19:44 -0700 Subject: [PATCH 0012/1028] Document filterMap --- Source/Signal.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Signal.swift b/Source/Signal.swift index 408451b7f4..d7e722c0df 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -8,6 +8,8 @@ import ReactiveCocoa +/// Applies `transform` to values from `signal` with non-`nil` results unwrapped and +/// forwared on the returned signal. public func filterMap(transform: T -> U?)(signal: Signal) -> Signal { return Signal { observer in signal.observe(next: { value in From b153b35259325d9768b72665604640a17fa348a4 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 9 May 2015 17:28:19 -0700 Subject: [PATCH 0013/1028] Create README.md Document the current set of operators --- README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000000..0196459cc0 --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +## Rex +Additional operators for ReactiveCocoa + +### Signal operators +Note that all `Signal` operators can also be lifted to`SignalProducer`. + +##### `filterMap` +Applies `transform` to values from `signal` with non-nil results unwrapped and forwared on the returned signal. This is equivalent to `signal |> map { … } |> filter { $0 != nil } |> map { $0! }` but doesn't create intermediate signals. + +```swift +func filterMap(transform: T -> U?)(signal: Signal) -> Signal +``` + +### SignalProducer operators + +##### `groupBy` +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. + +```swift +func groupBy(grouping: T -> K)(producer: SignalProducer) + -> SignalProducer<(K, SignalProducer), E> +``` From 8eb3d07a8738ceab033b511c367e172cccdea381 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 9 May 2015 17:29:50 -0700 Subject: [PATCH 0014/1028] Create LICENSE --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..790040a0b5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Neil Pankey + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From e4e78d389d550261d069e1c13f2b8e5476efbff4 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 9 May 2015 17:33:50 -0700 Subject: [PATCH 0015/1028] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0196459cc0..7073fde975 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Additional operators for ReactiveCocoa Note that all `Signal` operators can also be lifted to`SignalProducer`. ##### `filterMap` -Applies `transform` to values from `signal` with non-nil results unwrapped and forwared on the returned signal. This is equivalent to `signal |> map { … } |> filter { $0 != nil } |> map { $0! }` but doesn't create intermediate signals. +Applies `transform` to values from `signal` with non-nil results unwrapped and forwared on the returned signal. This is equivalent to `map { … } |> filter { $0 != nil } |> map { $0! }` but without creating extra intermediate signals. ```swift func filterMap(transform: T -> U?)(signal: Signal) -> Signal @@ -14,7 +14,7 @@ func filterMap(transform: T -> U?)(signal: Signal) -> Signal(grouping: T -> K)(producer: SignalProducer) From f79acabc01db49a38df65999603f3f25bf5b691e Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 9 May 2015 17:37:48 -0700 Subject: [PATCH 0016/1028] Update README.md --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7073fde975..f4ef06824f 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ ## Rex Additional operators for ReactiveCocoa -### Signal operators -Note that all `Signal` operators can also be lifted to`SignalProducer`. +## Signal +All `Signal` operators can also be lifted to`SignalProducer`. ##### `filterMap` Applies `transform` to values from `signal` with non-nil results unwrapped and forwared on the returned signal. This is equivalent to `map { … } |> filter { $0 != nil } |> map { $0! }` but without creating extra intermediate signals. @@ -11,7 +11,8 @@ Applies `transform` to values from `signal` with non-nil results unwrapped and f func filterMap(transform: T -> U?)(signal: Signal) -> Signal ``` -### SignalProducer operators +## SignalProducer +Operators specific to `SignalProducer`. ##### `groupBy` Partitions values from `producer` into new producer groups based on the key returned from `grouping`. Termination events on the original producer are forwarded to each inner producer group. @@ -20,3 +21,6 @@ Partitions values from `producer` into new producer groups based on the key retu func groupBy(grouping: T -> K)(producer: SignalProducer) -> SignalProducer<(K, SignalProducer), E> ``` + +## License +Rex is released under the [MIT license](LICENSE) From dcad8cc359b6f7cc8e7993c922419557d60f781d Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 9 May 2015 17:46:56 -0700 Subject: [PATCH 0017/1028] Shared scheme --- .../xcshareddata/xcschemes/Rex.xcscheme | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 Rex.xcodeproj/xcshareddata/xcschemes/Rex.xcscheme diff --git a/Rex.xcodeproj/xcshareddata/xcschemes/Rex.xcscheme b/Rex.xcodeproj/xcshareddata/xcschemes/Rex.xcscheme new file mode 100644 index 0000000000..4cea1cc7b8 --- /dev/null +++ b/Rex.xcodeproj/xcshareddata/xcschemes/Rex.xcscheme @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From b6be231685f91fa55900291337d6f3ad01d32d66 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 9 May 2015 18:08:08 -0700 Subject: [PATCH 0018/1028] iOS target --- Rex.xcodeproj/project.pbxproj | 339 ++++++++++++++++-- .../xcshareddata/xcschemes/Rex.xcscheme | 16 +- 2 files changed, 308 insertions(+), 47 deletions(-) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 5257e1f368..c618a1c5d1 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -8,10 +8,6 @@ /* Begin PBXBuildFile section */ D8003E941AFEC3D400D7D3C5 /* Rex.h in Headers */ = {isa = PBXBuildFile; fileRef = D8003E931AFEC3D400D7D3C5 /* Rex.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D8003E9A1AFEC3D400D7D3C5 /* Rex.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003E8E1AFEC3D400D7D3C5 /* Rex.framework */; }; - D8003EAF1AFEC68A00D7D3C5 /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAC1AFEC68A00D7D3C5 /* Box.framework */; }; - D8003EB01AFEC68A00D7D3C5 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; }; - D8003EB11AFEC68A00D7D3C5 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAE1AFEC68A00D7D3C5 /* Result.framework */; }; D8003EB31AFEC6B000D7D3C5 /* Box.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003EAC1AFEC68A00D7D3C5 /* Box.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 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, ); }; }; @@ -22,6 +18,22 @@ D8003EC41AFED36F00D7D3C5 /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAC1AFEC68A00D7D3C5 /* Box.framework */; }; D8003EC51AFED36F00D7D3C5 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; }; D8003EC61AFED36F00D7D3C5 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAE1AFEC68A00D7D3C5 /* Result.framework */; }; + 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 */; }; + D83457311AFEE4930070616A /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAC1AFEC68A00D7D3C5 /* Box.framework */; }; + D83457321AFEE4930070616A /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; }; + D83457331AFEE4930070616A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAE1AFEC68A00D7D3C5 /* Result.framework */; }; + D83457341AFEE4B20070616A /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EC81AFEE3ED00D7D3C5 /* Box.framework */; }; + D83457351AFEE4B20070616A /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */; }; + D83457361AFEE4B20070616A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */; }; + D83457381AFEE4BE0070616A /* Box.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003EC81AFEE3ED00D7D3C5 /* Box.framework */; }; + D83457391AFEE4BE0070616A /* ReactiveCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */; }; + D834573A1AFEE4BE0070616A /* Result.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */; }; + D834573B1AFEE57E0070616A /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EC81AFEE3ED00D7D3C5 /* Box.framework */; }; + D834573C1AFEE57E0070616A /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */; }; + D834573D1AFEE57E0070616A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */; }; + D83457411AFEE6050070616A /* SignalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -32,6 +44,13 @@ remoteGlobalIDString = D8003E8D1AFEC3D400D7D3C5; remoteInfo = Rex; }; + D83457201AFEE44E0070616A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D8003E851AFEC3D400D7D3C5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = D83457131AFEE44E0070616A; + remoteInfo = "Rex-iOS"; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -47,13 +66,23 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D83457371AFEE4B80070616A /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 16; + files = ( + D83457381AFEE4BE0070616A /* Box.framework in CopyFiles */, + D83457391AFEE4BE0070616A /* ReactiveCocoa.framework in CopyFiles */, + D834573A1AFEE4BE0070616A /* Result.framework in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - D8003E8E1AFEC3D400D7D3C5 /* Rex.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Rex.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D8003E921AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D8003E931AFEC3D400D7D3C5 /* Rex.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Rex.h; sourceTree = ""; }; - D8003E991AFEC3D400D7D3C5 /* RexTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RexTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; D8003E9F1AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D8003EAC1AFEC68A00D7D3C5 /* Box.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Box.framework; sourceTree = ""; }; D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ReactiveCocoa.framework; sourceTree = ""; }; @@ -62,6 +91,13 @@ D8003EBC1AFED01000D7D3C5 /* Signal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Signal.swift; sourceTree = ""; }; D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalTests.swift; sourceTree = ""; }; D8003EC01AFED30100D7D3C5 /* SignalProducerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalProducerTests.swift; sourceTree = ""; }; + D8003EC81AFEE3ED00D7D3C5 /* Box.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Box.framework; sourceTree = ""; }; + 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; }; + 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; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -69,9 +105,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D8003EB11AFEC68A00D7D3C5 /* Result.framework in Frameworks */, - D8003EAF1AFEC68A00D7D3C5 /* Box.framework in Frameworks */, - D8003EB01AFEC68A00D7D3C5 /* ReactiveCocoa.framework in Frameworks */, + D83457311AFEE4930070616A /* Box.framework in Frameworks */, + D83457321AFEE4930070616A /* ReactiveCocoa.framework in Frameworks */, + D83457331AFEE4930070616A /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -82,7 +118,26 @@ D8003EC41AFED36F00D7D3C5 /* Box.framework in Frameworks */, D8003EC51AFED36F00D7D3C5 /* ReactiveCocoa.framework in Frameworks */, D8003EC61AFED36F00D7D3C5 /* Result.framework in Frameworks */, - D8003E9A1AFEC3D400D7D3C5 /* Rex.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D83457101AFEE44E0070616A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D83457341AFEE4B20070616A /* Box.framework in Frameworks */, + D83457351AFEE4B20070616A /* ReactiveCocoa.framework in Frameworks */, + D83457361AFEE4B20070616A /* Result.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D834571B1AFEE44E0070616A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D834573B1AFEE57E0070616A /* Box.framework in Frameworks */, + D834573C1AFEE57E0070616A /* ReactiveCocoa.framework in Frameworks */, + D834573D1AFEE57E0070616A /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -94,18 +149,8 @@ children = ( D8003E901AFEC3D400D7D3C5 /* Source */, D8003E9D1AFEC3D400D7D3C5 /* Tests */, - D8003E8F1AFEC3D400D7D3C5 /* Products */, - D8003EAA1AFEC57200D7D3C5 /* External */, - ); - sourceTree = ""; - }; - D8003E8F1AFEC3D400D7D3C5 /* Products */ = { - isa = PBXGroup; - children = ( - D8003E8E1AFEC3D400D7D3C5 /* Rex.framework */, - D8003E991AFEC3D400D7D3C5 /* RexTests.xctest */, + D8003EAA1AFEC57200D7D3C5 /* Binaries */, ); - name = Products; sourceTree = ""; }; D8003E901AFEC3D400D7D3C5 /* Source */ = { @@ -145,18 +190,21 @@ name = "Supporting Files"; sourceTree = ""; }; - D8003EAA1AFEC57200D7D3C5 /* External */ = { + D8003EAA1AFEC57200D7D3C5 /* Binaries */ = { isa = PBXGroup; children = ( + D8003EC71AFEE39000D7D3C5 /* iOS */, D8003EAB1AFEC67700D7D3C5 /* Mac */, ); - name = External; + name = Binaries; path = Carthage/Build; sourceTree = ""; }; D8003EAB1AFEC67700D7D3C5 /* Mac */ = { isa = PBXGroup; children = ( + D86E77A91AFEE646004BF23D /* Rex.framework */, + D86E77AA1AFEE646004BF23D /* RexTests-Mac.xctest */, D8003EAC1AFEC68A00D7D3C5 /* Box.framework */, D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */, D8003EAE1AFEC68A00D7D3C5 /* Result.framework */, @@ -164,6 +212,18 @@ path = Mac; sourceTree = ""; }; + D8003EC71AFEE39000D7D3C5 /* iOS */ = { + isa = PBXGroup; + children = ( + D86E77AB1AFEE646004BF23D /* Rex.framework */, + D86E77AC1AFEE646004BF23D /* RexTests-iOS.xctest */, + D8003EC81AFEE3ED00D7D3C5 /* Box.framework */, + D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */, + D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */, + ); + path = iOS; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -175,12 +235,19 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D83457111AFEE44E0070616A /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ - D8003E8D1AFEC3D400D7D3C5 /* Rex */ = { + D8003E8D1AFEC3D400D7D3C5 /* Rex-Mac */ = { isa = PBXNativeTarget; - buildConfigurationList = D8003EA41AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "Rex" */; + buildConfigurationList = D8003EA41AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "Rex-Mac" */; buildPhases = ( D8003E891AFEC3D400D7D3C5 /* Sources */, D8003E8A1AFEC3D400D7D3C5 /* Frameworks */, @@ -191,14 +258,14 @@ ); dependencies = ( ); - name = Rex; + name = "Rex-Mac"; productName = Rex; - productReference = D8003E8E1AFEC3D400D7D3C5 /* Rex.framework */; + productReference = D86E77A91AFEE646004BF23D /* Rex.framework */; productType = "com.apple.product-type.framework"; }; - D8003E981AFEC3D400D7D3C5 /* RexTests */ = { + D8003E981AFEC3D400D7D3C5 /* RexTests-Mac */ = { isa = PBXNativeTarget; - buildConfigurationList = D8003EA71AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "RexTests" */; + buildConfigurationList = D8003EA71AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "RexTests-Mac" */; buildPhases = ( D8003E951AFEC3D400D7D3C5 /* Sources */, D8003E961AFEC3D400D7D3C5 /* Frameworks */, @@ -210,9 +277,46 @@ dependencies = ( D8003E9C1AFEC3D400D7D3C5 /* PBXTargetDependency */, ); - name = RexTests; + name = "RexTests-Mac"; productName = RexTests; - productReference = D8003E991AFEC3D400D7D3C5 /* RexTests.xctest */; + productReference = D86E77AA1AFEE646004BF23D /* RexTests-Mac.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + D83457131AFEE44E0070616A /* Rex-iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = D834572B1AFEE44E0070616A /* Build configuration list for PBXNativeTarget "Rex-iOS" */; + buildPhases = ( + D834570F1AFEE44E0070616A /* Sources */, + D83457101AFEE44E0070616A /* Frameworks */, + D83457111AFEE44E0070616A /* Headers */, + D83457121AFEE44E0070616A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Rex-iOS"; + productName = "Rex-iOS"; + productReference = D86E77AB1AFEE646004BF23D /* Rex.framework */; + productType = "com.apple.product-type.framework"; + }; + D834571D1AFEE44E0070616A /* RexTests-iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = D834572C1AFEE44E0070616A /* Build configuration list for PBXNativeTarget "RexTests-iOS" */; + buildPhases = ( + D834571A1AFEE44E0070616A /* Sources */, + D834571B1AFEE44E0070616A /* Frameworks */, + D834571C1AFEE44E0070616A /* Resources */, + D83457371AFEE4B80070616A /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + D83457211AFEE44E0070616A /* PBXTargetDependency */, + ); + name = "RexTests-iOS"; + productName = "Rex-iOSTests"; + productReference = D86E77AC1AFEE646004BF23D /* RexTests-iOS.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ @@ -230,6 +334,12 @@ D8003E981AFEC3D400D7D3C5 = { CreatedOnToolsVersion = 6.3; }; + D83457131AFEE44E0070616A = { + CreatedOnToolsVersion = 6.3; + }; + D834571D1AFEE44E0070616A = { + CreatedOnToolsVersion = 6.3; + }; }; }; buildConfigurationList = D8003E881AFEC3D400D7D3C5 /* Build configuration list for PBXProject "Rex" */; @@ -240,12 +350,14 @@ en, ); mainGroup = D8003E841AFEC3D400D7D3C5; - productRefGroup = D8003E8F1AFEC3D400D7D3C5 /* Products */; + productRefGroup = D8003E841AFEC3D400D7D3C5; projectDirPath = ""; projectRoot = ""; targets = ( - D8003E8D1AFEC3D400D7D3C5 /* Rex */, - D8003E981AFEC3D400D7D3C5 /* RexTests */, + D8003E8D1AFEC3D400D7D3C5 /* Rex-Mac */, + D8003E981AFEC3D400D7D3C5 /* RexTests-Mac */, + D83457131AFEE44E0070616A /* Rex-iOS */, + D834571D1AFEE44E0070616A /* RexTests-iOS */, ); }; /* End PBXProject section */ @@ -265,6 +377,20 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D83457121AFEE44E0070616A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D834571C1AFEE44E0070616A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -286,14 +412,37 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D834570F1AFEE44E0070616A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D834572D1AFEE45B0070616A /* Signal.swift in Sources */, + D834572E1AFEE45B0070616A /* SignalProducer.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D834571A1AFEE44E0070616A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D83457301AFEE45E0070616A /* SignalProducerTests.swift in Sources */, + D83457411AFEE6050070616A /* SignalTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ D8003E9C1AFEC3D400D7D3C5 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = D8003E8D1AFEC3D400D7D3C5 /* Rex */; + target = D8003E8D1AFEC3D400D7D3C5 /* Rex-Mac */; targetProxy = D8003E9B1AFEC3D400D7D3C5 /* PBXContainerItemProxy */; }; + D83457211AFEE44E0070616A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D83457131AFEE44E0070616A /* Rex-iOS */; + targetProxy = D83457201AFEE44E0070616A /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ @@ -394,12 +543,13 @@ FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Carthage/Build/Mac", + "$(PROJECT_DIR)/Carthage/Build/iOS", ); FRAMEWORK_VERSION = A; INFOPLIST_FILE = Source/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(TARGET_NAME)"; + PRODUCT_NAME = "$(PROJECT_NAME)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; @@ -418,12 +568,13 @@ FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Carthage/Build/Mac", + "$(PROJECT_DIR)/Carthage/Build/iOS", ); FRAMEWORK_VERSION = A; INFOPLIST_FILE = Source/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(TARGET_NAME)"; + PRODUCT_NAME = "$(PROJECT_NAME)"; SKIP_INSTALL = YES; }; name = Release; @@ -462,6 +613,98 @@ }; name = Release; }; + D83457271AFEE44E0070616A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = Source/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + D83457281AFEE44E0070616A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); + INFOPLIST_FILE = Source/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + D83457291AFEE44E0070616A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = Tests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 8.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + }; + name = Debug; + }; + D834572A1AFEE44E0070616A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); + INFOPLIST_FILE = Tests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 8.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -474,7 +717,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - D8003EA41AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "Rex" */ = { + D8003EA41AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "Rex-Mac" */ = { isa = XCConfigurationList; buildConfigurations = ( D8003EA51AFEC3D400D7D3C5 /* Debug */, @@ -483,7 +726,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - D8003EA71AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "RexTests" */ = { + D8003EA71AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "RexTests-Mac" */ = { isa = XCConfigurationList; buildConfigurations = ( D8003EA81AFEC3D400D7D3C5 /* Debug */, @@ -492,6 +735,24 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + D834572B1AFEE44E0070616A /* Build configuration list for PBXNativeTarget "Rex-iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D83457271AFEE44E0070616A /* Debug */, + D83457281AFEE44E0070616A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D834572C1AFEE44E0070616A /* Build configuration list for PBXNativeTarget "RexTests-iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D83457291AFEE44E0070616A /* Debug */, + D834572A1AFEE44E0070616A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = D8003E851AFEC3D400D7D3C5 /* Project object */; diff --git a/Rex.xcodeproj/xcshareddata/xcschemes/Rex.xcscheme b/Rex.xcodeproj/xcshareddata/xcschemes/Rex.xcscheme index 4cea1cc7b8..2e13f8698b 100644 --- a/Rex.xcodeproj/xcshareddata/xcschemes/Rex.xcscheme +++ b/Rex.xcodeproj/xcshareddata/xcschemes/Rex.xcscheme @@ -16,7 +16,7 @@ BuildableIdentifier = "primary" BlueprintIdentifier = "D8003E8D1AFEC3D400D7D3C5" BuildableName = "Rex.framework" - BlueprintName = "Rex" + BlueprintName = "Rex-Mac" ReferencedContainer = "container:Rex.xcodeproj"> @@ -29,8 +29,8 @@ @@ -47,8 +47,8 @@ @@ -58,7 +58,7 @@ BuildableIdentifier = "primary" BlueprintIdentifier = "D8003E8D1AFEC3D400D7D3C5" BuildableName = "Rex.framework" - BlueprintName = "Rex" + BlueprintName = "Rex-Mac" ReferencedContainer = "container:Rex.xcodeproj"> @@ -77,7 +77,7 @@ BuildableIdentifier = "primary" BlueprintIdentifier = "D8003E8D1AFEC3D400D7D3C5" BuildableName = "Rex.framework" - BlueprintName = "Rex" + BlueprintName = "Rex-Mac" ReferencedContainer = "container:Rex.xcodeproj"> @@ -95,7 +95,7 @@ BuildableIdentifier = "primary" BlueprintIdentifier = "D8003E8D1AFEC3D400D7D3C5" BuildableName = "Rex.framework" - BlueprintName = "Rex" + BlueprintName = "Rex-Mac" ReferencedContainer = "container:Rex.xcodeproj"> From 3e8567f963db6e98b79cc1e124691ced2c3b552e Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 9 May 2015 18:11:03 -0700 Subject: [PATCH 0019/1028] Update schemes --- .../{Rex.xcscheme => Rex-Mac.xcscheme} | 0 .../xcshareddata/xcschemes/Rex-iOS.xcscheme | 110 ++++++++++++++++++ .../xcshareddata/WorkspaceSettings.xcsettings | 8 ++ 3 files changed, 118 insertions(+) rename Rex.xcodeproj/xcshareddata/xcschemes/{Rex.xcscheme => Rex-Mac.xcscheme} (100%) create mode 100644 Rex.xcodeproj/xcshareddata/xcschemes/Rex-iOS.xcscheme create mode 100644 Rex.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/Rex.xcodeproj/xcshareddata/xcschemes/Rex.xcscheme b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme similarity index 100% rename from Rex.xcodeproj/xcshareddata/xcschemes/Rex.xcscheme rename to Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme diff --git a/Rex.xcodeproj/xcshareddata/xcschemes/Rex-iOS.xcscheme b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-iOS.xcscheme new file mode 100644 index 0000000000..acb5931db4 --- /dev/null +++ b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-iOS.xcscheme @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Rex.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/Rex.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000000..08de0be8d3 --- /dev/null +++ b/Rex.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded + + + From 667f325dc135fa388649453a9ea7bdbc8c830597 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 9 May 2015 18:15:28 -0700 Subject: [PATCH 0020/1028] Version 0.1 --- Source/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Info.plist b/Source/Info.plist index 2ebeb6a976..63746b787b 100644 --- a/Source/Info.plist +++ b/Source/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.0.1 + 0.1 CFBundleSignature ???? CFBundleVersion From 7f687a7332391563ebcee4200e80f1a754f43a82 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 10 May 2015 12:16:15 -0700 Subject: [PATCH 0021/1028] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f4ef06824f..153561a3c4 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ## Rex -Additional operators for ReactiveCocoa +Additional operators for [ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa) that may not fit in the core framework. ## Signal All `Signal` operators can also be lifted to`SignalProducer`. From 8c502a601cb9e9207eb389fc292a9a7a02e7d4ba Mon Sep 17 00:00:00 2001 From: Ilya Laryionau Date: Sun, 10 May 2015 22:34:55 +0300 Subject: [PATCH 0022/1028] adde printAll, printNext, printError, ignoreError methods --- Source/Signal.swift | 18 ++++++++++++++++++ Source/SignalProducer.swift | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/Source/Signal.swift b/Source/Signal.swift index d7e722c0df..babbb067fb 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -25,3 +25,21 @@ public func filterMap(transform: T -> U?)(signal: Signal) -> Sign }) } } + +/// Returns a signal that drops any errors +public func ignoreError(signal: Signal) -> Signal { + return Signal { observer in + return signal.observe(Signal.Observer { event in + switch event { + case let .Next(value): + sendNext(observer, value.value) + case let .Error(error): + break + case .Completed: + sendCompleted(observer) + case .Interrupted: + sendInterrupted(observer) + } + }) + } +} diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index e88a9c9615..dcd713a9b5 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -49,3 +49,35 @@ public func groupBy(grouping: T -> K)(producer: SignalProduce }) } } + +/// Prints all signal events +public func printAll(signal: SignalProducer) -> SignalProducer { + return signal + |> on(started: { + println("started") + }, error: { + println("error: \($0)") + }, completed: { + println("completed") + }, interrupted: { + println("interrupted") + }, next: { + println("next \($0)") + }) +} + +/// Prints all nexts of signal +public func printNext(signal: SignalProducer) -> SignalProducer { + return signal + |> on(next: { + println("next \($0)") + }) +} + +/// Prints all errors of signal +public func printError(signal: SignalProducer) -> SignalProducer { + return signal + |> on(error: { + println("error: \($0)") + }) +} From 729ac28535a74d7c335e05c536905386078bead6 Mon Sep 17 00:00:00 2001 From: larryonoff Date: Sun, 10 May 2015 22:39:38 +0300 Subject: [PATCH 0023/1028] fixed code style --- Source/SignalProducer.swift | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index dcd713a9b5..bd5d467a1c 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -55,14 +55,14 @@ public func printAll(signal: SignalProducer) -> SignalProducer return signal |> on(started: { println("started") - }, error: { - println("error: \($0)") - }, completed: { - println("completed") - }, interrupted: { - println("interrupted") - }, next: { - println("next \($0)") + }, error: { + println("error: \($0)") + }, completed: { + println("completed") + }, interrupted: { + println("interrupted") + }, next: { + println("next \($0)") }) } From 71f4ecea83001bab4594ad461986a65ae9637678 Mon Sep 17 00:00:00 2001 From: larryonoff Date: Sun, 10 May 2015 22:43:24 +0300 Subject: [PATCH 0024/1028] ignoreError infinity fixed --- Source/Signal.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Signal.swift b/Source/Signal.swift index babbb067fb..9d16e0188a 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -34,7 +34,7 @@ public func ignoreError(signal: Signal) -> Signal { case let .Next(value): sendNext(observer, value.value) case let .Error(error): - break + sendCompleted(observer) case .Completed: sendCompleted(observer) case .Interrupted: From d7784e4eb2bdc75fabc3dab579ed840f6e659071 Mon Sep 17 00:00:00 2001 From: larryonoff Date: Sun, 10 May 2015 22:44:08 +0300 Subject: [PATCH 0025/1028] fixed ignoreError documentation --- Source/Signal.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Signal.swift b/Source/Signal.swift index 9d16e0188a..0cfa699fc5 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -26,7 +26,7 @@ public func filterMap(transform: T -> U?)(signal: Signal) -> Sign } } -/// Returns a signal that drops any errors +/// Returns signal that drops any errors public func ignoreError(signal: Signal) -> Signal { return Signal { observer in return signal.observe(Signal.Observer { event in From 01d2465a85435cea36d32e3b2de4f6eb29d30687 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 10 May 2015 12:51:34 -0700 Subject: [PATCH 0026/1028] Lower deployment targets --- Rex.xcodeproj/project.pbxproj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index c618a1c5d1..39e92d913b 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -549,6 +549,7 @@ 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_NAME = "$(PROJECT_NAME)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -574,6 +575,7 @@ 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_NAME = "$(PROJECT_NAME)"; SKIP_INSTALL = YES; }; @@ -632,7 +634,7 @@ ); INFOPLIST_FILE = Source/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.3; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_NAME = "$(PROJECT_NAME)"; SDKROOT = iphoneos; @@ -655,7 +657,7 @@ ); INFOPLIST_FILE = Source/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.3; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_NAME = "$(PROJECT_NAME)"; SDKROOT = iphoneos; From a25fa6920e1831c08ec276dd67bebc7b2f1e95c9 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 10 May 2015 12:51:47 -0700 Subject: [PATCH 0027/1028] Version 0.1.1 --- Source/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Info.plist b/Source/Info.plist index 63746b787b..9efa180792 100644 --- a/Source/Info.plist +++ b/Source/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.1 + 0.1.1 CFBundleSignature ???? CFBundleVersion From dadb7af3710449c017cc94bcc6b3a50ccd8d7511 Mon Sep 17 00:00:00 2001 From: Ilya Laryionau Date: Sun, 10 May 2015 22:56:26 +0300 Subject: [PATCH 0028/1028] added NSData rac_dataWithContentsOfURL and NSUserDefaults rac_signalForKey --- Rex.xcodeproj/project.pbxproj | 6 ++++ Source/FoundationExtensions.swift | 47 +++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 Source/FoundationExtensions.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index c618a1c5d1..625d883b6c 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + CCF8C8F51AFFED9E00FF444A /* FoundationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCF8C8F41AFFED9E00FF444A /* FoundationExtensions.swift */; }; + CCF8C8F61AFFED9E00FF444A /* FoundationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCF8C8F41AFFED9E00FF444A /* FoundationExtensions.swift */; }; D8003E941AFEC3D400D7D3C5 /* Rex.h in Headers */ = {isa = PBXBuildFile; fileRef = D8003E931AFEC3D400D7D3C5 /* Rex.h */; settings = {ATTRIBUTES = (Public, ); }; }; D8003EB31AFEC6B000D7D3C5 /* Box.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003EAC1AFEC68A00D7D3C5 /* Box.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -81,6 +83,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + CCF8C8F41AFFED9E00FF444A /* FoundationExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoundationExtensions.swift; sourceTree = ""; }; D8003E921AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D8003E931AFEC3D400D7D3C5 /* Rex.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Rex.h; sourceTree = ""; }; D8003E9F1AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -158,6 +161,7 @@ children = ( D8003EBC1AFED01000D7D3C5 /* Signal.swift */, D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */, + CCF8C8F41AFFED9E00FF444A /* FoundationExtensions.swift */, D8003E911AFEC3D400D7D3C5 /* Supporting Files */, ); path = Source; @@ -400,6 +404,7 @@ files = ( D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */, D8003EBD1AFED01000D7D3C5 /* Signal.swift in Sources */, + CCF8C8F51AFFED9E00FF444A /* FoundationExtensions.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -418,6 +423,7 @@ files = ( D834572D1AFEE45B0070616A /* Signal.swift in Sources */, D834572E1AFEE45B0070616A /* SignalProducer.swift in Sources */, + CCF8C8F61AFFED9E00FF444A /* FoundationExtensions.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Source/FoundationExtensions.swift b/Source/FoundationExtensions.swift new file mode 100644 index 0000000000..381bfc09dc --- /dev/null +++ b/Source/FoundationExtensions.swift @@ -0,0 +1,47 @@ +// +// FoundationExtensions.swift +// Rex +// +// Created by Ilya Laryionau on 10/05/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import ReactiveCococa + +extension NSData { + /// Read the data at the URL. + /// Sends the data or the error. + class func rac_dataWithContentsOfURL(url: NSURL, options: NSDataReadingOptions = NSDataReadingOptions.allZeros) -> SignalProducer { + return SignalProducer { observer, disposable in + var error: NSError? + if let data = NSData(contentsOfURL: url, options: options, error: &error) { + sendNext(observer, data) + sendCompleted(observer) + } else { + sendError(observer, error!) + } + } + } +} + +extension NSUserDefaults { + /// Sends value of key when the value is changed + func rac_signalForKey(key: String) -> Signal { + let (signal, observer) = Signal.pipe() + + // send initial value + let initial: AnyObject? = self.objectForKey(UserDefaultsKeywordsKey) + sendNext(observer, initial) + + // observe other values + NSNotificationCenter.defaultCenter().rac_notifications(name: NSUserDefaultsDidChangeNotification, object: self).observe(next: { notification in + let value: AnyObject? = self.objectForKey(key) + + sendNext(observer, value) + }, completed: { + sendCompleted(observer) + }) + + return signal + } +} \ No newline at end of file From 4b712186f4586cea5e48ffa2aed7aee6ab95b7f0 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 10 May 2015 14:06:21 -0700 Subject: [PATCH 0029/1028] Parameterized terminal event for ignoreError --- Source/Signal.swift | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Source/Signal.swift b/Source/Signal.swift index 0cfa699fc5..db13f6fb0f 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -26,15 +26,18 @@ public func filterMap(transform: T -> U?)(signal: Signal) -> Sign } } -/// Returns signal that drops any errors -public func ignoreError(signal: Signal) -> Signal { +/// Returns a signal that drops any error events. A `replacement` terminal event +/// can be provided, the default value is `Completed`. +public func ignoreError(replacement: Event = .Completed)(signal: Signal) -> Signal { + precondition(replacement.isTerminating) + return Signal { observer in return signal.observe(Signal.Observer { event in switch event { case let .Next(value): sendNext(observer, value.value) - case let .Error(error): - sendCompleted(observer) + case .Error: + observer.put(replacement) case .Completed: sendCompleted(observer) case .Interrupted: From a5c573dd8be8cf60ef078c684048aaae54864b6f Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 10 May 2015 14:19:32 -0700 Subject: [PATCH 0030/1028] Overload ignoreError since curried defaults don't work --- Source/Signal.swift | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Source/Signal.swift b/Source/Signal.swift index db13f6fb0f..2f84087cf0 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -26,9 +26,13 @@ public func filterMap(transform: T -> U?)(signal: Signal) -> Sign } } -/// Returns a signal that drops any error events. A `replacement` terminal event -/// can be provided, the default value is `Completed`. -public func ignoreError(replacement: Event = .Completed)(signal: Signal) -> Signal { +/// Returns a signal that drops `Error` events, replacing them with `Completed`. +public func ignoreError(signal: Signal) -> Signal { + return signal |> ignoreError(replacement: .Completed) +} + +/// Returns a signal that drops `Error` sending `replacement` terminal event instead. +public func ignoreError(#replacement: Event)(signal: Signal) -> Signal { precondition(replacement.isTerminating) return Signal { observer in From 8ad848b599729e02d7e84febfb99d7e31e542a65 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 10 May 2015 14:19:39 -0700 Subject: [PATCH 0031/1028] Tests for ignoreError --- Tests/SignalTests.swift | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/Tests/SignalTests.swift b/Tests/SignalTests.swift index ce32fb2253..0e24e13945 100644 --- a/Tests/SignalTests.swift +++ b/Tests/SignalTests.swift @@ -34,4 +34,39 @@ final class SignalTests: XCTestCase { sendNext(sink, 6) XCTAssert(values == ["2", "6"]) } + + func testIgnoreErrorCompletion() { + let (signal, sink) = Signal.pipe() + var completed = false + + signal + |> ignoreError + |> observe(completed: { + completed = true + }) + + sendNext(sink, 1) + XCTAssertFalse(completed) + + sendError(sink, NSError()) + XCTAssertTrue(completed) + } + + func testIgnoreErrorInterruption() { + let (signal, sink) = Signal.pipe() + var interrupted = false + + signal + |> ignoreError(replacement: .Interrupted) + |> observe(interrupted: { + interrupted = true + }) + + sendNext(sink, 1) + XCTAssertFalse(interrupted) + + sendError(sink, NSError()) + XCTAssertTrue(interrupted) + } + } \ No newline at end of file From ae309393d38738ffeba24a3a8ac50bb50727419c Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 10 May 2015 14:37:52 -0700 Subject: [PATCH 0032/1028] Document ignoreError in README --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 153561a3c4..0320455d81 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## Rex +# Rex Additional operators for [ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa) that may not fit in the core framework. ## Signal @@ -11,6 +11,14 @@ Applies `transform` to values from `signal` with non-nil results unwrapped and f func filterMap(transform: T -> U?)(signal: Signal) -> Signal ``` +##### `ignoreError` +Wraps a `signal` in a version that drops `Error` events. By default errors are replaced with a `Completed` event but `Interrupted` can also be specified as `replacement`. + +```swift +func ignoreError(signal: Signal) -> Signal +func ignoreError(#replacement: Event)(signal: Signal) -> Signal +``` + ## SignalProducer Operators specific to `SignalProducer`. From a4d833e1d027c68f1083c76dbd42246ab89f39b4 Mon Sep 17 00:00:00 2001 From: Ilya Laryionau Date: Mon, 11 May 2015 07:46:57 +0300 Subject: [PATCH 0033/1028] print methods improvements --- Source/SignalProducer.swift | 41 +++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index bd5d467a1c..50bffabbc3 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -50,34 +50,35 @@ public func groupBy(grouping: T -> K)(producer: SignalProduce } } -/// Prints all signal events -public func printAll(signal: SignalProducer) -> SignalProducer { - return signal - |> on(started: { - println("started") - }, error: { - println("error: \($0)") - }, completed: { - println("completed") - }, interrupted: { - println("interrupted") - }, next: { - println("next \($0)") - }) +/// Returns a signal that prints the signal events +public func print(signal: SignalProducer) -> SignalProducer { + return signal |> on(event: println) } -/// Prints all nexts of signal +/// Returns a signal that prints the signal `Next` events public func printNext(signal: SignalProducer) -> SignalProducer { return signal - |> on(next: { - println("next \($0)") + |> on(event: { event in + switch event { + case let .Next(value): + println(event) + + default: + break + } }) } -/// Prints all errors of signal +/// Returns a signal that prints the signal `Error` events public func printError(signal: SignalProducer) -> SignalProducer { return signal - |> on(error: { - println("error: \($0)") + |> on(event: { event in + switch event { + case let .Error(value): + println(event) + + default: + break + } }) } From df34f4cda543cd4aeb0092d2afdcc4fcb0f3e439 Mon Sep 17 00:00:00 2001 From: Ilya Laryionau Date: Mon, 11 May 2015 08:12:05 +0300 Subject: [PATCH 0034/1028] fixed `dataWithContentsOfURL:` error unwrapping --- Source/FoundationExtensions.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/FoundationExtensions.swift b/Source/FoundationExtensions.swift index 381bfc09dc..d32ca47f7f 100644 --- a/Source/FoundationExtensions.swift +++ b/Source/FoundationExtensions.swift @@ -18,7 +18,7 @@ extension NSData { sendNext(observer, data) sendCompleted(observer) } else { - sendError(observer, error!) + sendError(observer, error ?? NSError()) } } } From 4ca5ed3376f3b5a8c6b53890768b0a6515538417 Mon Sep 17 00:00:00 2001 From: Ilya Laryionau Date: Mon, 11 May 2015 08:12:58 +0300 Subject: [PATCH 0035/1028] fixed NSUserDefaults.signalForKey initial value --- Source/FoundationExtensions.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/FoundationExtensions.swift b/Source/FoundationExtensions.swift index d32ca47f7f..3301741a18 100644 --- a/Source/FoundationExtensions.swift +++ b/Source/FoundationExtensions.swift @@ -30,7 +30,7 @@ extension NSUserDefaults { let (signal, observer) = Signal.pipe() // send initial value - let initial: AnyObject? = self.objectForKey(UserDefaultsKeywordsKey) + let initial: AnyObject? = self.objectForKey(key) sendNext(observer, initial) // observe other values From f8f85b9a28eb70abd92da53e2297f7634c88f511 Mon Sep 17 00:00:00 2001 From: Ilya Laryionau Date: Mon, 11 May 2015 08:13:31 +0300 Subject: [PATCH 0036/1028] changed Foundation extension methods prefix from rac to rex --- Source/FoundationExtensions.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/FoundationExtensions.swift b/Source/FoundationExtensions.swift index 3301741a18..e400a1aef1 100644 --- a/Source/FoundationExtensions.swift +++ b/Source/FoundationExtensions.swift @@ -11,7 +11,7 @@ import ReactiveCococa extension NSData { /// Read the data at the URL. /// Sends the data or the error. - class func rac_dataWithContentsOfURL(url: NSURL, options: NSDataReadingOptions = NSDataReadingOptions.allZeros) -> SignalProducer { + class func rex_dataWithContentsOfURL(url: NSURL, options: NSDataReadingOptions = NSDataReadingOptions.allZeros) -> SignalProducer { return SignalProducer { observer, disposable in var error: NSError? if let data = NSData(contentsOfURL: url, options: options, error: &error) { @@ -26,7 +26,7 @@ extension NSData { extension NSUserDefaults { /// Sends value of key when the value is changed - func rac_signalForKey(key: String) -> Signal { + func rex_signalForKey(key: String) -> Signal { let (signal, observer) = Signal.pipe() // send initial value From 091ac02bdf061c1f6a537672fdaa549fa97dd0d1 Mon Sep 17 00:00:00 2001 From: Ilya Laryionau Date: Mon, 11 May 2015 10:21:30 +0300 Subject: [PATCH 0037/1028] NSUserDefaults.rex_signalForKey now skips repeats --- Source/FoundationExtensions.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Source/FoundationExtensions.swift b/Source/FoundationExtensions.swift index e400a1aef1..85fbbcb92b 100644 --- a/Source/FoundationExtensions.swift +++ b/Source/FoundationExtensions.swift @@ -34,7 +34,8 @@ extension NSUserDefaults { sendNext(observer, initial) // observe other values - NSNotificationCenter.defaultCenter().rac_notifications(name: NSUserDefaultsDidChangeNotification, object: self).observe(next: { notification in + let defaultsDidChange = NSNotificationCenter.defaultCenter().rac_notifications(name: NSUserDefaultsDidChangeNotification, object: self) + defaultsDidChange.observe(next: { notification in let value: AnyObject? = self.objectForKey(key) sendNext(observer, value) @@ -43,5 +44,12 @@ extension NSUserDefaults { }) return signal + |> skipRepeats { a, b in + if let a = a as? NSObject, b = b as? NSObject where a.isEqual(b) { + return true + } else { + return false + } + } } } \ No newline at end of file From 86f1c9fd72dfd1b0098534fdc605e64b8651496f Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Mon, 11 May 2015 10:39:00 -0700 Subject: [PATCH 0038/1028] Fix the build --- Source/FoundationExtensions.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/FoundationExtensions.swift b/Source/FoundationExtensions.swift index 85fbbcb92b..9b125baa41 100644 --- a/Source/FoundationExtensions.swift +++ b/Source/FoundationExtensions.swift @@ -6,7 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // -import ReactiveCococa +import ReactiveCocoa extension NSData { /// Read the data at the URL. From 5a9366ce6e355aa539fa1c9c3866be6d627881bb Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Mon, 11 May 2015 10:41:08 -0700 Subject: [PATCH 0039/1028] Version 0.1.2 --- Rex.xcodeproj/project.pbxproj | 2 +- Source/Info.plist | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index ad2c624202..4543acb493 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -159,9 +159,9 @@ D8003E901AFEC3D400D7D3C5 /* Source */ = { isa = PBXGroup; children = ( + CCF8C8F41AFFED9E00FF444A /* FoundationExtensions.swift */, D8003EBC1AFED01000D7D3C5 /* Signal.swift */, D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */, - CCF8C8F41AFFED9E00FF444A /* FoundationExtensions.swift */, D8003E911AFEC3D400D7D3C5 /* Supporting Files */, ); path = Source; diff --git a/Source/Info.plist b/Source/Info.plist index 9efa180792..3c8636c89c 100644 --- a/Source/Info.plist +++ b/Source/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.1.1 + 0.1.2 CFBundleSignature ???? CFBundleVersion From 782a7664dee534069f0779dbe2b1f8398ff130c7 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Mon, 11 May 2015 10:49:42 -0700 Subject: [PATCH 0040/1028] Carthage compat badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0320455d81..2d62759dba 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Rex +# Rex [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) Additional operators for [ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa) that may not fit in the core framework. ## Signal From a66bd25f1f69c30524a39a973275329b0842730b Mon Sep 17 00:00:00 2001 From: Alexandros Salazar Date: Wed, 13 May 2015 11:32:41 -0400 Subject: [PATCH 0041/1028] Added operators. --- Rex.xcodeproj/project.pbxproj | 6 ++ .../xcshareddata/xcschemes/Rex-iOS.xcscheme | 4 +- Source/Operators.swift | 68 +++++++++++++++++++ 3 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 Source/Operators.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 4543acb493..a053048049 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 7DE995CB1B03A56800CA9420 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE995CA1B03A56800CA9420 /* Operators.swift */; }; + 7DE995CC1B03A56800CA9420 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE995CA1B03A56800CA9420 /* Operators.swift */; }; CCF8C8F51AFFED9E00FF444A /* FoundationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCF8C8F41AFFED9E00FF444A /* FoundationExtensions.swift */; }; CCF8C8F61AFFED9E00FF444A /* FoundationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCF8C8F41AFFED9E00FF444A /* FoundationExtensions.swift */; }; D8003E941AFEC3D400D7D3C5 /* Rex.h in Headers */ = {isa = PBXBuildFile; fileRef = D8003E931AFEC3D400D7D3C5 /* Rex.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -83,6 +85,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 7DE995CA1B03A56800CA9420 /* Operators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Operators.swift; sourceTree = ""; }; CCF8C8F41AFFED9E00FF444A /* FoundationExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoundationExtensions.swift; sourceTree = ""; }; D8003E921AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D8003E931AFEC3D400D7D3C5 /* Rex.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Rex.h; sourceTree = ""; }; @@ -163,6 +166,7 @@ D8003EBC1AFED01000D7D3C5 /* Signal.swift */, D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */, D8003E911AFEC3D400D7D3C5 /* Supporting Files */, + 7DE995CA1B03A56800CA9420 /* Operators.swift */, ); path = Source; sourceTree = ""; @@ -403,6 +407,7 @@ buildActionMask = 2147483647; files = ( D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */, + 7DE995CB1B03A56800CA9420 /* Operators.swift in Sources */, D8003EBD1AFED01000D7D3C5 /* Signal.swift in Sources */, CCF8C8F51AFFED9E00FF444A /* FoundationExtensions.swift in Sources */, ); @@ -422,6 +427,7 @@ buildActionMask = 2147483647; files = ( D834572D1AFEE45B0070616A /* Signal.swift in Sources */, + 7DE995CC1B03A56800CA9420 /* Operators.swift in Sources */, D834572E1AFEE45B0070616A /* SignalProducer.swift in Sources */, CCF8C8F61AFFED9E00FF444A /* FoundationExtensions.swift in Sources */, ); diff --git a/Rex.xcodeproj/xcshareddata/xcschemes/Rex-iOS.xcscheme b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-iOS.xcscheme index acb5931db4..c3e4fd7959 100644 --- a/Rex.xcodeproj/xcshareddata/xcschemes/Rex-iOS.xcscheme +++ b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-iOS.xcscheme @@ -29,7 +29,7 @@ @@ -47,7 +47,7 @@ diff --git a/Source/Operators.swift b/Source/Operators.swift new file mode 100644 index 0000000000..ce33874992 --- /dev/null +++ b/Source/Operators.swift @@ -0,0 +1,68 @@ +// +// Operators.swift +// Rex +// +// Created by Alexandros Salazar on 5/13/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import Foundation + +import Foundation +import ReactiveCocoa + +infix operator --> { +associativity left + +// bind as strong as assignment. +precedence 90 +} + +/// Sends the value as the next event. Usage: +/// +/// value --> sink +/// +/// :param: value the next value. +/// :param: observer the observer that will handle the value. +public func -->(value:T, sink:SinkOf>) { + sendNext(sink, value) +} + +/// Sends the error as an error event. Usage: +/// +/// error --> sink +/// +/// :param: error the error to send. +/// :param: observer the observer that will handle the value. +public func -->(error:E, sink:SinkOf>) { + sendError(sink, error) +} + +prefix operator --| {} + +/// Sends a completed event to the sink. Usage: +/// +/// --|sink +/// +/// :param: observer the sink to whcih the signal will be sent. +public prefix func --|(sink:SinkOf>) { + return sendCompleted(sink) +} + + +/// Adds the right-hand-side disposable to the left-hand-side +/// `CompositeDisposable`. Usage: +/// +/// disposable += producer +/// |> filter { ... } +/// |> map { ... } +/// |> start(sink) +/// +/// :param: lhs a composite disposable. +/// :param: rhs a disposable, or `nil`. This is to handle situations where +/// functions may or may not return a disposable. +public func +=(lhs:CompositeDisposable, rhs:Disposable?) { + lhs.addDisposable(rhs) +} + + From a6a34c52ee4c732d7bda48e79d9368a59aed65de Mon Sep 17 00:00:00 2001 From: Alexandros Salazar Date: Wed, 13 May 2015 11:40:31 -0400 Subject: [PATCH 0042/1028] Added tests for operators. --- Tests/SignalProducerTests.swift | 49 ++++++++++++++++++++++++++++++--- Tests/SignalTests.swift | 16 +++++------ 2 files changed, 53 insertions(+), 12 deletions(-) diff --git a/Tests/SignalProducerTests.swift b/Tests/SignalProducerTests.swift index 9f6d064b6c..237fbe579b 100644 --- a/Tests/SignalProducerTests.swift +++ b/Tests/SignalProducerTests.swift @@ -16,8 +16,11 @@ final class SignalProducerTests: XCTestCase { let (producer, sink) = SignalProducer.buffer() var evens: [Int] = [] var odds: [Int] = [] + let disposable = CompositeDisposable() + var interrupted = false + var completed = false - producer + disposable += producer |> groupBy { $0 % 2 == 0 } |> start(next: { key, group in if key { @@ -25,18 +28,56 @@ final class SignalProducerTests: XCTestCase { } else { group |> start(next: odds.append) } + },completed: { completed = true }, + interrupted: { + interrupted = true }) - sendNext(sink, 1) + 1 --> sink XCTAssert(evens == []) XCTAssert(odds == [1]) - sendNext(sink, 2) + 2 --> sink XCTAssert(evens == [2]) XCTAssert(odds == [1]) - sendNext(sink, 3) + 3 --> sink XCTAssert(evens == [2]) XCTAssert(odds == [1, 3]) + + disposable.dispose() + + 1 --> sink + XCTAssert(interrupted) + } + + func testCompletionOperator() { + let (producer, sink) = SignalProducer.buffer() + var evens: [Int] = [] + var odds: [Int] = [] + let disposable = CompositeDisposable() + var interrupted = false + var completed = false + + disposable += producer + |> groupBy { $0 % 2 == 0 } + |> start(next: { key, group in + if key { + group |> start(next: evens.append) + } else { + group |> start(next: odds.append) + } + },completed: { completed = true }, + interrupted: { + interrupted = true + }) + + 1 --> sink + XCTAssert(evens == []) + XCTAssert(odds == [1]) + + --|sink + XCTAssert(completed) + XCTAssertFalse(interrupted) } } \ No newline at end of file diff --git a/Tests/SignalTests.swift b/Tests/SignalTests.swift index 0e24e13945..bf5e4a7b51 100644 --- a/Tests/SignalTests.swift +++ b/Tests/SignalTests.swift @@ -22,16 +22,16 @@ final class SignalTests: XCTestCase { } |> observe(next: values.append) - sendNext(sink, 1) + 1 --> sink XCTAssert(values == []) - sendNext(sink, 2) + 2 --> sink XCTAssert(values == ["2"]) - sendNext(sink, 3) + 3 --> sink XCTAssert(values == ["2"]) - sendNext(sink, 6) + 6 --> sink XCTAssert(values == ["2", "6"]) } @@ -45,10 +45,10 @@ final class SignalTests: XCTestCase { completed = true }) - sendNext(sink, 1) + 1 --> sink XCTAssertFalse(completed) - sendError(sink, NSError()) + NSError() --> sink XCTAssertTrue(completed) } @@ -62,10 +62,10 @@ final class SignalTests: XCTestCase { interrupted = true }) - sendNext(sink, 1) + 1 --> sink XCTAssertFalse(interrupted) - sendError(sink, NSError()) + NSError() --> sink XCTAssertTrue(interrupted) } From 74e2d4fe29048e25498bc4f3344b524f8b769d1f Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 13 May 2015 23:27:54 -0700 Subject: [PATCH 0043/1028] Version 0.1.3 --- Source/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Info.plist b/Source/Info.plist index 3c8636c89c..cc2f79dcc2 100644 --- a/Source/Info.plist +++ b/Source/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.1.2 + 0.1.3 CFBundleSignature ???? CFBundleVersion From e2fde7f8aa019045b5156bbedf225d1a2b52bba7 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Mon, 25 May 2015 12:39:58 -0700 Subject: [PATCH 0044/1028] Add uncollect --- Rex.xcodeproj/project.pbxproj | 2 +- Source/Signal.swift | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index a053048049..0109f33830 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -163,10 +163,10 @@ isa = PBXGroup; children = ( CCF8C8F41AFFED9E00FF444A /* FoundationExtensions.swift */, + 7DE995CA1B03A56800CA9420 /* Operators.swift */, D8003EBC1AFED01000D7D3C5 /* Signal.swift */, D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */, D8003E911AFEC3D400D7D3C5 /* Supporting Files */, - 7DE995CA1B03A56800CA9420 /* Operators.swift */, ); path = Source; sourceTree = ""; diff --git a/Source/Signal.swift b/Source/Signal.swift index 2f84087cf0..2e1fdf95ff 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -50,3 +50,21 @@ public func ignoreError(#replacement: Event)(signal: Signal(signal: Signal<[T], E>) -> Signal { + return Signal { observer in + return signal.observe(Signal.Observer { event in + switch event { + case let .Next(batch): + batch.value.map { sendNext(observer, $0) } + case let .Error(error): + sendError(observer, error.value) + case .Completed: + sendCompleted(observer) + case .Interrupted: + sendInterrupted(observer) + } + }) + } +} From 17208623a5872e6ad859a49d1aa054f66c1b8656 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Mon, 25 May 2015 12:46:50 -0700 Subject: [PATCH 0045/1028] Test uncollect --- Tests/SignalTests.swift | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Tests/SignalTests.swift b/Tests/SignalTests.swift index bf5e4a7b51..d578c279d4 100644 --- a/Tests/SignalTests.swift +++ b/Tests/SignalTests.swift @@ -68,5 +68,24 @@ final class SignalTests: XCTestCase { NSError() --> sink XCTAssertTrue(interrupted) } - + + func testUncollect() { + let (signal, sink) = Signal<[Int], NoError>.pipe() + var values: [Int] = [] + + signal + |> uncollect + |> observe(next: { + values.append($0) + }) + + [] --> sink + XCTAssert(values.isEmpty) + + [1] --> sink + XCTAssert(values == [1]) + + [2, 3] --> sink + XCTAssert(values == [1, 2, 3]) + } } \ No newline at end of file From b48a0ac08e12be1c1b01d825ae5039b5a4f55163 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Mon, 25 May 2015 12:48:21 -0700 Subject: [PATCH 0046/1028] Uncollect any SequenceType --- Source/Signal.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Signal.swift b/Source/Signal.swift index 2e1fdf95ff..28f571458d 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -52,12 +52,12 @@ public func ignoreError(#replacement: Event)(signal: Signal(signal: Signal<[T], E>) -> Signal { +public func uncollect(signal: Signal) -> Signal { return Signal { observer in return signal.observe(Signal.Observer { event in switch event { - case let .Next(batch): - batch.value.map { sendNext(observer, $0) } + case let .Next(sequence): + map(sequence.value) { sendNext(observer, $0) } case let .Error(error): sendError(observer, error.value) case .Completed: From 63978fee0d93a286c5b2c5cebd13de9b47e9f24e Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Mon, 25 May 2015 12:58:03 -0700 Subject: [PATCH 0047/1028] Document uncollect --- README.md | 11 ++++++++++- Rex.xcworkspace/contents.xcworkspacedata | 3 +++ Source/Signal.swift | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2d62759dba..aa48c00f3c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Rex [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) -Additional operators for [ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa) that may not fit in the core framework. +Extensions for [ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa) that may not fit in the core framework. ## Signal All `Signal` operators can also be lifted to`SignalProducer`. @@ -19,6 +19,15 @@ func ignoreError(signal: Signal) -> Signal func ignoreError(#replacement: Event)(signal: Signal) -> Signal ``` +##### `uncollect` + +Flattens batches of elements sent on `signal` into each individual element. The inverse of `collect`. + +```swift +func uncollect(signal: Signal) -> Signal +``` + + ## SignalProducer Operators specific to `SignalProducer`. diff --git a/Rex.xcworkspace/contents.xcworkspacedata b/Rex.xcworkspace/contents.xcworkspacedata index c40667845e..4b96bd7a5a 100644 --- a/Rex.xcworkspace/contents.xcworkspacedata +++ b/Rex.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/Source/Signal.swift b/Source/Signal.swift index 28f571458d..66d7d27b4b 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -51,7 +51,7 @@ public func ignoreError(#replacement: Event)(signal: Signal(signal: Signal) -> Signal { return Signal { observer in return signal.observe(Signal.Observer { event in From 80ff3a554e312a917fd7b93f0e35163e467ddfb5 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Tue, 26 May 2015 11:10:40 -0700 Subject: [PATCH 0048/1028] Bump ReactiveCocoa - Removes += since thats part of ReactiveCocoa now - NSUserDefaults.rex_signalForKey is now rex_valueForKey and returns a producer like rac_notifications. --- .gitmodules | 6 +++++ Cartfile.resolved | 4 +-- Carthage/Checkouts/Box | 1 + Carthage/Checkouts/ReactiveCocoa | 2 +- Carthage/Checkouts/Result | 1 + Source/FoundationExtensions.swift | 43 ++++++++++++++----------------- Source/Operators.swift | 18 ------------- 7 files changed, 31 insertions(+), 44 deletions(-) create mode 160000 Carthage/Checkouts/Box create mode 160000 Carthage/Checkouts/Result diff --git a/.gitmodules b/.gitmodules index a87e043fc4..973c600a8d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,9 @@ [submodule "Carthage/Checkouts/ReactiveCocoa"] path = Carthage/Checkouts/ReactiveCocoa url = https://github.com/ReactiveCocoa/ReactiveCocoa.git +[submodule "Carthage/Checkouts/Result"] + path = Carthage/Checkouts/Result + url = https://github.com/antitypical/Result.git +[submodule "Carthage/Checkouts/Box"] + path = Carthage/Checkouts/Box + url = https://github.com/robrix/Box.git diff --git a/Cartfile.resolved b/Cartfile.resolved index 75f5fd8120..b84d80e2f4 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,3 +1,3 @@ github "robrix/Box" "1.2.2" -github "antitypical/Result" "0.4.1" -github "ReactiveCocoa/ReactiveCocoa" "v3.0-beta.4" +github "antitypical/Result" "0.4.3" +github "ReactiveCocoa/ReactiveCocoa" "v3.0-beta.6" diff --git a/Carthage/Checkouts/Box b/Carthage/Checkouts/Box new file mode 160000 index 0000000000..bbe4e612a0 --- /dev/null +++ b/Carthage/Checkouts/Box @@ -0,0 +1 @@ +Subproject commit bbe4e612a03ffe0bbb0e2e476c2be4534b6777a5 diff --git a/Carthage/Checkouts/ReactiveCocoa b/Carthage/Checkouts/ReactiveCocoa index 1c8b18751c..b15798ae4f 160000 --- a/Carthage/Checkouts/ReactiveCocoa +++ b/Carthage/Checkouts/ReactiveCocoa @@ -1 +1 @@ -Subproject commit 1c8b18751c4af1f3c6003077ff57bed80e184c3a +Subproject commit b15798ae4f6386b8461b3b06f49908f2009a28ce diff --git a/Carthage/Checkouts/Result b/Carthage/Checkouts/Result new file mode 160000 index 0000000000..f9045c2a1f --- /dev/null +++ b/Carthage/Checkouts/Result @@ -0,0 +1 @@ +Subproject commit f9045c2a1fee1af321e29eea7c633be5a6a42532 diff --git a/Source/FoundationExtensions.swift b/Source/FoundationExtensions.swift index 9b125baa41..65a98c3bf3 100644 --- a/Source/FoundationExtensions.swift +++ b/Source/FoundationExtensions.swift @@ -25,31 +25,28 @@ extension NSData { } extension NSUserDefaults { - /// Sends value of key when the value is changed - func rex_signalForKey(key: String) -> Signal { - let (signal, observer) = Signal.pipe() - - // send initial value - let initial: AnyObject? = self.objectForKey(key) - sendNext(observer, initial) + /// 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. + func rex_valueForKey(key: String) -> SignalProducer { - // observe other values - let defaultsDidChange = NSNotificationCenter.defaultCenter().rac_notifications(name: NSUserDefaultsDidChangeNotification, object: self) - defaultsDidChange.observe(next: { notification in - let value: AnyObject? = self.objectForKey(key) - - sendNext(observer, value) - }, completed: { - sendCompleted(observer) - }) + let center = NSNotificationCenter.defaultCenter() + let changes = center.rac_notifications(name: NSUserDefaultsDidChangeNotification) + |> map { notification in + // The notification doesn't provide what changed so we have to look + // it up every time + return self.objectForKey(key) + } - return signal - |> skipRepeats { a, b in - if let a = a as? NSObject, b = b as? NSObject where a.isEqual(b) { - return true - } else { - return false + return SignalProducer(value: objectForKey(key)) + |> concat(changes) + |> skipRepeats { previous, next in + if let previous = previous as? NSObject, + let next = next as? NSObject { + return previous == next } - } + return false + } } } \ No newline at end of file diff --git a/Source/Operators.swift b/Source/Operators.swift index ce33874992..d1f86edccc 100644 --- a/Source/Operators.swift +++ b/Source/Operators.swift @@ -48,21 +48,3 @@ prefix operator --| {} public prefix func --|(sink:SinkOf>) { return sendCompleted(sink) } - - -/// Adds the right-hand-side disposable to the left-hand-side -/// `CompositeDisposable`. Usage: -/// -/// disposable += producer -/// |> filter { ... } -/// |> map { ... } -/// |> start(sink) -/// -/// :param: lhs a composite disposable. -/// :param: rhs a disposable, or `nil`. This is to handle situations where -/// functions may or may not return a disposable. -public func +=(lhs:CompositeDisposable, rhs:Disposable?) { - lhs.addDisposable(rhs) -} - - From a16767a66c00c9c9df85335dcf347bd80a9507af Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Tue, 26 May 2015 12:52:19 -0700 Subject: [PATCH 0049/1028] Support app extensions --- Rex.xcodeproj/project.pbxproj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 0109f33830..6740e9ce1b 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -545,7 +545,7 @@ D8003EA51AFEC3D400D7D3C5 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - APPLICATION_EXTENSION_API_ONLY = NO; + APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; @@ -571,7 +571,7 @@ D8003EA61AFEC3D400D7D3C5 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - APPLICATION_EXTENSION_API_ONLY = NO; + APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; @@ -630,6 +630,7 @@ D83457271AFEE44E0070616A /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; @@ -658,6 +659,7 @@ D83457281AFEE44E0070616A /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; From ef8b70ca2842a074771df794f33107a3f98c7b7c Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Tue, 26 May 2015 12:52:35 -0700 Subject: [PATCH 0050/1028] Version 0.2.0 --- Source/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Info.plist b/Source/Info.plist index cc2f79dcc2..6ceda8f66d 100644 --- a/Source/Info.plist +++ b/Source/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.1.3 + 0.2.0 CFBundleSignature ???? CFBundleVersion From c9f22dd731f24c31e96914fc7212465b92e86f97 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Thu, 28 May 2015 14:19:46 -0700 Subject: [PATCH 0051/1028] Add timeoutAfter operator This is timeoutWithError but for any terminal event. --- Source/Signal.swift | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Source/Signal.swift b/Source/Signal.swift index 66d7d27b4b..5ca1f704af 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -51,6 +51,31 @@ public func ignoreError(#replacement: Event)(signal: Signal(interval: NSTimeInterval, withEvent event: Event, onScheduler scheduler: DateSchedulerType) -> Signal -> Signal { + precondition(interval >= 0) + precondition(event.isTerminating) + + return { signal in + return Signal { observer in + let disposable = CompositeDisposable() + + let date = scheduler.currentDate.dateByAddingTimeInterval(interval) + disposable += scheduler.scheduleAfter(date) { + observer.put(event) + } + + disposable += signal.observe(observer) + return disposable + } + } +} + /// Returns a signal that flattens sequences of elements. The inverse of `collect`. public func uncollect(signal: Signal) -> Signal { return Signal { observer in From 3d499271b692c44af2b6e92bc921f79ff62e5c53 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Thu, 28 May 2015 18:09:16 -0700 Subject: [PATCH 0052/1028] Split Foundation extensions --- Rex.xcodeproj/project.pbxproj | 26 ++++++++++++---- Source/Foundation/NSData.swift | 25 ++++++++++++++++ .../NSUserDefaults.swift} | 30 +++++-------------- 3 files changed, 52 insertions(+), 29 deletions(-) create mode 100644 Source/Foundation/NSData.swift rename Source/{FoundationExtensions.swift => Foundation/NSUserDefaults.swift} (57%) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 6740e9ce1b..3a85effd6f 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -9,8 +9,6 @@ /* Begin PBXBuildFile section */ 7DE995CB1B03A56800CA9420 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE995CA1B03A56800CA9420 /* Operators.swift */; }; 7DE995CC1B03A56800CA9420 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE995CA1B03A56800CA9420 /* Operators.swift */; }; - CCF8C8F51AFFED9E00FF444A /* FoundationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCF8C8F41AFFED9E00FF444A /* FoundationExtensions.swift */; }; - CCF8C8F61AFFED9E00FF444A /* FoundationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCF8C8F41AFFED9E00FF444A /* FoundationExtensions.swift */; }; D8003E941AFEC3D400D7D3C5 /* Rex.h in Headers */ = {isa = PBXBuildFile; fileRef = D8003E931AFEC3D400D7D3C5 /* Rex.h */; settings = {ATTRIBUTES = (Public, ); }; }; D8003EB31AFEC6B000D7D3C5 /* Box.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003EAC1AFEC68A00D7D3C5 /* Box.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -38,6 +36,10 @@ D834573C1AFEE57E0070616A /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */; }; D834573D1AFEE57E0070616A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */; }; D83457411AFEE6050070616A /* SignalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */; }; + 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 */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -86,7 +88,6 @@ /* Begin PBXFileReference section */ 7DE995CA1B03A56800CA9420 /* Operators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Operators.swift; sourceTree = ""; }; - CCF8C8F41AFFED9E00FF444A /* FoundationExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoundationExtensions.swift; sourceTree = ""; }; D8003E921AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D8003E931AFEC3D400D7D3C5 /* Rex.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Rex.h; sourceTree = ""; }; D8003E9F1AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -104,6 +105,8 @@ D86E77AA1AFEE646004BF23D /* RexTests-Mac.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RexTests-Mac.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; }; + D8F0973A1B17F2F7002E15BA /* NSData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSData.swift; sourceTree = ""; }; + D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSUserDefaults.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -162,10 +165,10 @@ D8003E901AFEC3D400D7D3C5 /* Source */ = { isa = PBXGroup; children = ( - CCF8C8F41AFFED9E00FF444A /* FoundationExtensions.swift */, 7DE995CA1B03A56800CA9420 /* Operators.swift */, D8003EBC1AFED01000D7D3C5 /* Signal.swift */, D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */, + D8F097391B17F2BF002E15BA /* Foundation */, D8003E911AFEC3D400D7D3C5 /* Supporting Files */, ); path = Source; @@ -232,6 +235,15 @@ path = iOS; sourceTree = ""; }; + D8F097391B17F2BF002E15BA /* Foundation */ = { + isa = PBXGroup; + children = ( + D8F0973A1B17F2F7002E15BA /* NSData.swift */, + D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */, + ); + path = Foundation; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -409,7 +421,8 @@ D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */, 7DE995CB1B03A56800CA9420 /* Operators.swift in Sources */, D8003EBD1AFED01000D7D3C5 /* Signal.swift in Sources */, - CCF8C8F51AFFED9E00FF444A /* FoundationExtensions.swift in Sources */, + D8F0973B1B17F2F7002E15BA /* NSData.swift in Sources */, + D8F0973D1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -426,10 +439,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + D8F0973E1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, D834572D1AFEE45B0070616A /* Signal.swift in Sources */, 7DE995CC1B03A56800CA9420 /* Operators.swift in Sources */, D834572E1AFEE45B0070616A /* SignalProducer.swift in Sources */, - CCF8C8F61AFFED9E00FF444A /* FoundationExtensions.swift in Sources */, + D8F0973F1B17F31E002E15BA /* NSData.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Source/Foundation/NSData.swift b/Source/Foundation/NSData.swift new file mode 100644 index 0000000000..b93854c52d --- /dev/null +++ b/Source/Foundation/NSData.swift @@ -0,0 +1,25 @@ +// +// NSData.swift +// Rex +// +// Created by Ilya Laryionau on 10/05/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa + +extension NSData { + /// Read the data at the URL. + /// Sends the data or the error. + class func rex_dataWithContentsOfURL(url: NSURL, options: NSDataReadingOptions = NSDataReadingOptions.allZeros) -> SignalProducer { + return SignalProducer { observer, disposable in + var error: NSError? + if let data = NSData(contentsOfURL: url, options: options, error: &error) { + sendNext(observer, data) + sendCompleted(observer) + } else { + sendError(observer, error ?? NSError()) + } + } + } +} diff --git a/Source/FoundationExtensions.swift b/Source/Foundation/NSUserDefaults.swift similarity index 57% rename from Source/FoundationExtensions.swift rename to Source/Foundation/NSUserDefaults.swift index 65a98c3bf3..18336be1d5 100644 --- a/Source/FoundationExtensions.swift +++ b/Source/Foundation/NSUserDefaults.swift @@ -1,29 +1,13 @@ // -// FoundationExtensions.swift +// NSUserDefaults.swift // Rex // -// Created by Ilya Laryionau on 10/05/15. +// Created by Neil Pankey on 5/28/15. // Copyright (c) 2015 Neil Pankey. All rights reserved. // import ReactiveCocoa -extension NSData { - /// Read the data at the URL. - /// Sends the data or the error. - class func rex_dataWithContentsOfURL(url: NSURL, options: NSDataReadingOptions = NSDataReadingOptions.allZeros) -> SignalProducer { - return SignalProducer { observer, disposable in - var error: NSError? - if let data = NSData(contentsOfURL: url, options: options, error: &error) { - sendNext(observer, data) - sendCompleted(observer) - } else { - sendError(observer, error ?? NSError()) - } - } - } -} - extension NSUserDefaults { /// 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 @@ -37,16 +21,16 @@ extension NSUserDefaults { // The notification doesn't provide what changed so we have to look // it up every time return self.objectForKey(key) - } + } return SignalProducer(value: objectForKey(key)) |> concat(changes) |> skipRepeats { previous, next in if let previous = previous as? NSObject, - let next = next as? NSObject { - return previous == next + let next = next as? NSObject { + return previous == next } return false - } + } } -} \ No newline at end of file +} From 772898ba70927038cff00c952cf8c3e07520fdc3 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Thu, 28 May 2015 18:16:55 -0700 Subject: [PATCH 0053/1028] Typed KVO signal producer --- Rex.xcodeproj/project.pbxproj | 6 ++++++ Source/Foundation/NSObject.swift | 24 ++++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 Source/Foundation/NSObject.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 3a85effd6f..3482622bbc 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -40,6 +40,8 @@ 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 */; }; + D8F097441B17F3C8002E15BA /* NSObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097431B17F3C8002E15BA /* NSObject.swift */; }; + D8F097451B17F3C8002E15BA /* NSObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097431B17F3C8002E15BA /* NSObject.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -107,6 +109,7 @@ D86E77AC1AFEE646004BF23D /* RexTests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RexTests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; D8F0973A1B17F2F7002E15BA /* NSData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSData.swift; sourceTree = ""; }; D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSUserDefaults.swift; sourceTree = ""; }; + D8F097431B17F3C8002E15BA /* NSObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSObject.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -239,6 +242,7 @@ isa = PBXGroup; children = ( D8F0973A1B17F2F7002E15BA /* NSData.swift */, + D8F097431B17F3C8002E15BA /* NSObject.swift */, D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */, ); path = Foundation; @@ -421,6 +425,7 @@ D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */, 7DE995CB1B03A56800CA9420 /* Operators.swift in Sources */, D8003EBD1AFED01000D7D3C5 /* Signal.swift in Sources */, + D8F097441B17F3C8002E15BA /* NSObject.swift in Sources */, D8F0973B1B17F2F7002E15BA /* NSData.swift in Sources */, D8F0973D1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, ); @@ -442,6 +447,7 @@ D8F0973E1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, D834572D1AFEE45B0070616A /* Signal.swift in Sources */, 7DE995CC1B03A56800CA9420 /* Operators.swift in Sources */, + D8F097451B17F3C8002E15BA /* NSObject.swift in Sources */, D834572E1AFEE45B0070616A /* SignalProducer.swift in Sources */, D8F0973F1B17F31E002E15BA /* NSData.swift in Sources */, ); diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift new file mode 100644 index 0000000000..e60fdbfa5d --- /dev/null +++ b/Source/Foundation/NSObject.swift @@ -0,0 +1,24 @@ +// +// NSObject.swift +// Rex +// +// Created by Neil Pankey on 5/28/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa + +extension NSObject { + /// Creates a strongly-typed producer to monitor `keyPath` via KVO. The caller + /// is responsible for ensuring that the associated value is castable to `T`. + public func rex_producerForKeyPath(keyPath: String) -> SignalProducer { + return self.rac_valuesForKeyPath(keyPath, observer: nil) + .toSignalProducer() + |> map { $0 as! T } + |> catch { error in + // Errors aren't possible, but the compiler doesn't know that. + assertionFailure("Unexpected error from KVO signal: \(error)") + return .empty + } + } +} From 6230876311c4ed1b06422db00ec87773ca709f60 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Thu, 28 May 2015 18:30:57 -0700 Subject: [PATCH 0054/1028] Test rex_producerForKeyPath --- Rex.xcodeproj/project.pbxproj | 14 ++++++++++++++ Source/Foundation/NSObject.swift | 3 +++ Tests/Foundation/NSObjectTests.swift | 29 ++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 Tests/Foundation/NSObjectTests.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 3482622bbc..2ce4152540 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -42,6 +42,8 @@ D8F0973F1B17F31E002E15BA /* NSData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973A1B17F2F7002E15BA /* NSData.swift */; }; D8F097441B17F3C8002E15BA /* NSObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097431B17F3C8002E15BA /* NSObject.swift */; }; D8F097451B17F3C8002E15BA /* NSObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097431B17F3C8002E15BA /* NSObject.swift */; }; + D8F0974A1B17F5E1002E15BA /* NSObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097471B17F5DD002E15BA /* NSObjectTests.swift */; }; + D8F0974B1B17F5E2002E15BA /* NSObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097471B17F5DD002E15BA /* NSObjectTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -110,6 +112,7 @@ D8F0973A1B17F2F7002E15BA /* NSData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSData.swift; sourceTree = ""; }; D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSUserDefaults.swift; sourceTree = ""; }; D8F097431B17F3C8002E15BA /* NSObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSObject.swift; sourceTree = ""; }; + D8F097471B17F5DD002E15BA /* NSObjectTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSObjectTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -191,6 +194,7 @@ children = ( D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */, D8003EC01AFED30100D7D3C5 /* SignalProducerTests.swift */, + D8F097461B17F5BF002E15BA /* Foundation */, D8003E9E1AFEC3D400D7D3C5 /* Supporting Files */, ); path = Tests; @@ -248,6 +252,14 @@ path = Foundation; sourceTree = ""; }; + D8F097461B17F5BF002E15BA /* Foundation */ = { + isa = PBXGroup; + children = ( + D8F097471B17F5DD002E15BA /* NSObjectTests.swift */, + ); + path = Foundation; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -435,6 +447,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + D8F0974A1B17F5E1002E15BA /* NSObjectTests.swift in Sources */, D8003EC21AFED30F00D7D3C5 /* SignalTests.swift in Sources */, D8003EC31AFED30F00D7D3C5 /* SignalProducerTests.swift in Sources */, ); @@ -457,6 +470,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + D8F0974B1B17F5E2002E15BA /* NSObjectTests.swift in Sources */, D83457301AFEE45E0070616A /* SignalProducerTests.swift in Sources */, D83457411AFEE6050070616A /* SignalTests.swift in Sources */, ); diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index e60fdbfa5d..87c06e5224 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -11,6 +11,9 @@ import ReactiveCocoa extension NSObject { /// Creates a strongly-typed producer to monitor `keyPath` via KVO. The caller /// is responsible for ensuring that the associated value is castable to `T`. + /// + /// Swift classes deriving `NSObject` must declare properties as `dynamic` for + /// them to work with KVO. However, this is not recommended practice. public func rex_producerForKeyPath(keyPath: String) -> SignalProducer { return self.rac_valuesForKeyPath(keyPath, observer: nil) .toSignalProducer() diff --git a/Tests/Foundation/NSObjectTests.swift b/Tests/Foundation/NSObjectTests.swift new file mode 100644 index 0000000000..7a22600fc6 --- /dev/null +++ b/Tests/Foundation/NSObjectTests.swift @@ -0,0 +1,29 @@ +// +// NSObjectTests.swift +// Rex +// +// Created by Neil Pankey on 5/28/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import Rex +import ReactiveCocoa +import XCTest + +final class NSObjectTests: XCTestCase { + + func testProducerForKeyPath() { + let object = Object() + var value: String = "" + + object.rex_producerForKeyPath("string").start(next: { value = $0 }) + XCTAssertEqual(value, "foo") + + object.string = "bar" + XCTAssertEqual(value, "bar") + } +} + +class Object: NSObject { + dynamic var string = "foo" +} From 9425ef5647585452f168a23dabc10e09419af865 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Fri, 29 May 2015 13:15:37 -0700 Subject: [PATCH 0055/1028] Test timeoutAfter --- Tests/SignalTests.swift | 46 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/Tests/SignalTests.swift b/Tests/SignalTests.swift index d578c279d4..2f03cfbf3b 100644 --- a/Tests/SignalTests.swift +++ b/Tests/SignalTests.swift @@ -69,6 +69,52 @@ final class SignalTests: XCTestCase { XCTAssertTrue(interrupted) } + func testTimeoutAfterTerminating() { + let scheduler = TestScheduler() + let (signal, sink) = Signal.pipe() + var interrupted = false + var completed = false + + signal + |> timeoutAfter(2, withEvent: .Interrupted, onScheduler: scheduler) + |> observe( + completed: { completed = true }, + interrupted: { interrupted = true } + ) + + scheduler.scheduleAfter(1) { sendCompleted(sink) } + + XCTAssertFalse(interrupted) + XCTAssertFalse(completed) + + scheduler.run() + XCTAssertTrue(completed) + XCTAssertFalse(interrupted) + } + + func testTimeoutAfterTimingOut() { + let scheduler = TestScheduler() + let (signal, sink) = Signal.pipe() + var interrupted = false + var completed = false + + signal + |> timeoutAfter(2, withEvent: .Interrupted, onScheduler: scheduler) + |> observe( + completed: { completed = true }, + interrupted: { interrupted = true } + ) + + scheduler.scheduleAfter(3) { sendCompleted(sink) } + + XCTAssertFalse(interrupted) + XCTAssertFalse(completed) + + scheduler.run() + XCTAssertTrue(interrupted) + XCTAssertFalse(completed) + } + func testUncollect() { let (signal, sink) = Signal<[Int], NoError>.pipe() var values: [Int] = [] From 9beb048c26ce5bb945943fe558cc60fe3c7ae7d5 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Fri, 29 May 2015 13:20:26 -0700 Subject: [PATCH 0056/1028] Add timeoutAfter to the README --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index aa48c00f3c..a11d9f0466 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,13 @@ func ignoreError(signal: Signal) -> Signal func ignoreError(#replacement: Event)(signal: Signal) -> Signal ``` +##### `timeoutAfter` +Forwards events from `signal` until it terminates or until `interval` time passes. This is nearly identical to `timeoutWithError` from RAC except any terminating `event` can be used for the timeout. + +```swift +func timeoutAfter(interval: NSTimeInterval, withEvent event: Event, onScheduler scheduler: DateSchedulerType) -> Signal -> Signal +``` + ##### `uncollect` Flattens batches of elements sent on `signal` into each individual element. The inverse of `collect`. From 3bd3f09410321b62eb279b0700c0ea6cdfb6479a Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Fri, 29 May 2015 13:25:08 -0700 Subject: [PATCH 0057/1028] Version 0.2.1 --- Source/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Info.plist b/Source/Info.plist index 6ceda8f66d..804608f064 100644 --- a/Source/Info.plist +++ b/Source/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.2.0 + 0.2.1 CFBundleSignature ???? CFBundleVersion From c1997964ce8e9cf16a41373b488ab08258c71e16 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Fri, 29 May 2015 14:17:39 -0700 Subject: [PATCH 0058/1028] Add SignalProperty --- Rex.xcodeproj/project.pbxproj | 6 ++++++ Source/Property.swift | 36 +++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 Source/Property.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 2ce4152540..4f5a843bfb 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -44,6 +44,8 @@ D8F097451B17F3C8002E15BA /* NSObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097431B17F3C8002E15BA /* NSObject.swift */; }; D8F0974A1B17F5E1002E15BA /* NSObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097471B17F5DD002E15BA /* NSObjectTests.swift */; }; D8F0974B1B17F5E2002E15BA /* NSObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097471B17F5DD002E15BA /* NSObjectTests.swift */; }; + D8F0974D1B190ECE002E15BA /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0974C1B190ECE002E15BA /* Property.swift */; }; + D8F0974E1B190ECE002E15BA /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0974C1B190ECE002E15BA /* Property.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -113,6 +115,7 @@ D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSUserDefaults.swift; sourceTree = ""; }; D8F097431B17F3C8002E15BA /* NSObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSObject.swift; sourceTree = ""; }; D8F097471B17F5DD002E15BA /* NSObjectTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSObjectTests.swift; sourceTree = ""; }; + D8F0974C1B190ECE002E15BA /* Property.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Property.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -174,6 +177,7 @@ 7DE995CA1B03A56800CA9420 /* Operators.swift */, D8003EBC1AFED01000D7D3C5 /* Signal.swift */, D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */, + D8F0974C1B190ECE002E15BA /* Property.swift */, D8F097391B17F2BF002E15BA /* Foundation */, D8003E911AFEC3D400D7D3C5 /* Supporting Files */, ); @@ -434,6 +438,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + D8F0974D1B190ECE002E15BA /* Property.swift in Sources */, D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */, 7DE995CB1B03A56800CA9420 /* Operators.swift in Sources */, D8003EBD1AFED01000D7D3C5 /* Signal.swift in Sources */, @@ -457,6 +462,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + D8F0974E1B190ECE002E15BA /* Property.swift in Sources */, D8F0973E1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, D834572D1AFEE45B0070616A /* Signal.swift in Sources */, 7DE995CC1B03A56800CA9420 /* Operators.swift in Sources */, diff --git a/Source/Property.swift b/Source/Property.swift new file mode 100644 index 0000000000..9d0b717413 --- /dev/null +++ b/Source/Property.swift @@ -0,0 +1,36 @@ +// +// Property.swift +// Rex +// +// Created by Neil Pankey on 5/29/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa + +public final class SignalProperty: PropertyType { + private let property: MutableProperty + private let disposable: Disposable? + + public var value: T { + return property.value + } + + public var producer: SignalProducer { + return property.producer + } + + public init(_ value: T, _ producer: SignalProducer) { + property = MutableProperty(value) + disposable = (property <~ producer) + } + + public init(_ value: T, _ signal: Signal) { + property = MutableProperty(value) + disposable = (property <~ signal) + } + + deinit { + disposable?.dispose() + } +} From e69f6ace2c0b5c4254f7ef9b78fc74a18d86558a Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Fri, 29 May 2015 15:05:33 -0700 Subject: [PATCH 0059/1028] Test signal property --- Rex.xcodeproj/project.pbxproj | 8 +++++- Source/Property.swift | 15 ++++-------- Tests/PropertyTests.swift | 46 +++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 11 deletions(-) create mode 100644 Tests/PropertyTests.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 4f5a843bfb..a4ffcf0776 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -46,6 +46,8 @@ D8F0974B1B17F5E2002E15BA /* NSObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097471B17F5DD002E15BA /* NSObjectTests.swift */; }; D8F0974D1B190ECE002E15BA /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0974C1B190ECE002E15BA /* Property.swift */; }; D8F0974E1B190ECE002E15BA /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0974C1B190ECE002E15BA /* Property.swift */; }; + D8F097501B190F1A002E15BA /* PropertyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0974F1B190F1A002E15BA /* PropertyTests.swift */; }; + D8F097511B190F1A002E15BA /* PropertyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0974F1B190F1A002E15BA /* PropertyTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -116,6 +118,7 @@ D8F097431B17F3C8002E15BA /* NSObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSObject.swift; sourceTree = ""; }; D8F097471B17F5DD002E15BA /* NSObjectTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSObjectTests.swift; sourceTree = ""; }; D8F0974C1B190ECE002E15BA /* Property.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Property.swift; sourceTree = ""; }; + D8F0974F1B190F1A002E15BA /* PropertyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PropertyTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -175,9 +178,9 @@ isa = PBXGroup; children = ( 7DE995CA1B03A56800CA9420 /* Operators.swift */, + D8F0974C1B190ECE002E15BA /* Property.swift */, D8003EBC1AFED01000D7D3C5 /* Signal.swift */, D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */, - D8F0974C1B190ECE002E15BA /* Property.swift */, D8F097391B17F2BF002E15BA /* Foundation */, D8003E911AFEC3D400D7D3C5 /* Supporting Files */, ); @@ -196,6 +199,7 @@ D8003E9D1AFEC3D400D7D3C5 /* Tests */ = { isa = PBXGroup; children = ( + D8F0974F1B190F1A002E15BA /* PropertyTests.swift */, D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */, D8003EC01AFED30100D7D3C5 /* SignalProducerTests.swift */, D8F097461B17F5BF002E15BA /* Foundation */, @@ -454,6 +458,7 @@ files = ( D8F0974A1B17F5E1002E15BA /* NSObjectTests.swift in Sources */, D8003EC21AFED30F00D7D3C5 /* SignalTests.swift in Sources */, + D8F097501B190F1A002E15BA /* PropertyTests.swift in Sources */, D8003EC31AFED30F00D7D3C5 /* SignalProducerTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -478,6 +483,7 @@ files = ( D8F0974B1B17F5E2002E15BA /* NSObjectTests.swift in Sources */, D83457301AFEE45E0070616A /* SignalProducerTests.swift in Sources */, + D8F097511B190F1A002E15BA /* PropertyTests.swift in Sources */, D83457411AFEE6050070616A /* SignalTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Source/Property.swift b/Source/Property.swift index 9d0b717413..f79fcac66b 100644 --- a/Source/Property.swift +++ b/Source/Property.swift @@ -8,9 +8,8 @@ import ReactiveCocoa -public final class SignalProperty: PropertyType { +public struct SignalProperty: PropertyType { private let property: MutableProperty - private let disposable: Disposable? public var value: T { return property.value @@ -20,17 +19,13 @@ public final class SignalProperty: PropertyType { return property.producer } - public init(_ value: T, _ producer: SignalProducer) { - property = MutableProperty(value) - disposable = (property <~ producer) - } - public init(_ value: T, _ signal: Signal) { property = MutableProperty(value) - disposable = (property <~ signal) + property <~ signal } - deinit { - disposable?.dispose() + public init(_ value: T, _ producer: SignalProducer) { + property = MutableProperty(value) + property <~ producer } } diff --git a/Tests/PropertyTests.swift b/Tests/PropertyTests.swift new file mode 100644 index 0000000000..8c2244817e --- /dev/null +++ b/Tests/PropertyTests.swift @@ -0,0 +1,46 @@ +// +// PropertyTests.swift +// Rex +// +// Created by Neil Pankey on 5/29/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import Rex +import ReactiveCocoa +import XCTest + +final class PropertyTests: XCTestCase { + + func testSignalPropertyValues() { + let (signal, sink) = Signal.pipe() + var property: SignalProperty? = SignalProperty(0, signal) + + var latest = -1 + property?.producer.start(next: { + println("Really?") + latest = $0 + }) + + XCTAssert(latest == 0) + + sendNext(sink, 1) + XCTAssert(latest == 1) + } + + func testSignalPropertyLifetime() { + let (signal, sink) = Signal.pipe() + var property: SignalProperty? = SignalProperty(0, signal) + + var completed = false + property?.producer.start(completed: { + println("Really?") + completed = true + }) + + println("Before") + property = nil + println("After") + XCTAssert(completed) + } +} From 423e5f0ca6e975495d4a127ce55159586f6fe023 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Fri, 29 May 2015 15:20:36 -0700 Subject: [PATCH 0060/1028] PropertyOf helpers and tests --- Source/Property.swift | 24 ++++++++++++++++++++++++ Tests/PropertyTests.swift | 23 +++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/Source/Property.swift b/Source/Property.swift index f79fcac66b..24de073cd2 100644 --- a/Source/Property.swift +++ b/Source/Property.swift @@ -29,3 +29,27 @@ public struct SignalProperty: PropertyType { property <~ producer } } + +public func propertyOf(initialValue: T, producer: SignalProducer) -> PropertyOf { + return PropertyOf(SignalProperty(initialValue, producer)) +} + +public func propertyOf(initialValue: T, signal: Signal) -> PropertyOf { + return PropertyOf(SignalProperty(initialValue, signal)) +} + +public func sinkProperty(initialValue: S, signal: Signal) -> PropertyOf { + return propertyOf(initialValue, signal + |> scan(initialValue) { (var value, change) in + value.put(change) + return value + }) +} + +public func sinkProperty(initialValue: S, producer: SignalProducer) -> PropertyOf { + return propertyOf(initialValue, producer + |> scan(initialValue) { (var value, change) in + value.put(change) + return value + }) +} diff --git a/Tests/PropertyTests.swift b/Tests/PropertyTests.swift index 8c2244817e..23edc75777 100644 --- a/Tests/PropertyTests.swift +++ b/Tests/PropertyTests.swift @@ -43,4 +43,27 @@ final class PropertyTests: XCTestCase { println("After") XCTAssert(completed) } + + func testSinkProperty() { + let (signal, sink) = Signal.pipe() + + var property = sinkProperty(Collector(), signal) + XCTAssert(property.value.values == []) + + sendNext(sink, 1) + XCTAssert(property.value.values == [1]) + + sendNext(sink, 2) + sendNext(sink, 3) + XCTAssert(property.value.values == [1, 2, 3]) + } +} + +struct Collector: SinkType { + typealias Element = T + var values: [T] = [] + + mutating func put(value: T) { + values.append(value) + } } From ad86427a1373c96842ddfdef195c342ea11a8784 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Fri, 29 May 2015 15:31:57 -0700 Subject: [PATCH 0061/1028] Curry property operators --- Source/Property.swift | 22 ++++++---------------- Tests/PropertyTests.swift | 2 +- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/Source/Property.swift b/Source/Property.swift index 24de073cd2..1e574b3b71 100644 --- a/Source/Property.swift +++ b/Source/Property.swift @@ -30,26 +30,16 @@ public struct SignalProperty: PropertyType { } } -public func propertyOf(initialValue: T, producer: SignalProducer) -> PropertyOf { - return PropertyOf(SignalProperty(initialValue, producer)) -} - -public func propertyOf(initialValue: T, signal: Signal) -> PropertyOf { +public func propertyOf(initialValue: T)(signal: Signal) -> PropertyOf { return PropertyOf(SignalProperty(initialValue, signal)) } -public func sinkProperty(initialValue: S, signal: Signal) -> PropertyOf { - return propertyOf(initialValue, signal - |> scan(initialValue) { (var value, change) in - value.put(change) - return value - }) -} - -public func sinkProperty(initialValue: S, producer: SignalProducer) -> PropertyOf { - return propertyOf(initialValue, producer +public func sinkProperty(initialValue: S)(signal: Signal) -> PropertyOf { + return signal |> scan(initialValue) { (var value, change) in value.put(change) return value - }) + } + |> map { $0.0 } + |> propertyOf(initialValue) } diff --git a/Tests/PropertyTests.swift b/Tests/PropertyTests.swift index 23edc75777..f5c0165085 100644 --- a/Tests/PropertyTests.swift +++ b/Tests/PropertyTests.swift @@ -47,7 +47,7 @@ final class PropertyTests: XCTestCase { func testSinkProperty() { let (signal, sink) = Signal.pipe() - var property = sinkProperty(Collector(), signal) + let property = signal |> sinkProperty(Collector()) XCTAssert(property.value.values == []) sendNext(sink, 1) From 80bc0e8bb4986d61b37d0222fb79763fda710def Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Fri, 29 May 2015 16:01:31 -0700 Subject: [PATCH 0062/1028] Document property helpers --- README.md | 19 +++++++++++++++++++ Source/Property.swift | 14 +++++++++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a11d9f0466..d4b662b52e 100644 --- a/README.md +++ b/README.md @@ -46,5 +46,24 @@ func groupBy(grouping: T -> K)(producer: SignalProducer -> SignalProducer<(K, SignalProducer), E> ``` + +## Property +Extensions for creating properties from signals. These are curried to support chaining with `|>` and lifting signal producers. + +##### `propertyOf` +Creates a new property bound to `signal` starting with `initialValue`. + +```swift +func propertyOf(initialValue: T)(signal: Signal) -> PropertyOf +``` + +##### `sinkProperty` +Wraps `sink` in a property bound to `signal`. Values sent on `signal` are `put` into the `sink` to update it. + +```swift +func sinkProperty(sink: S)(signal: Signal) -> PropertyOf +``` + + ## License Rex is released under the [MIT license](LICENSE) diff --git a/Source/Property.swift b/Source/Property.swift index 1e574b3b71..c02eb39663 100644 --- a/Source/Property.swift +++ b/Source/Property.swift @@ -8,38 +8,46 @@ import ReactiveCocoa +/// A property that tracks a signals most recent value. public struct SignalProperty: PropertyType { private let property: MutableProperty + /// Current value of the property. public var value: T { return property.value } + /// Sends the current `value` and any changes. public var producer: SignalProducer { return property.producer } + /// Creates a new property bound to `signal`. public init(_ value: T, _ signal: Signal) { property = MutableProperty(value) property <~ signal } + /// Creates a new property bound to `producer`. public init(_ value: T, _ producer: SignalProducer) { property = MutableProperty(value) property <~ producer } } +/// Creates a new property bound to `signal` starting with `initialValue`. public func propertyOf(initialValue: T)(signal: Signal) -> PropertyOf { return PropertyOf(SignalProperty(initialValue, signal)) } -public func sinkProperty(initialValue: S)(signal: Signal) -> PropertyOf { +/// Wraps `sink` in a property bound to `signal`. Values sent on `signal` are `put` into +/// the `sink` to update it. +public func sinkProperty(sink: S)(signal: Signal) -> PropertyOf { return signal - |> scan(initialValue) { (var value, change) in + |> scan(sink) { (var value, change) in value.put(change) return value } |> map { $0.0 } - |> propertyOf(initialValue) + |> propertyOf(sink) } From dff4acefcc6844ad44766db3ab662d5fb2017541 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Fri, 29 May 2015 16:04:41 -0700 Subject: [PATCH 0063/1028] sinkProperty -> propertySink --- README.md | 2 +- Source/Property.swift | 2 +- Tests/PropertyTests.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d4b662b52e..5306e6a52c 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ Creates a new property bound to `signal` starting with `initialValue`. func propertyOf(initialValue: T)(signal: Signal) -> PropertyOf ``` -##### `sinkProperty` +##### `propertySink` Wraps `sink` in a property bound to `signal`. Values sent on `signal` are `put` into the `sink` to update it. ```swift diff --git a/Source/Property.swift b/Source/Property.swift index c02eb39663..6595339483 100644 --- a/Source/Property.swift +++ b/Source/Property.swift @@ -42,7 +42,7 @@ public func propertyOf(initialValue: T)(signal: Signal) -> Proper /// Wraps `sink` in a property bound to `signal`. Values sent on `signal` are `put` into /// the `sink` to update it. -public func sinkProperty(sink: S)(signal: Signal) -> PropertyOf { +public func propertySink(sink: S)(signal: Signal) -> PropertyOf { return signal |> scan(sink) { (var value, change) in value.put(change) diff --git a/Tests/PropertyTests.swift b/Tests/PropertyTests.swift index f5c0165085..63fb16e967 100644 --- a/Tests/PropertyTests.swift +++ b/Tests/PropertyTests.swift @@ -47,7 +47,7 @@ final class PropertyTests: XCTestCase { func testSinkProperty() { let (signal, sink) = Signal.pipe() - let property = signal |> sinkProperty(Collector()) + let property = signal |> propertySink(Collector()) XCTAssert(property.value.values == []) sendNext(sink, 1) From 4604b39c29011a38edaf768f64dc042d5c8d48a8 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Fri, 29 May 2015 16:05:07 -0700 Subject: [PATCH 0064/1028] Test cleanup --- Tests/PropertyTests.swift | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Tests/PropertyTests.swift b/Tests/PropertyTests.swift index 63fb16e967..63550fd218 100644 --- a/Tests/PropertyTests.swift +++ b/Tests/PropertyTests.swift @@ -14,11 +14,10 @@ final class PropertyTests: XCTestCase { func testSignalPropertyValues() { let (signal, sink) = Signal.pipe() - var property: SignalProperty? = SignalProperty(0, signal) + var property = SignalProperty(0, signal) var latest = -1 - property?.producer.start(next: { - println("Really?") + property.producer.start(next: { latest = $0 }) @@ -34,13 +33,10 @@ final class PropertyTests: XCTestCase { var completed = false property?.producer.start(completed: { - println("Really?") completed = true }) - println("Before") property = nil - println("After") XCTAssert(completed) } From 3973421534bb31dbcd665d19e59cce7b821f3cca Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Fri, 29 May 2015 16:21:02 -0700 Subject: [PATCH 0065/1028] Property operators work with producers now --- README.md | 10 ++++++---- Source/Property.swift | 19 ++++++++++++++++--- Tests/PropertyTests.swift | 16 +++++++++++++++- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 5306e6a52c..c48c83ada2 100644 --- a/README.md +++ b/README.md @@ -48,20 +48,22 @@ func groupBy(grouping: T -> K)(producer: SignalProducer ## Property -Extensions for creating properties from signals. These are curried to support chaining with `|>` and lifting signal producers. +Extensions for creating properties from signals. These are curried to support chaining with `|>`. ##### `propertyOf` -Creates a new property bound to `signal` starting with `initialValue`. +Creates a new property bound to the provided signal/producer starting with `initialValue`. ```swift func propertyOf(initialValue: T)(signal: Signal) -> PropertyOf +func propertyOf(initialValue: T)(producer: SignalProducer) -> PropertyOf ``` ##### `propertySink` -Wraps `sink` in a property bound to `signal`. Values sent on `signal` are `put` into the `sink` to update it. +Wraps `sink` in a property bound to the provided signal/producer. Values sent on `signal` are `put` into the `sink` to update it. ```swift -func sinkProperty(sink: S)(signal: Signal) -> PropertyOf +func propertySink(sink: S)(signal: Signal) -> PropertyOf +func propertySink(sink: S)(producer: SignalProducer) -> PropertyOf ``` diff --git a/Source/Property.swift b/Source/Property.swift index 6595339483..a282155161 100644 --- a/Source/Property.swift +++ b/Source/Property.swift @@ -40,14 +40,27 @@ public func propertyOf(initialValue: T)(signal: Signal) -> Proper return PropertyOf(SignalProperty(initialValue, signal)) } +/// Creates a new property bound to `signal` starting with `initialValue`. +public func propertyOf(initialValue: T)(producer: SignalProducer) -> PropertyOf { + return PropertyOf(SignalProperty(initialValue, producer)) +} + /// Wraps `sink` in a property bound to `signal`. Values sent on `signal` are `put` into /// the `sink` to update it. public func propertySink(sink: S)(signal: Signal) -> PropertyOf { + return signal |> put(sink) |> propertyOf(sink) +} + +/// Wraps `sink` in a property bound to `signal`. Values sent on `signal` are `put` into +/// the `sink` to update it. +public func propertySink(sink: S)(producer: SignalProducer) -> PropertyOf { + return producer |> put(sink) |> propertyOf(sink) +} + +private func put(sink: S)(signal: Signal) -> Signal { return signal |> scan(sink) { (var value, change) in value.put(change) return value - } - |> map { $0.0 } - |> propertyOf(sink) + } } diff --git a/Tests/PropertyTests.swift b/Tests/PropertyTests.swift index 63550fd218..21df867fed 100644 --- a/Tests/PropertyTests.swift +++ b/Tests/PropertyTests.swift @@ -40,7 +40,7 @@ final class PropertyTests: XCTestCase { XCTAssert(completed) } - func testSinkProperty() { + func testSignalPropertySink() { let (signal, sink) = Signal.pipe() let property = signal |> propertySink(Collector()) @@ -53,6 +53,20 @@ final class PropertyTests: XCTestCase { sendNext(sink, 3) XCTAssert(property.value.values == [1, 2, 3]) } + + func testProducerPropertySink() { + let (producer, sink) = SignalProducer.buffer() + + let property = producer |> propertySink(Collector(values: [])) + XCTAssert(property.value.values == []) + + sendNext(sink, 1) + XCTAssert(property.value.values == [1]) + + sendNext(sink, 2) + sendNext(sink, 3) + XCTAssert(property.value.values == [1, 2, 3]) + } } struct Collector: SinkType { From 8dadc70ad1ac69353a595140112f25284418bd6d Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Fri, 29 May 2015 16:33:53 -0700 Subject: [PATCH 0066/1028] Typos --- Source/Property.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Property.swift b/Source/Property.swift index a282155161..13492a38a3 100644 --- a/Source/Property.swift +++ b/Source/Property.swift @@ -40,7 +40,7 @@ public func propertyOf(initialValue: T)(signal: Signal) -> Proper return PropertyOf(SignalProperty(initialValue, signal)) } -/// Creates a new property bound to `signal` starting with `initialValue`. +/// Creates a new property bound to `producer` starting with `initialValue`. public func propertyOf(initialValue: T)(producer: SignalProducer) -> PropertyOf { return PropertyOf(SignalProperty(initialValue, producer)) } @@ -51,7 +51,7 @@ public func propertySink(sink: S)(signal: Signal put(sink) |> propertyOf(sink) } -/// Wraps `sink` in a property bound to `signal`. Values sent on `signal` are `put` into +/// Wraps `sink` in a property bound to `producer`. Values sent on `producer` are `put` into /// the `sink` to update it. public func propertySink(sink: S)(producer: SignalProducer) -> PropertyOf { return producer |> put(sink) |> propertyOf(sink) From d47f0bd5ec6961f95043ea3abec1066b7e8debe2 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Fri, 29 May 2015 16:34:09 -0700 Subject: [PATCH 0067/1028] Version 0.2.2 --- Source/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Info.plist b/Source/Info.plist index 804608f064..78f54ce197 100644 --- a/Source/Info.plist +++ b/Source/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.2.1 + 0.2.2 CFBundleSignature ???? CFBundleVersion From b390175da61c74a1a5c298f168acfa7bcff328fb Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 10 Jun 2015 13:12:48 -0700 Subject: [PATCH 0068/1028] Run the swift 2 upgrade tool --- Rex.xcodeproj/project.pbxproj | 1 + Source/Foundation/NSObject.swift | 2 +- Source/Foundation/NSUserDefaults.swift | 2 +- Source/Operators.swift | 10 +++++----- Source/Signal.swift | 2 +- Source/SignalProducer.swift | 6 +++--- 6 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index a4ffcf0776..afcc1a45e1 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -369,6 +369,7 @@ D8003E851AFEC3D400D7D3C5 /* Project object */ = { isa = PBXProject; attributes = { + LastSwiftUpdateCheck = 0700; LastUpgradeCheck = 0630; ORGANIZATIONNAME = "Neil Pankey"; TargetAttributes = { diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index 87c06e5224..07170f0be6 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -18,7 +18,7 @@ extension NSObject { return self.rac_valuesForKeyPath(keyPath, observer: nil) .toSignalProducer() |> map { $0 as! T } - |> catch { error in + |> `catch` { error in // Errors aren't possible, but the compiler doesn't know that. assertionFailure("Unexpected error from KVO signal: \(error)") return .empty diff --git a/Source/Foundation/NSUserDefaults.swift b/Source/Foundation/NSUserDefaults.swift index 18336be1d5..725e049e2f 100644 --- a/Source/Foundation/NSUserDefaults.swift +++ b/Source/Foundation/NSUserDefaults.swift @@ -16,7 +16,7 @@ extension NSUserDefaults { func rex_valueForKey(key: String) -> SignalProducer { let center = NSNotificationCenter.defaultCenter() - let changes = center.rac_notifications(name: NSUserDefaultsDidChangeNotification) + let changes = center.rac_notifications(NSUserDefaultsDidChangeNotification) |> map { notification in // The notification doesn't provide what changed so we have to look // it up every time diff --git a/Source/Operators.swift b/Source/Operators.swift index d1f86edccc..019ab32bcd 100644 --- a/Source/Operators.swift +++ b/Source/Operators.swift @@ -22,8 +22,8 @@ precedence 90 /// /// value --> sink /// -/// :param: value the next value. -/// :param: observer the observer that will handle the value. +/// - parameter value: the next value. +/// - parameter observer: the observer that will handle the value. public func -->(value:T, sink:SinkOf>) { sendNext(sink, value) } @@ -32,8 +32,8 @@ public func -->(value:T, sink:SinkOf>) { /// /// error --> sink /// -/// :param: error the error to send. -/// :param: observer the observer that will handle the value. +/// - parameter error: the error to send. +/// - parameter observer: the observer that will handle the value. public func -->(error:E, sink:SinkOf>) { sendError(sink, error) } @@ -44,7 +44,7 @@ prefix operator --| {} /// /// --|sink /// -/// :param: observer the sink to whcih the signal will be sent. +/// - parameter observer: the sink to whcih the signal will be sent. public prefix func --|(sink:SinkOf>) { return sendCompleted(sink) } diff --git a/Source/Signal.swift b/Source/Signal.swift index 5ca1f704af..72f082a3d3 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -32,7 +32,7 @@ public func ignoreError(signal: Signal) -> Signal { } /// Returns a signal that drops `Error` sending `replacement` terminal event instead. -public func ignoreError(#replacement: Event)(signal: Signal) -> Signal { +public func ignoreError(replacement replacement: Event)(signal: Signal) -> Signal { precondition(replacement.isTerminating) return Signal { observer in diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index 50bffabbc3..077990962c 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -52,7 +52,7 @@ public func groupBy(grouping: T -> K)(producer: SignalProduce /// Returns a signal that prints the signal events public func print(signal: SignalProducer) -> SignalProducer { - return signal |> on(event: println) + return signal |> on(event: print) } /// Returns a signal that prints the signal `Next` events @@ -61,7 +61,7 @@ public func printNext(signal: SignalProducer) -> SignalProducer on(event: { event in switch event { case let .Next(value): - println(event) + print(event) default: break @@ -75,7 +75,7 @@ public func printError(signal: SignalProducer) -> SignalProducer on(event: { event in switch event { case let .Error(value): - println(event) + print(event) default: break From 9849812ba26f9537dd3c7e3f1bbe4001e72c7626 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 10 Jun 2015 13:14:55 -0700 Subject: [PATCH 0069/1028] carthage update swift2 --- Cartfile | 2 +- Cartfile.resolved | 5 ++--- Carthage/Checkouts/ReactiveCocoa | 2 +- Carthage/Checkouts/Result | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Cartfile b/Cartfile index f0f99fad61..0a6c99df44 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveCocoa" ~> 3.0 +github "ReactiveCocoa/ReactiveCocoa" "swift2" diff --git a/Cartfile.resolved b/Cartfile.resolved index b84d80e2f4..6b0f471e60 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,3 +1,2 @@ -github "robrix/Box" "1.2.2" -github "antitypical/Result" "0.4.3" -github "ReactiveCocoa/ReactiveCocoa" "v3.0-beta.6" +github "antitypical/Result" "0.5" +github "ReactiveCocoa/ReactiveCocoa" "84b7fdd08cd1e49d09876a491695db261e5599b7" diff --git a/Carthage/Checkouts/ReactiveCocoa b/Carthage/Checkouts/ReactiveCocoa index b15798ae4f..84b7fdd08c 160000 --- a/Carthage/Checkouts/ReactiveCocoa +++ b/Carthage/Checkouts/ReactiveCocoa @@ -1 +1 @@ -Subproject commit b15798ae4f6386b8461b3b06f49908f2009a28ce +Subproject commit 84b7fdd08cd1e49d09876a491695db261e5599b7 diff --git a/Carthage/Checkouts/Result b/Carthage/Checkouts/Result index f9045c2a1f..665c177914 160000 --- a/Carthage/Checkouts/Result +++ b/Carthage/Checkouts/Result @@ -1 +1 @@ -Subproject commit f9045c2a1fee1af321e29eea7c633be5a6a42532 +Subproject commit 665c1779141d584d9e2f1d5570caaee9e905fd0e From b2c3a2cc72a4ffbcf56d978e04f88b74570ad493 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 10 Jun 2015 13:18:24 -0700 Subject: [PATCH 0070/1028] Bye-bye Box --- .gitmodules | 3 --- Carthage/Checkouts/Box | 1 - Rex.xcodeproj/project.pbxproj | 16 ---------------- 3 files changed, 20 deletions(-) delete mode 160000 Carthage/Checkouts/Box diff --git a/.gitmodules b/.gitmodules index 973c600a8d..90b7f65581 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,6 +4,3 @@ [submodule "Carthage/Checkouts/Result"] path = Carthage/Checkouts/Result url = https://github.com/antitypical/Result.git -[submodule "Carthage/Checkouts/Box"] - path = Carthage/Checkouts/Box - url = https://github.com/robrix/Box.git diff --git a/Carthage/Checkouts/Box b/Carthage/Checkouts/Box deleted file mode 160000 index bbe4e612a0..0000000000 --- a/Carthage/Checkouts/Box +++ /dev/null @@ -1 +0,0 @@ -Subproject commit bbe4e612a03ffe0bbb0e2e476c2be4534b6777a5 diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index afcc1a45e1..17028a89e9 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -10,29 +10,23 @@ 7DE995CB1B03A56800CA9420 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE995CA1B03A56800CA9420 /* Operators.swift */; }; 7DE995CC1B03A56800CA9420 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE995CA1B03A56800CA9420 /* Operators.swift */; }; D8003E941AFEC3D400D7D3C5 /* Rex.h in Headers */ = {isa = PBXBuildFile; fileRef = D8003E931AFEC3D400D7D3C5 /* Rex.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D8003EB31AFEC6B000D7D3C5 /* Box.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003EAC1AFEC68A00D7D3C5 /* Box.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 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, ); }; }; 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 */; }; D8003EC31AFED30F00D7D3C5 /* SignalProducerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EC01AFED30100D7D3C5 /* SignalProducerTests.swift */; }; - D8003EC41AFED36F00D7D3C5 /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAC1AFEC68A00D7D3C5 /* Box.framework */; }; D8003EC51AFED36F00D7D3C5 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; }; D8003EC61AFED36F00D7D3C5 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAE1AFEC68A00D7D3C5 /* Result.framework */; }; 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 */; }; - D83457311AFEE4930070616A /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAC1AFEC68A00D7D3C5 /* Box.framework */; }; D83457321AFEE4930070616A /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; }; D83457331AFEE4930070616A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAE1AFEC68A00D7D3C5 /* Result.framework */; }; - D83457341AFEE4B20070616A /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EC81AFEE3ED00D7D3C5 /* Box.framework */; }; D83457351AFEE4B20070616A /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */; }; D83457361AFEE4B20070616A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */; }; - D83457381AFEE4BE0070616A /* Box.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003EC81AFEE3ED00D7D3C5 /* Box.framework */; }; D83457391AFEE4BE0070616A /* ReactiveCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */; }; D834573A1AFEE4BE0070616A /* Result.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */; }; - D834573B1AFEE57E0070616A /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EC81AFEE3ED00D7D3C5 /* Box.framework */; }; D834573C1AFEE57E0070616A /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */; }; D834573D1AFEE57E0070616A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */; }; D83457411AFEE6050070616A /* SignalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */; }; @@ -74,7 +68,6 @@ dstPath = ""; dstSubfolderSpec = 16; files = ( - D8003EB31AFEC6B000D7D3C5 /* Box.framework in CopyFiles */, D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in CopyFiles */, D8003EB51AFEC6B000D7D3C5 /* Result.framework in CopyFiles */, ); @@ -86,7 +79,6 @@ dstPath = ""; dstSubfolderSpec = 16; files = ( - D83457381AFEE4BE0070616A /* Box.framework in CopyFiles */, D83457391AFEE4BE0070616A /* ReactiveCocoa.framework in CopyFiles */, D834573A1AFEE4BE0070616A /* Result.framework in CopyFiles */, ); @@ -99,14 +91,12 @@ D8003E921AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D8003E931AFEC3D400D7D3C5 /* Rex.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Rex.h; sourceTree = ""; }; D8003E9F1AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - D8003EAC1AFEC68A00D7D3C5 /* Box.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Box.framework; sourceTree = ""; }; D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ReactiveCocoa.framework; sourceTree = ""; }; D8003EAE1AFEC68A00D7D3C5 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = ""; }; D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalProducer.swift; sourceTree = ""; }; D8003EBC1AFED01000D7D3C5 /* Signal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Signal.swift; sourceTree = ""; }; D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalTests.swift; sourceTree = ""; }; D8003EC01AFED30100D7D3C5 /* SignalProducerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalProducerTests.swift; sourceTree = ""; }; - D8003EC81AFEE3ED00D7D3C5 /* Box.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Box.framework; sourceTree = ""; }; 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; }; @@ -126,7 +116,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D83457311AFEE4930070616A /* Box.framework in Frameworks */, D83457321AFEE4930070616A /* ReactiveCocoa.framework in Frameworks */, D83457331AFEE4930070616A /* Result.framework in Frameworks */, ); @@ -136,7 +125,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D8003EC41AFED36F00D7D3C5 /* Box.framework in Frameworks */, D8003EC51AFED36F00D7D3C5 /* ReactiveCocoa.framework in Frameworks */, D8003EC61AFED36F00D7D3C5 /* Result.framework in Frameworks */, ); @@ -146,7 +134,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D83457341AFEE4B20070616A /* Box.framework in Frameworks */, D83457351AFEE4B20070616A /* ReactiveCocoa.framework in Frameworks */, D83457361AFEE4B20070616A /* Result.framework in Frameworks */, ); @@ -156,7 +143,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D834573B1AFEE57E0070616A /* Box.framework in Frameworks */, D834573C1AFEE57E0070616A /* ReactiveCocoa.framework in Frameworks */, D834573D1AFEE57E0070616A /* Result.framework in Frameworks */, ); @@ -231,7 +217,6 @@ children = ( D86E77A91AFEE646004BF23D /* Rex.framework */, D86E77AA1AFEE646004BF23D /* RexTests-Mac.xctest */, - D8003EAC1AFEC68A00D7D3C5 /* Box.framework */, D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */, D8003EAE1AFEC68A00D7D3C5 /* Result.framework */, ); @@ -243,7 +228,6 @@ children = ( D86E77AB1AFEE646004BF23D /* Rex.framework */, D86E77AC1AFEE646004BF23D /* RexTests-iOS.xctest */, - D8003EC81AFEE3ED00D7D3C5 /* Box.framework */, D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */, D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */, ); From 77a454f73db4026cd08bc69f978259e2ab05f094 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 10 Jun 2015 13:26:09 -0700 Subject: [PATCH 0071/1028] Builds for Swift 2 with tests passing --- Source/Foundation/NSData.swift | 10 +++++----- Source/Foundation/NSObject.swift | 2 +- Source/Operators.swift | 6 +++--- Source/Signal.swift | 6 +++--- Tests/SignalTests.swift | 20 ++++++++++++++------ 5 files changed, 26 insertions(+), 18 deletions(-) diff --git a/Source/Foundation/NSData.swift b/Source/Foundation/NSData.swift index b93854c52d..e51d9e4e46 100644 --- a/Source/Foundation/NSData.swift +++ b/Source/Foundation/NSData.swift @@ -11,14 +11,14 @@ import ReactiveCocoa extension NSData { /// Read the data at the URL. /// Sends the data or the error. - class func rex_dataWithContentsOfURL(url: NSURL, options: NSDataReadingOptions = NSDataReadingOptions.allZeros) -> SignalProducer { + class func rex_dataWithContentsOfURL(url: NSURL, options: NSDataReadingOptions = NSDataReadingOptions()) -> SignalProducer { return SignalProducer { observer, disposable in - var error: NSError? - if let data = NSData(contentsOfURL: url, options: options, error: &error) { + do { + let data = try NSData(contentsOfURL: url, options: options) sendNext(observer, data) sendCompleted(observer) - } else { - sendError(observer, error ?? NSError()) + } catch { + sendError(observer, error as NSError) } } } diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index 07170f0be6..6f96f70a07 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -18,7 +18,7 @@ extension NSObject { return self.rac_valuesForKeyPath(keyPath, observer: nil) .toSignalProducer() |> map { $0 as! T } - |> `catch` { error in + |> flatMapError { error in // Errors aren't possible, but the compiler doesn't know that. assertionFailure("Unexpected error from KVO signal: \(error)") return .empty diff --git a/Source/Operators.swift b/Source/Operators.swift index 019ab32bcd..9bfe300e9a 100644 --- a/Source/Operators.swift +++ b/Source/Operators.swift @@ -24,7 +24,7 @@ precedence 90 /// /// - parameter value: the next value. /// - parameter observer: the observer that will handle the value. -public func -->(value:T, sink:SinkOf>) { +public func -->(value:T, sink:SinkOf>) { sendNext(sink, value) } @@ -34,7 +34,7 @@ public func -->(value:T, sink:SinkOf>) { /// /// - parameter error: the error to send. /// - parameter observer: the observer that will handle the value. -public func -->(error:E, sink:SinkOf>) { +public func -->(error:E, sink:SinkOf>) { sendError(sink, error) } @@ -45,6 +45,6 @@ prefix operator --| {} /// --|sink /// /// - parameter observer: the sink to whcih the signal will be sent. -public prefix func --|(sink:SinkOf>) { +public prefix func --|(sink:SinkOf>) { return sendCompleted(sink) } diff --git a/Source/Signal.swift b/Source/Signal.swift index 72f082a3d3..3b085e9772 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -39,7 +39,7 @@ public func ignoreError(replacement replacement: Event)(signal return signal.observe(Signal.Observer { event in switch event { case let .Next(value): - sendNext(observer, value.value) + sendNext(observer, value) case .Error: observer.put(replacement) case .Completed: @@ -82,9 +82,9 @@ public func uncollect(signal: Signal) -> Signal filterMap { - return $0 % 2 == 0 ? toString($0) : nil + return $0 % 2 == 0 ? String($0) : nil } |> observe(next: values.append) @@ -36,7 +36,7 @@ final class SignalTests: XCTestCase { } func testIgnoreErrorCompletion() { - let (signal, sink) = Signal.pipe() + let (signal, sink) = Signal.pipe() var completed = false signal @@ -48,12 +48,12 @@ final class SignalTests: XCTestCase { 1 --> sink XCTAssertFalse(completed) - NSError() --> sink + .Default --> sink XCTAssertTrue(completed) } func testIgnoreErrorInterruption() { - let (signal, sink) = Signal.pipe() + let (signal, sink) = Signal.pipe() var interrupted = false signal @@ -65,7 +65,7 @@ final class SignalTests: XCTestCase { 1 --> sink XCTAssertFalse(interrupted) - NSError() --> sink + .Default --> sink XCTAssertTrue(interrupted) } @@ -134,4 +134,12 @@ final class SignalTests: XCTestCase { [2, 3] --> sink XCTAssert(values == [1, 2, 3]) } -} \ No newline at end of file +} + +enum TestError: Swift.ErrorType, ReactiveCocoa.ErrorType { + case Default + + var nsError: NSError { + return self as NSError + } +} From 7d2d1851a975dd73fb793047312bcd4aef40725d Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 10 Jun 2015 13:28:19 -0700 Subject: [PATCH 0072/1028] Xcode recommended project settings update --- Rex.xcodeproj/project.pbxproj | 11 ++++++++++- Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme | 5 ++++- Rex.xcodeproj/xcshareddata/xcschemes/Rex-iOS.xcscheme | 5 ++++- Source/Info.plist | 2 +- Tests/Info.plist | 2 +- 5 files changed, 20 insertions(+), 5 deletions(-) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 17028a89e9..40ee5f9d7e 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -354,7 +354,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0700; - LastUpgradeCheck = 0630; + LastUpgradeCheck = 0700; ORGANIZATIONNAME = "Neil Pankey"; TargetAttributes = { D8003E8D1AFEC3D400D7D3C5 = { @@ -510,6 +510,7 @@ CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -593,6 +594,7 @@ 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"; @@ -619,6 +621,7 @@ 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; }; @@ -639,6 +642,7 @@ ); INFOPLIST_FILE = Tests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "me.neilpa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -654,6 +658,7 @@ ); INFOPLIST_FILE = Tests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "me.neilpa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -680,6 +685,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "me.neilpa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -704,6 +710,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "me.neilpa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -729,6 +736,7 @@ INFOPLIST_FILE = Tests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.3; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "me.neilpa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; }; @@ -746,6 +754,7 @@ INFOPLIST_FILE = Tests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.3; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "me.neilpa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme index 2e13f8698b..a89ade2fe6 100644 --- a/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme +++ b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme @@ -1,6 +1,6 @@ + + + + CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - me.neilpa.$(PRODUCT_NAME:rfc1034identifier) + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/Tests/Info.plist b/Tests/Info.plist index 14a51c6942..ba72822e87 100644 --- a/Tests/Info.plist +++ b/Tests/Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - me.neilpa.$(PRODUCT_NAME:rfc1034identifier) + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName From 67a7de4df1ba9916c09ca840cffd6c0a083f5423 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 10 Jun 2015 13:35:16 -0700 Subject: [PATCH 0073/1028] Fix some new build warnings --- Source/SignalProducer.swift | 4 ++-- Tests/PropertyTests.swift | 4 ++-- Tests/SignalProducerTests.swift | 8 +++++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index 077990962c..40c4707423 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -60,7 +60,7 @@ public func printNext(signal: SignalProducer) -> SignalProducer on(event: { event in switch event { - case let .Next(value): + case .Next: print(event) default: @@ -74,7 +74,7 @@ public func printError(signal: SignalProducer) -> SignalProducer on(event: { event in switch event { - case let .Error(value): + case .Error: print(event) default: diff --git a/Tests/PropertyTests.swift b/Tests/PropertyTests.swift index 21df867fed..d7b5bc31ba 100644 --- a/Tests/PropertyTests.swift +++ b/Tests/PropertyTests.swift @@ -14,7 +14,7 @@ final class PropertyTests: XCTestCase { func testSignalPropertyValues() { let (signal, sink) = Signal.pipe() - var property = SignalProperty(0, signal) + let property = SignalProperty(0, signal) var latest = -1 property.producer.start(next: { @@ -28,7 +28,7 @@ final class PropertyTests: XCTestCase { } func testSignalPropertyLifetime() { - let (signal, sink) = Signal.pipe() + let (signal, _) = Signal.pipe() var property: SignalProperty? = SignalProperty(0, signal) var completed = false diff --git a/Tests/SignalProducerTests.swift b/Tests/SignalProducerTests.swift index 237fbe579b..93a411eb33 100644 --- a/Tests/SignalProducerTests.swift +++ b/Tests/SignalProducerTests.swift @@ -28,9 +28,10 @@ final class SignalProducerTests: XCTestCase { } else { group |> start(next: odds.append) } - },completed: { completed = true }, - interrupted: { - interrupted = true + },completed: { + completed = true + }, interrupted: { + interrupted = true }) 1 --> sink @@ -49,6 +50,7 @@ final class SignalProducerTests: XCTestCase { 1 --> sink XCTAssert(interrupted) + XCTAssertFalse(completed) } func testCompletionOperator() { From 628fd2df09145e43a448018f9eab295788f4e8e2 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Mon, 15 Jun 2015 15:19:35 -0700 Subject: [PATCH 0074/1028] Add an optionalize operator --- Source/Signal.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/Signal.swift b/Source/Signal.swift index 5ca1f704af..d712368778 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -51,6 +51,10 @@ public func ignoreError(#replacement: Event)(signal: Signal(signal: Signal) -> Signal { + return signal |> map { Optional($0) } +} /// Forwards events from `signal` until `interval`. Then if signal isn't completed yet, /// terminates with `event` on `scheduler`. From 02ad7d5c3c8421b9a027804379562bf2147d993f Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Fri, 19 Jun 2015 13:18:13 -0700 Subject: [PATCH 0075/1028] Associated object helpers --- Rex.xcodeproj/project.pbxproj | 6 +++ Source/Foundation/Association.swift | 61 +++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 Source/Foundation/Association.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index a4ffcf0776..e687ab8166 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -36,6 +36,8 @@ D834573C1AFEE57E0070616A /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */; }; D834573D1AFEE57E0070616A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */; }; D83457411AFEE6050070616A /* SignalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */; }; + D86FFBD11B34AD6F001A89B3 /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD01B34AD6F001A89B3 /* Association.swift */; }; + D86FFBD21B34AD7A001A89B3 /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD01B34AD6F001A89B3 /* Association.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 */; }; @@ -113,6 +115,7 @@ D86E77AA1AFEE646004BF23D /* RexTests-Mac.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RexTests-Mac.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; path = Association.swift; sourceTree = ""; }; D8F0973A1B17F2F7002E15BA /* NSData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSData.swift; sourceTree = ""; }; D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSUserDefaults.swift; sourceTree = ""; }; D8F097431B17F3C8002E15BA /* NSObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSObject.swift; sourceTree = ""; }; @@ -253,6 +256,7 @@ D8F097391B17F2BF002E15BA /* Foundation */ = { isa = PBXGroup; children = ( + D86FFBD01B34AD6F001A89B3 /* Association.swift */, D8F0973A1B17F2F7002E15BA /* NSData.swift */, D8F097431B17F3C8002E15BA /* NSObject.swift */, D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */, @@ -443,6 +447,7 @@ buildActionMask = 2147483647; files = ( D8F0974D1B190ECE002E15BA /* Property.swift in Sources */, + D86FFBD11B34AD6F001A89B3 /* Association.swift in Sources */, D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */, 7DE995CB1B03A56800CA9420 /* Operators.swift in Sources */, D8003EBD1AFED01000D7D3C5 /* Signal.swift in Sources */, @@ -468,6 +473,7 @@ buildActionMask = 2147483647; files = ( D8F0974E1B190ECE002E15BA /* Property.swift in Sources */, + D86FFBD21B34AD7A001A89B3 /* Association.swift in Sources */, D8F0973E1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, D834572D1AFEE45B0070616A /* Signal.swift in Sources */, 7DE995CC1B03A56800CA9420 /* Operators.swift in Sources */, diff --git a/Source/Foundation/Association.swift b/Source/Foundation/Association.swift new file mode 100644 index 0000000000..2707616b2b --- /dev/null +++ b/Source/Foundation/Association.swift @@ -0,0 +1,61 @@ +// +// Association.swift +// Rex +// +// Created by Neil Pankey on 6/19/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa + +/// Attaches a `MutableProperty` value to the `host` object using KVC to get the initial +/// value and write subsequent updates from the property's producer. Note that `keyPath` +/// is a `StaticString` because it's pointer value is used as key value when associating +/// the property. +/// +/// This can be used as an alternative to `DynamicProperty` for creating strongly typed +/// bindings on Cocoa objects. +public func associatedProperty(host: AnyObject, keyPath: StaticString) -> MutableProperty { + let initial = { host.valueForKeyPath(keyPath.stringValue) as? String ?? "" } + let setter: String -> () = { host.setValue($0, forKeyPath: keyPath.stringValue) } + return associatedProperty(host, keyPath.utf8Start, initial, setter) +} + +/// Attaches a `MutableProperty` value to the `host` object using KVC to get the initial +/// value and write subsequent updates from the property's producer. Note that `keyPath` +/// is a `StaticString` because it's pointer value is used as key value when associating +/// the property. +/// +/// This can be used as an alternative to `DynamicProperty` for creating strongly typed +/// bindings on Cocoa objects. +public func associatedProperty(host: AnyObject, keyPath: StaticString, placeholder: () -> T) -> MutableProperty { + let initial = { host.valueForKeyPath(keyPath.stringValue) as? T ?? placeholder() } + let setter: T -> () = { host.setValue($0, forKeyPath: keyPath.stringValue) } + return associatedProperty(host, keyPath.utf8Start, initial, setter) +} + +/// Attaches a `MutableProperty` value to the `host` object under `key`. The property is +/// initialized with the result of `initial`. Changes on the property's producer are +/// monitored and written to `setter`. +/// +/// This can be used as an alternative to `DynamicProperty` for creating strongly typed +/// bindings on Cocoa objects. +public func associatedProperty(host: AnyObject, key: UnsafePointer<()>, initial: () -> T, setter: T -> ()) -> MutableProperty { + return associatedObject(host, key) { + let property = MutableProperty(initial()) + property.producer.start(next: setter) + return property + } +} + +/// On first use attaches the object returned from `initial` to the `host` object +/// using `key` via `objc_setAssociatedObject`. On subsequent usage, returns said +/// object via `objc_getAssociatedObject`. +public func associatedObject(host: AnyObject, key: UnsafePointer<()>, initial: () -> T) -> T { + var value = objc_getAssociatedObject(host, key) as? T + if value == nil { + value = initial() + objc_setAssociatedObject(host, key, value, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN)) + } + return value! +} From 62137f865c10c18f2e015804a975ac5bfa0ccd8b Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Fri, 19 Jun 2015 13:24:46 -0700 Subject: [PATCH 0076/1028] UIControl.rex_enabled --- Rex.xcodeproj/project.pbxproj | 12 ++++++++++++ Source/UIKit/UIControl.swift | 16 ++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 Source/UIKit/UIControl.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index e687ab8166..14aebd0c9c 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -38,6 +38,7 @@ D83457411AFEE6050070616A /* SignalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */; }; D86FFBD11B34AD6F001A89B3 /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD01B34AD6F001A89B3 /* Association.swift */; }; D86FFBD21B34AD7A001A89B3 /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD01B34AD6F001A89B3 /* Association.swift */; }; + D86FFBD61B34B116001A89B3 /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD41B34B0FE001A89B3 /* UIControl.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 */; }; @@ -116,6 +117,7 @@ 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; path = Association.swift; sourceTree = ""; }; + D86FFBD41B34B0FE001A89B3 /* UIControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIControl.swift; sourceTree = ""; }; D8F0973A1B17F2F7002E15BA /* NSData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSData.swift; sourceTree = ""; }; D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSUserDefaults.swift; sourceTree = ""; }; D8F097431B17F3C8002E15BA /* NSObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSObject.swift; sourceTree = ""; }; @@ -185,6 +187,7 @@ D8003EBC1AFED01000D7D3C5 /* Signal.swift */, D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */, D8F097391B17F2BF002E15BA /* Foundation */, + D86FFBD31B34B0E2001A89B3 /* UIKit */, D8003E911AFEC3D400D7D3C5 /* Supporting Files */, ); path = Source; @@ -253,6 +256,14 @@ path = iOS; sourceTree = ""; }; + D86FFBD31B34B0E2001A89B3 /* UIKit */ = { + isa = PBXGroup; + children = ( + D86FFBD41B34B0FE001A89B3 /* UIControl.swift */, + ); + path = UIKit; + sourceTree = ""; + }; D8F097391B17F2BF002E15BA /* Foundation */ = { isa = PBXGroup; children = ( @@ -479,6 +490,7 @@ 7DE995CC1B03A56800CA9420 /* Operators.swift in Sources */, D8F097451B17F3C8002E15BA /* NSObject.swift in Sources */, D834572E1AFEE45B0070616A /* SignalProducer.swift in Sources */, + D86FFBD61B34B116001A89B3 /* UIControl.swift in Sources */, D8F0973F1B17F31E002E15BA /* NSData.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Source/UIKit/UIControl.swift b/Source/UIKit/UIControl.swift new file mode 100644 index 0000000000..519325a356 --- /dev/null +++ b/Source/UIKit/UIControl.swift @@ -0,0 +1,16 @@ +// +// UIView.swift +// Rex +// +// Created by Neil Pankey on 6/19/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import UIKit + +extension UIControl { + /// Wraps a control's `enabled` state in a bindable property. + public var rex_enabled: MutableProperty { + return associatedProperty(self, &Keys.enabled, { self.enabled }, { self.enabled = $0 }) + } +} \ No newline at end of file From 50a2d722abb37c75429f755dbc463f5a6a2dd516 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Fri, 19 Jun 2015 13:32:02 -0700 Subject: [PATCH 0077/1028] UILabel.rex_text --- Rex.xcodeproj/project.pbxproj | 4 ++++ Source/UIKit/UILabel.swift | 14 ++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 Source/UIKit/UILabel.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 14aebd0c9c..932706c297 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -39,6 +39,7 @@ D86FFBD11B34AD6F001A89B3 /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD01B34AD6F001A89B3 /* Association.swift */; }; D86FFBD21B34AD7A001A89B3 /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD01B34AD6F001A89B3 /* Association.swift */; }; D86FFBD61B34B116001A89B3 /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD41B34B0FE001A89B3 /* UIControl.swift */; }; + D86FFBD81B34B242001A89B3 /* UILabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD71B34B242001A89B3 /* UILabel.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 */; }; @@ -118,6 +119,7 @@ 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; path = Association.swift; sourceTree = ""; }; D86FFBD41B34B0FE001A89B3 /* UIControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIControl.swift; sourceTree = ""; }; + D86FFBD71B34B242001A89B3 /* UILabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabel.swift; sourceTree = ""; }; D8F0973A1B17F2F7002E15BA /* NSData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSData.swift; sourceTree = ""; }; D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSUserDefaults.swift; sourceTree = ""; }; D8F097431B17F3C8002E15BA /* NSObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSObject.swift; sourceTree = ""; }; @@ -260,6 +262,7 @@ isa = PBXGroup; children = ( D86FFBD41B34B0FE001A89B3 /* UIControl.swift */, + D86FFBD71B34B242001A89B3 /* UILabel.swift */, ); path = UIKit; sourceTree = ""; @@ -483,6 +486,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + D86FFBD81B34B242001A89B3 /* UILabel.swift in Sources */, D8F0974E1B190ECE002E15BA /* Property.swift in Sources */, D86FFBD21B34AD7A001A89B3 /* Association.swift in Sources */, D8F0973E1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, diff --git a/Source/UIKit/UILabel.swift b/Source/UIKit/UILabel.swift new file mode 100644 index 0000000000..b1277ecda2 --- /dev/null +++ b/Source/UIKit/UILabel.swift @@ -0,0 +1,14 @@ +// +// UILabel.swift +// Rex +// +// Created by Neil Pankey on 6/19/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +extension UILabel { + /// Wraps a label's `enabled` state in a bindable property. + public var rex_text: MutableProperty { + return associatedProperty(self, "text") + } +} \ No newline at end of file From e361c6389cd73cb6f12ce1252ca7282d2ebd8711 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Fri, 19 Jun 2015 13:39:29 -0700 Subject: [PATCH 0078/1028] Stuff actually builds --- Source/UIKit/UIControl.swift | 5 +++++ Source/UIKit/UILabel.swift | 3 +++ 2 files changed, 8 insertions(+) diff --git a/Source/UIKit/UIControl.swift b/Source/UIKit/UIControl.swift index 519325a356..bc0e6eb619 100644 --- a/Source/UIKit/UIControl.swift +++ b/Source/UIKit/UIControl.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveCocoa import UIKit extension UIControl { @@ -13,4 +14,8 @@ extension UIControl { public var rex_enabled: MutableProperty { return associatedProperty(self, &Keys.enabled, { self.enabled }, { self.enabled = $0 }) } +} + +private struct Keys { + static var enabled: UInt8 = 0 } \ No newline at end of file diff --git a/Source/UIKit/UILabel.swift b/Source/UIKit/UILabel.swift index b1277ecda2..5d82a23ad9 100644 --- a/Source/UIKit/UILabel.swift +++ b/Source/UIKit/UILabel.swift @@ -6,6 +6,9 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveCocoa +import UIKit + extension UILabel { /// Wraps a label's `enabled` state in a bindable property. public var rex_text: MutableProperty { From 677c89d69a42ef7bb5fdf6e12668e3fe8689a1ba Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Fri, 19 Jun 2015 13:47:51 -0700 Subject: [PATCH 0079/1028] UIButton.rex_pressed and CocoaAction extensions --- Rex.xcodeproj/project.pbxproj | 10 ++++++++++ Source/Action.swift | 35 +++++++++++++++++++++++++++++++++++ Source/UIKit/UIButton.swift | 31 +++++++++++++++++++++++++++++++ Source/UIKit/UIControl.swift | 6 ++---- 4 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 Source/Action.swift create mode 100644 Source/UIKit/UIButton.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 932706c297..d31c919bef 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -40,6 +40,9 @@ D86FFBD21B34AD7A001A89B3 /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD01B34AD6F001A89B3 /* Association.swift */; }; D86FFBD61B34B116001A89B3 /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD41B34B0FE001A89B3 /* UIControl.swift */; }; D86FFBD81B34B242001A89B3 /* UILabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD71B34B242001A89B3 /* UILabel.swift */; }; + D86FFBDA1B34B3F0001A89B3 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD91B34B3F0001A89B3 /* Action.swift */; }; + D86FFBDB1B34B3F0001A89B3 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD91B34B3F0001A89B3 /* Action.swift */; }; + D86FFBDD1B34B691001A89B3 /* UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBDC1B34B691001A89B3 /* UIButton.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 */; }; @@ -120,6 +123,8 @@ D86FFBD01B34AD6F001A89B3 /* Association.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Association.swift; sourceTree = ""; }; D86FFBD41B34B0FE001A89B3 /* UIControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIControl.swift; sourceTree = ""; }; D86FFBD71B34B242001A89B3 /* UILabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabel.swift; sourceTree = ""; }; + D86FFBD91B34B3F0001A89B3 /* Action.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Action.swift; sourceTree = ""; }; + D86FFBDC1B34B691001A89B3 /* UIButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIButton.swift; sourceTree = ""; }; D8F0973A1B17F2F7002E15BA /* NSData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSData.swift; sourceTree = ""; }; D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSUserDefaults.swift; sourceTree = ""; }; D8F097431B17F3C8002E15BA /* NSObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSObject.swift; sourceTree = ""; }; @@ -184,6 +189,7 @@ D8003E901AFEC3D400D7D3C5 /* Source */ = { isa = PBXGroup; children = ( + D86FFBD91B34B3F0001A89B3 /* Action.swift */, 7DE995CA1B03A56800CA9420 /* Operators.swift */, D8F0974C1B190ECE002E15BA /* Property.swift */, D8003EBC1AFED01000D7D3C5 /* Signal.swift */, @@ -261,6 +267,7 @@ D86FFBD31B34B0E2001A89B3 /* UIKit */ = { isa = PBXGroup; children = ( + D86FFBDC1B34B691001A89B3 /* UIButton.swift */, D86FFBD41B34B0FE001A89B3 /* UIControl.swift */, D86FFBD71B34B242001A89B3 /* UILabel.swift */, ); @@ -461,6 +468,7 @@ buildActionMask = 2147483647; files = ( D8F0974D1B190ECE002E15BA /* Property.swift in Sources */, + D86FFBDA1B34B3F0001A89B3 /* Action.swift in Sources */, D86FFBD11B34AD6F001A89B3 /* Association.swift in Sources */, D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */, 7DE995CB1B03A56800CA9420 /* Operators.swift in Sources */, @@ -487,6 +495,7 @@ buildActionMask = 2147483647; files = ( D86FFBD81B34B242001A89B3 /* UILabel.swift in Sources */, + D86FFBDB1B34B3F0001A89B3 /* Action.swift in Sources */, D8F0974E1B190ECE002E15BA /* Property.swift in Sources */, D86FFBD21B34AD7A001A89B3 /* Association.swift in Sources */, D8F0973E1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, @@ -496,6 +505,7 @@ D834572E1AFEE45B0070616A /* SignalProducer.swift in Sources */, D86FFBD61B34B116001A89B3 /* UIControl.swift in Sources */, D8F0973F1B17F31E002E15BA /* NSData.swift in Sources */, + D86FFBDD1B34B691001A89B3 /* UIButton.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Source/Action.swift b/Source/Action.swift new file mode 100644 index 0000000000..972ca8a783 --- /dev/null +++ b/Source/Action.swift @@ -0,0 +1,35 @@ +// +// Action.swift +// Rex +// +// Created by Neil Pankey on 6/19/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa + +extension Action { + /// Creates an always disabled action. + static var rex_disabled: Action { + return Action(enabledIf: ConstantProperty(false)) { _ in .empty } + } +} + +extension CocoaAction { + /// Creates an always disabled action that can be used as a default for + /// things like `rac_pressed`. + public static var rex_disabled: CocoaAction { + return CocoaAction(Action.rex_disabled, input: nil) + } + + /// Creates a producer for the `enabled` state of a CocoaAction. + public var rex_enabledProducer: SignalProducer { + return rex_producerForKeyPath("enabled") + } + + /// Creates a producer for the `executing` state of a CocoaAction. + public var rex_executingProducer: SignalProducer { + return rex_producerForKeyPath("executing") + } +} + diff --git a/Source/UIKit/UIButton.swift b/Source/UIKit/UIButton.swift new file mode 100644 index 0000000000..8dd3245423 --- /dev/null +++ b/Source/UIKit/UIButton.swift @@ -0,0 +1,31 @@ +// +// UIButton.swift +// Rex +// +// Created by Neil Pankey on 6/19/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa +import UIKit + +extension UIButton { + public var rac_pressed: MutableProperty { + return associatedObject(self, &pressed, { _ in + let initial = CocoaAction.rex_disabled + let property = MutableProperty(initial) + + property.producer + |> combinePrevious(initial) + |> start { previous, next in + self.removeTarget(previous, action: CocoaAction.selector, forControlEvents: .TouchUpInside) + self.addTarget(next, action: CocoaAction.selector, forControlEvents: .TouchUpInside) + } + + self.rex_enabled <~ property.producer |> flatMap(.Latest) { $0.rex_enabledProducer } + return property + }) + } +} + +private var pressed: UInt8 = 0 \ No newline at end of file diff --git a/Source/UIKit/UIControl.swift b/Source/UIKit/UIControl.swift index bc0e6eb619..77507a9677 100644 --- a/Source/UIKit/UIControl.swift +++ b/Source/UIKit/UIControl.swift @@ -12,10 +12,8 @@ import UIKit extension UIControl { /// Wraps a control's `enabled` state in a bindable property. public var rex_enabled: MutableProperty { - return associatedProperty(self, &Keys.enabled, { self.enabled }, { self.enabled = $0 }) + return associatedProperty(self, &enabled, { self.enabled }, { self.enabled = $0 }) } } -private struct Keys { - static var enabled: UInt8 = 0 -} \ No newline at end of file +private var enabled: UInt8 = 0 From 26b9fc9e4831ee5051b015ee1d30fba5dbce5834 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Fri, 19 Jun 2015 13:52:59 -0700 Subject: [PATCH 0080/1028] Document rex_pressed --- Source/UIKit/UIButton.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/UIKit/UIButton.swift b/Source/UIKit/UIButton.swift index 8dd3245423..c7f1bf5ffa 100644 --- a/Source/UIKit/UIButton.swift +++ b/Source/UIKit/UIButton.swift @@ -10,6 +10,10 @@ import ReactiveCocoa import UIKit extension UIButton { + /// Exposes a property that binds an action to button presses. The action is set as + /// a target of the button for `TouchUpInside` events. When property changes occur the + /// previous action is removed as a target. This also binds the enabled state of the + /// action to the `rex_enabled` property on the button. public var rac_pressed: MutableProperty { return associatedObject(self, &pressed, { _ in let initial = CocoaAction.rex_disabled From bdb62283027773f2ac1bf277f85cabdd5d999cc0 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Fri, 19 Jun 2015 14:17:55 -0700 Subject: [PATCH 0081/1028] Bump dependencies --- Cartfile.resolved | 4 ++-- Carthage/Checkouts/ReactiveCocoa | 2 +- Carthage/Checkouts/Result | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cartfile.resolved b/Cartfile.resolved index b84d80e2f4..1a6fbf29f2 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,3 +1,3 @@ github "robrix/Box" "1.2.2" -github "antitypical/Result" "0.4.3" -github "ReactiveCocoa/ReactiveCocoa" "v3.0-beta.6" +github "antitypical/Result" "0.4.4" +github "ReactiveCocoa/ReactiveCocoa" "v3.0-beta.8" diff --git a/Carthage/Checkouts/ReactiveCocoa b/Carthage/Checkouts/ReactiveCocoa index b15798ae4f..b89e1b73f2 160000 --- a/Carthage/Checkouts/ReactiveCocoa +++ b/Carthage/Checkouts/ReactiveCocoa @@ -1 +1 @@ -Subproject commit b15798ae4f6386b8461b3b06f49908f2009a28ce +Subproject commit b89e1b73f2d1c6a99e0916f699ab6dcda71c74b5 diff --git a/Carthage/Checkouts/Result b/Carthage/Checkouts/Result index f9045c2a1f..81b189645b 160000 --- a/Carthage/Checkouts/Result +++ b/Carthage/Checkouts/Result @@ -1 +1 @@ -Subproject commit f9045c2a1fee1af321e29eea7c633be5a6a42532 +Subproject commit 81b189645babd30c6f70ed3f82a83988a467fb02 From 242552f3ad2ebd002d8c420eabd37c38152c4f2f Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Fri, 19 Jun 2015 14:18:56 -0700 Subject: [PATCH 0082/1028] Version 0.2.3 --- Source/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Info.plist b/Source/Info.plist index 78f54ce197..0b184c4481 100644 --- a/Source/Info.plist +++ b/Source/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.2.2 + 0.2.3 CFBundleSignature ???? CFBundleVersion From 4f35289761fe538cf996ca6e188838a408f1da1d Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Fri, 19 Jun 2015 15:38:38 -0700 Subject: [PATCH 0083/1028] rac_pressed => rex_pressed --- Source/UIKit/UIButton.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/UIKit/UIButton.swift b/Source/UIKit/UIButton.swift index c7f1bf5ffa..54cf032608 100644 --- a/Source/UIKit/UIButton.swift +++ b/Source/UIKit/UIButton.swift @@ -14,7 +14,7 @@ extension UIButton { /// a target of the button for `TouchUpInside` events. When property changes occur the /// previous action is removed as a target. This also binds the enabled state of the /// action to the `rex_enabled` property on the button. - public var rac_pressed: MutableProperty { + public var rex_pressed: MutableProperty { return associatedObject(self, &pressed, { _ in let initial = CocoaAction.rex_disabled let property = MutableProperty(initial) From 45f4ee187279c11643969008d3fcd1fb9cabe7cb Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Fri, 19 Jun 2015 17:19:22 -0700 Subject: [PATCH 0084/1028] Fix UILabel comment --- Source/UIKit/UILabel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/UIKit/UILabel.swift b/Source/UIKit/UILabel.swift index 5d82a23ad9..12da8d32a0 100644 --- a/Source/UIKit/UILabel.swift +++ b/Source/UIKit/UILabel.swift @@ -10,7 +10,7 @@ import ReactiveCocoa import UIKit extension UILabel { - /// Wraps a label's `enabled` state in a bindable property. + /// Wraps a label's `text` value in a bindable property. public var rex_text: MutableProperty { return associatedProperty(self, "text") } From aca7ffc9d6800e6ceaa6ca025e5b8f53cfa444c0 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 20 Jun 2015 13:00:50 -0700 Subject: [PATCH 0085/1028] UIButton.rex_title --- Source/UIKit/UIButton.swift | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Source/UIKit/UIButton.swift b/Source/UIKit/UIButton.swift index 54cf032608..ba5f4bb173 100644 --- a/Source/UIKit/UIButton.swift +++ b/Source/UIKit/UIButton.swift @@ -30,6 +30,17 @@ extension UIButton { return property }) } + + /// 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, &title, { + self.titleForState(.Normal) + }, { + self.setTitle($0, forState: .Normal) + }) + } } -private var pressed: UInt8 = 0 \ No newline at end of file +private var pressed: UInt8 = 0 +private var title: UInt8 = 0 From 81273911d03055a1496e569ecd7f30b297458748 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 20 Jun 2015 13:01:47 -0700 Subject: [PATCH 0086/1028] Formatting --- Source/UIKit/UIButton.swift | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Source/UIKit/UIButton.swift b/Source/UIKit/UIButton.swift index ba5f4bb173..e35dac5084 100644 --- a/Source/UIKit/UIButton.swift +++ b/Source/UIKit/UIButton.swift @@ -24,7 +24,7 @@ extension UIButton { |> start { previous, next in self.removeTarget(previous, action: CocoaAction.selector, forControlEvents: .TouchUpInside) self.addTarget(next, action: CocoaAction.selector, forControlEvents: .TouchUpInside) - } + } self.rex_enabled <~ property.producer |> flatMap(.Latest) { $0.rex_enabledProducer } return property @@ -34,11 +34,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, &title, { - self.titleForState(.Normal) - }, { - self.setTitle($0, forState: .Normal) - }) + return associatedProperty(self, &title, { self.titleForState(.Normal) }, { self.setTitle($0, forState: .Normal) }) } } From 4bd3a2fb2310ad931414b71bb267912a1b0a5139 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 20 Jun 2015 13:11:59 -0700 Subject: [PATCH 0087/1028] NSObject wrappers for associated properties --- Source/Foundation/NSObject.swift | 34 ++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index 87c06e5224..62af92a537 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -24,4 +24,38 @@ extension NSObject { return .empty } } + + /// Attaches a `MutableProperty` relying on KVC for the initial value and subsequent + /// updates. Note that `keyPath` is a `StaticString` because it's pointer value is used + /// as key value when associating the property. + /// + /// This can be used as an alternative to `DynamicProperty` for creating strongly typed + /// bindings on Cocoa objects. + public func rex_stringProperty(keyPath: StaticString) -> MutableProperty { + return associatedProperty(self, keyPath) + } + + /// Attaches a `MutableProperty` relying on KVC for the initial value and subsequent + /// updates. Note that `keyPath` is a `StaticString` because it's pointer value is used + /// as key value when associating the property. + /// + /// This can be used as an alternative to `DynamicProperty` for creating strongly typed + /// bindings on Cocoa objects. + public func rex_classProperty(host: AnyObject, keyPath: StaticString, placeholder: () -> T) -> MutableProperty { + return associatedProperty(self, keyPath, placeholder) + } + + /// Attaches a `MutableProperty` value under `key`. The property is initialized with + /// the result of `initial`. Changes on the property's producer are monitored and + /// written to `setter`. + /// + /// This can be used as an alternative to `DynamicProperty` for creating strongly typed + /// bindings on Cocoa objects. + public func rex_valueProperty(host: AnyObject, key: UnsafePointer<()>, initial: () -> T, setter: T -> ()) -> MutableProperty { + return associatedObject(host, key) { + let property = MutableProperty(initial()) + property.producer.start(next: setter) + return property + } + } } From 951c092ee26f5761c0e34a93928531623384504a Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 20 Jun 2015 13:16:44 -0700 Subject: [PATCH 0088/1028] Associated object fixes --- Source/Foundation/NSObject.swift | 4 ++-- Source/UIKit/UIButton.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index 62af92a537..8c80dc3807 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -51,8 +51,8 @@ extension NSObject { /// /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. - public func rex_valueProperty(host: AnyObject, key: UnsafePointer<()>, initial: () -> T, setter: T -> ()) -> MutableProperty { - return associatedObject(host, key) { + public func rex_valueProperty(key: UnsafePointer<()>, initial: () -> T, setter: T -> ()) -> MutableProperty { + return associatedObject(self, key) { let property = MutableProperty(initial()) property.producer.start(next: setter) return property diff --git a/Source/UIKit/UIButton.swift b/Source/UIKit/UIButton.swift index e35dac5084..f303a0b7d3 100644 --- a/Source/UIKit/UIButton.swift +++ b/Source/UIKit/UIButton.swift @@ -34,7 +34,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, &title, { self.titleForState(.Normal) }, { self.setTitle($0, forState: .Normal) }) + return associatedProperty(self, &title, { self.titleForState(.Normal) ?? "" }, { self.setTitle($0, forState: .Normal) }) } } From 6e8606d0ffbe4edbbd56e79015b9f56ce6485be5 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 20 Jun 2015 13:21:39 -0700 Subject: [PATCH 0089/1028] Use the associated property methods --- Source/Foundation/NSObject.swift | 2 +- Source/UIKit/UIButton.swift | 2 +- Source/UIKit/UIControl.swift | 2 +- Source/UIKit/UILabel.swift | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index 8c80dc3807..963d073757 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -51,7 +51,7 @@ extension NSObject { /// /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. - public func rex_valueProperty(key: UnsafePointer<()>, initial: () -> T, setter: T -> ()) -> MutableProperty { + public func rex_valueProperty(key: UnsafePointer<()>, _ initial: () -> T, _ setter: T -> ()) -> MutableProperty { return associatedObject(self, key) { let property = MutableProperty(initial()) property.producer.start(next: setter) diff --git a/Source/UIKit/UIButton.swift b/Source/UIKit/UIButton.swift index f303a0b7d3..eb9187cf3b 100644 --- a/Source/UIKit/UIButton.swift +++ b/Source/UIKit/UIButton.swift @@ -34,7 +34,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, &title, { self.titleForState(.Normal) ?? "" }, { self.setTitle($0, forState: .Normal) }) + return rex_valueProperty(&title, { self.titleForState(.Normal) ?? "" }, { self.setTitle($0, forState: .Normal) }) } } diff --git a/Source/UIKit/UIControl.swift b/Source/UIKit/UIControl.swift index 77507a9677..02557b4176 100644 --- a/Source/UIKit/UIControl.swift +++ b/Source/UIKit/UIControl.swift @@ -12,7 +12,7 @@ import UIKit extension UIControl { /// Wraps a control's `enabled` state in a bindable property. public var rex_enabled: MutableProperty { - return associatedProperty(self, &enabled, { self.enabled }, { self.enabled = $0 }) + return rex_valueProperty(&enabled, { self.enabled }, { self.enabled = $0 }) } } diff --git a/Source/UIKit/UILabel.swift b/Source/UIKit/UILabel.swift index 12da8d32a0..24494fbcce 100644 --- a/Source/UIKit/UILabel.swift +++ b/Source/UIKit/UILabel.swift @@ -12,6 +12,6 @@ import UIKit extension UILabel { /// Wraps a label's `text` value in a bindable property. public var rex_text: MutableProperty { - return associatedProperty(self, "text") + return rex_stringProperty("text") } } \ No newline at end of file From 3b832280ac09e7da2e8308df3453c0e8cc2fbc42 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 20 Jun 2015 13:22:13 -0700 Subject: [PATCH 0090/1028] import Foundation --- Source/Foundation/Association.swift | 1 + Source/Foundation/NSData.swift | 1 + Source/Foundation/NSObject.swift | 1 + Source/Foundation/NSUserDefaults.swift | 2 +- 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Foundation/Association.swift b/Source/Foundation/Association.swift index 2707616b2b..6ab372ced3 100644 --- a/Source/Foundation/Association.swift +++ b/Source/Foundation/Association.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import Foundation import ReactiveCocoa /// Attaches a `MutableProperty` value to the `host` object using KVC to get the initial diff --git a/Source/Foundation/NSData.swift b/Source/Foundation/NSData.swift index b93854c52d..17c3427b8d 100644 --- a/Source/Foundation/NSData.swift +++ b/Source/Foundation/NSData.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import Foundation import ReactiveCocoa extension NSData { diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index 963d073757..feb7a1258c 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import Foundation import ReactiveCocoa extension NSObject { diff --git a/Source/Foundation/NSUserDefaults.swift b/Source/Foundation/NSUserDefaults.swift index 18336be1d5..8f5ebb6b27 100644 --- a/Source/Foundation/NSUserDefaults.swift +++ b/Source/Foundation/NSUserDefaults.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import Foundation import ReactiveCocoa extension NSUserDefaults { @@ -14,7 +15,6 @@ extension NSUserDefaults { /// convertible this will generate events whenever _any_ value in NSUserDefaults /// changes. func rex_valueForKey(key: String) -> SignalProducer { - let center = NSNotificationCenter.defaultCenter() let changes = center.rac_notifications(name: NSUserDefaultsDidChangeNotification) |> map { notification in From 389ca68a40ecf94262e76fe1777f4d12b9096bd7 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 24 Jun 2015 17:45:53 -0700 Subject: [PATCH 0091/1028] Use RAC extensions branch --- Cartfile | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/ReactiveCocoa | 2 +- Source/Foundation/NSObject.swift | 6 +- Source/Foundation/NSUserDefaults.swift | 22 ++--- Source/Operators.swift | 12 +-- Source/Property.swift | 49 ++++++---- Source/Signal.swift | 125 +++++++++++++------------ Source/SignalProducer.swift | 108 ++++++++++----------- Tests/PropertyTests.swift | 4 +- Tests/SignalProducerTests.swift | 23 ++--- Tests/SignalTests.swift | 30 +++--- 12 files changed, 195 insertions(+), 190 deletions(-) diff --git a/Cartfile b/Cartfile index 0a6c99df44..f5bef83bd0 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveCocoa" "swift2" +github "ReactiveCocoa/ReactiveCocoa" "protocol-extensions" diff --git a/Cartfile.resolved b/Cartfile.resolved index 6b0f471e60..4bc025d71f 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ github "antitypical/Result" "0.5" -github "ReactiveCocoa/ReactiveCocoa" "84b7fdd08cd1e49d09876a491695db261e5599b7" +github "ReactiveCocoa/ReactiveCocoa" "c32eb267a18dd8e396d8f1c8ee42497802480250" diff --git a/Carthage/Checkouts/ReactiveCocoa b/Carthage/Checkouts/ReactiveCocoa index 84b7fdd08c..c32eb267a1 160000 --- a/Carthage/Checkouts/ReactiveCocoa +++ b/Carthage/Checkouts/ReactiveCocoa @@ -1 +1 @@ -Subproject commit 84b7fdd08cd1e49d09876a491695db261e5599b7 +Subproject commit c32eb267a18dd8e396d8f1c8ee42497802480250 diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index 6f96f70a07..8852b8e61f 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -17,11 +17,11 @@ extension NSObject { public func rex_producerForKeyPath(keyPath: String) -> SignalProducer { return self.rac_valuesForKeyPath(keyPath, observer: nil) .toSignalProducer() - |> map { $0 as! T } - |> flatMapError { error in + .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 - } + } } } diff --git a/Source/Foundation/NSUserDefaults.swift b/Source/Foundation/NSUserDefaults.swift index 725e049e2f..9260672cb6 100644 --- a/Source/Foundation/NSUserDefaults.swift +++ b/Source/Foundation/NSUserDefaults.swift @@ -14,23 +14,23 @@ extension NSUserDefaults { /// convertible this will generate events whenever _any_ value in NSUserDefaults /// changes. func rex_valueForKey(key: String) -> SignalProducer { - let center = NSNotificationCenter.defaultCenter() + let initial = objectForKey(key) + let changes = center.rac_notifications(NSUserDefaultsDidChangeNotification) - |> map { notification in + .map { _ in // The notification doesn't provide what changed so we have to look // it up every time - return self.objectForKey(key) - } + self.objectForKey(key) + } - return SignalProducer(value: objectForKey(key)) - |> concat(changes) - |> skipRepeats { previous, next in - if let previous = previous as? NSObject, - let next = next as? NSObject { - return previous == next + return SignalProducer(value: initial) + .concat(changes) + .skipRepeats { previous, next in + if let previous = previous as? NSObject, next = next as? NSObject { + return previous == next } return false - } + } } } diff --git a/Source/Operators.swift b/Source/Operators.swift index 9bfe300e9a..7882beaea5 100644 --- a/Source/Operators.swift +++ b/Source/Operators.swift @@ -12,10 +12,10 @@ import Foundation import ReactiveCocoa infix operator --> { -associativity left + associativity left -// bind as strong as assignment. -precedence 90 + // bind as strong as assignment. + precedence 90 } /// Sends the value as the next event. Usage: @@ -24,7 +24,7 @@ precedence 90 /// /// - parameter value: the next value. /// - parameter observer: the observer that will handle the value. -public func -->(value:T, sink:SinkOf>) { +public func -->(value: T, sink: Signal.Observer) { sendNext(sink, value) } @@ -34,7 +34,7 @@ public func -->(value:T, sink:SinkOf>) { /// /// - parameter error: the error to send. /// - parameter observer: the observer that will handle the value. -public func -->(error:E, sink:SinkOf>) { +public func -->(error: E, sink: Signal.Observer) { sendError(sink, error) } @@ -45,6 +45,6 @@ prefix operator --| {} /// --|sink /// /// - parameter observer: the sink to whcih the signal will be sent. -public prefix func --|(sink:SinkOf>) { +public prefix func --|(sink: Signal.Observer) { return sendCompleted(sink) } diff --git a/Source/Property.swift b/Source/Property.swift index 13492a38a3..ea0e9a530e 100644 --- a/Source/Property.swift +++ b/Source/Property.swift @@ -35,32 +35,43 @@ public struct SignalProperty: PropertyType { } } -/// Creates a new property bound to `signal` starting with `initialValue`. -public func propertyOf(initialValue: T)(signal: Signal) -> PropertyOf { - return PropertyOf(SignalProperty(initialValue, signal)) -} +extension Signal where E: NoErrorType { + /// Creates a new property bound to `self` starting with `initialValue`. + public func propertyOf(initialValue: T) -> PropertyOf { + return PropertyOf(SignalProperty(initialValue, ignoreError())) + } -/// Creates a new property bound to `producer` starting with `initialValue`. -public func propertyOf(initialValue: T)(producer: SignalProducer) -> PropertyOf { - return PropertyOf(SignalProperty(initialValue, producer)) + /// Wraps `sink` in a property bound to `self`. Values sent on the signal are + /// `put` into the `sink` to update it. + public func propertySink(sink: S) -> PropertyOf { + return put(sink).propertyOf(sink) + } } -/// Wraps `sink` in a property bound to `signal`. Values sent on `signal` are `put` into -/// the `sink` to update it. -public func propertySink(sink: S)(signal: Signal) -> PropertyOf { - return signal |> put(sink) |> propertyOf(sink) -} +extension SignalProducer where E: NoErrorType { + /// Creates a new property bound to `producer` starting with `initialValue`. + public func propertyOf(initialValue: T) -> PropertyOf { + return PropertyOf(SignalProperty(initialValue, ignoreError())) + } -/// Wraps `sink` in a property bound to `producer`. Values sent on `producer` are `put` into -/// the `sink` to update it. -public func propertySink(sink: S)(producer: SignalProducer) -> PropertyOf { - return producer |> put(sink) |> propertyOf(sink) + /// Wraps `sink` in a property bound to `self`. Values sent on the signal are + /// `put` into the `sink` to update it. + public func propertySink(sink: S) -> PropertyOf { + return put(sink).propertyOf(sink) + } } -private func put(sink: S)(signal: Signal) -> Signal { - return signal - |> scan(sink) { (var value, change) in +extension Signal { + private func put(sink: S) -> Signal { + return scan(sink) { (var value, change) in value.put(change) return value + } + } +} + +extension SignalProducer { + private func put(sink: S) -> SignalProducer { + return lift { $0.put(sink) } } } diff --git a/Source/Signal.swift b/Source/Signal.swift index 3b085e9772..2ce15f3667 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -8,88 +8,89 @@ import ReactiveCocoa -/// Applies `transform` to values from `signal` with non-`nil` results unwrapped and -/// forwared on the returned signal. -public func filterMap(transform: T -> U?)(signal: Signal) -> Signal { - return Signal { observer in - signal.observe(next: { value in - if let val = transform(value) { - sendNext(observer, val) - } - }, error: { error in - sendError(observer, error) - }, completed: { - sendCompleted(observer) - }, interrupted: { - sendInterrupted(observer) - }) +extension Signal { + /// Applies `transform` to values from `signal` with non-`nil` results unwrapped and + /// forwared on the returned signal. + public func filterMap(transform: T -> U?) -> Signal { + return Signal { observer in + return self.observe(next: { value in + if let val = transform(value) { + sendNext(observer, val) + } + }, error: { error in + sendError(observer, error) + }, completed: { + sendCompleted(observer) + }, interrupted: { + sendInterrupted(observer) + }) + } } -} -/// Returns a signal that drops `Error` events, replacing them with `Completed`. -public func ignoreError(signal: Signal) -> Signal { - return signal |> ignoreError(replacement: .Completed) -} + /// Returns a signal that drops `Error` events, replacing them with `Completed`. + public func ignoreError() -> Signal { + return ignoreError(replacement: .Completed) + } -/// Returns a signal that drops `Error` sending `replacement` terminal event instead. -public func ignoreError(replacement replacement: Event)(signal: Signal) -> Signal { - precondition(replacement.isTerminating) + /// Returns a signal that drops `Error` sending `replacement` terminal event instead. + public func ignoreError(replacement replacement: Event) -> Signal { + precondition(replacement.isTerminating) - return Signal { observer in - return signal.observe(Signal.Observer { event in - switch event { - case let .Next(value): - sendNext(observer, value) - case .Error: - observer.put(replacement) - case .Completed: - sendCompleted(observer) - case .Interrupted: - sendInterrupted(observer) + return Signal { observer in + return self.observe { event in + switch event { + case let .Next(value): + sendNext(observer, value) + case .Error: + observer(replacement) + case .Completed: + sendCompleted(observer) + case .Interrupted: + sendInterrupted(observer) + } } - }) + } } -} - -/// Forwards events from `signal` until `interval`. Then if signal isn't completed yet, -/// terminates with `event` on `scheduler`. -/// -/// 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. -public func timeoutAfter(interval: NSTimeInterval, withEvent event: Event, onScheduler scheduler: DateSchedulerType) -> Signal -> Signal { - precondition(interval >= 0) - precondition(event.isTerminating) + /// Forwards events from `signal` until `interval`. Then if signal isn't completed yet, + /// terminates with `event` on `scheduler`. + /// + /// 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. + public func timeoutAfter(interval: NSTimeInterval, withEvent event: Event, onScheduler scheduler: DateSchedulerType) -> Signal { + precondition(interval >= 0) + precondition(event.isTerminating) - return { signal in return Signal { observer in let disposable = CompositeDisposable() let date = scheduler.currentDate.dateByAddingTimeInterval(interval) disposable += scheduler.scheduleAfter(date) { - observer.put(event) + observer(event) } - disposable += signal.observe(observer) + disposable += self.observe(observer) return disposable } } } -/// Returns a signal that flattens sequences of elements. The inverse of `collect`. -public func uncollect(signal: Signal) -> Signal { - return Signal { observer in - return signal.observe(Signal.Observer { event in - switch event { - case let .Next(sequence): - sequence.map { sendNext(observer, $0) } - case let .Error(error): - sendError(observer, error) - case .Completed: - sendCompleted(observer) - case .Interrupted: - sendInterrupted(observer) +extension Signal where T: SequenceType { + /// Returns a signal that flattens sequences of elements. The inverse of `collect`. + public func uncollect() -> Signal { + return Signal { observer in + return self.observe { event in + switch event { + case let .Next(sequence): + sequence.map { sendNext(observer, $0) } + case let .Error(error): + sendError(observer, error) + case .Completed: + sendCompleted(observer) + case .Interrupted: + sendInterrupted(observer) + } } - }) + } } } diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index 40c4707423..047f036bb6 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -8,77 +8,73 @@ import ReactiveCocoa -/// 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. -public func groupBy(grouping: T -> K)(producer: SignalProducer) -> SignalProducer<(K, SignalProducer), E> { +extension SignalProducer { + /// 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. + public func groupBy(grouping: T -> K) -> SignalProducer<(K, SignalProducer), E> { + return SignalProducer<(K, SignalProducer), E> { observer, disposable in + var groups: [K: Signal.Observer] = [:] - return SignalProducer { observer, disposable in - var groups: [K: Signal.Observer] = [:] + let lock = NSRecursiveLock() + lock.name = "me.neilpa.rex.groupBy" - let lock = NSRecursiveLock() - lock.name = "me.neilpa.rex.groupBy" + self.start(next: { value in + let key = grouping(value) - producer.start(next: { value in - let key = grouping(value) + lock.lock() + var group = groups[key] + if group == nil { + let (producer, sink) = SignalProducer.buffer() + sendNext(observer, (key, producer)) - lock.lock() - var group = groups[key] - if group == nil { - let (producer, sink) = SignalProducer.buffer() - sendNext(observer, (key, producer)) + groups[key] = sink + group = sink + } + lock.unlock() - groups[key] = sink - group = sink - } - lock.unlock() + sendNext(group!, value) - sendNext(group!, value) + }, error: { error in + sendError(observer, error) + groups.values.map { sendError($0, error) } - }, error: { error in - sendError(observer, error) - groups.values.map { sendError($0, error) } + }, completed: { _ in + sendCompleted(observer) + groups.values.map { sendCompleted($0) } - }, completed: { _ in - sendCompleted(observer) - groups.values.map { sendCompleted($0) } + }, interrupted: { _ in + sendInterrupted(observer) + groups.values.map { sendInterrupted($0) } + }) + } + } - }, interrupted: { _ in - sendInterrupted(observer) - groups.values.map { sendInterrupted($0) } - }) + /// Returns a signal that drops `Error` events, replacing them with `Completed`. + public func ignoreError() -> SignalProducer { + return lift { $0.ignoreError() } } -} -/// Returns a signal that prints the signal events -public func print(signal: SignalProducer) -> SignalProducer { - return signal |> on(event: print) -} + /// Returns a signal that prints the signal events + public func print() -> SignalProducer { + return on(event: Swift.print) + } -/// Returns a signal that prints the signal `Next` events -public func printNext(signal: SignalProducer) -> SignalProducer { - return signal - |> on(event: { event in - switch event { - case .Next: - print(event) - - default: - break + /// Returns a signal that prints the signal `Next` events + public func printNext() -> SignalProducer { + return on(event: { event in + if case .Next(_) = event { + Swift.print(event) } }) -} + } -/// Returns a signal that prints the signal `Error` events -public func printError(signal: SignalProducer) -> SignalProducer { - return signal - |> on(event: { event in - switch event { - case .Error: - print(event) - - default: - break + /// Returns a signal that prints the signal `Error` events + public func printError() -> SignalProducer { + return on(event: { event in + if case .Error(_) = event { + Swift.print(event) } }) + } } diff --git a/Tests/PropertyTests.swift b/Tests/PropertyTests.swift index d7b5bc31ba..e639f7a7d5 100644 --- a/Tests/PropertyTests.swift +++ b/Tests/PropertyTests.swift @@ -43,7 +43,7 @@ final class PropertyTests: XCTestCase { func testSignalPropertySink() { let (signal, sink) = Signal.pipe() - let property = signal |> propertySink(Collector()) + let property = signal.propertySink(Collector()) XCTAssert(property.value.values == []) sendNext(sink, 1) @@ -57,7 +57,7 @@ final class PropertyTests: XCTestCase { func testProducerPropertySink() { let (producer, sink) = SignalProducer.buffer() - let property = producer |> propertySink(Collector(values: [])) + let property = producer.propertySink(Collector(values: [])) XCTAssert(property.value.values == []) sendNext(sink, 1) diff --git a/Tests/SignalProducerTests.swift b/Tests/SignalProducerTests.swift index 93a411eb33..181ae5797b 100644 --- a/Tests/SignalProducerTests.swift +++ b/Tests/SignalProducerTests.swift @@ -21,12 +21,12 @@ final class SignalProducerTests: XCTestCase { var completed = false disposable += producer - |> groupBy { $0 % 2 == 0 } - |> start(next: { key, group in + .groupBy { $0 % 2 == 0 } + .start(next: { key, group in if key { - group |> start(next: evens.append) + group.start(next: { evens.append($0) }) } else { - group |> start(next: odds.append) + group.start(next: { odds.append($0) }) } },completed: { completed = true @@ -62,16 +62,17 @@ final class SignalProducerTests: XCTestCase { var completed = false disposable += producer - |> groupBy { $0 % 2 == 0 } - |> start(next: { key, group in + .groupBy { $0 % 2 == 0 } + .start(next: { key, group in if key { - group |> start(next: evens.append) + group.start(next: { evens.append($0) }) } else { - group |> start(next: odds.append) + group.start(next: { odds.append($0) }) } - },completed: { completed = true }, - interrupted: { - interrupted = true + },completed: { + completed = true + }, interrupted: { + interrupted = true }) 1 --> sink diff --git a/Tests/SignalTests.swift b/Tests/SignalTests.swift index 0b1119f347..e74f28af0f 100644 --- a/Tests/SignalTests.swift +++ b/Tests/SignalTests.swift @@ -17,10 +17,10 @@ final class SignalTests: XCTestCase { var values: [String] = [] signal - |> filterMap { + .filterMap { return $0 % 2 == 0 ? String($0) : nil } - |> observe(next: values.append) + .observe(next: { values.append($0) }) 1 --> sink XCTAssert(values == []) @@ -40,8 +40,8 @@ final class SignalTests: XCTestCase { var completed = false signal - |> ignoreError - |> observe(completed: { + .ignoreError() + .observe(completed: { completed = true }) @@ -57,8 +57,8 @@ final class SignalTests: XCTestCase { var interrupted = false signal - |> ignoreError(replacement: .Interrupted) - |> observe(interrupted: { + .ignoreError(replacement: .Interrupted) + .observe(interrupted: { interrupted = true }) @@ -76,8 +76,8 @@ final class SignalTests: XCTestCase { var completed = false signal - |> timeoutAfter(2, withEvent: .Interrupted, onScheduler: scheduler) - |> observe( + .timeoutAfter(2, withEvent: .Interrupted, onScheduler: scheduler) + .observe( completed: { completed = true }, interrupted: { interrupted = true } ) @@ -99,8 +99,8 @@ final class SignalTests: XCTestCase { var completed = false signal - |> timeoutAfter(2, withEvent: .Interrupted, onScheduler: scheduler) - |> observe( + .timeoutAfter(2, withEvent: .Interrupted, onScheduler: scheduler) + .observe( completed: { completed = true }, interrupted: { interrupted = true } ) @@ -120,8 +120,8 @@ final class SignalTests: XCTestCase { var values: [Int] = [] signal - |> uncollect - |> observe(next: { + .uncollect() + .observe(next: { values.append($0) }) @@ -136,10 +136,6 @@ final class SignalTests: XCTestCase { } } -enum TestError: Swift.ErrorType, ReactiveCocoa.ErrorType { +enum TestError: ErrorType { case Default - - var nsError: NSError { - return self as NSError - } } From 8af71c66c809a8d4cfedf49ac03c3cbea51e4348 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 24 Jun 2015 17:54:18 -0700 Subject: [PATCH 0092/1028] propertyOf => toProperty --- Source/Property.swift | 12 ++++++------ Tests/PropertyTests.swift | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Source/Property.swift b/Source/Property.swift index ea0e9a530e..67f5a9ab03 100644 --- a/Source/Property.swift +++ b/Source/Property.swift @@ -37,27 +37,27 @@ public struct SignalProperty: PropertyType { extension Signal where E: NoErrorType { /// Creates a new property bound to `self` starting with `initialValue`. - public func propertyOf(initialValue: T) -> PropertyOf { + public func toProperty(initialValue: T) -> PropertyOf { return PropertyOf(SignalProperty(initialValue, ignoreError())) } /// Wraps `sink` in a property bound to `self`. Values sent on the signal are /// `put` into the `sink` to update it. - public func propertySink(sink: S) -> PropertyOf { - return put(sink).propertyOf(sink) + public func toPropertySink(sink: S) -> PropertyOf { + return put(sink).toProperty(sink) } } extension SignalProducer where E: NoErrorType { /// Creates a new property bound to `producer` starting with `initialValue`. - public func propertyOf(initialValue: T) -> PropertyOf { + public func toProperty(initialValue: T) -> PropertyOf { return PropertyOf(SignalProperty(initialValue, ignoreError())) } /// Wraps `sink` in a property bound to `self`. Values sent on the signal are /// `put` into the `sink` to update it. - public func propertySink(sink: S) -> PropertyOf { - return put(sink).propertyOf(sink) + public func toPropertySink(sink: S) -> PropertyOf { + return put(sink).toProperty(sink) } } diff --git a/Tests/PropertyTests.swift b/Tests/PropertyTests.swift index e639f7a7d5..8f71dbf8ce 100644 --- a/Tests/PropertyTests.swift +++ b/Tests/PropertyTests.swift @@ -43,7 +43,7 @@ final class PropertyTests: XCTestCase { func testSignalPropertySink() { let (signal, sink) = Signal.pipe() - let property = signal.propertySink(Collector()) + let property = signal.toPropertySink(Collector()) XCTAssert(property.value.values == []) sendNext(sink, 1) @@ -57,7 +57,7 @@ final class PropertyTests: XCTestCase { func testProducerPropertySink() { let (producer, sink) = SignalProducer.buffer() - let property = producer.propertySink(Collector(values: [])) + let property = producer.toPropertySink(Collector(values: [])) XCTAssert(property.value.values == []) sendNext(sink, 1) From 3b45622dc9783de23577d43a6847e5400625cb78 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 24 Jun 2015 17:56:56 -0700 Subject: [PATCH 0093/1028] Default arg for ignoreError instead of overload --- Source/Signal.swift | 10 +++------- Source/SignalProducer.swift | 4 ++-- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Source/Signal.swift b/Source/Signal.swift index 2ce15f3667..699bd88ce6 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -27,13 +27,9 @@ extension Signal { } } - /// Returns a signal that drops `Error` events, replacing them with `Completed`. - public func ignoreError() -> Signal { - return ignoreError(replacement: .Completed) - } - - /// Returns a signal that drops `Error` sending `replacement` terminal event instead. - public func ignoreError(replacement replacement: Event) -> Signal { + /// Returns a signal that drops `Error` sending `replacement` terminal event + /// instead, defaulting to `Completed`. + public func ignoreError(replacement replacement: Event = .Completed) -> Signal { precondition(replacement.isTerminating) return Signal { observer in diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index 047f036bb6..50935f6f17 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -51,8 +51,8 @@ extension SignalProducer { } /// Returns a signal that drops `Error` events, replacing them with `Completed`. - public func ignoreError() -> SignalProducer { - return lift { $0.ignoreError() } + public func ignoreError(replacement: Event = .Completed) -> SignalProducer { + return lift { $0.ignoreError(replacement: replacement) } } /// Returns a signal that prints the signal events From f2ba5075bf45bae302bd8f57e60da1b0b142b589 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 4 Jul 2015 13:08:13 -0700 Subject: [PATCH 0094/1028] rex_controlEvents wrapper for rac_signalForControlEvents --- Source/UIKit/UIControl.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Source/UIKit/UIControl.swift b/Source/UIKit/UIControl.swift index 02557b4176..0a40ab72fd 100644 --- a/Source/UIKit/UIControl.swift +++ b/Source/UIKit/UIControl.swift @@ -10,6 +10,14 @@ import ReactiveCocoa import UIKit extension UIControl { + /// Creates a producer for the sender whenever a specified control event is triggered. + public func rex_controlEvents(events: UIControlEvents) -> SignalProducer { + return rac_signalForControlEvents(events) + .toSignalProducer() + |> map { $0 as? UIControl } + |> catch { _ in SignalProducer(value: nil) } + } + /// Wraps a control's `enabled` state in a bindable property. public var rex_enabled: MutableProperty { return rex_valueProperty(&enabled, { self.enabled }, { self.enabled = $0 }) From 4bed61c9615893bb3925f3a25f4aada062c383b2 Mon Sep 17 00:00:00 2001 From: skyylex Date: Wed, 8 Jul 2015 16:15:44 +0300 Subject: [PATCH 0095/1028] NSTextField extension for observing all text changes --- Rex.xcodeproj/project.pbxproj | 12 +++++++++ Source/AppKit/NSTextField.swift | 45 +++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 Source/AppKit/NSTextField.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index d31c919bef..fbc55727b1 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 4238D5961B4D5950008534C0 /* NSTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4238D5951B4D5950008534C0 /* NSTextField.swift */; }; 7DE995CB1B03A56800CA9420 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE995CA1B03A56800CA9420 /* Operators.swift */; }; 7DE995CC1B03A56800CA9420 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE995CA1B03A56800CA9420 /* Operators.swift */; }; D8003E941AFEC3D400D7D3C5 /* Rex.h in Headers */ = {isa = PBXBuildFile; fileRef = D8003E931AFEC3D400D7D3C5 /* Rex.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -102,6 +103,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 4238D5951B4D5950008534C0 /* NSTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NSTextField.swift; path = AppKit/NSTextField.swift; sourceTree = ""; }; 7DE995CA1B03A56800CA9420 /* Operators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Operators.swift; sourceTree = ""; }; D8003E921AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D8003E931AFEC3D400D7D3C5 /* Rex.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Rex.h; sourceTree = ""; }; @@ -177,6 +179,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 4238D5941B4D593E008534C0 /* AppKit */ = { + isa = PBXGroup; + children = ( + 4238D5951B4D5950008534C0 /* NSTextField.swift */, + ); + name = AppKit; + sourceTree = ""; + }; D8003E841AFEC3D400D7D3C5 = { isa = PBXGroup; children = ( @@ -196,6 +206,7 @@ D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */, D8F097391B17F2BF002E15BA /* Foundation */, D86FFBD31B34B0E2001A89B3 /* UIKit */, + 4238D5941B4D593E008534C0 /* AppKit */, D8003E911AFEC3D400D7D3C5 /* Supporting Files */, ); path = Source; @@ -475,6 +486,7 @@ D8003EBD1AFED01000D7D3C5 /* Signal.swift in Sources */, D8F097441B17F3C8002E15BA /* NSObject.swift in Sources */, D8F0973B1B17F2F7002E15BA /* NSData.swift in Sources */, + 4238D5961B4D5950008534C0 /* NSTextField.swift in Sources */, D8F0973D1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Source/AppKit/NSTextField.swift b/Source/AppKit/NSTextField.swift new file mode 100644 index 0000000000..5ddec6154c --- /dev/null +++ b/Source/AppKit/NSTextField.swift @@ -0,0 +1,45 @@ +// +// NSTextField.swift +// Rex +// +// Created by Yury Lapitsky on 7/8/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import Foundation +import ReactiveCocoa +import AppKit + +extension NSTextField { + /// Producer will forward all text changes related to current NSTextField + public var rex_stringValueChanged : SignalProducer { + return SignalProducer(values: [self.rex_stringValueChangedFromCode, self.rex_stringValueChangedFromUI]) |> flatten(FlattenStrategy.Merge) + } + + public var rex_stringValueChangedFromCode : SignalProducer { + let signalProducer : SignalProducer = self.rac_signalForSelector(Selector("setStringValue:")).toSignalProducer() + return signalProducer + |> map { value in + let tuple = value as? RACTuple ?? RACTuple() + if let first = tuple.first as? String { + return first + } + else { + return "" + } + } + |> catch() { _ in SignalProducer.empty } + } + + public var rex_stringValueChangedFromUI: SignalProducer { + return self.rac_textSignal().toSignalProducer() + |> flatMap(FlattenStrategy.Merge) { stringValue in + return SignalProducer { observer, disposable in + let stringValue = stringValue as? String ?? "" + sendNext(observer, stringValue) + sendCompleted(observer) + } + } + |> catch() { _ in SignalProducer.empty } + } +} \ No newline at end of file From 9aa9f1b23cb7a153253c058c696d1aced747029d Mon Sep 17 00:00:00 2001 From: skyylex Date: Wed, 8 Jul 2015 17:17:41 +0300 Subject: [PATCH 0096/1028] NSTextField extestion code zip --- Rex.xcodeproj/project.pbxproj | 1 + Source/AppKit/NSTextField.swift | 34 +++++++++++++++------------------ 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index fbc55727b1..1aa74cc3c0 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -405,6 +405,7 @@ D8003E851AFEC3D400D7D3C5 /* Project object */ = { isa = PBXProject; attributes = { + LastSwiftUpdateCheck = 0700; LastUpgradeCheck = 0630; ORGANIZATIONNAME = "Neil Pankey"; TargetAttributes = { diff --git a/Source/AppKit/NSTextField.swift b/Source/AppKit/NSTextField.swift index 5ddec6154c..e88656f3b7 100644 --- a/Source/AppKit/NSTextField.swift +++ b/Source/AppKit/NSTextField.swift @@ -12,34 +12,30 @@ import AppKit extension NSTextField { /// Producer will forward all text changes related to current NSTextField - public var rex_stringValueChanged : SignalProducer { - return SignalProducer(values: [self.rex_stringValueChangedFromCode, self.rex_stringValueChangedFromUI]) |> flatten(FlattenStrategy.Merge) + public var rex_textChanged : SignalProducer { + return SignalProducer(values: [rex_textChangedOnSelector, rex_textChangedOnNotification]) |> flatten(FlattenStrategy.Merge) } - public var rex_stringValueChangedFromCode : SignalProducer { - let signalProducer : SignalProducer = self.rac_signalForSelector(Selector("setStringValue:")).toSignalProducer() + private var rex_textChangedOnSelector : SignalProducer { + let signalProducer : SignalProducer = self.rac_signalForSelector("setStringValue:").toSignalProducer() return signalProducer - |> map { value in - let tuple = value as? RACTuple ?? RACTuple() - if let first = tuple.first as? String { - return first - } - else { + |> map { next in + if let tuple = next as? RACTuple, text = tuple.first as? String { + return text + } else { return "" } } - |> catch() { _ in SignalProducer.empty } + |> ignoreError } - public var rex_stringValueChangedFromUI: SignalProducer { + private var rex_textChangedOnNotification: SignalProducer { return self.rac_textSignal().toSignalProducer() - |> flatMap(FlattenStrategy.Merge) { stringValue in - return SignalProducer { observer, disposable in - let stringValue = stringValue as? String ?? "" - sendNext(observer, stringValue) - sendCompleted(observer) - } + |> map { next in + precondition(next == nil || next is String) + + return next as? String ?? "" } - |> catch() { _ in SignalProducer.empty } + |> ignoreError } } \ No newline at end of file From 8df8b9d561d8b514a1c7dc61409f31a2a20dd2c1 Mon Sep 17 00:00:00 2001 From: Alexander Schuch Date: Wed, 22 Jul 2015 00:31:13 +0200 Subject: [PATCH 0097/1028] Update documentation on UILabel.rex_text --- Source/UIKit/UILabel.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/UIKit/UILabel.swift b/Source/UIKit/UILabel.swift index 5d82a23ad9..7a1d5cb2d9 100644 --- a/Source/UIKit/UILabel.swift +++ b/Source/UIKit/UILabel.swift @@ -10,8 +10,8 @@ import ReactiveCocoa import UIKit extension UILabel { - /// Wraps a label's `enabled` state in a bindable property. + /// Wraps a label's `text` value in a bindable property. public var rex_text: MutableProperty { return associatedProperty(self, "text") } -} \ No newline at end of file +} From cc12082d38a5a9e0a4a9d13d61db4189f2488439 Mon Sep 17 00:00:00 2001 From: skyylex Date: Fri, 24 Jul 2015 18:49:32 +0300 Subject: [PATCH 0098/1028] rex_textSignal implementation --- Source/AppKit/NSTextField.swift | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/Source/AppKit/NSTextField.swift b/Source/AppKit/NSTextField.swift index e88656f3b7..456a568fd4 100644 --- a/Source/AppKit/NSTextField.swift +++ b/Source/AppKit/NSTextField.swift @@ -11,31 +11,12 @@ import ReactiveCocoa import AppKit extension NSTextField { - /// Producer will forward all text changes related to current NSTextField - public var rex_textChanged : SignalProducer { - return SignalProducer(values: [rex_textChangedOnSelector, rex_textChangedOnNotification]) |> flatten(FlattenStrategy.Merge) - } - - private var rex_textChangedOnSelector : SignalProducer { - let signalProducer : SignalProducer = self.rac_signalForSelector("setStringValue:").toSignalProducer() - return signalProducer - |> map { next in - if let tuple = next as? RACTuple, text = tuple.first as? String { - return text - } else { - return "" - } - } - |> ignoreError - } - - private var rex_textChangedOnNotification: SignalProducer { - return self.rac_textSignal().toSignalProducer() - |> map { next in - precondition(next == nil || next is String) - - return next as? String ?? "" - } - |> ignoreError + /// only changes from UI will be produced here + public var rex_textSignal: SignalProducer { + let notificationCenter = NSNotificationCenter.defaultCenter() + let notificationsProducer = notificationCenter.rac_notifications(name: NSControlTextDidChangeNotification, object: self) + return notificationsProducer + |> filter { notification in (notification.object as? NSTextField) == self } + |> map { notification in (notification.object as! NSTextField).stringValue } } } \ No newline at end of file From a196b8e1b3c2ebc3d4cd1577b3b12588579998d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bjarke=20Hesthaven=20S=C3=B8ndergaard?= Date: Fri, 24 Jul 2015 13:40:16 +0200 Subject: [PATCH 0099/1028] Allow action binding to UIBarButtonItem Allows for similar action binding to UIBarButtonItem as found for UIButton. --- Rex.xcodeproj/project.pbxproj | 8 +++++++ Source/UIKit/UIBarButtonItem.swift | 34 ++++++++++++++++++++++++++++++ Source/UIKit/UIBarItem.swift | 19 +++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 Source/UIKit/UIBarButtonItem.swift create mode 100644 Source/UIKit/UIBarItem.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index d31c919bef..9fa699a440 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 5173EBC61B625A2600C9B48E /* UIBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5173EBC51B625A2600C9B48E /* UIBarItem.swift */; }; + 5173EBC81B625A6800C9B48E /* UIBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */; }; 7DE995CB1B03A56800CA9420 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE995CA1B03A56800CA9420 /* Operators.swift */; }; 7DE995CC1B03A56800CA9420 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE995CA1B03A56800CA9420 /* Operators.swift */; }; D8003E941AFEC3D400D7D3C5 /* Rex.h in Headers */ = {isa = PBXBuildFile; fileRef = D8003E931AFEC3D400D7D3C5 /* Rex.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -102,6 +104,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 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 = ""; }; 7DE995CA1B03A56800CA9420 /* Operators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Operators.swift; sourceTree = ""; }; D8003E921AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D8003E931AFEC3D400D7D3C5 /* Rex.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Rex.h; sourceTree = ""; }; @@ -267,6 +271,8 @@ D86FFBD31B34B0E2001A89B3 /* UIKit */ = { isa = PBXGroup; children = ( + 5173EBC51B625A2600C9B48E /* UIBarItem.swift */, + 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */, D86FFBDC1B34B691001A89B3 /* UIButton.swift */, D86FFBD41B34B0FE001A89B3 /* UIControl.swift */, D86FFBD71B34B242001A89B3 /* UILabel.swift */, @@ -474,7 +480,9 @@ 7DE995CB1B03A56800CA9420 /* Operators.swift in Sources */, D8003EBD1AFED01000D7D3C5 /* Signal.swift in Sources */, D8F097441B17F3C8002E15BA /* NSObject.swift in Sources */, + 5173EBC81B625A6800C9B48E /* UIBarButtonItem.swift in Sources */, D8F0973B1B17F2F7002E15BA /* NSData.swift in Sources */, + 5173EBC61B625A2600C9B48E /* UIBarItem.swift in Sources */, D8F0973D1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Source/UIKit/UIBarButtonItem.swift b/Source/UIKit/UIBarButtonItem.swift new file mode 100644 index 0000000000..ab04b546a9 --- /dev/null +++ b/Source/UIKit/UIBarButtonItem.swift @@ -0,0 +1,34 @@ +// +// UIBarButtonItem.swift +// Rex +// +// Created by Bjarke Hesthaven Søndergaard on 24/07/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa +import UIKit + +extension UIBarButtonItem { + /// Exposes a property that binds an action to bar button item. The action is set as + /// a target of the button. When property changes occur the previous action is + /// overwritten. 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, &action, { _ in + let initial = CocoaAction.rex_disabled + let property = MutableProperty(initial) + + property.producer + |> start { next in + self.target = next + self.action = CocoaAction.selector + } + + self.rex_enabled <~ property.producer |> flatMap(.Latest) { $0.rex_enabledProducer } + return property + }) + } +} + +private var action: UInt8 = 0 \ No newline at end of file diff --git a/Source/UIKit/UIBarItem.swift b/Source/UIKit/UIBarItem.swift new file mode 100644 index 0000000000..6006308dd0 --- /dev/null +++ b/Source/UIKit/UIBarItem.swift @@ -0,0 +1,19 @@ +// +// UIBarItem.swift +// Rex +// +// Created by Bjarke Hesthaven Søndergaard on 24/07/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa +import UIKit + +extension UIBarItem { + /// Wraps a UIBarItem's `enabled` state in a bindable property. + public var rex_enabled: MutableProperty { + return associatedProperty(self, &enabled, { self.enabled }, { self.enabled = $0 }) + } +} + +private var enabled: UInt8 = 0 From d7981a0d2c11a473661b3bad3509108adccb9683 Mon Sep 17 00:00:00 2001 From: Yury Lapitsky Date: Sat, 25 Jul 2015 23:43:40 +0300 Subject: [PATCH 0100/1028] NSTextField extension. Code zipping --- Source/AppKit/NSTextField.swift | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Source/AppKit/NSTextField.swift b/Source/AppKit/NSTextField.swift index 456a568fd4..bec7c84b14 100644 --- a/Source/AppKit/NSTextField.swift +++ b/Source/AppKit/NSTextField.swift @@ -13,10 +13,7 @@ import AppKit extension NSTextField { /// only changes from UI will be produced here public var rex_textSignal: SignalProducer { - let notificationCenter = NSNotificationCenter.defaultCenter() - let notificationsProducer = notificationCenter.rac_notifications(name: NSControlTextDidChangeNotification, object: self) - return notificationsProducer - |> filter { notification in (notification.object as? NSTextField) == self } + return NSNotificationCenter.defaultCenter().rac_notifications(name: NSControlTextDidChangeNotification, object: self) |> map { notification in (notification.object as! NSTextField).stringValue } } -} \ No newline at end of file +} From a68d6375a9df52440514a337e482ba3f04e08f38 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 12 Aug 2015 09:55:03 -0700 Subject: [PATCH 0101/1028] Xcode file sort --- Rex.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index a52f8dd739..5ad1e2a05f 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -208,9 +208,9 @@ D8F0974C1B190ECE002E15BA /* Property.swift */, D8003EBC1AFED01000D7D3C5 /* Signal.swift */, D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */, + 4238D5941B4D593E008534C0 /* AppKit */, D8F097391B17F2BF002E15BA /* Foundation */, D86FFBD31B34B0E2001A89B3 /* UIKit */, - 4238D5941B4D593E008534C0 /* AppKit */, D8003E911AFEC3D400D7D3C5 /* Supporting Files */, ); path = Source; @@ -282,8 +282,8 @@ D86FFBD31B34B0E2001A89B3 /* UIKit */ = { isa = PBXGroup; children = ( - 5173EBC51B625A2600C9B48E /* UIBarItem.swift */, 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */, + 5173EBC51B625A2600C9B48E /* UIBarItem.swift */, D86FFBDC1B34B691001A89B3 /* UIButton.swift */, D86FFBD41B34B0FE001A89B3 /* UIControl.swift */, D86FFBD71B34B242001A89B3 /* UILabel.swift */, From ab4000df174f3ce27b0ddeff5abe6d871b06dbab Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 12 Aug 2015 10:11:43 -0700 Subject: [PATCH 0102/1028] Update to RAC RC-1 --- Cartfile | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/ReactiveCocoa | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cartfile b/Cartfile index f0f99fad61..53758e0042 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveCocoa" ~> 3.0 +github "ReactiveCocoa/ReactiveCocoa" "v3.0-RC.1" diff --git a/Cartfile.resolved b/Cartfile.resolved index 1a6fbf29f2..b096828f02 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,3 +1,3 @@ github "robrix/Box" "1.2.2" github "antitypical/Result" "0.4.4" -github "ReactiveCocoa/ReactiveCocoa" "v3.0-beta.8" +github "ReactiveCocoa/ReactiveCocoa" "6c37b5ef416799a3a2a61ff78f051d5deb0d76b2" diff --git a/Carthage/Checkouts/ReactiveCocoa b/Carthage/Checkouts/ReactiveCocoa index b89e1b73f2..6c37b5ef41 160000 --- a/Carthage/Checkouts/ReactiveCocoa +++ b/Carthage/Checkouts/ReactiveCocoa @@ -1 +1 @@ -Subproject commit b89e1b73f2d1c6a99e0916f699ab6dcda71c74b5 +Subproject commit 6c37b5ef416799a3a2a61ff78f051d5deb0d76b2 From 80449b5c459bdfd480ceab077cda8fdb274a4608 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 12 Aug 2015 10:22:46 -0700 Subject: [PATCH 0103/1028] Code formatting tweaks --- Source/AppKit/NSTextField.swift | 7 +++++-- Source/Foundation/NSObject.swift | 2 +- Source/Foundation/NSUserDefaults.swift | 2 +- Source/Property.swift | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Source/AppKit/NSTextField.swift b/Source/AppKit/NSTextField.swift index bec7c84b14..879c52f80a 100644 --- a/Source/AppKit/NSTextField.swift +++ b/Source/AppKit/NSTextField.swift @@ -13,7 +13,10 @@ import AppKit extension NSTextField { /// only changes from UI will be produced here public var rex_textSignal: SignalProducer { - return NSNotificationCenter.defaultCenter().rac_notifications(name: NSControlTextDidChangeNotification, object: self) - |> map { notification in (notification.object as! NSTextField).stringValue } + return NSNotificationCenter.defaultCenter() + .rac_notifications(name: NSControlTextDidChangeNotification, object: self) + |> map { notification in + (notification.object as! NSTextField).stringValue + } } } diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index feb7a1258c..fe15119025 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -23,7 +23,7 @@ extension NSObject { // Errors aren't possible, but the compiler doesn't know that. assertionFailure("Unexpected error from KVO signal: \(error)") return .empty - } + } } /// Attaches a `MutableProperty` relying on KVC for the initial value and subsequent diff --git a/Source/Foundation/NSUserDefaults.swift b/Source/Foundation/NSUserDefaults.swift index 8f5ebb6b27..0a5d0c319a 100644 --- a/Source/Foundation/NSUserDefaults.swift +++ b/Source/Foundation/NSUserDefaults.swift @@ -31,6 +31,6 @@ extension NSUserDefaults { return previous == next } return false - } + } } } diff --git a/Source/Property.swift b/Source/Property.swift index 13492a38a3..350d1ade21 100644 --- a/Source/Property.swift +++ b/Source/Property.swift @@ -62,5 +62,5 @@ private func put(sink: S)(signal: Signal) -> Signa |> scan(sink) { (var value, change) in value.put(change) return value - } + } } From c49b24b71581af2fcae34e6c80494d388b1bd5e5 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 12 Aug 2015 10:36:38 -0700 Subject: [PATCH 0104/1028] Include UIBar* code in iOS target, not Mac --- Rex.xcodeproj/project.pbxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 5ad1e2a05f..bd0a99e480 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -8,8 +8,6 @@ /* Begin PBXBuildFile section */ 4238D5961B4D5950008534C0 /* NSTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4238D5951B4D5950008534C0 /* NSTextField.swift */; }; - 5173EBC61B625A2600C9B48E /* UIBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5173EBC51B625A2600C9B48E /* UIBarItem.swift */; }; - 5173EBC81B625A6800C9B48E /* UIBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */; }; 7DE995CB1B03A56800CA9420 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE995CA1B03A56800CA9420 /* Operators.swift */; }; 7DE995CC1B03A56800CA9420 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE995CA1B03A56800CA9420 /* Operators.swift */; }; D8003E941AFEC3D400D7D3C5 /* Rex.h in Headers */ = {isa = PBXBuildFile; fileRef = D8003E931AFEC3D400D7D3C5 /* Rex.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -46,6 +44,8 @@ D86FFBDA1B34B3F0001A89B3 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD91B34B3F0001A89B3 /* Action.swift */; }; D86FFBDB1B34B3F0001A89B3 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD91B34B3F0001A89B3 /* Action.swift */; }; D86FFBDD1B34B691001A89B3 /* UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBDC1B34B691001A89B3 /* UIButton.swift */; }; + D8E4A6201B7BBB1600EAD8A8 /* UIBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */; }; + D8E4A6211B7BBB2100EAD8A8 /* UIBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5173EBC51B625A2600C9B48E /* UIBarItem.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 */; }; @@ -492,8 +492,6 @@ 7DE995CB1B03A56800CA9420 /* Operators.swift in Sources */, D8003EBD1AFED01000D7D3C5 /* Signal.swift in Sources */, D8F097441B17F3C8002E15BA /* NSObject.swift in Sources */, - 5173EBC81B625A6800C9B48E /* UIBarButtonItem.swift in Sources */, - 5173EBC61B625A2600C9B48E /* UIBarItem.swift in Sources */, D8F0973B1B17F2F7002E15BA /* NSData.swift in Sources */, 4238D5961B4D5950008534C0 /* NSTextField.swift in Sources */, D8F0973D1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, @@ -521,6 +519,8 @@ D86FFBD21B34AD7A001A89B3 /* Association.swift in Sources */, D8F0973E1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, D834572D1AFEE45B0070616A /* Signal.swift in Sources */, + D8E4A6211B7BBB2100EAD8A8 /* UIBarItem.swift in Sources */, + D8E4A6201B7BBB1600EAD8A8 /* UIBarButtonItem.swift in Sources */, 7DE995CC1B03A56800CA9420 /* Operators.swift in Sources */, D8F097451B17F3C8002E15BA /* NSObject.swift in Sources */, D834572E1AFEE45B0070616A /* SignalProducer.swift in Sources */, From 6baa55d5e40093610e59c99e4e27d2712f7835c2 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 12 Aug 2015 10:36:55 -0700 Subject: [PATCH 0105/1028] Version 0.3.0 --- Source/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Info.plist b/Source/Info.plist index 0b184c4481..8a5ebb9d22 100644 --- a/Source/Info.plist +++ b/Source/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.2.3 + 0.3.0 CFBundleSignature ???? CFBundleVersion From d0882f6b472d423f042ec66c6eb67eb4a9d898ec Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Tue, 18 Aug 2015 18:26:19 -0700 Subject: [PATCH 0106/1028] Bump dependencies --- Cartfile | 2 +- Cartfile.resolved | 4 ++-- Carthage/Checkouts/ReactiveCocoa | 2 +- Carthage/Checkouts/Result | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cartfile b/Cartfile index f5bef83bd0..0a6c99df44 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveCocoa" "protocol-extensions" +github "ReactiveCocoa/ReactiveCocoa" "swift2" diff --git a/Cartfile.resolved b/Cartfile.resolved index 4bc025d71f..5f8245e87f 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ -github "antitypical/Result" "0.5" -github "ReactiveCocoa/ReactiveCocoa" "c32eb267a18dd8e396d8f1c8ee42497802480250" +github "antitypical/Result" "0.6-beta.1" +github "ReactiveCocoa/ReactiveCocoa" "5cb359c2ac17625ae9f4ce3ac955cc100d666fa6" diff --git a/Carthage/Checkouts/ReactiveCocoa b/Carthage/Checkouts/ReactiveCocoa index c32eb267a1..5cb359c2ac 160000 --- a/Carthage/Checkouts/ReactiveCocoa +++ b/Carthage/Checkouts/ReactiveCocoa @@ -1 +1 @@ -Subproject commit c32eb267a18dd8e396d8f1c8ee42497802480250 +Subproject commit 5cb359c2ac17625ae9f4ce3ac955cc100d666fa6 diff --git a/Carthage/Checkouts/Result b/Carthage/Checkouts/Result index 665c177914..18a10d680a 160000 --- a/Carthage/Checkouts/Result +++ b/Carthage/Checkouts/Result @@ -1 +1 @@ -Subproject commit 665c1779141d584d9e2f1d5570caaee9e905fd0e +Subproject commit 18a10d680af395d93b47ff4a3d8b0af3b8cad094 From bd738b9dfdce814331eeb03ac608c40c91d66786 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 19 Aug 2015 16:36:13 -0700 Subject: [PATCH 0107/1028] Remove SinkType methods Now that SinkType is gone these aren't needed --- Source/Property.swift | 31 ++----------------------------- Tests/PropertyTests.swift | 37 ------------------------------------- 2 files changed, 2 insertions(+), 66 deletions(-) diff --git a/Source/Property.swift b/Source/Property.swift index 67f5a9ab03..78a51733dc 100644 --- a/Source/Property.swift +++ b/Source/Property.swift @@ -35,43 +35,16 @@ public struct SignalProperty: PropertyType { } } -extension Signal where E: NoErrorType { +extension Signal where E: NoError { /// Creates a new property bound to `self` starting with `initialValue`. public func toProperty(initialValue: T) -> PropertyOf { return PropertyOf(SignalProperty(initialValue, ignoreError())) } - - /// Wraps `sink` in a property bound to `self`. Values sent on the signal are - /// `put` into the `sink` to update it. - public func toPropertySink(sink: S) -> PropertyOf { - return put(sink).toProperty(sink) - } } -extension SignalProducer where E: NoErrorType { +extension SignalProducer where E: NoError { /// Creates a new property bound to `producer` starting with `initialValue`. public func toProperty(initialValue: T) -> PropertyOf { return PropertyOf(SignalProperty(initialValue, ignoreError())) } - - /// Wraps `sink` in a property bound to `self`. Values sent on the signal are - /// `put` into the `sink` to update it. - public func toPropertySink(sink: S) -> PropertyOf { - return put(sink).toProperty(sink) - } -} - -extension Signal { - private func put(sink: S) -> Signal { - return scan(sink) { (var value, change) in - value.put(change) - return value - } - } -} - -extension SignalProducer { - private func put(sink: S) -> SignalProducer { - return lift { $0.put(sink) } - } } diff --git a/Tests/PropertyTests.swift b/Tests/PropertyTests.swift index 8f71dbf8ce..e169a16c27 100644 --- a/Tests/PropertyTests.swift +++ b/Tests/PropertyTests.swift @@ -39,41 +39,4 @@ final class PropertyTests: XCTestCase { property = nil XCTAssert(completed) } - - func testSignalPropertySink() { - let (signal, sink) = Signal.pipe() - - let property = signal.toPropertySink(Collector()) - XCTAssert(property.value.values == []) - - sendNext(sink, 1) - XCTAssert(property.value.values == [1]) - - sendNext(sink, 2) - sendNext(sink, 3) - XCTAssert(property.value.values == [1, 2, 3]) - } - - func testProducerPropertySink() { - let (producer, sink) = SignalProducer.buffer() - - let property = producer.toPropertySink(Collector(values: [])) - XCTAssert(property.value.values == []) - - sendNext(sink, 1) - XCTAssert(property.value.values == [1]) - - sendNext(sink, 2) - sendNext(sink, 3) - XCTAssert(property.value.values == [1, 2, 3]) - } -} - -struct Collector: SinkType { - typealias Element = T - var values: [T] = [] - - mutating func put(value: T) { - values.append(value) - } } From f6cc523c61aa315166d151870d12a6f98872e3dd Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 19 Aug 2015 16:53:10 -0700 Subject: [PATCH 0108/1028] Remove the custom send* operators While an interesting idea, I don't think these are useful in practice. Especially considering that the |> operator is going away in the version of RAC targetting Swift 2 which was a source of confusion for people. --- Rex.xcodeproj/project.pbxproj | 6 ---- Source/Operators.swift | 50 --------------------------------- Tests/SignalProducerTests.swift | 26 ++++++++--------- Tests/SignalTests.swift | 22 +++++++-------- 4 files changed, 24 insertions(+), 80 deletions(-) delete mode 100644 Source/Operators.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 40ee5f9d7e..2edb476ecc 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -7,8 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 7DE995CB1B03A56800CA9420 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE995CA1B03A56800CA9420 /* Operators.swift */; }; - 7DE995CC1B03A56800CA9420 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE995CA1B03A56800CA9420 /* Operators.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, ); }; }; @@ -87,7 +85,6 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 7DE995CA1B03A56800CA9420 /* Operators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Operators.swift; sourceTree = ""; }; D8003E921AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D8003E931AFEC3D400D7D3C5 /* Rex.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Rex.h; sourceTree = ""; }; D8003E9F1AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -163,7 +160,6 @@ D8003E901AFEC3D400D7D3C5 /* Source */ = { isa = PBXGroup; children = ( - 7DE995CA1B03A56800CA9420 /* Operators.swift */, D8F0974C1B190ECE002E15BA /* Property.swift */, D8003EBC1AFED01000D7D3C5 /* Signal.swift */, D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */, @@ -429,7 +425,6 @@ files = ( D8F0974D1B190ECE002E15BA /* Property.swift in Sources */, D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */, - 7DE995CB1B03A56800CA9420 /* Operators.swift in Sources */, D8003EBD1AFED01000D7D3C5 /* Signal.swift in Sources */, D8F097441B17F3C8002E15BA /* NSObject.swift in Sources */, D8F0973B1B17F2F7002E15BA /* NSData.swift in Sources */, @@ -455,7 +450,6 @@ D8F0974E1B190ECE002E15BA /* Property.swift in Sources */, D8F0973E1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, D834572D1AFEE45B0070616A /* Signal.swift in Sources */, - 7DE995CC1B03A56800CA9420 /* Operators.swift in Sources */, D8F097451B17F3C8002E15BA /* NSObject.swift in Sources */, D834572E1AFEE45B0070616A /* SignalProducer.swift in Sources */, D8F0973F1B17F31E002E15BA /* NSData.swift in Sources */, diff --git a/Source/Operators.swift b/Source/Operators.swift deleted file mode 100644 index 7882beaea5..0000000000 --- a/Source/Operators.swift +++ /dev/null @@ -1,50 +0,0 @@ -// -// Operators.swift -// Rex -// -// Created by Alexandros Salazar on 5/13/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - -import Foundation - -import Foundation -import ReactiveCocoa - -infix operator --> { - associativity left - - // bind as strong as assignment. - precedence 90 -} - -/// Sends the value as the next event. Usage: -/// -/// value --> sink -/// -/// - parameter value: the next value. -/// - parameter observer: the observer that will handle the value. -public func -->(value: T, sink: Signal.Observer) { - sendNext(sink, value) -} - -/// Sends the error as an error event. Usage: -/// -/// error --> sink -/// -/// - parameter error: the error to send. -/// - parameter observer: the observer that will handle the value. -public func -->(error: E, sink: Signal.Observer) { - sendError(sink, error) -} - -prefix operator --| {} - -/// Sends a completed event to the sink. Usage: -/// -/// --|sink -/// -/// - parameter observer: the sink to whcih the signal will be sent. -public prefix func --|(sink: Signal.Observer) { - return sendCompleted(sink) -} diff --git a/Tests/SignalProducerTests.swift b/Tests/SignalProducerTests.swift index 181ae5797b..160f488743 100644 --- a/Tests/SignalProducerTests.swift +++ b/Tests/SignalProducerTests.swift @@ -34,25 +34,25 @@ final class SignalProducerTests: XCTestCase { interrupted = true }) - 1 --> sink + sendNext(sink, 1) XCTAssert(evens == []) XCTAssert(odds == [1]) - 2 --> sink + sendNext(sink, 2) XCTAssert(evens == [2]) XCTAssert(odds == [1]) - 3 --> sink + sendNext(sink, 3) XCTAssert(evens == [2]) XCTAssert(odds == [1, 3]) - + disposable.dispose() - - 1 --> sink + + sendNext(sink, 1) XCTAssert(interrupted) XCTAssertFalse(completed) } - + func testCompletionOperator() { let (producer, sink) = SignalProducer.buffer() var evens: [Int] = [] @@ -60,7 +60,7 @@ final class SignalProducerTests: XCTestCase { let disposable = CompositeDisposable() var interrupted = false var completed = false - + disposable += producer .groupBy { $0 % 2 == 0 } .start(next: { key, group in @@ -74,13 +74,13 @@ final class SignalProducerTests: XCTestCase { }, interrupted: { interrupted = true }) - - 1 --> sink + + sendNext(sink, 1) XCTAssert(evens == []) XCTAssert(odds == [1]) - - --|sink + + sendCompleted(sink) XCTAssert(completed) XCTAssertFalse(interrupted) } -} \ No newline at end of file +} diff --git a/Tests/SignalTests.swift b/Tests/SignalTests.swift index e74f28af0f..9b17e6430a 100644 --- a/Tests/SignalTests.swift +++ b/Tests/SignalTests.swift @@ -22,16 +22,16 @@ final class SignalTests: XCTestCase { } .observe(next: { values.append($0) }) - 1 --> sink + sendNext(sink, 1) XCTAssert(values == []) - 2 --> sink + sendNext(sink, 2) XCTAssert(values == ["2"]) - 3 --> sink + sendNext(sink, 3) XCTAssert(values == ["2"]) - 6 --> sink + sendNext(sink, 6) XCTAssert(values == ["2", "6"]) } @@ -45,10 +45,10 @@ final class SignalTests: XCTestCase { completed = true }) - 1 --> sink + sendNext(sink, 1) XCTAssertFalse(completed) - .Default --> sink + sendError(sink, .Default) XCTAssertTrue(completed) } @@ -62,10 +62,10 @@ final class SignalTests: XCTestCase { interrupted = true }) - 1 --> sink + sendNext(sink, 1) XCTAssertFalse(interrupted) - .Default --> sink + sendError(sink, .Default) XCTAssertTrue(interrupted) } @@ -125,13 +125,13 @@ final class SignalTests: XCTestCase { values.append($0) }) - [] --> sink + sendNext(sink, []) XCTAssert(values.isEmpty) - [1] --> sink + sendNext(sink, [1]) XCTAssert(values == [1]) - [2, 3] --> sink + sendNext(sink, [2, 3]) XCTAssert(values == [1, 2, 3]) } } From f3099c2dff7cb9e19cf0c2763ab13347b29ab166 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 19 Aug 2015 17:38:38 -0700 Subject: [PATCH 0109/1028] Fix iOS for Swift 2 changes --- Source/UIKit/UIBarButtonItem.swift | 13 ++++++------- Source/UIKit/UIBarItem.swift | 2 +- Source/UIKit/UIButton.swift | 10 +++++----- Source/UIKit/UIControl.swift | 4 ++-- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/Source/UIKit/UIBarButtonItem.swift b/Source/UIKit/UIBarButtonItem.swift index ab04b546a9..66326362dc 100644 --- a/Source/UIKit/UIBarButtonItem.swift +++ b/Source/UIKit/UIBarButtonItem.swift @@ -15,17 +15,16 @@ extension UIBarButtonItem { /// overwritten. 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, &action, { _ in + return associatedObject(self, key: &action, initial: { _ in let initial = CocoaAction.rex_disabled let property = MutableProperty(initial) - property.producer - |> start { next in - self.target = next - self.action = CocoaAction.selector - } + property.producer.start(next: { next in + self.target = next + self.action = CocoaAction.selector + }) - self.rex_enabled <~ property.producer |> flatMap(.Latest) { $0.rex_enabledProducer } + self.rex_enabled <~ property.producer.flatMap(.Latest) { $0.rex_enabledProducer } return property }) } diff --git a/Source/UIKit/UIBarItem.swift b/Source/UIKit/UIBarItem.swift index 6006308dd0..d95e8a7737 100644 --- a/Source/UIKit/UIBarItem.swift +++ b/Source/UIKit/UIBarItem.swift @@ -12,7 +12,7 @@ import UIKit extension UIBarItem { /// Wraps a UIBarItem's `enabled` state in a bindable property. public var rex_enabled: MutableProperty { - return associatedProperty(self, &enabled, { self.enabled }, { self.enabled = $0 }) + return associatedProperty(self, key: &enabled, initial: { self.enabled }, setter: { self.enabled = $0 }) } } diff --git a/Source/UIKit/UIButton.swift b/Source/UIKit/UIButton.swift index eb9187cf3b..588b62d3dd 100644 --- a/Source/UIKit/UIButton.swift +++ b/Source/UIKit/UIButton.swift @@ -15,18 +15,18 @@ extension UIButton { /// previous action is removed as a target. This also binds the enabled state of the /// action to the `rex_enabled` property on the button. public var rex_pressed: MutableProperty { - return associatedObject(self, &pressed, { _ in + return associatedObject(self, key: &pressed, initial: { _ in let initial = CocoaAction.rex_disabled let property = MutableProperty(initial) property.producer - |> combinePrevious(initial) - |> start { previous, next in + .combinePrevious(initial) + .start(next: { previous, next in self.removeTarget(previous, action: CocoaAction.selector, forControlEvents: .TouchUpInside) self.addTarget(next, action: CocoaAction.selector, forControlEvents: .TouchUpInside) - } + }) - self.rex_enabled <~ property.producer |> flatMap(.Latest) { $0.rex_enabledProducer } + self.rex_enabled <~ property.producer.flatMap(.Latest) { $0.rex_enabledProducer } return property }) } diff --git a/Source/UIKit/UIControl.swift b/Source/UIKit/UIControl.swift index 0a40ab72fd..c036f576a8 100644 --- a/Source/UIKit/UIControl.swift +++ b/Source/UIKit/UIControl.swift @@ -14,8 +14,8 @@ extension UIControl { public func rex_controlEvents(events: UIControlEvents) -> SignalProducer { return rac_signalForControlEvents(events) .toSignalProducer() - |> map { $0 as? UIControl } - |> catch { _ in SignalProducer(value: nil) } + .map { $0 as? UIControl } + .flatMapError { _ in SignalProducer(value: nil) } } /// Wraps a control's `enabled` state in a bindable property. From 88325f4346a84a3bdbdd685fdd4f5657ad6276a7 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Thu, 20 Aug 2015 10:36:31 -0700 Subject: [PATCH 0110/1028] Add failing dealloc test for rex_text --- Rex.xcodeproj/project.pbxproj | 12 ++++++++++++ Tests/UIKit/UILabelTests.swift | 29 +++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 Tests/UIKit/UILabelTests.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index bd0a99e480..d851f5b31b 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -46,6 +46,7 @@ D86FFBDD1B34B691001A89B3 /* UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBDC1B34B691001A89B3 /* UIButton.swift */; }; 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 */; }; @@ -131,6 +132,7 @@ D86FFBD71B34B242001A89B3 /* UILabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabel.swift; sourceTree = ""; }; D86FFBD91B34B3F0001A89B3 /* Action.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Action.swift; sourceTree = ""; }; D86FFBDC1B34B691001A89B3 /* UIButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIButton.swift; sourceTree = ""; }; + D8F073141B861B3A0047D546 /* UILabelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabelTests.swift; sourceTree = ""; }; D8F0973A1B17F2F7002E15BA /* NSData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSData.swift; sourceTree = ""; }; D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSUserDefaults.swift; sourceTree = ""; }; D8F097431B17F3C8002E15BA /* NSObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSObject.swift; sourceTree = ""; }; @@ -232,6 +234,7 @@ D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */, D8003EC01AFED30100D7D3C5 /* SignalProducerTests.swift */, D8F097461B17F5BF002E15BA /* Foundation */, + D8F073131B861B110047D546 /* UIKit */, D8003E9E1AFEC3D400D7D3C5 /* Supporting Files */, ); path = Tests; @@ -291,6 +294,14 @@ path = UIKit; sourceTree = ""; }; + D8F073131B861B110047D546 /* UIKit */ = { + isa = PBXGroup; + children = ( + D8F073141B861B3A0047D546 /* UILabelTests.swift */, + ); + path = UIKit; + sourceTree = ""; + }; D8F097391B17F2BF002E15BA /* Foundation */ = { isa = PBXGroup; children = ( @@ -535,6 +546,7 @@ buildActionMask = 2147483647; files = ( D8F0974B1B17F5E2002E15BA /* NSObjectTests.swift in Sources */, + D8F073161B863CE70047D546 /* UILabelTests.swift in Sources */, D83457301AFEE45E0070616A /* SignalProducerTests.swift in Sources */, D8F097511B190F1A002E15BA /* PropertyTests.swift in Sources */, D83457411AFEE6050070616A /* SignalTests.swift in Sources */, diff --git a/Tests/UIKit/UILabelTests.swift b/Tests/UIKit/UILabelTests.swift new file mode 100644 index 0000000000..3124f96f80 --- /dev/null +++ b/Tests/UIKit/UILabelTests.swift @@ -0,0 +1,29 @@ +// +// UILabelTests.swift +// Rex +// +// Created by Neil Pankey on 8/20/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa +import UIKit +import XCTest + +class UILabelTests: XCTestCase { + + weak var _label: UILabel? + + override func tearDown() { + XCTAssert(_label == nil, "Retain cycle in bindings preventing dealloc") + super.tearDown() + } + + func testBindingsDealloc() { + let label = UILabel(frame: CGRectZero) + _label = label + + label.rex_text <~ SignalProducer(value: "Test") + XCTAssert(_label?.text == "Test") + } +} From 396177bef9ee3c53bfd374fc596eede38565c320 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Thu, 20 Aug 2015 10:51:48 -0700 Subject: [PATCH 0111/1028] Fix retain cycles in associated properties The `associatedProperty` helpers now weakly capture the host object to break the retain cycle when attaching the associated closure. --- Source/Foundation/Association.swift | 16 ++++++++++++---- Tests/UIKit/UILabelTests.swift | 4 ++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/Source/Foundation/Association.swift b/Source/Foundation/Association.swift index 6ab372ced3..387b48291d 100644 --- a/Source/Foundation/Association.swift +++ b/Source/Foundation/Association.swift @@ -17,8 +17,12 @@ import ReactiveCocoa /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. public func associatedProperty(host: AnyObject, keyPath: StaticString) -> MutableProperty { - let initial = { host.valueForKeyPath(keyPath.stringValue) as? String ?? "" } - let setter: String -> () = { host.setValue($0, forKeyPath: keyPath.stringValue) } + let initial: () -> String = { [weak host] _ in + host?.valueForKeyPath(keyPath.stringValue) as? String ?? "" + } + let setter: String -> () = { [weak host] newValue in + host?.setValue(newValue, forKeyPath: keyPath.stringValue) + } return associatedProperty(host, keyPath.utf8Start, initial, setter) } @@ -30,8 +34,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. public func associatedProperty(host: AnyObject, keyPath: StaticString, placeholder: () -> T) -> MutableProperty { - let initial = { host.valueForKeyPath(keyPath.stringValue) as? T ?? placeholder() } - let setter: T -> () = { host.setValue($0, forKeyPath: keyPath.stringValue) } + let initial: () -> T = { [weak host] _ in + host?.valueForKeyPath(keyPath.stringValue) as? T ?? placeholder() + } + let setter: T -> () = { [weak host] newValue in + host?.setValue(newValue, forKeyPath: keyPath.stringValue) + } return associatedProperty(host, keyPath.utf8Start, initial, setter) } diff --git a/Tests/UIKit/UILabelTests.swift b/Tests/UIKit/UILabelTests.swift index 3124f96f80..bf52f71cb4 100644 --- a/Tests/UIKit/UILabelTests.swift +++ b/Tests/UIKit/UILabelTests.swift @@ -15,11 +15,11 @@ class UILabelTests: XCTestCase { weak var _label: UILabel? override func tearDown() { - XCTAssert(_label == nil, "Retain cycle in bindings preventing dealloc") + XCTAssert(_label == nil, "Retain cycle detected in UILabel properties") super.tearDown() } - func testBindingsDealloc() { + func testPropertiesDontCreateRetainCycles() { let label = UILabel(frame: CGRectZero) _label = label From e7b8762739fc6c1316d074d310992f8d06fe7cf1 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Thu, 20 Aug 2015 10:56:39 -0700 Subject: [PATCH 0112/1028] Warn about retain cycles Document and warn about the potential for retain cycles when using the lower level associated object helpers. --- Source/Foundation/Association.swift | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Source/Foundation/Association.swift b/Source/Foundation/Association.swift index 387b48291d..9724602187 100644 --- a/Source/Foundation/Association.swift +++ b/Source/Foundation/Association.swift @@ -49,6 +49,9 @@ public func associatedProperty(host: AnyObject, keyPath: StaticStr /// /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. +/// +/// N.B. Ensure that `host` isn't strongly captured by `initial` or `setter`, otherwise this +/// will create a retain cycle with `host` causing it to never dealloc. public func associatedProperty(host: AnyObject, key: UnsafePointer<()>, initial: () -> T, setter: T -> ()) -> MutableProperty { return associatedObject(host, key) { let property = MutableProperty(initial()) @@ -57,9 +60,12 @@ public func associatedProperty(host: AnyObject, key: UnsafePointer<()>, initi } } -/// 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`. +/// On first use attaches the object returned from `initial` to the `host` object using +/// `key` via `objc_setAssociatedObject`. On subsequent usage, returns said object via +/// `objc_getAssociatedObject`. +/// +/// N.B. Ensure that `host` isn't strongly captured by `initial`, otherwise this will +/// create a retain cycle with `host` causing it to never dealloc. public func associatedObject(host: AnyObject, key: UnsafePointer<()>, initial: () -> T) -> T { var value = objc_getAssociatedObject(host, key) as? T if value == nil { From 6135b693fe27fb7caab5956fd422335485fe6319 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Thu, 20 Aug 2015 11:07:47 -0700 Subject: [PATCH 0113/1028] More retain cycle warnings --- Source/Foundation/Association.swift | 3 +++ Source/Foundation/NSObject.swift | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/Source/Foundation/Association.swift b/Source/Foundation/Association.swift index 9724602187..d8b7491f5d 100644 --- a/Source/Foundation/Association.swift +++ b/Source/Foundation/Association.swift @@ -33,6 +33,9 @@ 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. +/// +/// N.B. Ensure that `host` isn't strongly captured by `placeholder`, otherwise this will +/// create a retain cycle with `host` causing it to never dealloc. public func associatedProperty(host: AnyObject, keyPath: StaticString, placeholder: () -> T) -> MutableProperty { let initial: () -> T = { [weak host] _ in host?.valueForKeyPath(keyPath.stringValue) as? T ?? placeholder() diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index fe15119025..d5b4392959 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -42,6 +42,9 @@ extension NSObject { /// /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. + /// + /// N.B. Ensure that `self` isn't strongly captured by `placeholder`, otherwise this will + /// create a retain cycle causing `self` to never dealloc. public func rex_classProperty(host: AnyObject, keyPath: StaticString, placeholder: () -> T) -> MutableProperty { return associatedProperty(self, keyPath, placeholder) } @@ -52,6 +55,9 @@ extension NSObject { /// /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. + /// + /// N.B. Ensure that `self` isn't strongly captured by `initial` or `setter`, otherwise this + /// will create a retain cycle causing `self` to never dealloc. public func rex_valueProperty(key: UnsafePointer<()>, _ initial: () -> T, _ setter: T -> ()) -> MutableProperty { return associatedObject(self, key) { let property = MutableProperty(initial()) From a58f32cc3592b12352eee6b7e9dcd36802266056 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Thu, 20 Aug 2015 11:29:35 -0700 Subject: [PATCH 0114/1028] Add more retain cycle tests --- Source/Foundation/NSObject.swift | 2 +- Tests/Foundation/NSObjectTests.swift | 27 +++++++++++++++++++++++++++ Tests/UIKit/UILabelTests.swift | 4 ++-- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index d5b4392959..bfd35779fe 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -45,7 +45,7 @@ extension NSObject { /// /// N.B. Ensure that `self` isn't strongly captured by `placeholder`, otherwise this will /// create a retain cycle causing `self` to never dealloc. - public func rex_classProperty(host: AnyObject, keyPath: StaticString, placeholder: () -> T) -> MutableProperty { + public func rex_classProperty(keyPath: StaticString, placeholder: () -> T) -> MutableProperty { return associatedProperty(self, keyPath, placeholder) } diff --git a/Tests/Foundation/NSObjectTests.swift b/Tests/Foundation/NSObjectTests.swift index 7a22600fc6..80bc9c1e31 100644 --- a/Tests/Foundation/NSObjectTests.swift +++ b/Tests/Foundation/NSObjectTests.swift @@ -24,6 +24,33 @@ final class NSObjectTests: XCTestCase { } } +final class NSObjectDeallocTests: XCTestCase { + + weak var _object: Object? + + override func tearDown() { + XCTAssert(_object == nil, "Retain cycle detected") + super.tearDown() + } + + func testStringPropertyDoesntCreateRetainCycle() { + let object = Object() + _object = object + + object.rex_stringProperty("string") <~ SignalProducer(value: "Test") + XCTAssert(_object?.string == "Test") + } + + func testClassPropertyDoesntCreateRetainCycle() { + let object = Object() + _object = object + + object.rex_classProperty("string", placeholder: { _ in "" }) <~ SignalProducer(value: "Test") + XCTAssert(_object?.string == "Test") + } +} + + class Object: NSObject { dynamic var string = "foo" } diff --git a/Tests/UIKit/UILabelTests.swift b/Tests/UIKit/UILabelTests.swift index bf52f71cb4..f9355db9ed 100644 --- a/Tests/UIKit/UILabelTests.swift +++ b/Tests/UIKit/UILabelTests.swift @@ -10,7 +10,7 @@ import ReactiveCocoa import UIKit import XCTest -class UILabelTests: XCTestCase { +class UILabelDeallocTests: XCTestCase { weak var _label: UILabel? @@ -19,7 +19,7 @@ class UILabelTests: XCTestCase { super.tearDown() } - func testPropertiesDontCreateRetainCycles() { + func testTextPropertyDoesntCreateRetainCycle() { let label = UILabel(frame: CGRectZero) _label = label From da5c26e8f1a1f778c6d0a4044a33ce66ce0bba7f Mon Sep 17 00:00:00 2001 From: "Andy Jacobs (REVOLVER)" Date: Fri, 21 Aug 2015 12:47:42 +0200 Subject: [PATCH 0115/1028] add failing dealloc test for UIKit extensions --- Rex.xcodeproj/project.pbxproj | 12 ++++++++ Tests/UIKit/UIBarButtonItemTests.swift | 32 +++++++++++++++++++++ Tests/UIKit/UIButtonTests.swift | 39 ++++++++++++++++++++++++++ Tests/UIKit/UIControlTests.swift | 29 +++++++++++++++++++ 4 files changed, 112 insertions(+) create mode 100644 Tests/UIKit/UIBarButtonItemTests.swift create mode 100644 Tests/UIKit/UIButtonTests.swift create mode 100644 Tests/UIKit/UIControlTests.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index d851f5b31b..048f6682f0 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -10,6 +10,9 @@ 4238D5961B4D5950008534C0 /* NSTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4238D5951B4D5950008534C0 /* NSTextField.swift */; }; 7DE995CB1B03A56800CA9420 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE995CA1B03A56800CA9420 /* Operators.swift */; }; 7DE995CC1B03A56800CA9420 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE995CA1B03A56800CA9420 /* Operators.swift */; }; + 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 */; }; D8003E941AFEC3D400D7D3C5 /* Rex.h in Headers */ = {isa = PBXBuildFile; fileRef = D8003E931AFEC3D400D7D3C5 /* Rex.h */; settings = {ATTRIBUTES = (Public, ); }; }; D8003EB31AFEC6B000D7D3C5 /* Box.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003EAC1AFEC68A00D7D3C5 /* Box.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -110,6 +113,9 @@ 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 = ""; }; 7DE995CA1B03A56800CA9420 /* Operators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Operators.swift; sourceTree = ""; }; + 8295FD851B873081007C9000 /* UIControlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIControlTests.swift; sourceTree = ""; }; + 8295FD881B873490007C9000 /* UIButtonTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIButtonTests.swift; sourceTree = ""; }; + 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBarButtonItemTests.swift; sourceTree = ""; }; D8003E921AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D8003E931AFEC3D400D7D3C5 /* Rex.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Rex.h; sourceTree = ""; }; D8003E9F1AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -298,6 +304,9 @@ isa = PBXGroup; children = ( D8F073141B861B3A0047D546 /* UILabelTests.swift */, + 8295FD851B873081007C9000 /* UIControlTests.swift */, + 8295FD881B873490007C9000 /* UIButtonTests.swift */, + 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */, ); path = UIKit; sourceTree = ""; @@ -547,9 +556,12 @@ files = ( D8F0974B1B17F5E2002E15BA /* NSObjectTests.swift in Sources */, D8F073161B863CE70047D546 /* UILabelTests.swift in Sources */, + 8295FD8A1B87352D007C9000 /* UIButtonTests.swift in Sources */, D83457301AFEE45E0070616A /* SignalProducerTests.swift in Sources */, D8F097511B190F1A002E15BA /* PropertyTests.swift in Sources */, D83457411AFEE6050070616A /* SignalTests.swift in Sources */, + 8295FD8D1B87374A007C9000 /* UIBarButtonItemTests.swift in Sources */, + 8295FD871B87309F007C9000 /* UIControlTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Tests/UIKit/UIBarButtonItemTests.swift b/Tests/UIKit/UIBarButtonItemTests.swift new file mode 100644 index 0000000000..980fedd18e --- /dev/null +++ b/Tests/UIKit/UIBarButtonItemTests.swift @@ -0,0 +1,32 @@ +// +// UIBarButtonItemTests.swift +// Rex +// +// Created by Andy Jacobs on 21/08/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa +import UIKit +import XCTest + +class UIBarButtonItemTests: XCTestCase { + + weak var _barButtonItem: UIBarButtonItem? + + override func tearDown() { + XCTAssert(_barButtonItem == nil, "Retain cycle detected in UIBarButtonItem properties") + super.tearDown() + } + + func testEnabledPropertyDoesntCreateRetainCycle() { + let barButtonItem = UIBarButtonItem() + _barButtonItem = barButtonItem + + let action = Action<(),(),NoError> { + SignalProducer(value: ()) + } + barButtonItem.rex_action <~ SignalProducer(value: CocoaAction(action, input: ())) + } + +} diff --git a/Tests/UIKit/UIButtonTests.swift b/Tests/UIKit/UIButtonTests.swift new file mode 100644 index 0000000000..db6b4bc839 --- /dev/null +++ b/Tests/UIKit/UIButtonTests.swift @@ -0,0 +1,39 @@ +// +// UIButtonTests.swift +// Rex +// +// Created by Andy Jacobs on 21/08/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa +import UIKit +import XCTest + +class UIButtonTests: XCTestCase { + + weak var _button: UIButton? + + override func tearDown() { + XCTAssert(_button == nil, "Retain cycle detected in UIButton properties") + super.tearDown() + } + + func testEnabledPropertyDoesntCreateRetainCycle() { + let button = UIButton(frame: CGRectZero) + _button = button + + button.rex_enabled <~ SignalProducer(value: false) + XCTAssert(_button?.enabled == false) + + let action = Action<(),(),NoError> { + SignalProducer(value: ()) + } + button.rex_pressed <~ SignalProducer(value: CocoaAction(action, input: ())) + + button.rex_title <~ SignalProducer(value: "button") + XCTAssert(_button?.titleForState(.Normal) == "button") + + } + +} diff --git a/Tests/UIKit/UIControlTests.swift b/Tests/UIKit/UIControlTests.swift new file mode 100644 index 0000000000..622da0e21e --- /dev/null +++ b/Tests/UIKit/UIControlTests.swift @@ -0,0 +1,29 @@ +// +// UIControlTests.swift +// Rex +// +// Created by Andy Jacobs on 21/08/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa +import UIKit +import XCTest + +class UIControlDeallocTests: XCTestCase { + + weak var _control: UIControl? + + override func tearDown() { + XCTAssert(_control == nil, "Retain cycle detected in UIControl properties") + super.tearDown() + } + + func testEnabledPropertyDoesntCreateRetainCycle() { + let control = UIControl(frame: CGRectZero) + _control = control + + control.rex_enabled <~ SignalProducer(value: false) + XCTAssert(_control?.enabled == false) + } +} From 4f69461cf1d1a82a6fd3ff3f13f503823066ec8b Mon Sep 17 00:00:00 2001 From: "Andy Jacobs (REVOLVER)" Date: Fri, 21 Aug 2015 12:47:59 +0200 Subject: [PATCH 0116/1028] fix retain cycles in UIKit extension --- Source/UIKit/UIBarButtonItem.swift | 11 ++++++----- Source/UIKit/UIBarItem.swift | 2 +- Source/UIKit/UIButton.swift | 13 +++++++------ Source/UIKit/UIControl.swift | 2 +- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/Source/UIKit/UIBarButtonItem.swift b/Source/UIKit/UIBarButtonItem.swift index ab04b546a9..620fdd901f 100644 --- a/Source/UIKit/UIBarButtonItem.swift +++ b/Source/UIKit/UIBarButtonItem.swift @@ -15,17 +15,18 @@ extension UIBarButtonItem { /// overwritten. 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, &action, { _ in + return associatedObject(self, &action, { [weak self] _ in let initial = CocoaAction.rex_disabled let property = MutableProperty(initial) property.producer |> start { next in - self.target = next - self.action = CocoaAction.selector + self?.target = next + self?.action = CocoaAction.selector + } + if let strongSelf = self { + strongSelf.rex_enabled <~ property.producer |> flatMap(.Latest) { $0.rex_enabledProducer } } - - self.rex_enabled <~ property.producer |> flatMap(.Latest) { $0.rex_enabledProducer } return property }) } diff --git a/Source/UIKit/UIBarItem.swift b/Source/UIKit/UIBarItem.swift index 6006308dd0..eff7be6966 100644 --- a/Source/UIKit/UIBarItem.swift +++ b/Source/UIKit/UIBarItem.swift @@ -12,7 +12,7 @@ import UIKit extension UIBarItem { /// Wraps a UIBarItem's `enabled` state in a bindable property. public var rex_enabled: MutableProperty { - return associatedProperty(self, &enabled, { self.enabled }, { self.enabled = $0 }) + return associatedProperty(self, &enabled, { [weak self] in self?.enabled ?? true }, { [weak self] in self?.enabled = $0 }) } } diff --git a/Source/UIKit/UIButton.swift b/Source/UIKit/UIButton.swift index eb9187cf3b..68096803d9 100644 --- a/Source/UIKit/UIButton.swift +++ b/Source/UIKit/UIButton.swift @@ -15,18 +15,19 @@ extension UIButton { /// previous action is removed as a target. This also binds the enabled state of the /// action to the `rex_enabled` property on the button. public var rex_pressed: MutableProperty { - return associatedObject(self, &pressed, { _ in + return associatedObject(self, &pressed, { [weak self] _ in let initial = CocoaAction.rex_disabled let property = MutableProperty(initial) property.producer |> combinePrevious(initial) |> start { previous, next in - self.removeTarget(previous, action: CocoaAction.selector, forControlEvents: .TouchUpInside) - self.addTarget(next, action: CocoaAction.selector, forControlEvents: .TouchUpInside) + self?.removeTarget(previous, action: CocoaAction.selector, forControlEvents: .TouchUpInside) + self?.addTarget(next, action: CocoaAction.selector, forControlEvents: .TouchUpInside) } - - self.rex_enabled <~ property.producer |> flatMap(.Latest) { $0.rex_enabledProducer } + if let strongSelf = self { + strongSelf.rex_enabled <~ property.producer |> flatMap(.Latest) { $0.rex_enabledProducer } + } return property }) } @@ -34,7 +35,7 @@ extension UIButton { /// Wraps a button's `title` text in a bindable property. Note that this only applies /// to `UIControlState.Normal`. public var rex_title: MutableProperty { - return rex_valueProperty(&title, { self.titleForState(.Normal) ?? "" }, { self.setTitle($0, forState: .Normal) }) + return rex_valueProperty(&title, { [weak self] in self?.titleForState(.Normal) ?? "" }, { [weak self] in self?.setTitle($0, forState: .Normal) }) } } diff --git a/Source/UIKit/UIControl.swift b/Source/UIKit/UIControl.swift index 0a40ab72fd..942506c4e4 100644 --- a/Source/UIKit/UIControl.swift +++ b/Source/UIKit/UIControl.swift @@ -20,7 +20,7 @@ extension UIControl { /// Wraps a control's `enabled` state in a bindable property. public var rex_enabled: MutableProperty { - return rex_valueProperty(&enabled, { self.enabled }, { self.enabled = $0 }) + return rex_valueProperty(&enabled, { [weak self] in self?.enabled ?? true }, { [weak self] in self?.enabled = $0 }) } } From 18f4f76b2e3d1ce61d1c4a181143080ad7ffbbf6 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Fri, 21 Aug 2015 08:03:07 -0700 Subject: [PATCH 0117/1028] Organize UIKit tests --- Rex.xcodeproj/project.pbxproj | 6 +++--- Tests/UIKit/UIBarButtonItemTests.swift | 2 +- Tests/UIKit/UIButtonTests.swift | 15 ++++++++++++--- Tests/UIKit/UIControlTests.swift | 2 +- Tests/UIKit/UILabelTests.swift | 2 +- 5 files changed, 18 insertions(+), 9 deletions(-) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 048f6682f0..5a5592064e 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -303,10 +303,10 @@ D8F073131B861B110047D546 /* UIKit */ = { isa = PBXGroup; children = ( - D8F073141B861B3A0047D546 /* UILabelTests.swift */, - 8295FD851B873081007C9000 /* UIControlTests.swift */, - 8295FD881B873490007C9000 /* UIButtonTests.swift */, 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */, + 8295FD881B873490007C9000 /* UIButtonTests.swift */, + 8295FD851B873081007C9000 /* UIControlTests.swift */, + D8F073141B861B3A0047D546 /* UILabelTests.swift */, ); path = UIKit; sourceTree = ""; diff --git a/Tests/UIKit/UIBarButtonItemTests.swift b/Tests/UIKit/UIBarButtonItemTests.swift index 980fedd18e..2336d5ddca 100644 --- a/Tests/UIKit/UIBarButtonItemTests.swift +++ b/Tests/UIKit/UIBarButtonItemTests.swift @@ -19,7 +19,7 @@ class UIBarButtonItemTests: XCTestCase { super.tearDown() } - func testEnabledPropertyDoesntCreateRetainCycle() { + func testActionPropertyDoesntCreateRetainCycle() { let barButtonItem = UIBarButtonItem() _barButtonItem = barButtonItem diff --git a/Tests/UIKit/UIButtonTests.swift b/Tests/UIKit/UIButtonTests.swift index db6b4bc839..cfcf085da0 100644 --- a/Tests/UIKit/UIButtonTests.swift +++ b/Tests/UIKit/UIButtonTests.swift @@ -25,15 +25,24 @@ class UIButtonTests: XCTestCase { button.rex_enabled <~ SignalProducer(value: false) XCTAssert(_button?.enabled == false) - + } + + func testPressedPropertyDoesntCreateRetainCycle() { + let button = UIButton(frame: CGRectZero) + _button = button + let action = Action<(),(),NoError> { SignalProducer(value: ()) } button.rex_pressed <~ SignalProducer(value: CocoaAction(action, input: ())) - + } + + func testTitlePropertyDoesntCreateRetainCycle() { + let button = UIButton(frame: CGRectZero) + _button = button + button.rex_title <~ SignalProducer(value: "button") XCTAssert(_button?.titleForState(.Normal) == "button") - } } diff --git a/Tests/UIKit/UIControlTests.swift b/Tests/UIKit/UIControlTests.swift index 622da0e21e..6ffd472970 100644 --- a/Tests/UIKit/UIControlTests.swift +++ b/Tests/UIKit/UIControlTests.swift @@ -10,7 +10,7 @@ import ReactiveCocoa import UIKit import XCTest -class UIControlDeallocTests: XCTestCase { +class UIControlTests: XCTestCase { weak var _control: UIControl? diff --git a/Tests/UIKit/UILabelTests.swift b/Tests/UIKit/UILabelTests.swift index f9355db9ed..299f28415d 100644 --- a/Tests/UIKit/UILabelTests.swift +++ b/Tests/UIKit/UILabelTests.swift @@ -10,7 +10,7 @@ import ReactiveCocoa import UIKit import XCTest -class UILabelDeallocTests: XCTestCase { +class UILabelTests: XCTestCase { weak var _label: UILabel? From 16a86d4d216766f5f4a4a4734d22f3d5631b95c2 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Mon, 24 Aug 2015 11:14:22 -0700 Subject: [PATCH 0118/1028] Note Swift 1.2 vs 2 in the README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c48c83ada2..5aa6cde4be 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Rex [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) -Extensions for [ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa) that may not fit in the core framework. +Extensions for [ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa) that may not fit in the core framework. This currently targets Swift 1.2, for Swift 2 support see the [`swift2` branch](https://github.com/neilpa/Rex/tree/swift2). ## Signal All `Signal` operators can also be lifted to`SignalProducer`. From 6001594b565f1f661f8f1bc36b9abd7dcb6558c0 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 30 Aug 2015 11:13:58 -0700 Subject: [PATCH 0119/1028] Workaround compiler warning --- Source/Foundation/Association.swift | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Source/Foundation/Association.swift b/Source/Foundation/Association.swift index d8b7491f5d..1883fea8e9 100644 --- a/Source/Foundation/Association.swift +++ b/Source/Foundation/Association.swift @@ -17,11 +17,13 @@ import ReactiveCocoa /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. public func associatedProperty(host: AnyObject, keyPath: StaticString) -> MutableProperty { + // Workaround compiler warning when using keyPath.stringValue + let key = "\(keyPath)" let initial: () -> String = { [weak host] _ in - host?.valueForKeyPath(keyPath.stringValue) as? String ?? "" + host?.valueForKeyPath(key) as? String ?? "" } let setter: String -> () = { [weak host] newValue in - host?.setValue(newValue, forKeyPath: keyPath.stringValue) + host?.setValue(newValue, forKeyPath: key) } return associatedProperty(host, keyPath.utf8Start, initial, setter) } @@ -37,11 +39,13 @@ public func associatedProperty(host: AnyObject, keyPath: StaticString) -> Mutabl /// N.B. Ensure that `host` isn't strongly captured by `placeholder`, otherwise this will /// create a retain cycle with `host` causing it to never dealloc. public func associatedProperty(host: AnyObject, keyPath: StaticString, placeholder: () -> T) -> MutableProperty { + // Workaround compiler warning when using keyPath.stringValue + let key = "\(keyPath)" let initial: () -> T = { [weak host] _ in - host?.valueForKeyPath(keyPath.stringValue) as? T ?? placeholder() + host?.valueForKeyPath(key) as? T ?? placeholder() } let setter: T -> () = { [weak host] newValue in - host?.setValue(newValue, forKeyPath: keyPath.stringValue) + host?.setValue(newValue, forKeyPath: key) } return associatedProperty(host, keyPath.utf8Start, initial, setter) } From ae02278287a47e0269d493ba6b1607928deeda81 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 6 Sep 2015 14:59:03 -0700 Subject: [PATCH 0120/1028] Version 0.4 --- Source/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Info.plist b/Source/Info.plist index 8a5ebb9d22..55fcc1ee30 100644 --- a/Source/Info.plist +++ b/Source/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.3.0 + 0.4.0 CFBundleSignature ???? CFBundleVersion From 38c30bd19f340713d5008a731ef65389764db784 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 6 Sep 2015 15:54:07 -0700 Subject: [PATCH 0121/1028] Update RAC for beta 6 --- Cartfile.resolved | 2 +- Carthage/Checkouts/ReactiveCocoa | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cartfile.resolved b/Cartfile.resolved index 5f8245e87f..fdb7c99b72 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ github "antitypical/Result" "0.6-beta.1" -github "ReactiveCocoa/ReactiveCocoa" "5cb359c2ac17625ae9f4ce3ac955cc100d666fa6" +github "ReactiveCocoa/ReactiveCocoa" "5e75726bf232b76b17169b3d9c9c64d1fd6bffbe" diff --git a/Carthage/Checkouts/ReactiveCocoa b/Carthage/Checkouts/ReactiveCocoa index 5cb359c2ac..5e75726bf2 160000 --- a/Carthage/Checkouts/ReactiveCocoa +++ b/Carthage/Checkouts/ReactiveCocoa @@ -1 +1 @@ -Subproject commit 5cb359c2ac17625ae9f4ce3ac955cc100d666fa6 +Subproject commit 5e75726bf232b76b17169b3d9c9c64d1fd6bffbe From cfdc650c6c13c0a87df6748f8f75beca79662093 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 6 Sep 2015 15:55:06 -0700 Subject: [PATCH 0122/1028] Update for Xcode7 beta6 --- Source/SignalProducer.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index 50935f6f17..21ddcebfa7 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -57,7 +57,7 @@ extension SignalProducer { /// Returns a signal that prints the signal events public func print() -> SignalProducer { - return on(event: Swift.print) + return on(event: { Swift.print($0) }) } /// Returns a signal that prints the signal `Next` events From 603ea5c6bcf49af1a8a9b60b3d9a004cd4877470 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 6 Sep 2015 16:41:17 -0700 Subject: [PATCH 0123/1028] Code signing failz --- Rex.xcodeproj/project.pbxproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 3830eea2b0..d8a9a34682 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -737,7 +737,7 @@ isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_IDENTITY = "iPhone Developer"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; @@ -767,7 +767,7 @@ isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_IDENTITY = "iPhone Developer"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -815,7 +815,7 @@ D834572A1AFEE44E0070616A /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_IDENTITY = "iPhone Developer"; FRAMEWORK_SEARCH_PATHS = ( "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", From a45fd23a2e284aa89148bd28fee5476156778670 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 6 Sep 2015 16:43:00 -0700 Subject: [PATCH 0124/1028] Remove Swift 1.2 note from README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5aa6cde4be..c48c83ada2 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Rex [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) -Extensions for [ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa) that may not fit in the core framework. This currently targets Swift 1.2, for Swift 2 support see the [`swift2` branch](https://github.com/neilpa/Rex/tree/swift2). +Extensions for [ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa) that may not fit in the core framework. ## Signal All `Signal` operators can also be lifted to`SignalProducer`. From aa7b578489bd9b26d9dff44e87ba261910034cdb Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 6 Sep 2015 17:14:51 -0700 Subject: [PATCH 0125/1028] Unused map results to forEach --- Source/Signal.swift | 2 +- Source/SignalProducer.swift | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Signal.swift b/Source/Signal.swift index 699bd88ce6..c906e7f2f2 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -78,7 +78,7 @@ extension Signal where T: SequenceType { return self.observe { event in switch event { case let .Next(sequence): - sequence.map { sendNext(observer, $0) } + sequence.forEach { sendNext(observer, $0) } case let .Error(error): sendError(observer, error) case .Completed: diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index 21ddcebfa7..cc72cec997 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -37,15 +37,15 @@ extension SignalProducer { }, error: { error in sendError(observer, error) - groups.values.map { sendError($0, error) } + groups.values.forEach { sendError($0, error) } }, completed: { _ in sendCompleted(observer) - groups.values.map { sendCompleted($0) } + groups.values.forEach { sendCompleted($0) } }, interrupted: { _ in sendInterrupted(observer) - groups.values.map { sendInterrupted($0) } + groups.values.forEach { sendInterrupted($0) } }) } } From bf1a994f330582179167cf325385eaf31177f040 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Mon, 14 Sep 2015 11:59:05 -0700 Subject: [PATCH 0126/1028] Depend on explicit version of RAC4 Until there's an actual version of RAC4 don't want to point at the head of the branch because there will be ongoing breaking changes. --- Cartfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cartfile b/Cartfile index 0a6c99df44..93875d79ed 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveCocoa" "swift2" +github "ReactiveCocoa/ReactiveCocoa" "5e75726b" From 5e65f6bdfb51705cc8ce27440476050287f993b2 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Tue, 15 Sep 2015 12:36:07 +0900 Subject: [PATCH 0127/1028] Stop building test target on Run and Analyze action --- Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme | 4 ++-- Rex.xcodeproj/xcshareddata/xcschemes/Rex-iOS.xcscheme | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme index 2e13f8698b..f864d5ad26 100644 --- a/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme +++ b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme @@ -22,10 +22,10 @@ + buildForAnalyzing = "NO"> + buildForAnalyzing = "NO"> Date: Tue, 15 Sep 2015 12:47:48 +0900 Subject: [PATCH 0128/1028] Update to RAC 3.0 final --- Cartfile | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/ReactiveCocoa | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cartfile b/Cartfile index 53758e0042..f0f99fad61 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveCocoa" "v3.0-RC.1" +github "ReactiveCocoa/ReactiveCocoa" ~> 3.0 diff --git a/Cartfile.resolved b/Cartfile.resolved index b096828f02..29e03c2ca6 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,3 +1,3 @@ github "robrix/Box" "1.2.2" github "antitypical/Result" "0.4.4" -github "ReactiveCocoa/ReactiveCocoa" "6c37b5ef416799a3a2a61ff78f051d5deb0d76b2" +github "ReactiveCocoa/ReactiveCocoa" "v3.0.0" diff --git a/Carthage/Checkouts/ReactiveCocoa b/Carthage/Checkouts/ReactiveCocoa index 6c37b5ef41..e16f47cf9c 160000 --- a/Carthage/Checkouts/ReactiveCocoa +++ b/Carthage/Checkouts/ReactiveCocoa @@ -1 +1 @@ -Subproject commit 6c37b5ef416799a3a2a61ff78f051d5deb0d76b2 +Subproject commit e16f47cf9cb568136ebd81430b24af274c3c27c7 From 4c068e078fd30bcaed4d6a0a563eac4d51c1c198 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Tue, 15 Sep 2015 13:06:50 +0900 Subject: [PATCH 0129/1028] Remove Carthage/Build/iOS from FRAMEWORK_SEARCH_PATHS of Mac target --- Rex.xcodeproj/project.pbxproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 5a5592064e..0e3cc6bc5a 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -678,7 +678,6 @@ FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Carthage/Build/Mac", - "$(PROJECT_DIR)/Carthage/Build/iOS", ); FRAMEWORK_VERSION = A; INFOPLIST_FILE = Source/Info.plist; @@ -704,7 +703,6 @@ FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Carthage/Build/Mac", - "$(PROJECT_DIR)/Carthage/Build/iOS", ); FRAMEWORK_VERSION = A; INFOPLIST_FILE = Source/Info.plist; From 0fe0d3b0f11441a892d3d6faddb5c3ca164cd4e7 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Tue, 15 Sep 2015 13:01:35 -0700 Subject: [PATCH 0130/1028] Update to RAC 4 alpha.1 For now this also adds back the `start` and `observe` overloads that were removed. These are likely to come back to RAC 4 in some manner or another. --- Cartfile | 2 +- Cartfile.resolved | 4 ++-- Carthage/Checkouts/ReactiveCocoa | 2 +- Carthage/Checkouts/Result | 2 +- Source/Signal.swift | 19 +++++++++++++++++++ Source/SignalProducer.swift | 19 +++++++++++++++++++ 6 files changed, 43 insertions(+), 5 deletions(-) diff --git a/Cartfile b/Cartfile index 93875d79ed..de7d65cc69 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveCocoa" "5e75726b" +github "ReactiveCocoa/ReactiveCocoa" "v4.0-alpha.1" diff --git a/Cartfile.resolved b/Cartfile.resolved index fdb7c99b72..b30f487b94 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ -github "antitypical/Result" "0.6-beta.1" -github "ReactiveCocoa/ReactiveCocoa" "5e75726bf232b76b17169b3d9c9c64d1fd6bffbe" +github "antitypical/Result" "76f9a972ed61ff872b731460bc610c8acbfae58c" +github "ReactiveCocoa/ReactiveCocoa" "v4.0-alpha.1" diff --git a/Carthage/Checkouts/ReactiveCocoa b/Carthage/Checkouts/ReactiveCocoa index 5e75726bf2..8ebff4f192 160000 --- a/Carthage/Checkouts/ReactiveCocoa +++ b/Carthage/Checkouts/ReactiveCocoa @@ -1 +1 @@ -Subproject commit 5e75726bf232b76b17169b3d9c9c64d1fd6bffbe +Subproject commit 8ebff4f192ad11dfc62c55e8151c235d005f390b diff --git a/Carthage/Checkouts/Result b/Carthage/Checkouts/Result index 18a10d680a..76f9a972ed 160000 --- a/Carthage/Checkouts/Result +++ b/Carthage/Checkouts/Result @@ -1 +1 @@ -Subproject commit 18a10d680af395d93b47ff4a3d8b0af3b8cad094 +Subproject commit 76f9a972ed61ff872b731460bc610c8acbfae58c diff --git a/Source/Signal.swift b/Source/Signal.swift index c906e7f2f2..36851fa509 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -9,6 +9,25 @@ import ReactiveCocoa extension Signal { + + /// Bring back the `observe` overload. The `observeNext` or pattern matching + /// on `observe(Event)` is still annoying in practice and more verbose. This is + /// also likely to change in a later RAC 4 alpha. + public func observe(next next: (T -> ())? = nil, error: (E -> ())? = nil, completed: (() -> ())? = nil, interrupted: (() -> ())? = nil) -> Disposable? { + return self.observe { (event: Event) in + switch event { + case let .Next(value): + next?(value) + case let .Error(err): + error?(err) + case .Completed: + completed?() + case .Interrupted: + interrupted?() + } + } + } + /// Applies `transform` to values from `signal` with non-`nil` results unwrapped and /// forwared on the returned signal. public func filterMap(transform: T -> U?) -> Signal { diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index cc72cec997..0d5d2a0cf7 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -9,6 +9,25 @@ import ReactiveCocoa extension SignalProducer { + + /// Bring back the `start` overload. The `startNext` or pattern matching + /// on `start(Event)` is annoying in practice and more verbose. This is also + /// likely to change in a later RAC 4 alpha. + public func start(next next: (T -> ())? = nil, error: (E -> ())? = nil, completed: (() -> ())? = nil, interrupted: (() -> ())? = nil) -> Disposable? { + return self.start { (event: Event) in + switch event { + case let .Next(value): + next?(value) + case let .Error(err): + error?(err) + case .Completed: + completed?() + case .Interrupted: + interrupted?() + } + } + } + /// 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. From 3ebea03d08fb3af5c811dceb04f828c0c763403f Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Tue, 15 Sep 2015 18:51:43 -0700 Subject: [PATCH 0131/1028] Internalize start and observe overloads for now --- Source/Signal.swift | 2 +- Source/SignalProducer.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Signal.swift b/Source/Signal.swift index 36851fa509..4cff976d8a 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -13,7 +13,7 @@ extension Signal { /// Bring back the `observe` overload. The `observeNext` or pattern matching /// on `observe(Event)` is still annoying in practice and more verbose. This is /// also likely to change in a later RAC 4 alpha. - public func observe(next next: (T -> ())? = nil, error: (E -> ())? = nil, completed: (() -> ())? = nil, interrupted: (() -> ())? = nil) -> Disposable? { + internal func observe(next next: (T -> ())? = nil, error: (E -> ())? = nil, completed: (() -> ())? = nil, interrupted: (() -> ())? = nil) -> Disposable? { return self.observe { (event: Event) in switch event { case let .Next(value): diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index 0d5d2a0cf7..ef4046d42f 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -13,7 +13,7 @@ extension SignalProducer { /// Bring back the `start` overload. The `startNext` or pattern matching /// on `start(Event)` is annoying in practice and more verbose. This is also /// likely to change in a later RAC 4 alpha. - public func start(next next: (T -> ())? = nil, error: (E -> ())? = nil, completed: (() -> ())? = nil, interrupted: (() -> ())? = nil) -> Disposable? { + internal func start(next next: (T -> ())? = nil, error: (E -> ())? = nil, completed: (() -> ())? = nil, interrupted: (() -> ())? = nil) -> Disposable? { return self.start { (event: Event) in switch event { case let .Next(value): From 8997b9dd055221db2144ceb375b1e1b5f2b87da1 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Tue, 15 Sep 2015 19:14:11 -0700 Subject: [PATCH 0132/1028] @testable for start/observe overloads --- Tests/Foundation/NSObjectTests.swift | 2 +- Tests/PropertyTests.swift | 2 +- Tests/SignalProducerTests.swift | 2 +- Tests/SignalTests.swift | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/Foundation/NSObjectTests.swift b/Tests/Foundation/NSObjectTests.swift index 80bc9c1e31..1cb7ae1a90 100644 --- a/Tests/Foundation/NSObjectTests.swift +++ b/Tests/Foundation/NSObjectTests.swift @@ -6,7 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // -import Rex +@testable import Rex import ReactiveCocoa import XCTest diff --git a/Tests/PropertyTests.swift b/Tests/PropertyTests.swift index e169a16c27..1582082b29 100644 --- a/Tests/PropertyTests.swift +++ b/Tests/PropertyTests.swift @@ -6,7 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // -import Rex +@testable import Rex import ReactiveCocoa import XCTest diff --git a/Tests/SignalProducerTests.swift b/Tests/SignalProducerTests.swift index 160f488743..a06f063d29 100644 --- a/Tests/SignalProducerTests.swift +++ b/Tests/SignalProducerTests.swift @@ -6,7 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // -import Rex +@testable import Rex import ReactiveCocoa import XCTest diff --git a/Tests/SignalTests.swift b/Tests/SignalTests.swift index 9b17e6430a..70922db766 100644 --- a/Tests/SignalTests.swift +++ b/Tests/SignalTests.swift @@ -6,7 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // -import Rex +@testable import Rex import ReactiveCocoa import XCTest From 25268776295e6155a50226f77a56e1c66bc2d9b0 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Tue, 15 Sep 2015 19:44:06 -0700 Subject: [PATCH 0133/1028] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5aa6cde4be..9e457c358b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Rex [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) -Extensions for [ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa) that may not fit in the core framework. This currently targets Swift 1.2, for Swift 2 support see the [`swift2` branch](https://github.com/neilpa/Rex/tree/swift2). +Extensions for [ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa) that may not fit in the core framework. This currently targets Swift 1.2/RAC 3, for Swift 2/RAC 4 support see the [`swift2` branch](https://github.com/neilpa/Rex/tree/swift2). ## Signal All `Signal` operators can also be lifted to`SignalProducer`. From 481bc0a2d29359e3b7a6c9d293c862ef69cf39af Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 20 Sep 2015 16:15:58 -0700 Subject: [PATCH 0134/1028] Ugh codesigning --- Rex.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 0e3cc6bc5a..509dd63d9d 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -805,7 +805,7 @@ D83457291AFEE44E0070616A /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_IDENTITY = "iPhone Developer"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; FRAMEWORK_SEARCH_PATHS = ( "$(SDKROOT)/Developer/Library/Frameworks", @@ -827,7 +827,7 @@ D834572A1AFEE44E0070616A /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_IDENTITY = "iPhone Developer"; FRAMEWORK_SEARCH_PATHS = ( "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", From b8447be2df04657d1d5e9791af63e861d14c2010 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 20 Sep 2015 16:16:42 -0700 Subject: [PATCH 0135/1028] Version 0.5 --- Source/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Info.plist b/Source/Info.plist index 55fcc1ee30..37ae6406fc 100644 --- a/Source/Info.plist +++ b/Source/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.4.0 + 0.5.0 CFBundleSignature ???? CFBundleVersion From 0c85b272488cf8d505a82f4b2908f306dd0a23d3 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 20 Sep 2015 17:00:19 -0700 Subject: [PATCH 0136/1028] Remove testCompletionOperator The operator was removed so this isn't testing anything --- Tests/SignalProducerTests.swift | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/Tests/SignalProducerTests.swift b/Tests/SignalProducerTests.swift index a06f063d29..a3471889a4 100644 --- a/Tests/SignalProducerTests.swift +++ b/Tests/SignalProducerTests.swift @@ -52,35 +52,4 @@ final class SignalProducerTests: XCTestCase { XCTAssert(interrupted) XCTAssertFalse(completed) } - - func testCompletionOperator() { - let (producer, sink) = SignalProducer.buffer() - var evens: [Int] = [] - var odds: [Int] = [] - let disposable = CompositeDisposable() - var interrupted = false - var completed = false - - disposable += producer - .groupBy { $0 % 2 == 0 } - .start(next: { key, group in - if key { - group.start(next: { evens.append($0) }) - } else { - group.start(next: { odds.append($0) }) - } - },completed: { - completed = true - }, interrupted: { - interrupted = true - }) - - sendNext(sink, 1) - XCTAssert(evens == []) - XCTAssert(odds == [1]) - - sendCompleted(sink) - XCTAssert(completed) - XCTAssertFalse(interrupted) - } } From 6d1ce5a5a6286af39416f6073ad882dbde7fceb7 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 20 Sep 2015 17:19:48 -0700 Subject: [PATCH 0137/1028] Explicitly lift missing producer operators --- Source/SignalProducer.swift | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index ef4046d42f..6e5a41c544 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -69,11 +69,28 @@ extension SignalProducer { } } - /// Returns a signal that drops `Error` events, replacing them with `Completed`. - public func ignoreError(replacement: Event = .Completed) -> SignalProducer { + /// Applies `transform` to values from self with non-`nil` results unwrapped and + /// forwared on the returned producer. + public func filterMap(transform: T -> U?) -> SignalProducer { + return lift { $0.filterMap(transform) } + } + + /// Returns a producer that drops `Error` sending `replacement` terminal event + /// instead, defaulting to `Completed`. + public func ignoreError(replacement replacement: Event = .Completed) -> SignalProducer { + precondition(replacement.isTerminating) return lift { $0.ignoreError(replacement: replacement) } } + /// Forwards events from self until `interval`. Then if producer isn't completed yet, + /// terminates with `event` on `scheduler`. + /// + /// 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. + public func timeoutAfter(interval: NSTimeInterval, withEvent event: Event, onScheduler scheduler: DateSchedulerType) -> SignalProducer { + return lift { $0.timeoutAfter(interval, withEvent: event, onScheduler: scheduler) } + } + /// Returns a signal that prints the signal events public func print() -> SignalProducer { return on(event: { Swift.print($0) }) @@ -97,3 +114,11 @@ extension SignalProducer { }) } } + +extension SignalProducer where T: SequenceType { + /// Returns a producer that flattens sequences of elements. The inverse of `collect`. + public func uncollect() -> SignalProducer { + return lift { $0.uncollect() } + } +} + From 473a6d31ea2ec4ebb5bd6d0b1bda515bf9db7bd4 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 20 Sep 2015 17:20:58 -0700 Subject: [PATCH 0138/1028] Protocol extensions instead of concrete ones --- Source/Signal.swift | 4 ++-- Source/SignalProducer.swift | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Signal.swift b/Source/Signal.swift index 4cff976d8a..4e2297a253 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -8,7 +8,7 @@ import ReactiveCocoa -extension Signal { +extension SignalType { /// Bring back the `observe` overload. The `observeNext` or pattern matching /// on `observe(Event)` is still annoying in practice and more verbose. This is @@ -90,7 +90,7 @@ extension Signal { } } -extension Signal where T: SequenceType { +extension SignalType where T: SequenceType { /// Returns a signal that flattens sequences of elements. The inverse of `collect`. public func uncollect() -> Signal { return Signal { observer in diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index 6e5a41c544..4f64073a1a 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -8,7 +8,7 @@ import ReactiveCocoa -extension SignalProducer { +extension SignalProducerType { /// Bring back the `start` overload. The `startNext` or pattern matching /// on `start(Event)` is annoying in practice and more verbose. This is also @@ -115,7 +115,7 @@ extension SignalProducer { } } -extension SignalProducer where T: SequenceType { +extension SignalProducerType where T: SequenceType { /// Returns a producer that flattens sequences of elements. The inverse of `collect`. public func uncollect() -> SignalProducer { return lift { $0.uncollect() } From 00d35accad3598ca3ee38d3c210ffb3ee70d59a6 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 20 Sep 2015 17:22:07 -0700 Subject: [PATCH 0139/1028] Remove print operators Honestly, these are trivial to add when needed for debugging and don't need to be here. --- Source/SignalProducer.swift | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index 4f64073a1a..bccd8a406a 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -90,29 +90,6 @@ extension SignalProducerType { public func timeoutAfter(interval: NSTimeInterval, withEvent event: Event, onScheduler scheduler: DateSchedulerType) -> SignalProducer { return lift { $0.timeoutAfter(interval, withEvent: event, onScheduler: scheduler) } } - - /// Returns a signal that prints the signal events - public func print() -> SignalProducer { - return on(event: { Swift.print($0) }) - } - - /// Returns a signal that prints the signal `Next` events - public func printNext() -> SignalProducer { - return on(event: { event in - if case .Next(_) = event { - Swift.print(event) - } - }) - } - - /// Returns a signal that prints the signal `Error` events - public func printError() -> SignalProducer { - return on(event: { event in - if case .Error(_) = event { - Swift.print(event) - } - }) - } } extension SignalProducerType where T: SequenceType { From b3694fb8013adaa108fd76c0386c7f22d7dcb6d7 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 20 Sep 2015 17:32:56 -0700 Subject: [PATCH 0140/1028] Update signal and producer README docs --- README.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 735d3239ef..a775f87b4a 100644 --- a/README.md +++ b/README.md @@ -4,36 +4,35 @@ Extensions for [ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa) t New development targets RAC 4/Swift 2/Xcode 7. For RAC 3 support [see the 0.5 release](https://github.com/neilpa/Rex/releases/tag/v0.5.0). ## Signal -All `Signal` operators can also be lifted to`SignalProducer`. +All `Signal` operators are available for `SignaProducer`s too via explicit `lift`ing. ##### `filterMap` -Applies `transform` to values from `signal` with non-nil results unwrapped and forwared on the returned signal. This is equivalent to `map { … } |> filter { $0 != nil } |> map { $0! }` but without creating extra intermediate signals. +Applies `transform` to values from `signal` with non-nil results unwrapped and forwared on the returned signal. This is equivalent to `map { … } .filter { $0 != nil } .map { $0! }` but without creating extra intermediate signals. ```swift -func filterMap(transform: T -> U?)(signal: Signal) -> Signal +func filterMap(transform: T -> U?) -> Signal ``` ##### `ignoreError` Wraps a `signal` in a version that drops `Error` events. By default errors are replaced with a `Completed` event but `Interrupted` can also be specified as `replacement`. ```swift -func ignoreError(signal: Signal) -> Signal -func ignoreError(#replacement: Event)(signal: Signal) -> Signal +func ignoreError(#replacement: Event = .Completed) -> Signal ``` ##### `timeoutAfter` Forwards events from `signal` until it terminates or until `interval` time passes. This is nearly identical to `timeoutWithError` from RAC except any terminating `event` can be used for the timeout. ```swift -func timeoutAfter(interval: NSTimeInterval, withEvent event: Event, onScheduler scheduler: DateSchedulerType) -> Signal -> Signal +func timeoutAfter(interval: NSTimeInterval, withEvent event: Event, onScheduler scheduler: DateSchedulerType) -> Signal ``` ##### `uncollect` -Flattens batches of elements sent on `signal` into each individual element. The inverse of `collect`. +Flattens batches of elements sent on `signal` into each individual element. The inverse of `collect`. Requires signal values to conform to `SequenceType`. ```swift -func uncollect(signal: Signal) -> Signal +func uncollect() -> Signal ``` @@ -44,8 +43,7 @@ Operators specific to `SignalProducer`. Partitions values from `producer` into new producer groups based on the key returned from `grouping`. Termination events on the original producer are forwarded to each inner producer group. ```swift -func groupBy(grouping: T -> K)(producer: SignalProducer) - -> SignalProducer<(K, SignalProducer), E> +func groupBy(grouping: T -> K) -> SignalProducer<(K, SignalProducer), E> ``` From d42c0c2d3fcd92caf80e816f38df0484d5a8215a Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 20 Sep 2015 17:39:33 -0700 Subject: [PATCH 0141/1028] Delete SignalProperty et. al. These are superceded by the `PropertyOf` initializers in RAC proper taking signal and producer. --- README.md | 20 -------------- Rex.xcodeproj/project.pbxproj | 12 --------- Source/Property.swift | 50 ----------------------------------- Tests/PropertyTests.swift | 42 ----------------------------- 4 files changed, 124 deletions(-) delete mode 100644 Source/Property.swift delete mode 100644 Tests/PropertyTests.swift diff --git a/README.md b/README.md index a775f87b4a..d1ee2be111 100644 --- a/README.md +++ b/README.md @@ -47,25 +47,5 @@ func groupBy(grouping: T -> K) -> SignalProducer<(K, SignalProducer ``` -## Property -Extensions for creating properties from signals. These are curried to support chaining with `|>`. - -##### `propertyOf` -Creates a new property bound to the provided signal/producer starting with `initialValue`. - -```swift -func propertyOf(initialValue: T)(signal: Signal) -> PropertyOf -func propertyOf(initialValue: T)(producer: SignalProducer) -> PropertyOf -``` - -##### `propertySink` -Wraps `sink` in a property bound to the provided signal/producer. Values sent on `signal` are `put` into the `sink` to update it. - -```swift -func propertySink(sink: S)(signal: Signal) -> PropertyOf -func propertySink(sink: S)(producer: SignalProducer) -> PropertyOf -``` - - ## License Rex is released under the [MIT license](LICENSE) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 8c40869e3a..3388f01945 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -50,10 +50,6 @@ D8F097451B17F3C8002E15BA /* NSObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097431B17F3C8002E15BA /* NSObject.swift */; }; D8F0974A1B17F5E1002E15BA /* NSObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097471B17F5DD002E15BA /* NSObjectTests.swift */; }; D8F0974B1B17F5E2002E15BA /* NSObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097471B17F5DD002E15BA /* NSObjectTests.swift */; }; - D8F0974D1B190ECE002E15BA /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0974C1B190ECE002E15BA /* Property.swift */; }; - D8F0974E1B190ECE002E15BA /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0974C1B190ECE002E15BA /* Property.swift */; }; - D8F097501B190F1A002E15BA /* PropertyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0974F1B190F1A002E15BA /* PropertyTests.swift */; }; - D8F097511B190F1A002E15BA /* PropertyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0974F1B190F1A002E15BA /* PropertyTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -130,8 +126,6 @@ D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSUserDefaults.swift; sourceTree = ""; }; D8F097431B17F3C8002E15BA /* NSObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSObject.swift; sourceTree = ""; }; D8F097471B17F5DD002E15BA /* NSObjectTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSObjectTests.swift; sourceTree = ""; }; - D8F0974C1B190ECE002E15BA /* Property.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Property.swift; sourceTree = ""; }; - D8F0974F1B190F1A002E15BA /* PropertyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PropertyTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -195,7 +189,6 @@ isa = PBXGroup; children = ( D86FFBD91B34B3F0001A89B3 /* Action.swift */, - D8F0974C1B190ECE002E15BA /* Property.swift */, D8003EBC1AFED01000D7D3C5 /* Signal.swift */, D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */, 4238D5941B4D593E008534C0 /* AppKit */, @@ -218,7 +211,6 @@ D8003E9D1AFEC3D400D7D3C5 /* Tests */ = { isa = PBXGroup; children = ( - D8F0974F1B190F1A002E15BA /* PropertyTests.swift */, D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */, D8003EC01AFED30100D7D3C5 /* SignalProducerTests.swift */, D8F097461B17F5BF002E15BA /* Foundation */, @@ -485,7 +477,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D8F0974D1B190ECE002E15BA /* Property.swift in Sources */, D86FFBDA1B34B3F0001A89B3 /* Action.swift in Sources */, D86FFBD11B34AD6F001A89B3 /* Association.swift in Sources */, D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */, @@ -503,7 +494,6 @@ files = ( D8F0974A1B17F5E1002E15BA /* NSObjectTests.swift in Sources */, D8003EC21AFED30F00D7D3C5 /* SignalTests.swift in Sources */, - D8F097501B190F1A002E15BA /* PropertyTests.swift in Sources */, D8003EC31AFED30F00D7D3C5 /* SignalProducerTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -514,7 +504,6 @@ files = ( D86FFBD81B34B242001A89B3 /* UILabel.swift in Sources */, D86FFBDB1B34B3F0001A89B3 /* Action.swift in Sources */, - D8F0974E1B190ECE002E15BA /* Property.swift in Sources */, D86FFBD21B34AD7A001A89B3 /* Association.swift in Sources */, D8F0973E1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, D834572D1AFEE45B0070616A /* Signal.swift in Sources */, @@ -536,7 +525,6 @@ D8F073161B863CE70047D546 /* UILabelTests.swift in Sources */, 8295FD8A1B87352D007C9000 /* UIButtonTests.swift in Sources */, D83457301AFEE45E0070616A /* SignalProducerTests.swift in Sources */, - D8F097511B190F1A002E15BA /* PropertyTests.swift in Sources */, D83457411AFEE6050070616A /* SignalTests.swift in Sources */, 8295FD8D1B87374A007C9000 /* UIBarButtonItemTests.swift in Sources */, 8295FD871B87309F007C9000 /* UIControlTests.swift in Sources */, diff --git a/Source/Property.swift b/Source/Property.swift deleted file mode 100644 index 78a51733dc..0000000000 --- a/Source/Property.swift +++ /dev/null @@ -1,50 +0,0 @@ -// -// Property.swift -// Rex -// -// Created by Neil Pankey on 5/29/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - -import ReactiveCocoa - -/// A property that tracks a signals most recent value. -public struct SignalProperty: PropertyType { - private let property: MutableProperty - - /// Current value of the property. - public var value: T { - return property.value - } - - /// Sends the current `value` and any changes. - public var producer: SignalProducer { - return property.producer - } - - /// Creates a new property bound to `signal`. - public init(_ value: T, _ signal: Signal) { - property = MutableProperty(value) - property <~ signal - } - - /// Creates a new property bound to `producer`. - public init(_ value: T, _ producer: SignalProducer) { - property = MutableProperty(value) - property <~ producer - } -} - -extension Signal where E: NoError { - /// Creates a new property bound to `self` starting with `initialValue`. - public func toProperty(initialValue: T) -> PropertyOf { - return PropertyOf(SignalProperty(initialValue, ignoreError())) - } -} - -extension SignalProducer where E: NoError { - /// Creates a new property bound to `producer` starting with `initialValue`. - public func toProperty(initialValue: T) -> PropertyOf { - return PropertyOf(SignalProperty(initialValue, ignoreError())) - } -} diff --git a/Tests/PropertyTests.swift b/Tests/PropertyTests.swift deleted file mode 100644 index 1582082b29..0000000000 --- a/Tests/PropertyTests.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// PropertyTests.swift -// Rex -// -// Created by Neil Pankey on 5/29/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - -@testable import Rex -import ReactiveCocoa -import XCTest - -final class PropertyTests: XCTestCase { - - func testSignalPropertyValues() { - let (signal, sink) = Signal.pipe() - let property = SignalProperty(0, signal) - - var latest = -1 - property.producer.start(next: { - latest = $0 - }) - - XCTAssert(latest == 0) - - sendNext(sink, 1) - XCTAssert(latest == 1) - } - - func testSignalPropertyLifetime() { - let (signal, _) = Signal.pipe() - var property: SignalProperty? = SignalProperty(0, signal) - - var completed = false - property?.producer.start(completed: { - completed = true - }) - - property = nil - XCTAssert(completed) - } -} From 445136aeba6e61194d5d7fa8518619d4b8e4ca12 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 20 Sep 2015 17:50:08 -0700 Subject: [PATCH 0142/1028] Version 0.8 First version targeting RAC 4. Skipping a few versions from 0.5 just in case there needs to be a RAC 3 update requiring breaking changes. --- Source/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Info.plist b/Source/Info.plist index 45035690ee..12b09b1221 100644 --- a/Source/Info.plist +++ b/Source/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.5.0 + 0.8.0 CFBundleSignature ???? CFBundleVersion From d1499722acba7d21b4c75bcfda96471101cee664 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Mon, 21 Sep 2015 16:26:26 +0900 Subject: [PATCH 0143/1028] Make some extension methods public --- Source/Action.swift | 2 +- Source/Foundation/NSData.swift | 2 +- Source/Foundation/NSUserDefaults.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Action.swift b/Source/Action.swift index 972ca8a783..9dda3379b2 100644 --- a/Source/Action.swift +++ b/Source/Action.swift @@ -10,7 +10,7 @@ import ReactiveCocoa extension Action { /// Creates an always disabled action. - static var rex_disabled: Action { + public static var rex_disabled: Action { return Action(enabledIf: ConstantProperty(false)) { _ in .empty } } } diff --git a/Source/Foundation/NSData.swift b/Source/Foundation/NSData.swift index f1384770dc..d445ccd2a3 100644 --- a/Source/Foundation/NSData.swift +++ b/Source/Foundation/NSData.swift @@ -12,7 +12,7 @@ import ReactiveCocoa extension NSData { /// Read the data at the URL. /// Sends the data or the error. - class func rex_dataWithContentsOfURL(url: NSURL, options: NSDataReadingOptions = NSDataReadingOptions()) -> SignalProducer { + public class func rex_dataWithContentsOfURL(url: NSURL, options: NSDataReadingOptions = NSDataReadingOptions()) -> SignalProducer { return SignalProducer { observer, disposable in do { let data = try NSData(contentsOfURL: url, options: options) diff --git a/Source/Foundation/NSUserDefaults.swift b/Source/Foundation/NSUserDefaults.swift index 7ea1848a81..65dd33a646 100644 --- a/Source/Foundation/NSUserDefaults.swift +++ b/Source/Foundation/NSUserDefaults.swift @@ -14,7 +14,7 @@ extension NSUserDefaults { /// by casting to NSObject and checking for equality. If the values aren't /// convertible this will generate events whenever _any_ value in NSUserDefaults /// changes. - func rex_valueForKey(key: String) -> SignalProducer { + public func rex_valueForKey(key: String) -> SignalProducer { let center = NSNotificationCenter.defaultCenter() let initial = objectForKey(key) From f4ee8f737f6aa86b87f5c285a2524f5381278f25 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Mon, 21 Sep 2015 16:44:17 +0900 Subject: [PATCH 0144/1028] Remove workarounds for compiler warning about StaticString The compiler warning disappears in Xcode 7. --- Source/Foundation/Association.swift | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Source/Foundation/Association.swift b/Source/Foundation/Association.swift index 3b5e2d9abb..f301bbfd68 100644 --- a/Source/Foundation/Association.swift +++ b/Source/Foundation/Association.swift @@ -17,13 +17,11 @@ import ReactiveCocoa /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. public func associatedProperty(host: AnyObject, keyPath: StaticString) -> MutableProperty { - // Workaround compiler warning when using keyPath.stringValue - let key = "\(keyPath)" let initial: () -> String = { [weak host] _ in - host?.valueForKeyPath(key) as? String ?? "" + host?.valueForKeyPath(keyPath.stringValue) as? String ?? "" } let setter: String -> () = { [weak host] newValue in - host?.setValue(newValue, forKeyPath: key) + host?.setValue(newValue, forKeyPath: keyPath.stringValue) } return associatedProperty(host, key: keyPath.utf8Start, initial: initial, setter: setter) } @@ -39,13 +37,11 @@ public func associatedProperty(host: AnyObject, keyPath: StaticString) -> Mutabl /// N.B. Ensure that `host` isn't strongly captured by `placeholder`, otherwise this will /// create a retain cycle with `host` causing it to never dealloc. public func associatedProperty(host: AnyObject, keyPath: StaticString, placeholder: () -> T) -> MutableProperty { - // Workaround compiler warning when using keyPath.stringValue - let key = "\(keyPath)" let initial: () -> T = { [weak host] _ in - host?.valueForKeyPath(key) as? T ?? placeholder() + host?.valueForKeyPath(keyPath.stringValue) as? T ?? placeholder() } let setter: T -> () = { [weak host] newValue in - host?.setValue(newValue, forKeyPath: key) + host?.setValue(newValue, forKeyPath: keyPath.stringValue) } return associatedProperty(host, key: keyPath.utf8Start, initial: initial, setter: setter) } From d498d057472942416894fcd3804b83fd5bb071ea Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Mon, 21 Sep 2015 17:03:15 +0900 Subject: [PATCH 0145/1028] Add Carthage/Build to .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 8060b61ebc..95ddb50814 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ xcuserdata *.xccheckout *.mode* *.pbxuser + +Carthage/Build From 06a42ff3cf5f79749c37c48e8b6fa64af03087c5 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 17 Oct 2015 08:24:45 -0400 Subject: [PATCH 0146/1028] Simple predicates for boolean PropertTypes --- Rex.xcodeproj/project.pbxproj | 6 ++++ Source/Predicate.swift | 65 +++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 Source/Predicate.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 3388f01945..a07eeebdcb 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -39,6 +39,8 @@ D86FFBDA1B34B3F0001A89B3 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD91B34B3F0001A89B3 /* Action.swift */; }; D86FFBDB1B34B3F0001A89B3 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD91B34B3F0001A89B3 /* Action.swift */; }; D86FFBDD1B34B691001A89B3 /* UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBDC1B34B691001A89B3 /* UIButton.swift */; }; + D8A454061BD26A1A00C9E790 /* Predicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Predicate.swift */; settings = {ASSET_TAGS = (); }; }; + D8A454071BD26A1A00C9E790 /* Predicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Predicate.swift */; settings = {ASSET_TAGS = (); }; }; 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 */; }; @@ -121,6 +123,7 @@ D86FFBD71B34B242001A89B3 /* UILabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabel.swift; sourceTree = ""; }; D86FFBD91B34B3F0001A89B3 /* Action.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Action.swift; sourceTree = ""; }; D86FFBDC1B34B691001A89B3 /* UIButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIButton.swift; sourceTree = ""; }; + D8A454051BD26A1A00C9E790 /* Predicate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Predicate.swift; sourceTree = ""; }; D8F073141B861B3A0047D546 /* UILabelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabelTests.swift; sourceTree = ""; }; D8F0973A1B17F2F7002E15BA /* NSData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSData.swift; sourceTree = ""; }; D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSUserDefaults.swift; sourceTree = ""; }; @@ -189,6 +192,7 @@ isa = PBXGroup; children = ( D86FFBD91B34B3F0001A89B3 /* Action.swift */, + D8A454051BD26A1A00C9E790 /* Predicate.swift */, D8003EBC1AFED01000D7D3C5 /* Signal.swift */, D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */, 4238D5941B4D593E008534C0 /* AppKit */, @@ -477,6 +481,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + D8A454061BD26A1A00C9E790 /* Predicate.swift in Sources */, D86FFBDA1B34B3F0001A89B3 /* Action.swift in Sources */, D86FFBD11B34AD6F001A89B3 /* Association.swift in Sources */, D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */, @@ -508,6 +513,7 @@ D8F0973E1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, D834572D1AFEE45B0070616A /* Signal.swift in Sources */, D8E4A6211B7BBB2100EAD8A8 /* UIBarItem.swift in Sources */, + D8A454071BD26A1A00C9E790 /* Predicate.swift in Sources */, D8E4A6201B7BBB1600EAD8A8 /* UIBarButtonItem.swift in Sources */, D8F097451B17F3C8002E15BA /* NSObject.swift in Sources */, D834572E1AFEE45B0070616A /* SignalProducer.swift in Sources */, diff --git a/Source/Predicate.swift b/Source/Predicate.swift new file mode 100644 index 0000000000..a5a610fa35 --- /dev/null +++ b/Source/Predicate.swift @@ -0,0 +1,65 @@ +// +// Predicate.swift +// Rex +// +// Created by Neil Pankey on 10/17/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa + +public protocol PredicateType: PropertyType { + typealias Value = Bool +} + +public struct AndProperty: PredicateType { + private let left: PropertyOf + private let right: PropertyOf + + public var value: Bool { + return left.value && right.value + } + + public var producer: SignalProducer { + return combineLatest(left.producer, right.producer).map { $0 && $1 } + } + + public init(lhs: L, rhs: R) { + left = PropertyOf(lhs) + right = PropertyOf(rhs) + } +} + +public struct OrProperty: PredicateType { + private let left: PropertyOf + private let right: PropertyOf + + public var value: Bool { + return left.value || right.value + } + + public var producer: SignalProducer { + return combineLatest(left.producer, right.producer).map { $0 || $1 } + } + + public init(lhs: L, rhs: R) { + left = PropertyOf(lhs) + right = PropertyOf(rhs) + } +} + +public struct NotProperty: PredicateType { + private let property: PropertyOf + + public var value: Bool { + return !property.value + } + + public var producer: SignalProducer { + return property.producer.map { !$0 } + } + + public init(property: P) { + self.property = PropertyOf(property) + } +} From 45d98378d6ebbdaf6a722b02602c49579640c7a8 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 17 Oct 2015 09:01:24 -0400 Subject: [PATCH 0147/1028] Basic predicate tests --- Rex.xcodeproj/project.pbxproj | 6 ++++ Source/Predicate.swift | 8 ++--- Tests/PredicateTests.swift | 67 +++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 Tests/PredicateTests.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index a07eeebdcb..2e6c57eb02 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -41,6 +41,8 @@ D86FFBDD1B34B691001A89B3 /* UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBDC1B34B691001A89B3 /* UIButton.swift */; }; D8A454061BD26A1A00C9E790 /* Predicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Predicate.swift */; settings = {ASSET_TAGS = (); }; }; D8A454071BD26A1A00C9E790 /* Predicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Predicate.swift */; settings = {ASSET_TAGS = (); }; }; + D8A454091BD2772700C9E790 /* PredicateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454081BD2772700C9E790 /* PredicateTests.swift */; settings = {ASSET_TAGS = (); }; }; + D8A4540A1BD2772700C9E790 /* PredicateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454081BD2772700C9E790 /* PredicateTests.swift */; settings = {ASSET_TAGS = (); }; }; 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 */; }; @@ -124,6 +126,7 @@ D86FFBD91B34B3F0001A89B3 /* Action.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Action.swift; sourceTree = ""; }; D86FFBDC1B34B691001A89B3 /* UIButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIButton.swift; sourceTree = ""; }; D8A454051BD26A1A00C9E790 /* Predicate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Predicate.swift; sourceTree = ""; }; + D8A454081BD2772700C9E790 /* PredicateTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PredicateTests.swift; sourceTree = ""; }; D8F073141B861B3A0047D546 /* UILabelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabelTests.swift; sourceTree = ""; }; D8F0973A1B17F2F7002E15BA /* NSData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSData.swift; sourceTree = ""; }; D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSUserDefaults.swift; sourceTree = ""; }; @@ -215,6 +218,7 @@ D8003E9D1AFEC3D400D7D3C5 /* Tests */ = { isa = PBXGroup; children = ( + D8A454081BD2772700C9E790 /* PredicateTests.swift */, D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */, D8003EC01AFED30100D7D3C5 /* SignalProducerTests.swift */, D8F097461B17F5BF002E15BA /* Foundation */, @@ -500,6 +504,7 @@ D8F0974A1B17F5E1002E15BA /* NSObjectTests.swift in Sources */, D8003EC21AFED30F00D7D3C5 /* SignalTests.swift in Sources */, D8003EC31AFED30F00D7D3C5 /* SignalProducerTests.swift in Sources */, + D8A454091BD2772700C9E790 /* PredicateTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -530,6 +535,7 @@ D8F0974B1B17F5E2002E15BA /* NSObjectTests.swift in Sources */, D8F073161B863CE70047D546 /* UILabelTests.swift in Sources */, 8295FD8A1B87352D007C9000 /* UIButtonTests.swift in Sources */, + D8A4540A1BD2772700C9E790 /* PredicateTests.swift in Sources */, D83457301AFEE45E0070616A /* SignalProducerTests.swift in Sources */, D83457411AFEE6050070616A /* SignalTests.swift in Sources */, 8295FD8D1B87374A007C9000 /* UIBarButtonItemTests.swift in Sources */, diff --git a/Source/Predicate.swift b/Source/Predicate.swift index a5a610fa35..ccb2c0e6ac 100644 --- a/Source/Predicate.swift +++ b/Source/Predicate.swift @@ -49,17 +49,17 @@ public struct OrProperty: PredicateType { } public struct NotProperty: PredicateType { - private let property: PropertyOf + private let source: PropertyOf public var value: Bool { - return !property.value + return !source.value } public var producer: SignalProducer { - return property.producer.map { !$0 } + return source.producer.map { !$0 } } public init(property: P) { - self.property = PropertyOf(property) + source = PropertyOf(property) } } diff --git a/Tests/PredicateTests.swift b/Tests/PredicateTests.swift new file mode 100644 index 0000000000..0300904789 --- /dev/null +++ b/Tests/PredicateTests.swift @@ -0,0 +1,67 @@ +// +// PredicateTests.swift +// Rex +// +// Created by Neil Pankey on 10/17/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +@testable import Rex +import ReactiveCocoa +import XCTest + +final class PredicateTests: XCTestCase { + + func testAndProperty() { + let lhs = MutableProperty(false), rhs = MutableProperty(false) + let and = AndProperty(lhs: lhs, rhs: rhs) + + var current: Bool! + and.producer.startWithNext { current = $0 } + + XCTAssertFalse(and.value) + XCTAssertFalse(current!) + + lhs.value = true + XCTAssertFalse(and.value) + XCTAssertFalse(current!) + + rhs.value = true + XCTAssertTrue(and.value) + XCTAssertTrue(current!) + } + + func testOrProperty() { + let lhs = MutableProperty(true), rhs = MutableProperty(true) + let or = OrProperty(lhs: lhs, rhs: rhs) + + var current: Bool! + or.producer.startWithNext { current = $0 } + + XCTAssertTrue(or.value) + XCTAssertTrue(current!) + + lhs.value = false + XCTAssertTrue(or.value) + XCTAssertTrue(current!) + + rhs.value = false + XCTAssertFalse(or.value) + XCTAssertFalse(current!) + } + + func testNotProperty() { + let source = MutableProperty(false) + let not = NotProperty(property: source) + + var current: Bool! + not.producer.startWithNext { current = $0 } + + XCTAssertTrue(not.value) + XCTAssertTrue(current!) + + source.value = true + XCTAssertFalse(not.value) + XCTAssertFalse(current!) + } +} From acb842cc6f08a98fc8914d28abbf929317d268e3 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Tue, 20 Oct 2015 05:35:40 -0400 Subject: [PATCH 0148/1028] Create CONTRIBUTING.md Copied the one from antitypical/Result --- CONTRIBUTING.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..f58c3fada3 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,23 @@ +We love that you're interested in contributing to this project! + +To make the process as painless as possible, we have just a couple of guidelines +that should make life easier for everyone involved. + +## Prefer Pull Requests + +If you know exactly how to implement the feature being suggested or fix the bug +being reported, please open a pull request instead of an issue. Pull requests are easier than +patches or inline code blocks for discussing and merging the changes. + +If you can't make the change yourself, please open an issue after making sure +that one isn't already logged. + +## Contributing Code + +Fork this repository, make it awesomer (preferably in a branch named for the +topic), send a pull request! + +All code contributions should match our [coding +conventions](https://github.com/github/swift-style-guide). + +Thanks for contributing! :boom::camel: From 8a4fa4aa6e0bb14b5a4c633bb7b0eddf96eefc23 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Tue, 20 Oct 2015 18:39:59 -0400 Subject: [PATCH 0149/1028] Prep for RAC 4 alpha-2 --- Cartfile | 2 +- Cartfile.resolved | 4 +-- Carthage/Checkouts/ReactiveCocoa | 2 +- Carthage/Checkouts/Result | 2 +- Source/Foundation/NSData.swift | 6 ++--- Source/Signal.swift | 46 ++++++++++++++++---------------- Source/SignalProducer.swift | 38 +++++++++++++------------- Tests/SignalProducerTests.swift | 8 +++--- Tests/SignalTests.swift | 26 +++++++++--------- 9 files changed, 67 insertions(+), 67 deletions(-) diff --git a/Cartfile b/Cartfile index de7d65cc69..ff44a5378d 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveCocoa" "v4.0-alpha.1" +github "ReactiveCocoa/ReactiveCocoa" "8706513be" diff --git a/Cartfile.resolved b/Cartfile.resolved index b30f487b94..1dc6dcb57a 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ -github "antitypical/Result" "76f9a972ed61ff872b731460bc610c8acbfae58c" -github "ReactiveCocoa/ReactiveCocoa" "v4.0-alpha.1" +github "antitypical/Result" "0.6.0-beta.3" +github "ReactiveCocoa/ReactiveCocoa" "8706513be8433552566511c52e00e0280b8e82bb" diff --git a/Carthage/Checkouts/ReactiveCocoa b/Carthage/Checkouts/ReactiveCocoa index 8ebff4f192..8706513be8 160000 --- a/Carthage/Checkouts/ReactiveCocoa +++ b/Carthage/Checkouts/ReactiveCocoa @@ -1 +1 @@ -Subproject commit 8ebff4f192ad11dfc62c55e8151c235d005f390b +Subproject commit 8706513be8433552566511c52e00e0280b8e82bb diff --git a/Carthage/Checkouts/Result b/Carthage/Checkouts/Result index 76f9a972ed..918d63b99b 160000 --- a/Carthage/Checkouts/Result +++ b/Carthage/Checkouts/Result @@ -1 +1 @@ -Subproject commit 76f9a972ed61ff872b731460bc610c8acbfae58c +Subproject commit 918d63b99b76d424c299c8b5efea7dc3e8d286fc diff --git a/Source/Foundation/NSData.swift b/Source/Foundation/NSData.swift index d445ccd2a3..335a7bc690 100644 --- a/Source/Foundation/NSData.swift +++ b/Source/Foundation/NSData.swift @@ -16,10 +16,10 @@ extension NSData { return SignalProducer { observer, disposable in do { let data = try NSData(contentsOfURL: url, options: options) - sendNext(observer, data) - sendCompleted(observer) + observer.sendNext(data) + observer.sendCompleted() } catch { - sendError(observer, error as NSError) + observer.sendError(error as NSError) } } } diff --git a/Source/Signal.swift b/Source/Signal.swift index 4e2297a253..0b83fd973e 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -13,8 +13,8 @@ extension SignalType { /// Bring back the `observe` overload. The `observeNext` or pattern matching /// on `observe(Event)` is still annoying in practice and more verbose. This is /// also likely to change in a later RAC 4 alpha. - internal func observe(next next: (T -> ())? = nil, error: (E -> ())? = nil, completed: (() -> ())? = nil, interrupted: (() -> ())? = nil) -> Disposable? { - return self.observe { (event: Event) in + internal func observe(next next: (Value -> ())? = nil, error: (Error -> ())? = nil, completed: (() -> ())? = nil, interrupted: (() -> ())? = nil) -> Disposable? { + return self.observe { (event: Event) in switch event { case let .Next(value): next?(value) @@ -30,38 +30,38 @@ extension SignalType { /// Applies `transform` to values from `signal` with non-`nil` results unwrapped and /// forwared on the returned signal. - public func filterMap(transform: T -> U?) -> Signal { - return Signal { observer in + public func filterMap(transform: Value -> U?) -> Signal { + return Signal { observer in return self.observe(next: { value in if let val = transform(value) { - sendNext(observer, val) + observer.sendNext(val) } }, error: { error in - sendError(observer, error) + observer.sendError(error) }, completed: { - sendCompleted(observer) + observer.sendCompleted() }, interrupted: { - sendInterrupted(observer) + observer.sendInterrupted() }) } } /// Returns a signal that drops `Error` sending `replacement` terminal event /// instead, defaulting to `Completed`. - public func ignoreError(replacement replacement: Event = .Completed) -> Signal { + public func ignoreError(replacement replacement: Event = .Completed) -> Signal { precondition(replacement.isTerminating) - return Signal { observer in + return Signal { observer in return self.observe { event in switch event { case let .Next(value): - sendNext(observer, value) + observer.sendNext(value) case .Error: - observer(replacement) + observer.action(replacement) case .Completed: - sendCompleted(observer) + observer.sendCompleted() case .Interrupted: - sendInterrupted(observer) + observer.sendInterrupted() } } } @@ -72,7 +72,7 @@ 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. - public func timeoutAfter(interval: NSTimeInterval, withEvent event: Event, onScheduler scheduler: DateSchedulerType) -> Signal { + public func timeoutAfter(interval: NSTimeInterval, withEvent event: Event, onScheduler scheduler: DateSchedulerType) -> Signal { precondition(interval >= 0) precondition(event.isTerminating) @@ -81,7 +81,7 @@ extension SignalType { let date = scheduler.currentDate.dateByAddingTimeInterval(interval) disposable += scheduler.scheduleAfter(date) { - observer(event) + observer.action(event) } disposable += self.observe(observer) @@ -90,20 +90,20 @@ extension SignalType { } } -extension SignalType where T: SequenceType { +extension SignalType where Value: SequenceType { /// Returns a signal that flattens sequences of elements. The inverse of `collect`. - 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): - sequence.forEach { sendNext(observer, $0) } + sequence.forEach { observer.sendNext($0) } case let .Error(error): - sendError(observer, error) + observer.sendError(error) case .Completed: - sendCompleted(observer) + observer.sendCompleted() case .Interrupted: - sendInterrupted(observer) + observer.sendInterrupted() } } } diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index bccd8a406a..957cc4e0ef 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -13,8 +13,8 @@ extension SignalProducerType { /// Bring back the `start` overload. The `startNext` or pattern matching /// on `start(Event)` is annoying in practice and more verbose. This is also /// likely to change in a later RAC 4 alpha. - internal func start(next next: (T -> ())? = nil, error: (E -> ())? = nil, completed: (() -> ())? = nil, interrupted: (() -> ())? = nil) -> Disposable? { - return self.start { (event: Event) in + internal func start(next next: (Value -> ())? = nil, error: (Error -> ())? = nil, completed: (() -> ())? = nil, interrupted: (() -> ())? = nil) -> Disposable? { + return self.start { (event: Event) in switch event { case let .Next(value): next?(value) @@ -31,9 +31,9 @@ extension SignalProducerType { /// 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. - public func groupBy(grouping: T -> K) -> SignalProducer<(K, SignalProducer), E> { - return SignalProducer<(K, SignalProducer), E> { observer, disposable in - var groups: [K: Signal.Observer] = [:] + public func groupBy(grouping: Value -> Key) -> SignalProducer<(Key, SignalProducer), Error> { + return SignalProducer<(Key, SignalProducer), Error> { observer, disposable in + var groups: [Key: Signal.Observer] = [:] let lock = NSRecursiveLock() lock.name = "me.neilpa.rex.groupBy" @@ -44,40 +44,40 @@ extension SignalProducerType { lock.lock() var group = groups[key] if group == nil { - let (producer, sink) = SignalProducer.buffer() - sendNext(observer, (key, producer)) + let (producer, sink) = SignalProducer.buffer() + observer.sendNext(key, producer) groups[key] = sink group = sink } lock.unlock() - sendNext(group!, value) + group!.sendNext(value) }, error: { error in - sendError(observer, error) - groups.values.forEach { sendError($0, error) } + observer.sendError(error) + groups.values.forEach { $0.sendError(error) } }, completed: { _ in - sendCompleted(observer) - groups.values.forEach { sendCompleted($0) } + observer.sendCompleted() + groups.values.forEach { $0.sendCompleted() } }, interrupted: { _ in - sendInterrupted(observer) - groups.values.forEach { sendInterrupted($0) } + observer.sendInterrupted() + groups.values.forEach { $0.sendInterrupted() } }) } } /// Applies `transform` to values from self with non-`nil` results unwrapped and /// forwared on the returned producer. - public func filterMap(transform: T -> U?) -> SignalProducer { + public func filterMap(transform: Value -> U?) -> SignalProducer { return lift { $0.filterMap(transform) } } /// Returns a producer that drops `Error` sending `replacement` terminal event /// instead, defaulting to `Completed`. - public func ignoreError(replacement replacement: Event = .Completed) -> SignalProducer { + public func ignoreError(replacement replacement: Event = .Completed) -> SignalProducer { precondition(replacement.isTerminating) return lift { $0.ignoreError(replacement: replacement) } } @@ -87,14 +87,14 @@ 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. - public func timeoutAfter(interval: NSTimeInterval, withEvent event: Event, onScheduler scheduler: DateSchedulerType) -> SignalProducer { + public func timeoutAfter(interval: NSTimeInterval, withEvent event: Event, onScheduler scheduler: DateSchedulerType) -> SignalProducer { return lift { $0.timeoutAfter(interval, withEvent: event, onScheduler: scheduler) } } } -extension SignalProducerType where T: SequenceType { +extension SignalProducerType where Value: SequenceType { /// Returns a producer that flattens sequences of elements. The inverse of `collect`. - public func uncollect() -> SignalProducer { + public func uncollect() -> SignalProducer { return lift { $0.uncollect() } } } diff --git a/Tests/SignalProducerTests.swift b/Tests/SignalProducerTests.swift index a3471889a4..52f524c2aa 100644 --- a/Tests/SignalProducerTests.swift +++ b/Tests/SignalProducerTests.swift @@ -34,21 +34,21 @@ final class SignalProducerTests: XCTestCase { interrupted = true }) - sendNext(sink, 1) + sink.sendNext(1) XCTAssert(evens == []) XCTAssert(odds == [1]) - sendNext(sink, 2) + sink.sendNext(2) XCTAssert(evens == [2]) XCTAssert(odds == [1]) - sendNext(sink, 3) + sink.sendNext(3) XCTAssert(evens == [2]) XCTAssert(odds == [1, 3]) disposable.dispose() - sendNext(sink, 1) + sink.sendNext(1) XCTAssert(interrupted) XCTAssertFalse(completed) } diff --git a/Tests/SignalTests.swift b/Tests/SignalTests.swift index 70922db766..50463c391f 100644 --- a/Tests/SignalTests.swift +++ b/Tests/SignalTests.swift @@ -22,16 +22,16 @@ final class SignalTests: XCTestCase { } .observe(next: { values.append($0) }) - sendNext(sink, 1) + sink.sendNext(1) XCTAssert(values == []) - sendNext(sink, 2) + sink.sendNext(2) XCTAssert(values == ["2"]) - sendNext(sink, 3) + sink.sendNext(3) XCTAssert(values == ["2"]) - sendNext(sink, 6) + sink.sendNext(6) XCTAssert(values == ["2", "6"]) } @@ -45,10 +45,10 @@ final class SignalTests: XCTestCase { completed = true }) - sendNext(sink, 1) + sink.sendNext(1) XCTAssertFalse(completed) - sendError(sink, .Default) + sink.sendError(.Default) XCTAssertTrue(completed) } @@ -62,10 +62,10 @@ final class SignalTests: XCTestCase { interrupted = true }) - sendNext(sink, 1) + sink.sendNext(1) XCTAssertFalse(interrupted) - sendError(sink, .Default) + sink.sendError(.Default) XCTAssertTrue(interrupted) } @@ -82,7 +82,7 @@ final class SignalTests: XCTestCase { interrupted: { interrupted = true } ) - scheduler.scheduleAfter(1) { sendCompleted(sink) } + scheduler.scheduleAfter(1) { sink.sendCompleted() } XCTAssertFalse(interrupted) XCTAssertFalse(completed) @@ -105,7 +105,7 @@ final class SignalTests: XCTestCase { interrupted: { interrupted = true } ) - scheduler.scheduleAfter(3) { sendCompleted(sink) } + scheduler.scheduleAfter(3) { sink.sendCompleted() } XCTAssertFalse(interrupted) XCTAssertFalse(completed) @@ -125,13 +125,13 @@ final class SignalTests: XCTestCase { values.append($0) }) - sendNext(sink, []) + sink.sendNext([]) XCTAssert(values.isEmpty) - sendNext(sink, [1]) + sink.sendNext([1]) XCTAssert(values == [1]) - sendNext(sink, [2, 3]) + sink.sendNext([2, 3]) XCTAssert(values == [1, 2, 3]) } } From a9c405241d932392b0f5f864417e22d48a5fe2fd Mon Sep 17 00:00:00 2001 From: "Andy Jacobs (REVOLVER)" Date: Wed, 21 Oct 2015 18:30:25 +0200 Subject: [PATCH 0150/1028] UIImageView bindable properties --- Rex.xcodeproj/project.pbxproj | 8 ++++++ Source/UIKit/UIImageView.swift | 25 ++++++++++++++++++ Tests/UIKit/UIImageViewTests.swift | 41 ++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 Source/UIKit/UIImageView.swift create mode 100644 Tests/UIKit/UIImageViewTests.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 3388f01945..cc4ebb409f 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -8,6 +8,8 @@ /* Begin PBXBuildFile section */ 4238D5961B4D5950008534C0 /* NSTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4238D5951B4D5950008534C0 /* NSTextField.swift */; }; + 8289A2E11BD7EF1F0097FB60 /* UIImageViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */; settings = {ASSET_TAGS = (); }; }; + 8289A2E31BD7EF740097FB60 /* UIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E21BD7EF740097FB60 /* UIImageView.swift */; settings = {ASSET_TAGS = (); }; }; 8295FD871B87309F007C9000 /* UIControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8295FD851B873081007C9000 /* UIControlTests.swift */; }; 8295FD8A1B87352D007C9000 /* UIButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8295FD881B873490007C9000 /* UIButtonTests.swift */; }; 8295FD8D1B87374A007C9000 /* UIBarButtonItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */; }; @@ -98,6 +100,8 @@ 4238D5951B4D5950008534C0 /* NSTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NSTextField.swift; path = AppKit/NSTextField.swift; sourceTree = ""; }; 5173EBC51B625A2600C9B48E /* UIBarItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBarItem.swift; sourceTree = ""; }; 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBarButtonItem.swift; sourceTree = ""; }; + 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageViewTests.swift; sourceTree = ""; }; + 8289A2E21BD7EF740097FB60 /* UIImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageView.swift; sourceTree = ""; }; 8295FD851B873081007C9000 /* UIControlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIControlTests.swift; sourceTree = ""; }; 8295FD881B873490007C9000 /* UIButtonTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIButtonTests.swift; sourceTree = ""; }; 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBarButtonItemTests.swift; sourceTree = ""; }; @@ -268,6 +272,7 @@ D86FFBDC1B34B691001A89B3 /* UIButton.swift */, D86FFBD41B34B0FE001A89B3 /* UIControl.swift */, D86FFBD71B34B242001A89B3 /* UILabel.swift */, + 8289A2E21BD7EF740097FB60 /* UIImageView.swift */, ); path = UIKit; sourceTree = ""; @@ -279,6 +284,7 @@ 8295FD881B873490007C9000 /* UIButtonTests.swift */, 8295FD851B873081007C9000 /* UIControlTests.swift */, D8F073141B861B3A0047D546 /* UILabelTests.swift */, + 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */, ); path = UIKit; sourceTree = ""; @@ -505,6 +511,7 @@ D86FFBD81B34B242001A89B3 /* UILabel.swift in Sources */, D86FFBDB1B34B3F0001A89B3 /* Action.swift in Sources */, D86FFBD21B34AD7A001A89B3 /* Association.swift in Sources */, + 8289A2E31BD7EF740097FB60 /* UIImageView.swift in Sources */, D8F0973E1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, D834572D1AFEE45B0070616A /* Signal.swift in Sources */, D8E4A6211B7BBB2100EAD8A8 /* UIBarItem.swift in Sources */, @@ -521,6 +528,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8289A2E11BD7EF1F0097FB60 /* UIImageViewTests.swift in Sources */, D8F0974B1B17F5E2002E15BA /* NSObjectTests.swift in Sources */, D8F073161B863CE70047D546 /* UILabelTests.swift in Sources */, 8295FD8A1B87352D007C9000 /* UIButtonTests.swift in Sources */, diff --git a/Source/UIKit/UIImageView.swift b/Source/UIKit/UIImageView.swift new file mode 100644 index 0000000000..e522c01937 --- /dev/null +++ b/Source/UIKit/UIImageView.swift @@ -0,0 +1,25 @@ +// +// UIImageView.swift +// Rex +// +// Created by Andy Jacobs on 21/10/15. +// Copyright © 2015 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa +import UIKit + +extension UIImageView { + /// Wraps a imageView's `image` value in a bindable property. + public var rex_image: MutableProperty { + return rex_valueProperty(&image, { [weak self] in self?.image }, { [weak self] in self?.image = $0 }) + } + + /// Wraps a imageView's `highlightedImage` value in a bindable property. + public var rex_highlightedImage: MutableProperty { + return rex_valueProperty(&highlightedImage, { [weak self] in self?.highlightedImage }, { [weak self] in self?.highlightedImage = $0 }) + } +} + +private var image: UInt8 = 0 +private var highlightedImage: UInt8 = 0 \ No newline at end of file diff --git a/Tests/UIKit/UIImageViewTests.swift b/Tests/UIKit/UIImageViewTests.swift new file mode 100644 index 0000000000..0f367bf1b5 --- /dev/null +++ b/Tests/UIKit/UIImageViewTests.swift @@ -0,0 +1,41 @@ +// +// UIImageViewTests.swift +// Rex +// +// Created by Andy Jacobs on 21/10/15. +// Copyright © 2015 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa +import UIKit +import XCTest + +class UIImageViewTests: XCTestCase { + + weak var _imageView: UIImageView? + + override func tearDown() { + XCTAssert(_imageView == nil, "Retain cycle detected in UIImageView properties") + super.tearDown() + } + + func testImagePropertyDoesntCreateRetainCycle() { + let imageView = UIImageView(frame: CGRectZero) + _imageView = imageView + + let image = UIImage() + + imageView.rex_image <~ SignalProducer(value: image) + XCTAssert(_imageView?.image == image) + } + + func testHighlightedImagePropertyDoesntCreateRetainCycle() { + let imageView = UIImageView(frame: CGRectZero) + _imageView = imageView + + let image = UIImage() + + imageView.rex_highlightedImage <~ SignalProducer(value: image) + XCTAssert(_imageView?.highlightedImage == image) + } +} From 0aa212e9893a8dbd4bbdea8c4059fa4b7e55a781 Mon Sep 17 00:00:00 2001 From: "Andy Jacobs (REVOLVER)" Date: Wed, 21 Oct 2015 18:34:56 +0200 Subject: [PATCH 0151/1028] UIControl.rex_selected --- Source/UIKit/UIControl.swift | 6 ++++++ Tests/UIKit/UIControlTests.swift | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/Source/UIKit/UIControl.swift b/Source/UIKit/UIControl.swift index bff8b389f2..c0ac3eb9d3 100644 --- a/Source/UIKit/UIControl.swift +++ b/Source/UIKit/UIControl.swift @@ -22,6 +22,12 @@ extension UIControl { public var rex_enabled: MutableProperty { return rex_valueProperty(&enabled, { [weak self] in self?.enabled ?? true }, { [weak self] in self?.enabled = $0 }) } + + /// Wraps a control's `selected` state in a bindable property. + public var rex_selected: MutableProperty { + return rex_valueProperty(&selected, { [weak self] in self?.selected ?? false }, { [weak self] in self?.selected = $0 }) + } } private var enabled: UInt8 = 0 +private var selected: UInt8 = 0 diff --git a/Tests/UIKit/UIControlTests.swift b/Tests/UIKit/UIControlTests.swift index 6ffd472970..a18a1ce58b 100644 --- a/Tests/UIKit/UIControlTests.swift +++ b/Tests/UIKit/UIControlTests.swift @@ -26,4 +26,12 @@ class UIControlTests: XCTestCase { control.rex_enabled <~ SignalProducer(value: false) XCTAssert(_control?.enabled == false) } + + func testSelectedPropertyDoesntCreateRetainCycle() { + let control = UIControl(frame: CGRectZero) + _control = control + + control.rex_selected <~ SignalProducer(value: false) + XCTAssert(_control?.selected == false) + } } From db5530be1eedcbcf8867d92297e63181eaaa7a5c Mon Sep 17 00:00:00 2001 From: "Andy Jacobs (REVOLVER)" Date: Wed, 21 Oct 2015 18:36:50 +0200 Subject: [PATCH 0152/1028] UIControl.rex_highlighted --- Source/UIKit/UIControl.swift | 6 ++++++ Tests/UIKit/UIControlTests.swift | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/Source/UIKit/UIControl.swift b/Source/UIKit/UIControl.swift index c0ac3eb9d3..33b44ebade 100644 --- a/Source/UIKit/UIControl.swift +++ b/Source/UIKit/UIControl.swift @@ -27,7 +27,13 @@ extension UIControl { public var rex_selected: MutableProperty { return rex_valueProperty(&selected, { [weak self] in self?.selected ?? false }, { [weak self] in self?.selected = $0 }) } + + /// Wraps a control's `highlighted` state in a bindable property. + public var rex_highlighted: MutableProperty { + return rex_valueProperty(&highlighted, { [weak self] in self?.highlighted ?? false }, { [weak self] in self?.highlighted = $0 }) + } } private var enabled: UInt8 = 0 private var selected: UInt8 = 0 +private var highlighted: UInt8 = 0 diff --git a/Tests/UIKit/UIControlTests.swift b/Tests/UIKit/UIControlTests.swift index a18a1ce58b..e097598c15 100644 --- a/Tests/UIKit/UIControlTests.swift +++ b/Tests/UIKit/UIControlTests.swift @@ -34,4 +34,12 @@ class UIControlTests: XCTestCase { control.rex_selected <~ SignalProducer(value: false) XCTAssert(_control?.selected == false) } + + func testHighlightedPropertyDoesntCreateRetainCycle() { + let control = UIControl(frame: CGRectZero) + _control = control + + control.rex_highlighted <~ SignalProducer(value: true) + XCTAssert(_control?.highlighted == true) + } } From 2e499c322cd24ae9a2b398af4ebb11f90c3d1959 Mon Sep 17 00:00:00 2001 From: "Andy Jacobs (REVOLVER)" Date: Wed, 21 Oct 2015 18:37:24 +0200 Subject: [PATCH 0153/1028] update UIControl.rex_selected tests --- Tests/UIKit/UIControlTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/UIKit/UIControlTests.swift b/Tests/UIKit/UIControlTests.swift index e097598c15..312c45152a 100644 --- a/Tests/UIKit/UIControlTests.swift +++ b/Tests/UIKit/UIControlTests.swift @@ -31,8 +31,8 @@ class UIControlTests: XCTestCase { let control = UIControl(frame: CGRectZero) _control = control - control.rex_selected <~ SignalProducer(value: false) - XCTAssert(_control?.selected == false) + control.rex_selected <~ SignalProducer(value: true) + XCTAssert(_control?.selected == true) } func testHighlightedPropertyDoesntCreateRetainCycle() { From 27b2a1f996f844454e919096e4678ee17ff1b32b Mon Sep 17 00:00:00 2001 From: "Andy Jacobs (REVOLVER)" Date: Wed, 21 Oct 2015 18:43:37 +0200 Subject: [PATCH 0154/1028] UIView.rex_alpha, UIView.rex_hidden --- Rex.xcodeproj/project.pbxproj | 8 ++++++++ Source/UIKit/UIView.swift | 25 +++++++++++++++++++++++ Tests/UIKit/UIViewTests.swift | 37 +++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 Source/UIKit/UIView.swift create mode 100644 Tests/UIKit/UIViewTests.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index cc4ebb409f..e8922e3ba8 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -10,6 +10,8 @@ 4238D5961B4D5950008534C0 /* NSTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4238D5951B4D5950008534C0 /* NSTextField.swift */; }; 8289A2E11BD7EF1F0097FB60 /* UIImageViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */; settings = {ASSET_TAGS = (); }; }; 8289A2E31BD7EF740097FB60 /* UIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E21BD7EF740097FB60 /* UIImageView.swift */; settings = {ASSET_TAGS = (); }; }; + 8289A2E51BD7F6DD0097FB60 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E41BD7F6DD0097FB60 /* UIView.swift */; settings = {ASSET_TAGS = (); }; }; + 8289A2E81BD7F7900097FB60 /* UIViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */; settings = {ASSET_TAGS = (); }; }; 8295FD871B87309F007C9000 /* UIControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8295FD851B873081007C9000 /* UIControlTests.swift */; }; 8295FD8A1B87352D007C9000 /* UIButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8295FD881B873490007C9000 /* UIButtonTests.swift */; }; 8295FD8D1B87374A007C9000 /* UIBarButtonItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */; }; @@ -102,6 +104,8 @@ 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBarButtonItem.swift; sourceTree = ""; }; 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageViewTests.swift; sourceTree = ""; }; 8289A2E21BD7EF740097FB60 /* UIImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageView.swift; sourceTree = ""; }; + 8289A2E41BD7F6DD0097FB60 /* UIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = ""; }; + 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewTests.swift; sourceTree = ""; }; 8295FD851B873081007C9000 /* UIControlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIControlTests.swift; sourceTree = ""; }; 8295FD881B873490007C9000 /* UIButtonTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIButtonTests.swift; sourceTree = ""; }; 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBarButtonItemTests.swift; sourceTree = ""; }; @@ -273,6 +277,7 @@ D86FFBD41B34B0FE001A89B3 /* UIControl.swift */, D86FFBD71B34B242001A89B3 /* UILabel.swift */, 8289A2E21BD7EF740097FB60 /* UIImageView.swift */, + 8289A2E41BD7F6DD0097FB60 /* UIView.swift */, ); path = UIKit; sourceTree = ""; @@ -285,6 +290,7 @@ 8295FD851B873081007C9000 /* UIControlTests.swift */, D8F073141B861B3A0047D546 /* UILabelTests.swift */, 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */, + 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */, ); path = UIKit; sourceTree = ""; @@ -518,6 +524,7 @@ D8E4A6201B7BBB1600EAD8A8 /* UIBarButtonItem.swift in Sources */, D8F097451B17F3C8002E15BA /* NSObject.swift in Sources */, D834572E1AFEE45B0070616A /* SignalProducer.swift in Sources */, + 8289A2E51BD7F6DD0097FB60 /* UIView.swift in Sources */, D86FFBD61B34B116001A89B3 /* UIControl.swift in Sources */, D8F0973F1B17F31E002E15BA /* NSData.swift in Sources */, D86FFBDD1B34B691001A89B3 /* UIButton.swift in Sources */, @@ -530,6 +537,7 @@ files = ( 8289A2E11BD7EF1F0097FB60 /* UIImageViewTests.swift in Sources */, D8F0974B1B17F5E2002E15BA /* NSObjectTests.swift in Sources */, + 8289A2E81BD7F7900097FB60 /* UIViewTests.swift in Sources */, D8F073161B863CE70047D546 /* UILabelTests.swift in Sources */, 8295FD8A1B87352D007C9000 /* UIButtonTests.swift in Sources */, D83457301AFEE45E0070616A /* SignalProducerTests.swift in Sources */, diff --git a/Source/UIKit/UIView.swift b/Source/UIKit/UIView.swift new file mode 100644 index 0000000000..853231080d --- /dev/null +++ b/Source/UIKit/UIView.swift @@ -0,0 +1,25 @@ +// +// UIView.swift +// Rex +// +// Created by Andy Jacobs on 21/10/15. +// Copyright © 2015 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa +import UIKit + +extension UIView { + /// Wraps a view's `alpha` value in a bindable property. + public var rex_alpha: MutableProperty { + return rex_valueProperty(&alpha, { [weak self] in self?.alpha ?? 1.0 }, { [weak self] in self?.alpha = $0 }) + } + + /// Wraps a view's `hidden` state in a bindable property. + public var rex_hidden: MutableProperty { + return rex_valueProperty(&hidden, { [weak self] in self?.hidden ?? false }, { [weak self] in self?.hidden = $0 }) + } +} + +private var alpha: UInt8 = 0 +private var hidden: UInt8 = 0 \ No newline at end of file diff --git a/Tests/UIKit/UIViewTests.swift b/Tests/UIKit/UIViewTests.swift new file mode 100644 index 0000000000..8873171ebd --- /dev/null +++ b/Tests/UIKit/UIViewTests.swift @@ -0,0 +1,37 @@ +// +// UIViewTests.swift +// Rex +// +// Created by Andy Jacobs on 21/10/15. +// Copyright © 2015 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa +import UIKit +import XCTest + +class UIViewTests: XCTestCase { + + weak var _view: UIView? + + override func tearDown() { + XCTAssert(_view == nil, "Retain cycle detected in UIView properties") + super.tearDown() + } + + func testAlphaPropertyDoesntCreateRetainCycle() { + let view = UIView(frame: CGRectZero) + _view = view + + view.rex_alpha <~ SignalProducer(value: 0.5) + XCTAssert(_view?.alpha == 0.5) + } + + func testHiddenPropertyDoesntCreateRetainCycle() { + let view = UIView(frame: CGRectZero) + _view = view + + view.rex_hidden <~ SignalProducer(value: true) + XCTAssert(_view?.hidden == true) + } +} \ No newline at end of file From b3c6adbefafb37ac289896f2469d549b6a1380cb Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 21 Oct 2015 17:32:54 -0400 Subject: [PATCH 0155/1028] Better extensions for boolean properties --- Source/Predicate.swift | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/Source/Predicate.swift b/Source/Predicate.swift index ccb2c0e6ac..b5fa32539a 100644 --- a/Source/Predicate.swift +++ b/Source/Predicate.swift @@ -8,11 +8,21 @@ import ReactiveCocoa -public protocol PredicateType: PropertyType { - typealias Value = Bool +extension PropertyType where Value == Bool { + public func and(other: Self) -> AndProperty { + return AndProperty(lhs: self, rhs: other) + } + + public func or(other: Self) -> OrProperty { + return OrProperty(lhs: self, rhs: other) + } + + public func not() -> NotProperty { + return NotProperty(property: self) + } } -public struct AndProperty: PredicateType { +public struct AndProperty: PropertyType { private let left: PropertyOf private let right: PropertyOf @@ -30,7 +40,7 @@ public struct AndProperty: PredicateType { } } -public struct OrProperty: PredicateType { +public struct OrProperty: PropertyType { private let left: PropertyOf private let right: PropertyOf @@ -48,7 +58,7 @@ public struct OrProperty: PredicateType { } } -public struct NotProperty: PredicateType { +public struct NotProperty: PropertyType { private let source: PropertyOf public var value: Bool { From f825fd3aae4aa5e0b435b8490b1bc4f918a09152 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 17 Oct 2015 11:24:51 -0400 Subject: [PATCH 0156/1028] Arity based tuple-helpers that may be useful --- Rex.xcodeproj/project.pbxproj | 6 +++++ Source/Arity.swift | 47 +++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 Source/Arity.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 2e6c57eb02..3b4d048b8f 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -43,6 +43,8 @@ D8A454071BD26A1A00C9E790 /* Predicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Predicate.swift */; settings = {ASSET_TAGS = (); }; }; D8A454091BD2772700C9E790 /* PredicateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454081BD2772700C9E790 /* PredicateTests.swift */; settings = {ASSET_TAGS = (); }; }; D8A4540A1BD2772700C9E790 /* PredicateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454081BD2772700C9E790 /* PredicateTests.swift */; settings = {ASSET_TAGS = (); }; }; + D8A4540C1BD29A2C00C9E790 /* Arity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A4540B1BD29A2C00C9E790 /* Arity.swift */; settings = {ASSET_TAGS = (); }; }; + D8A4540D1BD29A2C00C9E790 /* Arity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A4540B1BD29A2C00C9E790 /* Arity.swift */; settings = {ASSET_TAGS = (); }; }; 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 */; }; @@ -127,6 +129,7 @@ D86FFBDC1B34B691001A89B3 /* UIButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIButton.swift; sourceTree = ""; }; D8A454051BD26A1A00C9E790 /* Predicate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Predicate.swift; sourceTree = ""; }; D8A454081BD2772700C9E790 /* PredicateTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PredicateTests.swift; sourceTree = ""; }; + D8A4540B1BD29A2C00C9E790 /* Arity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Arity.swift; sourceTree = ""; }; D8F073141B861B3A0047D546 /* UILabelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabelTests.swift; sourceTree = ""; }; D8F0973A1B17F2F7002E15BA /* NSData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSData.swift; sourceTree = ""; }; D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSUserDefaults.swift; sourceTree = ""; }; @@ -195,6 +198,7 @@ isa = PBXGroup; children = ( D86FFBD91B34B3F0001A89B3 /* Action.swift */, + D8A4540B1BD29A2C00C9E790 /* Arity.swift */, D8A454051BD26A1A00C9E790 /* Predicate.swift */, D8003EBC1AFED01000D7D3C5 /* Signal.swift */, D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */, @@ -490,6 +494,7 @@ D86FFBD11B34AD6F001A89B3 /* Association.swift in Sources */, D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */, D8003EBD1AFED01000D7D3C5 /* Signal.swift in Sources */, + D8A4540C1BD29A2C00C9E790 /* Arity.swift in Sources */, D8F097441B17F3C8002E15BA /* NSObject.swift in Sources */, D8F0973B1B17F2F7002E15BA /* NSData.swift in Sources */, 4238D5961B4D5950008534C0 /* NSTextField.swift in Sources */, @@ -521,6 +526,7 @@ D8A454071BD26A1A00C9E790 /* Predicate.swift in Sources */, D8E4A6201B7BBB1600EAD8A8 /* UIBarButtonItem.swift in Sources */, D8F097451B17F3C8002E15BA /* NSObject.swift in Sources */, + D8A4540D1BD29A2C00C9E790 /* Arity.swift in Sources */, D834572E1AFEE45B0070616A /* SignalProducer.swift in Sources */, D86FFBD61B34B116001A89B3 /* UIControl.swift in Sources */, D8F0973F1B17F31E002E15BA /* NSData.swift in Sources */, diff --git a/Source/Arity.swift b/Source/Arity.swift new file mode 100644 index 0000000000..2b03122b90 --- /dev/null +++ b/Source/Arity.swift @@ -0,0 +1,47 @@ +// +// Tuple.swift +// Rex +// +// Created by Neil Pankey on 10/17/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +public protocol ArityType { + static var size: Int { get } + + // TODO Can this be guaranteed somehow? + typealias TupleType + var tuple: TupleType { get } +} + +public struct Nullary: ArityType { + public static var size: Int { + return 0 + } + + public let tuple: () = () +} + +public struct Unary: ArityType { + public static var size: Int { + return 1 + } + + public let tuple: A +} + +public struct Binary: ArityType { + public static var size: Int { + return 2 + } + + public let tuple: (A, B) +} + +public struct Ternary: ArityType { + public static var size: Int { + return 3 + } + + public let tuple: (A, B, C) +} From ece8b976ef45d7344813a9cddad23163705d23d8 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 18 Oct 2015 15:52:45 -0400 Subject: [PATCH 0157/1028] Protocols for all arities --- Source/Arity.swift | 45 ++++++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/Source/Arity.swift b/Source/Arity.swift index 2b03122b90..1f473d04cb 100644 --- a/Source/Arity.swift +++ b/Source/Arity.swift @@ -14,34 +14,45 @@ public protocol ArityType { var tuple: TupleType { get } } -public struct Nullary: ArityType { - public static var size: Int { - return 0 - } +public protocol NullaryType: ArityType { } +extension NullaryType { + public static var size: Int { return 0 } +} + +public struct Nullary: NullaryType { public let tuple: () = () } -public struct Unary: ArityType { - public static var size: Int { - return 1 - } +public protocol UnaryType: ArityType { } + +extension UnaryType { + public static var size: Int { return 1 } +} + +public struct Unary: UnaryType { public let tuple: A } -public struct Binary: ArityType { - public static var size: Int { - return 2 - } +public protocol BinaryType: ArityType { } + +extension BinaryType { + public static var size: Int { return 2 } +} + +public struct Binary: BinaryType { public let tuple: (A, B) } -public struct Ternary: ArityType { - public static var size: Int { - return 3 - } - + +public protocol TernaryType: ArityType { } + +extension TernaryType { + public static var size: Int { return 3 } +} + +public struct Ternary: TernaryType { public let tuple: (A, B, C) } From 1014b5678f6d692fa8af2af966482a14907a0008 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Thu, 22 Oct 2015 20:23:09 -0400 Subject: [PATCH 0158/1028] Use RAC 4 alpha 2 release --- Cartfile | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/ReactiveCocoa | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cartfile b/Cartfile index ff44a5378d..9a02558ccd 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveCocoa" "8706513be" +github "ReactiveCocoa/ReactiveCocoa" "v4.0.0-alpha.2" diff --git a/Cartfile.resolved b/Cartfile.resolved index 1dc6dcb57a..8b7603e952 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ github "antitypical/Result" "0.6.0-beta.3" -github "ReactiveCocoa/ReactiveCocoa" "8706513be8433552566511c52e00e0280b8e82bb" +github "ReactiveCocoa/ReactiveCocoa" "v4.0.0-alpha.2" diff --git a/Carthage/Checkouts/ReactiveCocoa b/Carthage/Checkouts/ReactiveCocoa index 8706513be8..ff52a3ea24 160000 --- a/Carthage/Checkouts/ReactiveCocoa +++ b/Carthage/Checkouts/ReactiveCocoa @@ -1 +1 @@ -Subproject commit 8706513be8433552566511c52e00e0280b8e82bb +Subproject commit ff52a3ea246b459c6b0a956f1ec6e67e7b333f38 From 85e2a312cb7b2bb5c0243efecd566a7127e182dd Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Thu, 22 Oct 2015 20:45:13 -0400 Subject: [PATCH 0159/1028] Xcode 7.1 project settings --- Rex.xcodeproj/project.pbxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index e8922e3ba8..3cf33f8c41 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -8,10 +8,10 @@ /* Begin PBXBuildFile section */ 4238D5961B4D5950008534C0 /* NSTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4238D5951B4D5950008534C0 /* NSTextField.swift */; }; - 8289A2E11BD7EF1F0097FB60 /* UIImageViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */; settings = {ASSET_TAGS = (); }; }; - 8289A2E31BD7EF740097FB60 /* UIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E21BD7EF740097FB60 /* UIImageView.swift */; settings = {ASSET_TAGS = (); }; }; - 8289A2E51BD7F6DD0097FB60 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E41BD7F6DD0097FB60 /* UIView.swift */; settings = {ASSET_TAGS = (); }; }; - 8289A2E81BD7F7900097FB60 /* UIViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */; settings = {ASSET_TAGS = (); }; }; + 8289A2E11BD7EF1F0097FB60 /* UIImageViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */; }; + 8289A2E31BD7EF740097FB60 /* UIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E21BD7EF740097FB60 /* UIImageView.swift */; }; + 8289A2E51BD7F6DD0097FB60 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E41BD7F6DD0097FB60 /* UIView.swift */; }; + 8289A2E81BD7F7900097FB60 /* UIViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */; }; 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 */; }; From 22b37da3dbc24028cdb8245ead3bb81f91f6d594 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Thu, 22 Oct 2015 20:47:09 -0400 Subject: [PATCH 0160/1028] Update plist version to 0.9.0 --- Source/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Info.plist b/Source/Info.plist index 12b09b1221..f849d15d8b 100644 --- a/Source/Info.plist +++ b/Source/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.8.0 + 0.9.0 CFBundleSignature ???? CFBundleVersion From 268ab5ec61619856523152c14231360dafa6d656 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Fri, 23 Oct 2015 11:18:55 +0900 Subject: [PATCH 0161/1028] Replace `start` and `observe` overloads with new Observer API --- Source/Foundation/Association.swift | 2 +- Source/Foundation/NSObject.swift | 2 +- Source/Signal.swift | 22 ++-------------------- Source/SignalProducer.swift | 22 ++-------------------- Source/UIKit/UIBarButtonItem.swift | 4 ++-- Source/UIKit/UIButton.swift | 4 ++-- Tests/Foundation/NSObjectTests.swift | 4 ++-- Tests/SignalProducerTests.swift | 10 +++++----- Tests/SignalTests.swift | 24 ++++++++++++------------ 9 files changed, 29 insertions(+), 65 deletions(-) diff --git a/Source/Foundation/Association.swift b/Source/Foundation/Association.swift index f301bbfd68..6109df9609 100644 --- a/Source/Foundation/Association.swift +++ b/Source/Foundation/Association.swift @@ -58,7 +58,7 @@ public func associatedProperty(host: AnyObject, keyPath: StaticStr public func associatedProperty(host: AnyObject, key: UnsafePointer<()>, initial: () -> T, setter: T -> ()) -> MutableProperty { return associatedObject(host, key: key) { let property = MutableProperty(initial()) - property.producer.start(next: setter) + property.producer.start(Observer(next: setter)) return property } } diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index b826c45ede..d852ed1657 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -61,7 +61,7 @@ extension NSObject { public func rex_valueProperty(key: UnsafePointer<()>, _ initial: () -> T, _ setter: T -> ()) -> MutableProperty { return associatedObject(self, key: key) { let property = MutableProperty(initial()) - property.producer.start(next: setter) + property.producer.start(Observer(next: setter)) return property } } diff --git a/Source/Signal.swift b/Source/Signal.swift index 0b83fd973e..b301b9be0e 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -10,29 +10,11 @@ import ReactiveCocoa extension SignalType { - /// Bring back the `observe` overload. The `observeNext` or pattern matching - /// on `observe(Event)` is still annoying in practice and more verbose. This is - /// also likely to change in a later RAC 4 alpha. - internal func observe(next next: (Value -> ())? = nil, error: (Error -> ())? = nil, completed: (() -> ())? = nil, interrupted: (() -> ())? = nil) -> Disposable? { - return self.observe { (event: Event) in - switch event { - case let .Next(value): - next?(value) - case let .Error(err): - error?(err) - case .Completed: - completed?() - case .Interrupted: - interrupted?() - } - } - } - /// Applies `transform` to values from `signal` with non-`nil` results unwrapped and /// forwared on the returned signal. public func filterMap(transform: Value -> U?) -> Signal { return Signal { observer in - return self.observe(next: { value in + return self.observe(Observer(next: { value in if let val = transform(value) { observer.sendNext(val) } @@ -42,7 +24,7 @@ extension SignalType { observer.sendCompleted() }, interrupted: { observer.sendInterrupted() - }) + })) } } diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index 957cc4e0ef..c93dcd130f 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -10,24 +10,6 @@ import ReactiveCocoa extension SignalProducerType { - /// Bring back the `start` overload. The `startNext` or pattern matching - /// on `start(Event)` is annoying in practice and more verbose. This is also - /// likely to change in a later RAC 4 alpha. - internal func start(next next: (Value -> ())? = nil, error: (Error -> ())? = nil, completed: (() -> ())? = nil, interrupted: (() -> ())? = nil) -> Disposable? { - return self.start { (event: Event) in - switch event { - case let .Next(value): - next?(value) - case let .Error(err): - error?(err) - case .Completed: - completed?() - case .Interrupted: - interrupted?() - } - } - } - /// 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. @@ -38,7 +20,7 @@ extension SignalProducerType { let lock = NSRecursiveLock() lock.name = "me.neilpa.rex.groupBy" - self.start(next: { value in + self.start(Observer(next: { value in let key = grouping(value) lock.lock() @@ -65,7 +47,7 @@ extension SignalProducerType { }, interrupted: { _ in observer.sendInterrupted() groups.values.forEach { $0.sendInterrupted() } - }) + })) } } diff --git a/Source/UIKit/UIBarButtonItem.swift b/Source/UIKit/UIBarButtonItem.swift index 6931ffd586..f74b3a77e2 100644 --- a/Source/UIKit/UIBarButtonItem.swift +++ b/Source/UIKit/UIBarButtonItem.swift @@ -19,10 +19,10 @@ extension UIBarButtonItem { let initial = CocoaAction.rex_disabled let property = MutableProperty(initial) - property.producer.start(next: { next in + property.producer.start(Observer(next: { next in self?.target = next self?.action = CocoaAction.selector - }) + })) if let strongSelf = self { strongSelf.rex_enabled <~ property.producer.flatMap(.Latest) { $0.rex_enabledProducer } diff --git a/Source/UIKit/UIButton.swift b/Source/UIKit/UIButton.swift index 4b943a9d98..7003f9892e 100644 --- a/Source/UIKit/UIButton.swift +++ b/Source/UIKit/UIButton.swift @@ -21,10 +21,10 @@ extension UIButton { property.producer .combinePrevious(initial) - .start(next: { previous, next in + .start(Observer(next: { previous, next in self?.removeTarget(previous, action: CocoaAction.selector, forControlEvents: .TouchUpInside) self?.addTarget(next, action: CocoaAction.selector, forControlEvents: .TouchUpInside) - }) + })) if let strongSelf = self { strongSelf.rex_enabled <~ property.producer.flatMap(.Latest) { $0.rex_enabledProducer } diff --git a/Tests/Foundation/NSObjectTests.swift b/Tests/Foundation/NSObjectTests.swift index 1cb7ae1a90..c9718d2af8 100644 --- a/Tests/Foundation/NSObjectTests.swift +++ b/Tests/Foundation/NSObjectTests.swift @@ -6,7 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // -@testable import Rex +import Rex import ReactiveCocoa import XCTest @@ -16,7 +16,7 @@ final class NSObjectTests: XCTestCase { let object = Object() var value: String = "" - object.rex_producerForKeyPath("string").start(next: { value = $0 }) + object.rex_producerForKeyPath("string").start(Observer(next: { value = $0 })) XCTAssertEqual(value, "foo") object.string = "bar" diff --git a/Tests/SignalProducerTests.swift b/Tests/SignalProducerTests.swift index 52f524c2aa..fa6aa1b554 100644 --- a/Tests/SignalProducerTests.swift +++ b/Tests/SignalProducerTests.swift @@ -6,7 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // -@testable import Rex +import Rex import ReactiveCocoa import XCTest @@ -22,17 +22,17 @@ final class SignalProducerTests: XCTestCase { disposable += producer .groupBy { $0 % 2 == 0 } - .start(next: { key, group in + .start(Observer(next: { key, group in if key { - group.start(next: { evens.append($0) }) + group.start(Observer(next: { evens.append($0) })) } else { - group.start(next: { odds.append($0) }) + group.start(Observer(next: { odds.append($0) })) } },completed: { completed = true }, interrupted: { interrupted = true - }) + })) sink.sendNext(1) XCTAssert(evens == []) diff --git a/Tests/SignalTests.swift b/Tests/SignalTests.swift index 50463c391f..9d0efcfb05 100644 --- a/Tests/SignalTests.swift +++ b/Tests/SignalTests.swift @@ -6,7 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // -@testable import Rex +import Rex import ReactiveCocoa import XCTest @@ -20,7 +20,7 @@ final class SignalTests: XCTestCase { .filterMap { return $0 % 2 == 0 ? String($0) : nil } - .observe(next: { values.append($0) }) + .observe(Observer(next: { values.append($0) })) sink.sendNext(1) XCTAssert(values == []) @@ -41,9 +41,9 @@ final class SignalTests: XCTestCase { signal .ignoreError() - .observe(completed: { + .observe(Observer(completed: { completed = true - }) + })) sink.sendNext(1) XCTAssertFalse(completed) @@ -58,9 +58,9 @@ final class SignalTests: XCTestCase { signal .ignoreError(replacement: .Interrupted) - .observe(interrupted: { + .observe(Observer(interrupted: { interrupted = true - }) + })) sink.sendNext(1) XCTAssertFalse(interrupted) @@ -77,10 +77,10 @@ final class SignalTests: XCTestCase { signal .timeoutAfter(2, withEvent: .Interrupted, onScheduler: scheduler) - .observe( + .observe(Observer( completed: { completed = true }, interrupted: { interrupted = true } - ) + )) scheduler.scheduleAfter(1) { sink.sendCompleted() } @@ -100,10 +100,10 @@ final class SignalTests: XCTestCase { signal .timeoutAfter(2, withEvent: .Interrupted, onScheduler: scheduler) - .observe( + .observe(Observer( completed: { completed = true }, interrupted: { interrupted = true } - ) + )) scheduler.scheduleAfter(3) { sink.sendCompleted() } @@ -121,9 +121,9 @@ final class SignalTests: XCTestCase { signal .uncollect() - .observe(next: { + .observe(Observer(next: { values.append($0) - }) + })) sink.sendNext([]) XCTAssert(values.isEmpty) From 1d8c55cb80e395d0cfdaaab3b0aba08fef3e9e33 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Fri, 23 Oct 2015 18:05:17 -0400 Subject: [PATCH 0162/1028] Update to Event.Failure --- Cartfile | 2 +- Cartfile.resolved | 4 ++-- Carthage/Checkouts/ReactiveCocoa | 2 +- Carthage/Checkouts/Result | 2 +- Source/Foundation/NSData.swift | 2 +- Source/Signal.swift | 10 +++++----- Source/SignalProducer.swift | 6 +++--- Tests/SignalTests.swift | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Cartfile b/Cartfile index 9a02558ccd..5c6f9ac924 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveCocoa" "v4.0.0-alpha.2" +github "ReactiveCocoa/ReactiveCocoa" "58fbf768" diff --git a/Cartfile.resolved b/Cartfile.resolved index 8b7603e952..faf36736f0 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ -github "antitypical/Result" "0.6.0-beta.3" -github "ReactiveCocoa/ReactiveCocoa" "v4.0.0-alpha.2" +github "antitypical/Result" "0.6.0-beta.5" +github "ReactiveCocoa/ReactiveCocoa" "58fbf76861c80babd93966b57e69683aed914ab3" diff --git a/Carthage/Checkouts/ReactiveCocoa b/Carthage/Checkouts/ReactiveCocoa index ff52a3ea24..58fbf76861 160000 --- a/Carthage/Checkouts/ReactiveCocoa +++ b/Carthage/Checkouts/ReactiveCocoa @@ -1 +1 @@ -Subproject commit ff52a3ea246b459c6b0a956f1ec6e67e7b333f38 +Subproject commit 58fbf76861c80babd93966b57e69683aed914ab3 diff --git a/Carthage/Checkouts/Result b/Carthage/Checkouts/Result index 918d63b99b..b1638cd1ca 160000 --- a/Carthage/Checkouts/Result +++ b/Carthage/Checkouts/Result @@ -1 +1 @@ -Subproject commit 918d63b99b76d424c299c8b5efea7dc3e8d286fc +Subproject commit b1638cd1ca4f9c9c169f1f8f105c09de58efeb4d diff --git a/Source/Foundation/NSData.swift b/Source/Foundation/NSData.swift index 335a7bc690..d3983f18a8 100644 --- a/Source/Foundation/NSData.swift +++ b/Source/Foundation/NSData.swift @@ -19,7 +19,7 @@ extension NSData { observer.sendNext(data) observer.sendCompleted() } catch { - observer.sendError(error as NSError) + observer.sendFailed(error as NSError) } } } diff --git a/Source/Signal.swift b/Source/Signal.swift index b301b9be0e..b48448526f 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -18,8 +18,8 @@ extension SignalType { if let val = transform(value) { observer.sendNext(val) } - }, error: { error in - observer.sendError(error) + }, failed: { error in + observer.sendFailed(error) }, completed: { observer.sendCompleted() }, interrupted: { @@ -38,7 +38,7 @@ extension SignalType { switch event { case let .Next(value): observer.sendNext(value) - case .Error: + case .Failed: observer.action(replacement) case .Completed: observer.sendCompleted() @@ -80,8 +80,8 @@ extension SignalType where Value: SequenceType { switch event { case let .Next(sequence): sequence.forEach { observer.sendNext($0) } - case let .Error(error): - observer.sendError(error) + case let .Failed(error): + observer.sendFailed(error) case .Completed: observer.sendCompleted() case .Interrupted: diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index c93dcd130f..7c4857f540 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -36,9 +36,9 @@ extension SignalProducerType { group!.sendNext(value) - }, error: { error in - observer.sendError(error) - groups.values.forEach { $0.sendError(error) } + }, failed: { error in + observer.sendFailed(error) + groups.values.forEach { $0.sendFailed(error) } }, completed: { _ in observer.sendCompleted() diff --git a/Tests/SignalTests.swift b/Tests/SignalTests.swift index 9d0efcfb05..fa0e5e2f62 100644 --- a/Tests/SignalTests.swift +++ b/Tests/SignalTests.swift @@ -48,7 +48,7 @@ final class SignalTests: XCTestCase { sink.sendNext(1) XCTAssertFalse(completed) - sink.sendError(.Default) + sink.sendFailed(.Default) XCTAssertTrue(completed) } @@ -65,7 +65,7 @@ final class SignalTests: XCTestCase { sink.sendNext(1) XCTAssertFalse(interrupted) - sink.sendError(.Default) + sink.sendFailed(.Default) XCTAssertTrue(interrupted) } From 35afe8a8b5e910c08fb1fb570dacf76da3691d86 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Fri, 23 Oct 2015 18:06:29 -0400 Subject: [PATCH 0163/1028] Remove commented out code --- Source/Foundation/Association.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Foundation/Association.swift b/Source/Foundation/Association.swift index 6109df9609..87f7b65761 100644 --- a/Source/Foundation/Association.swift +++ b/Source/Foundation/Association.swift @@ -74,7 +74,6 @@ public func associatedObject(host: AnyObject, key: UnsafePointer<( if value == nil { value = initial() objc_setAssociatedObject(host, key, value, .OBJC_ASSOCIATION_RETAIN) -// objc_setAssociatedObject(host, key, value, objc_AssociationPolicy()) } return value! } From c08502fa5949fc729a10fafa6dcca12fea0260fe Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Fri, 23 Oct 2015 18:07:52 -0400 Subject: [PATCH 0164/1028] Update NSTextField.rex_textSignal comment --- Source/AppKit/NSTextField.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/AppKit/NSTextField.swift b/Source/AppKit/NSTextField.swift index 8d0db6f7c7..c7bf7b75d5 100644 --- a/Source/AppKit/NSTextField.swift +++ b/Source/AppKit/NSTextField.swift @@ -11,7 +11,7 @@ import ReactiveCocoa import AppKit extension NSTextField { - /// only changes from UI will be produced here + /// Sends the field's string value whenever it changes. public var rex_textSignal: SignalProducer { return NSNotificationCenter.defaultCenter() .rac_notifications(NSControlTextDidChangeNotification, object: self) From 268e4f2aaa23b2e4949931826380d419d965df6c Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 25 Oct 2015 10:25:34 -0400 Subject: [PATCH 0165/1028] Update RAC to 4.0-alpha.3 --- Cartfile | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/ReactiveCocoa | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cartfile b/Cartfile index 5c6f9ac924..822b36944e 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveCocoa" "58fbf768" +github "ReactiveCocoa/ReactiveCocoa" "v4.0.0-alpha.3" diff --git a/Cartfile.resolved b/Cartfile.resolved index faf36736f0..2c22f63c20 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ github "antitypical/Result" "0.6.0-beta.5" -github "ReactiveCocoa/ReactiveCocoa" "58fbf76861c80babd93966b57e69683aed914ab3" +github "ReactiveCocoa/ReactiveCocoa" "v4.0.0-alpha.3" diff --git a/Carthage/Checkouts/ReactiveCocoa b/Carthage/Checkouts/ReactiveCocoa index 58fbf76861..6e202fb9dc 160000 --- a/Carthage/Checkouts/ReactiveCocoa +++ b/Carthage/Checkouts/ReactiveCocoa @@ -1 +1 @@ -Subproject commit 58fbf76861c80babd93966b57e69683aed914ab3 +Subproject commit 6e202fb9dc03030a0c308f6e68d73c56bc288802 From 2d359f9133a893c1663739291e0e91c6600705e6 Mon Sep 17 00:00:00 2001 From: Yury Lapitsky Date: Fri, 30 Oct 2015 14:38:38 +0300 Subject: [PATCH 0166/1028] UIButton.rex_pressed example in README --- README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.md b/README.md index c48c83ada2..87b967c806 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,28 @@ func propertySink(sink: S)(signal: Signal) -> P func propertySink(sink: S)(producer: SignalProducer) -> PropertyOf ``` +## UIKit Extensions + +##### `UIButton.rex_pressed` + +Flexible way to bind CocoaAction to the press of button. + +```swift +let downloadAction: Action = Action { _ in + return SignalProducer { observer, disposable in + if let zipURL = NSURL(string: "https://github.com/neilpa/Rex/archive/master.zip") { + if let zipData = NSData(contentsOfURL: zipURL) { + observer.sendNext(zipData) + observer.sendCompleted() + } + } + } + } + +let downloadCocoaAction = CocoaAction(downloadAction, input: ()) +let cocoaActionProducer: SignalProducer = SignalProducer(value: downloadCocoaAction) +startDownloadButton.rex_pressed <~ cocoaActionProducer +``` ## License Rex is released under the [MIT license](LICENSE) From b402f1cea670425664f906945b1cf9ceeebf8118 Mon Sep 17 00:00:00 2001 From: Yury Lapitsky Date: Fri, 30 Oct 2015 22:50:39 +0300 Subject: [PATCH 0167/1028] Code zip and extended explanation --- README.md | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 87b967c806..ef12f1c331 100644 --- a/README.md +++ b/README.md @@ -70,23 +70,16 @@ func propertySink(sink: S)(producer: SignalProducer = Action { _ in - return SignalProducer { observer, disposable in - if let zipURL = NSURL(string: "https://github.com/neilpa/Rex/archive/master.zip") { - if let zipData = NSData(contentsOfURL: zipURL) { - observer.sendNext(zipData) - observer.sendCompleted() - } - } - } - } - -let downloadCocoaAction = CocoaAction(downloadAction, input: ()) -let cocoaActionProducer: SignalProducer = SignalProducer(value: downloadCocoaAction) -startDownloadButton.rex_pressed <~ cocoaActionProducer +let downloadAction = Action { _ in + let url = NSURL(string: "https://github.com/neilpa/Rex/archive/master.zip") + let request = NSURLRequest(URL: url!) + return NSURLSession.sharedSession().rac_dataWithRequest(request).map { $0.0 } +} + +downloadButton.rex_pressed.value = downloadAction.unsafeCocoaAction ``` ## License From afbe3476fc3f8f74904459aa73630df800359610 Mon Sep 17 00:00:00 2001 From: skyylex Date: Wed, 4 Nov 2015 15:43:42 +0300 Subject: [PATCH 0168/1028] Functional tests for UIKit Extensions --- Tests/UIKit/UIBarButtonItemTests.swift | 12 ++++++ Tests/UIKit/UIButtonTests.swift | 51 ++++++++++++++++++++++++++ Tests/UIKit/UIControlTests.swift | 39 ++++++++++++++++++++ Tests/UIKit/UIImageViewTests.swift | 30 +++++++++++++++ Tests/UIKit/UILabelTests.swift | 16 ++++++++ Tests/UIKit/UIViewTests.swift | 32 ++++++++++++++++ 6 files changed, 180 insertions(+) diff --git a/Tests/UIKit/UIBarButtonItemTests.swift b/Tests/UIKit/UIBarButtonItemTests.swift index 2336d5ddca..fc93e27937 100644 --- a/Tests/UIKit/UIBarButtonItemTests.swift +++ b/Tests/UIKit/UIBarButtonItemTests.swift @@ -28,5 +28,17 @@ class UIBarButtonItemTests: XCTestCase { } barButtonItem.rex_action <~ SignalProducer(value: CocoaAction(action, input: ())) } + + func testEnabledProperty() { + let barButtonItem = UIBarButtonItem() + let (pipeSignal, observer) = Signal.pipe() + barButtonItem.rex_enabled <~ SignalProducer(signal: pipeSignal) + barButtonItem.enabled = true + + observer.sendNext(false) + XCTAssert(barButtonItem.enabled == false, "#1 change of the enabled state failed [UIBarButtonItem]") + observer.sendNext(true) + XCTAssert(barButtonItem.enabled == true, "#2 change of the enabled state failed [UIBarButtonItem]") + } } diff --git a/Tests/UIKit/UIButtonTests.swift b/Tests/UIKit/UIButtonTests.swift index cfcf085da0..fc72219d4d 100644 --- a/Tests/UIKit/UIButtonTests.swift +++ b/Tests/UIKit/UIButtonTests.swift @@ -10,6 +10,17 @@ import ReactiveCocoa import UIKit import XCTest +public extension UIButton { + static func button() -> UIButton { + let button = UIButton(type: UIButtonType.Custom) + return button; + } + + override func sendAction(action: Selector, to target: AnyObject?, forEvent event: UIEvent?) { + target?.performSelector(action) + } +} + class UIButtonTests: XCTestCase { weak var _button: UIButton? @@ -44,5 +55,45 @@ class UIButtonTests: XCTestCase { button.rex_title <~ SignalProducer(value: "button") XCTAssert(_button?.titleForState(.Normal) == "button") } + + func testTitleProperty() { + let firstTitle = "First title" + let secondTitle = "Second title" + let button = UIButton(frame: CGRectZero) + let (pipeSignal, observer) = Signal.pipe() + button.rex_title <~ SignalProducer(signal: pipeSignal) + button.setTitle("", forState: .Selected) + button.setTitle("", forState: .Highlighted) + + observer.sendNext(firstTitle) + XCTAssert(button.titleForState(.Normal) == firstTitle, "#1 change of the title for .Normal state failed [UIButton]") + XCTAssert(button.titleForState(.Highlighted) == "", "#1 .Highlighted state shouldn't change [UIButton]") + XCTAssert(button.titleForState(.Selected) == "", "#1 .Selected state shouldn't change [UIButton]") + + observer.sendNext(secondTitle) + XCTAssert(button.titleForState(.Normal) == secondTitle, "#2 change of the title for .Normal state failed [UIButton]") + XCTAssert(button.titleForState(.Highlighted) == "", "#2 .Highlighted state shouldn't change [UIButton]") + XCTAssert(button.titleForState(.Selected) == "", "#2 .Selected state shouldn't change [UIButton]") + } + + func testPressedProperty() { + let button = UIButton(frame: CGRectZero) + button.enabled = true + button.userInteractionEnabled = true + + let passed = MutableProperty(false) + let action = Action<(), Bool, NoError> { _ in + SignalProducer(value: true) + } + + passed <~ SignalProducer(signal: action.values) + button.rex_pressed.value = CocoaAction(action, input: ()) + + button.sendActionsForControlEvents(.TouchUpInside) + + XCTAssert(passed.value == true, "Press doesn't perform cocoa action") + } + + } diff --git a/Tests/UIKit/UIControlTests.swift b/Tests/UIKit/UIControlTests.swift index 312c45152a..a0513fa0c5 100644 --- a/Tests/UIKit/UIControlTests.swift +++ b/Tests/UIKit/UIControlTests.swift @@ -42,4 +42,43 @@ class UIControlTests: XCTestCase { control.rex_highlighted <~ SignalProducer(value: true) XCTAssert(_control?.highlighted == true) } + + func testEnabledProperty () { + let control = UIControl(frame: CGRectZero) + control.enabled = false + + let (pipeSignal, observer) = Signal.pipe() + control.rex_enabled <~ SignalProducer(signal: pipeSignal) + + observer.sendNext(true) + XCTAssert(control.enabled == true, "UIControl.rex_enabled change #1 failed") + observer.sendNext(false) + XCTAssert(control.enabled == false, "UIControl.rex_enabled change #2 failed") + } + + func testSelectedProperty() { + let control = UIControl(frame: CGRectZero) + control.selected = false + + let (pipeSignal, observer) = Signal.pipe() + control.rex_selected <~ SignalProducer(signal: pipeSignal) + + observer.sendNext(true) + XCTAssert(control.selected == true, "UIControl.rex_selected change #1 failed") + observer.sendNext(false) + XCTAssert(control.selected == false, "UIControl.rex_selected change #2 failed") + } + + func testHighlightedProperty() { + let control = UIControl(frame: CGRectZero) + control.highlighted = false + + let (pipeSignal, observer) = Signal.pipe() + control.rex_highlighted <~ SignalProducer(signal: pipeSignal) + + observer.sendNext(true) + XCTAssert(control.highlighted == true, "UIControl.rex_highlighted change #1 failed") + observer.sendNext(false) + XCTAssert(control.highlighted == false, "UIControl.rex_highlighted change #2 failed") + } } diff --git a/Tests/UIKit/UIImageViewTests.swift b/Tests/UIKit/UIImageViewTests.swift index 0f367bf1b5..5df3cda8fb 100644 --- a/Tests/UIKit/UIImageViewTests.swift +++ b/Tests/UIKit/UIImageViewTests.swift @@ -38,4 +38,34 @@ class UIImageViewTests: XCTestCase { imageView.rex_highlightedImage <~ SignalProducer(value: image) XCTAssert(_imageView?.highlightedImage == image) } + + func testImageProperty() { + let imageView = UIImageView(frame: CGRectZero) + + let firstChange = UIImage() + let secondChange = UIImage() + + let (pipeSignal, observer) = Signal.pipe() + imageView.rex_image <~ SignalProducer(signal: pipeSignal) + + observer.sendNext(firstChange) + XCTAssert(imageView.image === firstChange, "UIImageView.rex_image change #1 failed") + observer.sendNext(secondChange) + XCTAssert(imageView.image === secondChange, "UIImageView.rex_image change #2 failed") + } + + func testHighlightedImageProperty() { + let imageView = UIImageView(frame: CGRectZero) + + let firstChange = UIImage() + let secondChange = UIImage() + + let (pipeSignal, observer) = Signal.pipe() + imageView.rex_highlightedImage <~ SignalProducer(signal: pipeSignal) + + observer.sendNext(firstChange) + XCTAssert(imageView.highlightedImage === firstChange, "UIImageView.rex_highlightedImage change #1 failed") + observer.sendNext(secondChange) + XCTAssert(imageView.highlightedImage === secondChange, "UIImageView.rex_highlightedImage change #2 failed") + } } diff --git a/Tests/UIKit/UILabelTests.swift b/Tests/UIKit/UILabelTests.swift index 299f28415d..be2cb9cf6d 100644 --- a/Tests/UIKit/UILabelTests.swift +++ b/Tests/UIKit/UILabelTests.swift @@ -26,4 +26,20 @@ class UILabelTests: XCTestCase { label.rex_text <~ SignalProducer(value: "Test") XCTAssert(_label?.text == "Test") } + + func testTextProperty() { + let firstChange = "first" + let secondChange = "second" + + let label = UILabel(frame: CGRectZero) + label.text = "" + + let (pipeSignal, observer) = Signal.pipe() + label.rex_text <~ SignalProducer(signal: pipeSignal) + + observer.sendNext(firstChange) + XCTAssert(label.text == firstChange, "UILabel.rex_text change #1 failed") + observer.sendNext(secondChange) + XCTAssert(label.text == secondChange, "UILabel.rex_text change #2 failed") + } } diff --git a/Tests/UIKit/UIViewTests.swift b/Tests/UIKit/UIViewTests.swift index 8873171ebd..bcda4f01bf 100644 --- a/Tests/UIKit/UIViewTests.swift +++ b/Tests/UIKit/UIViewTests.swift @@ -34,4 +34,36 @@ class UIViewTests: XCTestCase { view.rex_hidden <~ SignalProducer(value: true) XCTAssert(_view?.hidden == true) } + + func testHiddenProperty() { + let view = UIView(frame: CGRectZero) + view.hidden = true + + let firstChange = false + let secondChange = true + + let (pipeSignal, observer) = Signal.pipe() + view.rex_hidden <~ SignalProducer(signal: pipeSignal) + + observer.sendNext(firstChange) + XCTAssert(view.hidden == firstChange, "UIView.rex_hidden change #1 failed") + observer.sendNext(secondChange) + XCTAssert(view.hidden == secondChange, "UIView.rex_hidden change #2 failed") + } + + func testAlphaProperty() { + let view = UIView(frame: CGRectZero) + view.alpha = 0.0 + + let firstChange = CGFloat(0.5) + let secondChange = CGFloat(0.7) + + let (pipeSignal, observer) = Signal.pipe() + view.rex_alpha <~ SignalProducer(signal: pipeSignal) + + observer.sendNext(firstChange) + XCTAssert(view.alpha == firstChange, "UIView.rex_alpha change #1 failed") + observer.sendNext(secondChange) + XCTAssert(view.alpha == secondChange, "UIView.rex_alpha change #2 failed") + } } \ No newline at end of file From 75f87bdd278437df5b24f35cd5b1522165aef79a Mon Sep 17 00:00:00 2001 From: skyylex Date: Mon, 9 Nov 2015 14:15:56 +0300 Subject: [PATCH 0169/1028] Improving UIKit test with proper test operators. --- Tests/UIKit/UIBarButtonItemTests.swift | 8 +++++--- Tests/UIKit/UIButtonTests.swift | 18 +++++++++--------- Tests/UIKit/UIControlTests.swift | 12 ++++++------ Tests/UIKit/UIImageViewTests.swift | 8 ++++---- Tests/UIKit/UILabelTests.swift | 4 ++-- Tests/UIKit/UIViewTests.swift | 15 ++++++--------- 6 files changed, 32 insertions(+), 33 deletions(-) diff --git a/Tests/UIKit/UIBarButtonItemTests.swift b/Tests/UIKit/UIBarButtonItemTests.swift index fc93e27937..97e2e78493 100644 --- a/Tests/UIKit/UIBarButtonItemTests.swift +++ b/Tests/UIKit/UIBarButtonItemTests.swift @@ -31,14 +31,16 @@ class UIBarButtonItemTests: XCTestCase { func testEnabledProperty() { let barButtonItem = UIBarButtonItem() + barButtonItem.enabled = true + let (pipeSignal, observer) = Signal.pipe() barButtonItem.rex_enabled <~ SignalProducer(signal: pipeSignal) - barButtonItem.enabled = true + observer.sendNext(false) - XCTAssert(barButtonItem.enabled == false, "#1 change of the enabled state failed [UIBarButtonItem]") + XCTAssertFalse(barButtonItem.enabled) observer.sendNext(true) - XCTAssert(barButtonItem.enabled == true, "#2 change of the enabled state failed [UIBarButtonItem]") + XCTAssertTrue(barButtonItem.enabled) } } diff --git a/Tests/UIKit/UIButtonTests.swift b/Tests/UIKit/UIButtonTests.swift index fc72219d4d..87899a284b 100644 --- a/Tests/UIKit/UIButtonTests.swift +++ b/Tests/UIKit/UIButtonTests.swift @@ -10,13 +10,13 @@ import ReactiveCocoa import UIKit import XCTest -public extension UIButton { +extension UIButton { static func button() -> UIButton { let button = UIButton(type: UIButtonType.Custom) return button; } - override func sendAction(action: Selector, to target: AnyObject?, forEvent event: UIEvent?) { + override public func sendAction(action: Selector, to target: AnyObject?, forEvent event: UIEvent?) { target?.performSelector(action) } } @@ -66,14 +66,14 @@ class UIButtonTests: XCTestCase { button.setTitle("", forState: .Highlighted) observer.sendNext(firstTitle) - XCTAssert(button.titleForState(.Normal) == firstTitle, "#1 change of the title for .Normal state failed [UIButton]") - XCTAssert(button.titleForState(.Highlighted) == "", "#1 .Highlighted state shouldn't change [UIButton]") - XCTAssert(button.titleForState(.Selected) == "", "#1 .Selected state shouldn't change [UIButton]") + XCTAssertEqual(button.titleForState(.Normal), firstTitle) + XCTAssertEqual(button.titleForState(.Highlighted), "") + XCTAssertEqual(button.titleForState(.Selected), "") observer.sendNext(secondTitle) - XCTAssert(button.titleForState(.Normal) == secondTitle, "#2 change of the title for .Normal state failed [UIButton]") - XCTAssert(button.titleForState(.Highlighted) == "", "#2 .Highlighted state shouldn't change [UIButton]") - XCTAssert(button.titleForState(.Selected) == "", "#2 .Selected state shouldn't change [UIButton]") + XCTAssertEqual(button.titleForState(.Normal), secondTitle) + XCTAssertEqual(button.titleForState(.Highlighted), "") + XCTAssertEqual(button.titleForState(.Selected), "") } func testPressedProperty() { @@ -91,7 +91,7 @@ class UIButtonTests: XCTestCase { button.sendActionsForControlEvents(.TouchUpInside) - XCTAssert(passed.value == true, "Press doesn't perform cocoa action") + XCTAssertTrue(passed.value) } diff --git a/Tests/UIKit/UIControlTests.swift b/Tests/UIKit/UIControlTests.swift index a0513fa0c5..b6eb474875 100644 --- a/Tests/UIKit/UIControlTests.swift +++ b/Tests/UIKit/UIControlTests.swift @@ -51,9 +51,9 @@ class UIControlTests: XCTestCase { control.rex_enabled <~ SignalProducer(signal: pipeSignal) observer.sendNext(true) - XCTAssert(control.enabled == true, "UIControl.rex_enabled change #1 failed") + XCTAssertTrue(control.enabled) observer.sendNext(false) - XCTAssert(control.enabled == false, "UIControl.rex_enabled change #2 failed") + XCTAssertFalse(control.enabled) } func testSelectedProperty() { @@ -64,9 +64,9 @@ class UIControlTests: XCTestCase { control.rex_selected <~ SignalProducer(signal: pipeSignal) observer.sendNext(true) - XCTAssert(control.selected == true, "UIControl.rex_selected change #1 failed") + XCTAssertTrue(control.selected) observer.sendNext(false) - XCTAssert(control.selected == false, "UIControl.rex_selected change #2 failed") + XCTAssertFalse(control.selected) } func testHighlightedProperty() { @@ -77,8 +77,8 @@ class UIControlTests: XCTestCase { control.rex_highlighted <~ SignalProducer(signal: pipeSignal) observer.sendNext(true) - XCTAssert(control.highlighted == true, "UIControl.rex_highlighted change #1 failed") + XCTAssertTrue(control.highlighted) observer.sendNext(false) - XCTAssert(control.highlighted == false, "UIControl.rex_highlighted change #2 failed") + XCTAssertFalse(control.highlighted) } } diff --git a/Tests/UIKit/UIImageViewTests.swift b/Tests/UIKit/UIImageViewTests.swift index 5df3cda8fb..bb98ea9979 100644 --- a/Tests/UIKit/UIImageViewTests.swift +++ b/Tests/UIKit/UIImageViewTests.swift @@ -49,9 +49,9 @@ class UIImageViewTests: XCTestCase { imageView.rex_image <~ SignalProducer(signal: pipeSignal) observer.sendNext(firstChange) - XCTAssert(imageView.image === firstChange, "UIImageView.rex_image change #1 failed") + XCTAssertEqual(imageView.image, firstChange) observer.sendNext(secondChange) - XCTAssert(imageView.image === secondChange, "UIImageView.rex_image change #2 failed") + XCTAssertEqual(imageView.image, secondChange) } func testHighlightedImageProperty() { @@ -64,8 +64,8 @@ class UIImageViewTests: XCTestCase { imageView.rex_highlightedImage <~ SignalProducer(signal: pipeSignal) observer.sendNext(firstChange) - XCTAssert(imageView.highlightedImage === firstChange, "UIImageView.rex_highlightedImage change #1 failed") + XCTAssertEqual(imageView.highlightedImage, firstChange) observer.sendNext(secondChange) - XCTAssert(imageView.highlightedImage === secondChange, "UIImageView.rex_highlightedImage change #2 failed") + XCTAssertEqual(imageView.highlightedImage, secondChange) } } diff --git a/Tests/UIKit/UILabelTests.swift b/Tests/UIKit/UILabelTests.swift index be2cb9cf6d..e365e2865e 100644 --- a/Tests/UIKit/UILabelTests.swift +++ b/Tests/UIKit/UILabelTests.swift @@ -38,8 +38,8 @@ class UILabelTests: XCTestCase { label.rex_text <~ SignalProducer(signal: pipeSignal) observer.sendNext(firstChange) - XCTAssert(label.text == firstChange, "UILabel.rex_text change #1 failed") + XCTAssertEqual(label.text, firstChange) observer.sendNext(secondChange) - XCTAssert(label.text == secondChange, "UILabel.rex_text change #2 failed") + XCTAssertEqual(label.text, secondChange) } } diff --git a/Tests/UIKit/UIViewTests.swift b/Tests/UIKit/UIViewTests.swift index bcda4f01bf..888107e8fe 100644 --- a/Tests/UIKit/UIViewTests.swift +++ b/Tests/UIKit/UIViewTests.swift @@ -39,16 +39,13 @@ class UIViewTests: XCTestCase { let view = UIView(frame: CGRectZero) view.hidden = true - let firstChange = false - let secondChange = true - let (pipeSignal, observer) = Signal.pipe() view.rex_hidden <~ SignalProducer(signal: pipeSignal) - observer.sendNext(firstChange) - XCTAssert(view.hidden == firstChange, "UIView.rex_hidden change #1 failed") - observer.sendNext(secondChange) - XCTAssert(view.hidden == secondChange, "UIView.rex_hidden change #2 failed") + observer.sendNext(true) + XCTAssertTrue(view.hidden) + observer.sendNext(false) + XCTAssertFalse(view.hidden) } func testAlphaProperty() { @@ -62,8 +59,8 @@ class UIViewTests: XCTestCase { view.rex_alpha <~ SignalProducer(signal: pipeSignal) observer.sendNext(firstChange) - XCTAssert(view.alpha == firstChange, "UIView.rex_alpha change #1 failed") + XCTAssertEqual(view.alpha, firstChange) observer.sendNext(secondChange) - XCTAssert(view.alpha == secondChange, "UIView.rex_alpha change #2 failed") + XCTAssertEqual(view.alpha, secondChange) } } \ No newline at end of file From 42435875167de59d530a3df2c153eb34baf601ad Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Thu, 12 Nov 2015 12:09:34 +0100 Subject: [PATCH 0170/1028] add `rex_textColor` to UILabel --- Source/UIKit/UILabel.swift | 4 ++++ Tests/UIKit/UILabelTests.swift | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/Source/UIKit/UILabel.swift b/Source/UIKit/UILabel.swift index d353f5e909..719c22ad7c 100644 --- a/Source/UIKit/UILabel.swift +++ b/Source/UIKit/UILabel.swift @@ -14,4 +14,8 @@ extension UILabel { public var rex_text: MutableProperty { return rex_stringProperty("text") } + + public var rex_textColor: MutableProperty { + return rex_valueProperty(&textColor, { [weak self] in (self?.textColor)! }, { [weak self] in self?.textColor = $0 }) + } } diff --git a/Tests/UIKit/UILabelTests.swift b/Tests/UIKit/UILabelTests.swift index e365e2865e..24c64c48a0 100644 --- a/Tests/UIKit/UILabelTests.swift +++ b/Tests/UIKit/UILabelTests.swift @@ -42,4 +42,20 @@ class UILabelTests: XCTestCase { observer.sendNext(secondChange) XCTAssertEqual(label.text, secondChange) } + + func testTextColorProperty() { + let firstChange = UIColor.redColor() + let secondChange = UIColor.blackColor() + + let label = UILabel(frame: CGRectZero) + + let (pipeSignal, observer) = Signal.pipe() + label.textColor = UIColor.blackColor() + label.rex_textColor <~ SignalProducer(signal: pipeSignal) + + observer.sendNext(firstChange) + XCTAssertEqual(label.textColor, firstChange) + observer.sendNext(secondChange) + XCTAssertEqual(label.textColor, secondChange) + } } From ab52f03031cec7302923d6d265a48ed871a85aca Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Thu, 12 Nov 2015 13:19:46 +0100 Subject: [PATCH 0171/1028] fix crash in UIButtonTests caused by `performSelector` without object where object was expected --- Tests/UIKit/UIButtonTests.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Tests/UIKit/UIButtonTests.swift b/Tests/UIKit/UIButtonTests.swift index 87899a284b..5fa5be878a 100644 --- a/Tests/UIKit/UIButtonTests.swift +++ b/Tests/UIKit/UIButtonTests.swift @@ -17,7 +17,7 @@ extension UIButton { } override public func sendAction(action: Selector, to target: AnyObject?, forEvent event: UIEvent?) { - target?.performSelector(action) + target?.performSelector(action, withObject: nil) } } @@ -87,10 +87,11 @@ class UIButtonTests: XCTestCase { } passed <~ SignalProducer(signal: action.values) - button.rex_pressed.value = CocoaAction(action, input: ()) + button.rex_pressed <~ SignalProducer(value: CocoaAction(action, input: ())) button.sendActionsForControlEvents(.TouchUpInside) + XCTAssertTrue(passed.value) } From 41d5c2292806776049e7bf0bc2913808e54a424f Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Thu, 12 Nov 2015 13:23:29 +0100 Subject: [PATCH 0172/1028] fix assertions for floats by using XCTAssertEqualWithAccuracy --- Tests/UIKit/UIViewTests.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/UIKit/UIViewTests.swift b/Tests/UIKit/UIViewTests.swift index 888107e8fe..08828b2865 100644 --- a/Tests/UIKit/UIViewTests.swift +++ b/Tests/UIKit/UIViewTests.swift @@ -24,7 +24,7 @@ class UIViewTests: XCTestCase { _view = view view.rex_alpha <~ SignalProducer(value: 0.5) - XCTAssert(_view?.alpha == 0.5) + XCTAssertEqualWithAccuracy(_view!.alpha, 0.5, accuracy: 0.01) } func testHiddenPropertyDoesntCreateRetainCycle() { @@ -59,8 +59,8 @@ class UIViewTests: XCTestCase { view.rex_alpha <~ SignalProducer(signal: pipeSignal) observer.sendNext(firstChange) - XCTAssertEqual(view.alpha, firstChange) + XCTAssertEqualWithAccuracy(view.alpha, firstChange, accuracy: 0.01) observer.sendNext(secondChange) - XCTAssertEqual(view.alpha, secondChange) + XCTAssertEqualWithAccuracy(view.alpha, secondChange, accuracy: 0.01) } } \ No newline at end of file From e00912c7c169814a1aafb2f17746d0192c36b4fb Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Tue, 17 Nov 2015 14:24:57 +0100 Subject: [PATCH 0173/1028] Add testcase that replicates a bug with binding UIControl.enabled and UIControl.selected --- Tests/UIKit/UIControlTests.swift | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Tests/UIKit/UIControlTests.swift b/Tests/UIKit/UIControlTests.swift index b6eb474875..d25bd11235 100644 --- a/Tests/UIKit/UIControlTests.swift +++ b/Tests/UIKit/UIControlTests.swift @@ -81,4 +81,26 @@ class UIControlTests: XCTestCase { observer.sendNext(false) XCTAssertFalse(control.highlighted) } + + func testEnabledAndSelectedProperty() { + let control = UIControl(frame: CGRectZero) + control.selected = false + control.enabled = false + + let (pipeSignalSelected, observerSelected) = Signal.pipe() + let (pipeSignalEnabled, observerEnabled) = Signal.pipe() + control.rex_selected <~ SignalProducer(signal: pipeSignalSelected) + control.rex_enabled <~ SignalProducer(signal: pipeSignalEnabled) + + observerSelected.sendNext(true) + observerEnabled.sendNext(true) + XCTAssertTrue(control.enabled) + XCTAssertTrue(control.selected) + observerSelected.sendNext(false) + XCTAssertTrue(control.enabled) + XCTAssertFalse(control.selected) + observerEnabled.sendNext(false) + XCTAssertFalse(control.enabled) + XCTAssertFalse(control.selected) + } } From a071507afbd4814b5e54ca826094e2a0db9d4d98 Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Sun, 22 Nov 2015 13:44:05 +0100 Subject: [PATCH 0174/1028] Use the default color of a new UILabel as fallback instead of force unwrapping --- Source/UIKit/UILabel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/UIKit/UILabel.swift b/Source/UIKit/UILabel.swift index 719c22ad7c..c999eec597 100644 --- a/Source/UIKit/UILabel.swift +++ b/Source/UIKit/UILabel.swift @@ -16,6 +16,6 @@ extension UILabel { } public var rex_textColor: MutableProperty { - return rex_valueProperty(&textColor, { [weak self] in (self?.textColor)! }, { [weak self] in self?.textColor = $0 }) + return rex_valueProperty(&textColor, { [weak self] in self?.textColor ?? UILabel().textColor }, { [weak self] in self?.textColor = $0 }) } } From a295a1ae4013d19506cd784320249f39ecb9c7a0 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 22 Nov 2015 10:30:24 -0800 Subject: [PATCH 0175/1028] Rename associated object keys to avoid shadowing Previously these were taking the address of fields on `self` which shadowed the static keys declared in the file. This could've gone unnoticed if the fields on UIControl didn't all resolve to the same address. --- Source/UIKit/UIBarButtonItem.swift | 4 ++-- Source/UIKit/UIBarItem.swift | 4 ++-- Source/UIKit/UIButton.swift | 8 ++++---- Source/UIKit/UIControl.swift | 12 ++++++------ Source/UIKit/UIImageView.swift | 8 ++++---- Source/UIKit/UIView.swift | 8 ++++---- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/Source/UIKit/UIBarButtonItem.swift b/Source/UIKit/UIBarButtonItem.swift index f74b3a77e2..d48b7b3fe4 100644 --- a/Source/UIKit/UIBarButtonItem.swift +++ b/Source/UIKit/UIBarButtonItem.swift @@ -15,7 +15,7 @@ extension UIBarButtonItem { /// overwritten. 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: &action) { [weak self] _ in + return associatedObject(self, key: &actionKey) { [weak self] _ in let initial = CocoaAction.rex_disabled let property = MutableProperty(initial) @@ -33,4 +33,4 @@ extension UIBarButtonItem { } } -private var action: UInt8 = 0 +private var actionKey: UInt8 = 0 diff --git a/Source/UIKit/UIBarItem.swift b/Source/UIKit/UIBarItem.swift index d474406cff..76d33a294d 100644 --- a/Source/UIKit/UIBarItem.swift +++ b/Source/UIKit/UIBarItem.swift @@ -12,8 +12,8 @@ import UIKit extension UIBarItem { /// Wraps a UIBarItem's `enabled` state in a bindable property. public var rex_enabled: MutableProperty { - return associatedProperty(self, key: &enabled, initial: { [weak self] in self?.enabled ?? true }, setter: { [weak self] in self?.enabled = $0 }) + return associatedProperty(self, key: &enabledKey, initial: { [weak self] in self?.enabled ?? true }, setter: { [weak self] in self?.enabled = $0 }) } } -private var enabled: UInt8 = 0 +private var enabledKey: UInt8 = 0 diff --git a/Source/UIKit/UIButton.swift b/Source/UIKit/UIButton.swift index 7003f9892e..f8fb2b8a9c 100644 --- a/Source/UIKit/UIButton.swift +++ b/Source/UIKit/UIButton.swift @@ -15,7 +15,7 @@ extension UIButton { /// previous action is removed as a target. This also binds the enabled state of the /// action to the `rex_enabled` property on the button. public var rex_pressed: MutableProperty { - return associatedObject(self, key: &pressed, initial: { [weak self] _ in + return associatedObject(self, key: &pressedKey, initial: { [weak self] _ in let initial = CocoaAction.rex_disabled let property = MutableProperty(initial) @@ -36,9 +36,9 @@ 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 rex_valueProperty(&title, { [weak self] in self?.titleForState(.Normal) ?? "" }, { [weak self] in self?.setTitle($0, forState: .Normal) }) + return rex_valueProperty(&titleKey, { [weak self] in self?.titleForState(.Normal) ?? "" }, { [weak self] in self?.setTitle($0, forState: .Normal) }) } } -private var pressed: UInt8 = 0 -private var title: UInt8 = 0 +private var pressedKey: UInt8 = 0 +private var titleKey: UInt8 = 0 diff --git a/Source/UIKit/UIControl.swift b/Source/UIKit/UIControl.swift index 33b44ebade..7e30ec8b38 100644 --- a/Source/UIKit/UIControl.swift +++ b/Source/UIKit/UIControl.swift @@ -20,20 +20,20 @@ extension UIControl { /// Wraps a control's `enabled` state in a bindable property. public var rex_enabled: MutableProperty { - return rex_valueProperty(&enabled, { [weak self] in self?.enabled ?? true }, { [weak self] in self?.enabled = $0 }) + return rex_valueProperty(&enabledKey, { [weak self] in self?.enabled ?? true }, { [weak self] in self?.enabled = $0 }) } /// Wraps a control's `selected` state in a bindable property. public var rex_selected: MutableProperty { - return rex_valueProperty(&selected, { [weak self] in self?.selected ?? false }, { [weak self] in self?.selected = $0 }) + return rex_valueProperty(&selectedKey, { [weak self] in self?.selected ?? false }, { [weak self] in self?.selected = $0 }) } /// Wraps a control's `highlighted` state in a bindable property. public var rex_highlighted: MutableProperty { - return rex_valueProperty(&highlighted, { [weak self] in self?.highlighted ?? false }, { [weak self] in self?.highlighted = $0 }) + return rex_valueProperty(&highlightedKey, { [weak self] in self?.highlighted ?? false }, { [weak self] in self?.highlighted = $0 }) } } -private var enabled: UInt8 = 0 -private var selected: UInt8 = 0 -private var highlighted: UInt8 = 0 +private var enabledKey: UInt8 = 0 +private var selectedKey: UInt8 = 0 +private var highlightedKey: UInt8 = 0 diff --git a/Source/UIKit/UIImageView.swift b/Source/UIKit/UIImageView.swift index e522c01937..dfc41d60c7 100644 --- a/Source/UIKit/UIImageView.swift +++ b/Source/UIKit/UIImageView.swift @@ -12,14 +12,14 @@ import UIKit extension UIImageView { /// Wraps a imageView's `image` value in a bindable property. public var rex_image: MutableProperty { - return rex_valueProperty(&image, { [weak self] in self?.image }, { [weak self] in self?.image = $0 }) + return rex_valueProperty(&imageKey, { [weak self] in self?.image }, { [weak self] in self?.image = $0 }) } /// Wraps a imageView's `highlightedImage` value in a bindable property. public var rex_highlightedImage: MutableProperty { - return rex_valueProperty(&highlightedImage, { [weak self] in self?.highlightedImage }, { [weak self] in self?.highlightedImage = $0 }) + return rex_valueProperty(&highlightedImageKey, { [weak self] in self?.highlightedImage }, { [weak self] in self?.highlightedImage = $0 }) } } -private var image: UInt8 = 0 -private var highlightedImage: UInt8 = 0 \ No newline at end of file +private var imageKey: UInt8 = 0 +private var highlightedImageKey: UInt8 = 0 \ No newline at end of file diff --git a/Source/UIKit/UIView.swift b/Source/UIKit/UIView.swift index 853231080d..1ce154838b 100644 --- a/Source/UIKit/UIView.swift +++ b/Source/UIKit/UIView.swift @@ -12,14 +12,14 @@ import UIKit extension UIView { /// Wraps a view's `alpha` value in a bindable property. public var rex_alpha: MutableProperty { - return rex_valueProperty(&alpha, { [weak self] in self?.alpha ?? 1.0 }, { [weak self] in self?.alpha = $0 }) + return rex_valueProperty(&alphaKey, { [weak self] in self?.alpha ?? 1.0 }, { [weak self] in self?.alpha = $0 }) } /// Wraps a view's `hidden` state in a bindable property. public var rex_hidden: MutableProperty { - return rex_valueProperty(&hidden, { [weak self] in self?.hidden ?? false }, { [weak self] in self?.hidden = $0 }) + return rex_valueProperty(&hiddenKey, { [weak self] in self?.hidden ?? false }, { [weak self] in self?.hidden = $0 }) } } -private var alpha: UInt8 = 0 -private var hidden: UInt8 = 0 \ No newline at end of file +private var alphaKey: UInt8 = 0 +private var hiddenKey: UInt8 = 0 \ No newline at end of file From ac00b1a33bcdc6c39b5ff6874f92dfb78aec06b7 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 22 Nov 2015 11:32:01 -0800 Subject: [PATCH 0176/1028] Setup travis-ci --- .travis.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..1500e125e5 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,20 @@ +language: objective-c +matrix: + include: + - osx_image: xcode7.1 + +env: + global: + - LC_CTYPE=en_US.UTF-8 + +git: + submodules: false +before_install: + - git submodule update --init --recursive + +script: + - set -o pipefail + - xcodebuild test -scheme Rex-Mac | xcpretty -c + +notifications: + email: false From 4a596429beda80c58814b8a779ec5389b252a79b Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 22 Nov 2015 11:57:46 -0800 Subject: [PATCH 0177/1028] Run carthage on travis --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 1500e125e5..bd57bebf12 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,9 +11,13 @@ git: submodules: false before_install: - git submodule update --init --recursive + - curl -OlL "https://github.com/Carthage/Carthage/releases/download/0.10/Carthage.pkg" + - sudo installer -pkg "Carthage.pkg" -target / + - rm "Carthage.pkg" script: - set -o pipefail + - carthage bootstrap --platform Mac - xcodebuild test -scheme Rex-Mac | xcpretty -c notifications: From 6a6aee87064d3bfd5840ce5b3e0e9857d90afc53 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 22 Nov 2015 12:16:28 -0800 Subject: [PATCH 0178/1028] Try using the default travis test script --- .travis.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index bd57bebf12..034689e3ee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,9 @@ matrix: include: - osx_image: xcode7.1 +xcode_workspace: Rex.xcworkspace +xcode_scheme: Rex-Mac + env: global: - LC_CTYPE=en_US.UTF-8 @@ -15,10 +18,8 @@ before_install: - sudo installer -pkg "Carthage.pkg" -target / - rm "Carthage.pkg" -script: - - set -o pipefail +before_script: - carthage bootstrap --platform Mac - - xcodebuild test -scheme Rex-Mac | xcpretty -c notifications: email: false From 28c993ab90923d7432602732a7c187a387d04d46 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 22 Nov 2015 12:24:16 -0800 Subject: [PATCH 0179/1028] Revert "Try using the default travis test script" This reverts commit 6a6aee87064d3bfd5840ce5b3e0e9857d90afc53. --- .travis.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 034689e3ee..bd57bebf12 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,9 +3,6 @@ matrix: include: - osx_image: xcode7.1 -xcode_workspace: Rex.xcworkspace -xcode_scheme: Rex-Mac - env: global: - LC_CTYPE=en_US.UTF-8 @@ -18,8 +15,10 @@ before_install: - sudo installer -pkg "Carthage.pkg" -target / - rm "Carthage.pkg" -before_script: +script: + - set -o pipefail - carthage bootstrap --platform Mac + - xcodebuild test -scheme Rex-Mac | xcpretty -c notifications: email: false From c4b415dca5d9b1947248c3bd1c7adbc374c36060 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 22 Nov 2015 12:26:09 -0800 Subject: [PATCH 0180/1028] Add iOS testing to Travis --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index bd57bebf12..282acd1987 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,8 +17,9 @@ before_install: script: - set -o pipefail - - carthage bootstrap --platform Mac + - carthage bootstrap --platform Mac,iOS - xcodebuild test -scheme Rex-Mac | xcpretty -c + - xcodebuild test -scheme Rex-iOS -sdk iphonesimulator | xcpretty -c notifications: email: false From 5efe98e16be141b02e44aae737e171ba5dd758ce Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 22 Nov 2015 12:47:15 -0800 Subject: [PATCH 0181/1028] Remove xcpretty from travis to test --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 282acd1987..e29db46b70 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,8 +18,8 @@ before_install: script: - set -o pipefail - carthage bootstrap --platform Mac,iOS - - xcodebuild test -scheme Rex-Mac | xcpretty -c - - xcodebuild test -scheme Rex-iOS -sdk iphonesimulator | xcpretty -c + - xcodebuild test -scheme Rex-Mac + - xcodebuild test -scheme Rex-iOS -sdk iphonesimulator notifications: email: false From 182e077ffa11c6fb1e8d07637929ce971916b253 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 22 Nov 2015 13:17:38 -0800 Subject: [PATCH 0182/1028] Specify iphonesimulator for carthage in travis --- .travis.yml | 2 +- Carthage/Checkouts/ReactiveCocoa | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 160000 Carthage/Checkouts/ReactiveCocoa diff --git a/.travis.yml b/.travis.yml index e29db46b70..b62fd60f4f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ before_install: script: - set -o pipefail - - carthage bootstrap --platform Mac,iOS + - carthage bootstrap --platform mac,iphonesimulator - xcodebuild test -scheme Rex-Mac - xcodebuild test -scheme Rex-iOS -sdk iphonesimulator diff --git a/Carthage/Checkouts/ReactiveCocoa b/Carthage/Checkouts/ReactiveCocoa deleted file mode 160000 index 6e202fb9dc..0000000000 --- a/Carthage/Checkouts/ReactiveCocoa +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6e202fb9dc03030a0c308f6e68d73c56bc288802 From dd0a4011f3c86de0401cb814add0278041116103 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 22 Nov 2015 13:23:00 -0800 Subject: [PATCH 0183/1028] carthage bootstrap in travis should use submodules --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b62fd60f4f..f6e14c6577 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,14 +10,13 @@ env: git: submodules: false before_install: - - git submodule update --init --recursive - curl -OlL "https://github.com/Carthage/Carthage/releases/download/0.10/Carthage.pkg" - sudo installer -pkg "Carthage.pkg" -target / - rm "Carthage.pkg" script: - set -o pipefail - - carthage bootstrap --platform mac,iphonesimulator + - carthage bootstrap --use-submodules --platform mac,iphonesimulator - xcodebuild test -scheme Rex-Mac - xcodebuild test -scheme Rex-iOS -sdk iphonesimulator From 0d9797ad74bd31a7ffe6a67cf582f6f09de20b95 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 22 Nov 2015 13:42:56 -0800 Subject: [PATCH 0184/1028] Stop using submodules with carthage --- .gitignore | 2 +- .gitmodules | 6 ------ .travis.yml | 2 +- Carthage/Checkouts/Result | 1 - 4 files changed, 2 insertions(+), 9 deletions(-) delete mode 100644 .gitmodules delete mode 160000 Carthage/Checkouts/Result diff --git a/.gitignore b/.gitignore index 95ddb50814..6984e77129 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,4 @@ xcuserdata *.mode* *.pbxuser -Carthage/Build +/Carthage diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 90b7f65581..0000000000 --- a/.gitmodules +++ /dev/null @@ -1,6 +0,0 @@ -[submodule "Carthage/Checkouts/ReactiveCocoa"] - path = Carthage/Checkouts/ReactiveCocoa - url = https://github.com/ReactiveCocoa/ReactiveCocoa.git -[submodule "Carthage/Checkouts/Result"] - path = Carthage/Checkouts/Result - url = https://github.com/antitypical/Result.git diff --git a/.travis.yml b/.travis.yml index f6e14c6577..6294958286 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,7 @@ before_install: script: - set -o pipefail - - carthage bootstrap --use-submodules --platform mac,iphonesimulator + - carthage bootstrap --platform mac,iphonesimulator - xcodebuild test -scheme Rex-Mac - xcodebuild test -scheme Rex-iOS -sdk iphonesimulator diff --git a/Carthage/Checkouts/Result b/Carthage/Checkouts/Result deleted file mode 160000 index b1638cd1ca..0000000000 --- a/Carthage/Checkouts/Result +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b1638cd1ca4f9c9c169f1f8f105c09de58efeb4d From 09e71e32f4705316af8cee492de6b5da2a83eb66 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 22 Nov 2015 14:12:14 -0800 Subject: [PATCH 0185/1028] Disable ios builds in travis until RAC signing issue is resolved --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6294958286..821f20b430 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,9 +16,9 @@ before_install: script: - set -o pipefail - - carthage bootstrap --platform mac,iphonesimulator - - xcodebuild test -scheme Rex-Mac - - xcodebuild test -scheme Rex-iOS -sdk iphonesimulator + - carthage bootstrap --platform mac + - xcodebuild test -scheme Rex-Mac | xcpretty -c +# - xcodebuild test -scheme Rex-iOS -sdk iphonesimulator | xcpretty -c notifications: email: false From d44d29389fa66310808eef24ac67333cec1ada01 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 22 Nov 2015 14:15:25 -0800 Subject: [PATCH 0186/1028] Don't codesign simulator builds --- Rex.xcodeproj/project.pbxproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 3cf33f8c41..4ce240219a 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -740,6 +740,7 @@ buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphonesimulator*]" = ""; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; @@ -770,6 +771,7 @@ buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphonesimulator*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -795,6 +797,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphonesimulator*]" = ""; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; FRAMEWORK_SEARCH_PATHS = ( "$(SDKROOT)/Developer/Library/Frameworks", @@ -818,6 +821,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphonesimulator*]" = ""; FRAMEWORK_SEARCH_PATHS = ( "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", From 7a3c0d7c8d559d4b01effd69bb502e09ddda4d4d Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 22 Nov 2015 14:23:25 -0800 Subject: [PATCH 0187/1028] One last try at building ios on travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 821f20b430..2c0d6ff0ba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ script: - set -o pipefail - carthage bootstrap --platform mac - xcodebuild test -scheme Rex-Mac | xcpretty -c -# - xcodebuild test -scheme Rex-iOS -sdk iphonesimulator | xcpretty -c + - xcodebuild test -scheme Rex-iOS -sdk iphonesimulator | xcpretty -c notifications: email: false From fd3ba529bf1bca8cffe1a3b9008c56bad7930d17 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 22 Nov 2015 14:29:27 -0800 Subject: [PATCH 0188/1028] Forgot to add the iphonesimulator back to carthage bootstrap --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2c0d6ff0ba..b9f833eeea 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,7 @@ before_install: script: - set -o pipefail - - carthage bootstrap --platform mac + - carthage bootstrap --platform mac,iphonesimulator - xcodebuild test -scheme Rex-Mac | xcpretty -c - xcodebuild test -scheme Rex-iOS -sdk iphonesimulator | xcpretty -c From 7456ef0367a775230a61dacebfb6f5218ff83b49 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 22 Nov 2015 14:35:11 -0800 Subject: [PATCH 0189/1028] ios travis builds still don't work after updating rex code signing --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b9f833eeea..268f190690 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ script: - set -o pipefail - carthage bootstrap --platform mac,iphonesimulator - xcodebuild test -scheme Rex-Mac | xcpretty -c - - xcodebuild test -scheme Rex-iOS -sdk iphonesimulator | xcpretty -c +# - xcodebuild test -scheme Rex-iOS -sdk iphonesimulator | xcpretty -c notifications: email: false From add168a834195da9c035175e91ebdb16184a8c81 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 22 Nov 2015 14:45:22 -0800 Subject: [PATCH 0190/1028] Manually download RAC prebuilt framework for Travis This should workaround the download failures due to "Bad Credentials" on Travis via Carthage and the code signing issues. --- .travis.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 268f190690..53014a258b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,12 +13,15 @@ before_install: - curl -OlL "https://github.com/Carthage/Carthage/releases/download/0.10/Carthage.pkg" - sudo installer -pkg "Carthage.pkg" -target / - rm "Carthage.pkg" + - curl -OlL "https://github.com/ReactiveCocoa/ReactiveCocoa/releases/download/v4.0.0-alpha.3/ReactiveCocoa.framework.zip" + - unzip "ReactiveCocoa.framework.zip" + - rm "ReactiveCocoa.framework.zip" script: - set -o pipefail - carthage bootstrap --platform mac,iphonesimulator - xcodebuild test -scheme Rex-Mac | xcpretty -c -# - xcodebuild test -scheme Rex-iOS -sdk iphonesimulator | xcpretty -c + - xcodebuild test -scheme Rex-iOS -sdk iphonesimulator | xcpretty -c notifications: email: false From 75a289b1dae9437fd72dbbfd2045e0c039184af7 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 22 Nov 2015 17:44:49 -0800 Subject: [PATCH 0191/1028] PropertyOf => AnyProperty --- Source/Predicate.swift | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Source/Predicate.swift b/Source/Predicate.swift index b5fa32539a..045d47398d 100644 --- a/Source/Predicate.swift +++ b/Source/Predicate.swift @@ -23,8 +23,8 @@ extension PropertyType where Value == Bool { } public struct AndProperty: PropertyType { - private let left: PropertyOf - private let right: PropertyOf + private let left: AnyProperty + private let right: AnyProperty public var value: Bool { return left.value && right.value @@ -35,14 +35,14 @@ public struct AndProperty: PropertyType { } public init(lhs: L, rhs: R) { - left = PropertyOf(lhs) - right = PropertyOf(rhs) + left = AnyProperty(lhs) + right = AnyProperty(rhs) } } public struct OrProperty: PropertyType { - private let left: PropertyOf - private let right: PropertyOf + private let left: AnyProperty + private let right: AnyProperty public var value: Bool { return left.value || right.value @@ -53,13 +53,13 @@ public struct OrProperty: PropertyType { } public init(lhs: L, rhs: R) { - left = PropertyOf(lhs) - right = PropertyOf(rhs) + left = AnyProperty(lhs) + right = AnyProperty(rhs) } } public struct NotProperty: PropertyType { - private let source: PropertyOf + private let source: AnyProperty public var value: Bool { return !source.value @@ -70,6 +70,6 @@ public struct NotProperty: PropertyType { } public init(property: P) { - source = PropertyOf(property) + source = AnyProperty(property) } } From b4131644e90ba311ea903d8ae2add09e23e15636 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Wed, 25 Nov 2015 00:20:50 +0900 Subject: [PATCH 0192/1028] Refactor NSObject.rex_valueProperty() using associatedProperty() --- Source/Foundation/NSObject.swift | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index d852ed1657..6f547d48bf 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -59,10 +59,6 @@ extension NSObject { /// N.B. Ensure that `self` isn't strongly captured by `initial` or `setter`, otherwise this /// will create a retain cycle causing `self` to never dealloc. public func rex_valueProperty(key: UnsafePointer<()>, _ initial: () -> T, _ setter: T -> ()) -> MutableProperty { - return associatedObject(self, key: key) { - let property = MutableProperty(initial()) - property.producer.start(Observer(next: setter)) - return property - } + return associatedProperty(self, key: key, initial: initial, setter: setter) } } From 6a7a3bf14960c9784e1b1d4e7fd38307664188ad Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Wed, 25 Nov 2015 12:34:39 +0900 Subject: [PATCH 0193/1028] [POC] Reconsider associatedProperty(), associatedObject(), rex_valueProperty() interfaces This change enables to pass the host (receiver) instance to `initial` and `setter` closures. Thus the caller of the functions don't need to use weak-captured host instance to implement the closures. `NSObjectType` protocol and `rex_valueProperty()` which is basend on its `InstanceType` typealias might seem tricky. We can remove the trick if needed with the way as `UIBarItem.rex_enabled` is implemented (direct use of `associatedProperty()`). --- Source/Foundation/Association.swift | 34 +++++++++++++++++------------ Source/Foundation/NSObject.swift | 10 ++++++++- Source/UIKit/UIBarItem.swift | 2 +- Source/UIKit/UIButton.swift | 4 +++- Source/UIKit/UIControl.swift | 8 ++++--- Source/UIKit/UIImageView.swift | 8 ++++--- Source/UIKit/UILabel.swift | 4 +++- Source/UIKit/UIView.swift | 8 ++++--- 8 files changed, 51 insertions(+), 27 deletions(-) diff --git a/Source/Foundation/Association.swift b/Source/Foundation/Association.swift index 87f7b65761..a56ddfa149 100644 --- a/Source/Foundation/Association.swift +++ b/Source/Foundation/Association.swift @@ -17,11 +17,11 @@ import ReactiveCocoa /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. public func associatedProperty(host: AnyObject, keyPath: StaticString) -> MutableProperty { - let initial: () -> String = { [weak host] _ in - host?.valueForKeyPath(keyPath.stringValue) as? String ?? "" + let initial: AnyObject -> String = { host in + host.valueForKeyPath(keyPath.stringValue) as? String ?? "" } - let setter: String -> () = { [weak host] newValue in - host?.setValue(newValue, forKeyPath: keyPath.stringValue) + let setter: (AnyObject, String) -> () = { host, newValue in + host.setValue(newValue, forKeyPath: keyPath.stringValue) } return associatedProperty(host, key: keyPath.utf8Start, initial: initial, setter: setter) } @@ -37,11 +37,11 @@ public func associatedProperty(host: AnyObject, keyPath: StaticString) -> Mutabl /// N.B. Ensure that `host` isn't strongly captured by `placeholder`, otherwise this will /// create a retain cycle with `host` causing it to never dealloc. public func associatedProperty(host: AnyObject, keyPath: StaticString, placeholder: () -> T) -> MutableProperty { - let initial: () -> T = { [weak host] _ in - host?.valueForKeyPath(keyPath.stringValue) as? T ?? placeholder() + let initial: AnyObject -> T = { host in + host.valueForKeyPath(keyPath.stringValue) as? T ?? placeholder() } - let setter: T -> () = { [weak host] newValue in - host?.setValue(newValue, forKeyPath: keyPath.stringValue) + let setter: (AnyObject, T) -> () = { host, newValue in + host.setValue(newValue, forKeyPath: keyPath.stringValue) } return associatedProperty(host, key: keyPath.utf8Start, initial: initial, setter: setter) } @@ -55,10 +55,16 @@ public func associatedProperty(host: AnyObject, keyPath: StaticStr /// /// N.B. Ensure that `host` isn't strongly captured by `initial` or `setter`, otherwise this /// will create a retain cycle with `host` causing it to never dealloc. -public func associatedProperty(host: AnyObject, key: UnsafePointer<()>, initial: () -> T, setter: T -> ()) -> MutableProperty { - return associatedObject(host, key: key) { - let property = MutableProperty(initial()) - property.producer.start(Observer(next: setter)) +public func associatedProperty(host: Host, key: UnsafePointer<()>, initial: Host -> T, setter: (Host, T) -> ()) -> MutableProperty { + return associatedObject(host, key: key) { host in + let property = MutableProperty(initial(host)) + + property.producer.startWithNext { [weak host] next in + if let host = host { + setter(host, next) + } + } + return property } } @@ -69,10 +75,10 @@ public func associatedProperty(host: AnyObject, key: UnsafePointer<()>, initi /// /// N.B. Ensure that `host` isn't strongly captured by `initial`, otherwise this will /// create a retain cycle with `host` causing it to never dealloc. -public func associatedObject(host: AnyObject, key: UnsafePointer<()>, initial: () -> T) -> T { +public func associatedObject(host: Host, key: UnsafePointer<()>, initial: Host -> T) -> T { var value = objc_getAssociatedObject(host, key) as? T if value == nil { - value = initial() + value = initial(host) objc_setAssociatedObject(host, key, value, .OBJC_ASSOCIATION_RETAIN) } return value! diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index 6f547d48bf..b69a32ac98 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -9,6 +9,12 @@ import Foundation import ReactiveCocoa +public protocol NSObjectType { + typealias InstanceType = Self +} + +extension NSObject: NSObjectType {} + extension NSObject { /// Creates a strongly-typed producer to monitor `keyPath` via KVO. The caller /// is responsible for ensuring that the associated value is castable to `T`. @@ -48,7 +54,9 @@ extension NSObject { public func rex_classProperty(keyPath: StaticString, placeholder: () -> T) -> MutableProperty { return associatedProperty(self, keyPath: keyPath, placeholder: placeholder) } +} +extension NSObjectType where Self: NSObject, InstanceType == Self { /// Attaches a `MutableProperty` value under `key`. The property is initialized with /// the result of `initial`. Changes on the property's producer are monitored and /// written to `setter`. @@ -58,7 +66,7 @@ extension NSObject { /// /// N.B. Ensure that `self` isn't strongly captured by `initial` or `setter`, otherwise this /// will create a retain cycle causing `self` to never dealloc. - public func rex_valueProperty(key: UnsafePointer<()>, _ initial: () -> T, _ setter: T -> ()) -> MutableProperty { + public func rex_valueProperty(key: UnsafePointer<()>, _ initial: InstanceType -> T, _ setter: (InstanceType, T) -> ()) -> MutableProperty { return associatedProperty(self, key: key, initial: initial, setter: setter) } } diff --git a/Source/UIKit/UIBarItem.swift b/Source/UIKit/UIBarItem.swift index 76d33a294d..999bd94b52 100644 --- a/Source/UIKit/UIBarItem.swift +++ b/Source/UIKit/UIBarItem.swift @@ -12,7 +12,7 @@ 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: { [weak self] in self?.enabled ?? true }, setter: { [weak self] in self?.enabled = $0 }) + return associatedProperty(self, key: &enabledKey, initial: { $0.enabled }, setter: { $0.enabled = $1 }) } } diff --git a/Source/UIKit/UIButton.swift b/Source/UIKit/UIButton.swift index f8fb2b8a9c..10f80f22b1 100644 --- a/Source/UIKit/UIButton.swift +++ b/Source/UIKit/UIButton.swift @@ -10,6 +10,8 @@ import ReactiveCocoa import UIKit extension UIButton { + public typealias InstanceType = UIButton + /// Exposes a property that binds an action to button presses. The action is set as /// a target of the button for `TouchUpInside` events. When property changes occur the /// previous action is removed as a target. This also binds the enabled state of the @@ -36,7 +38,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 rex_valueProperty(&titleKey, { [weak self] in self?.titleForState(.Normal) ?? "" }, { [weak self] in self?.setTitle($0, forState: .Normal) }) + return rex_valueProperty(&titleKey, { $0.titleForState(.Normal) ?? "" }, { $0.setTitle($1, forState: .Normal) }) } } diff --git a/Source/UIKit/UIControl.swift b/Source/UIKit/UIControl.swift index 7e30ec8b38..055cbf6cb8 100644 --- a/Source/UIKit/UIControl.swift +++ b/Source/UIKit/UIControl.swift @@ -10,6 +10,8 @@ import ReactiveCocoa import UIKit extension UIControl { + public typealias InstanceType = UIControl + /// Creates a producer for the sender whenever a specified control event is triggered. public func rex_controlEvents(events: UIControlEvents) -> SignalProducer { return rac_signalForControlEvents(events) @@ -20,17 +22,17 @@ extension UIControl { /// Wraps a control's `enabled` state in a bindable property. public var rex_enabled: MutableProperty { - return rex_valueProperty(&enabledKey, { [weak self] in self?.enabled ?? true }, { [weak self] in self?.enabled = $0 }) + return rex_valueProperty(&enabledKey, { $0.enabled }, { $0.enabled = $1 }) } /// Wraps a control's `selected` state in a bindable property. public var rex_selected: MutableProperty { - return rex_valueProperty(&selectedKey, { [weak self] in self?.selected ?? false }, { [weak self] in self?.selected = $0 }) + return rex_valueProperty(&selectedKey, { $0.selected }, { $0.selected = $1 }) } /// Wraps a control's `highlighted` state in a bindable property. public var rex_highlighted: MutableProperty { - return rex_valueProperty(&highlightedKey, { [weak self] in self?.highlighted ?? false }, { [weak self] in self?.highlighted = $0 }) + return rex_valueProperty(&highlightedKey, { $0.highlighted }, { $0.highlighted = $1 }) } } diff --git a/Source/UIKit/UIImageView.swift b/Source/UIKit/UIImageView.swift index dfc41d60c7..8a5672fc60 100644 --- a/Source/UIKit/UIImageView.swift +++ b/Source/UIKit/UIImageView.swift @@ -10,16 +10,18 @@ import ReactiveCocoa import UIKit extension UIImageView { + public typealias InstanceType = UIImageView + /// Wraps a imageView's `image` value in a bindable property. public var rex_image: MutableProperty { - return rex_valueProperty(&imageKey, { [weak self] in self?.image }, { [weak self] in self?.image = $0 }) + return rex_valueProperty(&imageKey, { $0.image }, { $0.image = $1 }) } /// Wraps a imageView's `highlightedImage` value in a bindable property. public var rex_highlightedImage: MutableProperty { - return rex_valueProperty(&highlightedImageKey, { [weak self] in self?.highlightedImage }, { [weak self] in self?.highlightedImage = $0 }) + return rex_valueProperty(&highlightedImageKey, { $0.highlightedImage }, { $0.highlightedImage = $1 }) } } private var imageKey: UInt8 = 0 -private var highlightedImageKey: UInt8 = 0 \ No newline at end of file +private var highlightedImageKey: UInt8 = 0 diff --git a/Source/UIKit/UILabel.swift b/Source/UIKit/UILabel.swift index c999eec597..95b69e361b 100644 --- a/Source/UIKit/UILabel.swift +++ b/Source/UIKit/UILabel.swift @@ -10,12 +10,14 @@ import ReactiveCocoa import UIKit extension UILabel { + public typealias InstanceType = UILabel + /// Wraps a label's `text` value in a bindable property. public var rex_text: MutableProperty { return rex_stringProperty("text") } public var rex_textColor: MutableProperty { - return rex_valueProperty(&textColor, { [weak self] in self?.textColor ?? UILabel().textColor }, { [weak self] in self?.textColor = $0 }) + return rex_valueProperty(&textColor, { $0.textColor }, { $0.textColor = $1 }) } } diff --git a/Source/UIKit/UIView.swift b/Source/UIKit/UIView.swift index 1ce154838b..94e32de18c 100644 --- a/Source/UIKit/UIView.swift +++ b/Source/UIKit/UIView.swift @@ -10,16 +10,18 @@ import ReactiveCocoa import UIKit extension UIView { + public typealias InstanceType = UIView + /// Wraps a view's `alpha` value in a bindable property. public var rex_alpha: MutableProperty { - return rex_valueProperty(&alphaKey, { [weak self] in self?.alpha ?? 1.0 }, { [weak self] in self?.alpha = $0 }) + return rex_valueProperty(&alphaKey, { $0.alpha }, { $0.alpha = $1 }) } /// Wraps a view's `hidden` state in a bindable property. public var rex_hidden: MutableProperty { - return rex_valueProperty(&hiddenKey, { [weak self] in self?.hidden ?? false }, { [weak self] in self?.hidden = $0 }) + return rex_valueProperty(&hiddenKey, { $0.hidden }, { $0.hidden = $1 }) } } private var alphaKey: UInt8 = 0 -private var hiddenKey: UInt8 = 0 \ No newline at end of file +private var hiddenKey: UInt8 = 0 From 6d4a2babd6f34114c507c2e4e4beb0ccbe97528e Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Wed, 25 Nov 2015 14:29:22 +0900 Subject: [PATCH 0194/1028] Remove NSObjectType and NSObject.rex_valueProperty --- Source/Foundation/NSObject.swift | 21 --------------------- Source/UIKit/UIButton.swift | 4 +--- Source/UIKit/UIControl.swift | 8 +++----- Source/UIKit/UIImageView.swift | 6 ++---- Source/UIKit/UILabel.swift | 4 +--- Source/UIKit/UIView.swift | 6 ++---- 6 files changed, 9 insertions(+), 40 deletions(-) diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index b69a32ac98..11352ecb64 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -9,12 +9,6 @@ import Foundation import ReactiveCocoa -public protocol NSObjectType { - typealias InstanceType = Self -} - -extension NSObject: NSObjectType {} - extension NSObject { /// Creates a strongly-typed producer to monitor `keyPath` via KVO. The caller /// is responsible for ensuring that the associated value is castable to `T`. @@ -55,18 +49,3 @@ extension NSObject { return associatedProperty(self, keyPath: keyPath, placeholder: placeholder) } } - -extension NSObjectType where Self: NSObject, InstanceType == Self { - /// Attaches a `MutableProperty` value under `key`. The property is initialized with - /// the result of `initial`. Changes on the property's producer are monitored and - /// written to `setter`. - /// - /// This can be used as an alternative to `DynamicProperty` for creating strongly typed - /// bindings on Cocoa objects. - /// - /// N.B. Ensure that `self` isn't strongly captured by `initial` or `setter`, otherwise this - /// will create a retain cycle causing `self` to never dealloc. - public func rex_valueProperty(key: UnsafePointer<()>, _ initial: InstanceType -> T, _ setter: (InstanceType, T) -> ()) -> MutableProperty { - return associatedProperty(self, key: key, initial: initial, setter: setter) - } -} diff --git a/Source/UIKit/UIButton.swift b/Source/UIKit/UIButton.swift index 10f80f22b1..fcccda5742 100644 --- a/Source/UIKit/UIButton.swift +++ b/Source/UIKit/UIButton.swift @@ -10,8 +10,6 @@ import ReactiveCocoa import UIKit extension UIButton { - public typealias InstanceType = UIButton - /// Exposes a property that binds an action to button presses. The action is set as /// a target of the button for `TouchUpInside` events. When property changes occur the /// previous action is removed as a target. This also binds the enabled state of the @@ -38,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 rex_valueProperty(&titleKey, { $0.titleForState(.Normal) ?? "" }, { $0.setTitle($1, forState: .Normal) }) + return associatedProperty(self, key: &titleKey, initial: { $0.titleForState(.Normal) ?? "" }, setter: { $0.setTitle($1, forState: .Normal) }) } } diff --git a/Source/UIKit/UIControl.swift b/Source/UIKit/UIControl.swift index 055cbf6cb8..c1c2e3d2f7 100644 --- a/Source/UIKit/UIControl.swift +++ b/Source/UIKit/UIControl.swift @@ -10,8 +10,6 @@ import ReactiveCocoa import UIKit extension UIControl { - public typealias InstanceType = UIControl - /// Creates a producer for the sender whenever a specified control event is triggered. public func rex_controlEvents(events: UIControlEvents) -> SignalProducer { return rac_signalForControlEvents(events) @@ -22,17 +20,17 @@ extension UIControl { /// Wraps a control's `enabled` state in a bindable property. public var rex_enabled: MutableProperty { - return rex_valueProperty(&enabledKey, { $0.enabled }, { $0.enabled = $1 }) + return associatedProperty(self, key: &enabledKey, initial: { $0.enabled }, setter: { $0.enabled = $1 }) } /// Wraps a control's `selected` state in a bindable property. public var rex_selected: MutableProperty { - return rex_valueProperty(&selectedKey, { $0.selected }, { $0.selected = $1 }) + return associatedProperty(self, key: &selectedKey, initial: { $0.selected }, setter: { $0.selected = $1 }) } /// Wraps a control's `highlighted` state in a bindable property. public var rex_highlighted: MutableProperty { - return rex_valueProperty(&highlightedKey, { $0.highlighted }, { $0.highlighted = $1 }) + return associatedProperty(self, key: &highlightedKey, initial: { $0.highlighted }, setter: { $0.highlighted = $1 }) } } diff --git a/Source/UIKit/UIImageView.swift b/Source/UIKit/UIImageView.swift index 8a5672fc60..db6d09ea77 100644 --- a/Source/UIKit/UIImageView.swift +++ b/Source/UIKit/UIImageView.swift @@ -10,16 +10,14 @@ import ReactiveCocoa import UIKit extension UIImageView { - public typealias InstanceType = UIImageView - /// Wraps a imageView's `image` value in a bindable property. public var rex_image: MutableProperty { - return rex_valueProperty(&imageKey, { $0.image }, { $0.image = $1 }) + return associatedProperty(self, key: &imageKey, initial: { $0.image }, setter: { $0.image = $1 }) } /// Wraps a imageView's `highlightedImage` value in a bindable property. public var rex_highlightedImage: MutableProperty { - return rex_valueProperty(&highlightedImageKey, { $0.highlightedImage }, { $0.highlightedImage = $1 }) + return associatedProperty(self, key: &highlightedImageKey, initial: { $0.highlightedImage }, setter: { $0.highlightedImage = $1 }) } } diff --git a/Source/UIKit/UILabel.swift b/Source/UIKit/UILabel.swift index 95b69e361b..43fc945ef5 100644 --- a/Source/UIKit/UILabel.swift +++ b/Source/UIKit/UILabel.swift @@ -10,14 +10,12 @@ import ReactiveCocoa import UIKit extension UILabel { - public typealias InstanceType = UILabel - /// Wraps a label's `text` value in a bindable property. public var rex_text: MutableProperty { return rex_stringProperty("text") } public var rex_textColor: MutableProperty { - return rex_valueProperty(&textColor, { $0.textColor }, { $0.textColor = $1 }) + return associatedProperty(self, key: &textColor, initial: { $0.textColor }, setter: { $0.textColor = $1 }) } } diff --git a/Source/UIKit/UIView.swift b/Source/UIKit/UIView.swift index 94e32de18c..a7903394e1 100644 --- a/Source/UIKit/UIView.swift +++ b/Source/UIKit/UIView.swift @@ -10,16 +10,14 @@ import ReactiveCocoa import UIKit extension UIView { - public typealias InstanceType = UIView - /// Wraps a view's `alpha` value in a bindable property. public var rex_alpha: MutableProperty { - return rex_valueProperty(&alphaKey, { $0.alpha }, { $0.alpha = $1 }) + return associatedProperty(self, key: &alphaKey, initial: { $0.alpha }, setter: { $0.alpha = $1 }) } /// Wraps a view's `hidden` state in a bindable property. public var rex_hidden: MutableProperty { - return rex_valueProperty(&hiddenKey, { $0.hidden }, { $0.hidden = $1 }) + return associatedProperty(self, key: &hiddenKey, initial: { $0.hidden }, setter: { $0.hidden = $1 }) } } From f405c9ee27d28bd9181ca2bc2f33a622a91482d9 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Wed, 25 Nov 2015 14:32:47 +0900 Subject: [PATCH 0195/1028] Remove NSObject.rex_classProperty --- Source/Foundation/NSObject.swift | 13 ------------- Tests/Foundation/NSObjectTests.swift | 2 +- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index 11352ecb64..23f8296353 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -35,17 +35,4 @@ extension NSObject { public func rex_stringProperty(keyPath: StaticString) -> MutableProperty { return associatedProperty(self, keyPath: keyPath) } - - /// Attaches a `MutableProperty` relying on KVC for the initial value and subsequent - /// updates. Note that `keyPath` is a `StaticString` because it's pointer value is used - /// as key value when associating the property. - /// - /// This can be used as an alternative to `DynamicProperty` for creating strongly typed - /// bindings on Cocoa objects. - /// - /// N.B. Ensure that `self` isn't strongly captured by `placeholder`, otherwise this will - /// create a retain cycle causing `self` to never dealloc. - public func rex_classProperty(keyPath: StaticString, placeholder: () -> T) -> MutableProperty { - return associatedProperty(self, keyPath: keyPath, placeholder: placeholder) - } } diff --git a/Tests/Foundation/NSObjectTests.swift b/Tests/Foundation/NSObjectTests.swift index c9718d2af8..b333845cad 100644 --- a/Tests/Foundation/NSObjectTests.swift +++ b/Tests/Foundation/NSObjectTests.swift @@ -45,7 +45,7 @@ final class NSObjectDeallocTests: XCTestCase { let object = Object() _object = object - object.rex_classProperty("string", placeholder: { _ in "" }) <~ SignalProducer(value: "Test") + associatedProperty(object, keyPath: "string", placeholder: { _ in "" }) <~ SignalProducer(value: "Test") XCTAssert(_object?.string == "Test") } } From 0e6ff6803478222397330b0d77f5642884236d1e Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Wed, 25 Nov 2015 14:34:48 +0900 Subject: [PATCH 0196/1028] Remove NSObject.rex_stringProperty --- Source/Foundation/NSObject.swift | 10 ---------- Source/UIKit/UILabel.swift | 2 +- Tests/Foundation/NSObjectTests.swift | 2 +- 3 files changed, 2 insertions(+), 12 deletions(-) diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index 23f8296353..657a039914 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -25,14 +25,4 @@ extension NSObject { return .empty } } - - /// Attaches a `MutableProperty` relying on KVC for the initial value and subsequent - /// updates. Note that `keyPath` is a `StaticString` because it's pointer value is used - /// as key value when associating the property. - /// - /// This can be used as an alternative to `DynamicProperty` for creating strongly typed - /// bindings on Cocoa objects. - public func rex_stringProperty(keyPath: StaticString) -> MutableProperty { - return associatedProperty(self, keyPath: keyPath) - } } diff --git a/Source/UIKit/UILabel.swift b/Source/UIKit/UILabel.swift index 43fc945ef5..de67fae06d 100644 --- a/Source/UIKit/UILabel.swift +++ b/Source/UIKit/UILabel.swift @@ -12,7 +12,7 @@ import UIKit extension UILabel { /// Wraps a label's `text` value in a bindable property. public var rex_text: MutableProperty { - return rex_stringProperty("text") + return associatedProperty(self, keyPath: "text") } public var rex_textColor: MutableProperty { diff --git a/Tests/Foundation/NSObjectTests.swift b/Tests/Foundation/NSObjectTests.swift index b333845cad..45ec54e86e 100644 --- a/Tests/Foundation/NSObjectTests.swift +++ b/Tests/Foundation/NSObjectTests.swift @@ -37,7 +37,7 @@ final class NSObjectDeallocTests: XCTestCase { let object = Object() _object = object - object.rex_stringProperty("string") <~ SignalProducer(value: "Test") + associatedProperty(object, keyPath: "string") <~ SignalProducer(value: "Test") XCTAssert(_object?.string == "Test") } From a59834b4a8f536b5caabe6341ffd361f6eba064a Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Wed, 25 Nov 2015 14:37:10 +0900 Subject: [PATCH 0197/1028] Remove unneeded comments --- Source/Foundation/Association.swift | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Source/Foundation/Association.swift b/Source/Foundation/Association.swift index a56ddfa149..897d74bc27 100644 --- a/Source/Foundation/Association.swift +++ b/Source/Foundation/Association.swift @@ -52,9 +52,6 @@ public func associatedProperty(host: AnyObject, keyPath: StaticStr /// /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. -/// -/// N.B. Ensure that `host` isn't strongly captured by `initial` or `setter`, otherwise this -/// will create a retain cycle with `host` causing it to never dealloc. public func associatedProperty(host: Host, key: UnsafePointer<()>, initial: Host -> T, setter: (Host, T) -> ()) -> MutableProperty { return associatedObject(host, key: key) { host in let property = MutableProperty(initial(host)) @@ -72,9 +69,6 @@ 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`. -/// -/// N.B. Ensure that `host` isn't strongly captured by `initial`, otherwise this will -/// create a retain cycle with `host` causing it to never dealloc. public func associatedObject(host: Host, key: UnsafePointer<()>, initial: Host -> T) -> T { var value = objc_getAssociatedObject(host, key) as? T if value == nil { From a2518478edc90890cbdd32332138868094369f8f Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 25 Nov 2015 11:13:33 -0800 Subject: [PATCH 0198/1028] Use extension methods in predicate tests --- Tests/PredicateTests.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/PredicateTests.swift b/Tests/PredicateTests.swift index 0300904789..fe05525441 100644 --- a/Tests/PredicateTests.swift +++ b/Tests/PredicateTests.swift @@ -14,7 +14,7 @@ final class PredicateTests: XCTestCase { func testAndProperty() { let lhs = MutableProperty(false), rhs = MutableProperty(false) - let and = AndProperty(lhs: lhs, rhs: rhs) + let and = lhs.and(rhs) var current: Bool! and.producer.startWithNext { current = $0 } @@ -33,7 +33,7 @@ final class PredicateTests: XCTestCase { func testOrProperty() { let lhs = MutableProperty(true), rhs = MutableProperty(true) - let or = OrProperty(lhs: lhs, rhs: rhs) + let or = lhs.or(rhs) var current: Bool! or.producer.startWithNext { current = $0 } @@ -52,7 +52,7 @@ final class PredicateTests: XCTestCase { func testNotProperty() { let source = MutableProperty(false) - let not = NotProperty(property: source) + let not = source.not() var current: Bool! not.producer.startWithNext { current = $0 } From 15cdbbb1b1342d1a1610f53af0df0e230640f882 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 25 Nov 2015 15:46:53 -0800 Subject: [PATCH 0199/1028] Add CompoundPropertyType exposing terms --- Source/Predicate.swift | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/Source/Predicate.swift b/Source/Predicate.swift index 045d47398d..59f1f99146 100644 --- a/Source/Predicate.swift +++ b/Source/Predicate.swift @@ -22,39 +22,45 @@ extension PropertyType where Value == Bool { } } -public struct AndProperty: PropertyType { - private let left: AnyProperty - private let right: AnyProperty - +public protocol CompoundPropertyType: PropertyType { + var terms: [AnyProperty] { get } +} + +public struct AndProperty: CompoundPropertyType { + public let terms: [AnyProperty] + public var value: Bool { - return left.value && right.value + return terms.reduce(true) { $0 && $1.value } } public var producer: SignalProducer { - return combineLatest(left.producer, right.producer).map { $0 && $1 } + let producers = terms.map { $0.producer } + return combineLatest(producers).map { values in + return values.reduce(true) { $0 && $1 } + } } - + public init(lhs: L, rhs: R) { - left = AnyProperty(lhs) - right = AnyProperty(rhs) + terms = [AnyProperty(lhs), AnyProperty(rhs)] } } -public struct OrProperty: PropertyType { - private let left: AnyProperty - private let right: AnyProperty +public struct OrProperty: CompoundPropertyType { + public let terms: [AnyProperty] public var value: Bool { - return left.value || right.value + return terms.reduce(false) { $0 || $1.value } } public var producer: SignalProducer { - return combineLatest(left.producer, right.producer).map { $0 || $1 } + let producers = terms.map { $0.producer } + return combineLatest(producers).map { values in + return values.reduce(false) { $0 || $1 } + } } - + public init(lhs: L, rhs: R) { - left = AnyProperty(lhs) - right = AnyProperty(rhs) + terms = [AnyProperty(lhs), AnyProperty(rhs)] } } From a324076050bc7f7fc79ab6818854c12d23b3e454 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 25 Nov 2015 16:02:20 -0800 Subject: [PATCH 0200/1028] Initialize predicate properties with terms --- Source/Predicate.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/Predicate.swift b/Source/Predicate.swift index 59f1f99146..8eb12bba7e 100644 --- a/Source/Predicate.swift +++ b/Source/Predicate.swift @@ -10,11 +10,11 @@ import ReactiveCocoa extension PropertyType where Value == Bool { public func and(other: Self) -> AndProperty { - return AndProperty(lhs: self, rhs: other) + return AndProperty(terms: [AnyProperty(self), AnyProperty(other)]) } public func or(other: Self) -> OrProperty { - return OrProperty(lhs: self, rhs: other) + return OrProperty(terms: [AnyProperty(self), AnyProperty(other)]) } public func not() -> NotProperty { @@ -40,8 +40,8 @@ public struct AndProperty: CompoundPropertyType { } } - public init(lhs: L, rhs: R) { - terms = [AnyProperty(lhs), AnyProperty(rhs)] + public init(terms: [AnyProperty]) { + self.terms = terms } } @@ -58,9 +58,9 @@ public struct OrProperty: CompoundPropertyType { return values.reduce(false) { $0 || $1 } } } - - public init(lhs: L, rhs: R) { - terms = [AnyProperty(lhs), AnyProperty(rhs)] + + public init(terms: [AnyProperty]) { + self.terms = terms } } From 605abc9efa1533b40d0bd2f95ee84ff9b15fd5fe Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Thu, 26 Nov 2015 09:47:02 +0900 Subject: [PATCH 0201/1028] Add noescape attribute for `initial` and `placeholder` closures of associatedProperty --- Source/Foundation/Association.swift | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/Source/Foundation/Association.swift b/Source/Foundation/Association.swift index 897d74bc27..a42ec39bd0 100644 --- a/Source/Foundation/Association.swift +++ b/Source/Foundation/Association.swift @@ -33,17 +33,13 @@ 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. -/// -/// N.B. Ensure that `host` isn't strongly captured by `placeholder`, otherwise this will -/// create a retain cycle with `host` causing it to never dealloc. -public func associatedProperty(host: AnyObject, keyPath: StaticString, placeholder: () -> T) -> MutableProperty { - let initial: AnyObject -> T = { host in - host.valueForKeyPath(keyPath.stringValue) as? T ?? placeholder() - } +public func associatedProperty(host: AnyObject, keyPath: StaticString, @noescape placeholder: () -> T) -> MutableProperty { let setter: (AnyObject, T) -> () = { host, newValue in host.setValue(newValue, forKeyPath: keyPath.stringValue) } - return associatedProperty(host, key: keyPath.utf8Start, initial: initial, setter: setter) + return associatedProperty(host, key: keyPath.utf8Start, initial: { host in + host.valueForKeyPath(keyPath.stringValue) as? T ?? placeholder() + }, setter: setter) } /// Attaches a `MutableProperty` value to the `host` object under `key`. The property is @@ -52,7 +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. -public func associatedProperty(host: Host, key: UnsafePointer<()>, initial: Host -> T, setter: (Host, T) -> ()) -> MutableProperty { +public func associatedProperty(host: Host, key: UnsafePointer<()>, @noescape initial: Host -> T, setter: (Host, T) -> ()) -> MutableProperty { return associatedObject(host, key: key) { host in let property = MutableProperty(initial(host)) @@ -69,7 +65,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<()>, initial: Host -> T) -> T { +public func associatedObject(host: Host, key: UnsafePointer<()>, @noescape initial: Host -> T) -> T { var value = objc_getAssociatedObject(host, key) as? T if value == nil { value = initial(host) From bfab21a0187741b4932c419422cea1c50b2a5998 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 25 Nov 2015 19:04:01 -0800 Subject: [PATCH 0202/1028] Customize and/or for predicate properties These optimizations avoid extra layers of AnyProperty wrapping when combining longer chains of the same type. --- Source/Predicate.swift | 12 ++++++++++-- Tests/PredicateTests.swift | 22 ++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/Source/Predicate.swift b/Source/Predicate.swift index 8eb12bba7e..6b89a7fdf6 100644 --- a/Source/Predicate.swift +++ b/Source/Predicate.swift @@ -9,11 +9,11 @@ import ReactiveCocoa extension PropertyType where Value == Bool { - public func and(other: Self) -> AndProperty { + public func and(other: P) -> AndProperty { return AndProperty(terms: [AnyProperty(self), AnyProperty(other)]) } - public func or(other: Self) -> OrProperty { + public func or(other: P) -> OrProperty { return OrProperty(terms: [AnyProperty(self), AnyProperty(other)]) } @@ -43,6 +43,10 @@ public struct AndProperty: CompoundPropertyType { public init(terms: [AnyProperty]) { self.terms = terms } + + public func and

(other: P) -> AndProperty { + return AndProperty(terms: terms + [AnyProperty(other)]) + } } public struct OrProperty: CompoundPropertyType { @@ -62,6 +66,10 @@ public struct OrProperty: CompoundPropertyType { public init(terms: [AnyProperty]) { self.terms = terms } + + public func or

(other: P) -> OrProperty { + return OrProperty(terms: terms + [AnyProperty(other)]) + } } public struct NotProperty: PropertyType { diff --git a/Tests/PredicateTests.swift b/Tests/PredicateTests.swift index fe05525441..a7f35fcf65 100644 --- a/Tests/PredicateTests.swift +++ b/Tests/PredicateTests.swift @@ -29,6 +29,17 @@ final class PredicateTests: XCTestCase { rhs.value = true XCTAssertTrue(and.value) XCTAssertTrue(current!) + + let (signal, pipe) = Signal.pipe() + let and2 = and.and(AnyProperty(initialValue: false, signal: signal)) + and2.producer.startWithNext { current = $0 } + + XCTAssertFalse(and2.value) + XCTAssertFalse(current!) + + pipe.sendNext(true) + XCTAssertTrue(and2.value) + XCTAssertTrue(current!) } func testOrProperty() { @@ -48,6 +59,17 @@ final class PredicateTests: XCTestCase { rhs.value = false XCTAssertFalse(or.value) XCTAssertFalse(current!) + + let (signal, pipe) = Signal.pipe() + let or2 = or.or(AnyProperty(initialValue: true, signal: signal)) + or2.producer.startWithNext { current = $0 } + + XCTAssertTrue(or2.value) + XCTAssertTrue(current!) + + pipe.sendNext(false) + XCTAssertFalse(or2.value) + XCTAssertFalse(current!) } func testNotProperty() { From 9ea1c051369acd019d7c5847898f55feca6a209d Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 25 Nov 2015 19:17:39 -0800 Subject: [PATCH 0203/1028] More overloads to minimize AnyProperty wrapping --- Source/Predicate.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Source/Predicate.swift b/Source/Predicate.swift index 6b89a7fdf6..a0d0bab30f 100644 --- a/Source/Predicate.swift +++ b/Source/Predicate.swift @@ -13,10 +13,18 @@ extension PropertyType where Value == Bool { return AndProperty(terms: [AnyProperty(self), AnyProperty(other)]) } + public func and(other: AnyProperty) -> AndProperty { + return AndProperty(terms: [AnyProperty(self), other]) + } + public func or(other: P) -> OrProperty { return OrProperty(terms: [AnyProperty(self), AnyProperty(other)]) } + public func or(other: AnyProperty) -> OrProperty { + return OrProperty(terms: [AnyProperty(self), other]) + } + public func not() -> NotProperty { return NotProperty(property: self) } @@ -47,6 +55,10 @@ public struct AndProperty: CompoundPropertyType { public func and

(other: P) -> AndProperty { return AndProperty(terms: terms + [AnyProperty(other)]) } + + public func and(other: AnyProperty) -> AndProperty { + return AndProperty(terms: terms + [other]) + } } public struct OrProperty: CompoundPropertyType { @@ -70,6 +82,10 @@ public struct OrProperty: CompoundPropertyType { public func or

(other: P) -> OrProperty { return OrProperty(terms: terms + [AnyProperty(other)]) } + + public func or(other: AnyProperty) -> OrProperty { + return OrProperty(terms: terms + [other]) + } } public struct NotProperty: PropertyType { From 1fb2d354e423e0561fed0d8980a0ac05e00ee58f Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 25 Nov 2015 19:24:11 -0800 Subject: [PATCH 0204/1028] Remove unused CompoundPropertyType --- Source/Predicate.swift | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Source/Predicate.swift b/Source/Predicate.swift index a0d0bab30f..55bb1962c4 100644 --- a/Source/Predicate.swift +++ b/Source/Predicate.swift @@ -30,11 +30,7 @@ extension PropertyType where Value == Bool { } } -public protocol CompoundPropertyType: PropertyType { - var terms: [AnyProperty] { get } -} - -public struct AndProperty: CompoundPropertyType { +public struct AndProperty: PropertyType { public let terms: [AnyProperty] public var value: Bool { @@ -61,7 +57,7 @@ public struct AndProperty: CompoundPropertyType { } } -public struct OrProperty: CompoundPropertyType { +public struct OrProperty: PropertyType { public let terms: [AnyProperty] public var value: Bool { From b701071170d2593abbe03e100e39b759621fa921 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 25 Nov 2015 19:32:04 -0800 Subject: [PATCH 0205/1028] Private predicate initializers --- Source/Predicate.swift | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Source/Predicate.swift b/Source/Predicate.swift index 55bb1962c4..bb6762c2c5 100644 --- a/Source/Predicate.swift +++ b/Source/Predicate.swift @@ -44,10 +44,6 @@ public struct AndProperty: PropertyType { } } - public init(terms: [AnyProperty]) { - self.terms = terms - } - public func and

(other: P) -> AndProperty { return AndProperty(terms: terms + [AnyProperty(other)]) } @@ -55,6 +51,10 @@ public struct AndProperty: PropertyType { public func and(other: AnyProperty) -> AndProperty { return AndProperty(terms: terms + [other]) } + + private init(terms: [AnyProperty]) { + self.terms = terms + } } public struct OrProperty: PropertyType { @@ -71,10 +71,6 @@ public struct OrProperty: PropertyType { } } - public init(terms: [AnyProperty]) { - self.terms = terms - } - public func or

(other: P) -> AndProperty { return AndProperty(terms: terms + [AnyProperty(other)]) } + /// Creates a new property with an additional conjunctive term. public func and(other: AnyProperty) -> AndProperty { return AndProperty(terms: terms + [other]) } @@ -57,6 +65,7 @@ public struct AndProperty: PropertyType { } } +/// Specialized `PropertyType` for the disjunction of a set of boolean properties. public struct OrProperty: PropertyType { public let terms: [AnyProperty] @@ -71,10 +80,12 @@ public struct OrProperty: PropertyType { } } + /// Creates a new property with an additional disjunctive term. public func or

(other: P) -> OrProperty { return OrProperty(terms: terms + [AnyProperty(other)]) } + /// Creates a new property with an additional disjunctive term. public func or(other: AnyProperty) -> OrProperty { return OrProperty(terms: terms + [other]) } @@ -84,6 +95,7 @@ public struct OrProperty: PropertyType { } } +/// Specialized `PropertyType` for the negation of a boolean property. public struct NotProperty: PropertyType { private let source: AnyProperty private let invert: Bool @@ -96,6 +108,7 @@ public struct NotProperty: PropertyType { return source.producer.map { $0 != self.invert } } + /// A negated property of `self`. public func not() -> NotProperty { return NotProperty(source: source, invert: !invert) } From 357513f1b74012ed0e3631d71cdc11e5671bc7ba Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Tue, 15 Dec 2015 20:13:13 -0800 Subject: [PATCH 0217/1028] Missing doc comments --- Source/Foundation/NSData.swift | 3 +-- Source/UIKit/UILabel.swift | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Foundation/NSData.swift b/Source/Foundation/NSData.swift index d3983f18a8..652c419fbe 100644 --- a/Source/Foundation/NSData.swift +++ b/Source/Foundation/NSData.swift @@ -10,8 +10,7 @@ import Foundation import ReactiveCocoa extension NSData { - /// Read the data at the URL. - /// Sends the data or the error. + /// Read the data at the URL, sending the result or an error. public class func rex_dataWithContentsOfURL(url: NSURL, options: NSDataReadingOptions = NSDataReadingOptions()) -> SignalProducer { return SignalProducer { observer, disposable in do { diff --git a/Source/UIKit/UILabel.swift b/Source/UIKit/UILabel.swift index de67fae06d..8cfb425986 100644 --- a/Source/UIKit/UILabel.swift +++ b/Source/UIKit/UILabel.swift @@ -14,7 +14,8 @@ extension UILabel { public var rex_text: MutableProperty { return associatedProperty(self, keyPath: "text") } - + + /// Wraps a label's `textColor` value in a bindable property. public var rex_textColor: MutableProperty { return associatedProperty(self, key: &textColor, initial: { $0.textColor }, setter: { $0.textColor = $1 }) } From dea1386be642472efe45b553ec7fa66f0f33fdfb Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Tue, 15 Dec 2015 21:39:45 -0800 Subject: [PATCH 0218/1028] Add SignalProducer.delayedStart --- Source/SignalProducer.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index 7c4857f540..1362219058 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -72,6 +72,14 @@ extension SignalProducerType { public func timeoutAfter(interval: NSTimeInterval, withEvent event: Event, onScheduler scheduler: DateSchedulerType) -> SignalProducer { return lift { $0.timeoutAfter(interval, withEvent: event, onScheduler: scheduler) } } + + /// Delays the start of the producer by `interval` on the provided scheduler. + public func delayedStart(interval: NSTimeInterval, onScheduler scheduler: DateSchedulerType) -> SignalProducer { + return timer(interval, onScheduler: scheduler) + .take(1) + .promoteErrors(Error) + .then(self.producer) + } } extension SignalProducerType where Value: SequenceType { From 1ffefce525bc3b79d33612f9c81ae31a7d392c45 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 16 Dec 2015 19:24:52 -0800 Subject: [PATCH 0219/1028] Add a debounce operator --- Source/Signal.swift | 14 ++++++++++++++ Source/SignalProducer.swift | 10 ++++++++++ 2 files changed, 24 insertions(+) diff --git a/Source/Signal.swift b/Source/Signal.swift index b48448526f..bea8729ce7 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -70,6 +70,20 @@ extension SignalType { return disposable } } + + /// Enforces that at least `interval` time passes before forwarding a value. If a + /// new value arrives, the previous one is dropped and the `interval` delay starts + /// again. Error events are immediately forwarded, even if there's a queued value. + /// + /// This operator is useful for scenarios like type-to-search where you want to + /// wait for a "lull" in typing before kicking off a search request. + public func debounce(interval: NSTimeInterval, onScheduler scheduler: DateSchedulerType) -> Signal { + precondition(interval >= 0) + + return flatMap(.Latest) { + SignalProducer(value: $0).delay(interval, onScheduler: scheduler) + } + } } extension SignalType where Value: SequenceType { diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index 1362219058..5d8a3a7750 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -73,6 +73,16 @@ extension SignalProducerType { return lift { $0.timeoutAfter(interval, withEvent: event, onScheduler: scheduler) } } + /// Enforces that at least `interval` time passes before forwarding a value. If a + /// new value arrives, the previous one is dropped and the `interval` delay starts + /// again. Error events are immediately forwarded, even if there's a queued value. + /// + /// This operator is useful for scenarios like type-to-search where you want to + /// wait for a "lull" in typing before kicking off a search request. + public func debounce(interval: NSTimeInterval, onScheduler scheduler: DateSchedulerType) -> SignalProducer { + return lift { $0.debounce(interval, onScheduler: scheduler) } + } + /// Delays the start of the producer by `interval` on the provided scheduler. public func delayedStart(interval: NSTimeInterval, onScheduler scheduler: DateSchedulerType) -> SignalProducer { return timer(interval, onScheduler: scheduler) From fdc6b733e21851a2766e557d0e6263b8d58cce5d Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 16 Dec 2015 19:46:43 -0800 Subject: [PATCH 0220/1028] Add muteFor operator --- Source/Signal.swift | 22 ++++++++++++++++++++++ Source/SignalProducer.swift | 11 +++++++++++ 2 files changed, 33 insertions(+) diff --git a/Source/Signal.swift b/Source/Signal.swift index bea8729ce7..e3c0592453 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -84,6 +84,28 @@ extension SignalType { SignalProducer(value: $0).delay(interval, onScheduler: scheduler) } } + + /// Forwards a value and then mutes the signal by dropping all subsequent values + /// for `interval` seconds. Once time elapses the next new value will be forwarded + /// and repeat the muting process. Error events are immediately forwarded even while + /// the signal is muted. + /// + /// This operator could be used to coalesce multiple notifications in a short time + /// frame by only showing the first one. + public func muteFor(interval: NSTimeInterval, withScheduler scheduler: DateSchedulerType) -> Signal { + precondition(interval > 0) + + var expires = scheduler.currentDate + return filter { _ in + let now = scheduler.currentDate + + if expires.compare(now) != .OrderedDescending { + expires = now.dateByAddingTimeInterval(interval) + return true + } + return false + } + } } extension SignalType where Value: SequenceType { diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index 5d8a3a7750..6d042faaeb 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -83,6 +83,17 @@ extension SignalProducerType { return lift { $0.debounce(interval, onScheduler: scheduler) } } + /// Forwards a value and then mutes the producer by dropping all subsequent values + /// for `interval` seconds. Once time elapses the next new value will be forwarded + /// and repeat the muting process. Error events are immediately forwarded even while + /// the producer is muted. + /// + /// This operator could be used to coalesce multiple notifications in a short time + /// frame by only showing the first one. + public func muteFor(interval: NSTimeInterval, withScheduler scheduler: DateSchedulerType) -> SignalProducer { + return lift { $0.muteFor(interval, withScheduler: scheduler) } + } + /// Delays the start of the producer by `interval` on the provided scheduler. public func delayedStart(interval: NSTimeInterval, onScheduler scheduler: DateSchedulerType) -> SignalProducer { return timer(interval, onScheduler: scheduler) From bde2abb8ee7e965fe697ecd675277bea5e7c15c0 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 16 Dec 2015 20:04:13 -0800 Subject: [PATCH 0221/1028] Add SignalProducer.delayedRetry --- Source/SignalProducer.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index 6d042faaeb..16784b6787 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -101,6 +101,22 @@ extension SignalProducerType { .promoteErrors(Error) .then(self.producer) } + + /// Delays retrying on failure by `interval`. The last error received is forwarded + /// if all `attempts` are exhausted. + public func delayedRetry(interval: NSTimeInterval, onScheduler scheduler: DateSchedulerType, attempts: Int = .max) -> SignalProducer { + precondition(attempts > 0) + + if attempts == 1 { + return producer + } else { + return flatMapError { _ in + self.producer + .delayedStart(interval, onScheduler: scheduler) + .delayedRetry(interval, onScheduler: scheduler, attempts: attempts - 1) + } + } + } } extension SignalProducerType where Value: SequenceType { From c1679631d2de643094c9141846b60a719af351f0 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 16 Dec 2015 20:25:33 -0800 Subject: [PATCH 0222/1028] Add debounce test --- Tests/SignalTests.swift | 55 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/Tests/SignalTests.swift b/Tests/SignalTests.swift index fa0e5e2f62..f4f8f1c61b 100644 --- a/Tests/SignalTests.swift +++ b/Tests/SignalTests.swift @@ -134,6 +134,61 @@ final class SignalTests: XCTestCase { sink.sendNext([2, 3]) XCTAssert(values == [1, 2, 3]) } + + func testDebounceValues() { + let scheduler = TestScheduler() + let (signal, sink) = Signal.pipe() + var value = -1 + + signal + .debounce(1, onScheduler: scheduler) + .observeNext { + value = $0 + print("seeing \($0)") + } + + scheduler.schedule { sink.sendNext(1) } + scheduler.advance() + XCTAssertEqual(value, -1) + + scheduler.advanceByInterval(1) + XCTAssertEqual(value, 1) + + scheduler.schedule { sink.sendNext(2) } + scheduler.advance() + XCTAssertEqual(value, 1) + + scheduler.schedule { sink.sendNext(3) } + scheduler.advance() + XCTAssertEqual(value, 1) + + scheduler.advanceByInterval(1) + XCTAssertEqual(value, 3) + } + + func testDebounceError() { + let scheduler = TestScheduler() + let (signal, sink) = Signal.pipe() + var value = -1 + var failed = false + + signal + .debounce(10, onScheduler: scheduler) + .observe(Observer(next: { + value = $0 + }, failed: { _ in + failed = true + })) + + scheduler.schedule { sink.sendNext(1) } + scheduler.advance() + XCTAssertEqual(value, -1) + + scheduler.schedule { sink.sendFailed(.Default) } + scheduler.advance() + XCTAssertTrue(failed) + XCTAssertEqual(value, -1) + } } enum TestError: ErrorType { From ac7d145dffbb14fcee11748139c24be588ed8f86 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 16 Dec 2015 20:31:30 -0800 Subject: [PATCH 0223/1028] Add muteFor tests --- Tests/SignalTests.swift | 65 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 6 deletions(-) diff --git a/Tests/SignalTests.swift b/Tests/SignalTests.swift index f4f8f1c61b..e141c20f88 100644 --- a/Tests/SignalTests.swift +++ b/Tests/SignalTests.swift @@ -142,10 +142,7 @@ final class SignalTests: XCTestCase { signal .debounce(1, onScheduler: scheduler) - .observeNext { - value = $0 - print("seeing \($0)") - } + .observeNext { value = $0 } scheduler.schedule { sink.sendNext(1) } scheduler.advance() @@ -166,14 +163,14 @@ final class SignalTests: XCTestCase { XCTAssertEqual(value, 3) } - func testDebounceError() { + func testDebounceFailure() { let scheduler = TestScheduler() let (signal, sink) = Signal.pipe() var value = -1 var failed = false signal - .debounce(10, onScheduler: scheduler) + .debounce(1, onScheduler: scheduler) .observe(Observer(next: { value = $0 }, failed: { _ in @@ -189,6 +186,62 @@ final class SignalTests: XCTestCase { XCTAssertTrue(failed) XCTAssertEqual(value, -1) } + + func testMuteForValues() { + let scheduler = TestScheduler() + let (signal, sink) = Signal.pipe() + var value = -1 + + signal + .muteFor(1, withScheduler: scheduler) + .observeNext { value = $0 } + + scheduler.schedule { sink.sendNext(1) } + scheduler.advance() + XCTAssertEqual(value, 1) + + scheduler.schedule { sink.sendNext(2) } + scheduler.advance() + XCTAssertEqual(value, 1) + + scheduler.schedule { sink.sendNext(3) } + scheduler.schedule { sink.sendNext(4) } + scheduler.advance() + XCTAssertEqual(value, 1) + + scheduler.advanceByInterval(1) + XCTAssertEqual(value, 1) + + scheduler.schedule { sink.sendNext(5) } + scheduler.schedule { sink.sendNext(6) } + scheduler.advance() + XCTAssertEqual(value, 5) + } + + func testMuteForFailure() { + let scheduler = TestScheduler() + let (signal, sink) = Signal.pipe() + var value = -1 + var failed = false + + signal + .muteFor(1, withScheduler: scheduler) + .observe(Observer(next: { + value = $0 + }, failed: { _ in + failed = true + })) + + scheduler.schedule { sink.sendNext(1) } + scheduler.advance() + XCTAssertEqual(value, 1) + + scheduler.schedule { sink.sendNext(2) } + scheduler.schedule { sink.sendFailed(.Default) } + scheduler.advance() + XCTAssertTrue(failed) + XCTAssertEqual(value, 1) + } } enum TestError: ErrorType { From fa9119421dcec4a23a756fffd6a32c01fcbdecad Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 16 Dec 2015 20:36:50 -0800 Subject: [PATCH 0224/1028] Use observe helpers in tests --- Tests/SignalTests.swift | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/Tests/SignalTests.swift b/Tests/SignalTests.swift index e141c20f88..4c9a3371f6 100644 --- a/Tests/SignalTests.swift +++ b/Tests/SignalTests.swift @@ -20,7 +20,7 @@ final class SignalTests: XCTestCase { .filterMap { return $0 % 2 == 0 ? String($0) : nil } - .observe(Observer(next: { values.append($0) })) + .observeNext { values.append($0) } sink.sendNext(1) XCTAssert(values == []) @@ -41,9 +41,7 @@ final class SignalTests: XCTestCase { signal .ignoreError() - .observe(Observer(completed: { - completed = true - })) + .observeCompleted { completed = true } sink.sendNext(1) XCTAssertFalse(completed) @@ -58,9 +56,7 @@ final class SignalTests: XCTestCase { signal .ignoreError(replacement: .Interrupted) - .observe(Observer(interrupted: { - interrupted = true - })) + .observeInterrupted { interrupted = true } sink.sendNext(1) XCTAssertFalse(interrupted) @@ -121,9 +117,7 @@ final class SignalTests: XCTestCase { signal .uncollect() - .observe(Observer(next: { - values.append($0) - })) + .observeNext { values.append($0) } sink.sendNext([]) XCTAssert(values.isEmpty) @@ -171,11 +165,10 @@ final class SignalTests: XCTestCase { signal .debounce(1, onScheduler: scheduler) - .observe(Observer(next: { - value = $0 - }, failed: { _ in - failed = true - })) + .observe(Observer( + next: { value = $0 }, + failed: { _ in failed = true } + )) scheduler.schedule { sink.sendNext(1) } scheduler.advance() @@ -226,11 +219,10 @@ final class SignalTests: XCTestCase { signal .muteFor(1, withScheduler: scheduler) - .observe(Observer(next: { - value = $0 - }, failed: { _ in - failed = true - })) + .observe(Observer( + next: { value = $0 }, + failed: { _ in failed = true } + )) scheduler.schedule { sink.sendNext(1) } scheduler.advance() From b6d9f75fc4da5606525cdb79c671f99bf37362fd Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 16 Dec 2015 20:42:54 -0800 Subject: [PATCH 0225/1028] Add delayedStart test --- Tests/SignalProducerTests.swift | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Tests/SignalProducerTests.swift b/Tests/SignalProducerTests.swift index fa6aa1b554..e4268d7940 100644 --- a/Tests/SignalProducerTests.swift +++ b/Tests/SignalProducerTests.swift @@ -52,4 +52,31 @@ final class SignalProducerTests: XCTestCase { XCTAssert(interrupted) XCTAssertFalse(completed) } + + func testDelayedStart() { + let scheduler = TestScheduler() + + var delayed = false + let producer = SignalProducer<(), NoError> { _ in + delayed = true + } + + var started = false + producer + .delayedStart(1, onScheduler: scheduler) + .on(started: { started = true }) + .start() + + XCTAssertTrue(started) + XCTAssertFalse(delayed) + + scheduler.advance() + XCTAssertFalse(delayed) + + scheduler.advanceByInterval(0.9) + XCTAssertFalse(delayed) + + scheduler.advanceByInterval(0.2) + XCTAssertTrue(delayed) + } } From bad1aa45c5240313d022b3c09910336051bf0589 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Thu, 17 Dec 2015 19:15:20 -0800 Subject: [PATCH 0226/1028] Simplify delayedStart and delayedRetry and test --- Source/SignalProducer.swift | 19 +++---- Tests/SignalProducerTests.swift | 87 +++++++++++++++++++++++++++++++-- 2 files changed, 91 insertions(+), 15 deletions(-) diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index 16784b6787..1055b786b5 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -96,10 +96,9 @@ extension SignalProducerType { /// Delays the start of the producer by `interval` on the provided scheduler. public func delayedStart(interval: NSTimeInterval, onScheduler scheduler: DateSchedulerType) -> SignalProducer { - return timer(interval, onScheduler: scheduler) - .take(1) - .promoteErrors(Error) - .then(self.producer) + return SignalProducer.empty + .delay(interval, onScheduler: scheduler) + .concat(self.producer) } /// Delays retrying on failure by `interval`. The last error received is forwarded @@ -107,15 +106,11 @@ extension SignalProducerType { public func delayedRetry(interval: NSTimeInterval, onScheduler scheduler: DateSchedulerType, attempts: Int = .max) -> SignalProducer { precondition(attempts > 0) - if attempts == 1 { - return producer - } else { - return flatMapError { _ in - self.producer - .delayedStart(interval, onScheduler: scheduler) - .delayedRetry(interval, onScheduler: scheduler, attempts: attempts - 1) + return flatMapError { + // TODO This delays the final error if all attempts are exhausted + SignalProducer(error: $0).delayedStart(interval, onScheduler: scheduler) } - } + .retry(attempts - 1) } } diff --git a/Tests/SignalProducerTests.swift b/Tests/SignalProducerTests.swift index e4268d7940..6ad18ba0be 100644 --- a/Tests/SignalProducerTests.swift +++ b/Tests/SignalProducerTests.swift @@ -57,9 +57,7 @@ final class SignalProducerTests: XCTestCase { let scheduler = TestScheduler() var delayed = false - let producer = SignalProducer<(), NoError> { _ in - delayed = true - } + let producer = SignalProducer<(), NoError> { _ in delayed = true } var started = false producer @@ -79,4 +77,87 @@ final class SignalProducerTests: XCTestCase { scheduler.advanceByInterval(0.2) XCTAssertTrue(delayed) } + + func testDelayedRetry() { + let scheduler = TestScheduler() + + var count = 0 + let producer = SignalProducer { observer, _ in + if count < 2 { + scheduler.schedule { observer.sendNext(count) } + scheduler.schedule { observer.sendFailed(.Default) } + } else { + scheduler.schedule { observer.sendCompleted() } + } + count += 1 + } + + var value = -1 + var completed = false + producer + .delayedRetry(1, onScheduler: scheduler) + .start(Observer( + next: { value = $0 }, + completed: { completed = true } + )) + + XCTAssertEqual(count, 1) + XCTAssertEqual(value, -1) + + scheduler.advance() + XCTAssertEqual(count, 1) + XCTAssertEqual(value, 1) + XCTAssertFalse(completed) + + scheduler.advanceByInterval(1) + XCTAssertEqual(count, 2) + XCTAssertEqual(value, 2) + XCTAssertFalse(completed) + + scheduler.advanceByInterval(1) + XCTAssertEqual(count, 3) + XCTAssertEqual(value, 2) + XCTAssertTrue(completed) + } + + func testDelayedRetryFailure() { + let scheduler = TestScheduler() + + var count = 0 + let producer = SignalProducer { observer, _ in + observer.sendNext(count) + observer.sendFailed(.Default) + count += 1 + } + + var value = -1 + var failed = false + producer + .delayedRetry(1, onScheduler: scheduler, attempts: 3) + .start(Observer( + next: { value = $0 }, + failed: { _ in failed = true } + )) + + XCTAssertEqual(count, 1) + XCTAssertEqual(value, 0) + + scheduler.advance() + XCTAssertEqual(count, 1) + XCTAssertEqual(value, 0) + XCTAssertFalse(failed) + + scheduler.advanceByInterval(1) + XCTAssertEqual(count, 2) + XCTAssertEqual(value, 1) + XCTAssertFalse(failed) + + scheduler.advanceByInterval(1) + XCTAssertEqual(count, 3) + XCTAssertEqual(value, 2) + + // TODO The final failure if attempts are exhausted + scheduler.advanceByInterval(1) + XCTAssertTrue(failed) + } } From 531cb908c2bb92d7df1c97b719bde1aa08c6b008 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 20 Dec 2015 16:36:55 -0800 Subject: [PATCH 0227/1028] delayed => deferred --- Source/SignalProducer.swift | 6 +++--- Tests/SignalProducerTests.swift | 24 ++++++++++++------------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index 1055b786b5..debf1b4347 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -95,7 +95,7 @@ extension SignalProducerType { } /// Delays the start of the producer by `interval` on the provided scheduler. - public func delayedStart(interval: NSTimeInterval, onScheduler scheduler: DateSchedulerType) -> SignalProducer { + public func deferred(interval: NSTimeInterval, onScheduler scheduler: DateSchedulerType) -> SignalProducer { return SignalProducer.empty .delay(interval, onScheduler: scheduler) .concat(self.producer) @@ -103,12 +103,12 @@ extension SignalProducerType { /// Delays retrying on failure by `interval`. The last error received is forwarded /// if all `attempts` are exhausted. - public func delayedRetry(interval: NSTimeInterval, onScheduler scheduler: DateSchedulerType, attempts: Int = .max) -> SignalProducer { + public func deferredRetry(interval: NSTimeInterval, onScheduler scheduler: DateSchedulerType, attempts: Int = .max) -> SignalProducer { precondition(attempts > 0) return flatMapError { // TODO This delays the final error if all attempts are exhausted - SignalProducer(error: $0).delayedStart(interval, onScheduler: scheduler) + SignalProducer(error: $0).deferred(interval, onScheduler: scheduler) } .retry(attempts - 1) } diff --git a/Tests/SignalProducerTests.swift b/Tests/SignalProducerTests.swift index 6ad18ba0be..92fe0aa0e0 100644 --- a/Tests/SignalProducerTests.swift +++ b/Tests/SignalProducerTests.swift @@ -53,32 +53,32 @@ final class SignalProducerTests: XCTestCase { XCTAssertFalse(completed) } - func testDelayedStart() { + func testDeferred() { let scheduler = TestScheduler() - var delayed = false - let producer = SignalProducer<(), NoError> { _ in delayed = true } + var deferred = false + let producer = SignalProducer<(), NoError> { _ in deferred = true } var started = false producer - .delayedStart(1, onScheduler: scheduler) + .deferred(1, onScheduler: scheduler) .on(started: { started = true }) .start() XCTAssertTrue(started) - XCTAssertFalse(delayed) + XCTAssertFalse(deferred) scheduler.advance() - XCTAssertFalse(delayed) + XCTAssertFalse(deferred) scheduler.advanceByInterval(0.9) - XCTAssertFalse(delayed) + XCTAssertFalse(deferred) scheduler.advanceByInterval(0.2) - XCTAssertTrue(delayed) + XCTAssertTrue(deferred) } - func testDelayedRetry() { + func testDeferredRetry() { let scheduler = TestScheduler() var count = 0 @@ -95,7 +95,7 @@ final class SignalProducerTests: XCTestCase { var value = -1 var completed = false producer - .delayedRetry(1, onScheduler: scheduler) + .deferredRetry(1, onScheduler: scheduler) .start(Observer( next: { value = $0 }, completed: { completed = true } @@ -120,7 +120,7 @@ final class SignalProducerTests: XCTestCase { XCTAssertTrue(completed) } - func testDelayedRetryFailure() { + func testDeferredRetryFailure() { let scheduler = TestScheduler() var count = 0 @@ -133,7 +133,7 @@ final class SignalProducerTests: XCTestCase { var value = -1 var failed = false producer - .delayedRetry(1, onScheduler: scheduler, attempts: 3) + .deferredRetry(1, onScheduler: scheduler, attempts: 3) .start(Observer( next: { value = $0 }, failed: { _ in failed = true } From a161e9ed6e9f6f22460e1df656c34860a405cd5e Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 20 Dec 2015 17:18:31 -0800 Subject: [PATCH 0228/1028] deferredRetry doesn't delay a final failure --- Source/SignalProducer.swift | 28 +++++++++++++++++++--------- Tests/SignalProducerTests.swift | 5 +---- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index debf1b4347..d05fd0635f 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -101,16 +101,26 @@ extension SignalProducerType { .concat(self.producer) } - /// Delays retrying on failure by `interval`. The last error received is forwarded - /// if all `attempts` are exhausted. - public func deferredRetry(interval: NSTimeInterval, onScheduler scheduler: DateSchedulerType, attempts: Int = .max) -> SignalProducer { - precondition(attempts > 0) - - return flatMapError { - // TODO This delays the final error if all attempts are exhausted - SignalProducer(error: $0).deferred(interval, onScheduler: scheduler) + /// Delays retrying on failure by `interval` up to `count` attempts. + public func deferredRetry(interval: NSTimeInterval, onScheduler scheduler: DateSchedulerType, count: Int = .max) -> SignalProducer { + precondition(count >= 0) + + if count == 0 { + return producer + } + + var retries = count + return flatMapError { error in + // 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) + } + + retries -= 1 + return producer } - .retry(attempts - 1) + .retry(count) } } diff --git a/Tests/SignalProducerTests.swift b/Tests/SignalProducerTests.swift index 92fe0aa0e0..9ea0656ad4 100644 --- a/Tests/SignalProducerTests.swift +++ b/Tests/SignalProducerTests.swift @@ -133,7 +133,7 @@ final class SignalProducerTests: XCTestCase { var value = -1 var failed = false producer - .deferredRetry(1, onScheduler: scheduler, attempts: 3) + .deferredRetry(1, onScheduler: scheduler, count: 2) .start(Observer( next: { value = $0 }, failed: { _ in failed = true } @@ -155,9 +155,6 @@ final class SignalProducerTests: XCTestCase { scheduler.advanceByInterval(1) XCTAssertEqual(count, 3) XCTAssertEqual(value, 2) - - // TODO The final failure if attempts are exhausted - scheduler.advanceByInterval(1) XCTAssertTrue(failed) } } From 6520504bbce17387503665f1b8d2a2ede96c72e9 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sun, 20 Dec 2015 17:44:36 -0800 Subject: [PATCH 0229/1028] Rename muteFor param clock --- Source/Signal.swift | 6 +++--- Source/SignalProducer.swift | 4 ++-- Tests/SignalTests.swift | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/Signal.swift b/Source/Signal.swift index e3c0592453..c587bb4f8a 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -92,12 +92,12 @@ extension SignalType { /// /// This operator could be used to coalesce multiple notifications in a short time /// frame by only showing the first one. - public func muteFor(interval: NSTimeInterval, withScheduler scheduler: DateSchedulerType) -> Signal { + public func muteFor(interval: NSTimeInterval, clock: DateSchedulerType) -> Signal { precondition(interval > 0) - var expires = scheduler.currentDate + var expires = clock.currentDate return filter { _ in - let now = scheduler.currentDate + let now = clock.currentDate if expires.compare(now) != .OrderedDescending { expires = now.dateByAddingTimeInterval(interval) diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index d05fd0635f..bee61c7b40 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -90,8 +90,8 @@ extension SignalProducerType { /// /// This operator could be used to coalesce multiple notifications in a short time /// frame by only showing the first one. - public func muteFor(interval: NSTimeInterval, withScheduler scheduler: DateSchedulerType) -> SignalProducer { - return lift { $0.muteFor(interval, withScheduler: scheduler) } + public func muteFor(interval: NSTimeInterval, clock: DateSchedulerType) -> SignalProducer { + return lift { $0.muteFor(interval, clock: clock) } } /// Delays the start of the producer by `interval` on the provided scheduler. diff --git a/Tests/SignalTests.swift b/Tests/SignalTests.swift index 4c9a3371f6..1ec9ee8f00 100644 --- a/Tests/SignalTests.swift +++ b/Tests/SignalTests.swift @@ -186,7 +186,7 @@ final class SignalTests: XCTestCase { var value = -1 signal - .muteFor(1, withScheduler: scheduler) + .muteFor(1, clock: scheduler) .observeNext { value = $0 } scheduler.schedule { sink.sendNext(1) } @@ -218,7 +218,7 @@ final class SignalTests: XCTestCase { var failed = false signal - .muteFor(1, withScheduler: scheduler) + .muteFor(1, clock: scheduler) .observe(Observer( next: { value = $0 }, failed: { _ in failed = true } From f6c43916501135405cbfb86ccf36117522247120 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Tue, 22 Dec 2015 12:57:34 -0800 Subject: [PATCH 0230/1028] Remove a bad framework search path from ios tests to fix warning --- Rex.xcodeproj/project.pbxproj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 0873ed6356..99dd0fc57a 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -1095,7 +1095,6 @@ "CODE_SIGN_IDENTITY[sdk=iphonesimulator*]" = ""; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", "$(PROJECT_DIR)/Carthage/Build/iOS", ); @@ -1118,7 +1117,6 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphonesimulator*]" = ""; FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", "$(PROJECT_DIR)/Carthage/Build/iOS", ); @@ -1318,6 +1316,7 @@ D8715D9A1C210F97005F4191 /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; D8715DB51C21123E005F4191 /* Build configuration list for PBXNativeTarget "Rex-tvOS" */ = { isa = XCConfigurationList; @@ -1326,6 +1325,7 @@ D8715DB71C21123E005F4191 /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; D8715DD91C21160A005F4191 /* Build configuration list for PBXNativeTarget "RexTests-tvOS" */ = { isa = XCConfigurationList; @@ -1334,6 +1334,7 @@ D8715DDB1C21160A005F4191 /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; From 1c4155d5d82385ca084a7f868d82f51dce3f039a Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Tue, 12 Jan 2016 10:18:21 -0800 Subject: [PATCH 0231/1028] Update xcode and carthage in travis --- .travis.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 53014a258b..8337d3b10a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: objective-c matrix: include: - - osx_image: xcode7.1 + - osx_image: xcode7.2 env: global: @@ -10,12 +10,9 @@ env: git: submodules: false before_install: - - curl -OlL "https://github.com/Carthage/Carthage/releases/download/0.10/Carthage.pkg" + - curl -OlL "https://github.com/Carthage/Carthage/releases/download/0.11/Carthage.pkg" - sudo installer -pkg "Carthage.pkg" -target / - rm "Carthage.pkg" - - curl -OlL "https://github.com/ReactiveCocoa/ReactiveCocoa/releases/download/v4.0.0-alpha.3/ReactiveCocoa.framework.zip" - - unzip "ReactiveCocoa.framework.zip" - - rm "ReactiveCocoa.framework.zip" script: - set -o pipefail From e979606f8c33b42d650c85ad08663f85b9843803 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Tue, 12 Jan 2016 10:44:03 -0800 Subject: [PATCH 0232/1028] Specify an ios simulator in travis for testing --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8337d3b10a..c616d3b45f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ script: - set -o pipefail - carthage bootstrap --platform mac,iphonesimulator - xcodebuild test -scheme Rex-Mac | xcpretty -c - - xcodebuild test -scheme Rex-iOS -sdk iphonesimulator | xcpretty -c + - xcodebuild test -scheme Rex-iOS -sdk iphonesimulator -destination "platform=iOS Simulator,name=iPhone 6s" | xcpretty -c notifications: email: false From 87d814576de08c0f7d3eaaf81389e18a7217cd7b Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Tue, 12 Jan 2016 11:12:02 -0800 Subject: [PATCH 0233/1028] Add tvOS and watchOS to travis --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c616d3b45f..52a06e0426 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,9 +16,11 @@ before_install: script: - set -o pipefail - - carthage bootstrap --platform mac,iphonesimulator + - carthage bootstrap - xcodebuild test -scheme Rex-Mac | xcpretty -c - xcodebuild test -scheme Rex-iOS -sdk iphonesimulator -destination "platform=iOS Simulator,name=iPhone 6s" | xcpretty -c + - xcodebuild test -scheme Rex-tvOS -sdk appletvsimulator -destination "platform=tvOS Simulator,name=Apple TV 1080p" | xcpretty -c + - xcodebuild build -scheme Rex-watchOS -sdk watchsimulator | xcpretty -c notifications: email: false From efdd8cdf25dfe59f93102e01c04bf8326532cdb3 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Tue, 12 Jan 2016 11:44:51 -0800 Subject: [PATCH 0234/1028] Add destination for watchOS --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 52a06e0426..ff7ba0f1d9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,7 @@ script: - xcodebuild test -scheme Rex-Mac | xcpretty -c - xcodebuild test -scheme Rex-iOS -sdk iphonesimulator -destination "platform=iOS Simulator,name=iPhone 6s" | xcpretty -c - xcodebuild test -scheme Rex-tvOS -sdk appletvsimulator -destination "platform=tvOS Simulator,name=Apple TV 1080p" | xcpretty -c - - xcodebuild build -scheme Rex-watchOS -sdk watchsimulator | xcpretty -c + - xcodebuild build -scheme Rex-watchOS -sdk watchsimulator -destination "platform=watchOS Simulator,name=Apple Watch - 38mm" notifications: email: false From f353e3dc61af6559e9ab816f1feb9ad461b64fe5 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Tue, 12 Jan 2016 12:49:39 -0800 Subject: [PATCH 0235/1028] Pipe watchOS build through xcpretty on travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ff7ba0f1d9..387cdfe116 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,7 @@ script: - xcodebuild test -scheme Rex-Mac | xcpretty -c - xcodebuild test -scheme Rex-iOS -sdk iphonesimulator -destination "platform=iOS Simulator,name=iPhone 6s" | xcpretty -c - xcodebuild test -scheme Rex-tvOS -sdk appletvsimulator -destination "platform=tvOS Simulator,name=Apple TV 1080p" | xcpretty -c - - xcodebuild build -scheme Rex-watchOS -sdk watchsimulator -destination "platform=watchOS Simulator,name=Apple Watch - 38mm" + - xcodebuild build -scheme Rex-watchOS -sdk watchsimulator -destination "platform=watchOS Simulator,name=Apple Watch - 38mm" | xcpretty -c notifications: email: false From 2e4d4a8daf9fbf10381d684b7d613e2b6f8c7d2d Mon Sep 17 00:00:00 2001 From: Rui Peres Date: Sun, 17 Jan 2016 03:20:44 +0000 Subject: [PATCH 0236/1028] Added the rex_textSignal to UITextField --- Rex.xcodeproj/project.pbxproj | 4 ++++ Source/UIKit/UITextField.swift | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 Source/UIKit/UITextField.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 99dd0fc57a..8653ffc80e 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ 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 */; }; + C7932E831C4B3F3000086F3C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E811C4B3EDB00086F3C /* UITextField.swift */; }; D8003E941AFEC3D400D7D3C5 /* Rex.h in Headers */ = {isa = PBXBuildFile; fileRef = D8003E931AFEC3D400D7D3C5 /* Rex.h */; settings = {ATTRIBUTES = (Public, ); }; }; D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; D8003EB51AFEC6B000D7D3C5 /* Result.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003EAE1AFEC68A00D7D3C5 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -175,6 +176,7 @@ 8295FD851B873081007C9000 /* UIControlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIControlTests.swift; sourceTree = ""; }; 8295FD881B873490007C9000 /* UIButtonTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIButtonTests.swift; sourceTree = ""; }; 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBarButtonItemTests.swift; sourceTree = ""; }; + C7932E811C4B3EDB00086F3C /* UITextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextField.swift; sourceTree = ""; }; D8003E921AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D8003E931AFEC3D400D7D3C5 /* Rex.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Rex.h; sourceTree = ""; }; D8003E9F1AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -384,6 +386,7 @@ D86FFBD71B34B242001A89B3 /* UILabel.swift */, 8289A2E21BD7EF740097FB60 /* UIImageView.swift */, 8289A2E41BD7F6DD0097FB60 /* UIView.swift */, + C7932E811C4B3EDB00086F3C /* UITextField.swift */, ); path = UIKit; sourceTree = ""; @@ -751,6 +754,7 @@ D86FFBD81B34B242001A89B3 /* UILabel.swift in Sources */, D86FFBDB1B34B3F0001A89B3 /* Action.swift in Sources */, D86FFBD21B34AD7A001A89B3 /* Association.swift in Sources */, + C7932E831C4B3F3000086F3C /* UITextField.swift in Sources */, 8289A2E31BD7EF740097FB60 /* UIImageView.swift in Sources */, D8F0973E1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, D834572D1AFEE45B0070616A /* Signal.swift in Sources */, diff --git a/Source/UIKit/UITextField.swift b/Source/UIKit/UITextField.swift new file mode 100644 index 0000000000..efe00db404 --- /dev/null +++ b/Source/UIKit/UITextField.swift @@ -0,0 +1,24 @@ +// +// UITextField.swift +// Rex +// +// Created by Rui Peres on 17/01/2016. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import Foundation +import ReactiveCocoa +import UIKit + +extension UITextField { + + /// Sends the field's string value whenever it changes. + public var rex_textSignal: SignalProducer { + return NSNotificationCenter.defaultCenter() + .rac_notifications(UITextFieldTextDidChangeNotification, object: self) + .filterMap { notification in + guard let textField = notification.object as? UITextField else { return nil} + return textField.text + } + } +} From ab4bbaf7f16c1daab93438fd90846d14ee5e153b Mon Sep 17 00:00:00 2001 From: Rui Peres Date: Sun, 17 Jan 2016 03:28:56 +0000 Subject: [PATCH 0237/1028] Added it to the tvOS target --- Rex.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 8653ffc80e..9fc90d679a 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ 8295FD8A1B87352D007C9000 /* UIButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8295FD881B873490007C9000 /* UIButtonTests.swift */; }; 8295FD8D1B87374A007C9000 /* UIBarButtonItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */; }; C7932E831C4B3F3000086F3C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E811C4B3EDB00086F3C /* UITextField.swift */; }; + C7932E841C4B41E100086F3C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E811C4B3EDB00086F3C /* UITextField.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, ); }; }; @@ -809,6 +810,7 @@ D8715DBB1C2112D1005F4191 /* Property.swift in Sources */, D8715DCA1C211553005F4191 /* UILabel.swift in Sources */, D8715DCB1C211553005F4191 /* UIImageView.swift in Sources */, + C7932E841C4B41E100086F3C /* UITextField.swift in Sources */, D8715DC61C211553005F4191 /* UIBarButtonItem.swift in Sources */, D8715DBD1C2112D1005F4191 /* SignalProducer.swift in Sources */, D8715DBE1C2112D6005F4191 /* Association.swift in Sources */, From 4dfa3503ee816e622a3f521f9d64ba4a279f2da3 Mon Sep 17 00:00:00 2001 From: Rui Peres Date: Sun, 17 Jan 2016 03:53:55 +0000 Subject: [PATCH 0238/1028] Added test --- Rex.xcodeproj/project.pbxproj | 4 ++++ Tests/UIKit/UITextFieldTests.swift | 29 +++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 Tests/UIKit/UITextFieldTests.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 9fc90d679a..8cb27e72fc 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 8295FD8D1B87374A007C9000 /* UIBarButtonItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */; }; C7932E831C4B3F3000086F3C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E811C4B3EDB00086F3C /* UITextField.swift */; }; C7932E841C4B41E100086F3C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E811C4B3EDB00086F3C /* UITextField.swift */; }; + C7932E871C4B42F500086F3C /* UITextFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E851C4B420A00086F3C /* UITextFieldTests.swift */; }; 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, ); }; }; @@ -178,6 +179,7 @@ 8295FD881B873490007C9000 /* UIButtonTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIButtonTests.swift; sourceTree = ""; }; 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBarButtonItemTests.swift; sourceTree = ""; }; C7932E811C4B3EDB00086F3C /* UITextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextField.swift; sourceTree = ""; }; + C7932E851C4B420A00086F3C /* UITextFieldTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextFieldTests.swift; sourceTree = ""; }; D8003E921AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D8003E931AFEC3D400D7D3C5 /* Rex.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Rex.h; sourceTree = ""; }; D8003E9F1AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -422,6 +424,7 @@ D8F073141B861B3A0047D546 /* UILabelTests.swift */, 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */, 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */, + C7932E851C4B420A00086F3C /* UITextFieldTests.swift */, ); path = UIKit; sourceTree = ""; @@ -779,6 +782,7 @@ D8F0974B1B17F5E2002E15BA /* NSObjectTests.swift in Sources */, 8289A2E81BD7F7900097FB60 /* UIViewTests.swift in Sources */, D8F073161B863CE70047D546 /* UILabelTests.swift in Sources */, + C7932E871C4B42F500086F3C /* UITextFieldTests.swift in Sources */, 8295FD8A1B87352D007C9000 /* UIButtonTests.swift in Sources */, D8A4540A1BD2772700C9E790 /* PropertyTests.swift in Sources */, D83457301AFEE45E0070616A /* SignalProducerTests.swift in Sources */, diff --git a/Tests/UIKit/UITextFieldTests.swift b/Tests/UIKit/UITextFieldTests.swift new file mode 100644 index 0000000000..7ca02d34d6 --- /dev/null +++ b/Tests/UIKit/UITextFieldTests.swift @@ -0,0 +1,29 @@ +// +// UITextFieldTests.swift +// Rex +// +// Created by Rui Peres on 17/01/2016. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa +import UIKit +import XCTest + +class UITextFieldTests: XCTestCase { + + func testTextProperty() { + let expectation = self.expectationWithDescription("Expected textSignal's value to equal to the textField's text") + defer { self.waitForExpectationsWithTimeout(2, handler: nil) } + + let textField = UITextField(frame: CGRectZero) + textField.text = "Test" + + textField.rex_textSignal.startWithNext { text in + XCTAssertEqual(text, textField.text) + expectation.fulfill() + } + + NSNotificationCenter.defaultCenter().postNotificationName(UITextFieldTextDidChangeNotification, object: textField) + } +} From 40169a8d1442ba628c6b0b05669fa52b3cb08362 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Thu, 21 Jan 2016 12:10:20 -0800 Subject: [PATCH 0239/1028] Update dependencies to RAC 4 RC 2 --- Cartfile | 2 +- Cartfile.resolved | 2 +- Source/Property.swift | 18 ++++++++++++++++++ Source/SignalProducer.swift | 2 +- Tests/SignalProducerTests.swift | 2 +- 5 files changed, 22 insertions(+), 4 deletions(-) diff --git a/Cartfile b/Cartfile index ce85889a5b..95aecd9379 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveCocoa" "v4.0.0-RC.1" +github "ReactiveCocoa/ReactiveCocoa" "v4.0.0-RC.2" diff --git a/Cartfile.resolved b/Cartfile.resolved index 52670dde4d..73eabce966 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ github "antitypical/Result" "1.0.1" -github "ReactiveCocoa/ReactiveCocoa" "v4.0.0-RC.1" +github "ReactiveCocoa/ReactiveCocoa" "v4.0.0-RC.2" diff --git a/Source/Property.swift b/Source/Property.swift index 5c2e21ad56..2da5003a79 100644 --- a/Source/Property.swift +++ b/Source/Property.swift @@ -50,6 +50,13 @@ public struct AndProperty: PropertyType { } } + public var signal: Signal { + let signals = terms.map { $0.signal } + return combineLatest(signals).map { values in + return values.reduce(true) { $0 && $1 } + } + } + /// Creates a new property with an additional conjunctive term. public func and

(other: P) -> AndProperty { return AndProperty(terms: terms + [AnyProperty(other)]) @@ -80,6 +87,13 @@ public struct OrProperty: PropertyType { } } + public var signal: Signal { + let signals = terms.map { $0.signal } + return combineLatest(signals).map { values in + return values.reduce(false) { $0 || $1 } + } + } + /// Creates a new property with an additional disjunctive term. public func or

(other: P) -> OrProperty { return OrProperty(terms: terms + [AnyProperty(other)]) @@ -108,6 +122,10 @@ public struct NotProperty: PropertyType { return source.producer.map { $0 != self.invert } } + public var signal: Signal { + return source.signal.map { $0 != self.invert } + } + /// A negated property of `self`. public func not() -> NotProperty { return NotProperty(source: source, invert: !invert) diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index bee61c7b40..b0d8f2af81 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -26,7 +26,7 @@ extension SignalProducerType { lock.lock() var group = groups[key] if group == nil { - let (producer, sink) = SignalProducer.buffer() + let (producer, sink) = SignalProducer.buffer(Int.max) observer.sendNext(key, producer) groups[key] = sink diff --git a/Tests/SignalProducerTests.swift b/Tests/SignalProducerTests.swift index 9ea0656ad4..44f1dae747 100644 --- a/Tests/SignalProducerTests.swift +++ b/Tests/SignalProducerTests.swift @@ -13,7 +13,7 @@ import XCTest final class SignalProducerTests: XCTestCase { func testGroupBy() { - let (producer, sink) = SignalProducer.buffer() + let (producer, sink) = SignalProducer.buffer(Int.max) var evens: [Int] = [] var odds: [Int] = [] let disposable = CompositeDisposable() From ce1b8417783a98e1c1e0d8740c3547d22773f943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bjarke=20Hesthaven=20S=C3=B8ndergaard?= Date: Thu, 17 Sep 2015 09:59:38 +0200 Subject: [PATCH 0240/1028] Added podspec --- Rex.podspec | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 Rex.podspec diff --git a/Rex.podspec b/Rex.podspec new file mode 100644 index 0000000000..f0b78e0ca8 --- /dev/null +++ b/Rex.podspec @@ -0,0 +1,136 @@ +# +# Be sure to run `pod spec lint Rex.podspec' to ensure this is a +# valid spec and to remove all comments including this before submitting the spec. +# +# To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html +# To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ +# + +Pod::Spec.new do |s| + + # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # These will help people to find your library, and whilst it + # can feel like a chore to fill in it's definitely to your advantage. The + # summary should be tweet-length, and the description more in depth. + # + + s.name = "Rex" + s.module_name = "Rex" + s.version = "0.4.0-swift2" + s.summary = "Additional ReactiveCocoa operators" + + s.description = <<-DESC + Extensions for ReactiveCocoa that may not fit in the core framework. + DESC + + s.homepage = "https://github.com/neilpa/Rex" + # s.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" + + + # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # Licensing your code is important. See http://choosealicense.com for more info. + # CocoaPods will detect a license file if there is a named LICENSE* + # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. + # + + s.license = "MIT" + # s.license = { :type => "MIT", :file => "FILE_LICENSE" } + + + # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # Specify the authors of the library, with email addresses. Email addresses + # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also + # accepts just a name if you'd rather not provide an email address. + # + # Specify a social_media_url where others can refer to, for example a twitter + # profile URL. + # + + s.author = { "Neil Pankey" => "npankey@gmail.com" } + # Or just: s.author = "Bjarke Hesthaven Søndergaard" + # s.authors = { "Bjarke Hesthaven Søndergaard" => "bhs@trifork.com" } + # s.social_media_url = "http://twitter.com/Bjarke Hesthaven Søndergaard" + + # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # If this Pod runs only on iOS or OS X, then specify the platform and + # the deployment target. You can optionally include the target after the platform. + # + + # s.platform = :ios + # s.platform = :ios, "8.0" + + # When using multiple platforms + s.ios.deployment_target = "8.0" + s.osx.deployment_target = "10.10" + # s.watchos.deployment_target = "2.0" + + + # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # Specify the location from where the source should be retrieved. + # Supports git, hg, bzr, svn and HTTP. + # + + s.source = { :git => "https://github.com/neilpa/Rex.git", :branch => "swift2", :HEAD } + + + # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # CocoaPods is smart about how it includes source code. For source files + # giving a folder will include any swift, h, m, mm, c & cpp files. + # For header files it will include any header in the folder. + # Not including the public_header_files will make all headers public. + # + + s.source_files = "Source/**/*.swift" + s.ios.exclude_files = "Source/AppKit/*" + s.osx.exclude_files = "Source/UIKit/*" + + # s.public_header_files = "Classes/**/*.h" + + + # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # A list of resources included with the Pod. These are copied into the + # target bundle with a build phase script. Anything else will be cleaned. + # You can preserve files from being cleaned, please don't preserve + # non-essential files like tests, examples and documentation. + # + + # s.resource = "icon.png" + # s.resources = "Resources/*.png" + + # s.preserve_paths = "FilesToSave", "MoreFilesToSave" + + + # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # Link your library with frameworks, or libraries. Libraries do not include + # the lib prefix of their name. + # + + s.ios.framework = "UIKit" + s.osx.framework = "AppKit" + # s.frameworks = "SomeFramework", "AnotherFramework" + + # s.library = "iconv" + # s.libraries = "iconv", "xml2" + + + # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # If your library depends on compiler flags you can set them in the xcconfig hash + # where they will only apply to your library. If you depend on other Podspecs + # you can include multiple dependencies to ensure it works. + + # s.requires_arc = true + s.dependency 'ReactiveCocoa', '4.0.2-alpha-1' + + # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } + # s.dependency "JSONKit", "~> 1.4" + +end From dcaf888eec53cf82cb30571788a0fad7de9ed050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bjarke=20Hesthaven=20S=C3=B8ndergaard?= Date: Mon, 26 Oct 2015 10:02:17 +0100 Subject: [PATCH 0241/1028] Updated podspec to 0.9.0-alpha.3 --- Rex.podspec | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Rex.podspec b/Rex.podspec index f0b78e0ca8..ef37b33873 100644 --- a/Rex.podspec +++ b/Rex.podspec @@ -17,8 +17,8 @@ Pod::Spec.new do |s| s.name = "Rex" s.module_name = "Rex" - s.version = "0.4.0-swift2" - s.summary = "Additional ReactiveCocoa operators" + s.version = "0.9.0-alpha.3" + s.summary = "ReactiveCocoa Extensions" s.description = <<-DESC Extensions for ReactiveCocoa that may not fit in the core framework. @@ -75,7 +75,7 @@ Pod::Spec.new do |s| # Supports git, hg, bzr, svn and HTTP. # - s.source = { :git => "https://github.com/neilpa/Rex.git", :branch => "swift2", :HEAD } + s.source = { :git => "https://github.com/neilpa/Rex.git", :tag => "0.9.0-alpha.3" } # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # @@ -128,7 +128,7 @@ Pod::Spec.new do |s| # you can include multiple dependencies to ensure it works. # s.requires_arc = true - s.dependency 'ReactiveCocoa', '4.0.2-alpha-1' + s.dependency 'ReactiveCocoa', '4.0.0-alpha-3' # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } # s.dependency "JSONKit", "~> 1.4" From 37ebb40c4f8baf50bdb9594eb8e6b1c15fd94bec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bjarke=20Hesthaven=20S=C3=B8ndergaard?= Date: Thu, 28 Jan 2016 16:36:09 +0100 Subject: [PATCH 0242/1028] Updated podspec for release candidate 2 --- Rex.podspec | 144 ++++++++-------------------------------------------- 1 file changed, 20 insertions(+), 124 deletions(-) diff --git a/Rex.podspec b/Rex.podspec index ef37b33873..07b8525c78 100644 --- a/Rex.podspec +++ b/Rex.podspec @@ -1,136 +1,32 @@ -# -# Be sure to run `pod spec lint Rex.podspec' to ensure this is a -# valid spec and to remove all comments including this before submitting the spec. -# -# To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html -# To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ -# - Pod::Spec.new do |s| - - # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # These will help people to find your library, and whilst it - # can feel like a chore to fill in it's definitely to your advantage. The - # summary should be tweet-length, and the description more in depth. - # - - s.name = "Rex" - s.module_name = "Rex" - s.version = "0.9.0-alpha.3" - s.summary = "ReactiveCocoa Extensions" + s.name = 'Rex' + s.module_name = 'Rex' + s.version = '0.9.0-RC.2' + s.summary = 'ReactiveCocoa Extensions' s.description = <<-DESC Extensions for ReactiveCocoa that may not fit in the core framework. DESC - s.homepage = "https://github.com/neilpa/Rex" - # s.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" - - - # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # Licensing your code is important. See http://choosealicense.com for more info. - # CocoaPods will detect a license file if there is a named LICENSE* - # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. - # - - s.license = "MIT" - # s.license = { :type => "MIT", :file => "FILE_LICENSE" } - - - # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # Specify the authors of the library, with email addresses. Email addresses - # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also - # accepts just a name if you'd rather not provide an email address. - # - # Specify a social_media_url where others can refer to, for example a twitter - # profile URL. - # - - s.author = { "Neil Pankey" => "npankey@gmail.com" } - # Or just: s.author = "Bjarke Hesthaven Søndergaard" - # s.authors = { "Bjarke Hesthaven Søndergaard" => "bhs@trifork.com" } - # s.social_media_url = "http://twitter.com/Bjarke Hesthaven Søndergaard" - - # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # If this Pod runs only on iOS or OS X, then specify the platform and - # the deployment target. You can optionally include the target after the platform. - # - - # s.platform = :ios - # s.platform = :ios, "8.0" - - # When using multiple platforms - s.ios.deployment_target = "8.0" - s.osx.deployment_target = "10.10" - # s.watchos.deployment_target = "2.0" - - - # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # Specify the location from where the source should be retrieved. - # Supports git, hg, bzr, svn and HTTP. - # - - s.source = { :git => "https://github.com/neilpa/Rex.git", :tag => "0.9.0-alpha.3" } - - - # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # CocoaPods is smart about how it includes source code. For source files - # giving a folder will include any swift, h, m, mm, c & cpp files. - # For header files it will include any header in the folder. - # Not including the public_header_files will make all headers public. - # - - s.source_files = "Source/**/*.swift" - s.ios.exclude_files = "Source/AppKit/*" - s.osx.exclude_files = "Source/UIKit/*" - - # s.public_header_files = "Classes/**/*.h" - - - # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # A list of resources included with the Pod. These are copied into the - # target bundle with a build phase script. Anything else will be cleaned. - # You can preserve files from being cleaned, please don't preserve - # non-essential files like tests, examples and documentation. - # - - # s.resource = "icon.png" - # s.resources = "Resources/*.png" - - # s.preserve_paths = "FilesToSave", "MoreFilesToSave" - - - # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # Link your library with frameworks, or libraries. Libraries do not include - # the lib prefix of their name. - # - - s.ios.framework = "UIKit" - s.osx.framework = "AppKit" - # s.frameworks = "SomeFramework", "AnotherFramework" - - # s.library = "iconv" - # s.libraries = "iconv", "xml2" + s.homepage = 'https://github.com/neilpa/Rex' + s.license = 'MIT' + s.author = { 'Neil Pankey' => 'npankey@gmail.com' } - # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # If your library depends on compiler flags you can set them in the xcconfig hash - # where they will only apply to your library. If you depend on other Podspecs - # you can include multiple dependencies to ensure it works. + s.ios.deployment_target = '8.0' + s.osx.deployment_target = '10.10' + s.watchos.deployment_target = '2.0' + s.tvos.deployment_target = '9.0' - # s.requires_arc = true - s.dependency 'ReactiveCocoa', '4.0.0-alpha-3' + s.source = { :git => 'https://github.com/neilpa/Rex.git', :tag => '0.9.0-RC.2' } + s.dependency 'ReactiveCocoa', '4.0.0-RC.2' + s.ios.framework = 'UIKit' + s.osx.framework = 'AppKit' - # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } - # s.dependency "JSONKit", "~> 1.4" + s.source_files = 'Source/**/*.swift' + s.ios.exclude_files = 'Source/AppKit/*' + s.tvos.exclude_files = 'Source/AppKit/*' + s.watchos.exclude_files = 'Source/AppKit/*', 'Source/UIKit/*' + s.osx.exclude_files = 'Source/UIKit/*' end From c812b2db08a4f6c4132e3524e7874e0167a32a37 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Fri, 29 Jan 2016 13:40:38 +0900 Subject: [PATCH 0243/1028] Update RAC to 4.0 final :tada: --- Cartfile | 2 +- Cartfile.resolved | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cartfile b/Cartfile index 95aecd9379..ef5f54db76 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveCocoa" "v4.0.0-RC.2" +github "ReactiveCocoa/ReactiveCocoa" ~> 4.0 diff --git a/Cartfile.resolved b/Cartfile.resolved index 73eabce966..8c6e502003 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ github "antitypical/Result" "1.0.1" -github "ReactiveCocoa/ReactiveCocoa" "v4.0.0-RC.2" +github "ReactiveCocoa/ReactiveCocoa" "v4.0.0" From 2ca14309bae2866e2968de9c40d342f0e3be881f Mon Sep 17 00:00:00 2001 From: Matthias Buchetics Date: Tue, 2 Feb 2016 09:11:05 +0100 Subject: [PATCH 0244/1028] use Result.NoError --- Cartfile.resolved | 4 ++-- Source/Action.swift | 1 + Source/Foundation/NSObject.swift | 1 + Source/Foundation/NSUserDefaults.swift | 1 + Source/Property.swift | 1 + Source/Signal.swift | 1 + Source/SignalProducer.swift | 1 + Source/UIKit/UIControl.swift | 1 + Source/UIKit/UITextField.swift | 1 + 9 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Cartfile.resolved b/Cartfile.resolved index 8c6e502003..03fee973a2 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ -github "antitypical/Result" "1.0.1" -github "ReactiveCocoa/ReactiveCocoa" "v4.0.0" +github "antitypical/Result" "1.0.2" +github "ReactiveCocoa/ReactiveCocoa" "v4.0.1" diff --git a/Source/Action.swift b/Source/Action.swift index 9dda3379b2..9b325cc52c 100644 --- a/Source/Action.swift +++ b/Source/Action.swift @@ -7,6 +7,7 @@ // import ReactiveCocoa +import enum Result.NoError extension Action { /// Creates an always disabled action. diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index 657a039914..324122c829 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -8,6 +8,7 @@ import Foundation import ReactiveCocoa +import enum Result.NoError extension NSObject { /// Creates a strongly-typed producer to monitor `keyPath` via KVO. The caller diff --git a/Source/Foundation/NSUserDefaults.swift b/Source/Foundation/NSUserDefaults.swift index 65dd33a646..b8be9572ac 100644 --- a/Source/Foundation/NSUserDefaults.swift +++ b/Source/Foundation/NSUserDefaults.swift @@ -8,6 +8,7 @@ import Foundation import ReactiveCocoa +import enum Result.NoError extension NSUserDefaults { /// Sends value of `key` whenever it changes. Attempts to filter out repeats diff --git a/Source/Property.swift b/Source/Property.swift index 2da5003a79..7703b93ae9 100644 --- a/Source/Property.swift +++ b/Source/Property.swift @@ -7,6 +7,7 @@ // import ReactiveCocoa +import enum Result.NoError extension PropertyType where Value == Bool { /// The conjunction of `self` and `other`. diff --git a/Source/Signal.swift b/Source/Signal.swift index c587bb4f8a..c47d353c99 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -7,6 +7,7 @@ // import ReactiveCocoa +import enum Result.NoError extension SignalType { diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index b0d8f2af81..4c65e5248f 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -7,6 +7,7 @@ // import ReactiveCocoa +import enum Result.NoError extension SignalProducerType { diff --git a/Source/UIKit/UIControl.swift b/Source/UIKit/UIControl.swift index f92da0ac22..3c7b4fbec9 100644 --- a/Source/UIKit/UIControl.swift +++ b/Source/UIKit/UIControl.swift @@ -8,6 +8,7 @@ import ReactiveCocoa import UIKit +import enum Result.NoError extension UIControl { #if os(iOS) diff --git a/Source/UIKit/UITextField.swift b/Source/UIKit/UITextField.swift index efe00db404..9833399028 100644 --- a/Source/UIKit/UITextField.swift +++ b/Source/UIKit/UITextField.swift @@ -9,6 +9,7 @@ import Foundation import ReactiveCocoa import UIKit +import enum Result.NoError extension UITextField { From 1e5fe946b6be8854b4beece32f113c7a5c7b9068 Mon Sep 17 00:00:00 2001 From: Matthias Buchetics Date: Tue, 2 Feb 2016 09:13:56 +0100 Subject: [PATCH 0245/1028] updated podspec --- Rex.podspec | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Rex.podspec b/Rex.podspec index 07b8525c78..dbbad1a8ae 100644 --- a/Rex.podspec +++ b/Rex.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = 'Rex' s.module_name = 'Rex' - s.version = '0.9.0-RC.2' + s.version = '0.9.0' s.summary = 'ReactiveCocoa Extensions' s.description = <<-DESC @@ -18,8 +18,8 @@ Pod::Spec.new do |s| s.watchos.deployment_target = '2.0' s.tvos.deployment_target = '9.0' - s.source = { :git => 'https://github.com/neilpa/Rex.git', :tag => '0.9.0-RC.2' } - s.dependency 'ReactiveCocoa', '4.0.0-RC.2' + s.source = { :git => 'https://github.com/neilpa/Rex.git', :tag => '0.9.0' } + s.dependency 'ReactiveCocoa', '~> 4.0' s.ios.framework = 'UIKit' s.osx.framework = 'AppKit' From 8522530fc4aed521e666958d20011f17370e013b Mon Sep 17 00:00:00 2001 From: Matthias Buchetics Date: Tue, 2 Feb 2016 13:53:21 +0100 Subject: [PATCH 0246/1028] fixed tests --- Tests/PropertyTests.swift | 1 + Tests/SignalProducerTests.swift | 1 + Tests/SignalTests.swift | 1 + Tests/UIKit/UIBarButtonItemTests.swift | 1 + Tests/UIKit/UIButtonTests.swift | 1 + Tests/UIKit/UIControlTests.swift | 1 + Tests/UIKit/UIImageViewTests.swift | 1 + Tests/UIKit/UILabelTests.swift | 1 + Tests/UIKit/UIViewTests.swift | 1 + 9 files changed, 9 insertions(+) diff --git a/Tests/PropertyTests.swift b/Tests/PropertyTests.swift index a1fa20d2d6..ea32ccbe93 100644 --- a/Tests/PropertyTests.swift +++ b/Tests/PropertyTests.swift @@ -9,6 +9,7 @@ @testable import Rex import ReactiveCocoa import XCTest +import enum Result.NoError final class PropertyTests: XCTestCase { diff --git a/Tests/SignalProducerTests.swift b/Tests/SignalProducerTests.swift index 44f1dae747..36590b687a 100644 --- a/Tests/SignalProducerTests.swift +++ b/Tests/SignalProducerTests.swift @@ -9,6 +9,7 @@ import Rex import ReactiveCocoa import XCTest +import enum Result.NoError final class SignalProducerTests: XCTestCase { diff --git a/Tests/SignalTests.swift b/Tests/SignalTests.swift index 1ec9ee8f00..52a2cc6cbc 100644 --- a/Tests/SignalTests.swift +++ b/Tests/SignalTests.swift @@ -9,6 +9,7 @@ import Rex import ReactiveCocoa import XCTest +import enum Result.NoError final class SignalTests: XCTestCase { diff --git a/Tests/UIKit/UIBarButtonItemTests.swift b/Tests/UIKit/UIBarButtonItemTests.swift index 97e2e78493..438553a125 100644 --- a/Tests/UIKit/UIBarButtonItemTests.swift +++ b/Tests/UIKit/UIBarButtonItemTests.swift @@ -9,6 +9,7 @@ import ReactiveCocoa import UIKit import XCTest +import enum Result.NoError class UIBarButtonItemTests: XCTestCase { diff --git a/Tests/UIKit/UIButtonTests.swift b/Tests/UIKit/UIButtonTests.swift index 5fa5be878a..b2d4e10112 100644 --- a/Tests/UIKit/UIButtonTests.swift +++ b/Tests/UIKit/UIButtonTests.swift @@ -9,6 +9,7 @@ import ReactiveCocoa import UIKit import XCTest +import enum Result.NoError extension UIButton { static func button() -> UIButton { diff --git a/Tests/UIKit/UIControlTests.swift b/Tests/UIKit/UIControlTests.swift index d25bd11235..b16bfe480d 100644 --- a/Tests/UIKit/UIControlTests.swift +++ b/Tests/UIKit/UIControlTests.swift @@ -9,6 +9,7 @@ import ReactiveCocoa import UIKit import XCTest +import enum Result.NoError class UIControlTests: XCTestCase { diff --git a/Tests/UIKit/UIImageViewTests.swift b/Tests/UIKit/UIImageViewTests.swift index bb98ea9979..b632f512e0 100644 --- a/Tests/UIKit/UIImageViewTests.swift +++ b/Tests/UIKit/UIImageViewTests.swift @@ -9,6 +9,7 @@ import ReactiveCocoa import UIKit import XCTest +import enum Result.NoError class UIImageViewTests: XCTestCase { diff --git a/Tests/UIKit/UILabelTests.swift b/Tests/UIKit/UILabelTests.swift index 24c64c48a0..15bd78cefe 100644 --- a/Tests/UIKit/UILabelTests.swift +++ b/Tests/UIKit/UILabelTests.swift @@ -9,6 +9,7 @@ import ReactiveCocoa import UIKit import XCTest +import enum Result.NoError class UILabelTests: XCTestCase { diff --git a/Tests/UIKit/UIViewTests.swift b/Tests/UIKit/UIViewTests.swift index 08828b2865..defdca62ce 100644 --- a/Tests/UIKit/UIViewTests.swift +++ b/Tests/UIKit/UIViewTests.swift @@ -9,6 +9,7 @@ import ReactiveCocoa import UIKit import XCTest +import enum Result.NoError class UIViewTests: XCTestCase { From ff6bc9f64acef0eda2eabe1ab62a6582fed565e7 Mon Sep 17 00:00:00 2001 From: Matthias Buchetics Date: Tue, 2 Feb 2016 20:51:48 +0100 Subject: [PATCH 0247/1028] fixed Mac target --- Source/AppKit/NSTextField.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/AppKit/NSTextField.swift b/Source/AppKit/NSTextField.swift index c7bf7b75d5..c53ac10c16 100644 --- a/Source/AppKit/NSTextField.swift +++ b/Source/AppKit/NSTextField.swift @@ -9,6 +9,7 @@ import Foundation import ReactiveCocoa import AppKit +import enum Result.NoError extension NSTextField { /// Sends the field's string value whenever it changes. From 3b239b9d60ad7e900b10b6605a617756af94d461 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bjarke=20Hesthaven=20S=C3=B8ndergaard?= Date: Fri, 5 Feb 2016 09:34:40 +0100 Subject: [PATCH 0248/1028] We require 4.0.1 of RAC to work We cannot actually compile without using Result 1.0.2, and this requires RAC 4.0.1 at least. --- Rex.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rex.podspec b/Rex.podspec index dbbad1a8ae..45f59a6e99 100644 --- a/Rex.podspec +++ b/Rex.podspec @@ -19,7 +19,7 @@ Pod::Spec.new do |s| s.tvos.deployment_target = '9.0' s.source = { :git => 'https://github.com/neilpa/Rex.git', :tag => '0.9.0' } - s.dependency 'ReactiveCocoa', '~> 4.0' + s.dependency 'ReactiveCocoa', '~> 4.0.1' s.ios.framework = 'UIKit' s.osx.framework = 'AppKit' From ea3b58b8ea62e90c311d274276f56de18a883b7c Mon Sep 17 00:00:00 2001 From: Guido Marucci Blas Date: Sat, 26 Mar 2016 00:13:54 -0300 Subject: [PATCH 0249/1028] Adds rex_date to UIDatePicker. --- Rex.xcodeproj/project.pbxproj | 8 +++++ Source/UIKit/UIDatePicker.swift | 28 +++++++++++++++++ Tests/UIKit/UIDatePickerTests.swift | 48 +++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 Source/UIKit/UIDatePicker.swift create mode 100644 Tests/UIKit/UIDatePickerTests.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 8cb27e72fc..9e65d9f502 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -15,6 +15,8 @@ 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 */; }; + 9DA915A41CA6301C003723B9 /* UIDatePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DA915A31CA6301C003723B9 /* UIDatePicker.swift */; }; + 9DA915A61CA63046003723B9 /* UIDatePickerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DA915A51CA63046003723B9 /* UIDatePickerTests.swift */; }; C7932E831C4B3F3000086F3C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E811C4B3EDB00086F3C /* UITextField.swift */; }; C7932E841C4B41E100086F3C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E811C4B3EDB00086F3C /* UITextField.swift */; }; C7932E871C4B42F500086F3C /* UITextFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E851C4B420A00086F3C /* UITextFieldTests.swift */; }; @@ -178,6 +180,8 @@ 8295FD851B873081007C9000 /* UIControlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIControlTests.swift; sourceTree = ""; }; 8295FD881B873490007C9000 /* UIButtonTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIButtonTests.swift; sourceTree = ""; }; 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBarButtonItemTests.swift; sourceTree = ""; }; + 9DA915A31CA6301C003723B9 /* UIDatePicker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIDatePicker.swift; sourceTree = ""; }; + 9DA915A51CA63046003723B9 /* UIDatePickerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIDatePickerTests.swift; sourceTree = ""; }; C7932E811C4B3EDB00086F3C /* UITextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextField.swift; sourceTree = ""; }; C7932E851C4B420A00086F3C /* UITextFieldTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextFieldTests.swift; sourceTree = ""; }; D8003E921AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -390,6 +394,7 @@ 8289A2E21BD7EF740097FB60 /* UIImageView.swift */, 8289A2E41BD7F6DD0097FB60 /* UIView.swift */, C7932E811C4B3EDB00086F3C /* UITextField.swift */, + 9DA915A31CA6301C003723B9 /* UIDatePicker.swift */, ); path = UIKit; sourceTree = ""; @@ -425,6 +430,7 @@ 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */, 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */, C7932E851C4B420A00086F3C /* UITextFieldTests.swift */, + 9DA915A51CA63046003723B9 /* UIDatePickerTests.swift */, ); path = UIKit; sourceTree = ""; @@ -755,6 +761,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9DA915A41CA6301C003723B9 /* UIDatePicker.swift in Sources */, D86FFBD81B34B242001A89B3 /* UILabel.swift in Sources */, D86FFBDB1B34B3F0001A89B3 /* Action.swift in Sources */, D86FFBD21B34AD7A001A89B3 /* Association.swift in Sources */, @@ -785,6 +792,7 @@ C7932E871C4B42F500086F3C /* UITextFieldTests.swift in Sources */, 8295FD8A1B87352D007C9000 /* UIButtonTests.swift in Sources */, D8A4540A1BD2772700C9E790 /* PropertyTests.swift in Sources */, + 9DA915A61CA63046003723B9 /* UIDatePickerTests.swift in Sources */, D83457301AFEE45E0070616A /* SignalProducerTests.swift in Sources */, D83457411AFEE6050070616A /* SignalTests.swift in Sources */, 8295FD8D1B87374A007C9000 /* UIBarButtonItemTests.swift in Sources */, diff --git a/Source/UIKit/UIDatePicker.swift b/Source/UIKit/UIDatePicker.swift new file mode 100644 index 0000000000..8c6376b602 --- /dev/null +++ b/Source/UIKit/UIDatePicker.swift @@ -0,0 +1,28 @@ +// +// UIDatePicker.swift +// Rex +// +// Created by Guido Marucci Blas on 3/25/16. +// Copyright © 2016 Neil Pankey. All rights reserved. +// +import UIKit +import ReactiveCocoa + +extension UIDatePicker { + + public var rex_date: MutableProperty { + let initial = { (picker: UIDatePicker) -> NSDate in + picker.addTarget(self, action: "rex_changedDate", forControlEvents: .ValueChanged) + return picker.date + } + return associatedProperty(self, key: &dateKey, initial: initial) { $0.date = $1 } + } + + @objc + private func rex_changedDate() { + rex_date.value = date + } + +} + +private var dateKey: UInt8 = 0 \ No newline at end of file diff --git a/Tests/UIKit/UIDatePickerTests.swift b/Tests/UIKit/UIDatePickerTests.swift new file mode 100644 index 0000000000..a0d37bd4ba --- /dev/null +++ b/Tests/UIKit/UIDatePickerTests.swift @@ -0,0 +1,48 @@ +// +// UIDatePickerTests.swift +// Rex +// +// Created by Guido Marucci Blas on 3/25/16. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa +import UIKit +import XCTest +import Rex + +class UIDatePickerTests: XCTestCase { + + var date: NSDate! + var picker: UIDatePicker! + + override func setUp() { + let formatter = NSDateFormatter() + formatter.dateFormat = "MM/dd/YYYY" + date = formatter.dateFromString("11/29/1988")! + + picker = UIDatePicker(frame: CGRectZero) + } + + func testUpdatePickerFromProperty() { + picker.rex_date.value = date + + XCTAssertEqual(picker.date, date) + } + + 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) } + + picker.rex_date.signal.observeNext { changedDate in + XCTAssertEqual(changedDate, self.date) + expectation.fulfill() + } + + picker.date = date + picker.enabled = true + picker.userInteractionEnabled = true + picker.sendActionsForControlEvents(.ValueChanged) + } + +} From fb53d91aab32582f229e6bca72a2ad2dd67fa853 Mon Sep 17 00:00:00 2001 From: Rui Peres Date: Tue, 29 Mar 2016 11:43:33 +0100 Subject: [PATCH 0250/1028] Updated Dependencies for Swift 2.2 --- .travis.yml | 2 +- Cartfile | 2 +- Cartfile.resolved | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 387cdfe116..c694c6482a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: objective-c matrix: include: - - osx_image: xcode7.2 + - osx_image: xcode7.3 env: global: diff --git a/Cartfile b/Cartfile index ef5f54db76..d637b9a8d4 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveCocoa" ~> 4.0 +github "ReactiveCocoa/ReactiveCocoa" ~> 4.1 diff --git a/Cartfile.resolved b/Cartfile.resolved index 03fee973a2..4726855e02 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ -github "antitypical/Result" "1.0.2" -github "ReactiveCocoa/ReactiveCocoa" "v4.0.1" +github "antitypical/Result" "2.0.0" +github "ReactiveCocoa/ReactiveCocoa" "v4.1.0" From 8ac755dcefbde82da19d751c094589c10180e43e Mon Sep 17 00:00:00 2001 From: Rui Peres Date: Fri, 1 Apr 2016 16:29:15 +0100 Subject: [PATCH 0251/1028] Added AttributedText to UILabel --- Source/UIKit/UILabel.swift | 5 +++++ Tests/UIKit/UILabelTests.swift | 24 ++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/Source/UIKit/UILabel.swift b/Source/UIKit/UILabel.swift index 8cfb425986..b0375bb098 100644 --- a/Source/UIKit/UILabel.swift +++ b/Source/UIKit/UILabel.swift @@ -14,6 +14,11 @@ extension UILabel { public var rex_text: MutableProperty { return associatedProperty(self, keyPath: "text") } + + /// Wraps a label's `attributedText` value in a bindable property. + public var rex_attributedText: MutableProperty { + return associatedProperty(self, key: &attributedText, initial: { $0.attributedText }, setter: { $0.attributedText = $1 }) + } /// Wraps a label's `textColor` value in a bindable property. public var rex_textColor: MutableProperty { diff --git a/Tests/UIKit/UILabelTests.swift b/Tests/UIKit/UILabelTests.swift index 15bd78cefe..8d10099fb8 100644 --- a/Tests/UIKit/UILabelTests.swift +++ b/Tests/UIKit/UILabelTests.swift @@ -44,6 +44,30 @@ class UILabelTests: XCTestCase { XCTAssertEqual(label.text, secondChange) } + func testAttributedTextPropertyDoesntCreateRetainCycle() { + let label = UILabel(frame: CGRectZero) + _label = label + + label.rex_attributedText <~ SignalProducer(value: NSAttributedString(string: "Test")) + XCTAssert(_label?.attributedText?.string == "Test") + } + + func testAttributedTextProperty() { + let firstChange = NSAttributedString(string: "first") + let secondChange = NSAttributedString(string: "second") + + let label = UILabel(frame: CGRectZero) + label.attributedText = NSAttributedString(string: "") + + let (pipeSignal, observer) = Signal.pipe() + label.rex_attributedText <~ SignalProducer(signal: pipeSignal) + + observer.sendNext(firstChange) + XCTAssertEqual(label.attributedText, firstChange) + observer.sendNext(secondChange) + XCTAssertEqual(label.attributedText, secondChange) + } + func testTextColorProperty() { let firstChange = UIColor.redColor() let secondChange = UIColor.blackColor() From ade2e0db165504f2ce6930ed16a6c1aded0abf82 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Fri, 1 Apr 2016 10:17:23 -0700 Subject: [PATCH 0252/1028] Keys for UILabel associations These were missed when the other extensions were updated to use keys instead of corresponding members which can be problematic (#65). --- Source/UIKit/UILabel.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Source/UIKit/UILabel.swift b/Source/UIKit/UILabel.swift index b0375bb098..1ec0c2529f 100644 --- a/Source/UIKit/UILabel.swift +++ b/Source/UIKit/UILabel.swift @@ -17,11 +17,14 @@ extension UILabel { /// Wraps a label's `attributedText` value in a bindable property. public var rex_attributedText: MutableProperty { - return associatedProperty(self, key: &attributedText, initial: { $0.attributedText }, setter: { $0.attributedText = $1 }) + return associatedProperty(self, key: &attributedTextKey, initial: { $0.attributedText }, setter: { $0.attributedText = $1 }) } /// Wraps a label's `textColor` value in a bindable property. public var rex_textColor: MutableProperty { - return associatedProperty(self, key: &textColor, initial: { $0.textColor }, setter: { $0.textColor = $1 }) + return associatedProperty(self, key: &textColorKey, initial: { $0.textColor }, setter: { $0.textColor = $1 }) } } + +private var attributedTextKey: UInt8 = 0 +private var textColorKey: UInt8 = 0 From e15714db4a5db68b01c7f8f161aafa5b2028b1f3 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Fri, 1 Apr 2016 10:42:05 -0700 Subject: [PATCH 0253/1028] Disable `testUpdatePropertyFromPicker` --- Rex.xcodeproj/project.pbxproj | 12 ++++++------ Tests/UIKit/UIDatePickerTests.swift | 5 +++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 9e65d9f502..203c3be9c8 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -390,11 +390,11 @@ 5173EBC51B625A2600C9B48E /* UIBarItem.swift */, D86FFBDC1B34B691001A89B3 /* UIButton.swift */, D86FFBD41B34B0FE001A89B3 /* UIControl.swift */, - D86FFBD71B34B242001A89B3 /* UILabel.swift */, + 9DA915A31CA6301C003723B9 /* UIDatePicker.swift */, 8289A2E21BD7EF740097FB60 /* UIImageView.swift */, - 8289A2E41BD7F6DD0097FB60 /* UIView.swift */, + D86FFBD71B34B242001A89B3 /* UILabel.swift */, C7932E811C4B3EDB00086F3C /* UITextField.swift */, - 9DA915A31CA6301C003723B9 /* UIDatePicker.swift */, + 8289A2E41BD7F6DD0097FB60 /* UIView.swift */, ); path = UIKit; sourceTree = ""; @@ -426,11 +426,11 @@ 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */, 8295FD881B873490007C9000 /* UIButtonTests.swift */, 8295FD851B873081007C9000 /* UIControlTests.swift */, - D8F073141B861B3A0047D546 /* UILabelTests.swift */, + 9DA915A51CA63046003723B9 /* UIDatePickerTests.swift */, 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */, - 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */, + D8F073141B861B3A0047D546 /* UILabelTests.swift */, C7932E851C4B420A00086F3C /* UITextFieldTests.swift */, - 9DA915A51CA63046003723B9 /* UIDatePickerTests.swift */, + 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */, ); path = UIKit; sourceTree = ""; diff --git a/Tests/UIKit/UIDatePickerTests.swift b/Tests/UIKit/UIDatePickerTests.swift index a0d37bd4ba..2212405f53 100644 --- a/Tests/UIKit/UIDatePickerTests.swift +++ b/Tests/UIKit/UIDatePickerTests.swift @@ -29,8 +29,9 @@ class UIDatePickerTests: XCTestCase { XCTAssertEqual(picker.date, date) } - - func testUpdatePropertyFromPicker() { + + // FIXME Can this actually be made to work inside XCTest? + 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) } From 9bcda00f34e4588d3ecfa63ff66bed9373d3bfe5 Mon Sep 17 00:00:00 2001 From: Yoshiki Kudo Date: Sat, 2 Apr 2016 03:21:42 +0900 Subject: [PATCH 0254/1028] Use #selector --- Source/UIKit/UIDatePicker.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/UIKit/UIDatePicker.swift b/Source/UIKit/UIDatePicker.swift index 8c6376b602..ff3e975605 100644 --- a/Source/UIKit/UIDatePicker.swift +++ b/Source/UIKit/UIDatePicker.swift @@ -12,7 +12,7 @@ extension UIDatePicker { public var rex_date: MutableProperty { let initial = { (picker: UIDatePicker) -> NSDate in - picker.addTarget(self, action: "rex_changedDate", forControlEvents: .ValueChanged) + picker.addTarget(self, action: #selector(UIDatePicker.rex_changedDate), forControlEvents: .ValueChanged) return picker.date } return associatedProperty(self, key: &dateKey, initial: initial) { $0.date = $1 } From 49f08dc0ac65fd5f5890d80e4f9717f5e8fa655a Mon Sep 17 00:00:00 2001 From: David Rodrigues Date: Mon, 4 Apr 2016 16:31:01 +0100 Subject: [PATCH 0255/1028] Embrace #selector to fix a warning and achieve a green state --- Source/UIKit/UIDatePicker.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/UIKit/UIDatePicker.swift b/Source/UIKit/UIDatePicker.swift index 8c6376b602..ff3e975605 100644 --- a/Source/UIKit/UIDatePicker.swift +++ b/Source/UIKit/UIDatePicker.swift @@ -12,7 +12,7 @@ extension UIDatePicker { public var rex_date: MutableProperty { let initial = { (picker: UIDatePicker) -> NSDate in - picker.addTarget(self, action: "rex_changedDate", forControlEvents: .ValueChanged) + picker.addTarget(self, action: #selector(UIDatePicker.rex_changedDate), forControlEvents: .ValueChanged) return picker.date } return associatedProperty(self, key: &dateKey, initial: initial) { $0.date = $1 } From 370e27f7bc189ce512b259255f1d9957b987c00e Mon Sep 17 00:00:00 2001 From: David Rodrigues Date: Mon, 4 Apr 2016 16:33:07 +0100 Subject: [PATCH 0256/1028] Fix podspec and xcodeproj versions - Rex's version should be 0.10.0 - ReactiveCoca needs to be bumped to 4.1 --- Rex.podspec | 6 +++--- Source/Info.plist | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Rex.podspec b/Rex.podspec index 45f59a6e99..21822d2ad5 100644 --- a/Rex.podspec +++ b/Rex.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = 'Rex' s.module_name = 'Rex' - s.version = '0.9.0' + s.version = '0.10.0' s.summary = 'ReactiveCocoa Extensions' s.description = <<-DESC @@ -18,8 +18,8 @@ Pod::Spec.new do |s| s.watchos.deployment_target = '2.0' s.tvos.deployment_target = '9.0' - s.source = { :git => 'https://github.com/neilpa/Rex.git', :tag => '0.9.0' } - s.dependency 'ReactiveCocoa', '~> 4.0.1' + s.source = { :git => 'https://github.com/neilpa/Rex.git', :tag => s.version } + s.dependency 'ReactiveCocoa', '~> 4.1' s.ios.framework = 'UIKit' s.osx.framework = 'AppKit' diff --git a/Source/Info.plist b/Source/Info.plist index f849d15d8b..80718e6547 100644 --- a/Source/Info.plist +++ b/Source/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.9.0 + 0.10.0 CFBundleSignature ???? CFBundleVersion From b529c3405e1215a2aafd9217ded2dcf148f4f6af Mon Sep 17 00:00:00 2001 From: Rui Peres Date: Tue, 5 Apr 2016 11:28:14 +0100 Subject: [PATCH 0257/1028] Added UITextView text's producer --- Rex.xcodeproj/project.pbxproj | 8 ++++++++ Source/UIKit/UITextView.swift | 24 ++++++++++++++++++++++++ Tests/UIKit/UITextViewTests.swift | 29 +++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 Source/UIKit/UITextView.swift create mode 100644 Tests/UIKit/UITextViewTests.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 203c3be9c8..adc65d3173 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -20,6 +20,8 @@ C7932E831C4B3F3000086F3C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E811C4B3EDB00086F3C /* UITextField.swift */; }; C7932E841C4B41E100086F3C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E811C4B3EDB00086F3C /* UITextField.swift */; }; C7932E871C4B42F500086F3C /* UITextFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E851C4B420A00086F3C /* UITextFieldTests.swift */; }; + C7DCE2B41CB3C89A001217D8 /* UITextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DCE2B21CB3C872001217D8 /* UITextView.swift */; }; + C7DCE2B71CB3C9D6001217D8 /* UITextViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DCE2B51CB3C9C1001217D8 /* UITextViewTests.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, ); }; }; @@ -184,6 +186,8 @@ 9DA915A51CA63046003723B9 /* UIDatePickerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIDatePickerTests.swift; sourceTree = ""; }; C7932E811C4B3EDB00086F3C /* UITextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextField.swift; sourceTree = ""; }; C7932E851C4B420A00086F3C /* UITextFieldTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextFieldTests.swift; sourceTree = ""; }; + C7DCE2B21CB3C872001217D8 /* UITextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextView.swift; sourceTree = ""; }; + C7DCE2B51CB3C9C1001217D8 /* UITextViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextViewTests.swift; sourceTree = ""; }; D8003E921AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D8003E931AFEC3D400D7D3C5 /* Rex.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Rex.h; sourceTree = ""; }; D8003E9F1AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -394,6 +398,7 @@ 8289A2E21BD7EF740097FB60 /* UIImageView.swift */, D86FFBD71B34B242001A89B3 /* UILabel.swift */, C7932E811C4B3EDB00086F3C /* UITextField.swift */, + C7DCE2B21CB3C872001217D8 /* UITextView.swift */, 8289A2E41BD7F6DD0097FB60 /* UIView.swift */, ); path = UIKit; @@ -430,6 +435,7 @@ 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */, D8F073141B861B3A0047D546 /* UILabelTests.swift */, C7932E851C4B420A00086F3C /* UITextFieldTests.swift */, + C7DCE2B51CB3C9C1001217D8 /* UITextViewTests.swift */, 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */, ); path = UIKit; @@ -774,6 +780,7 @@ D8E4A6201B7BBB1600EAD8A8 /* UIBarButtonItem.swift in Sources */, D8F097451B17F3C8002E15BA /* NSObject.swift in Sources */, D834572E1AFEE45B0070616A /* SignalProducer.swift in Sources */, + C7DCE2B41CB3C89A001217D8 /* UITextView.swift in Sources */, 8289A2E51BD7F6DD0097FB60 /* UIView.swift in Sources */, D86FFBD61B34B116001A89B3 /* UIControl.swift in Sources */, D8F0973F1B17F31E002E15BA /* NSData.swift in Sources */, @@ -797,6 +804,7 @@ D83457411AFEE6050070616A /* SignalTests.swift in Sources */, 8295FD8D1B87374A007C9000 /* UIBarButtonItemTests.swift in Sources */, 8295FD871B87309F007C9000 /* UIControlTests.swift in Sources */, + C7DCE2B71CB3C9D6001217D8 /* UITextViewTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Source/UIKit/UITextView.swift b/Source/UIKit/UITextView.swift new file mode 100644 index 0000000000..716f7675b8 --- /dev/null +++ b/Source/UIKit/UITextView.swift @@ -0,0 +1,24 @@ +// +// UITextView.swift +// Rex +// +// Created by Rui Peres on 05/04/2016. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa +import UIKit +import enum Result.NoError + +extension UITextView { + + /// Sends the textView's string value whenever it changes. + public var rex_textSignal: SignalProducer { + return NSNotificationCenter.defaultCenter() + .rac_notifications(UITextViewTextDidChangeNotification, object: self) + .filterMap { notification in + guard let textView = notification.object as? UITextView else { return nil} + return textView.text + } + } +} diff --git a/Tests/UIKit/UITextViewTests.swift b/Tests/UIKit/UITextViewTests.swift new file mode 100644 index 0000000000..9786cb4100 --- /dev/null +++ b/Tests/UIKit/UITextViewTests.swift @@ -0,0 +1,29 @@ +// +// UITextViewTests.swift +// Rex +// +// Created by Rui Peres on 05/04/2016. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa +import UIKit +import XCTest + +class UITextViewTests: XCTestCase { + + func testTextProperty() { + let expectation = self.expectationWithDescription("Expected textSignal's value to equal to the textViews's text") + defer { self.waitForExpectationsWithTimeout(2, handler: nil) } + + let textView = UITextView(frame: CGRectZero) + textView.text = "Test" + + textView.rex_textSignal.startWithNext { text in + XCTAssertEqual(text, textView.text) + expectation.fulfill() + } + + NSNotificationCenter.defaultCenter().postNotificationName(UITextViewTextDidChangeNotification, object: textView) + } +} From 5bc779329682857cc49d68b2b4e5faf80ef456d3 Mon Sep 17 00:00:00 2001 From: Rui Peres Date: Fri, 8 Apr 2016 00:47:22 +0100 Subject: [PATCH 0258/1028] Simplified logic --- Source/UIKit/UITextField.swift | 5 +---- Source/UIKit/UITextView.swift | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/Source/UIKit/UITextField.swift b/Source/UIKit/UITextField.swift index 9833399028..407aede0f4 100644 --- a/Source/UIKit/UITextField.swift +++ b/Source/UIKit/UITextField.swift @@ -17,9 +17,6 @@ extension UITextField { public var rex_textSignal: SignalProducer { return NSNotificationCenter.defaultCenter() .rac_notifications(UITextFieldTextDidChangeNotification, object: self) - .filterMap { notification in - guard let textField = notification.object as? UITextField else { return nil} - return textField.text - } + .filterMap { ($0.object as? UITextField)?.text } } } diff --git a/Source/UIKit/UITextView.swift b/Source/UIKit/UITextView.swift index 716f7675b8..3315749b9e 100644 --- a/Source/UIKit/UITextView.swift +++ b/Source/UIKit/UITextView.swift @@ -16,9 +16,6 @@ extension UITextView { public var rex_textSignal: SignalProducer { return NSNotificationCenter.defaultCenter() .rac_notifications(UITextViewTextDidChangeNotification, object: self) - .filterMap { notification in - guard let textView = notification.object as? UITextView else { return nil} - return textView.text - } + .filterMap { ($0.object as? UITextView)?.text } } } From 279dbbf49ea1372651e91f06632b9a68d154b03b Mon Sep 17 00:00:00 2001 From: David Rodrigues Date: Thu, 7 Apr 2016 22:37:25 +0100 Subject: [PATCH 0259/1028] Add UISwitch on's property --- Rex.xcodeproj/project.pbxproj | 8 ++++++++ Source/UIKit/UISwitch.swift | 28 ++++++++++++++++++++++++++++ Tests/UIKit/UISwitchTests.swift | 28 ++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 Source/UIKit/UISwitch.swift create mode 100644 Tests/UIKit/UISwitchTests.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 203c3be9c8..f2ccfb80b7 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -8,6 +8,8 @@ /* Begin PBXBuildFile section */ 4238D5961B4D5950008534C0 /* NSTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4238D5951B4D5950008534C0 /* NSTextField.swift */; }; + 7D2AA99B1CB6EFEB008AB5C9 /* UISwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D2AA99A1CB6EFEB008AB5C9 /* UISwitch.swift */; }; + 7D2AA99D1CB6F275008AB5C9 /* UISwitchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D2AA99C1CB6F275008AB5C9 /* UISwitchTests.swift */; }; 8289A2E11BD7EF1F0097FB60 /* UIImageViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */; }; 8289A2E31BD7EF740097FB60 /* UIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E21BD7EF740097FB60 /* UIImageView.swift */; }; 8289A2E51BD7F6DD0097FB60 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E41BD7F6DD0097FB60 /* UIView.swift */; }; @@ -173,6 +175,8 @@ 4238D5951B4D5950008534C0 /* NSTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NSTextField.swift; path = AppKit/NSTextField.swift; sourceTree = ""; }; 5173EBC51B625A2600C9B48E /* UIBarItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBarItem.swift; sourceTree = ""; }; 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBarButtonItem.swift; sourceTree = ""; }; + 7D2AA99A1CB6EFEB008AB5C9 /* UISwitch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISwitch.swift; sourceTree = ""; }; + 7D2AA99C1CB6F275008AB5C9 /* UISwitchTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISwitchTests.swift; sourceTree = ""; }; 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageViewTests.swift; sourceTree = ""; }; 8289A2E21BD7EF740097FB60 /* UIImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageView.swift; sourceTree = ""; }; 8289A2E41BD7F6DD0097FB60 /* UIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = ""; }; @@ -393,6 +397,7 @@ 9DA915A31CA6301C003723B9 /* UIDatePicker.swift */, 8289A2E21BD7EF740097FB60 /* UIImageView.swift */, D86FFBD71B34B242001A89B3 /* UILabel.swift */, + 7D2AA99A1CB6EFEB008AB5C9 /* UISwitch.swift */, C7932E811C4B3EDB00086F3C /* UITextField.swift */, 8289A2E41BD7F6DD0097FB60 /* UIView.swift */, ); @@ -429,6 +434,7 @@ 9DA915A51CA63046003723B9 /* UIDatePickerTests.swift */, 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */, D8F073141B861B3A0047D546 /* UILabelTests.swift */, + 7D2AA99C1CB6F275008AB5C9 /* UISwitchTests.swift */, C7932E851C4B420A00086F3C /* UITextFieldTests.swift */, 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */, ); @@ -770,6 +776,7 @@ D8F0973E1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, D834572D1AFEE45B0070616A /* Signal.swift in Sources */, D8E4A6211B7BBB2100EAD8A8 /* UIBarItem.swift in Sources */, + 7D2AA99B1CB6EFEB008AB5C9 /* UISwitch.swift in Sources */, D8A454071BD26A1A00C9E790 /* Property.swift in Sources */, D8E4A6201B7BBB1600EAD8A8 /* UIBarButtonItem.swift in Sources */, D8F097451B17F3C8002E15BA /* NSObject.swift in Sources */, @@ -789,6 +796,7 @@ D8F0974B1B17F5E2002E15BA /* NSObjectTests.swift in Sources */, 8289A2E81BD7F7900097FB60 /* UIViewTests.swift in Sources */, D8F073161B863CE70047D546 /* UILabelTests.swift in Sources */, + 7D2AA99D1CB6F275008AB5C9 /* UISwitchTests.swift in Sources */, C7932E871C4B42F500086F3C /* UITextFieldTests.swift in Sources */, 8295FD8A1B87352D007C9000 /* UIButtonTests.swift in Sources */, D8A4540A1BD2772700C9E790 /* PropertyTests.swift in Sources */, diff --git a/Source/UIKit/UISwitch.swift b/Source/UIKit/UISwitch.swift new file mode 100644 index 0000000000..fc434220a4 --- /dev/null +++ b/Source/UIKit/UISwitch.swift @@ -0,0 +1,28 @@ +// +// UISwitch.swift +// Rex +// +// Created by David Rodrigues on 07/04/16. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa +import UIKit + +extension UISwitch { + + /// Wraps a switch's `on` state in a bindable property. + public var rex_on: MutableProperty { + + let property = associatedProperty(self, key: &onKey, initial: { $0.on }, setter: { $0.on = $1 }) + + property <~ rex_controlEvents(.ValueChanged) + .filterMap { $0 as? UISwitch } + .map { $0.on } + + return property + } + +} + +private var onKey: UInt8 = 0 diff --git a/Tests/UIKit/UISwitchTests.swift b/Tests/UIKit/UISwitchTests.swift new file mode 100644 index 0000000000..bc3c1ef1f0 --- /dev/null +++ b/Tests/UIKit/UISwitchTests.swift @@ -0,0 +1,28 @@ +// +// UISwitchTests.swift +// Rex +// +// Created by David Rodrigues on 07/04/16. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import XCTest +import ReactiveCocoa +import Result + +class UISwitchTests: XCTestCase { + + func testOnProperty() { + let s = UISwitch(frame: CGRectZero) + s.on = false + + let (pipeSignal, observer) = Signal.pipe() + s.rex_on <~ SignalProducer(signal: pipeSignal) + + observer.sendNext(true) + XCTAssertTrue(s.on) + observer.sendNext(false) + XCTAssertFalse(s.on) + } + +} From dbca9ff036255c3488288ac2e1630d8a0a918949 Mon Sep 17 00:00:00 2001 From: David Rodrigues Date: Mon, 11 Apr 2016 19:41:52 +0100 Subject: [PATCH 0260/1028] Unified chained `*map` operators into a single `filterMap` --- Source/UIKit/UISwitch.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/UIKit/UISwitch.swift b/Source/UIKit/UISwitch.swift index fc434220a4..89f0b9296e 100644 --- a/Source/UIKit/UISwitch.swift +++ b/Source/UIKit/UISwitch.swift @@ -17,8 +17,7 @@ extension UISwitch { let property = associatedProperty(self, key: &onKey, initial: { $0.on }, setter: { $0.on = $1 }) property <~ rex_controlEvents(.ValueChanged) - .filterMap { $0 as? UISwitch } - .map { $0.on } + .filterMap { ($0 as? UISwitch)?.on } return property } From 6d398a7aec631abc5d508222b4ee69aed495767a Mon Sep 17 00:00:00 2001 From: Rui Peres Date: Thu, 14 Apr 2016 01:41:06 +0100 Subject: [PATCH 0261/1028] Added deallocate trigger signal --- Rex.xcodeproj/project.pbxproj | 10 ++++++++ Source/Foundation/NSObject.swift | 26 +++++++++++++++++++- Source/RACSignal.swift | 36 ++++++++++++++++++++++++++++ Tests/Foundation/NSObjectTests.swift | 16 ++++++++++++- 4 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 Source/RACSignal.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 1e963b7668..a8883d46de 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -19,6 +19,10 @@ 8295FD8D1B87374A007C9000 /* UIBarButtonItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.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 */; }; + C72CF3E61CBF188A00E19897 /* RACSignal.swift in Sources */ = {isa = PBXBuildFile; fileRef = C72CF3E41CBF188A00E19897 /* RACSignal.swift */; }; + C72CF3E71CBF188A00E19897 /* RACSignal.swift in Sources */ = {isa = PBXBuildFile; fileRef = C72CF3E41CBF188A00E19897 /* RACSignal.swift */; }; + C72CF3E81CBF188A00E19897 /* RACSignal.swift in Sources */ = {isa = PBXBuildFile; fileRef = C72CF3E41CBF188A00E19897 /* RACSignal.swift */; }; C7932E831C4B3F3000086F3C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E811C4B3EDB00086F3C /* UITextField.swift */; }; C7932E841C4B41E100086F3C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E811C4B3EDB00086F3C /* UITextField.swift */; }; C7932E871C4B42F500086F3C /* UITextFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E851C4B420A00086F3C /* UITextFieldTests.swift */; }; @@ -188,6 +192,7 @@ 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBarButtonItemTests.swift; sourceTree = ""; }; 9DA915A31CA6301C003723B9 /* UIDatePicker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIDatePicker.swift; sourceTree = ""; }; 9DA915A51CA63046003723B9 /* UIDatePickerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIDatePickerTests.swift; sourceTree = ""; }; + C72CF3E41CBF188A00E19897 /* RACSignal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RACSignal.swift; sourceTree = ""; }; C7932E811C4B3EDB00086F3C /* UITextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextField.swift; sourceTree = ""; }; C7932E851C4B420A00086F3C /* UITextFieldTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextFieldTests.swift; sourceTree = ""; }; C7DCE2B21CB3C872001217D8 /* UITextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextView.swift; sourceTree = ""; }; @@ -319,6 +324,7 @@ D8A454051BD26A1A00C9E790 /* Property.swift */, D8003EBC1AFED01000D7D3C5 /* Signal.swift */, D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */, + C72CF3E41CBF188A00E19897 /* RACSignal.swift */, 4238D5941B4D593E008534C0 /* AppKit */, D8F097391B17F2BF002E15BA /* Foundation */, D86FFBD31B34B0E2001A89B3 /* UIKit */, @@ -746,6 +752,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + C72CF3E51CBF188A00E19897 /* RACSignal.swift in Sources */, D8A454061BD26A1A00C9E790 /* Property.swift in Sources */, D86FFBDA1B34B3F0001A89B3 /* Action.swift in Sources */, D86FFBD11B34AD6F001A89B3 /* Association.swift in Sources */, @@ -783,6 +790,7 @@ D834572D1AFEE45B0070616A /* Signal.swift in Sources */, D8E4A6211B7BBB2100EAD8A8 /* UIBarItem.swift in Sources */, 7D2AA99B1CB6EFEB008AB5C9 /* UISwitch.swift in Sources */, + C72CF3E61CBF188A00E19897 /* RACSignal.swift in Sources */, D8A454071BD26A1A00C9E790 /* Property.swift in Sources */, D8E4A6201B7BBB1600EAD8A8 /* UIBarButtonItem.swift in Sources */, D8F097451B17F3C8002E15BA /* NSObject.swift in Sources */, @@ -825,6 +833,7 @@ 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 */, D8715D9D1C210FF9005F4191 /* Action.swift in Sources */, D8715DA61C21107F005F4191 /* NSUserDefaults.swift in Sources */, @@ -845,6 +854,7 @@ D8715DC01C2112D6005F4191 /* NSObject.swift in Sources */, D8715DC91C211553005F4191 /* UIControl.swift in Sources */, D8715DBC1C2112D1005F4191 /* Signal.swift in Sources */, + C72CF3E71CBF188A00E19897 /* RACSignal.swift in Sources */, D8715DBF1C2112D6005F4191 /* NSData.swift in Sources */, D8715DCC1C211553005F4191 /* UIView.swift in Sources */, D8715DBA1C2112D1005F4191 /* Action.swift in Sources */, diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index 324122c829..a8931b0a2b 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -24,6 +24,30 @@ extension NSObject { // 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 + /// is deallocated. + public final func willDeallocSignal() -> Signal<(), NoError> { + return self + .rac_willDeallocSignal() + .toTriggerSignal() + } +} + +extension SignalProducerType { + /// Forwards events from `self` until `object` is deallocated, + /// at which point the returned producer will complete. + public final func takeUntilObjectDeallocates(object: NSObject) -> SignalProducer { + return self.lift { $0.takeUntilObjectDeallocates(object) } + } } + +extension SignalType { + /// Forwards events from `self` until `object` is deallocated, + /// at which point the returned signal will complete. + public final func takeUntilObjectDeallocates(object: NSObject) -> Signal { + return self.takeUntil(object.willDeallocSignal()) + } +} \ No newline at end of file diff --git a/Source/RACSignal.swift b/Source/RACSignal.swift new file mode 100644 index 0000000000..7b565d1ae9 --- /dev/null +++ b/Source/RACSignal.swift @@ -0,0 +1,36 @@ +// +// RACSignal.swift +// Rex +// +// Created by Rui Peres on 14/04/2016. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import Foundation +import Result +import ReactiveCocoa + +extension RACSignal { + + /// Converts `self` into a `Signal`. + /// + /// Because the operator can't know whether `self` is hot or cold, + /// for certain things, like event streams (see `UIControl.signalForControlEvents`) + /// use this method to be able to expose these inherently hot streams + /// as `Signal`s. + public func toSignalAssumingHot() -> Signal { + return Signal { observer in + return self.toSignalProducer().start(observer) + } + } + + /// Converts `self` into a `Signal`, that can be used + /// with the `takeUntil` operator, or as an "activation" signal. + /// (e.g. a button) + public final func toTriggerSignal() -> Signal<(), NoError> { + return self + .toSignalAssumingHot() + .map { _ in () } + .ignoreError() + } +} \ No newline at end of file diff --git a/Tests/Foundation/NSObjectTests.swift b/Tests/Foundation/NSObjectTests.swift index 45ec54e86e..8b5728da72 100644 --- a/Tests/Foundation/NSObjectTests.swift +++ b/Tests/Foundation/NSObjectTests.swift @@ -11,7 +11,7 @@ import ReactiveCocoa import XCTest final class NSObjectTests: XCTestCase { - + func testProducerForKeyPath() { let object = Object() var value: String = "" @@ -22,6 +22,20 @@ final class NSObjectTests: XCTestCase { object.string = "bar" XCTAssertEqual(value, "bar") } + + func testObjectsWillBeDeallocatedSignal() { + + let expectation = self.expectationWithDescription("Expected timer to send `completed` event when object deallocates") + defer { self.waitForExpectationsWithTimeout(2, handler: nil) } + + let object = Object() + + timer(1, onScheduler: QueueScheduler(name: "test.queue")) + .takeUntilObjectDeallocates(object) + .startWithCompleted { + expectation.fulfill() + } + } } final class NSObjectDeallocTests: XCTestCase { From 0e2cdded87b7e662daed9cd01c8b7aca8c0ea9a3 Mon Sep 17 00:00:00 2001 From: Rui Peres Date: Thu, 14 Apr 2016 01:47:27 +0100 Subject: [PATCH 0262/1028] Adding in unused warnings --- Source/Foundation/NSObject.swift | 3 +++ Source/RACSignal.swift | 2 ++ 2 files changed, 5 insertions(+) diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index a8931b0a2b..b55849c32e 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -29,6 +29,7 @@ extension NSObject { /// Creates a signal that will be triggered when the object /// is deallocated. + @warn_unused_result(message="Did you forget to call `observe` on the signal?") public final func willDeallocSignal() -> Signal<(), NoError> { return self .rac_willDeallocSignal() @@ -39,6 +40,7 @@ extension NSObject { extension SignalProducerType { /// Forwards events from `self` until `object` is deallocated, /// at which point the returned producer will complete. + @warn_unused_result(message="Did you forget to call `start` on the producer?") public final func takeUntilObjectDeallocates(object: NSObject) -> SignalProducer { return self.lift { $0.takeUntilObjectDeallocates(object) } } @@ -47,6 +49,7 @@ extension SignalProducerType { extension SignalType { /// Forwards events from `self` until `object` is deallocated, /// at which point the returned signal will complete. + @warn_unused_result(message="Did you forget to call `observe` on the signal?") public final func takeUntilObjectDeallocates(object: NSObject) -> Signal { return self.takeUntil(object.willDeallocSignal()) } diff --git a/Source/RACSignal.swift b/Source/RACSignal.swift index 7b565d1ae9..42172e777e 100644 --- a/Source/RACSignal.swift +++ b/Source/RACSignal.swift @@ -18,6 +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 toSignalAssumingHot() -> Signal { return Signal { observer in return self.toSignalProducer().start(observer) @@ -27,6 +28,7 @@ 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 toTriggerSignal() -> Signal<(), NoError> { return self .toSignalAssumingHot() From 87cbcffd7f43f146145cc7cfe5bc9c3d452688cf Mon Sep 17 00:00:00 2001 From: Rui Peres Date: Fri, 15 Apr 2016 10:50:15 +0100 Subject: [PATCH 0263/1028] Code review --- Source/Foundation/NSObject.swift | 21 +-------------------- Source/RACSignal.swift | 4 ++-- Tests/Foundation/NSObjectTests.swift | 2 +- 3 files changed, 4 insertions(+), 23 deletions(-) diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index b55849c32e..0defe0c747 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -29,28 +29,9 @@ extension NSObject { /// Creates a signal that will be triggered when the object /// is deallocated. - @warn_unused_result(message="Did you forget to call `observe` on the signal?") - public final func willDeallocSignal() -> Signal<(), NoError> { + public var willDeallocSignal: Signal<(), NoError> { return self .rac_willDeallocSignal() .toTriggerSignal() } -} - -extension SignalProducerType { - /// Forwards events from `self` until `object` is deallocated, - /// at which point the returned producer will complete. - @warn_unused_result(message="Did you forget to call `start` on the producer?") - public final func takeUntilObjectDeallocates(object: NSObject) -> SignalProducer { - return self.lift { $0.takeUntilObjectDeallocates(object) } - } -} - -extension SignalType { - /// Forwards events from `self` until `object` is deallocated, - /// at which point the returned signal will complete. - @warn_unused_result(message="Did you forget to call `observe` on the signal?") - public final func takeUntilObjectDeallocates(object: NSObject) -> Signal { - return self.takeUntil(object.willDeallocSignal()) - } } \ No newline at end of file diff --git a/Source/RACSignal.swift b/Source/RACSignal.swift index 42172e777e..a1ac297ea9 100644 --- a/Source/RACSignal.swift +++ b/Source/RACSignal.swift @@ -19,7 +19,7 @@ extension RACSignal { /// 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 toSignalAssumingHot() -> Signal { + public func toSignal() -> Signal { return Signal { observer in return self.toSignalProducer().start(observer) } @@ -31,7 +31,7 @@ extension RACSignal { @warn_unused_result(message="Did you forget to call `observe` on the signal?") public final func toTriggerSignal() -> Signal<(), NoError> { return self - .toSignalAssumingHot() + .toSignal() .map { _ in () } .ignoreError() } diff --git a/Tests/Foundation/NSObjectTests.swift b/Tests/Foundation/NSObjectTests.swift index 8b5728da72..40f4d637d6 100644 --- a/Tests/Foundation/NSObjectTests.swift +++ b/Tests/Foundation/NSObjectTests.swift @@ -31,7 +31,7 @@ final class NSObjectTests: XCTestCase { let object = Object() timer(1, onScheduler: QueueScheduler(name: "test.queue")) - .takeUntilObjectDeallocates(object) + .takeUntil(object.willDeallocSignal) .startWithCompleted { expectation.fulfill() } From c501edadbab03a42edfcea21effa0b6a554eaf36 Mon Sep 17 00:00:00 2001 From: Rui Peres Date: Fri, 15 Apr 2016 10:53:34 +0100 Subject: [PATCH 0264/1028] To be consistent with the nomenclature. --- Source/Foundation/NSObject.swift | 4 ++-- Source/RACSignal.swift | 6 +++--- Tests/Foundation/NSObjectTests.swift | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index 0defe0c747..dda40bbb40 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -29,9 +29,9 @@ extension NSObject { /// Creates a signal that will be triggered when the object /// is deallocated. - public var willDeallocSignal: Signal<(), NoError> { + public var rex_willDeallocSignal: Signal<(), NoError> { return self .rac_willDeallocSignal() - .toTriggerSignal() + .rex_toTriggerSignal() } } \ No newline at end of file diff --git a/Source/RACSignal.swift b/Source/RACSignal.swift index a1ac297ea9..57f86b8e1c 100644 --- a/Source/RACSignal.swift +++ b/Source/RACSignal.swift @@ -19,7 +19,7 @@ extension RACSignal { /// 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 toSignal() -> Signal { + public func rex_toSignal() -> Signal { return Signal { observer in return self.toSignalProducer().start(observer) } @@ -29,9 +29,9 @@ extension RACSignal { /// 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 toTriggerSignal() -> Signal<(), NoError> { + public final func rex_toTriggerSignal() -> Signal<(), NoError> { return self - .toSignal() + .rex_toSignal() .map { _ in () } .ignoreError() } diff --git a/Tests/Foundation/NSObjectTests.swift b/Tests/Foundation/NSObjectTests.swift index 40f4d637d6..9a5b2cfc06 100644 --- a/Tests/Foundation/NSObjectTests.swift +++ b/Tests/Foundation/NSObjectTests.swift @@ -31,7 +31,7 @@ final class NSObjectTests: XCTestCase { let object = Object() timer(1, onScheduler: QueueScheduler(name: "test.queue")) - .takeUntil(object.willDeallocSignal) + .takeUntil(object.rex_willDeallocSignal) .startWithCompleted { expectation.fulfill() } From 304f585e2d3d4c4a1dee2e08c05ad64781ebaa4d Mon Sep 17 00:00:00 2001 From: Rui Peres Date: Sat, 16 Apr 2016 04:08:59 +0100 Subject: [PATCH 0265/1028] VC's did/will disappear --- Rex.xcodeproj/project.pbxproj | 8 ++++ Source/UIKit/UIViewController.swift | 33 ++++++++++++++++ Tests/UIKit/UIViewControllerTests.swift | 52 +++++++++++++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 Source/UIKit/UIViewController.swift create mode 100644 Tests/UIKit/UIViewControllerTests.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index a8883d46de..303d1e5091 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -26,6 +26,8 @@ C7932E831C4B3F3000086F3C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E811C4B3EDB00086F3C /* UITextField.swift */; }; C7932E841C4B41E100086F3C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E811C4B3EDB00086F3C /* UITextField.swift */; }; C7932E871C4B42F500086F3C /* UITextFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E851C4B420A00086F3C /* UITextFieldTests.swift */; }; + C7945F111CC192E800DC9E37 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7945F101CC192E800DC9E37 /* UIViewController.swift */; }; + C7945F141CC1DFBE00DC9E37 /* UIViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7945F121CC1DFB400DC9E37 /* UIViewControllerTests.swift */; }; C7DCE2B41CB3C89A001217D8 /* UITextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DCE2B21CB3C872001217D8 /* UITextView.swift */; }; C7DCE2B71CB3C9D6001217D8 /* UITextViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DCE2B51CB3C9C1001217D8 /* UITextViewTests.swift */; }; D8003E941AFEC3D400D7D3C5 /* Rex.h in Headers */ = {isa = PBXBuildFile; fileRef = D8003E931AFEC3D400D7D3C5 /* Rex.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -195,6 +197,8 @@ C72CF3E41CBF188A00E19897 /* RACSignal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RACSignal.swift; sourceTree = ""; }; C7932E811C4B3EDB00086F3C /* UITextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextField.swift; sourceTree = ""; }; C7932E851C4B420A00086F3C /* UITextFieldTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextFieldTests.swift; sourceTree = ""; }; + C7945F101CC192E800DC9E37 /* UIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = ""; }; + C7945F121CC1DFB400DC9E37 /* UIViewControllerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewControllerTests.swift; sourceTree = ""; }; C7DCE2B21CB3C872001217D8 /* UITextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextView.swift; sourceTree = ""; }; C7DCE2B51CB3C9C1001217D8 /* UITextViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextViewTests.swift; sourceTree = ""; }; D8003E921AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -411,6 +415,7 @@ C7932E811C4B3EDB00086F3C /* UITextField.swift */, C7DCE2B21CB3C872001217D8 /* UITextView.swift */, 8289A2E41BD7F6DD0097FB60 /* UIView.swift */, + C7945F101CC192E800DC9E37 /* UIViewController.swift */, ); path = UIKit; sourceTree = ""; @@ -449,6 +454,7 @@ C7932E851C4B420A00086F3C /* UITextFieldTests.swift */, C7DCE2B51CB3C9C1001217D8 /* UITextViewTests.swift */, 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */, + C7945F121CC1DFB400DC9E37 /* UIViewControllerTests.swift */, ); path = UIKit; sourceTree = ""; @@ -794,6 +800,7 @@ D8A454071BD26A1A00C9E790 /* Property.swift in Sources */, D8E4A6201B7BBB1600EAD8A8 /* UIBarButtonItem.swift in Sources */, D8F097451B17F3C8002E15BA /* NSObject.swift in Sources */, + C7945F111CC192E800DC9E37 /* UIViewController.swift in Sources */, D834572E1AFEE45B0070616A /* SignalProducer.swift in Sources */, C7DCE2B41CB3C89A001217D8 /* UITextView.swift in Sources */, 8289A2E51BD7F6DD0097FB60 /* UIView.swift in Sources */, @@ -815,6 +822,7 @@ C7932E871C4B42F500086F3C /* UITextFieldTests.swift in Sources */, 8295FD8A1B87352D007C9000 /* UIButtonTests.swift in Sources */, D8A4540A1BD2772700C9E790 /* PropertyTests.swift in Sources */, + C7945F141CC1DFBE00DC9E37 /* UIViewControllerTests.swift in Sources */, 9DA915A61CA63046003723B9 /* UIDatePickerTests.swift in Sources */, D83457301AFEE45E0070616A /* SignalProducerTests.swift in Sources */, D83457411AFEE6050070616A /* SignalTests.swift in Sources */, diff --git a/Source/UIKit/UIViewController.swift b/Source/UIKit/UIViewController.swift new file mode 100644 index 0000000000..457136910c --- /dev/null +++ b/Source/UIKit/UIViewController.swift @@ -0,0 +1,33 @@ +// +// UIViewController.swift +// Rex +// +// Created by Rui Peres on 14/04/2016. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import Result +import ReactiveCocoa +import UIKit + +extension UIViewController { + /// Returns a `Signal`, that will be triggered + /// when `self`'s `viewDidDisappear` is called + public var rex_viewDidDisappearSignal: Signal<(), NoError> { + return triggerForSelector(#selector(UIViewController.viewDidDisappear(_:))) + } + + /// Returns a `Signal`, that will be triggered + /// when `self`'s `viewWillDisappear` is called + public var rex_viewWillDisappearSignal: Signal<(), NoError> { + return triggerForSelector(#selector(UIViewController.viewWillDisappear(_:))) + } + + private func triggerForSelector(selector: Selector) -> Signal<(), NoError> { + return self + .rac_signalForSelector(selector) + .rex_toSignal() + .map { _ in () } + .ignoreError() + } +} \ No newline at end of file diff --git a/Tests/UIKit/UIViewControllerTests.swift b/Tests/UIKit/UIViewControllerTests.swift new file mode 100644 index 0000000000..72a20fddde --- /dev/null +++ b/Tests/UIKit/UIViewControllerTests.swift @@ -0,0 +1,52 @@ +// +// UIViewControllerTests.swift +// Rex +// +// Created by Rui Peres on 16/04/2016. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa +import UIKit +import XCTest +import enum Result.NoError + +class UIViewControllerTests: XCTestCase { + + weak var _viewController: UIViewController? + + override func tearDown() { + XCTAssert(_viewController == nil, "Retain cycle detected in UIViewController properties") + super.tearDown() + } + + func testViewDidDisappear() { + + let expectation = self.expectationWithDescription("Expected rex_viewDidDisappearSignal to be triggered") + defer { self.waitForExpectationsWithTimeout(2, handler: nil) } + + let viewController = UIViewController() + _viewController = viewController + + viewController.rex_viewDidDisappearSignal.observeNext { + expectation.fulfill() + } + + viewController.viewDidDisappear(true) + } + + func testViewWillDisappear() { + + let expectation = self.expectationWithDescription("Expected rex_viewWillDisappearSignal to be triggered") + defer { self.waitForExpectationsWithTimeout(2, handler: nil) } + + let viewController = UIViewController() + _viewController = viewController + + viewController.rex_viewWillDisappearSignal.observeNext { + expectation.fulfill() + } + + viewController.viewWillDisappear(true) + } +} \ No newline at end of file From fd58ac255ddf3ef70d318e53d9bb93d63bddf5ab Mon Sep 17 00:00:00 2001 From: Rui Peres Date: Sat, 16 Apr 2016 17:39:55 +0100 Subject: [PATCH 0266/1028] Use `rex_toTriggerSignal` instead --- Source/UIKit/UIViewController.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Source/UIKit/UIViewController.swift b/Source/UIKit/UIViewController.swift index 457136910c..02cccf43f1 100644 --- a/Source/UIKit/UIViewController.swift +++ b/Source/UIKit/UIViewController.swift @@ -26,8 +26,6 @@ extension UIViewController { private func triggerForSelector(selector: Selector) -> Signal<(), NoError> { return self .rac_signalForSelector(selector) - .rex_toSignal() - .map { _ in () } - .ignoreError() + .rex_toTriggerSignal() } } \ No newline at end of file From 58f38759b333eee827e98a8b2c0923630938cdbe Mon Sep 17 00:00:00 2001 From: Rui Peres Date: Sat, 16 Apr 2016 17:40:27 +0100 Subject: [PATCH 0267/1028] Added the other pair viewDidAppear/viewWillAppear --- Source/UIKit/UIViewController.swift | 12 ++++++++++ Tests/UIKit/UIViewControllerTests.swift | 30 +++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/Source/UIKit/UIViewController.swift b/Source/UIKit/UIViewController.swift index 02cccf43f1..844fdee266 100644 --- a/Source/UIKit/UIViewController.swift +++ b/Source/UIKit/UIViewController.swift @@ -23,6 +23,18 @@ extension UIViewController { return triggerForSelector(#selector(UIViewController.viewWillDisappear(_:))) } + /// Returns a `Signal`, that will be triggered + /// when `self`'s `viewDidAppear` is called + public var rex_viewDidAppearSignal: Signal<(), NoError> { + return triggerForSelector(#selector(UIViewController.viewDidAppear(_:))) + } + + /// Returns a `Signal`, that will be triggered + /// when `self`'s `viewWillAppear` is called + public var rex_viewWillAppearSignal: Signal<(), NoError> { + return triggerForSelector(#selector(UIViewController.viewWillAppear(_:))) + } + private func triggerForSelector(selector: Selector) -> Signal<(), NoError> { return self .rac_signalForSelector(selector) diff --git a/Tests/UIKit/UIViewControllerTests.swift b/Tests/UIKit/UIViewControllerTests.swift index 72a20fddde..271d27fed9 100644 --- a/Tests/UIKit/UIViewControllerTests.swift +++ b/Tests/UIKit/UIViewControllerTests.swift @@ -49,4 +49,34 @@ class UIViewControllerTests: XCTestCase { viewController.viewWillDisappear(true) } + + func testViewDidAppear() { + + let expectation = self.expectationWithDescription("Expected rex_viewDidAppearSignal to be triggered") + defer { self.waitForExpectationsWithTimeout(2, handler: nil) } + + let viewController = UIViewController() + _viewController = viewController + + viewController.rex_viewDidAppearSignal.observeNext { + expectation.fulfill() + } + + viewController.viewDidAppear(true) + } + + func testViewWillAppear() { + + let expectation = self.expectationWithDescription("Expected rex_viewWillAppearSignal to be triggered") + defer { self.waitForExpectationsWithTimeout(2, handler: nil) } + + let viewController = UIViewController() + _viewController = viewController + + viewController.rex_viewWillAppearSignal.observeNext { + expectation.fulfill() + } + + viewController.viewWillAppear(true) + } } \ No newline at end of file From 88becfa84b21c454155397af304569ffb9fdc254 Mon Sep 17 00:00:00 2001 From: Rui Peres Date: Sat, 16 Apr 2016 21:32:06 +0100 Subject: [PATCH 0268/1028] DismissModally Property --- Rex.xcodeproj/project.pbxproj | 8 ++++++ Source/UIKit/UIViewController.swift | 32 +++++++++++++++++++++ Tests/UIKit/UIViewControllerTests.swift | 38 +++++++++++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 Source/UIKit/UIViewController.swift create mode 100644 Tests/UIKit/UIViewControllerTests.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index a8883d46de..2663e3cfc3 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -26,6 +26,8 @@ C7932E831C4B3F3000086F3C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E811C4B3EDB00086F3C /* UITextField.swift */; }; C7932E841C4B41E100086F3C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E811C4B3EDB00086F3C /* UITextField.swift */; }; C7932E871C4B42F500086F3C /* UITextFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E851C4B420A00086F3C /* UITextFieldTests.swift */; }; + C7945F161CC2D83100DC9E37 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7945F151CC2D83100DC9E37 /* UIViewController.swift */; }; + C7945F191CC2D88800DC9E37 /* UIViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7945F171CC2D87300DC9E37 /* UIViewControllerTests.swift */; }; C7DCE2B41CB3C89A001217D8 /* UITextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DCE2B21CB3C872001217D8 /* UITextView.swift */; }; C7DCE2B71CB3C9D6001217D8 /* UITextViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DCE2B51CB3C9C1001217D8 /* UITextViewTests.swift */; }; D8003E941AFEC3D400D7D3C5 /* Rex.h in Headers */ = {isa = PBXBuildFile; fileRef = D8003E931AFEC3D400D7D3C5 /* Rex.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -195,6 +197,8 @@ C72CF3E41CBF188A00E19897 /* RACSignal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RACSignal.swift; sourceTree = ""; }; C7932E811C4B3EDB00086F3C /* UITextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextField.swift; sourceTree = ""; }; C7932E851C4B420A00086F3C /* UITextFieldTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextFieldTests.swift; sourceTree = ""; }; + C7945F151CC2D83100DC9E37 /* UIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = ""; }; + C7945F171CC2D87300DC9E37 /* UIViewControllerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewControllerTests.swift; sourceTree = ""; }; C7DCE2B21CB3C872001217D8 /* UITextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextView.swift; sourceTree = ""; }; C7DCE2B51CB3C9C1001217D8 /* UITextViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextViewTests.swift; sourceTree = ""; }; D8003E921AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -411,6 +415,7 @@ C7932E811C4B3EDB00086F3C /* UITextField.swift */, C7DCE2B21CB3C872001217D8 /* UITextView.swift */, 8289A2E41BD7F6DD0097FB60 /* UIView.swift */, + C7945F151CC2D83100DC9E37 /* UIViewController.swift */, ); path = UIKit; sourceTree = ""; @@ -449,6 +454,7 @@ C7932E851C4B420A00086F3C /* UITextFieldTests.swift */, C7DCE2B51CB3C9C1001217D8 /* UITextViewTests.swift */, 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */, + C7945F171CC2D87300DC9E37 /* UIViewControllerTests.swift */, ); path = UIKit; sourceTree = ""; @@ -790,6 +796,7 @@ D834572D1AFEE45B0070616A /* Signal.swift in Sources */, D8E4A6211B7BBB2100EAD8A8 /* UIBarItem.swift in Sources */, 7D2AA99B1CB6EFEB008AB5C9 /* UISwitch.swift in Sources */, + C7945F161CC2D83100DC9E37 /* UIViewController.swift in Sources */, C72CF3E61CBF188A00E19897 /* RACSignal.swift in Sources */, D8A454071BD26A1A00C9E790 /* Property.swift in Sources */, D8E4A6201B7BBB1600EAD8A8 /* UIBarButtonItem.swift in Sources */, @@ -815,6 +822,7 @@ C7932E871C4B42F500086F3C /* UITextFieldTests.swift in Sources */, 8295FD8A1B87352D007C9000 /* UIButtonTests.swift in Sources */, D8A4540A1BD2772700C9E790 /* PropertyTests.swift in Sources */, + C7945F191CC2D88800DC9E37 /* UIViewControllerTests.swift in Sources */, 9DA915A61CA63046003723B9 /* UIDatePickerTests.swift in Sources */, D83457301AFEE45E0070616A /* SignalProducerTests.swift in Sources */, D83457411AFEE6050070616A /* SignalTests.swift in Sources */, diff --git a/Source/UIKit/UIViewController.swift b/Source/UIKit/UIViewController.swift new file mode 100644 index 0000000000..334bfc4c07 --- /dev/null +++ b/Source/UIKit/UIViewController.swift @@ -0,0 +1,32 @@ +// +// UIViewController.swift +// Rex +// +// Created by Rui Peres on 14/04/2016. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import Result +import ReactiveCocoa +import UIKit + +extension UIViewController { + + public typealias Completion = (Void -> Void)? + public typealias DismissingInformation = (Bool, Completion)? + public var rex_dismissModally: MutableProperty { + + let initial: UIViewController -> DismissingInformation = { _ in nil } + let setter: (UIViewController, DismissingInformation) -> Void = { host, dismissingInfo in + + guard let unwrapped = dismissingInfo else { return } + host.dismissViewControllerAnimated(unwrapped.0, completion: unwrapped.1) + } + + let property = associatedProperty(self, key: &dismissModally, initial: initial, setter: setter) + + return property + } +} + +private var dismissModally: UInt8 = 0 \ No newline at end of file diff --git a/Tests/UIKit/UIViewControllerTests.swift b/Tests/UIKit/UIViewControllerTests.swift new file mode 100644 index 0000000000..038ad7c3ff --- /dev/null +++ b/Tests/UIKit/UIViewControllerTests.swift @@ -0,0 +1,38 @@ +// +// UIViewControllerTests.swift +// Rex +// +// Created by Rui Peres on 16/04/2016. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa +import UIKit +import XCTest +import enum Result.NoError + +class UIViewControllerTests: XCTestCase { + + weak var _viewController: UIViewController? + + override func tearDown() { + XCTAssert(_viewController == nil, "Retain cycle detected in UIViewController properties") + super.tearDown() + } + + func testDismissViewController() { + + let expectation = self.expectationWithDescription("Expected rex_dismissModally to be triggered") + defer { self.waitForExpectationsWithTimeout(2, handler: nil) } + + let viewController = UIViewController() + _viewController = viewController + + viewController.rex_dismissModally.signal.observeNext { _ in + expectation.fulfill() + } + + viewController.rex_dismissModally <~ SignalProducer(value: (true, nil)) + } + +} \ No newline at end of file From c015b963022be178a993504aa36603b998c2c207 Mon Sep 17 00:00:00 2001 From: Rui Peres Date: Mon, 18 Apr 2016 10:29:43 +0100 Subject: [PATCH 0269/1028] Fix the problem with dismiss via Cocoa API --- Source/UIKit/UIViewController.swift | 6 ++++++ Tests/UIKit/UIViewControllerTests.swift | 16 +++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Source/UIKit/UIViewController.swift b/Source/UIKit/UIViewController.swift index 334bfc4c07..f40f3f1531 100644 --- a/Source/UIKit/UIViewController.swift +++ b/Source/UIKit/UIViewController.swift @@ -24,7 +24,13 @@ extension UIViewController { } let property = associatedProperty(self, key: &dismissModally, initial: initial, setter: setter) + + property <~ rac_signalForSelector(#selector(UIViewController.dismissViewControllerAnimated(_:completion:))) + .takeUntilBlock { _ in property.value != nil } + .rex_toTriggerSignal() + .map { _ in return nil } + return property } } diff --git a/Tests/UIKit/UIViewControllerTests.swift b/Tests/UIKit/UIViewControllerTests.swift index 038ad7c3ff..8846db0ccb 100644 --- a/Tests/UIKit/UIViewControllerTests.swift +++ b/Tests/UIKit/UIViewControllerTests.swift @@ -20,7 +20,7 @@ class UIViewControllerTests: XCTestCase { super.tearDown() } - func testDismissViewController() { + func testDismissViewController_via_property() { let expectation = self.expectationWithDescription("Expected rex_dismissModally to be triggered") defer { self.waitForExpectationsWithTimeout(2, handler: nil) } @@ -35,4 +35,18 @@ class UIViewControllerTests: XCTestCase { viewController.rex_dismissModally <~ SignalProducer(value: (true, nil)) } + func testDismissViewController_via_cocoaDismiss() { + + let expectation = self.expectationWithDescription("Expected rex_dismissModally to be triggered") + defer { self.waitForExpectationsWithTimeout(2, handler: nil) } + + let viewController = UIViewController() + _viewController = viewController + + viewController.rex_dismissModally.signal.observeNext { _ in + expectation.fulfill() + } + + viewController.dismissViewControllerAnimated(true, completion: nil) + } } \ No newline at end of file From 63bb581bd6b13fdf94de4bb47d74a092139ddb1e Mon Sep 17 00:00:00 2001 From: Rui Peres Date: Mon, 18 Apr 2016 15:50:01 +0100 Subject: [PATCH 0270/1028] Code review (func name + tuple named parameters) --- Source/UIKit/UIViewController.swift | 19 +++++++++++++++---- Tests/UIKit/UIViewControllerTests.swift | 6 +++--- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/Source/UIKit/UIViewController.swift b/Source/UIKit/UIViewController.swift index f40f3f1531..0af4dfb94f 100644 --- a/Source/UIKit/UIViewController.swift +++ b/Source/UIKit/UIViewController.swift @@ -12,15 +12,26 @@ import UIKit extension UIViewController { - public typealias Completion = (Void -> Void)? - public typealias DismissingInformation = (Bool, Completion)? - public var rex_dismissModally: MutableProperty { + 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`. + /// E.g: + /// ``` + /// //Dismissed with animation (`true`) and `nil` completion + /// viewController.rex_dismissAnimated <~ aProducer.map { _ in (true, nil) } + /// ``` + /// The dismissal observation can be made either with binding (example above) + /// or `viewController.dismissViewControllerAnimated(true, completion: nil)` + public var rex_dismissAnimated: MutableProperty { let initial: UIViewController -> DismissingInformation = { _ in nil } let setter: (UIViewController, DismissingInformation) -> Void = { host, dismissingInfo in guard let unwrapped = dismissingInfo else { return } - host.dismissViewControllerAnimated(unwrapped.0, completion: unwrapped.1) + host.dismissViewControllerAnimated(unwrapped.animated, completion: unwrapped.completion) } let property = associatedProperty(self, key: &dismissModally, initial: initial, setter: setter) diff --git a/Tests/UIKit/UIViewControllerTests.swift b/Tests/UIKit/UIViewControllerTests.swift index 8846db0ccb..3df480606e 100644 --- a/Tests/UIKit/UIViewControllerTests.swift +++ b/Tests/UIKit/UIViewControllerTests.swift @@ -28,11 +28,11 @@ class UIViewControllerTests: XCTestCase { let viewController = UIViewController() _viewController = viewController - viewController.rex_dismissModally.signal.observeNext { _ in + viewController.rex_dismissAnimated.signal.observeNext { _ in expectation.fulfill() } - viewController.rex_dismissModally <~ SignalProducer(value: (true, nil)) + viewController.rex_dismissAnimated <~ SignalProducer(value: (true, nil)) } func testDismissViewController_via_cocoaDismiss() { @@ -43,7 +43,7 @@ class UIViewControllerTests: XCTestCase { let viewController = UIViewController() _viewController = viewController - viewController.rex_dismissModally.signal.observeNext { _ in + viewController.rex_dismissAnimated.signal.observeNext { _ in expectation.fulfill() } From 307296d6f36c2fcb419f176f7826aebfde29debd Mon Sep 17 00:00:00 2001 From: Rui Peres Date: Mon, 18 Apr 2016 17:19:40 +0100 Subject: [PATCH 0271/1028] Added the parameter labels. --- Tests/UIKit/UIViewControllerTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/UIKit/UIViewControllerTests.swift b/Tests/UIKit/UIViewControllerTests.swift index 3df480606e..94f29ed402 100644 --- a/Tests/UIKit/UIViewControllerTests.swift +++ b/Tests/UIKit/UIViewControllerTests.swift @@ -32,7 +32,7 @@ class UIViewControllerTests: XCTestCase { expectation.fulfill() } - viewController.rex_dismissAnimated <~ SignalProducer(value: (true, nil)) + viewController.rex_dismissAnimated <~ SignalProducer(value: (animated: true, completion: nil)) } func testDismissViewController_via_cocoaDismiss() { From 463ddecf1829d4cb0ba84a787493d0097a2b3458 Mon Sep 17 00:00:00 2001 From: Rui Peres Date: Wed, 20 Apr 2016 00:37:04 +0100 Subject: [PATCH 0272/1028] To be aligned with the RAC repo --- Source/Action.swift | 3 +-- Source/AppKit/NSTextField.swift | 3 +-- Source/Foundation/Association.swift | 6 ++++-- Source/Foundation/NSData.swift | 4 ++-- Source/Foundation/NSObject.swift | 2 +- Source/Foundation/NSUserDefaults.swift | 4 ++-- Source/Property.swift | 12 +++++++++++- Source/RACSignal.swift | 3 +-- Source/Signal.swift | 6 ++++++ Source/SignalProducer.swift | 12 ++++++++++-- Source/UIKit/UIBarButtonItem.swift | 2 +- Source/UIKit/UIBarItem.swift | 2 +- Source/UIKit/UIButton.swift | 2 +- Source/UIKit/UIControl.swift | 3 ++- Source/UIKit/UIImageView.swift | 2 +- Source/UIKit/UILabel.swift | 2 +- Source/UIKit/UISwitch.swift | 2 +- Source/UIKit/UITextField.swift | 3 +-- Source/UIKit/UITextView.swift | 2 +- Source/UIKit/UIView.swift | 2 +- Tests/Foundation/NSObjectTests.swift | 3 +-- Tests/PropertyTests.swift | 2 +- Tests/SignalProducerTests.swift | 2 +- Tests/SignalTests.swift | 2 +- Tests/UIKit/UIBarButtonItemTests.swift | 3 +-- Tests/UIKit/UIButtonTests.swift | 5 +---- Tests/UIKit/UIControlTests.swift | 2 +- Tests/UIKit/UIDatePickerTests.swift | 3 +-- Tests/UIKit/UIImageViewTests.swift | 2 +- Tests/UIKit/UILabelTests.swift | 2 +- Tests/UIKit/UISwitchTests.swift | 3 +-- Tests/UIKit/UITextFieldTests.swift | 2 +- Tests/UIKit/UITextViewTests.swift | 2 +- 33 files changed, 63 insertions(+), 47 deletions(-) diff --git a/Source/Action.swift b/Source/Action.swift index 9b325cc52c..34219e35c6 100644 --- a/Source/Action.swift +++ b/Source/Action.swift @@ -32,5 +32,4 @@ extension CocoaAction { public var rex_executingProducer: SignalProducer { return rex_producerForKeyPath("executing") } -} - +} \ No newline at end of file diff --git a/Source/AppKit/NSTextField.swift b/Source/AppKit/NSTextField.swift index c53ac10c16..2e665fe21f 100644 --- a/Source/AppKit/NSTextField.swift +++ b/Source/AppKit/NSTextField.swift @@ -6,7 +6,6 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // -import Foundation import ReactiveCocoa import AppKit import enum Result.NoError @@ -20,4 +19,4 @@ extension NSTextField { (notification.object as! NSTextField).stringValue } } -} +} \ No newline at end of file diff --git a/Source/Foundation/Association.swift b/Source/Foundation/Association.swift index a42ec39bd0..b8e8c0435d 100644 --- a/Source/Foundation/Association.swift +++ b/Source/Foundation/Association.swift @@ -6,7 +6,6 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // -import Foundation import ReactiveCocoa /// Attaches a `MutableProperty` value to the `host` object using KVC to get the initial @@ -16,6 +15,7 @@ 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 ?? "" @@ -33,6 +33,7 @@ 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 { let setter: (AnyObject, T) -> () = { host, newValue in host.setValue(newValue, forKeyPath: keyPath.stringValue) @@ -48,6 +49,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) -> ()) -> MutableProperty { return associatedObject(host, key: key) { host in let property = MutableProperty(initial(host)) @@ -72,4 +74,4 @@ public func associatedObject(host: Host, key: Uns objc_setAssociatedObject(host, key, value, .OBJC_ASSOCIATION_RETAIN) } return value! -} +} \ No newline at end of file diff --git a/Source/Foundation/NSData.swift b/Source/Foundation/NSData.swift index 652c419fbe..d1d0d101d8 100644 --- a/Source/Foundation/NSData.swift +++ b/Source/Foundation/NSData.swift @@ -6,11 +6,11 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // -import Foundation import ReactiveCocoa extension NSData { /// Read the data at the URL, sending the result or an error. + @warn_unused_result(message="Did you forget to call `start` on the producer?") public class func rex_dataWithContentsOfURL(url: NSURL, options: NSDataReadingOptions = NSDataReadingOptions()) -> SignalProducer { return SignalProducer { observer, disposable in do { @@ -22,4 +22,4 @@ extension NSData { } } } -} +} \ No newline at end of file diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index dda40bbb40..1327bcc44e 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -6,7 +6,6 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // -import Foundation import ReactiveCocoa import enum Result.NoError @@ -16,6 +15,7 @@ extension NSObject { /// /// Swift classes deriving `NSObject` must declare properties as `dynamic` for /// them to work with KVO. However, this is not recommended practice. + @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) .toSignalProducer() diff --git a/Source/Foundation/NSUserDefaults.swift b/Source/Foundation/NSUserDefaults.swift index b8be9572ac..053505b799 100644 --- a/Source/Foundation/NSUserDefaults.swift +++ b/Source/Foundation/NSUserDefaults.swift @@ -6,7 +6,6 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // -import Foundation import ReactiveCocoa import enum Result.NoError @@ -15,6 +14,7 @@ extension NSUserDefaults { /// 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) @@ -35,4 +35,4 @@ extension NSUserDefaults { return false } } -} +} \ No newline at end of file diff --git a/Source/Property.swift b/Source/Property.swift index 7703b93ae9..6287b39c03 100644 --- a/Source/Property.swift +++ b/Source/Property.swift @@ -11,26 +11,31 @@ import enum Result.NoError extension PropertyType 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)]) } /// 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]) } /// 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)]) } /// 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]) } /// 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) } @@ -59,11 +64,13 @@ public struct AndProperty: PropertyType { } /// 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)]) } /// 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 { return AndProperty(terms: terms + [other]) } @@ -96,11 +103,13 @@ public struct OrProperty: PropertyType { } /// 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)]) } /// 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 { return OrProperty(terms: terms + [other]) } @@ -128,6 +137,7 @@ 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) } @@ -136,4 +146,4 @@ public struct NotProperty: PropertyType { self.source = source self.invert = invert } -} +} \ No newline at end of file diff --git a/Source/RACSignal.swift b/Source/RACSignal.swift index 57f86b8e1c..0e133fd8f8 100644 --- a/Source/RACSignal.swift +++ b/Source/RACSignal.swift @@ -6,9 +6,8 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // -import Foundation -import Result import ReactiveCocoa +import enum Result.NoError extension RACSignal { diff --git a/Source/Signal.swift b/Source/Signal.swift index c47d353c99..f6fa8c86e0 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -13,6 +13,7 @@ extension SignalType { /// 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 { return Signal { observer in return self.observe(Observer(next: { value in @@ -31,6 +32,7 @@ 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 { precondition(replacement.isTerminating) @@ -55,6 +57,7 @@ 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 { precondition(interval >= 0) precondition(event.isTerminating) @@ -78,6 +81,7 @@ extension SignalType { /// /// This operator is useful for scenarios like type-to-search where you want to /// wait for a "lull" in typing before kicking off a search request. + @warn_unused_result(message="Did you forget to call `observe` on the signal?") public func debounce(interval: NSTimeInterval, onScheduler scheduler: DateSchedulerType) -> Signal { precondition(interval >= 0) @@ -93,6 +97,7 @@ 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 { precondition(interval > 0) @@ -111,6 +116,7 @@ extension SignalType { extension SignalType where Value: SequenceType { /// 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 return self.observe { event in diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index 4c65e5248f..0f35d8d9e1 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -14,6 +14,7 @@ extension SignalProducerType { /// 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> { return SignalProducer<(Key, SignalProducer), Error> { observer, disposable in var groups: [Key: Signal.Observer] = [:] @@ -54,12 +55,14 @@ 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 { 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 { precondition(replacement.isTerminating) return lift { $0.ignoreError(replacement: replacement) } @@ -70,6 +73,7 @@ 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) } } @@ -80,6 +84,7 @@ extension SignalProducerType { /// /// This operator is useful for scenarios like type-to-search where you want to /// wait for a "lull" in typing before kicking off a search request. + @warn_unused_result(message="Did you forget to call `start` on the producer?") public func debounce(interval: NSTimeInterval, onScheduler scheduler: DateSchedulerType) -> SignalProducer { return lift { $0.debounce(interval, onScheduler: scheduler) } } @@ -91,11 +96,13 @@ 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) } } /// 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 { return SignalProducer.empty .delay(interval, onScheduler: scheduler) @@ -103,6 +110,7 @@ extension SignalProducerType { } /// 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 { precondition(count >= 0) @@ -127,8 +135,8 @@ extension SignalProducerType { extension SignalProducerType where Value: SequenceType { /// 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 { return lift { $0.uncollect() } } -} - +} \ No newline at end of file diff --git a/Source/UIKit/UIBarButtonItem.swift b/Source/UIKit/UIBarButtonItem.swift index d48b7b3fe4..735ff4e8e8 100644 --- a/Source/UIKit/UIBarButtonItem.swift +++ b/Source/UIKit/UIBarButtonItem.swift @@ -33,4 +33,4 @@ extension UIBarButtonItem { } } -private var actionKey: UInt8 = 0 +private var actionKey: UInt8 = 0 \ No newline at end of file diff --git a/Source/UIKit/UIBarItem.swift b/Source/UIKit/UIBarItem.swift index 999bd94b52..757f76d38e 100644 --- a/Source/UIKit/UIBarItem.swift +++ b/Source/UIKit/UIBarItem.swift @@ -16,4 +16,4 @@ extension UIBarItem { } } -private var enabledKey: UInt8 = 0 +private var enabledKey: UInt8 = 0 \ No newline at end of file diff --git a/Source/UIKit/UIButton.swift b/Source/UIKit/UIButton.swift index fcccda5742..2fba0b5536 100644 --- a/Source/UIKit/UIButton.swift +++ b/Source/UIKit/UIButton.swift @@ -41,4 +41,4 @@ extension UIButton { } private var pressedKey: UInt8 = 0 -private var titleKey: UInt8 = 0 +private var titleKey: UInt8 = 0 \ No newline at end of file diff --git a/Source/UIKit/UIControl.swift b/Source/UIKit/UIControl.swift index 3c7b4fbec9..7b9f8f5aa7 100644 --- a/Source/UIKit/UIControl.swift +++ b/Source/UIKit/UIControl.swift @@ -13,6 +13,7 @@ import enum Result.NoError 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) .toSignalProducer() @@ -39,4 +40,4 @@ extension UIControl { private var enabledKey: UInt8 = 0 private var selectedKey: UInt8 = 0 -private var highlightedKey: UInt8 = 0 +private var highlightedKey: UInt8 = 0 \ No newline at end of file diff --git a/Source/UIKit/UIImageView.swift b/Source/UIKit/UIImageView.swift index db6d09ea77..6f846f8248 100644 --- a/Source/UIKit/UIImageView.swift +++ b/Source/UIKit/UIImageView.swift @@ -22,4 +22,4 @@ extension UIImageView { } private var imageKey: UInt8 = 0 -private var highlightedImageKey: UInt8 = 0 +private var highlightedImageKey: UInt8 = 0 \ No newline at end of file diff --git a/Source/UIKit/UILabel.swift b/Source/UIKit/UILabel.swift index 1ec0c2529f..f24455701c 100644 --- a/Source/UIKit/UILabel.swift +++ b/Source/UIKit/UILabel.swift @@ -27,4 +27,4 @@ extension UILabel { } private var attributedTextKey: UInt8 = 0 -private var textColorKey: UInt8 = 0 +private var textColorKey: UInt8 = 0 \ No newline at end of file diff --git a/Source/UIKit/UISwitch.swift b/Source/UIKit/UISwitch.swift index 89f0b9296e..f657275084 100644 --- a/Source/UIKit/UISwitch.swift +++ b/Source/UIKit/UISwitch.swift @@ -24,4 +24,4 @@ extension UISwitch { } -private var onKey: UInt8 = 0 +private var onKey: UInt8 = 0 \ No newline at end of file diff --git a/Source/UIKit/UITextField.swift b/Source/UIKit/UITextField.swift index 407aede0f4..65350ff971 100644 --- a/Source/UIKit/UITextField.swift +++ b/Source/UIKit/UITextField.swift @@ -6,7 +6,6 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // -import Foundation import ReactiveCocoa import UIKit import enum Result.NoError @@ -19,4 +18,4 @@ extension UITextField { .rac_notifications(UITextFieldTextDidChangeNotification, object: self) .filterMap { ($0.object as? UITextField)?.text } } -} +} \ No newline at end of file diff --git a/Source/UIKit/UITextView.swift b/Source/UIKit/UITextView.swift index 3315749b9e..6239936e38 100644 --- a/Source/UIKit/UITextView.swift +++ b/Source/UIKit/UITextView.swift @@ -18,4 +18,4 @@ extension UITextView { .rac_notifications(UITextViewTextDidChangeNotification, object: self) .filterMap { ($0.object as? UITextView)?.text } } -} +} \ No newline at end of file diff --git a/Source/UIKit/UIView.swift b/Source/UIKit/UIView.swift index a7903394e1..5a82315442 100644 --- a/Source/UIKit/UIView.swift +++ b/Source/UIKit/UIView.swift @@ -22,4 +22,4 @@ extension UIView { } private var alphaKey: UInt8 = 0 -private var hiddenKey: UInt8 = 0 +private var hiddenKey: UInt8 = 0 \ No newline at end of file diff --git a/Tests/Foundation/NSObjectTests.swift b/Tests/Foundation/NSObjectTests.swift index 9a5b2cfc06..b187fc38ce 100644 --- a/Tests/Foundation/NSObjectTests.swift +++ b/Tests/Foundation/NSObjectTests.swift @@ -64,7 +64,6 @@ final class NSObjectDeallocTests: XCTestCase { } } - class Object: NSObject { dynamic var string = "foo" -} +} \ No newline at end of file diff --git a/Tests/PropertyTests.swift b/Tests/PropertyTests.swift index ea32ccbe93..be45ba3741 100644 --- a/Tests/PropertyTests.swift +++ b/Tests/PropertyTests.swift @@ -87,4 +87,4 @@ final class PropertyTests: XCTestCase { XCTAssertFalse(not.value) XCTAssertFalse(current!) } -} +} \ No newline at end of file diff --git a/Tests/SignalProducerTests.swift b/Tests/SignalProducerTests.swift index 36590b687a..8de703826f 100644 --- a/Tests/SignalProducerTests.swift +++ b/Tests/SignalProducerTests.swift @@ -158,4 +158,4 @@ final class SignalProducerTests: XCTestCase { XCTAssertEqual(value, 2) XCTAssertTrue(failed) } -} +} \ No newline at end of file diff --git a/Tests/SignalTests.swift b/Tests/SignalTests.swift index 52a2cc6cbc..05bdc91bbf 100644 --- a/Tests/SignalTests.swift +++ b/Tests/SignalTests.swift @@ -239,4 +239,4 @@ final class SignalTests: XCTestCase { enum TestError: ErrorType { case Default -} +} \ No newline at end of file diff --git a/Tests/UIKit/UIBarButtonItemTests.swift b/Tests/UIKit/UIBarButtonItemTests.swift index 438553a125..99b3a5ba78 100644 --- a/Tests/UIKit/UIBarButtonItemTests.swift +++ b/Tests/UIKit/UIBarButtonItemTests.swift @@ -43,5 +43,4 @@ class UIBarButtonItemTests: XCTestCase { observer.sendNext(true) XCTAssertTrue(barButtonItem.enabled) } - -} +} \ No newline at end of file diff --git a/Tests/UIKit/UIButtonTests.swift b/Tests/UIKit/UIButtonTests.swift index b2d4e10112..db80e318bd 100644 --- a/Tests/UIKit/UIButtonTests.swift +++ b/Tests/UIKit/UIButtonTests.swift @@ -95,7 +95,4 @@ class UIButtonTests: XCTestCase { XCTAssertTrue(passed.value) } - - - -} +} \ No newline at end of file diff --git a/Tests/UIKit/UIControlTests.swift b/Tests/UIKit/UIControlTests.swift index b16bfe480d..ff716ad45a 100644 --- a/Tests/UIKit/UIControlTests.swift +++ b/Tests/UIKit/UIControlTests.swift @@ -104,4 +104,4 @@ class UIControlTests: XCTestCase { XCTAssertFalse(control.enabled) XCTAssertFalse(control.selected) } -} +} \ No newline at end of file diff --git a/Tests/UIKit/UIDatePickerTests.swift b/Tests/UIKit/UIDatePickerTests.swift index 2212405f53..6b7ca0ca9e 100644 --- a/Tests/UIKit/UIDatePickerTests.swift +++ b/Tests/UIKit/UIDatePickerTests.swift @@ -45,5 +45,4 @@ class UIDatePickerTests: XCTestCase { picker.userInteractionEnabled = true picker.sendActionsForControlEvents(.ValueChanged) } - -} +} \ No newline at end of file diff --git a/Tests/UIKit/UIImageViewTests.swift b/Tests/UIKit/UIImageViewTests.swift index b632f512e0..096cfa440d 100644 --- a/Tests/UIKit/UIImageViewTests.swift +++ b/Tests/UIKit/UIImageViewTests.swift @@ -69,4 +69,4 @@ class UIImageViewTests: XCTestCase { observer.sendNext(secondChange) XCTAssertEqual(imageView.highlightedImage, secondChange) } -} +} \ No newline at end of file diff --git a/Tests/UIKit/UILabelTests.swift b/Tests/UIKit/UILabelTests.swift index 8d10099fb8..641c3cd09a 100644 --- a/Tests/UIKit/UILabelTests.swift +++ b/Tests/UIKit/UILabelTests.swift @@ -83,4 +83,4 @@ class UILabelTests: XCTestCase { observer.sendNext(secondChange) XCTAssertEqual(label.textColor, secondChange) } -} +} \ No newline at end of file diff --git a/Tests/UIKit/UISwitchTests.swift b/Tests/UIKit/UISwitchTests.swift index bc3c1ef1f0..cdeca9b425 100644 --- a/Tests/UIKit/UISwitchTests.swift +++ b/Tests/UIKit/UISwitchTests.swift @@ -24,5 +24,4 @@ class UISwitchTests: XCTestCase { observer.sendNext(false) XCTAssertFalse(s.on) } - -} +} \ No newline at end of file diff --git a/Tests/UIKit/UITextFieldTests.swift b/Tests/UIKit/UITextFieldTests.swift index 7ca02d34d6..939a05b15d 100644 --- a/Tests/UIKit/UITextFieldTests.swift +++ b/Tests/UIKit/UITextFieldTests.swift @@ -26,4 +26,4 @@ class UITextFieldTests: XCTestCase { NSNotificationCenter.defaultCenter().postNotificationName(UITextFieldTextDidChangeNotification, object: textField) } -} +} \ No newline at end of file diff --git a/Tests/UIKit/UITextViewTests.swift b/Tests/UIKit/UITextViewTests.swift index 9786cb4100..3158079fff 100644 --- a/Tests/UIKit/UITextViewTests.swift +++ b/Tests/UIKit/UITextViewTests.swift @@ -26,4 +26,4 @@ class UITextViewTests: XCTestCase { NSNotificationCenter.defaultCenter().postNotificationName(UITextViewTextDidChangeNotification, object: textView) } -} +} \ No newline at end of file From 43037fd92717f7116ce94db142f5183e67b7cd53 Mon Sep 17 00:00:00 2001 From: David Rodrigues Date: Wed, 20 Apr 2016 00:57:27 +0100 Subject: [PATCH 0273/1028] Added rex_prepareForReuseSignal for UITableView's components --- Rex.xcodeproj/project.pbxproj | 24 +++++++++++ Source/UIKit/UITableViewCell.swift | 41 ++++++++++++++++++ .../UIKit/UITableViewHeaderFooterView.swift | 41 ++++++++++++++++++ Tests/UIKit/UITableViewCellTests.swift | 43 +++++++++++++++++++ .../UITableViewHeaderFooterViewTests.swift | 39 +++++++++++++++++ 5 files changed, 188 insertions(+) create mode 100644 Source/UIKit/UITableViewCell.swift create mode 100644 Source/UIKit/UITableViewHeaderFooterView.swift create mode 100644 Tests/UIKit/UITableViewCellTests.swift create mode 100644 Tests/UIKit/UITableViewHeaderFooterViewTests.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 303d1e5091..a2f3b82c99 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -10,6 +10,14 @@ 4238D5961B4D5950008534C0 /* NSTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4238D5951B4D5950008534C0 /* NSTextField.swift */; }; 7D2AA99B1CB6EFEB008AB5C9 /* UISwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D2AA99A1CB6EFEB008AB5C9 /* UISwitch.swift */; }; 7D2AA99D1CB6F275008AB5C9 /* UISwitchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D2AA99C1CB6F275008AB5C9 /* UISwitchTests.swift */; }; + 7DC325741CC6FCF100746D88 /* UITableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC325721CC6FCF100746D88 /* UITableViewCell.swift */; }; + 7DC325751CC6FCF100746D88 /* UITableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC325721CC6FCF100746D88 /* UITableViewCell.swift */; }; + 7DC325761CC6FCF100746D88 /* UITableViewHeaderFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC325731CC6FCF100746D88 /* UITableViewHeaderFooterView.swift */; }; + 7DC325771CC6FCF100746D88 /* UITableViewHeaderFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC325731CC6FCF100746D88 /* UITableViewHeaderFooterView.swift */; }; + 7DC3257E1CC6FD1C00746D88 /* UITableViewCellTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC325781CC6FD0A00746D88 /* UITableViewCellTests.swift */; }; + 7DC3257F1CC6FD1E00746D88 /* UITableViewCellTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC325781CC6FD0A00746D88 /* UITableViewCellTests.swift */; }; + 7DC325801CC6FD2100746D88 /* UITableViewHeaderFooterViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC325791CC6FD0A00746D88 /* UITableViewHeaderFooterViewTests.swift */; }; + 7DC325811CC6FD2300746D88 /* UITableViewHeaderFooterViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC325791CC6FD0A00746D88 /* UITableViewHeaderFooterViewTests.swift */; }; 8289A2E11BD7EF1F0097FB60 /* UIImageViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */; }; 8289A2E31BD7EF740097FB60 /* UIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E21BD7EF740097FB60 /* UIImageView.swift */; }; 8289A2E51BD7F6DD0097FB60 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E41BD7F6DD0097FB60 /* UIView.swift */; }; @@ -185,6 +193,10 @@ 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBarButtonItem.swift; sourceTree = ""; }; 7D2AA99A1CB6EFEB008AB5C9 /* UISwitch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISwitch.swift; sourceTree = ""; }; 7D2AA99C1CB6F275008AB5C9 /* UISwitchTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISwitchTests.swift; sourceTree = ""; }; + 7DC325721CC6FCF100746D88 /* UITableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewCell.swift; sourceTree = ""; }; + 7DC325731CC6FCF100746D88 /* UITableViewHeaderFooterView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewHeaderFooterView.swift; sourceTree = ""; }; + 7DC325781CC6FD0A00746D88 /* UITableViewCellTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewCellTests.swift; sourceTree = ""; }; + 7DC325791CC6FD0A00746D88 /* UITableViewHeaderFooterViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewHeaderFooterViewTests.swift; sourceTree = ""; }; 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageViewTests.swift; sourceTree = ""; }; 8289A2E21BD7EF740097FB60 /* UIImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageView.swift; sourceTree = ""; }; 8289A2E41BD7F6DD0097FB60 /* UIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = ""; }; @@ -412,6 +424,8 @@ 8289A2E21BD7EF740097FB60 /* UIImageView.swift */, D86FFBD71B34B242001A89B3 /* UILabel.swift */, 7D2AA99A1CB6EFEB008AB5C9 /* UISwitch.swift */, + 7DC325721CC6FCF100746D88 /* UITableViewCell.swift */, + 7DC325731CC6FCF100746D88 /* UITableViewHeaderFooterView.swift */, C7932E811C4B3EDB00086F3C /* UITextField.swift */, C7DCE2B21CB3C872001217D8 /* UITextView.swift */, 8289A2E41BD7F6DD0097FB60 /* UIView.swift */, @@ -451,6 +465,8 @@ 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */, D8F073141B861B3A0047D546 /* UILabelTests.swift */, 7D2AA99C1CB6F275008AB5C9 /* UISwitchTests.swift */, + 7DC325781CC6FD0A00746D88 /* UITableViewCellTests.swift */, + 7DC325791CC6FD0A00746D88 /* UITableViewHeaderFooterViewTests.swift */, C7932E851C4B420A00086F3C /* UITextFieldTests.swift */, C7DCE2B51CB3C9C1001217D8 /* UITextViewTests.swift */, 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */, @@ -791,6 +807,8 @@ D86FFBDB1B34B3F0001A89B3 /* Action.swift in Sources */, D86FFBD21B34AD7A001A89B3 /* Association.swift in Sources */, C7932E831C4B3F3000086F3C /* UITextField.swift in Sources */, + 7DC325761CC6FCF100746D88 /* UITableViewHeaderFooterView.swift in Sources */, + 7DC325741CC6FCF100746D88 /* UITableViewCell.swift in Sources */, 8289A2E31BD7EF740097FB60 /* UIImageView.swift in Sources */, D8F0973E1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, D834572D1AFEE45B0070616A /* Signal.swift in Sources */, @@ -814,6 +832,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 7DC325801CC6FD2100746D88 /* UITableViewHeaderFooterViewTests.swift in Sources */, 8289A2E11BD7EF1F0097FB60 /* UIImageViewTests.swift in Sources */, D8F0974B1B17F5E2002E15BA /* NSObjectTests.swift in Sources */, 8289A2E81BD7F7900097FB60 /* UIViewTests.swift in Sources */, @@ -823,6 +842,7 @@ 8295FD8A1B87352D007C9000 /* UIButtonTests.swift in Sources */, D8A4540A1BD2772700C9E790 /* PropertyTests.swift in Sources */, C7945F141CC1DFBE00DC9E37 /* UIViewControllerTests.swift in Sources */, + 7DC3257F1CC6FD1E00746D88 /* UITableViewCellTests.swift in Sources */, 9DA915A61CA63046003723B9 /* UIDatePickerTests.swift in Sources */, D83457301AFEE45E0070616A /* SignalProducerTests.swift in Sources */, D83457411AFEE6050070616A /* SignalTests.swift in Sources */, @@ -859,8 +879,10 @@ D8715DC61C211553005F4191 /* UIBarButtonItem.swift in Sources */, D8715DBD1C2112D1005F4191 /* SignalProducer.swift in Sources */, D8715DBE1C2112D6005F4191 /* Association.swift in Sources */, + 7DC325771CC6FCF100746D88 /* UITableViewHeaderFooterView.swift in Sources */, D8715DC01C2112D6005F4191 /* NSObject.swift in Sources */, D8715DC91C211553005F4191 /* UIControl.swift in Sources */, + 7DC325751CC6FCF100746D88 /* UITableViewCell.swift in Sources */, D8715DBC1C2112D1005F4191 /* Signal.swift in Sources */, C72CF3E71CBF188A00E19897 /* RACSignal.swift in Sources */, D8715DBF1C2112D6005F4191 /* NSData.swift in Sources */, @@ -881,8 +903,10 @@ D8715DE11C211643005F4191 /* UIButtonTests.swift in Sources */, D8715DE21C211643005F4191 /* UIControlTests.swift in Sources */, D8715DDF1C21163B005F4191 /* NSObjectTests.swift in Sources */, + 7DC3257E1CC6FD1C00746D88 /* UITableViewCellTests.swift in Sources */, D8715DDC1C211637005F4191 /* PropertyTests.swift in Sources */, D8715DDD1C211637005F4191 /* SignalTests.swift in Sources */, + 7DC325811CC6FD2300746D88 /* UITableViewHeaderFooterViewTests.swift in Sources */, D8715DE41C211643005F4191 /* UIImageViewTests.swift in Sources */, D8715DE31C211643005F4191 /* UILabelTests.swift in Sources */, D8715DE01C211643005F4191 /* UIBarButtonItemTests.swift in Sources */, diff --git a/Source/UIKit/UITableViewCell.swift b/Source/UIKit/UITableViewCell.swift new file mode 100644 index 0000000000..1889baacf5 --- /dev/null +++ b/Source/UIKit/UITableViewCell.swift @@ -0,0 +1,41 @@ +// +// UITableViewCell.swift +// Rex +// +// Created by David Rodrigues on 19/04/16. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import UIKit +import ReactiveCocoa +import Result + +extension UITableViewCell { + + /// A signal which will send a `Next` event whenever `prepareForReuse` is invoked upon + /// the receiver. + /// + /// - Note: This signal is particular useful to be used as a trigger for the `takeUntil` + /// operator. + /// + /// #### Examples + /// + /// ``` + /// self.button + /// .rex_controlEvents(.TouchUpInside) + /// .takeUntil(self.rex_prepareForReuseSignal) + /// .startWithNext { _ in + /// // do other things + /// } + /// + /// self.label.rex_text <~ + /// titleProperty + /// .producer + /// .takeUntil(self.rex_prepareForReuseSignal) + /// ``` + /// + public var rex_prepareForReuseSignal: Signal { + return rac_prepareForReuseSignal.rex_toTriggerSignal() + } + +} \ No newline at end of file diff --git a/Source/UIKit/UITableViewHeaderFooterView.swift b/Source/UIKit/UITableViewHeaderFooterView.swift new file mode 100644 index 0000000000..2547a03469 --- /dev/null +++ b/Source/UIKit/UITableViewHeaderFooterView.swift @@ -0,0 +1,41 @@ +// +// UITableViewHeaderFooterView.swift +// Rex +// +// Created by David Rodrigues on 19/04/16. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import UIKit +import ReactiveCocoa +import Result + +extension UITableViewHeaderFooterView { + + /// A signal which will send a `Next` event whenever `prepareForReuse` is invoked upon + /// the receiver. + /// + /// - Note: This signal is particular useful to be used as a trigger for the `takeUntil` + /// operator. + /// + /// #### Examples + /// + /// ``` + /// self.button + /// .rex_controlEvents(.TouchUpInside) + /// .takeUntil(self.rex_prepareForReuseSignal) + /// .startWithNext { _ in + /// // do other things + /// } + /// + /// self.label.rex_text <~ + /// titleProperty + /// .producer + /// .takeUntil(self.rex_prepareForReuseSignal) + /// ``` + /// + public var rex_prepareForReuseSignal: Signal { + return rac_prepareForReuseSignal.rex_toTriggerSignal() + } + +} diff --git a/Tests/UIKit/UITableViewCellTests.swift b/Tests/UIKit/UITableViewCellTests.swift new file mode 100644 index 0000000000..67cad16d1c --- /dev/null +++ b/Tests/UIKit/UITableViewCellTests.swift @@ -0,0 +1,43 @@ +// +// UITableViewCellTests.swift +// Rex +// +// Created by David Rodrigues on 19/04/16. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import XCTest +import ReactiveCocoa +import Result + +class UITableViewCellTests: XCTestCase { + + func testPrepareForReuseSignal() { + + let titleProperty = MutableProperty("John") + + let cell = UITableViewCell() + + guard let label = cell.textLabel else { + fatalError() + } + + label.rex_text <~ + titleProperty + .producer + .takeUntil(cell.rex_prepareForReuseSignal) + + XCTAssertEqual(label.text, "John") + + titleProperty <~ SignalProducer(value: "Frank") + + XCTAssertEqual(label.text, "Frank") + + cell.prepareForReuse() + + titleProperty <~ SignalProducer(value: "Will") + + XCTAssertEqual(label.text, "Frank") + } + +} diff --git a/Tests/UIKit/UITableViewHeaderFooterViewTests.swift b/Tests/UIKit/UITableViewHeaderFooterViewTests.swift new file mode 100644 index 0000000000..03cc2d1b69 --- /dev/null +++ b/Tests/UIKit/UITableViewHeaderFooterViewTests.swift @@ -0,0 +1,39 @@ +// +// UITableViewHeaderFooterViewTests.swift +// Rex +// +// Created by David Rodrigues on 19/04/16. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import XCTest +import ReactiveCocoa +import Result + +class UITableViewHeaderFooterViewTests: XCTestCase { + + func testPrepareForReuseSignal() { + + let hiddenProperty = MutableProperty(false) + + let header = UITableViewHeaderFooterView() + + header.rex_hidden <~ + hiddenProperty + .producer + .takeUntil(header.rex_prepareForReuseSignal) + + XCTAssertFalse(header.hidden) + + hiddenProperty <~ SignalProducer(value: true) + + XCTAssertTrue(header.hidden) + + header.prepareForReuse() + + hiddenProperty <~ SignalProducer(value: false) + + XCTAssertTrue(header.hidden) + } + +} From 44ec7461bba5957ec2cf4cba7f7dbd10372ce2de Mon Sep 17 00:00:00 2001 From: David Rodrigues Date: Wed, 20 Apr 2016 21:21:50 +0100 Subject: [PATCH 0274/1028] Rename rex_prepareForReuseSignal and define a protocol to be shared by reusable components --- Rex.xcodeproj/project.pbxproj | 6 +++ Source/Reusable.swift | 48 +++++++++++++++++++ Source/UIKit/UITableViewCell.swift | 30 +----------- .../UIKit/UITableViewHeaderFooterView.swift | 30 +----------- Tests/UIKit/UITableViewCellTests.swift | 4 +- .../UITableViewHeaderFooterViewTests.swift | 4 +- 6 files changed, 60 insertions(+), 62 deletions(-) create mode 100644 Source/Reusable.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index a2f3b82c99..ebb12f2050 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -10,6 +10,8 @@ 4238D5961B4D5950008534C0 /* NSTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4238D5951B4D5950008534C0 /* NSTextField.swift */; }; 7D2AA99B1CB6EFEB008AB5C9 /* UISwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D2AA99A1CB6EFEB008AB5C9 /* UISwitch.swift */; }; 7D2AA99D1CB6F275008AB5C9 /* UISwitchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D2AA99C1CB6F275008AB5C9 /* UISwitchTests.swift */; }; + 7DBD48F31CC8141D0077AD4F /* Reusable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DBD48F21CC8141D0077AD4F /* Reusable.swift */; }; + 7DBD48F41CC8141D0077AD4F /* Reusable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DBD48F21CC8141D0077AD4F /* Reusable.swift */; }; 7DC325741CC6FCF100746D88 /* UITableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC325721CC6FCF100746D88 /* UITableViewCell.swift */; }; 7DC325751CC6FCF100746D88 /* UITableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC325721CC6FCF100746D88 /* UITableViewCell.swift */; }; 7DC325761CC6FCF100746D88 /* UITableViewHeaderFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC325731CC6FCF100746D88 /* UITableViewHeaderFooterView.swift */; }; @@ -193,6 +195,7 @@ 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBarButtonItem.swift; sourceTree = ""; }; 7D2AA99A1CB6EFEB008AB5C9 /* UISwitch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISwitch.swift; sourceTree = ""; }; 7D2AA99C1CB6F275008AB5C9 /* UISwitchTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISwitchTests.swift; sourceTree = ""; }; + 7DBD48F21CC8141D0077AD4F /* Reusable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reusable.swift; sourceTree = ""; }; 7DC325721CC6FCF100746D88 /* UITableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewCell.swift; sourceTree = ""; }; 7DC325731CC6FCF100746D88 /* UITableViewHeaderFooterView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewHeaderFooterView.swift; sourceTree = ""; }; 7DC325781CC6FD0A00746D88 /* UITableViewCellTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewCellTests.swift; sourceTree = ""; }; @@ -341,6 +344,7 @@ D8003EBC1AFED01000D7D3C5 /* Signal.swift */, D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */, C72CF3E41CBF188A00E19897 /* RACSignal.swift */, + 7DBD48F21CC8141D0077AD4F /* Reusable.swift */, 4238D5941B4D593E008534C0 /* AppKit */, D8F097391B17F2BF002E15BA /* Foundation */, D86FFBD31B34B0E2001A89B3 /* UIKit */, @@ -814,6 +818,7 @@ D834572D1AFEE45B0070616A /* Signal.swift in Sources */, D8E4A6211B7BBB2100EAD8A8 /* UIBarItem.swift in Sources */, 7D2AA99B1CB6EFEB008AB5C9 /* UISwitch.swift in Sources */, + 7DBD48F31CC8141D0077AD4F /* Reusable.swift in Sources */, C72CF3E61CBF188A00E19897 /* RACSignal.swift in Sources */, D8A454071BD26A1A00C9E790 /* Property.swift in Sources */, D8E4A6201B7BBB1600EAD8A8 /* UIBarButtonItem.swift in Sources */, @@ -876,6 +881,7 @@ D8715DCA1C211553005F4191 /* UILabel.swift in Sources */, D8715DCB1C211553005F4191 /* UIImageView.swift in Sources */, C7932E841C4B41E100086F3C /* UITextField.swift in Sources */, + 7DBD48F41CC8141D0077AD4F /* Reusable.swift in Sources */, D8715DC61C211553005F4191 /* UIBarButtonItem.swift in Sources */, D8715DBD1C2112D1005F4191 /* SignalProducer.swift in Sources */, D8715DBE1C2112D6005F4191 /* Association.swift in Sources */, diff --git a/Source/Reusable.swift b/Source/Reusable.swift new file mode 100644 index 0000000000..3a27875113 --- /dev/null +++ b/Source/Reusable.swift @@ -0,0 +1,48 @@ +// +// Reusable.swift +// Rex +// +// Created by David Rodrigues on 20/04/16. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import Foundation +import ReactiveCocoa +import Result + + +/// A protocol for components that can be reused using `prepareForReuse`. +public protocol Reusable { + var rac_prepareForReuseSignal: RACSignal! { get } +} + +extension Reusable { + + /// A signal which will send a `Next` event whenever `prepareForReuse` is invoked upon + /// the receiver. + /// + /// - Note: This signal is particular useful to be used as a trigger for the `takeUntil` + /// operator. + /// + /// #### Examples + /// + /// ``` + /// button + /// .rex_controlEvents(.TouchUpInside) + /// .takeUntil(self.rex_prepareForReuse) + /// .startWithNext { _ in + /// // do other things + /// } + /// + /// label.rex_text <~ + /// titleProperty + /// .producer + /// .takeUntil(self.rex_prepareForReuse) + /// ``` + /// + public var rex_prepareForReuse: Signal { + return rac_prepareForReuseSignal + .rex_toTriggerSignal() + } + +} diff --git a/Source/UIKit/UITableViewCell.swift b/Source/UIKit/UITableViewCell.swift index 1889baacf5..0ce4fbf70d 100644 --- a/Source/UIKit/UITableViewCell.swift +++ b/Source/UIKit/UITableViewCell.swift @@ -10,32 +10,4 @@ import UIKit import ReactiveCocoa import Result -extension UITableViewCell { - - /// A signal which will send a `Next` event whenever `prepareForReuse` is invoked upon - /// the receiver. - /// - /// - Note: This signal is particular useful to be used as a trigger for the `takeUntil` - /// operator. - /// - /// #### Examples - /// - /// ``` - /// self.button - /// .rex_controlEvents(.TouchUpInside) - /// .takeUntil(self.rex_prepareForReuseSignal) - /// .startWithNext { _ in - /// // do other things - /// } - /// - /// self.label.rex_text <~ - /// titleProperty - /// .producer - /// .takeUntil(self.rex_prepareForReuseSignal) - /// ``` - /// - public var rex_prepareForReuseSignal: Signal { - return rac_prepareForReuseSignal.rex_toTriggerSignal() - } - -} \ No newline at end of file +extension UITableViewCell: Reusable {} diff --git a/Source/UIKit/UITableViewHeaderFooterView.swift b/Source/UIKit/UITableViewHeaderFooterView.swift index 2547a03469..92b5947c20 100644 --- a/Source/UIKit/UITableViewHeaderFooterView.swift +++ b/Source/UIKit/UITableViewHeaderFooterView.swift @@ -10,32 +10,4 @@ import UIKit import ReactiveCocoa import Result -extension UITableViewHeaderFooterView { - - /// A signal which will send a `Next` event whenever `prepareForReuse` is invoked upon - /// the receiver. - /// - /// - Note: This signal is particular useful to be used as a trigger for the `takeUntil` - /// operator. - /// - /// #### Examples - /// - /// ``` - /// self.button - /// .rex_controlEvents(.TouchUpInside) - /// .takeUntil(self.rex_prepareForReuseSignal) - /// .startWithNext { _ in - /// // do other things - /// } - /// - /// self.label.rex_text <~ - /// titleProperty - /// .producer - /// .takeUntil(self.rex_prepareForReuseSignal) - /// ``` - /// - public var rex_prepareForReuseSignal: Signal { - return rac_prepareForReuseSignal.rex_toTriggerSignal() - } - -} +extension UITableViewHeaderFooterView: Reusable {} diff --git a/Tests/UIKit/UITableViewCellTests.swift b/Tests/UIKit/UITableViewCellTests.swift index 67cad16d1c..f4a9c44233 100644 --- a/Tests/UIKit/UITableViewCellTests.swift +++ b/Tests/UIKit/UITableViewCellTests.swift @@ -12,7 +12,7 @@ import Result class UITableViewCellTests: XCTestCase { - func testPrepareForReuseSignal() { + func testPrepareForReuse() { let titleProperty = MutableProperty("John") @@ -25,7 +25,7 @@ class UITableViewCellTests: XCTestCase { label.rex_text <~ titleProperty .producer - .takeUntil(cell.rex_prepareForReuseSignal) + .takeUntil(cell.rex_prepareForReuse) XCTAssertEqual(label.text, "John") diff --git a/Tests/UIKit/UITableViewHeaderFooterViewTests.swift b/Tests/UIKit/UITableViewHeaderFooterViewTests.swift index 03cc2d1b69..214d292d05 100644 --- a/Tests/UIKit/UITableViewHeaderFooterViewTests.swift +++ b/Tests/UIKit/UITableViewHeaderFooterViewTests.swift @@ -12,7 +12,7 @@ import Result class UITableViewHeaderFooterViewTests: XCTestCase { - func testPrepareForReuseSignal() { + func testPrepareForReuse() { let hiddenProperty = MutableProperty(false) @@ -21,7 +21,7 @@ class UITableViewHeaderFooterViewTests: XCTestCase { header.rex_hidden <~ hiddenProperty .producer - .takeUntil(header.rex_prepareForReuseSignal) + .takeUntil(header.rex_prepareForReuse) XCTAssertFalse(header.hidden) From 1e87a296c2a0604cd98a2fb1ffbafd8d0030856a Mon Sep 17 00:00:00 2001 From: Rui Peres Date: Wed, 20 Apr 2016 21:23:07 +0100 Subject: [PATCH 0275/1028] method names were inconsistent --- Source/Foundation/NSObject.swift | 2 +- Source/UIKit/UITextField.swift | 2 +- Source/UIKit/UITextView.swift | 2 +- Source/UIKit/UIViewController.swift | 8 ++++---- Tests/Foundation/NSObjectTests.swift | 2 +- Tests/UIKit/UITextFieldTests.swift | 4 ++-- Tests/UIKit/UITextViewTests.swift | 4 ++-- Tests/UIKit/UIViewControllerTests.swift | 16 ++++++++-------- 8 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index 1327bcc44e..5927fb25b7 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -29,7 +29,7 @@ extension NSObject { /// Creates a signal that will be triggered when the object /// is deallocated. - public var rex_willDeallocSignal: Signal<(), NoError> { + public var rex_willDealloc: Signal<(), NoError> { return self .rac_willDeallocSignal() .rex_toTriggerSignal() diff --git a/Source/UIKit/UITextField.swift b/Source/UIKit/UITextField.swift index 65350ff971..eebee49631 100644 --- a/Source/UIKit/UITextField.swift +++ b/Source/UIKit/UITextField.swift @@ -13,7 +13,7 @@ import enum Result.NoError extension UITextField { /// Sends the field's string value whenever it changes. - public var rex_textSignal: SignalProducer { + public var rex_text: SignalProducer { return NSNotificationCenter.defaultCenter() .rac_notifications(UITextFieldTextDidChangeNotification, object: self) .filterMap { ($0.object as? UITextField)?.text } diff --git a/Source/UIKit/UITextView.swift b/Source/UIKit/UITextView.swift index 6239936e38..1d546f0219 100644 --- a/Source/UIKit/UITextView.swift +++ b/Source/UIKit/UITextView.swift @@ -13,7 +13,7 @@ import enum Result.NoError extension UITextView { /// Sends the textView's string value whenever it changes. - public var rex_textSignal: SignalProducer { + public var rex_text: SignalProducer { return NSNotificationCenter.defaultCenter() .rac_notifications(UITextViewTextDidChangeNotification, object: self) .filterMap { ($0.object as? UITextView)?.text } diff --git a/Source/UIKit/UIViewController.swift b/Source/UIKit/UIViewController.swift index 322ed5f670..93dde4eddb 100644 --- a/Source/UIKit/UIViewController.swift +++ b/Source/UIKit/UIViewController.swift @@ -13,25 +13,25 @@ import UIKit extension UIViewController { /// Returns a `Signal`, that will be triggered /// when `self`'s `viewDidDisappear` is called - public var rex_viewDidDisappearSignal: Signal<(), NoError> { + public var rex_viewDidDisappear: Signal<(), NoError> { return triggerForSelector(#selector(UIViewController.viewDidDisappear(_:))) } /// Returns a `Signal`, that will be triggered /// when `self`'s `viewWillDisappear` is called - public var rex_viewWillDisappearSignal: Signal<(), NoError> { + public var rex_viewWillDisappear: Signal<(), NoError> { return triggerForSelector(#selector(UIViewController.viewWillDisappear(_:))) } /// Returns a `Signal`, that will be triggered /// when `self`'s `viewDidAppear` is called - public var rex_viewDidAppearSignal: Signal<(), NoError> { + public var rex_viewDidAppear: Signal<(), NoError> { return triggerForSelector(#selector(UIViewController.viewDidAppear(_:))) } /// Returns a `Signal`, that will be triggered /// when `self`'s `viewWillAppear` is called - public var rex_viewWillAppearSignal: Signal<(), NoError> { + public var rex_viewWillAppear: Signal<(), NoError> { return triggerForSelector(#selector(UIViewController.viewWillAppear(_:))) } diff --git a/Tests/Foundation/NSObjectTests.swift b/Tests/Foundation/NSObjectTests.swift index b187fc38ce..8f5d3a2d44 100644 --- a/Tests/Foundation/NSObjectTests.swift +++ b/Tests/Foundation/NSObjectTests.swift @@ -31,7 +31,7 @@ final class NSObjectTests: XCTestCase { let object = Object() timer(1, onScheduler: QueueScheduler(name: "test.queue")) - .takeUntil(object.rex_willDeallocSignal) + .takeUntil(object.rex_willDealloc) .startWithCompleted { expectation.fulfill() } diff --git a/Tests/UIKit/UITextFieldTests.swift b/Tests/UIKit/UITextFieldTests.swift index 939a05b15d..4745a6e75e 100644 --- a/Tests/UIKit/UITextFieldTests.swift +++ b/Tests/UIKit/UITextFieldTests.swift @@ -13,13 +13,13 @@ import XCTest class UITextFieldTests: XCTestCase { func testTextProperty() { - let expectation = self.expectationWithDescription("Expected textSignal's value to equal to the textField's text") + let expectation = self.expectationWithDescription("Expected `rex_text`'s value to equal to the textField's text") defer { self.waitForExpectationsWithTimeout(2, handler: nil) } let textField = UITextField(frame: CGRectZero) textField.text = "Test" - textField.rex_textSignal.startWithNext { text in + textField.rex_text.startWithNext { text in XCTAssertEqual(text, textField.text) expectation.fulfill() } diff --git a/Tests/UIKit/UITextViewTests.swift b/Tests/UIKit/UITextViewTests.swift index 3158079fff..06aab6c25c 100644 --- a/Tests/UIKit/UITextViewTests.swift +++ b/Tests/UIKit/UITextViewTests.swift @@ -13,13 +13,13 @@ import XCTest class UITextViewTests: XCTestCase { func testTextProperty() { - let expectation = self.expectationWithDescription("Expected textSignal's value to equal to the textViews's text") + let expectation = self.expectationWithDescription("Expected `rex_text`'s value to equal to the textViews's text") defer { self.waitForExpectationsWithTimeout(2, handler: nil) } let textView = UITextView(frame: CGRectZero) textView.text = "Test" - textView.rex_textSignal.startWithNext { text in + textView.rex_text.startWithNext { text in XCTAssertEqual(text, textView.text) expectation.fulfill() } diff --git a/Tests/UIKit/UIViewControllerTests.swift b/Tests/UIKit/UIViewControllerTests.swift index 5551d6217d..a5ef94e140 100644 --- a/Tests/UIKit/UIViewControllerTests.swift +++ b/Tests/UIKit/UIViewControllerTests.swift @@ -22,13 +22,13 @@ class UIViewControllerTests: XCTestCase { func testViewDidDisappear() { - let expectation = self.expectationWithDescription("Expected rex_viewDidDisappearSignal to be triggered") + let expectation = self.expectationWithDescription("Expected rex_viewDidDisappear to be triggered") defer { self.waitForExpectationsWithTimeout(2, handler: nil) } let viewController = UIViewController() _viewController = viewController - viewController.rex_viewDidDisappearSignal.observeNext { + viewController.rex_viewDidDisappear.observeNext { expectation.fulfill() } @@ -37,13 +37,13 @@ class UIViewControllerTests: XCTestCase { func testViewWillDisappear() { - let expectation = self.expectationWithDescription("Expected rex_viewWillDisappearSignal to be triggered") + let expectation = self.expectationWithDescription("Expected rex_viewWillDisappear to be triggered") defer { self.waitForExpectationsWithTimeout(2, handler: nil) } let viewController = UIViewController() _viewController = viewController - viewController.rex_viewWillDisappearSignal.observeNext { + viewController.rex_viewWillDisappear.observeNext { expectation.fulfill() } @@ -52,13 +52,13 @@ class UIViewControllerTests: XCTestCase { func testViewDidAppear() { - let expectation = self.expectationWithDescription("Expected rex_viewDidAppearSignal to be triggered") + let expectation = self.expectationWithDescription("Expected rex_viewDidAppear to be triggered") defer { self.waitForExpectationsWithTimeout(2, handler: nil) } let viewController = UIViewController() _viewController = viewController - viewController.rex_viewDidAppearSignal.observeNext { + viewController.rex_viewDidAppear.observeNext { expectation.fulfill() } @@ -67,13 +67,13 @@ class UIViewControllerTests: XCTestCase { func testViewWillAppear() { - let expectation = self.expectationWithDescription("Expected rex_viewWillAppearSignal to be triggered") + let expectation = self.expectationWithDescription("Expected rex_viewWillAppear to be triggered") defer { self.waitForExpectationsWithTimeout(2, handler: nil) } let viewController = UIViewController() _viewController = viewController - viewController.rex_viewWillAppearSignal.observeNext { + viewController.rex_viewWillAppear.observeNext { expectation.fulfill() } From 3ca32944853f302f9bcb75fa671241668dd54cd2 Mon Sep 17 00:00:00 2001 From: David Rodrigues Date: Wed, 20 Apr 2016 21:25:19 +0100 Subject: [PATCH 0276/1028] Extend UICollectionReusableView to provide `rex_prepareForReuse` --- Rex.xcodeproj/project.pbxproj | 12 ++++++ Source/UIKit/UICollectionReusableView.swift | 13 +++++++ .../UIKit/UICollectionReusableViewTests.swift | 38 +++++++++++++++++++ Tests/UIKit/UITableViewCellTests.swift | 2 - .../UITableViewHeaderFooterViewTests.swift | 2 - 5 files changed, 63 insertions(+), 4 deletions(-) create mode 100644 Source/UIKit/UICollectionReusableView.swift create mode 100644 Tests/UIKit/UICollectionReusableViewTests.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index ebb12f2050..d2a7aeb041 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -20,6 +20,10 @@ 7DC3257F1CC6FD1E00746D88 /* UITableViewCellTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC325781CC6FD0A00746D88 /* UITableViewCellTests.swift */; }; 7DC325801CC6FD2100746D88 /* UITableViewHeaderFooterViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC325791CC6FD0A00746D88 /* UITableViewHeaderFooterViewTests.swift */; }; 7DC325811CC6FD2300746D88 /* UITableViewHeaderFooterViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC325791CC6FD0A00746D88 /* UITableViewHeaderFooterViewTests.swift */; }; + 7DCF5B331CC80D77004AEE75 /* UICollectionReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DCF5B311CC80D0E004AEE75 /* UICollectionReusableView.swift */; }; + 7DCF5B341CC80D78004AEE75 /* UICollectionReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DCF5B311CC80D0E004AEE75 /* UICollectionReusableView.swift */; }; + 7DCF5B361CC80E8E004AEE75 /* UICollectionReusableViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DCF5B351CC80E8E004AEE75 /* UICollectionReusableViewTests.swift */; }; + 7DCF5B371CC80E8E004AEE75 /* UICollectionReusableViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DCF5B351CC80E8E004AEE75 /* UICollectionReusableViewTests.swift */; }; 8289A2E11BD7EF1F0097FB60 /* UIImageViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */; }; 8289A2E31BD7EF740097FB60 /* UIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E21BD7EF740097FB60 /* UIImageView.swift */; }; 8289A2E51BD7F6DD0097FB60 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E41BD7F6DD0097FB60 /* UIView.swift */; }; @@ -200,6 +204,8 @@ 7DC325731CC6FCF100746D88 /* UITableViewHeaderFooterView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewHeaderFooterView.swift; sourceTree = ""; }; 7DC325781CC6FD0A00746D88 /* UITableViewCellTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewCellTests.swift; sourceTree = ""; }; 7DC325791CC6FD0A00746D88 /* UITableViewHeaderFooterViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewHeaderFooterViewTests.swift; sourceTree = ""; }; + 7DCF5B311CC80D0E004AEE75 /* UICollectionReusableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UICollectionReusableView.swift; sourceTree = ""; }; + 7DCF5B351CC80E8E004AEE75 /* UICollectionReusableViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UICollectionReusableViewTests.swift; sourceTree = ""; }; 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageViewTests.swift; sourceTree = ""; }; 8289A2E21BD7EF740097FB60 /* UIImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageView.swift; sourceTree = ""; }; 8289A2E41BD7F6DD0097FB60 /* UIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = ""; }; @@ -423,6 +429,7 @@ 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */, 5173EBC51B625A2600C9B48E /* UIBarItem.swift */, D86FFBDC1B34B691001A89B3 /* UIButton.swift */, + 7DCF5B311CC80D0E004AEE75 /* UICollectionReusableView.swift */, D86FFBD41B34B0FE001A89B3 /* UIControl.swift */, 9DA915A31CA6301C003723B9 /* UIDatePicker.swift */, 8289A2E21BD7EF740097FB60 /* UIImageView.swift */, @@ -465,6 +472,7 @@ 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */, 8295FD881B873490007C9000 /* UIButtonTests.swift */, 8295FD851B873081007C9000 /* UIControlTests.swift */, + 7DCF5B351CC80E8E004AEE75 /* UICollectionReusableViewTests.swift */, 9DA915A51CA63046003723B9 /* UIDatePickerTests.swift */, 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */, D8F073141B861B3A0047D546 /* UILabelTests.swift */, @@ -808,6 +816,7 @@ files = ( 9DA915A41CA6301C003723B9 /* UIDatePicker.swift in Sources */, D86FFBD81B34B242001A89B3 /* UILabel.swift in Sources */, + 7DCF5B331CC80D77004AEE75 /* UICollectionReusableView.swift in Sources */, D86FFBDB1B34B3F0001A89B3 /* Action.swift in Sources */, D86FFBD21B34AD7A001A89B3 /* Association.swift in Sources */, C7932E831C4B3F3000086F3C /* UITextField.swift in Sources */, @@ -840,6 +849,7 @@ 7DC325801CC6FD2100746D88 /* UITableViewHeaderFooterViewTests.swift in Sources */, 8289A2E11BD7EF1F0097FB60 /* UIImageViewTests.swift in Sources */, D8F0974B1B17F5E2002E15BA /* NSObjectTests.swift in Sources */, + 7DCF5B361CC80E8E004AEE75 /* UICollectionReusableViewTests.swift in Sources */, 8289A2E81BD7F7900097FB60 /* UIViewTests.swift in Sources */, D8F073161B863CE70047D546 /* UILabelTests.swift in Sources */, 7D2AA99D1CB6F275008AB5C9 /* UISwitchTests.swift in Sources */, @@ -879,6 +889,7 @@ files = ( D8715DBB1C2112D1005F4191 /* Property.swift in Sources */, D8715DCA1C211553005F4191 /* UILabel.swift in Sources */, + 7DCF5B341CC80D78004AEE75 /* UICollectionReusableView.swift in Sources */, D8715DCB1C211553005F4191 /* UIImageView.swift in Sources */, C7932E841C4B41E100086F3C /* UITextField.swift in Sources */, 7DBD48F41CC8141D0077AD4F /* Reusable.swift in Sources */, @@ -905,6 +916,7 @@ buildActionMask = 2147483647; files = ( D8715DE51C211643005F4191 /* UIViewTests.swift in Sources */, + 7DCF5B371CC80E8E004AEE75 /* UICollectionReusableViewTests.swift in Sources */, D8715DDE1C211637005F4191 /* SignalProducerTests.swift in Sources */, D8715DE11C211643005F4191 /* UIButtonTests.swift in Sources */, D8715DE21C211643005F4191 /* UIControlTests.swift in Sources */, diff --git a/Source/UIKit/UICollectionReusableView.swift b/Source/UIKit/UICollectionReusableView.swift new file mode 100644 index 0000000000..84c901ad75 --- /dev/null +++ b/Source/UIKit/UICollectionReusableView.swift @@ -0,0 +1,13 @@ +// +// UICollectionReusableView.swift +// Rex +// +// Created by David Rodrigues on 20/04/16. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import Foundation +import ReactiveCocoa +import Result + +extension UICollectionReusableView: Reusable {} diff --git a/Tests/UIKit/UICollectionReusableViewTests.swift b/Tests/UIKit/UICollectionReusableViewTests.swift new file mode 100644 index 0000000000..9f1f69b2a0 --- /dev/null +++ b/Tests/UIKit/UICollectionReusableViewTests.swift @@ -0,0 +1,38 @@ +// +// UICollectionReusableViewTests.swift +// Rex +// +// Created by David Rodrigues on 20/04/16. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import XCTest +import ReactiveCocoa +import Result + + +class UICollectionReusableViewTests: XCTestCase { + + func testPrepareForReuse() { + + let hiddenProperty = MutableProperty(false) + + let cell = UICollectionViewCell() + + cell.rex_hidden <~ + hiddenProperty + .producer + .takeUntil(cell.rex_prepareForReuse) + + XCTAssertFalse(cell.hidden) + + hiddenProperty <~ SignalProducer(value: true) + XCTAssertTrue(cell.hidden) + + cell.prepareForReuse() + + hiddenProperty <~ SignalProducer(value: false) + XCTAssertTrue(cell.hidden) + } + +} diff --git a/Tests/UIKit/UITableViewCellTests.swift b/Tests/UIKit/UITableViewCellTests.swift index f4a9c44233..ce268a23d5 100644 --- a/Tests/UIKit/UITableViewCellTests.swift +++ b/Tests/UIKit/UITableViewCellTests.swift @@ -30,13 +30,11 @@ class UITableViewCellTests: XCTestCase { XCTAssertEqual(label.text, "John") titleProperty <~ SignalProducer(value: "Frank") - XCTAssertEqual(label.text, "Frank") cell.prepareForReuse() titleProperty <~ SignalProducer(value: "Will") - XCTAssertEqual(label.text, "Frank") } diff --git a/Tests/UIKit/UITableViewHeaderFooterViewTests.swift b/Tests/UIKit/UITableViewHeaderFooterViewTests.swift index 214d292d05..aa900a8dfe 100644 --- a/Tests/UIKit/UITableViewHeaderFooterViewTests.swift +++ b/Tests/UIKit/UITableViewHeaderFooterViewTests.swift @@ -26,13 +26,11 @@ class UITableViewHeaderFooterViewTests: XCTestCase { XCTAssertFalse(header.hidden) hiddenProperty <~ SignalProducer(value: true) - XCTAssertTrue(header.hidden) header.prepareForReuse() hiddenProperty <~ SignalProducer(value: false) - XCTAssertTrue(header.hidden) } From beec680ce4792dde1b039e792a3251709c89df7f Mon Sep 17 00:00:00 2001 From: David Rodrigues Date: Wed, 20 Apr 2016 21:48:12 +0100 Subject: [PATCH 0277/1028] Update formatting and remove some unnecessary imports --- Source/Reusable.swift | 4 +--- Source/UIKit/UICollectionReusableView.swift | 4 +--- Source/UIKit/UITableViewCell.swift | 2 -- Source/UIKit/UITableViewHeaderFooterView.swift | 2 -- Tests/UIKit/UICollectionReusableViewTests.swift | 3 --- Tests/UIKit/UITableViewCellTests.swift | 2 -- Tests/UIKit/UITableViewHeaderFooterViewTests.swift | 2 -- 7 files changed, 2 insertions(+), 17 deletions(-) diff --git a/Source/Reusable.swift b/Source/Reusable.swift index 3a27875113..a71197f131 100644 --- a/Source/Reusable.swift +++ b/Source/Reusable.swift @@ -6,9 +6,8 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // -import Foundation import ReactiveCocoa -import Result +import enum Result.NoError /// A protocol for components that can be reused using `prepareForReuse`. @@ -44,5 +43,4 @@ extension Reusable { return rac_prepareForReuseSignal .rex_toTriggerSignal() } - } diff --git a/Source/UIKit/UICollectionReusableView.swift b/Source/UIKit/UICollectionReusableView.swift index 84c901ad75..827b6e537c 100644 --- a/Source/UIKit/UICollectionReusableView.swift +++ b/Source/UIKit/UICollectionReusableView.swift @@ -6,8 +6,6 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // -import Foundation -import ReactiveCocoa -import Result +import UIKit extension UICollectionReusableView: Reusable {} diff --git a/Source/UIKit/UITableViewCell.swift b/Source/UIKit/UITableViewCell.swift index 0ce4fbf70d..d59c372722 100644 --- a/Source/UIKit/UITableViewCell.swift +++ b/Source/UIKit/UITableViewCell.swift @@ -7,7 +7,5 @@ // import UIKit -import ReactiveCocoa -import Result extension UITableViewCell: Reusable {} diff --git a/Source/UIKit/UITableViewHeaderFooterView.swift b/Source/UIKit/UITableViewHeaderFooterView.swift index 92b5947c20..d013da0fd3 100644 --- a/Source/UIKit/UITableViewHeaderFooterView.swift +++ b/Source/UIKit/UITableViewHeaderFooterView.swift @@ -7,7 +7,5 @@ // import UIKit -import ReactiveCocoa -import Result extension UITableViewHeaderFooterView: Reusable {} diff --git a/Tests/UIKit/UICollectionReusableViewTests.swift b/Tests/UIKit/UICollectionReusableViewTests.swift index 9f1f69b2a0..b95afacc2e 100644 --- a/Tests/UIKit/UICollectionReusableViewTests.swift +++ b/Tests/UIKit/UICollectionReusableViewTests.swift @@ -8,8 +8,6 @@ import XCTest import ReactiveCocoa -import Result - class UICollectionReusableViewTests: XCTestCase { @@ -34,5 +32,4 @@ class UICollectionReusableViewTests: XCTestCase { hiddenProperty <~ SignalProducer(value: false) XCTAssertTrue(cell.hidden) } - } diff --git a/Tests/UIKit/UITableViewCellTests.swift b/Tests/UIKit/UITableViewCellTests.swift index ce268a23d5..72eba7bafa 100644 --- a/Tests/UIKit/UITableViewCellTests.swift +++ b/Tests/UIKit/UITableViewCellTests.swift @@ -8,7 +8,6 @@ import XCTest import ReactiveCocoa -import Result class UITableViewCellTests: XCTestCase { @@ -37,5 +36,4 @@ class UITableViewCellTests: XCTestCase { titleProperty <~ SignalProducer(value: "Will") XCTAssertEqual(label.text, "Frank") } - } diff --git a/Tests/UIKit/UITableViewHeaderFooterViewTests.swift b/Tests/UIKit/UITableViewHeaderFooterViewTests.swift index aa900a8dfe..8255e93fdc 100644 --- a/Tests/UIKit/UITableViewHeaderFooterViewTests.swift +++ b/Tests/UIKit/UITableViewHeaderFooterViewTests.swift @@ -8,7 +8,6 @@ import XCTest import ReactiveCocoa -import Result class UITableViewHeaderFooterViewTests: XCTestCase { @@ -33,5 +32,4 @@ class UITableViewHeaderFooterViewTests: XCTestCase { hiddenProperty <~ SignalProducer(value: false) XCTAssertTrue(header.hidden) } - } From 938c683fe69d70c465dbbe05e585f4683ae494bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bjarke=20Hesthaven=20S=C3=B8ndergaard?= Date: Thu, 21 Apr 2016 16:49:01 +0200 Subject: [PATCH 0278/1028] Fix tvOS in podspec and add to travis --- .travis.yml | 1 + Rex.podspec | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c694c6482a..265cfaf441 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,6 +21,7 @@ script: - xcodebuild test -scheme Rex-iOS -sdk iphonesimulator -destination "platform=iOS Simulator,name=iPhone 6s" | xcpretty -c - xcodebuild test -scheme Rex-tvOS -sdk appletvsimulator -destination "platform=tvOS Simulator,name=Apple TV 1080p" | xcpretty -c - xcodebuild build -scheme Rex-watchOS -sdk watchsimulator -destination "platform=watchOS Simulator,name=Apple Watch - 38mm" | xcpretty -c + - pod lib lint notifications: email: false diff --git a/Rex.podspec b/Rex.podspec index 21822d2ad5..d4e53d6450 100644 --- a/Rex.podspec +++ b/Rex.podspec @@ -21,11 +21,12 @@ Pod::Spec.new do |s| s.source = { :git => 'https://github.com/neilpa/Rex.git', :tag => s.version } s.dependency 'ReactiveCocoa', '~> 4.1' s.ios.framework = 'UIKit' + s.tvos.framework = 'UIKit' s.osx.framework = 'AppKit' s.source_files = 'Source/**/*.swift' s.ios.exclude_files = 'Source/AppKit/*' - s.tvos.exclude_files = 'Source/AppKit/*' + s.tvos.exclude_files = 'Source/AppKit/*', 'Source/UIKit/UIDatePicker.swift' s.watchos.exclude_files = 'Source/AppKit/*', 'Source/UIKit/*' s.osx.exclude_files = 'Source/UIKit/*' From 9432131c5086b7d2fe725fe4870dac8c0235bf53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bjarke=20Hesthaven=20S=C3=B8ndergaard?= Date: Thu, 21 Apr 2016 19:30:22 +0200 Subject: [PATCH 0279/1028] Exclude UISwitch as well --- Rex.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rex.podspec b/Rex.podspec index d4e53d6450..5020b7c36f 100644 --- a/Rex.podspec +++ b/Rex.podspec @@ -26,7 +26,7 @@ Pod::Spec.new do |s| s.source_files = 'Source/**/*.swift' s.ios.exclude_files = 'Source/AppKit/*' - s.tvos.exclude_files = 'Source/AppKit/*', 'Source/UIKit/UIDatePicker.swift' + s.tvos.exclude_files = 'Source/AppKit/*', 'Source/UIKit/UIDatePicker.swift', 'Source/UIKit/UISwitch.swift' s.watchos.exclude_files = 'Source/AppKit/*', 'Source/UIKit/*' s.osx.exclude_files = 'Source/UIKit/*' From 9394b50a5ae4123bebbcbc8b1662b7658ed5a4b8 Mon Sep 17 00:00:00 2001 From: Ilya Laryionau Date: Wed, 20 Apr 2016 09:27:28 +0300 Subject: [PATCH 0280/1028] add completed and started signals to Action --- Source/Action.swift | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Source/Action.swift b/Source/Action.swift index 34219e35c6..47e5c9cc1f 100644 --- a/Source/Action.swift +++ b/Source/Action.swift @@ -14,6 +14,26 @@ extension Action { public static var rex_disabled: Action { return Action(enabledIf: ConstantProperty(false)) { _ in .empty } } + + /// Whether the action execution was completed succesfully. + public var completed: Signal { + return events + .filter { event in + if case .Completed = event { + return true + } else { + return false + } + } + .map { _ in } + } + + /// Whether the action execution was started. + public var started: Signal { + return self.executing.signal + .filter { $0 } + .map { _ in } + } } extension CocoaAction { From 8a949b1e0688b016a97f32faf732656618d25cdf Mon Sep 17 00:00:00 2001 From: Ilya Laryionau Date: Wed, 20 Apr 2016 09:50:15 +0300 Subject: [PATCH 0281/1028] add `rex_` prefix to new properties --- Source/Action.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Action.swift b/Source/Action.swift index 47e5c9cc1f..ab2cfb6e05 100644 --- a/Source/Action.swift +++ b/Source/Action.swift @@ -16,7 +16,7 @@ extension Action { } /// Whether the action execution was completed succesfully. - public var completed: Signal { + public var rex_completed: Signal { return events .filter { event in if case .Completed = event { @@ -29,7 +29,7 @@ extension Action { } /// Whether the action execution was started. - public var started: Signal { + public var rex_started: Signal { return self.executing.signal .filter { $0 } .map { _ in } From 8f4bdfbf8db29ea9eeae7652359eff9d7e1b19dc Mon Sep 17 00:00:00 2001 From: Ilya Laryionau Date: Wed, 20 Apr 2016 13:15:18 +0300 Subject: [PATCH 0282/1028] fix typo --- Source/Action.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Action.swift b/Source/Action.swift index ab2cfb6e05..a4f88af751 100644 --- a/Source/Action.swift +++ b/Source/Action.swift @@ -15,7 +15,7 @@ extension Action { return Action(enabledIf: ConstantProperty(false)) { _ in .empty } } - /// Whether the action execution was completed succesfully. + /// Whether the action execution was completed successfully. public var rex_completed: Signal { return events .filter { event in From ef93173ad76da2f623e39042493fc5fde044fcb7 Mon Sep 17 00:00:00 2001 From: Ilya Laryionau Date: Wed, 20 Apr 2016 15:33:55 +0300 Subject: [PATCH 0283/1028] add Action tests --- Rex.xcodeproj/project.pbxproj | 8 ++++ Tests/ActionTests.swift | 71 +++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 Tests/ActionTests.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index d2a7aeb041..9c28d26840 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -44,6 +44,9 @@ C7945F141CC1DFBE00DC9E37 /* UIViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7945F121CC1DFB400DC9E37 /* UIViewControllerTests.swift */; }; C7DCE2B41CB3C89A001217D8 /* UITextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DCE2B21CB3C872001217D8 /* UITextView.swift */; }; C7DCE2B71CB3C9D6001217D8 /* UITextViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DCE2B51CB3C9C1001217D8 /* UITextViewTests.swift */; }; + CC02C18A1CCA704E0025CC04 /* ActionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC02C1881CCA704C0025CC04 /* ActionTests.swift */; }; + 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, ); }; }; @@ -222,6 +225,7 @@ C7945F121CC1DFB400DC9E37 /* UIViewControllerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewControllerTests.swift; sourceTree = ""; }; C7DCE2B21CB3C872001217D8 /* UITextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextView.swift; sourceTree = ""; }; C7DCE2B51CB3C9C1001217D8 /* UITextViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextViewTests.swift; sourceTree = ""; }; + CC02C1881CCA704C0025CC04 /* ActionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionTests.swift; sourceTree = ""; }; D8003E921AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D8003E931AFEC3D400D7D3C5 /* Rex.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Rex.h; sourceTree = ""; }; D8003E9F1AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -371,6 +375,7 @@ D8003E9D1AFEC3D400D7D3C5 /* Tests */ = { isa = PBXGroup; children = ( + CC02C1881CCA704C0025CC04 /* ActionTests.swift */, D8A454081BD2772700C9E790 /* PropertyTests.swift */, D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */, D8003EC01AFED30100D7D3C5 /* SignalProducerTests.swift */, @@ -803,6 +808,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + CC02C18A1CCA704E0025CC04 /* ActionTests.swift in Sources */, D8F0974A1B17F5E1002E15BA /* NSObjectTests.swift in Sources */, D8003EC21AFED30F00D7D3C5 /* SignalTests.swift in Sources */, D8003EC31AFED30F00D7D3C5 /* SignalProducerTests.swift in Sources */, @@ -846,6 +852,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + CC02C18B1CCA704F0025CC04 /* ActionTests.swift in Sources */, 7DC325801CC6FD2100746D88 /* UITableViewHeaderFooterViewTests.swift in Sources */, 8289A2E11BD7EF1F0097FB60 /* UIImageViewTests.swift in Sources */, D8F0974B1B17F5E2002E15BA /* NSObjectTests.swift in Sources */, @@ -920,6 +927,7 @@ D8715DDE1C211637005F4191 /* SignalProducerTests.swift in Sources */, D8715DE11C211643005F4191 /* UIButtonTests.swift in Sources */, D8715DE21C211643005F4191 /* UIControlTests.swift in Sources */, + CC02C18C1CCA704F0025CC04 /* ActionTests.swift in Sources */, D8715DDF1C21163B005F4191 /* NSObjectTests.swift in Sources */, 7DC3257E1CC6FD1C00746D88 /* UITableViewCellTests.swift in Sources */, D8715DDC1C211637005F4191 /* PropertyTests.swift in Sources */, diff --git a/Tests/ActionTests.swift b/Tests/ActionTests.swift new file mode 100644 index 0000000000..7df33d6f84 --- /dev/null +++ b/Tests/ActionTests.swift @@ -0,0 +1,71 @@ +// +// ActionTests.swift +// Rex +// +// Created by Ilya Laryionau on 4/20/16. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +@testable import Rex +import ReactiveCocoa +import XCTest +import enum Result.NoError + +final class ActionTests: XCTestCase { + + enum TestError: ErrorType { + case Unknown + } + + func testStarted() { + let action = Action { .empty } + + var started = false + action + .rex_started + .observeNext { started = true } + + action + .apply() + .start() + + XCTAssertTrue(started) + } + + func testCompleted() { + let (producer, sink) = SignalProducer.buffer(Int.max) + let action = Action { producer } + + var completed = false + action + .rex_completed + .observeNext { completed = true } + + action + .apply() + .start() + + sink.sendNext(1) + XCTAssertFalse(completed) + + sink.sendCompleted() + XCTAssertTrue(completed) + } + + func testCompletedOnFailed() { + let (producer, sink) = SignalProducer.buffer(Int.max) + let action = Action { producer } + + var completed = false + action + .rex_completed + .observeNext { completed = true } + + action + .apply() + .start() + + sink.sendFailed(.Unknown) + XCTAssertFalse(completed) + } +} From ba976b0fe36aa80ebfa4b2eaf31b9127e2285a58 Mon Sep 17 00:00:00 2001 From: Ilya Laryionau Date: Wed, 20 Apr 2016 15:34:56 +0300 Subject: [PATCH 0284/1028] change the order of properties in source file --- Source/Action.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/Action.swift b/Source/Action.swift index a4f88af751..f664809ce5 100644 --- a/Source/Action.swift +++ b/Source/Action.swift @@ -14,6 +14,13 @@ extension Action { public static var rex_disabled: Action { return Action(enabledIf: ConstantProperty(false)) { _ in .empty } } + + /// Whether the action execution was started. + public var rex_started: Signal { + return self.executing.signal + .filter { $0 } + .map { _ in } + } /// Whether the action execution was completed successfully. public var rex_completed: Signal { @@ -27,13 +34,6 @@ extension Action { } .map { _ in } } - - /// Whether the action execution was started. - public var rex_started: Signal { - return self.executing.signal - .filter { $0 } - .map { _ in } - } } extension CocoaAction { From b3b6e296d70da0a3a1a54d241edace1524b01ddb Mon Sep 17 00:00:00 2001 From: Ilya Laryionau Date: Wed, 20 Apr 2016 20:24:17 +0300 Subject: [PATCH 0285/1028] rename sink to observer --- Tests/ActionTests.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Tests/ActionTests.swift b/Tests/ActionTests.swift index 7df33d6f84..abd4d1ccaa 100644 --- a/Tests/ActionTests.swift +++ b/Tests/ActionTests.swift @@ -33,7 +33,7 @@ final class ActionTests: XCTestCase { } func testCompleted() { - let (producer, sink) = SignalProducer.buffer(Int.max) + let (producer, observer) = SignalProducer.buffer(Int.max) let action = Action { producer } var completed = false @@ -45,15 +45,15 @@ final class ActionTests: XCTestCase { .apply() .start() - sink.sendNext(1) + observer.sendNext(1) XCTAssertFalse(completed) - sink.sendCompleted() + observer.sendCompleted() XCTAssertTrue(completed) } func testCompletedOnFailed() { - let (producer, sink) = SignalProducer.buffer(Int.max) + let (producer, observer) = SignalProducer.buffer(Int.max) let action = Action { producer } var completed = false @@ -65,7 +65,7 @@ final class ActionTests: XCTestCase { .apply() .start() - sink.sendFailed(.Unknown) + observer.sendFailed(.Unknown) XCTAssertFalse(completed) } } From d5bb2d1ddd62666c86ae9d765d84e0ba5eaffec8 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Sat, 23 Apr 2016 02:05:26 +0900 Subject: [PATCH 0286/1028] Add/Restore trailing newlines for consistency with RAC's codebase --- Rex.xcodeproj/project.pbxproj | 72 ++++++++++++------------- Source/Action.swift | 2 +- Source/AppKit/NSTextField.swift | 2 +- Source/Foundation/Association.swift | 2 +- Source/Foundation/NSData.swift | 2 +- Source/Foundation/NSObject.swift | 2 +- Source/Foundation/NSUserDefaults.swift | 2 +- Source/Property.swift | 2 +- Source/RACSignal.swift | 2 +- Source/SignalProducer.swift | 2 +- Source/UIKit/UIBarButtonItem.swift | 2 +- Source/UIKit/UIBarItem.swift | 2 +- Source/UIKit/UIButton.swift | 2 +- Source/UIKit/UIControl.swift | 2 +- Source/UIKit/UIDatePicker.swift | 2 +- Source/UIKit/UIImageView.swift | 2 +- Source/UIKit/UILabel.swift | 2 +- Source/UIKit/UISwitch.swift | 2 +- Source/UIKit/UITextField.swift | 2 +- Source/UIKit/UITextView.swift | 2 +- Source/UIKit/UIView.swift | 2 +- Source/UIKit/UIViewController.swift | 2 +- Tests/Foundation/NSObjectTests.swift | 2 +- Tests/PropertyTests.swift | 2 +- Tests/SignalProducerTests.swift | 2 +- Tests/SignalTests.swift | 2 +- Tests/UIKit/UIBarButtonItemTests.swift | 2 +- Tests/UIKit/UIButtonTests.swift | 2 +- Tests/UIKit/UIControlTests.swift | 2 +- Tests/UIKit/UIDatePickerTests.swift | 2 +- Tests/UIKit/UIImageViewTests.swift | 2 +- Tests/UIKit/UILabelTests.swift | 2 +- Tests/UIKit/UISwitchTests.swift | 2 +- Tests/UIKit/UITextFieldTests.swift | 2 +- Tests/UIKit/UITextViewTests.swift | 2 +- Tests/UIKit/UIViewControllerTests.swift | 2 +- Tests/UIKit/UIViewTests.swift | 2 +- 37 files changed, 72 insertions(+), 72 deletions(-) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 9c28d26840..ec2f1b1828 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -197,11 +197,11 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 4238D5951B4D5950008534C0 /* NSTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NSTextField.swift; path = AppKit/NSTextField.swift; sourceTree = ""; }; - 5173EBC51B625A2600C9B48E /* UIBarItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBarItem.swift; sourceTree = ""; }; - 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBarButtonItem.swift; sourceTree = ""; }; - 7D2AA99A1CB6EFEB008AB5C9 /* UISwitch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISwitch.swift; sourceTree = ""; }; - 7D2AA99C1CB6F275008AB5C9 /* UISwitchTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISwitchTests.swift; sourceTree = ""; }; + 4238D5951B4D5950008534C0 /* NSTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; name = NSTextField.swift; path = AppKit/NSTextField.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 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; }; + 7D2AA99A1CB6EFEB008AB5C9 /* UISwitch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UISwitch.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 7D2AA99C1CB6F275008AB5C9 /* UISwitchTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UISwitchTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 7DBD48F21CC8141D0077AD4F /* Reusable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reusable.swift; sourceTree = ""; }; 7DC325721CC6FCF100746D88 /* UITableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewCell.swift; sourceTree = ""; }; 7DC325731CC6FCF100746D88 /* UITableViewHeaderFooterView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewHeaderFooterView.swift; sourceTree = ""; }; @@ -209,43 +209,43 @@ 7DC325791CC6FD0A00746D88 /* UITableViewHeaderFooterViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewHeaderFooterViewTests.swift; sourceTree = ""; }; 7DCF5B311CC80D0E004AEE75 /* UICollectionReusableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UICollectionReusableView.swift; sourceTree = ""; }; 7DCF5B351CC80E8E004AEE75 /* UICollectionReusableViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UICollectionReusableViewTests.swift; sourceTree = ""; }; - 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageViewTests.swift; sourceTree = ""; }; - 8289A2E21BD7EF740097FB60 /* UIImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageView.swift; sourceTree = ""; }; - 8289A2E41BD7F6DD0097FB60 /* UIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = ""; }; - 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewTests.swift; sourceTree = ""; }; - 8295FD851B873081007C9000 /* UIControlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIControlTests.swift; sourceTree = ""; }; - 8295FD881B873490007C9000 /* UIButtonTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIButtonTests.swift; sourceTree = ""; }; - 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBarButtonItemTests.swift; sourceTree = ""; }; - 9DA915A31CA6301C003723B9 /* UIDatePicker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIDatePicker.swift; sourceTree = ""; }; - 9DA915A51CA63046003723B9 /* UIDatePickerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIDatePickerTests.swift; sourceTree = ""; }; - C72CF3E41CBF188A00E19897 /* RACSignal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RACSignal.swift; sourceTree = ""; }; - C7932E811C4B3EDB00086F3C /* UITextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextField.swift; sourceTree = ""; }; - C7932E851C4B420A00086F3C /* UITextFieldTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextFieldTests.swift; sourceTree = ""; }; - C7945F101CC192E800DC9E37 /* UIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = ""; }; - C7945F121CC1DFB400DC9E37 /* UIViewControllerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewControllerTests.swift; sourceTree = ""; }; - C7DCE2B21CB3C872001217D8 /* UITextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextView.swift; sourceTree = ""; }; - C7DCE2B51CB3C9C1001217D8 /* UITextViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextViewTests.swift; sourceTree = ""; }; + 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIImageViewTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 8289A2E21BD7EF740097FB60 /* UIImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIImageView.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 8289A2E41BD7F6DD0097FB60 /* UIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIView.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIViewTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 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; }; + 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; }; + 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; }; + C7DCE2B51CB3C9C1001217D8 /* UITextViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UITextViewTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; CC02C1881CCA704C0025CC04 /* ActionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionTests.swift; sourceTree = ""; }; D8003E921AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D8003E931AFEC3D400D7D3C5 /* Rex.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Rex.h; sourceTree = ""; }; D8003E9F1AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ReactiveCocoa.framework; sourceTree = ""; }; D8003EAE1AFEC68A00D7D3C5 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = ""; }; - D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalProducer.swift; sourceTree = ""; }; + D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SignalProducer.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; D8003EBC1AFED01000D7D3C5 /* Signal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Signal.swift; sourceTree = ""; }; - D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalTests.swift; sourceTree = ""; }; - D8003EC01AFED30100D7D3C5 /* SignalProducerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalProducerTests.swift; sourceTree = ""; }; + D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SignalTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + D8003EC01AFED30100D7D3C5 /* SignalProducerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SignalProducerTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 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; }; 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; path = Association.swift; sourceTree = ""; }; - D86FFBD41B34B0FE001A89B3 /* UIControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIControl.swift; sourceTree = ""; }; - D86FFBD71B34B242001A89B3 /* UILabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabel.swift; sourceTree = ""; }; - D86FFBD91B34B3F0001A89B3 /* Action.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Action.swift; sourceTree = ""; }; - D86FFBDC1B34B691001A89B3 /* UIButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIButton.swift; sourceTree = ""; }; + D86FFBD01B34AD6F001A89B3 /* Association.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Association.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + D86FFBD41B34B0FE001A89B3 /* UIControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIControl.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + D86FFBD71B34B242001A89B3 /* UILabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UILabel.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + D86FFBD91B34B3F0001A89B3 /* Action.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Action.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + D86FFBDC1B34B691001A89B3 /* UIButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIButton.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; D8715D941C210F97005F4191 /* Rex.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Rex.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D8715DA71C2110DA005F4191 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveCocoa.framework; path = watchOS/ReactiveCocoa.framework; sourceTree = ""; }; D8715DA81C2110DA005F4191 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = watchOS/Result.framework; sourceTree = ""; }; @@ -253,13 +253,13 @@ D8715DC21C211310005F4191 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveCocoa.framework; path = tvOS/ReactiveCocoa.framework; sourceTree = ""; }; D8715DC31C211310005F4191 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = tvOS/Result.framework; sourceTree = ""; }; D8715DD11C21160A005F4191 /* RexTests-tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RexTests-tvOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; - D8A454051BD26A1A00C9E790 /* Property.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Property.swift; sourceTree = ""; }; - D8A454081BD2772700C9E790 /* PropertyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PropertyTests.swift; sourceTree = ""; }; - D8F073141B861B3A0047D546 /* UILabelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabelTests.swift; sourceTree = ""; }; - D8F0973A1B17F2F7002E15BA /* NSData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSData.swift; sourceTree = ""; }; - D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSUserDefaults.swift; sourceTree = ""; }; - D8F097431B17F3C8002E15BA /* NSObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSObject.swift; sourceTree = ""; }; - D8F097471B17F5DD002E15BA /* NSObjectTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSObjectTests.swift; sourceTree = ""; }; + 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; }; + 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; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ diff --git a/Source/Action.swift b/Source/Action.swift index f664809ce5..96ebc16ba9 100644 --- a/Source/Action.swift +++ b/Source/Action.swift @@ -52,4 +52,4 @@ extension CocoaAction { public var rex_executingProducer: SignalProducer { return rex_producerForKeyPath("executing") } -} \ No newline at end of file +} diff --git a/Source/AppKit/NSTextField.swift b/Source/AppKit/NSTextField.swift index 2e665fe21f..cf14eee2df 100644 --- a/Source/AppKit/NSTextField.swift +++ b/Source/AppKit/NSTextField.swift @@ -19,4 +19,4 @@ extension NSTextField { (notification.object as! NSTextField).stringValue } } -} \ No newline at end of file +} diff --git a/Source/Foundation/Association.swift b/Source/Foundation/Association.swift index b8e8c0435d..0f71e3f62d 100644 --- a/Source/Foundation/Association.swift +++ b/Source/Foundation/Association.swift @@ -74,4 +74,4 @@ public func associatedObject(host: Host, key: Uns objc_setAssociatedObject(host, key, value, .OBJC_ASSOCIATION_RETAIN) } return value! -} \ No newline at end of file +} diff --git a/Source/Foundation/NSData.swift b/Source/Foundation/NSData.swift index d1d0d101d8..1055125995 100644 --- a/Source/Foundation/NSData.swift +++ b/Source/Foundation/NSData.swift @@ -22,4 +22,4 @@ extension NSData { } } } -} \ No newline at end of file +} diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index 5927fb25b7..c839ef1785 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -34,4 +34,4 @@ extension NSObject { .rac_willDeallocSignal() .rex_toTriggerSignal() } -} \ No newline at end of file +} diff --git a/Source/Foundation/NSUserDefaults.swift b/Source/Foundation/NSUserDefaults.swift index 053505b799..702dd932b7 100644 --- a/Source/Foundation/NSUserDefaults.swift +++ b/Source/Foundation/NSUserDefaults.swift @@ -35,4 +35,4 @@ extension NSUserDefaults { return false } } -} \ No newline at end of file +} diff --git a/Source/Property.swift b/Source/Property.swift index 6287b39c03..230d0ab6d7 100644 --- a/Source/Property.swift +++ b/Source/Property.swift @@ -146,4 +146,4 @@ public struct NotProperty: PropertyType { self.source = source self.invert = invert } -} \ No newline at end of file +} diff --git a/Source/RACSignal.swift b/Source/RACSignal.swift index 0e133fd8f8..8d01fa5678 100644 --- a/Source/RACSignal.swift +++ b/Source/RACSignal.swift @@ -34,4 +34,4 @@ extension RACSignal { .map { _ in () } .ignoreError() } -} \ No newline at end of file +} diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index 0f35d8d9e1..3dd3b5d03a 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -139,4 +139,4 @@ extension SignalProducerType where Value: SequenceType { public func uncollect() -> SignalProducer { return lift { $0.uncollect() } } -} \ No newline at end of file +} diff --git a/Source/UIKit/UIBarButtonItem.swift b/Source/UIKit/UIBarButtonItem.swift index 735ff4e8e8..d48b7b3fe4 100644 --- a/Source/UIKit/UIBarButtonItem.swift +++ b/Source/UIKit/UIBarButtonItem.swift @@ -33,4 +33,4 @@ extension UIBarButtonItem { } } -private var actionKey: UInt8 = 0 \ No newline at end of file +private var actionKey: UInt8 = 0 diff --git a/Source/UIKit/UIBarItem.swift b/Source/UIKit/UIBarItem.swift index 757f76d38e..999bd94b52 100644 --- a/Source/UIKit/UIBarItem.swift +++ b/Source/UIKit/UIBarItem.swift @@ -16,4 +16,4 @@ extension UIBarItem { } } -private var enabledKey: UInt8 = 0 \ No newline at end of file +private var enabledKey: UInt8 = 0 diff --git a/Source/UIKit/UIButton.swift b/Source/UIKit/UIButton.swift index 2fba0b5536..fcccda5742 100644 --- a/Source/UIKit/UIButton.swift +++ b/Source/UIKit/UIButton.swift @@ -41,4 +41,4 @@ extension UIButton { } private var pressedKey: UInt8 = 0 -private var titleKey: UInt8 = 0 \ No newline at end of file +private var titleKey: UInt8 = 0 diff --git a/Source/UIKit/UIControl.swift b/Source/UIKit/UIControl.swift index 7b9f8f5aa7..d9cbc20bbb 100644 --- a/Source/UIKit/UIControl.swift +++ b/Source/UIKit/UIControl.swift @@ -40,4 +40,4 @@ extension UIControl { private var enabledKey: UInt8 = 0 private var selectedKey: UInt8 = 0 -private var highlightedKey: UInt8 = 0 \ No newline at end of file +private var highlightedKey: UInt8 = 0 diff --git a/Source/UIKit/UIDatePicker.swift b/Source/UIKit/UIDatePicker.swift index ff3e975605..014223cf23 100644 --- a/Source/UIKit/UIDatePicker.swift +++ b/Source/UIKit/UIDatePicker.swift @@ -25,4 +25,4 @@ extension UIDatePicker { } -private var dateKey: UInt8 = 0 \ No newline at end of file +private var dateKey: UInt8 = 0 diff --git a/Source/UIKit/UIImageView.swift b/Source/UIKit/UIImageView.swift index 6f846f8248..db6d09ea77 100644 --- a/Source/UIKit/UIImageView.swift +++ b/Source/UIKit/UIImageView.swift @@ -22,4 +22,4 @@ extension UIImageView { } private var imageKey: UInt8 = 0 -private var highlightedImageKey: UInt8 = 0 \ No newline at end of file +private var highlightedImageKey: UInt8 = 0 diff --git a/Source/UIKit/UILabel.swift b/Source/UIKit/UILabel.swift index f24455701c..1ec0c2529f 100644 --- a/Source/UIKit/UILabel.swift +++ b/Source/UIKit/UILabel.swift @@ -27,4 +27,4 @@ extension UILabel { } private var attributedTextKey: UInt8 = 0 -private var textColorKey: UInt8 = 0 \ No newline at end of file +private var textColorKey: UInt8 = 0 diff --git a/Source/UIKit/UISwitch.swift b/Source/UIKit/UISwitch.swift index f657275084..89f0b9296e 100644 --- a/Source/UIKit/UISwitch.swift +++ b/Source/UIKit/UISwitch.swift @@ -24,4 +24,4 @@ extension UISwitch { } -private var onKey: UInt8 = 0 \ No newline at end of file +private var onKey: UInt8 = 0 diff --git a/Source/UIKit/UITextField.swift b/Source/UIKit/UITextField.swift index eebee49631..f73c4f4000 100644 --- a/Source/UIKit/UITextField.swift +++ b/Source/UIKit/UITextField.swift @@ -18,4 +18,4 @@ extension UITextField { .rac_notifications(UITextFieldTextDidChangeNotification, object: self) .filterMap { ($0.object as? UITextField)?.text } } -} \ No newline at end of file +} diff --git a/Source/UIKit/UITextView.swift b/Source/UIKit/UITextView.swift index 1d546f0219..95a9b197e6 100644 --- a/Source/UIKit/UITextView.swift +++ b/Source/UIKit/UITextView.swift @@ -18,4 +18,4 @@ extension UITextView { .rac_notifications(UITextViewTextDidChangeNotification, object: self) .filterMap { ($0.object as? UITextView)?.text } } -} \ No newline at end of file +} diff --git a/Source/UIKit/UIView.swift b/Source/UIKit/UIView.swift index 5a82315442..a7903394e1 100644 --- a/Source/UIKit/UIView.swift +++ b/Source/UIKit/UIView.swift @@ -22,4 +22,4 @@ extension UIView { } private var alphaKey: UInt8 = 0 -private var hiddenKey: UInt8 = 0 \ No newline at end of file +private var hiddenKey: UInt8 = 0 diff --git a/Source/UIKit/UIViewController.swift b/Source/UIKit/UIViewController.swift index 93dde4eddb..63a5435ae6 100644 --- a/Source/UIKit/UIViewController.swift +++ b/Source/UIKit/UIViewController.swift @@ -75,4 +75,4 @@ extension UIViewController { } } -private var dismissModally: UInt8 = 0 \ No newline at end of file +private var dismissModally: UInt8 = 0 diff --git a/Tests/Foundation/NSObjectTests.swift b/Tests/Foundation/NSObjectTests.swift index 8f5d3a2d44..c73d1635d1 100644 --- a/Tests/Foundation/NSObjectTests.swift +++ b/Tests/Foundation/NSObjectTests.swift @@ -66,4 +66,4 @@ final class NSObjectDeallocTests: XCTestCase { class Object: NSObject { dynamic var string = "foo" -} \ No newline at end of file +} diff --git a/Tests/PropertyTests.swift b/Tests/PropertyTests.swift index be45ba3741..ea32ccbe93 100644 --- a/Tests/PropertyTests.swift +++ b/Tests/PropertyTests.swift @@ -87,4 +87,4 @@ final class PropertyTests: XCTestCase { XCTAssertFalse(not.value) XCTAssertFalse(current!) } -} \ No newline at end of file +} diff --git a/Tests/SignalProducerTests.swift b/Tests/SignalProducerTests.swift index 8de703826f..36590b687a 100644 --- a/Tests/SignalProducerTests.swift +++ b/Tests/SignalProducerTests.swift @@ -158,4 +158,4 @@ final class SignalProducerTests: XCTestCase { XCTAssertEqual(value, 2) XCTAssertTrue(failed) } -} \ No newline at end of file +} diff --git a/Tests/SignalTests.swift b/Tests/SignalTests.swift index 05bdc91bbf..52a2cc6cbc 100644 --- a/Tests/SignalTests.swift +++ b/Tests/SignalTests.swift @@ -239,4 +239,4 @@ final class SignalTests: XCTestCase { enum TestError: ErrorType { case Default -} \ No newline at end of file +} diff --git a/Tests/UIKit/UIBarButtonItemTests.swift b/Tests/UIKit/UIBarButtonItemTests.swift index 99b3a5ba78..8b65c363c4 100644 --- a/Tests/UIKit/UIBarButtonItemTests.swift +++ b/Tests/UIKit/UIBarButtonItemTests.swift @@ -43,4 +43,4 @@ class UIBarButtonItemTests: XCTestCase { observer.sendNext(true) XCTAssertTrue(barButtonItem.enabled) } -} \ No newline at end of file +} diff --git a/Tests/UIKit/UIButtonTests.swift b/Tests/UIKit/UIButtonTests.swift index db80e318bd..b8d33f7036 100644 --- a/Tests/UIKit/UIButtonTests.swift +++ b/Tests/UIKit/UIButtonTests.swift @@ -95,4 +95,4 @@ class UIButtonTests: XCTestCase { XCTAssertTrue(passed.value) } -} \ No newline at end of file +} diff --git a/Tests/UIKit/UIControlTests.swift b/Tests/UIKit/UIControlTests.swift index ff716ad45a..b16bfe480d 100644 --- a/Tests/UIKit/UIControlTests.swift +++ b/Tests/UIKit/UIControlTests.swift @@ -104,4 +104,4 @@ class UIControlTests: XCTestCase { XCTAssertFalse(control.enabled) XCTAssertFalse(control.selected) } -} \ No newline at end of file +} diff --git a/Tests/UIKit/UIDatePickerTests.swift b/Tests/UIKit/UIDatePickerTests.swift index 6b7ca0ca9e..e15375f425 100644 --- a/Tests/UIKit/UIDatePickerTests.swift +++ b/Tests/UIKit/UIDatePickerTests.swift @@ -45,4 +45,4 @@ class UIDatePickerTests: XCTestCase { picker.userInteractionEnabled = true picker.sendActionsForControlEvents(.ValueChanged) } -} \ No newline at end of file +} diff --git a/Tests/UIKit/UIImageViewTests.swift b/Tests/UIKit/UIImageViewTests.swift index 096cfa440d..b632f512e0 100644 --- a/Tests/UIKit/UIImageViewTests.swift +++ b/Tests/UIKit/UIImageViewTests.swift @@ -69,4 +69,4 @@ class UIImageViewTests: XCTestCase { observer.sendNext(secondChange) XCTAssertEqual(imageView.highlightedImage, secondChange) } -} \ No newline at end of file +} diff --git a/Tests/UIKit/UILabelTests.swift b/Tests/UIKit/UILabelTests.swift index 641c3cd09a..8d10099fb8 100644 --- a/Tests/UIKit/UILabelTests.swift +++ b/Tests/UIKit/UILabelTests.swift @@ -83,4 +83,4 @@ class UILabelTests: XCTestCase { observer.sendNext(secondChange) XCTAssertEqual(label.textColor, secondChange) } -} \ No newline at end of file +} diff --git a/Tests/UIKit/UISwitchTests.swift b/Tests/UIKit/UISwitchTests.swift index cdeca9b425..ba9595681c 100644 --- a/Tests/UIKit/UISwitchTests.swift +++ b/Tests/UIKit/UISwitchTests.swift @@ -24,4 +24,4 @@ class UISwitchTests: XCTestCase { observer.sendNext(false) XCTAssertFalse(s.on) } -} \ No newline at end of file +} diff --git a/Tests/UIKit/UITextFieldTests.swift b/Tests/UIKit/UITextFieldTests.swift index 4745a6e75e..bed69d3345 100644 --- a/Tests/UIKit/UITextFieldTests.swift +++ b/Tests/UIKit/UITextFieldTests.swift @@ -26,4 +26,4 @@ class UITextFieldTests: XCTestCase { NSNotificationCenter.defaultCenter().postNotificationName(UITextFieldTextDidChangeNotification, object: textField) } -} \ No newline at end of file +} diff --git a/Tests/UIKit/UITextViewTests.swift b/Tests/UIKit/UITextViewTests.swift index 06aab6c25c..2ae4fc58d2 100644 --- a/Tests/UIKit/UITextViewTests.swift +++ b/Tests/UIKit/UITextViewTests.swift @@ -26,4 +26,4 @@ class UITextViewTests: XCTestCase { NSNotificationCenter.defaultCenter().postNotificationName(UITextViewTextDidChangeNotification, object: textView) } -} \ No newline at end of file +} diff --git a/Tests/UIKit/UIViewControllerTests.swift b/Tests/UIKit/UIViewControllerTests.swift index a5ef94e140..04bf211a63 100644 --- a/Tests/UIKit/UIViewControllerTests.swift +++ b/Tests/UIKit/UIViewControllerTests.swift @@ -109,4 +109,4 @@ class UIViewControllerTests: XCTestCase { viewController.dismissViewControllerAnimated(true, completion: nil) } -} \ No newline at end of file +} diff --git a/Tests/UIKit/UIViewTests.swift b/Tests/UIKit/UIViewTests.swift index defdca62ce..f67bc69121 100644 --- a/Tests/UIKit/UIViewTests.swift +++ b/Tests/UIKit/UIViewTests.swift @@ -64,4 +64,4 @@ class UIViewTests: XCTestCase { observer.sendNext(secondChange) XCTAssertEqualWithAccuracy(view.alpha, secondChange, accuracy: 0.01) } -} \ No newline at end of file +} From 50eb15e51bc19d672fe35ba2478902b2c9503510 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Sat, 23 Apr 2016 02:57:20 +0900 Subject: [PATCH 0287/1028] Minor refactorings for rex_started, rex_completed and filterMap --- Source/Action.swift | 10 ++++------ Source/Signal.swift | 23 +++++++++++++---------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/Source/Action.swift b/Source/Action.swift index 96ebc16ba9..75208d46f3 100644 --- a/Source/Action.swift +++ b/Source/Action.swift @@ -18,21 +18,19 @@ extension Action { /// Whether the action execution was started. public var rex_started: Signal { return self.executing.signal - .filter { $0 } - .map { _ in } + .filterMap { $0 ? () : nil } } /// Whether the action execution was completed successfully. public var rex_completed: Signal { return events - .filter { event in + .filterMap { event -> Void? in if case .Completed = event { - return true + return () } else { - return false + return nil } } - .map { _ in } } } diff --git a/Source/Signal.swift b/Source/Signal.swift index f6fa8c86e0..7ae00bfc10 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -16,17 +16,20 @@ extension SignalType { @warn_unused_result(message="Did you forget to call `observe` on the signal?") public func filterMap(transform: Value -> U?) -> Signal { return Signal { observer in - return self.observe(Observer(next: { value in - if let val = transform(value) { - observer.sendNext(val) + return self.observe { event in + switch event { + case let .Next(value): + if let mapped = transform(value) { + observer.sendNext(mapped) + } + case let .Failed(error): + observer.sendFailed(error) + case .Completed: + observer.sendCompleted() + case .Interrupted: + observer.sendInterrupted() } - }, failed: { error in - observer.sendFailed(error) - }, completed: { - observer.sendCompleted() - }, interrupted: { - observer.sendInterrupted() - })) + } } } From 38f3627896ea1de4d107d71aea5536dc93fb3192 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Tue, 26 Apr 2016 02:37:37 +0900 Subject: [PATCH 0288/1028] Refactor UIBarButtonItem.rex_action and UIButton.rex_pressed --- Source/UIKit/UIBarButtonItem.swift | 15 +++++++-------- Source/UIKit/UIButton.swift | 17 ++++++++--------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/Source/UIKit/UIBarButtonItem.swift b/Source/UIKit/UIBarButtonItem.swift index d48b7b3fe4..25a05c56c2 100644 --- a/Source/UIKit/UIBarButtonItem.swift +++ b/Source/UIKit/UIBarButtonItem.swift @@ -15,18 +15,17 @@ extension UIBarButtonItem { /// overwritten. 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) { [weak self] _ in + return associatedObject(self, key: &actionKey) { host in let initial = CocoaAction.rex_disabled let property = MutableProperty(initial) - property.producer.start(Observer(next: { next in - self?.target = next - self?.action = CocoaAction.selector - })) + property.producer + .startWithNext { [weak host] action in + host?.target = action + host?.action = CocoaAction.selector + } - if let strongSelf = self { - strongSelf.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/UIButton.swift b/Source/UIKit/UIButton.swift index fcccda5742..ea49aef9b5 100644 --- a/Source/UIKit/UIButton.swift +++ b/Source/UIKit/UIButton.swift @@ -15,22 +15,21 @@ extension UIButton { /// previous action is removed as a target. This also binds the enabled state of the /// action to the `rex_enabled` property on the button. public var rex_pressed: MutableProperty { - return associatedObject(self, key: &pressedKey, initial: { [weak self] _ in + return associatedObject(self, key: &pressedKey) { host in let initial = CocoaAction.rex_disabled let property = MutableProperty(initial) property.producer .combinePrevious(initial) - .start(Observer(next: { previous, next in - self?.removeTarget(previous, action: CocoaAction.selector, forControlEvents: .TouchUpInside) - self?.addTarget(next, action: CocoaAction.selector, forControlEvents: .TouchUpInside) - })) + .startWithNext { [weak host] previous, next in + host?.removeTarget(previous, action: CocoaAction.selector, forControlEvents: .TouchUpInside) + host?.addTarget(next, action: CocoaAction.selector, forControlEvents: .TouchUpInside) + } + + host.rex_enabled <~ property.producer.flatMap(.Latest) { $0.rex_enabledProducer } - if let strongSelf = self { - strongSelf.rex_enabled <~ property.producer.flatMap(.Latest) { $0.rex_enabledProducer } - } return property - }) + } } /// Wraps a button's `title` text in a bindable property. Note that this only applies From 651f84c5181701dbeae11eda337da316f0ef0736 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Tue, 26 Apr 2016 02:48:22 +0900 Subject: [PATCH 0289/1028] Replace `sink` with `observer` --- Source/SignalProducer.swift | 6 +-- Tests/SignalProducerTests.swift | 10 ++--- Tests/SignalTests.swift | 74 ++++++++++++++++----------------- 3 files changed, 45 insertions(+), 45 deletions(-) diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index 3dd3b5d03a..0f9d83b9cf 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -28,11 +28,11 @@ extension SignalProducerType { lock.lock() var group = groups[key] if group == nil { - let (producer, sink) = SignalProducer.buffer(Int.max) + let (producer, innerObserver) = SignalProducer.buffer(Int.max) observer.sendNext(key, producer) - groups[key] = sink - group = sink + groups[key] = innerObserver + group = innerObserver } lock.unlock() diff --git a/Tests/SignalProducerTests.swift b/Tests/SignalProducerTests.swift index 36590b687a..94a672d2e2 100644 --- a/Tests/SignalProducerTests.swift +++ b/Tests/SignalProducerTests.swift @@ -14,7 +14,7 @@ import enum Result.NoError final class SignalProducerTests: XCTestCase { func testGroupBy() { - let (producer, sink) = SignalProducer.buffer(Int.max) + let (producer, observer) = SignalProducer.buffer(Int.max) var evens: [Int] = [] var odds: [Int] = [] let disposable = CompositeDisposable() @@ -35,21 +35,21 @@ final class SignalProducerTests: XCTestCase { interrupted = true })) - sink.sendNext(1) + observer.sendNext(1) XCTAssert(evens == []) XCTAssert(odds == [1]) - sink.sendNext(2) + observer.sendNext(2) XCTAssert(evens == [2]) XCTAssert(odds == [1]) - sink.sendNext(3) + observer.sendNext(3) XCTAssert(evens == [2]) XCTAssert(odds == [1, 3]) disposable.dispose() - sink.sendNext(1) + observer.sendNext(1) XCTAssert(interrupted) XCTAssertFalse(completed) } diff --git a/Tests/SignalTests.swift b/Tests/SignalTests.swift index 52a2cc6cbc..3a1d7968f2 100644 --- a/Tests/SignalTests.swift +++ b/Tests/SignalTests.swift @@ -14,7 +14,7 @@ import enum Result.NoError final class SignalTests: XCTestCase { func testFilterMap() { - let (signal, sink) = Signal.pipe() + let (signal, observer) = Signal.pipe() var values: [String] = [] signal @@ -23,52 +23,52 @@ final class SignalTests: XCTestCase { } .observeNext { values.append($0) } - sink.sendNext(1) + observer.sendNext(1) XCTAssert(values == []) - sink.sendNext(2) + observer.sendNext(2) XCTAssert(values == ["2"]) - sink.sendNext(3) + observer.sendNext(3) XCTAssert(values == ["2"]) - sink.sendNext(6) + observer.sendNext(6) XCTAssert(values == ["2", "6"]) } func testIgnoreErrorCompletion() { - let (signal, sink) = Signal.pipe() + let (signal, observer) = Signal.pipe() var completed = false signal .ignoreError() .observeCompleted { completed = true } - sink.sendNext(1) + observer.sendNext(1) XCTAssertFalse(completed) - sink.sendFailed(.Default) + observer.sendFailed(.Default) XCTAssertTrue(completed) } func testIgnoreErrorInterruption() { - let (signal, sink) = Signal.pipe() + let (signal, observer) = Signal.pipe() var interrupted = false signal .ignoreError(replacement: .Interrupted) .observeInterrupted { interrupted = true } - sink.sendNext(1) + observer.sendNext(1) XCTAssertFalse(interrupted) - sink.sendFailed(.Default) + observer.sendFailed(.Default) XCTAssertTrue(interrupted) } func testTimeoutAfterTerminating() { let scheduler = TestScheduler() - let (signal, sink) = Signal.pipe() + let (signal, observer) = Signal.pipe() var interrupted = false var completed = false @@ -79,7 +79,7 @@ final class SignalTests: XCTestCase { interrupted: { interrupted = true } )) - scheduler.scheduleAfter(1) { sink.sendCompleted() } + scheduler.scheduleAfter(1) { observer.sendCompleted() } XCTAssertFalse(interrupted) XCTAssertFalse(completed) @@ -91,7 +91,7 @@ final class SignalTests: XCTestCase { func testTimeoutAfterTimingOut() { let scheduler = TestScheduler() - let (signal, sink) = Signal.pipe() + let (signal, observer) = Signal.pipe() var interrupted = false var completed = false @@ -102,7 +102,7 @@ final class SignalTests: XCTestCase { interrupted: { interrupted = true } )) - scheduler.scheduleAfter(3) { sink.sendCompleted() } + scheduler.scheduleAfter(3) { observer.sendCompleted() } XCTAssertFalse(interrupted) XCTAssertFalse(completed) @@ -113,44 +113,44 @@ final class SignalTests: XCTestCase { } func testUncollect() { - let (signal, sink) = Signal<[Int], NoError>.pipe() + let (signal, observer) = Signal<[Int], NoError>.pipe() var values: [Int] = [] signal .uncollect() .observeNext { values.append($0) } - sink.sendNext([]) + observer.sendNext([]) XCTAssert(values.isEmpty) - sink.sendNext([1]) + observer.sendNext([1]) XCTAssert(values == [1]) - sink.sendNext([2, 3]) + observer.sendNext([2, 3]) XCTAssert(values == [1, 2, 3]) } func testDebounceValues() { let scheduler = TestScheduler() - let (signal, sink) = Signal.pipe() + let (signal, observer) = Signal.pipe() var value = -1 signal .debounce(1, onScheduler: scheduler) .observeNext { value = $0 } - scheduler.schedule { sink.sendNext(1) } + scheduler.schedule { observer.sendNext(1) } scheduler.advance() XCTAssertEqual(value, -1) scheduler.advanceByInterval(1) XCTAssertEqual(value, 1) - scheduler.schedule { sink.sendNext(2) } + scheduler.schedule { observer.sendNext(2) } scheduler.advance() XCTAssertEqual(value, 1) - scheduler.schedule { sink.sendNext(3) } + scheduler.schedule { observer.sendNext(3) } scheduler.advance() XCTAssertEqual(value, 1) @@ -160,7 +160,7 @@ final class SignalTests: XCTestCase { func testDebounceFailure() { let scheduler = TestScheduler() - let (signal, sink) = Signal.pipe() + let (signal, observer) = Signal.pipe() var value = -1 var failed = false @@ -171,11 +171,11 @@ final class SignalTests: XCTestCase { failed: { _ in failed = true } )) - scheduler.schedule { sink.sendNext(1) } + scheduler.schedule { observer.sendNext(1) } scheduler.advance() XCTAssertEqual(value, -1) - scheduler.schedule { sink.sendFailed(.Default) } + scheduler.schedule { observer.sendFailed(.Default) } scheduler.advance() XCTAssertTrue(failed) XCTAssertEqual(value, -1) @@ -183,38 +183,38 @@ final class SignalTests: XCTestCase { func testMuteForValues() { let scheduler = TestScheduler() - let (signal, sink) = Signal.pipe() + let (signal, observer) = Signal.pipe() var value = -1 signal .muteFor(1, clock: scheduler) .observeNext { value = $0 } - scheduler.schedule { sink.sendNext(1) } + scheduler.schedule { observer.sendNext(1) } scheduler.advance() XCTAssertEqual(value, 1) - scheduler.schedule { sink.sendNext(2) } + scheduler.schedule { observer.sendNext(2) } scheduler.advance() XCTAssertEqual(value, 1) - scheduler.schedule { sink.sendNext(3) } - scheduler.schedule { sink.sendNext(4) } + scheduler.schedule { observer.sendNext(3) } + scheduler.schedule { observer.sendNext(4) } scheduler.advance() XCTAssertEqual(value, 1) scheduler.advanceByInterval(1) XCTAssertEqual(value, 1) - scheduler.schedule { sink.sendNext(5) } - scheduler.schedule { sink.sendNext(6) } + scheduler.schedule { observer.sendNext(5) } + scheduler.schedule { observer.sendNext(6) } scheduler.advance() XCTAssertEqual(value, 5) } func testMuteForFailure() { let scheduler = TestScheduler() - let (signal, sink) = Signal.pipe() + let (signal, observer) = Signal.pipe() var value = -1 var failed = false @@ -225,12 +225,12 @@ final class SignalTests: XCTestCase { failed: { _ in failed = true } )) - scheduler.schedule { sink.sendNext(1) } + scheduler.schedule { observer.sendNext(1) } scheduler.advance() XCTAssertEqual(value, 1) - scheduler.schedule { sink.sendNext(2) } - scheduler.schedule { sink.sendFailed(.Default) } + scheduler.schedule { observer.sendNext(2) } + scheduler.schedule { observer.sendFailed(.Default) } scheduler.advance() XCTAssertTrue(failed) XCTAssertEqual(value, 1) From 47f4a81b7d6ab4981a519c6736d954380defe54f Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Tue, 26 Apr 2016 16:18:28 +0900 Subject: [PATCH 0290/1028] Replace some Observer use cases with start / startWithNext --- Source/SignalProducer.swift | 57 +++++++++++++++------------- Tests/Foundation/NSObjectTests.swift | 2 +- Tests/SignalProducerTests.swift | 4 +- 3 files changed, 33 insertions(+), 30 deletions(-) diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index 0f9d83b9cf..f94c3e8b7d 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -22,34 +22,37 @@ extension SignalProducerType { let lock = NSRecursiveLock() lock.name = "me.neilpa.rex.groupBy" - self.start(Observer(next: { value in - let key = grouping(value) - - lock.lock() - var group = groups[key] - if group == nil { - let (producer, innerObserver) = SignalProducer.buffer(Int.max) - observer.sendNext(key, producer) - - groups[key] = innerObserver - group = innerObserver + self.start { event in + switch event { + case let .Next(value): + let key = grouping(value) + + lock.lock() + var group = groups[key] + if group == nil { + let (producer, innerObserver) = SignalProducer.buffer(Int.max) + observer.sendNext(key, producer) + + groups[key] = innerObserver + group = innerObserver + } + lock.unlock() + + group!.sendNext(value) + + case let .Failed(error): + observer.sendFailed(error) + groups.values.forEach { $0.sendFailed(error) } + + case .Completed: + observer.sendCompleted() + groups.values.forEach { $0.sendCompleted() } + + case .Interrupted: + observer.sendInterrupted() + groups.values.forEach { $0.sendInterrupted() } } - lock.unlock() - - group!.sendNext(value) - - }, failed: { error in - observer.sendFailed(error) - groups.values.forEach { $0.sendFailed(error) } - - }, completed: { _ in - observer.sendCompleted() - groups.values.forEach { $0.sendCompleted() } - - }, interrupted: { _ in - observer.sendInterrupted() - groups.values.forEach { $0.sendInterrupted() } - })) + } } } diff --git a/Tests/Foundation/NSObjectTests.swift b/Tests/Foundation/NSObjectTests.swift index c73d1635d1..5ffc070359 100644 --- a/Tests/Foundation/NSObjectTests.swift +++ b/Tests/Foundation/NSObjectTests.swift @@ -16,7 +16,7 @@ final class NSObjectTests: XCTestCase { let object = Object() var value: String = "" - object.rex_producerForKeyPath("string").start(Observer(next: { value = $0 })) + object.rex_producerForKeyPath("string").startWithNext { value = $0 } XCTAssertEqual(value, "foo") object.string = "bar" diff --git a/Tests/SignalProducerTests.swift b/Tests/SignalProducerTests.swift index 94a672d2e2..35ef801361 100644 --- a/Tests/SignalProducerTests.swift +++ b/Tests/SignalProducerTests.swift @@ -25,9 +25,9 @@ final class SignalProducerTests: XCTestCase { .groupBy { $0 % 2 == 0 } .start(Observer(next: { key, group in if key { - group.start(Observer(next: { evens.append($0) })) + group.startWithNext { evens.append($0) } } else { - group.start(Observer(next: { odds.append($0) })) + group.startWithNext { odds.append($0) } } },completed: { completed = true From a9b829e3070d9680a96f6ddfc9f252068ff537a1 Mon Sep 17 00:00:00 2001 From: eofs Date: Wed, 4 May 2016 08:48:38 +0300 Subject: [PATCH 0291/1028] Added rex_progress to UIProgressView --- Rex.xcodeproj/project.pbxproj | 16 ++++++++-- Source/UIKit/UIProgressView.swift | 19 +++++++++++ Tests/UIKit/UIProgressViewTests.swift | 45 +++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 Source/UIKit/UIProgressView.swift create mode 100644 Tests/UIKit/UIProgressViewTests.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index ec2f1b1828..b208e35646 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -134,6 +134,10 @@ D8F097451B17F3C8002E15BA /* NSObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097431B17F3C8002E15BA /* NSObject.swift */; }; D8F0974A1B17F5E1002E15BA /* NSObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097471B17F5DD002E15BA /* NSObjectTests.swift */; }; D8F0974B1B17F5E2002E15BA /* NSObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097471B17F5DD002E15BA /* NSObjectTests.swift */; }; + E6933BEA1CD9C335006F7CE7 /* UIProgressViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6933BE81CD9C1F1006F7CE7 /* UIProgressViewTests.swift */; }; + E6933BEB1CD9C363006F7CE7 /* UIProgressViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6933BE81CD9C1F1006F7CE7 /* UIProgressViewTests.swift */; }; + E6933BEC1CD9C37D006F7CE7 /* UIProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6933BE61CD9C0B2006F7CE7 /* UIProgressView.swift */; }; + E6933BED1CD9C37D006F7CE7 /* UIProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6933BE61CD9C0B2006F7CE7 /* UIProgressView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -260,6 +264,8 @@ D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = NSUserDefaults.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; D8F097431B17F3C8002E15BA /* NSObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = NSObject.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; D8F097471B17F5DD002E15BA /* NSObjectTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = NSObjectTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + E6933BE61CD9C0B2006F7CE7 /* UIProgressView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIProgressView.swift; sourceTree = ""; }; + E6933BE81CD9C1F1006F7CE7 /* UIProgressViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIProgressViewTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -439,6 +445,7 @@ 9DA915A31CA6301C003723B9 /* UIDatePicker.swift */, 8289A2E21BD7EF740097FB60 /* UIImageView.swift */, D86FFBD71B34B242001A89B3 /* UILabel.swift */, + E6933BE61CD9C0B2006F7CE7 /* UIProgressView.swift */, 7D2AA99A1CB6EFEB008AB5C9 /* UISwitch.swift */, 7DC325721CC6FCF100746D88 /* UITableViewCell.swift */, 7DC325731CC6FCF100746D88 /* UITableViewHeaderFooterView.swift */, @@ -476,18 +483,19 @@ children = ( 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */, 8295FD881B873490007C9000 /* UIButtonTests.swift */, - 8295FD851B873081007C9000 /* UIControlTests.swift */, 7DCF5B351CC80E8E004AEE75 /* UICollectionReusableViewTests.swift */, + 8295FD851B873081007C9000 /* UIControlTests.swift */, 9DA915A51CA63046003723B9 /* UIDatePickerTests.swift */, 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */, D8F073141B861B3A0047D546 /* UILabelTests.swift */, + E6933BE81CD9C1F1006F7CE7 /* UIProgressViewTests.swift */, 7D2AA99C1CB6F275008AB5C9 /* UISwitchTests.swift */, 7DC325781CC6FD0A00746D88 /* UITableViewCellTests.swift */, 7DC325791CC6FD0A00746D88 /* UITableViewHeaderFooterViewTests.swift */, C7932E851C4B420A00086F3C /* UITextFieldTests.swift */, C7DCE2B51CB3C9C1001217D8 /* UITextViewTests.swift */, - 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */, C7945F121CC1DFB400DC9E37 /* UIViewControllerTests.swift */, + 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */, ); path = UIKit; sourceTree = ""; @@ -839,6 +847,7 @@ D8E4A6201B7BBB1600EAD8A8 /* UIBarButtonItem.swift in Sources */, D8F097451B17F3C8002E15BA /* NSObject.swift in Sources */, C7945F111CC192E800DC9E37 /* UIViewController.swift in Sources */, + E6933BEC1CD9C37D006F7CE7 /* UIProgressView.swift in Sources */, D834572E1AFEE45B0070616A /* SignalProducer.swift in Sources */, C7DCE2B41CB3C89A001217D8 /* UITextView.swift in Sources */, 8289A2E51BD7F6DD0097FB60 /* UIView.swift in Sources */, @@ -852,6 +861,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + E6933BEA1CD9C335006F7CE7 /* UIProgressViewTests.swift in Sources */, CC02C18B1CCA704F0025CC04 /* ActionTests.swift in Sources */, 7DC325801CC6FD2100746D88 /* UITableViewHeaderFooterViewTests.swift in Sources */, 8289A2E11BD7EF1F0097FB60 /* UIImageViewTests.swift in Sources */, @@ -909,6 +919,7 @@ 7DC325751CC6FCF100746D88 /* UITableViewCell.swift in Sources */, D8715DBC1C2112D1005F4191 /* Signal.swift in Sources */, C72CF3E71CBF188A00E19897 /* RACSignal.swift in Sources */, + E6933BED1CD9C37D006F7CE7 /* UIProgressView.swift in Sources */, D8715DBF1C2112D6005F4191 /* NSData.swift in Sources */, D8715DCC1C211553005F4191 /* UIView.swift in Sources */, D8715DBA1C2112D1005F4191 /* Action.swift in Sources */, @@ -922,6 +933,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + E6933BEB1CD9C363006F7CE7 /* UIProgressViewTests.swift in Sources */, D8715DE51C211643005F4191 /* UIViewTests.swift in Sources */, 7DCF5B371CC80E8E004AEE75 /* UICollectionReusableViewTests.swift in Sources */, D8715DDE1C211637005F4191 /* SignalProducerTests.swift in Sources */, diff --git a/Source/UIKit/UIProgressView.swift b/Source/UIKit/UIProgressView.swift new file mode 100644 index 0000000000..52119d4ad4 --- /dev/null +++ b/Source/UIKit/UIProgressView.swift @@ -0,0 +1,19 @@ +// +// UIProgressView.swift +// Rex +// +// Created by Tomi Pajunen on 04/05/16. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa +import UIKit + +extension UIProgressView { + /// Wraps a progressView's `progress` value in a bindable property. + public var rex_progress: MutableProperty { + return associatedProperty(self, key: &progressKey, initial: { $0.progress }, setter: { $0.progress = $1 }) + } +} + +private var progressKey: UInt8 = 0 \ No newline at end of file diff --git a/Tests/UIKit/UIProgressViewTests.swift b/Tests/UIKit/UIProgressViewTests.swift new file mode 100644 index 0000000000..4fbe69575e --- /dev/null +++ b/Tests/UIKit/UIProgressViewTests.swift @@ -0,0 +1,45 @@ +// +// UIProgressViewTests.swift +// Rex +// +// Created by Tomi Pajunen on 04/05/16. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa +import UIKit +import XCTest +import enum Result.NoError + +class UIProgressViewTests: XCTestCase { + weak var _progressView: UIProgressView? + + override func tearDown() { + XCTAssert(_progressView == nil, "Retain cycle detected in UIProgressView properties") + super.tearDown() + } + + func testProgressPropertyDoesntCreateRetainCycle() { + let progressView = UIProgressView(frame: CGRectZero) + _progressView = progressView + + progressView.rex_progress <~ SignalProducer(value: 0.5) + XCTAssert(_progressView?.progress == 0.5) + } + + func testProgressProperty() { + let firstChange: Float = 0.5 + let secondChange: Float = 0.0 + + let progressView = UIProgressView(frame: CGRectZero) + progressView.progress = 1.0 + + let (pipeSignal, observer) = Signal.pipe() + progressView.rex_progress <~ SignalProducer(signal: pipeSignal) + + observer.sendNext(firstChange) + XCTAssertEqual(progressView.progress, firstChange) + observer.sendNext(secondChange) + XCTAssertEqual(progressView.progress, secondChange) + } +} From 658ff6afbb1622ba408e438f717b46d99d51a230 Mon Sep 17 00:00:00 2001 From: Andrew Ware Date: Wed, 8 Jun 2016 04:53:05 -0400 Subject: [PATCH 0292/1028] Add userInteractionEnabled to UITableViewCell (#127) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add userInteractionEnabled to UITableViewCell This allows the binding of a Bool to the cell's userInteractionEnabled attribute. * Move code into correct file * Remove UITableViewCell.swift file from root dir * Revert UITableViewCell.swift to original * Add userInteractionEnabled to UIView * Update comments in UIView.swift sorry for the commit spamming 😁 * Add testUserInteractionEnabledProperty --- Source/UIKit/UITableViewCell.swift | 2 ++ Source/UIKit/UIView.swift | 7 +++++++ Tests/UIKit/UIViewTests.swift | 13 +++++++++++++ 3 files changed, 22 insertions(+) diff --git a/Source/UIKit/UITableViewCell.swift b/Source/UIKit/UITableViewCell.swift index d59c372722..122df0153f 100644 --- a/Source/UIKit/UITableViewCell.swift +++ b/Source/UIKit/UITableViewCell.swift @@ -3,9 +3,11 @@ // Rex // // Created by David Rodrigues on 19/04/16. +// modified by Andrew Ware on 6/7/16. // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveCocoa import UIKit extension UITableViewCell: Reusable {} diff --git a/Source/UIKit/UIView.swift b/Source/UIKit/UIView.swift index a7903394e1..f0a67f749d 100644 --- a/Source/UIKit/UIView.swift +++ b/Source/UIKit/UIView.swift @@ -19,7 +19,14 @@ extension UIView { public var rex_hidden: MutableProperty { return associatedProperty(self, key: &hiddenKey, initial: { $0.hidden }, setter: { $0.hidden = $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 }) + } } private var alphaKey: UInt8 = 0 private var hiddenKey: UInt8 = 0 +private var userInteractionEnabledKey: UInt8 = 0 diff --git a/Tests/UIKit/UIViewTests.swift b/Tests/UIKit/UIViewTests.swift index f67bc69121..acd51c3d63 100644 --- a/Tests/UIKit/UIViewTests.swift +++ b/Tests/UIKit/UIViewTests.swift @@ -64,4 +64,17 @@ class UIViewTests: XCTestCase { observer.sendNext(secondChange) XCTAssertEqualWithAccuracy(view.alpha, secondChange, accuracy: 0.01) } + + func testUserInteractionEnabledProperty() { + let view = UIView(frame: CGRectZero) + view.userInteractionEnabled = true + + let (pipeSignal, observer) = Signal.pipe() + view.rex_userInteractionEnabled <~ SignalProducer(signal: pipeSignal) + + observer.sendNext(true) + XCTAssertTrue(view.userInteractionEnabled) + observer.sendNext(false) + XCTAssertFalse(view.userInteractionEnabled) + } } From 8d556bf4280ec9a0761abf4c9b5c1f07202a6520 Mon Sep 17 00:00:00 2001 From: Rui Peres Date: Wed, 8 Jun 2016 09:53:19 +0100 Subject: [PATCH 0293/1028] bump RAC to 4.2 (#124) --- Cartfile | 2 +- Cartfile.resolved | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cartfile b/Cartfile index d637b9a8d4..a4583d1085 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveCocoa" ~> 4.1 +github "ReactiveCocoa/ReactiveCocoa" ~> 4.2 diff --git a/Cartfile.resolved b/Cartfile.resolved index 4726855e02..a82c40d165 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ github "antitypical/Result" "2.0.0" -github "ReactiveCocoa/ReactiveCocoa" "v4.1.0" +github "ReactiveCocoa/ReactiveCocoa" "v4.2.0" From 489b3dd2576a24738c90b9b8f78a9b9f0c14c08e Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Tue, 7 Jun 2016 20:16:18 +0200 Subject: [PATCH 0294/1028] Implement `rex_selectedSegmentIndex` for UISegmentedControl --- Rex.podspec | 2 +- Rex.xcodeproj/project.pbxproj | 8 +++++++ Source/UIKit/UISegmentedControl.swift | 22 +++++++++++++++++ Tests/UIKit/UISegmentedControlTests.swift | 29 +++++++++++++++++++++++ 4 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 Source/UIKit/UISegmentedControl.swift create mode 100644 Tests/UIKit/UISegmentedControlTests.swift diff --git a/Rex.podspec b/Rex.podspec index 5020b7c36f..917d3e6e9a 100644 --- a/Rex.podspec +++ b/Rex.podspec @@ -26,7 +26,7 @@ Pod::Spec.new do |s| s.source_files = 'Source/**/*.swift' s.ios.exclude_files = 'Source/AppKit/*' - s.tvos.exclude_files = 'Source/AppKit/*', 'Source/UIKit/UIDatePicker.swift', 'Source/UIKit/UISwitch.swift' + s.tvos.exclude_files = 'Source/AppKit/*', 'Source/UIKit/UIDatePicker.swift', 'Source/UIKit/UISwitch.swift', 'Source/UIKit/UISegmentedControl.swift' s.watchos.exclude_files = 'Source/AppKit/*', 'Source/UIKit/*' s.osx.exclude_files = 'Source/UIKit/*' diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index b208e35646..42d3c4bbba 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -8,6 +8,8 @@ /* Begin PBXBuildFile section */ 4238D5961B4D5950008534C0 /* NSTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4238D5951B4D5950008534C0 /* NSTextField.swift */; }; + 5B7F81E31D0842AD0014B06D /* UISegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1C882D1D0715CE000B888F /* UISegmentedControl.swift */; }; + 5B7F81E41D0842B50014B06D /* UISegmentedControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1C882F1D071639000B888F /* UISegmentedControlTests.swift */; }; 7D2AA99B1CB6EFEB008AB5C9 /* UISwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D2AA99A1CB6EFEB008AB5C9 /* UISwitch.swift */; }; 7D2AA99D1CB6F275008AB5C9 /* UISwitchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D2AA99C1CB6F275008AB5C9 /* UISwitchTests.swift */; }; 7DBD48F31CC8141D0077AD4F /* Reusable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DBD48F21CC8141D0077AD4F /* Reusable.swift */; }; @@ -204,6 +206,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; }; 5173EBC51B625A2600C9B48E /* UIBarItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIBarItem.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIBarButtonItem.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 5B1C882D1D0715CE000B888F /* UISegmentedControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISegmentedControl.swift; sourceTree = ""; }; + 5B1C882F1D071639000B888F /* UISegmentedControlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISegmentedControlTests.swift; sourceTree = ""; }; 7D2AA99A1CB6EFEB008AB5C9 /* UISwitch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UISwitch.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 7D2AA99C1CB6F275008AB5C9 /* UISwitchTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UISwitchTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 7DBD48F21CC8141D0077AD4F /* Reusable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reusable.swift; sourceTree = ""; }; @@ -447,6 +451,7 @@ D86FFBD71B34B242001A89B3 /* UILabel.swift */, E6933BE61CD9C0B2006F7CE7 /* UIProgressView.swift */, 7D2AA99A1CB6EFEB008AB5C9 /* UISwitch.swift */, + 5B1C882D1D0715CE000B888F /* UISegmentedControl.swift */, 7DC325721CC6FCF100746D88 /* UITableViewCell.swift */, 7DC325731CC6FCF100746D88 /* UITableViewHeaderFooterView.swift */, C7932E811C4B3EDB00086F3C /* UITextField.swift */, @@ -490,6 +495,7 @@ D8F073141B861B3A0047D546 /* UILabelTests.swift */, E6933BE81CD9C1F1006F7CE7 /* UIProgressViewTests.swift */, 7D2AA99C1CB6F275008AB5C9 /* UISwitchTests.swift */, + 5B1C882F1D071639000B888F /* UISegmentedControlTests.swift */, 7DC325781CC6FD0A00746D88 /* UITableViewCellTests.swift */, 7DC325791CC6FD0A00746D88 /* UITableViewHeaderFooterViewTests.swift */, C7932E851C4B420A00086F3C /* UITextFieldTests.swift */, @@ -836,6 +842,7 @@ C7932E831C4B3F3000086F3C /* UITextField.swift in Sources */, 7DC325761CC6FCF100746D88 /* UITableViewHeaderFooterView.swift in Sources */, 7DC325741CC6FCF100746D88 /* UITableViewCell.swift in Sources */, + 5B7F81E31D0842AD0014B06D /* UISegmentedControl.swift in Sources */, 8289A2E31BD7EF740097FB60 /* UIImageView.swift in Sources */, D8F0973E1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, D834572D1AFEE45B0070616A /* Signal.swift in Sources */, @@ -881,6 +888,7 @@ 8295FD8D1B87374A007C9000 /* UIBarButtonItemTests.swift in Sources */, 8295FD871B87309F007C9000 /* UIControlTests.swift in Sources */, C7DCE2B71CB3C9D6001217D8 /* UITextViewTests.swift in Sources */, + 5B7F81E41D0842B50014B06D /* UISegmentedControlTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Source/UIKit/UISegmentedControl.swift b/Source/UIKit/UISegmentedControl.swift new file mode 100644 index 0000000000..465c76e16d --- /dev/null +++ b/Source/UIKit/UISegmentedControl.swift @@ -0,0 +1,22 @@ +// +// UISegmentedControl.swift +// Rex +// +// Created by Markus Chmelar on 07/06/16. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa +import UIKit + +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) + .filterMap { ($0 as? UISegmentedControl)?.selectedSegmentIndex } + return property + } +} + +private var selectedSegmentIndexKey: UInt8 = 0 diff --git a/Tests/UIKit/UISegmentedControlTests.swift b/Tests/UIKit/UISegmentedControlTests.swift new file mode 100644 index 0000000000..d7c7bfa7b1 --- /dev/null +++ b/Tests/UIKit/UISegmentedControlTests.swift @@ -0,0 +1,29 @@ +// +// UISegmentedControlTests.swift +// Rex +// +// Created by Markus Chmelar on 07/06/16. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import XCTest +import ReactiveCocoa +import Result + +class UISegmentedControlTests: XCTestCase { + + func testSelectedSegmentIndexProperty() { + let s = UISegmentedControl(items: ["0", "1", "2"]) + s.selectedSegmentIndex = UISegmentedControlNoSegment + XCTAssertEqual(s.numberOfSegments, 3) + + let (pipeSignal, observer) = Signal.pipe() + s.rex_selectedSegmentIndex <~ SignalProducer(signal: pipeSignal) + + XCTAssertEqual(s.selectedSegmentIndex, UISegmentedControlNoSegment) + observer.sendNext(1) + XCTAssertEqual(s.selectedSegmentIndex, 1) + observer.sendNext(2) + XCTAssertEqual(s.selectedSegmentIndex, 2) + } +} From 5f6a5270555cd0cd166600e1279ce10f1a0b3c2b Mon Sep 17 00:00:00 2001 From: Rui Peres Date: Wed, 29 Jun 2016 14:40:23 +0100 Subject: [PATCH 0295/1028] bumped RAC to 4.2.1 --- Cartfile | 2 +- Cartfile.resolved | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cartfile b/Cartfile index a4583d1085..fb56d4ce5f 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveCocoa" ~> 4.2 +github "ReactiveCocoa/ReactiveCocoa" ~> 4.2.1 diff --git a/Cartfile.resolved b/Cartfile.resolved index a82c40d165..421a6de515 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ -github "antitypical/Result" "2.0.0" -github "ReactiveCocoa/ReactiveCocoa" "v4.2.0" +github "antitypical/Result" "2.1.1" +github "ReactiveCocoa/ReactiveCocoa" "v4.2.1" From 84404881e503fca7a70a4fbe701088ac525fcbd3 Mon Sep 17 00:00:00 2001 From: Rui Peres Date: Wed, 29 Jun 2016 14:40:39 +0100 Subject: [PATCH 0296/1028] RAC 4.2.1 now provides debounce --- Source/Signal.swift | 17 +------------ Source/SignalProducer.swift | 11 -------- Tests/SignalTests.swift | 51 ------------------------------------- 3 files changed, 1 insertion(+), 78 deletions(-) diff --git a/Source/Signal.swift b/Source/Signal.swift index 7ae00bfc10..4bc4837bf1 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -77,22 +77,7 @@ extension SignalType { return disposable } } - - /// Enforces that at least `interval` time passes before forwarding a value. If a - /// new value arrives, the previous one is dropped and the `interval` delay starts - /// again. Error events are immediately forwarded, even if there's a queued value. - /// - /// This operator is useful for scenarios like type-to-search where you want to - /// wait for a "lull" in typing before kicking off a search request. - @warn_unused_result(message="Did you forget to call `observe` on the signal?") - public func debounce(interval: NSTimeInterval, onScheduler scheduler: DateSchedulerType) -> Signal { - precondition(interval >= 0) - - return flatMap(.Latest) { - SignalProducer(value: $0).delay(interval, onScheduler: scheduler) - } - } - + /// Forwards a value and then mutes the signal by dropping all subsequent values /// for `interval` seconds. Once time elapses the next new value will be forwarded /// and repeat the muting process. Error events are immediately forwarded even while diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index f94c3e8b7d..c977428437 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -81,17 +81,6 @@ extension SignalProducerType { return lift { $0.timeoutAfter(interval, withEvent: event, onScheduler: scheduler) } } - /// Enforces that at least `interval` time passes before forwarding a value. If a - /// new value arrives, the previous one is dropped and the `interval` delay starts - /// again. Error events are immediately forwarded, even if there's a queued value. - /// - /// This operator is useful for scenarios like type-to-search where you want to - /// wait for a "lull" in typing before kicking off a search request. - @warn_unused_result(message="Did you forget to call `start` on the producer?") - public func debounce(interval: NSTimeInterval, onScheduler scheduler: DateSchedulerType) -> SignalProducer { - return lift { $0.debounce(interval, onScheduler: scheduler) } - } - /// Forwards a value and then mutes the producer by dropping all subsequent values /// for `interval` seconds. Once time elapses the next new value will be forwarded /// and repeat the muting process. Error events are immediately forwarded even while diff --git a/Tests/SignalTests.swift b/Tests/SignalTests.swift index 3a1d7968f2..af55e1bb35 100644 --- a/Tests/SignalTests.swift +++ b/Tests/SignalTests.swift @@ -130,57 +130,6 @@ final class SignalTests: XCTestCase { XCTAssert(values == [1, 2, 3]) } - func testDebounceValues() { - let scheduler = TestScheduler() - let (signal, observer) = Signal.pipe() - var value = -1 - - signal - .debounce(1, onScheduler: scheduler) - .observeNext { value = $0 } - - scheduler.schedule { observer.sendNext(1) } - scheduler.advance() - XCTAssertEqual(value, -1) - - scheduler.advanceByInterval(1) - XCTAssertEqual(value, 1) - - scheduler.schedule { observer.sendNext(2) } - scheduler.advance() - XCTAssertEqual(value, 1) - - scheduler.schedule { observer.sendNext(3) } - scheduler.advance() - XCTAssertEqual(value, 1) - - scheduler.advanceByInterval(1) - XCTAssertEqual(value, 3) - } - - func testDebounceFailure() { - let scheduler = TestScheduler() - let (signal, observer) = Signal.pipe() - var value = -1 - var failed = false - - signal - .debounce(1, onScheduler: scheduler) - .observe(Observer( - next: { value = $0 }, - failed: { _ in failed = true } - )) - - scheduler.schedule { observer.sendNext(1) } - scheduler.advance() - XCTAssertEqual(value, -1) - - scheduler.schedule { observer.sendFailed(.Default) } - scheduler.advance() - XCTAssertTrue(failed) - XCTAssertEqual(value, -1) - } - func testMuteForValues() { let scheduler = TestScheduler() let (signal, observer) = Signal.pipe() From 0a1927ddfaaca30863ec4ed8ed9350872b775003 Mon Sep 17 00:00:00 2001 From: David Rodrigues Date: Tue, 5 Jul 2016 10:53:40 +0100 Subject: [PATCH 0297/1028] Changed `rex_text` bindable property to be an optional string (#133) * Changed `rex_text` bindable property to be an optional string Fixes #125, #129. * Updated UILabel's text property test to include nullability --- Source/UIKit/UILabel.swift | 5 +++-- Tests/UIKit/UILabelTests.swift | 4 +++- Tests/UIKit/UITableViewCellTests.swift | 1 + 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Source/UIKit/UILabel.swift b/Source/UIKit/UILabel.swift index 1ec0c2529f..c47746f326 100644 --- a/Source/UIKit/UILabel.swift +++ b/Source/UIKit/UILabel.swift @@ -11,8 +11,8 @@ import UIKit extension UILabel { /// Wraps a label's `text` value in a bindable property. - public var rex_text: MutableProperty { - return associatedProperty(self, keyPath: "text") + public var rex_text: MutableProperty { + return associatedProperty(self, key: &attributedTextKey, initial: { $0.text }, setter: { $0.text = $1 }) } /// Wraps a label's `attributedText` value in a bindable property. @@ -26,5 +26,6 @@ extension UILabel { } } +private var textKey: UInt8 = 0 private var attributedTextKey: UInt8 = 0 private var textColorKey: UInt8 = 0 diff --git a/Tests/UIKit/UILabelTests.swift b/Tests/UIKit/UILabelTests.swift index 8d10099fb8..9356b9fd77 100644 --- a/Tests/UIKit/UILabelTests.swift +++ b/Tests/UIKit/UILabelTests.swift @@ -35,13 +35,15 @@ class UILabelTests: XCTestCase { let label = UILabel(frame: CGRectZero) label.text = "" - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() label.rex_text <~ SignalProducer(signal: pipeSignal) observer.sendNext(firstChange) XCTAssertEqual(label.text, firstChange) observer.sendNext(secondChange) XCTAssertEqual(label.text, secondChange) + observer.sendNext(nil) + XCTAssertNil(label.text) } func testAttributedTextPropertyDoesntCreateRetainCycle() { diff --git a/Tests/UIKit/UITableViewCellTests.swift b/Tests/UIKit/UITableViewCellTests.swift index 72eba7bafa..d08767feef 100644 --- a/Tests/UIKit/UITableViewCellTests.swift +++ b/Tests/UIKit/UITableViewCellTests.swift @@ -24,6 +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) XCTAssertEqual(label.text, "John") From 4b1b8649bfbe69e54d40dd4f5e17ce0d7b7556ea Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Tue, 5 Jul 2016 13:17:57 -0700 Subject: [PATCH 0298/1028] Update README to point at RACCommunity --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c9f5cd1453..a7670db36d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # Rex [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) Extensions for [ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa) that may not fit in the core framework. -New development targets RAC 4/Swift 2/Xcode 7. For RAC 3 support [see the 0.5 release](https://github.com/neilpa/Rex/releases/tag/v0.5.0). +New development targets RAC 4/Swift 2/Xcode 7. For RAC 3 support [see the 0.5 +release](https://github.com/RACCommunity/Rex/releases/tag/v0.5.0). ## Signal All `Signal` operators are available for `SignaProducer`s too via explicit `lift`ing. @@ -54,7 +55,7 @@ Flexible way to bind `CocoaAction` to the press of button. In addition the butto ```swift let downloadAction = Action { _ in - let url = NSURL(string: "https://github.com/neilpa/Rex/archive/master.zip") + let url = NSURL(string: "https://github.com/RACCommunity/Rex/archive/master.zip") let request = NSURLRequest(URL: url!) return NSURLSession.sharedSession().rac_dataWithRequest(request).map { $0.0 } } From c254b95854a95f6f8cefb5ecaf2e6e5e3b046119 Mon Sep 17 00:00:00 2001 From: Evgeny Kazakov Date: Wed, 6 Jul 2016 13:34:11 +0300 Subject: [PATCH 0299/1028] Implement rex_animating for UIActivityIndicatorView (#134) --- Rex.xcodeproj/project.pbxproj | 12 +++++ Source/UIKit/UIActivityIndicatorView.swift | 39 ++++++++++++++++ .../UIKit/UIActivityIndicatorViewTests.swift | 44 +++++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 Source/UIKit/UIActivityIndicatorView.swift create mode 100644 Tests/UIKit/UIActivityIndicatorViewTests.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 42d3c4bbba..d1fedc301e 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -8,6 +8,10 @@ /* Begin PBXBuildFile section */ 4238D5961B4D5950008534C0 /* NSTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4238D5951B4D5950008534C0 /* NSTextField.swift */; }; + 45CED46F1D27C1E300788BDC /* UIActivityIndicatorViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CED46D1D27C1D400788BDC /* UIActivityIndicatorViewTests.swift */; }; + 45CED4701D27C1E400788BDC /* UIActivityIndicatorViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CED46D1D27C1D400788BDC /* UIActivityIndicatorViewTests.swift */; }; + 45CED4711D27C1EB00788BDC /* UIActivityIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CED46B1D27BB8700788BDC /* UIActivityIndicatorView.swift */; }; + 45CED4721D27C1EC00788BDC /* UIActivityIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CED46B1D27BB8700788BDC /* UIActivityIndicatorView.swift */; }; 5B7F81E31D0842AD0014B06D /* UISegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1C882D1D0715CE000B888F /* UISegmentedControl.swift */; }; 5B7F81E41D0842B50014B06D /* UISegmentedControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1C882F1D071639000B888F /* UISegmentedControlTests.swift */; }; 7D2AA99B1CB6EFEB008AB5C9 /* UISwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D2AA99A1CB6EFEB008AB5C9 /* UISwitch.swift */; }; @@ -204,6 +208,8 @@ /* Begin PBXFileReference section */ 4238D5951B4D5950008534C0 /* NSTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; name = NSTextField.swift; path = AppKit/NSTextField.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 45CED46B1D27BB8700788BDC /* UIActivityIndicatorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorView.swift; sourceTree = ""; }; + 45CED46D1D27C1D400788BDC /* UIActivityIndicatorViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorViewTests.swift; sourceTree = ""; }; 5173EBC51B625A2600C9B48E /* UIBarItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIBarItem.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIBarButtonItem.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 5B1C882D1D0715CE000B888F /* UISegmentedControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISegmentedControl.swift; sourceTree = ""; }; @@ -441,6 +447,7 @@ D86FFBD31B34B0E2001A89B3 /* UIKit */ = { isa = PBXGroup; children = ( + 45CED46B1D27BB8700788BDC /* UIActivityIndicatorView.swift */, 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */, 5173EBC51B625A2600C9B48E /* UIBarItem.swift */, D86FFBDC1B34B691001A89B3 /* UIButton.swift */, @@ -486,6 +493,7 @@ D8F073131B861B110047D546 /* UIKit */ = { isa = PBXGroup; children = ( + 45CED46D1D27C1D400788BDC /* UIActivityIndicatorViewTests.swift */, 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */, 8295FD881B873490007C9000 /* UIButtonTests.swift */, 7DCF5B351CC80E8E004AEE75 /* UICollectionReusableViewTests.swift */, @@ -861,6 +869,7 @@ D86FFBD61B34B116001A89B3 /* UIControl.swift in Sources */, D8F0973F1B17F31E002E15BA /* NSData.swift in Sources */, D86FFBDD1B34B691001A89B3 /* UIButton.swift in Sources */, + 45CED4711D27C1EB00788BDC /* UIActivityIndicatorView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -869,6 +878,7 @@ buildActionMask = 2147483647; files = ( E6933BEA1CD9C335006F7CE7 /* UIProgressViewTests.swift in Sources */, + 45CED46F1D27C1E300788BDC /* UIActivityIndicatorViewTests.swift in Sources */, CC02C18B1CCA704F0025CC04 /* ActionTests.swift in Sources */, 7DC325801CC6FD2100746D88 /* UITableViewHeaderFooterViewTests.swift in Sources */, 8289A2E11BD7EF1F0097FB60 /* UIImageViewTests.swift in Sources */, @@ -934,6 +944,7 @@ D8715DC81C211553005F4191 /* UIButton.swift in Sources */, D8715DC71C211553005F4191 /* UIBarItem.swift in Sources */, D8715DC11C2112D6005F4191 /* NSUserDefaults.swift in Sources */, + 45CED4721D27C1EC00788BDC /* UIActivityIndicatorView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -956,6 +967,7 @@ D8715DE41C211643005F4191 /* UIImageViewTests.swift in Sources */, D8715DE31C211643005F4191 /* UILabelTests.swift in Sources */, D8715DE01C211643005F4191 /* UIBarButtonItemTests.swift in Sources */, + 45CED4701D27C1E400788BDC /* UIActivityIndicatorViewTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Source/UIKit/UIActivityIndicatorView.swift b/Source/UIKit/UIActivityIndicatorView.swift new file mode 100644 index 0000000000..e9c8308d39 --- /dev/null +++ b/Source/UIKit/UIActivityIndicatorView.swift @@ -0,0 +1,39 @@ +// +// UIActivityIndicatorView.swift +// Rex +// +// Created by Evgeny Kazakov on 02/07/16. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa +import UIKit + +extension UIActivityIndicatorView { + + /// Returns whether the receiver is animating. + /// `true` if the receiver is animating, otherwise `false`. + /// + /// Setting the value of this property to `true` starts animation of the progress indicator, + /// and setting it to `false` stops animation. + public var animating: Bool { + get { + return isAnimating() + } + set { + if newValue { + startAnimating() + } else { + stopAnimating() + } + } + } + + /// Wraps an indicator's `animating` state in a bindable property. + public var rex_animating: MutableProperty { + return associatedProperty(self, key: &animatingKey, initial: { $0.animating }, setter: { $0.animating = $1 }) + } + +} + +private var animatingKey: UInt8 = 0 diff --git a/Tests/UIKit/UIActivityIndicatorViewTests.swift b/Tests/UIKit/UIActivityIndicatorViewTests.swift new file mode 100644 index 0000000000..82b4df5cc9 --- /dev/null +++ b/Tests/UIKit/UIActivityIndicatorViewTests.swift @@ -0,0 +1,44 @@ +// +// UIActivityIndicatorViewTests.swift +// Rex +// +// Created by Evgeny Kazakov on 02/07/16. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import XCTest +import ReactiveCocoa +import Result + +class UIActivityIndicatorTests: XCTestCase { + + weak var _activityIndicatorView: UIActivityIndicatorView? + + override func tearDown() { + XCTAssert(_activityIndicatorView == nil, "Retain cycle detected in UIActivityIndicatorView properties") + super.tearDown() + } + + func testRexAnimatingProperty() { + let indicatorView = UIActivityIndicatorView(frame: CGRectZero) + _activityIndicatorView = indicatorView + + let (pipeSignal, observer) = Signal.pipe() + indicatorView.rex_animating <~ SignalProducer(signal: pipeSignal) + + observer.sendNext(true) + XCTAssertTrue(indicatorView.animating) + observer.sendNext(false) + XCTAssertFalse(indicatorView.animating) + } + + func testAnimatingProperty() { + let indicatorView = UIActivityIndicatorView(frame: CGRectZero) + + indicatorView.animating = true + XCTAssertTrue(indicatorView.isAnimating()) + + indicatorView.animating = false + XCTAssertFalse(indicatorView.isAnimating()) + } +} From 2c38832783cf590579f1628e2566535306d4b12f Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Wed, 6 Jul 2016 20:24:01 +0900 Subject: [PATCH 0300/1028] Remove confusing UIActivityIndicatorView.animating extended property (#139) --- Source/UIKit/UIActivityIndicatorView.swift | 28 ++++++------------- .../UIKit/UIActivityIndicatorViewTests.swift | 14 ++-------- 2 files changed, 11 insertions(+), 31 deletions(-) diff --git a/Source/UIKit/UIActivityIndicatorView.swift b/Source/UIKit/UIActivityIndicatorView.swift index e9c8308d39..179b795090 100644 --- a/Source/UIKit/UIActivityIndicatorView.swift +++ b/Source/UIKit/UIActivityIndicatorView.swift @@ -11,27 +11,17 @@ import UIKit extension UIActivityIndicatorView { - /// Returns whether the receiver is animating. - /// `true` if the receiver is animating, otherwise `false`. - /// - /// Setting the value of this property to `true` starts animation of the progress indicator, - /// and setting it to `false` stops animation. - public var animating: Bool { - get { - return isAnimating() - } - set { - if newValue { - startAnimating() + /// Wraps an indicator's `isAnimating()` state in a bindable property. + /// Setting a new value to the property would call `startAnimating()` or + /// `stopAnimating()` depending on the value. + public var rex_animating: MutableProperty { + return associatedProperty(self, key: &animatingKey, initial: { $0.isAnimating() }, setter: { host, animating in + if animating { + host.startAnimating() } else { - stopAnimating() + host.stopAnimating() } - } - } - - /// Wraps an indicator's `animating` state in a bindable property. - public var rex_animating: MutableProperty { - return associatedProperty(self, key: &animatingKey, initial: { $0.animating }, setter: { $0.animating = $1 }) + }) } } diff --git a/Tests/UIKit/UIActivityIndicatorViewTests.swift b/Tests/UIKit/UIActivityIndicatorViewTests.swift index 82b4df5cc9..d47d1cb698 100644 --- a/Tests/UIKit/UIActivityIndicatorViewTests.swift +++ b/Tests/UIKit/UIActivityIndicatorViewTests.swift @@ -19,7 +19,7 @@ class UIActivityIndicatorTests: XCTestCase { super.tearDown() } - func testRexAnimatingProperty() { + func testAnimatingProperty() { let indicatorView = UIActivityIndicatorView(frame: CGRectZero) _activityIndicatorView = indicatorView @@ -27,18 +27,8 @@ class UIActivityIndicatorTests: XCTestCase { indicatorView.rex_animating <~ SignalProducer(signal: pipeSignal) observer.sendNext(true) - XCTAssertTrue(indicatorView.animating) - observer.sendNext(false) - XCTAssertFalse(indicatorView.animating) - } - - func testAnimatingProperty() { - let indicatorView = UIActivityIndicatorView(frame: CGRectZero) - - indicatorView.animating = true XCTAssertTrue(indicatorView.isAnimating()) - - indicatorView.animating = false + observer.sendNext(false) XCTAssertFalse(indicatorView.isAnimating()) } } From e004d7d80a549d6b53fb890db6caa48208fb4230 Mon Sep 17 00:00:00 2001 From: Rui Peres Date: Wed, 6 Jul 2016 12:06:48 +0100 Subject: [PATCH 0301/1028] Automatically upload .framework when tagging --- .travis.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.travis.yml b/.travis.yml index 265cfaf441..2f4d87fd29 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,3 +25,15 @@ script: notifications: email: false +before_deploy: + - carthage build --no-skip-current + - carthage archive Rex +deploy: + provider: releases + api_key: + secure: lNuRUOxMrw2YSsibAAK+8GH35KfvgheiyBGsqD7Rqwjzf3orhTmNbWeRt38YiUxzmNZSzhQdcglwJnw9ymXuGyynOwWxRPbQnX0KE+fTIyoyZrxIwxkqyU6aGzgi1bGa/URKU83pDZsBrfPeLa89w5PYZ8UVPVs+alD796WTjNoXFhxvj4cZtT65Pqk4usSgq3l6GRGzVDmaOgiiDT509LdTi+x+BjRuQcP2wvxCKWGWtaR4COo+PH96mQ/vcykL97zyxScWOBRbVq5YEeqHC/OHV7kXMLRK6X0SBcpB8otV9ObxN76zqZjpxQ59/g0WN4bUogd5VYT11dxjSAQDNAtS/H0iHcw6ojDuAobQbD1W4Um6tHBPaT4ZVXDack8J2gSa2DhiFBt198XRSEWFczff5LevxFJaDwqLwEj5qtB6bkdvarsaZdlUzaPfmBfEjmLQdQmiEe82xYb+VcZK0SlgbNulvSt8J2FpLRcVQSs7Ef75zMKQECtxJCsOhSFGT+1Zal2YEk70HFdbkNE0DS57AX0hmgDFF0WhK1ZzpBgy432Hyo71srAJMyalMfF1zuc5kHSssezQ30p7ZdegDnkvbt0lhjGFgUlbKIoLS9e21uo3i96XZQagL5k/mZPxaq1hf1bsH+ow+Jcg3X7b8RJRqniHxASyffdzHYbmfyE= + file: Rex.framework.zip + skip_cleanup: true + on: + repo: RACCommunity/Rex + tags: true From 4a3bd7974e95d3363ceb12042889372bd7053232 Mon Sep 17 00:00:00 2001 From: David Rodrigues Date: Wed, 6 Jul 2016 14:20:41 +0100 Subject: [PATCH 0302/1028] Add bindable property to wrap a control's value (#119) * Provide a `setUp` to properly configure the associated property The introduced `setUp` is extremely helpful to setup any signals that may affect the property once. * Add bindable property to wrap a control's value This property is common to every `UIControl` and can be used by providing a getter and a setter for the value wrapped. This property uses `UIControlEvents.ValueChanged` and `UIControlEvents.EditingChanged` events to detect changes and keep the value up-to-date. This kind of logic can be reused instead of being defined and used for each control. * Use `setUp` to install the dependent signal of `rex_dismissAnimated` This prevents repetition of events in cases where the property is accessed more than once (each access adds a new `rac_signalForSelector` for the dismiss of the view controller). * Fixes #129 - UITextField's text bindable property should be of optional type --- Rex.xcodeproj/project.pbxproj | 74 +++++++++++-------- Source/Foundation/Association.swift | 4 +- Source/UIKit/UIControl.swift | 17 +++++ Source/UIKit/UIDatePicker.swift | 20 ++--- Source/UIKit/UISwitch.swift | 13 +--- Source/UIKit/UITextField.swift | 23 ++++-- Source/UIKit/UIViewController.swift | 13 ++-- ...ol+EnableSendActionsForControlEvents.swift | 57 ++++++++++++++ Tests/UIKit/UIDatePickerTests.swift | 3 +- Tests/UIKit/UISwitchTests.swift | 14 ++-- Tests/UIKit/UITextFieldTests.swift | 12 ++- 11 files changed, 169 insertions(+), 81 deletions(-) create mode 100644 Tests/Helpers/UIControl+EnableSendActionsForControlEvents.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index d1fedc301e..a043fc43de 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -14,8 +14,10 @@ 45CED4721D27C1EC00788BDC /* UIActivityIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CED46B1D27BB8700788BDC /* UIActivityIndicatorView.swift */; }; 5B7F81E31D0842AD0014B06D /* UISegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1C882D1D0715CE000B888F /* UISegmentedControl.swift */; }; 5B7F81E41D0842B50014B06D /* UISegmentedControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1C882F1D071639000B888F /* UISegmentedControlTests.swift */; }; + 7D0DABAA1CCC381F00B6CD2B /* UIControl+EnableSendActionsForControlEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D0DABA91CCC381F00B6CD2B /* UIControl+EnableSendActionsForControlEvents.swift */; }; 7D2AA99B1CB6EFEB008AB5C9 /* UISwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D2AA99A1CB6EFEB008AB5C9 /* UISwitch.swift */; }; 7D2AA99D1CB6F275008AB5C9 /* UISwitchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D2AA99C1CB6F275008AB5C9 /* UISwitchTests.swift */; }; + 7D5FE3081CD4B04E00834675 /* UITextFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E851C4B420A00086F3C /* UITextFieldTests.swift */; }; 7DBD48F31CC8141D0077AD4F /* Reusable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DBD48F21CC8141D0077AD4F /* Reusable.swift */; }; 7DBD48F41CC8141D0077AD4F /* Reusable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DBD48F21CC8141D0077AD4F /* Reusable.swift */; }; 7DC325741CC6FCF100746D88 /* UITableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC325721CC6FCF100746D88 /* UITableViewCell.swift */; }; @@ -54,8 +56,8 @@ CC02C18B1CCA704F0025CC04 /* ActionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC02C1881CCA704C0025CC04 /* ActionTests.swift */; }; CC02C18C1CCA704F0025CC04 /* ActionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC02C1881CCA704C0025CC04 /* ActionTests.swift */; }; D8003E941AFEC3D400D7D3C5 /* Rex.h in Headers */ = {isa = PBXBuildFile; fileRef = D8003E931AFEC3D400D7D3C5 /* Rex.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in 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 (null) */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D8003EB51AFEC6B000D7D3C5 /* Result.framework in (null) */ = {isa = PBXBuildFile; fileRef = D8003EAE1AFEC68A00D7D3C5 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 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 */; }; @@ -69,8 +71,8 @@ D83457331AFEE4930070616A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAE1AFEC68A00D7D3C5 /* Result.framework */; }; D83457351AFEE4B20070616A /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */; }; D83457361AFEE4B20070616A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */; }; - D83457391AFEE4BE0070616A /* ReactiveCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */; }; - D834573A1AFEE4BE0070616A /* Result.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */; }; + D83457391AFEE4BE0070616A /* ReactiveCocoa.framework in (null) */ = {isa = PBXBuildFile; fileRef = D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */; }; + D834573A1AFEE4BE0070616A /* Result.framework in (null) */ = {isa = PBXBuildFile; fileRef = D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */; }; D834573C1AFEE57E0070616A /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */; }; D834573D1AFEE57E0070616A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */; }; D83457411AFEE6050070616A /* SignalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */; }; @@ -123,8 +125,8 @@ D8715DE51C211643005F4191 /* UIViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */; }; D8715DE61C21170C005F4191 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8715DC21C211310005F4191 /* ReactiveCocoa.framework */; }; D8715DE71C21170C005F4191 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8715DC31C211310005F4191 /* Result.framework */; }; - D8715DE91C211739005F4191 /* ReactiveCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8715DC21C211310005F4191 /* ReactiveCocoa.framework */; }; - D8715DEA1C211739005F4191 /* Result.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8715DC31C211310005F4191 /* Result.framework */; }; + D8715DE91C211739005F4191 /* ReactiveCocoa.framework in (null) */ = {isa = PBXBuildFile; fileRef = D8715DC21C211310005F4191 /* ReactiveCocoa.framework */; }; + D8715DEA1C211739005F4191 /* Result.framework in (null) */ = {isa = PBXBuildFile; fileRef = D8715DC31C211310005F4191 /* Result.framework */; }; D8A454061BD26A1A00C9E790 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Property.swift */; }; D8A454071BD26A1A00C9E790 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Property.swift */; }; D8A454091BD2772700C9E790 /* PropertyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454081BD2772700C9E790 /* PropertyTests.swift */; }; @@ -138,12 +140,12 @@ D8F0973F1B17F31E002E15BA /* NSData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973A1B17F2F7002E15BA /* NSData.swift */; }; D8F097441B17F3C8002E15BA /* NSObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097431B17F3C8002E15BA /* NSObject.swift */; }; D8F097451B17F3C8002E15BA /* NSObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097431B17F3C8002E15BA /* NSObject.swift */; }; - D8F0974A1B17F5E1002E15BA /* NSObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097471B17F5DD002E15BA /* NSObjectTests.swift */; }; - D8F0974B1B17F5E2002E15BA /* NSObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097471B17F5DD002E15BA /* NSObjectTests.swift */; }; E6933BEA1CD9C335006F7CE7 /* UIProgressViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6933BE81CD9C1F1006F7CE7 /* UIProgressViewTests.swift */; }; E6933BEB1CD9C363006F7CE7 /* UIProgressViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6933BE81CD9C1F1006F7CE7 /* UIProgressViewTests.swift */; }; E6933BEC1CD9C37D006F7CE7 /* UIProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6933BE61CD9C0B2006F7CE7 /* UIProgressView.swift */; }; E6933BED1CD9C37D006F7CE7 /* UIProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6933BE61CD9C0B2006F7CE7 /* UIProgressView.swift */; }; + D8F0974A1B17F5E1002E15BA /* NSObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097471B17F5DD002E15BA /* NSObjectTests.swift */; }; + D8F0974B1B17F5E2002E15BA /* NSObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097471B17F5DD002E15BA /* NSObjectTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -171,49 +173,50 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ - D8003EB21AFEC6A800D7D3C5 /* CopyFiles */ = { + D8003EB21AFEC6A800D7D3C5 /* (null) */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 16; files = ( - D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in CopyFiles */, - D8003EB51AFEC6B000D7D3C5 /* Result.framework in CopyFiles */, + D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in (null) */, + D8003EB51AFEC6B000D7D3C5 /* Result.framework in (null) */, ); runOnlyForDeploymentPostprocessing = 0; }; - D83457371AFEE4B80070616A /* CopyFiles */ = { + D83457371AFEE4B80070616A /* (null) */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 16; files = ( - D83457391AFEE4BE0070616A /* ReactiveCocoa.framework in CopyFiles */, - D834573A1AFEE4BE0070616A /* Result.framework in CopyFiles */, + D83457391AFEE4BE0070616A /* ReactiveCocoa.framework in (null) */, + D834573A1AFEE4BE0070616A /* Result.framework in (null) */, ); runOnlyForDeploymentPostprocessing = 0; }; - D8715DE81C21172A005F4191 /* CopyFiles */ = { + D8715DE81C21172A005F4191 /* (null) */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 16; files = ( - D8715DE91C211739005F4191 /* ReactiveCocoa.framework in CopyFiles */, - D8715DEA1C211739005F4191 /* Result.framework in CopyFiles */, + D8715DE91C211739005F4191 /* ReactiveCocoa.framework in (null) */, + D8715DEA1C211739005F4191 /* Result.framework in (null) */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 4238D5951B4D5950008534C0 /* NSTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; name = NSTextField.swift; path = AppKit/NSTextField.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 45CED46B1D27BB8700788BDC /* UIActivityIndicatorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorView.swift; sourceTree = ""; }; 45CED46D1D27C1D400788BDC /* UIActivityIndicatorViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorViewTests.swift; sourceTree = ""; }; - 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; }; + 4238D5951B4D5950008534C0 /* NSTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; name = NSTextField.swift; path = AppKit/NSTextField.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 5B1C882D1D0715CE000B888F /* UISegmentedControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISegmentedControl.swift; sourceTree = ""; }; 5B1C882F1D071639000B888F /* UISegmentedControlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISegmentedControlTests.swift; sourceTree = ""; }; + 5173EBC51B625A2600C9B48E /* UIBarItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIBarItem.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIBarButtonItem.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 7D0DABA91CCC381F00B6CD2B /* UIControl+EnableSendActionsForControlEvents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIControl+EnableSendActionsForControlEvents.swift"; sourceTree = ""; }; 7D2AA99A1CB6EFEB008AB5C9 /* UISwitch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UISwitch.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 7D2AA99C1CB6F275008AB5C9 /* UISwitchTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UISwitchTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 7DBD48F21CC8141D0077AD4F /* Reusable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reusable.swift; sourceTree = ""; }; @@ -271,11 +274,11 @@ D8A454081BD2772700C9E790 /* PropertyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = PropertyTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; D8F073141B861B3A0047D546 /* UILabelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UILabelTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; D8F0973A1B17F2F7002E15BA /* NSData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = NSData.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + E6933BE61CD9C0B2006F7CE7 /* UIProgressView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIProgressView.swift; sourceTree = ""; }; + E6933BE81CD9C1F1006F7CE7 /* UIProgressViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIProgressViewTests.swift; sourceTree = ""; }; D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = NSUserDefaults.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; D8F097431B17F3C8002E15BA /* NSObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = NSObject.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; D8F097471B17F5DD002E15BA /* NSObjectTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = NSObjectTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - E6933BE61CD9C0B2006F7CE7 /* UIProgressView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIProgressView.swift; sourceTree = ""; }; - E6933BE81CD9C1F1006F7CE7 /* UIProgressViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIProgressViewTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -353,6 +356,14 @@ name = AppKit; sourceTree = ""; }; + 7D0DABA81CCC380700B6CD2B /* Helpers */ = { + isa = PBXGroup; + children = ( + 7D0DABA91CCC381F00B6CD2B /* UIControl+EnableSendActionsForControlEvents.swift */, + ); + path = Helpers; + sourceTree = ""; + }; D8003E841AFEC3D400D7D3C5 = { isa = PBXGroup; children = ( @@ -396,6 +407,7 @@ D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */, D8003EC01AFED30100D7D3C5 /* SignalProducerTests.swift */, D8F097461B17F5BF002E15BA /* Foundation */, + 7D0DABA81CCC380700B6CD2B /* Helpers */, D8F073131B861B110047D546 /* UIKit */, D8003E9E1AFEC3D400D7D3C5 /* Supporting Files */, ); @@ -496,8 +508,8 @@ 45CED46D1D27C1D400788BDC /* UIActivityIndicatorViewTests.swift */, 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */, 8295FD881B873490007C9000 /* UIButtonTests.swift */, - 7DCF5B351CC80E8E004AEE75 /* UICollectionReusableViewTests.swift */, 8295FD851B873081007C9000 /* UIControlTests.swift */, + 7DCF5B351CC80E8E004AEE75 /* UICollectionReusableViewTests.swift */, 9DA915A51CA63046003723B9 /* UIDatePickerTests.swift */, 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */, D8F073141B861B3A0047D546 /* UILabelTests.swift */, @@ -508,8 +520,8 @@ 7DC325791CC6FD0A00746D88 /* UITableViewHeaderFooterViewTests.swift */, C7932E851C4B420A00086F3C /* UITextFieldTests.swift */, C7DCE2B51CB3C9C1001217D8 /* UITextViewTests.swift */, - C7945F121CC1DFB400DC9E37 /* UIViewControllerTests.swift */, 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */, + C7945F121CC1DFB400DC9E37 /* UIViewControllerTests.swift */, ); path = UIKit; sourceTree = ""; @@ -596,7 +608,7 @@ D8003E951AFEC3D400D7D3C5 /* Sources */, D8003E961AFEC3D400D7D3C5 /* Frameworks */, D8003E971AFEC3D400D7D3C5 /* Resources */, - D8003EB21AFEC6A800D7D3C5 /* CopyFiles */, + D8003EB21AFEC6A800D7D3C5 /* (null) */, ); buildRules = ( ); @@ -633,7 +645,7 @@ D834571A1AFEE44E0070616A /* Sources */, D834571B1AFEE44E0070616A /* Frameworks */, D834571C1AFEE44E0070616A /* Resources */, - D83457371AFEE4B80070616A /* CopyFiles */, + D83457371AFEE4B80070616A /* (null) */, ); buildRules = ( ); @@ -688,7 +700,7 @@ D8715DCD1C21160A005F4191 /* Sources */, D8715DCE1C21160A005F4191 /* Frameworks */, D8715DCF1C21160A005F4191 /* Resources */, - D8715DE81C21172A005F4191 /* CopyFiles */, + D8715DE81C21172A005F4191 /* (null) */, ); buildRules = ( ); @@ -733,7 +745,7 @@ }; }; }; - buildConfigurationList = D8003E881AFEC3D400D7D3C5 /* Build configuration list for PBXProject "Rex" */; + buildConfigurationList = D8003E881AFEC3D400D7D3C5 /* Build configuration list for PBXProject "Rex-Mac" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; @@ -870,6 +882,7 @@ D8F0973F1B17F31E002E15BA /* NSData.swift in Sources */, D86FFBDD1B34B691001A89B3 /* UIButton.swift in Sources */, 45CED4711D27C1EB00788BDC /* UIActivityIndicatorView.swift in Sources */, + 7D0DABAA1CCC381F00B6CD2B /* UIControl+EnableSendActionsForControlEvents.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -957,6 +970,7 @@ 7DCF5B371CC80E8E004AEE75 /* UICollectionReusableViewTests.swift in Sources */, D8715DDE1C211637005F4191 /* SignalProducerTests.swift in Sources */, D8715DE11C211643005F4191 /* UIButtonTests.swift in Sources */, + 7D5FE3081CD4B04E00834675 /* UITextFieldTests.swift in Sources */, D8715DE21C211643005F4191 /* UIControlTests.swift in Sources */, CC02C18C1CCA704F0025CC04 /* ActionTests.swift in Sources */, D8715DDF1C21163B005F4191 /* NSObjectTests.swift in Sources */, @@ -966,8 +980,8 @@ 7DC325811CC6FD2300746D88 /* UITableViewHeaderFooterViewTests.swift in Sources */, D8715DE41C211643005F4191 /* UIImageViewTests.swift in Sources */, D8715DE31C211643005F4191 /* UILabelTests.swift in Sources */, - D8715DE01C211643005F4191 /* UIBarButtonItemTests.swift in Sources */, 45CED4701D27C1E400788BDC /* UIActivityIndicatorViewTests.swift in Sources */, + D8715DE01C211643005F4191 /* UIBarButtonItemTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1398,7 +1412,7 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - D8003E881AFEC3D400D7D3C5 /* Build configuration list for PBXProject "Rex" */ = { + D8003E881AFEC3D400D7D3C5 /* Build configuration list for PBXProject "Rex-Mac" */ = { isa = XCConfigurationList; buildConfigurations = ( D8003EA21AFEC3D400D7D3C5 /* Debug */, diff --git a/Source/Foundation/Association.swift b/Source/Foundation/Association.swift index 0f71e3f62d..976b6c9b98 100644 --- a/Source/Foundation/Association.swift +++ b/Source/Foundation/Association.swift @@ -50,10 +50,12 @@ 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) -> ()) -> MutableProperty { +public func associatedProperty(host: Host, key: UnsafePointer<()>, @noescape initial: Host -> T, setter: (Host, T) -> (), @noescape setUp: MutableProperty -> () = { _ in }) -> MutableProperty { return associatedObject(host, key: key) { host in let property = MutableProperty(initial(host)) + setUp(property) + property.producer.startWithNext { [weak host] next in if let host = host { setter(host, next) diff --git a/Source/UIKit/UIControl.swift b/Source/UIKit/UIControl.swift index d9cbc20bbb..1ad6e9a17c 100644 --- a/Source/UIKit/UIControl.swift +++ b/Source/UIKit/UIControl.swift @@ -11,6 +11,7 @@ import UIKit import enum Result.NoError 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?") @@ -20,6 +21,21 @@ extension UIControl { .map { $0 as? UIControl } .flatMapError { _ in SignalProducer(value: nil) } } + + /// Creates a bindable property to wrap a control's value. + /// + /// 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 { + return associatedProperty(host, key: &valueChangedKey, initial: getter, setter: setter) { property in + property <~ + host.rex_controlEvents([.ValueChanged, .EditingChanged]) + .filterMap { $0 as? Host } + .filterMap(getter) + } + } #endif /// Wraps a control's `enabled` state in a bindable property. @@ -41,3 +57,4 @@ extension UIControl { private var enabledKey: UInt8 = 0 private var selectedKey: UInt8 = 0 private var highlightedKey: UInt8 = 0 +private var valueChangedKey: UInt8 = 0 diff --git a/Source/UIKit/UIDatePicker.swift b/Source/UIKit/UIDatePicker.swift index 014223cf23..75f0d38a18 100644 --- a/Source/UIKit/UIDatePicker.swift +++ b/Source/UIKit/UIDatePicker.swift @@ -5,24 +5,14 @@ // Created by Guido Marucci Blas on 3/25/16. // Copyright © 2016 Neil Pankey. All rights reserved. // -import UIKit + import ReactiveCocoa +import UIKit extension UIDatePicker { - + + // Wraps a datePicker's `date` value in a bindable property. public var rex_date: MutableProperty { - let initial = { (picker: UIDatePicker) -> NSDate in - picker.addTarget(self, action: #selector(UIDatePicker.rex_changedDate), forControlEvents: .ValueChanged) - return picker.date - } - return associatedProperty(self, key: &dateKey, initial: initial) { $0.date = $1 } - } - - @objc - private func rex_changedDate() { - rex_date.value = date + return UIControl.rex_value(self, getter: { $0.date }, setter: { $0.date = $1 }) } - } - -private var dateKey: UInt8 = 0 diff --git a/Source/UIKit/UISwitch.swift b/Source/UIKit/UISwitch.swift index 89f0b9296e..e02c89003d 100644 --- a/Source/UIKit/UISwitch.swift +++ b/Source/UIKit/UISwitch.swift @@ -11,17 +11,8 @@ import UIKit extension UISwitch { - /// Wraps a switch's `on` state in a bindable property. + /// Wraps a switch's `on` value in a bindable property. public var rex_on: MutableProperty { - - let property = associatedProperty(self, key: &onKey, initial: { $0.on }, setter: { $0.on = $1 }) - - property <~ rex_controlEvents(.ValueChanged) - .filterMap { ($0 as? UISwitch)?.on } - - return property + return UIControl.rex_value(self, getter: { $0.on }, setter: { $0.on = $1 }) } - } - -private var onKey: UInt8 = 0 diff --git a/Source/UIKit/UITextField.swift b/Source/UIKit/UITextField.swift index f73c4f4000..c9fa38c6e4 100644 --- a/Source/UIKit/UITextField.swift +++ b/Source/UIKit/UITextField.swift @@ -8,14 +8,25 @@ import ReactiveCocoa import UIKit -import enum Result.NoError extension UITextField { - /// Sends the field's string value whenever it changes. - public var rex_text: SignalProducer { - return NSNotificationCenter.defaultCenter() - .rac_notifications(UITextFieldTextDidChangeNotification, object: self) - .filterMap { ($0.object as? UITextField)?.text } + /// Wraps a textField's `text` value in a bindable property. + public var rex_text: MutableProperty { + 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) + .filterMap { ($0.object as? UITextField)?.text } + } +#endif } + } + +private var textKey: UInt8 = 0 diff --git a/Source/UIKit/UIViewController.swift b/Source/UIKit/UIViewController.swift index 63a5435ae6..f16264b666 100644 --- a/Source/UIKit/UIViewController.swift +++ b/Source/UIKit/UIViewController.swift @@ -63,13 +63,12 @@ extension UIViewController { host.dismissViewControllerAnimated(unwrapped.animated, completion: unwrapped.completion) } - let property = associatedProperty(self, key: &dismissModally, initial: initial, setter: setter) - - property <~ rac_signalForSelector(#selector(UIViewController.dismissViewControllerAnimated(_:completion:))) - .takeUntilBlock { _ in property.value != nil } - .rex_toTriggerSignal() - .map { _ in return nil } - + 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 } + .rex_toTriggerSignal() + .map { _ in return nil } + } return property } diff --git a/Tests/Helpers/UIControl+EnableSendActionsForControlEvents.swift b/Tests/Helpers/UIControl+EnableSendActionsForControlEvents.swift new file mode 100644 index 0000000000..24d5db1bf4 --- /dev/null +++ b/Tests/Helpers/UIControl+EnableSendActionsForControlEvents.swift @@ -0,0 +1,57 @@ +// +// UIControl+EnableSendActionsForControlEvents.swift +// Rex +// +// Created by David Rodrigues on 24/04/16. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import UIKit + +/// 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 +/// in the future. To be able to test them, we're now using swizzling to manually invoke +/// the pair target+action. +extension UIControl { + + public override class func initialize() { + + struct Static { + static var token: dispatch_once_t = 0 + } + + if self !== UIControl.self { + return + } + + dispatch_once(&Static.token) { + + let originalSelector = #selector(UIControl.sendAction(_:to:forEvent:)) + let swizzledSelector = #selector(UIControl.rex_sendAction(_:to:forEvent:)) + + let originalMethod = class_getInstanceMethod(self, originalSelector) + let swizzledMethod = class_getInstanceMethod(self, swizzledSelector) + + let didAddMethod = class_addMethod(self, + originalSelector, + method_getImplementation(swizzledMethod), + method_getTypeEncoding(swizzledMethod)) + + if didAddMethod { + class_replaceMethod(self, + swizzledSelector, + method_getImplementation(originalMethod), + method_getTypeEncoding(originalMethod)) + } else { + method_exchangeImplementations(originalMethod, swizzledMethod) + } + } + } + + // MARK: - Method Swizzling + + func rex_sendAction(action: Selector, to target: AnyObject?, forEvent event: UIEvent?) { + target?.performSelector(action, withObject: self) + } +} diff --git a/Tests/UIKit/UIDatePickerTests.swift b/Tests/UIKit/UIDatePickerTests.swift index e15375f425..f29dbd6e9c 100644 --- a/Tests/UIKit/UIDatePickerTests.swift +++ b/Tests/UIKit/UIDatePickerTests.swift @@ -30,8 +30,7 @@ class UIDatePickerTests: XCTestCase { XCTAssertEqual(picker.date, date) } - // FIXME Can this actually be made to work inside XCTest? - func _testUpdatePropertyFromPicker() { + 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) } diff --git a/Tests/UIKit/UISwitchTests.swift b/Tests/UIKit/UISwitchTests.swift index ba9595681c..9391052edd 100644 --- a/Tests/UIKit/UISwitchTests.swift +++ b/Tests/UIKit/UISwitchTests.swift @@ -13,15 +13,19 @@ import Result class UISwitchTests: XCTestCase { func testOnProperty() { - let s = UISwitch(frame: CGRectZero) - s.on = false + let `switch` = UISwitch(frame: CGRectZero) + `switch`.on = false let (pipeSignal, observer) = Signal.pipe() - s.rex_on <~ SignalProducer(signal: pipeSignal) + `switch`.rex_on <~ SignalProducer(signal: pipeSignal) observer.sendNext(true) - XCTAssertTrue(s.on) + XCTAssertTrue(`switch`.on) observer.sendNext(false) - XCTAssertFalse(s.on) + XCTAssertFalse(`switch`.on) + + `switch`.on = true + `switch`.sendActionsForControlEvents(.ValueChanged) + XCTAssertTrue(`switch`.rex_on.value) } } diff --git a/Tests/UIKit/UITextFieldTests.swift b/Tests/UIKit/UITextFieldTests.swift index bed69d3345..fee8e85578 100644 --- a/Tests/UIKit/UITextFieldTests.swift +++ b/Tests/UIKit/UITextFieldTests.swift @@ -11,19 +11,23 @@ import UIKit 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 textField = UITextField(frame: CGRectZero) textField.text = "Test" - textField.rex_text.startWithNext { text in + textField.rex_text.signal.observeNext { text in XCTAssertEqual(text, textField.text) expectation.fulfill() } - + +#if os(iOS) + textField.sendActionsForControlEvents(.EditingChanged) +#else NSNotificationCenter.defaultCenter().postNotificationName(UITextFieldTextDidChangeNotification, object: textField) +#endif } } From 4d395ad75bc47623c26e25410d0ca746d881d3b5 Mon Sep 17 00:00:00 2001 From: Rui Peres Date: Wed, 6 Jul 2016 15:41:25 +0100 Subject: [PATCH 0303/1028] Removed before_install step for carthage setup --- .travis.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2f4d87fd29..47dd0d1ea8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,10 +9,6 @@ env: git: submodules: false -before_install: - - curl -OlL "https://github.com/Carthage/Carthage/releases/download/0.11/Carthage.pkg" - - sudo installer -pkg "Carthage.pkg" -target / - - rm "Carthage.pkg" script: - set -o pipefail From 2df4b4b9d0fc871dcf01b14ea52bd40b982f1cbb Mon Sep 17 00:00:00 2001 From: Anders Date: Sun, 10 Jul 2016 14:05:39 +0800 Subject: [PATCH 0304/1028] Swift 3 enablement. --- Cartfile | 2 +- Cartfile.resolved | 4 +- Rex.xcodeproj/project.pbxproj | 111 ++++++----- .../xcshareddata/xcschemes/Rex-Mac.xcscheme | 2 +- .../xcshareddata/xcschemes/Rex-iOS.xcscheme | 2 +- .../xcshareddata/xcschemes/Rex-tvOS.xcscheme | 2 +- .../xcschemes/Rex-watchOS.xcscheme | 2 +- Source/Action.swift | 4 +- Source/AppKit/NSTextField.swift | 4 +- Source/Foundation/Association.swift | 21 +-- Source/Foundation/Data.swift | 39 ++++ Source/Foundation/NSObject.swift | 7 +- ...SUserDefaults.swift => UserDefaults.swift} | 13 +- Source/Property.swift | 42 ++--- Source/RACSignal.swift | 2 - Source/Signal.swift | 53 +++--- Source/SignalProducer.swift | 172 +++++++++++++++--- Source/UIKit/UIBarButtonItem.swift | 2 +- Source/UIKit/UIBarItem.swift | 2 +- Source/UIKit/UIButton.swift | 10 +- Source/UIKit/UIControl.swift | 16 +- Source/UIKit/UIDatePicker.swift | 2 +- Source/UIKit/UILabel.swift | 2 +- Source/UIKit/UISegmentedControl.swift | 2 +- Source/UIKit/UISwitch.swift | 2 +- Source/UIKit/UITextField.swift | 2 +- Source/UIKit/UITextView.swift | 4 +- Source/UIKit/UIView.swift | 6 +- Source/UIKit/UIViewController.swift | 14 +- 29 files changed, 344 insertions(+), 202 deletions(-) create mode 100644 Source/Foundation/Data.swift rename Source/Foundation/{NSUserDefaults.swift => UserDefaults.swift} (69%) diff --git a/Cartfile b/Cartfile index fb56d4ce5f..87745f3216 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveCocoa" ~> 4.2.1 +github "ReactiveCocoa/ReactiveCocoa" "RAC5-swift3" diff --git a/Cartfile.resolved b/Cartfile.resolved index 421a6de515..a3bf652f43 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ -github "antitypical/Result" "2.1.1" -github "ReactiveCocoa/ReactiveCocoa" "v4.2.1" +github "antitypical/Result" "2fe88d1d41615ed060489e8cd06fc1b3e8f0ca53" +github "ReactiveCocoa/ReactiveCocoa" "3073a487acd53008a41b89ef6512040a1347212d" diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index a043fc43de..ae90df0b03 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -56,8 +56,8 @@ CC02C18B1CCA704F0025CC04 /* ActionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC02C1881CCA704C0025CC04 /* ActionTests.swift */; }; CC02C18C1CCA704F0025CC04 /* ActionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC02C1881CCA704C0025CC04 /* ActionTests.swift */; }; D8003E941AFEC3D400D7D3C5 /* Rex.h in Headers */ = {isa = PBXBuildFile; fileRef = D8003E931AFEC3D400D7D3C5 /* Rex.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in (null) */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - D8003EB51AFEC6B000D7D3C5 /* Result.framework in (null) */ = {isa = PBXBuildFile; fileRef = D8003EAE1AFEC68A00D7D3C5 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D8003EB51AFEC6B000D7D3C5 /* Result.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = D8003EAE1AFEC68A00D7D3C5 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */; }; D8003EBD1AFED01000D7D3C5 /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBC1AFED01000D7D3C5 /* Signal.swift */; }; D8003EC21AFED30F00D7D3C5 /* SignalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */; }; @@ -71,8 +71,8 @@ D83457331AFEE4930070616A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAE1AFEC68A00D7D3C5 /* Result.framework */; }; D83457351AFEE4B20070616A /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */; }; D83457361AFEE4B20070616A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */; }; - D83457391AFEE4BE0070616A /* ReactiveCocoa.framework in (null) */ = {isa = PBXBuildFile; fileRef = D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */; }; - D834573A1AFEE4BE0070616A /* Result.framework in (null) */ = {isa = PBXBuildFile; fileRef = D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */; }; + D83457391AFEE4BE0070616A /* ReactiveCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */; }; + D834573A1AFEE4BE0070616A /* Result.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */; }; D834573C1AFEE57E0070616A /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */; }; D834573D1AFEE57E0070616A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */; }; D83457411AFEE6050070616A /* SignalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */; }; @@ -90,9 +90,9 @@ D8715DA11C211011005F4191 /* Rex.h in Headers */ = {isa = PBXBuildFile; fileRef = D8003E931AFEC3D400D7D3C5 /* Rex.h */; settings = {ATTRIBUTES = (Public, ); }; }; D8715DA21C211014005F4191 /* Rex.h in Headers */ = {isa = PBXBuildFile; fileRef = D8003E931AFEC3D400D7D3C5 /* Rex.h */; settings = {ATTRIBUTES = (Public, ); }; }; D8715DA31C21107F005F4191 /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD01B34AD6F001A89B3 /* Association.swift */; }; - D8715DA41C21107F005F4191 /* NSData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973A1B17F2F7002E15BA /* NSData.swift */; }; + D8715DA41C21107F005F4191 /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973A1B17F2F7002E15BA /* Data.swift */; }; D8715DA51C21107F005F4191 /* NSObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097431B17F3C8002E15BA /* NSObject.swift */; }; - D8715DA61C21107F005F4191 /* NSUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */; }; + D8715DA61C21107F005F4191 /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973C1B17F30D002E15BA /* UserDefaults.swift */; }; D8715DA91C2110DA005F4191 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8715DA71C2110DA005F4191 /* ReactiveCocoa.framework */; }; D8715DAA1C2110DA005F4191 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8715DA81C2110DA005F4191 /* Result.framework */; }; D8715DB91C2112A9005F4191 /* Rex.h in Headers */ = {isa = PBXBuildFile; fileRef = D8003E931AFEC3D400D7D3C5 /* Rex.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -101,9 +101,9 @@ D8715DBC1C2112D1005F4191 /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBC1AFED01000D7D3C5 /* Signal.swift */; }; D8715DBD1C2112D1005F4191 /* SignalProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */; }; D8715DBE1C2112D6005F4191 /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD01B34AD6F001A89B3 /* Association.swift */; }; - D8715DBF1C2112D6005F4191 /* NSData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973A1B17F2F7002E15BA /* NSData.swift */; }; + D8715DBF1C2112D6005F4191 /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973A1B17F2F7002E15BA /* Data.swift */; }; D8715DC01C2112D6005F4191 /* NSObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097431B17F3C8002E15BA /* NSObject.swift */; }; - D8715DC11C2112D6005F4191 /* NSUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */; }; + D8715DC11C2112D6005F4191 /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973C1B17F30D002E15BA /* UserDefaults.swift */; }; D8715DC41C211310005F4191 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8715DC21C211310005F4191 /* ReactiveCocoa.framework */; }; D8715DC51C211310005F4191 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8715DC31C211310005F4191 /* Result.framework */; }; D8715DC61C211553005F4191 /* UIBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */; }; @@ -125,8 +125,8 @@ D8715DE51C211643005F4191 /* UIViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */; }; D8715DE61C21170C005F4191 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8715DC21C211310005F4191 /* ReactiveCocoa.framework */; }; D8715DE71C21170C005F4191 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8715DC31C211310005F4191 /* Result.framework */; }; - D8715DE91C211739005F4191 /* ReactiveCocoa.framework in (null) */ = {isa = PBXBuildFile; fileRef = D8715DC21C211310005F4191 /* ReactiveCocoa.framework */; }; - D8715DEA1C211739005F4191 /* Result.framework in (null) */ = {isa = PBXBuildFile; fileRef = D8715DC31C211310005F4191 /* Result.framework */; }; + D8715DE91C211739005F4191 /* ReactiveCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8715DC21C211310005F4191 /* ReactiveCocoa.framework */; }; + D8715DEA1C211739005F4191 /* Result.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8715DC31C211310005F4191 /* Result.framework */; }; D8A454061BD26A1A00C9E790 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Property.swift */; }; D8A454071BD26A1A00C9E790 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Property.swift */; }; D8A454091BD2772700C9E790 /* PropertyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454081BD2772700C9E790 /* PropertyTests.swift */; }; @@ -134,18 +134,18 @@ D8E4A6201B7BBB1600EAD8A8 /* UIBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */; }; D8E4A6211B7BBB2100EAD8A8 /* UIBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5173EBC51B625A2600C9B48E /* UIBarItem.swift */; }; D8F073161B863CE70047D546 /* UILabelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F073141B861B3A0047D546 /* UILabelTests.swift */; }; - D8F0973B1B17F2F7002E15BA /* NSData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973A1B17F2F7002E15BA /* NSData.swift */; }; - D8F0973D1B17F30D002E15BA /* NSUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */; }; - D8F0973E1B17F30D002E15BA /* NSUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */; }; - D8F0973F1B17F31E002E15BA /* NSData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973A1B17F2F7002E15BA /* NSData.swift */; }; + D8F0973B1B17F2F7002E15BA /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973A1B17F2F7002E15BA /* Data.swift */; }; + D8F0973D1B17F30D002E15BA /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973C1B17F30D002E15BA /* UserDefaults.swift */; }; + D8F0973E1B17F30D002E15BA /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973C1B17F30D002E15BA /* UserDefaults.swift */; }; + D8F0973F1B17F31E002E15BA /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973A1B17F2F7002E15BA /* Data.swift */; }; D8F097441B17F3C8002E15BA /* NSObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097431B17F3C8002E15BA /* NSObject.swift */; }; D8F097451B17F3C8002E15BA /* NSObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097431B17F3C8002E15BA /* NSObject.swift */; }; + D8F0974A1B17F5E1002E15BA /* NSObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097471B17F5DD002E15BA /* NSObjectTests.swift */; }; + D8F0974B1B17F5E2002E15BA /* NSObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097471B17F5DD002E15BA /* NSObjectTests.swift */; }; E6933BEA1CD9C335006F7CE7 /* UIProgressViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6933BE81CD9C1F1006F7CE7 /* UIProgressViewTests.swift */; }; E6933BEB1CD9C363006F7CE7 /* UIProgressViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6933BE81CD9C1F1006F7CE7 /* UIProgressViewTests.swift */; }; E6933BEC1CD9C37D006F7CE7 /* UIProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6933BE61CD9C0B2006F7CE7 /* UIProgressView.swift */; }; E6933BED1CD9C37D006F7CE7 /* UIProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6933BE61CD9C0B2006F7CE7 /* UIProgressView.swift */; }; - D8F0974A1B17F5E1002E15BA /* NSObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097471B17F5DD002E15BA /* NSObjectTests.swift */; }; - D8F0974B1B17F5E2002E15BA /* NSObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097471B17F5DD002E15BA /* NSObjectTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -173,49 +173,50 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ - D8003EB21AFEC6A800D7D3C5 /* (null) */ = { + D8003EB21AFEC6A800D7D3C5 /* Copy Files */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 16; files = ( - D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in (null) */, - D8003EB51AFEC6B000D7D3C5 /* Result.framework in (null) */, + D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in Copy Files */, + D8003EB51AFEC6B000D7D3C5 /* Result.framework in Copy Files */, ); + name = "Copy Files"; runOnlyForDeploymentPostprocessing = 0; }; - D83457371AFEE4B80070616A /* (null) */ = { + D83457371AFEE4B80070616A /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 16; files = ( - D83457391AFEE4BE0070616A /* ReactiveCocoa.framework in (null) */, - D834573A1AFEE4BE0070616A /* Result.framework in (null) */, + D83457391AFEE4BE0070616A /* ReactiveCocoa.framework in CopyFiles */, + D834573A1AFEE4BE0070616A /* Result.framework in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; - D8715DE81C21172A005F4191 /* (null) */ = { + D8715DE81C21172A005F4191 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 16; files = ( - D8715DE91C211739005F4191 /* ReactiveCocoa.framework in (null) */, - D8715DEA1C211739005F4191 /* Result.framework in (null) */, + D8715DE91C211739005F4191 /* ReactiveCocoa.framework in CopyFiles */, + D8715DEA1C211739005F4191 /* Result.framework in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 4238D5951B4D5950008534C0 /* NSTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; name = NSTextField.swift; path = AppKit/NSTextField.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 45CED46B1D27BB8700788BDC /* UIActivityIndicatorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorView.swift; sourceTree = ""; }; 45CED46D1D27C1D400788BDC /* UIActivityIndicatorViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorViewTests.swift; sourceTree = ""; }; - 4238D5951B4D5950008534C0 /* NSTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; name = NSTextField.swift; path = AppKit/NSTextField.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 5B1C882D1D0715CE000B888F /* UISegmentedControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISegmentedControl.swift; sourceTree = ""; }; - 5B1C882F1D071639000B888F /* UISegmentedControlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISegmentedControlTests.swift; sourceTree = ""; }; 5173EBC51B625A2600C9B48E /* UIBarItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIBarItem.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIBarButtonItem.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 5B1C882D1D0715CE000B888F /* UISegmentedControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISegmentedControl.swift; sourceTree = ""; }; + 5B1C882F1D071639000B888F /* UISegmentedControlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISegmentedControlTests.swift; sourceTree = ""; }; 7D0DABA91CCC381F00B6CD2B /* UIControl+EnableSendActionsForControlEvents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIControl+EnableSendActionsForControlEvents.swift"; sourceTree = ""; }; 7D2AA99A1CB6EFEB008AB5C9 /* UISwitch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UISwitch.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 7D2AA99C1CB6F275008AB5C9 /* UISwitchTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UISwitchTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; @@ -273,12 +274,12 @@ D8A454051BD26A1A00C9E790 /* Property.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Property.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; D8A454081BD2772700C9E790 /* PropertyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = PropertyTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; D8F073141B861B3A0047D546 /* UILabelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UILabelTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - D8F0973A1B17F2F7002E15BA /* NSData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = NSData.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - E6933BE61CD9C0B2006F7CE7 /* UIProgressView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIProgressView.swift; sourceTree = ""; }; - E6933BE81CD9C1F1006F7CE7 /* UIProgressViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIProgressViewTests.swift; sourceTree = ""; }; - D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = NSUserDefaults.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + D8F0973A1B17F2F7002E15BA /* Data.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Data.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + D8F0973C1B17F30D002E15BA /* UserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UserDefaults.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; D8F097431B17F3C8002E15BA /* NSObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = NSObject.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; D8F097471B17F5DD002E15BA /* NSObjectTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = NSObjectTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + E6933BE61CD9C0B2006F7CE7 /* UIProgressView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIProgressView.swift; sourceTree = ""; }; + E6933BE81CD9C1F1006F7CE7 /* UIProgressViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIProgressViewTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -371,7 +372,10 @@ D8003E9D1AFEC3D400D7D3C5 /* Tests */, D8003EAA1AFEC57200D7D3C5 /* Binaries */, ); + indentWidth = 4; sourceTree = ""; + tabWidth = 4; + usesTabs = 0; }; D8003E901AFEC3D400D7D3C5 /* Source */ = { isa = PBXGroup; @@ -530,9 +534,9 @@ isa = PBXGroup; children = ( D86FFBD01B34AD6F001A89B3 /* Association.swift */, - D8F0973A1B17F2F7002E15BA /* NSData.swift */, + D8F0973A1B17F2F7002E15BA /* Data.swift */, D8F097431B17F3C8002E15BA /* NSObject.swift */, - D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */, + D8F0973C1B17F30D002E15BA /* UserDefaults.swift */, ); path = Foundation; sourceTree = ""; @@ -608,7 +612,7 @@ D8003E951AFEC3D400D7D3C5 /* Sources */, D8003E961AFEC3D400D7D3C5 /* Frameworks */, D8003E971AFEC3D400D7D3C5 /* Resources */, - D8003EB21AFEC6A800D7D3C5 /* (null) */, + D8003EB21AFEC6A800D7D3C5 /* Copy Files */, ); buildRules = ( ); @@ -645,7 +649,7 @@ D834571A1AFEE44E0070616A /* Sources */, D834571B1AFEE44E0070616A /* Frameworks */, D834571C1AFEE44E0070616A /* Resources */, - D83457371AFEE4B80070616A /* (null) */, + D83457371AFEE4B80070616A /* CopyFiles */, ); buildRules = ( ); @@ -700,7 +704,7 @@ D8715DCD1C21160A005F4191 /* Sources */, D8715DCE1C21160A005F4191 /* Frameworks */, D8715DCF1C21160A005F4191 /* Resources */, - D8715DE81C21172A005F4191 /* (null) */, + D8715DE81C21172A005F4191 /* CopyFiles */, ); buildRules = ( ); @@ -719,7 +723,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0720; - LastUpgradeCheck = 0700; + LastUpgradeCheck = 0800; ORGANIZATIONNAME = "Neil Pankey"; TargetAttributes = { D8003E8D1AFEC3D400D7D3C5 = { @@ -745,7 +749,7 @@ }; }; }; - buildConfigurationList = D8003E881AFEC3D400D7D3C5 /* Build configuration list for PBXProject "Rex-Mac" */; + buildConfigurationList = D8003E881AFEC3D400D7D3C5 /* Build configuration list for PBXProject "Rex" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; @@ -832,9 +836,9 @@ D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */, D8003EBD1AFED01000D7D3C5 /* Signal.swift in Sources */, D8F097441B17F3C8002E15BA /* NSObject.swift in Sources */, - D8F0973B1B17F2F7002E15BA /* NSData.swift in Sources */, + D8F0973B1B17F2F7002E15BA /* Data.swift in Sources */, 4238D5961B4D5950008534C0 /* NSTextField.swift in Sources */, - D8F0973D1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, + D8F0973D1B17F30D002E15BA /* UserDefaults.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -864,7 +868,7 @@ 7DC325741CC6FCF100746D88 /* UITableViewCell.swift in Sources */, 5B7F81E31D0842AD0014B06D /* UISegmentedControl.swift in Sources */, 8289A2E31BD7EF740097FB60 /* UIImageView.swift in Sources */, - D8F0973E1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, + D8F0973E1B17F30D002E15BA /* UserDefaults.swift in Sources */, D834572D1AFEE45B0070616A /* Signal.swift in Sources */, D8E4A6211B7BBB2100EAD8A8 /* UIBarItem.swift in Sources */, 7D2AA99B1CB6EFEB008AB5C9 /* UISwitch.swift in Sources */, @@ -879,7 +883,7 @@ C7DCE2B41CB3C89A001217D8 /* UITextView.swift in Sources */, 8289A2E51BD7F6DD0097FB60 /* UIView.swift in Sources */, D86FFBD61B34B116001A89B3 /* UIControl.swift in Sources */, - D8F0973F1B17F31E002E15BA /* NSData.swift in Sources */, + D8F0973F1B17F31E002E15BA /* Data.swift in Sources */, D86FFBDD1B34B691001A89B3 /* UIButton.swift in Sources */, 45CED4711D27C1EB00788BDC /* UIActivityIndicatorView.swift in Sources */, 7D0DABAA1CCC381F00B6CD2B /* UIControl+EnableSendActionsForControlEvents.swift in Sources */, @@ -925,9 +929,9 @@ D8715DA51C21107F005F4191 /* NSObject.swift in Sources */, D8715D9F1C210FF9005F4191 /* Signal.swift in Sources */, C72CF3E81CBF188A00E19897 /* RACSignal.swift in Sources */, - D8715DA41C21107F005F4191 /* NSData.swift in Sources */, + D8715DA41C21107F005F4191 /* Data.swift in Sources */, D8715D9D1C210FF9005F4191 /* Action.swift in Sources */, - D8715DA61C21107F005F4191 /* NSUserDefaults.swift in Sources */, + D8715DA61C21107F005F4191 /* UserDefaults.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -951,12 +955,12 @@ D8715DBC1C2112D1005F4191 /* Signal.swift in Sources */, C72CF3E71CBF188A00E19897 /* RACSignal.swift in Sources */, E6933BED1CD9C37D006F7CE7 /* UIProgressView.swift in Sources */, - D8715DBF1C2112D6005F4191 /* NSData.swift in Sources */, + D8715DBF1C2112D6005F4191 /* Data.swift in Sources */, D8715DCC1C211553005F4191 /* UIView.swift in Sources */, D8715DBA1C2112D1005F4191 /* Action.swift in Sources */, D8715DC81C211553005F4191 /* UIButton.swift in Sources */, D8715DC71C211553005F4191 /* UIBarItem.swift in Sources */, - D8715DC11C2112D6005F4191 /* NSUserDefaults.swift in Sources */, + D8715DC11C2112D6005F4191 /* UserDefaults.swift in Sources */, 45CED4721D27C1EC00788BDC /* UIActivityIndicatorView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1043,6 +1047,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; @@ -1083,6 +1088,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; @@ -1109,7 +1115,6 @@ INFOPLIST_FILE = Source/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "me.neilpa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; SKIP_INSTALL = YES; @@ -1135,10 +1140,10 @@ INFOPLIST_FILE = Source/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "me.neilpa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; }; name = Release; }; @@ -1175,6 +1180,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "me.neilpa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; }; name = Release; }; @@ -1231,6 +1237,7 @@ PRODUCT_NAME = "$(PROJECT_NAME)"; SDKROOT = iphoneos; SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -1274,6 +1281,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "me.neilpa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; VALIDATE_PRODUCT = YES; }; name = Release; @@ -1321,6 +1329,7 @@ PRODUCT_NAME = "$(PROJECT_NAME)"; SDKROOT = watchos; SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = 4; VALIDATE_PRODUCT = YES; WATCHOS_DEPLOYMENT_TARGET = 2.0; @@ -1370,6 +1379,7 @@ PRODUCT_NAME = "$(PROJECT_NAME)"; SDKROOT = appletvos; SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = 3; TVOS_DEPLOYMENT_TARGET = 9.0; VALIDATE_PRODUCT = YES; @@ -1404,6 +1414,7 @@ PRODUCT_BUNDLE_IDENTIFIER = me.neilpa.RexTests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TVOS_DEPLOYMENT_TARGET = 9.0; VALIDATE_PRODUCT = YES; }; @@ -1412,7 +1423,7 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - D8003E881AFEC3D400D7D3C5 /* Build configuration list for PBXProject "Rex-Mac" */ = { + D8003E881AFEC3D400D7D3C5 /* Build configuration list for PBXProject "Rex" */ = { isa = XCConfigurationList; buildConfigurations = ( D8003EA21AFEC3D400D7D3C5 /* Debug */, diff --git a/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme index cc58cdc730..3a84fae0dc 100644 --- a/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme +++ b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme @@ -1,6 +1,6 @@ { - return self.executing.signal + return self.isExecuting.signal .filterMap { $0 ? () : nil } } @@ -25,7 +25,7 @@ extension Action { public var rex_completed: Signal { return events .filterMap { event -> Void? in - if case .Completed = event { + if case .completed = event { return () } else { return nil diff --git a/Source/AppKit/NSTextField.swift b/Source/AppKit/NSTextField.swift index cf14eee2df..4f2ba9875a 100644 --- a/Source/AppKit/NSTextField.swift +++ b/Source/AppKit/NSTextField.swift @@ -13,8 +13,8 @@ import enum Result.NoError extension NSTextField { /// Sends the field's string value whenever it changes. public var rex_textSignal: SignalProducer { - return NSNotificationCenter.defaultCenter() - .rac_notifications(NSControlTextDidChangeNotification, object: self) + return NotificationCenter.default + .rac_notifications(forName: .NSControlTextDidChange, object: self) .map { notification in (notification.object as! NSTextField).stringValue } diff --git a/Source/Foundation/Association.swift b/Source/Foundation/Association.swift index 976b6c9b98..8be4d03066 100644 --- a/Source/Foundation/Association.swift +++ b/Source/Foundation/Association.swift @@ -15,13 +15,12 @@ import ReactiveCocoa /// /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. -@warn_unused_result(message="Did you forget to use the property?") -public func associatedProperty(host: AnyObject, keyPath: StaticString) -> MutableProperty { - let initial: AnyObject -> String = { host in - host.valueForKeyPath(keyPath.stringValue) as? String ?? "" +public func associatedProperty(_ host: AnyObject, keyPath: StaticString) -> MutableProperty { + let initial: (AnyObject) -> String = { host in + host.value(forKeyPath: String(keyPath)) as? String ?? "" } let setter: (AnyObject, String) -> () = { host, newValue in - host.setValue(newValue, forKeyPath: keyPath.stringValue) + host.setValue(newValue, forKeyPath: String(keyPath)) } return associatedProperty(host, key: keyPath.utf8Start, initial: initial, setter: setter) } @@ -33,13 +32,12 @@ public func associatedProperty(host: AnyObject, keyPath: StaticString) -> Mutabl /// /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. -@warn_unused_result(message="Did you forget to use the property?") -public func associatedProperty(host: AnyObject, keyPath: StaticString, @noescape placeholder: () -> T) -> MutableProperty { +public func associatedProperty(_ host: AnyObject, keyPath: StaticString, placeholder: @noescape () -> T) -> MutableProperty { let setter: (AnyObject, T) -> () = { host, newValue in - host.setValue(newValue, forKeyPath: keyPath.stringValue) + host.setValue(newValue, forKeyPath: String(keyPath)) } return associatedProperty(host, key: keyPath.utf8Start, initial: { host in - host.valueForKeyPath(keyPath.stringValue) as? T ?? placeholder() + host.value(forKeyPath: String(keyPath)) as? T ?? placeholder() }, setter: setter) } @@ -49,8 +47,7 @@ public func associatedProperty(host: AnyObject, keyPath: StaticStr /// /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. -@warn_unused_result(message="Did you forget to use the property?") -public func associatedProperty(host: Host, key: UnsafePointer<()>, @noescape initial: Host -> T, setter: (Host, T) -> (), @noescape setUp: MutableProperty -> () = { _ in }) -> MutableProperty { +public func associatedProperty(_ host: Host, key: UnsafePointer<()>, initial: @noescape (Host) -> T, setter: (Host, T) -> (), setUp: @noescape (MutableProperty) -> () = { _ in }) -> MutableProperty { return associatedObject(host, key: key) { host in let property = MutableProperty(initial(host)) @@ -69,7 +66,7 @@ public func associatedProperty(host: Host, key: UnsafePointe /// On first use attaches the object returned from `initial` to the `host` object using /// `key` via `objc_setAssociatedObject`. On subsequent usage, returns said object via /// `objc_getAssociatedObject`. -public func associatedObject(host: Host, key: UnsafePointer<()>, @noescape initial: Host -> T) -> T { +public func associatedObject(_ host: Host, key: UnsafePointer<()>, initial: @noescape (Host) -> T) -> T { var value = objc_getAssociatedObject(host, key) as? T if value == nil { value = initial(host) diff --git a/Source/Foundation/Data.swift b/Source/Foundation/Data.swift new file mode 100644 index 0000000000..91d5403b1d --- /dev/null +++ b/Source/Foundation/Data.swift @@ -0,0 +1,39 @@ +// +// Data.swift +// Rex +// +// Created by Ilya Laryionau on 10/05/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa + +extension Data { + /// Read the data at the URL, sending the result or an error. + public static func rex_dataWithContentsOfURL(_ url: URL, options: Data.ReadingOptions = []) -> SignalProducer { + return SignalProducer { observer, disposable in + do { + let data = try Data(contentsOf: url, options: options) + observer.sendNext(data) + observer.sendCompleted() + } catch { + observer.sendFailed(error as NSError) + } + } + } +} + +extension NSData { + /// Read the data at the URL, sending the result or an error. + public static func rex_dataWithContentsOfURL(_ url: URL, options: NSData.ReadingOptions = []) -> SignalProducer { + return SignalProducer { observer, disposable in + do { + let data = try NSData(contentsOf: url, options: options) + observer.sendNext(data) + observer.sendCompleted() + } catch { + observer.sendFailed(error as NSError) + } + } + } +} diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index c839ef1785..5747ef1511 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -15,16 +15,15 @@ extension NSObject { /// /// Swift classes deriving `NSObject` must declare properties as `dynamic` for /// them to work with KVO. However, this is not recommended practice. - @warn_unused_result(message="Did you forget to call `start` on the producer?") - public func rex_producerForKeyPath(keyPath: String) -> SignalProducer { - return self.rac_valuesForKeyPath(keyPath, observer: nil) + public func rex_producerForKeyPath(_ keyPath: String) -> SignalProducer { + return self.rac_values(forKeyPath: keyPath, observer: nil) .toSignalProducer() .map { $0 as! T } .flatMapError { error in // Errors aren't possible, but the compiler doesn't know that. assertionFailure("Unexpected error from KVO signal: \(error)") return .empty - } + } } /// Creates a signal that will be triggered when the object diff --git a/Source/Foundation/NSUserDefaults.swift b/Source/Foundation/UserDefaults.swift similarity index 69% rename from Source/Foundation/NSUserDefaults.swift rename to Source/Foundation/UserDefaults.swift index 702dd932b7..73282cd657 100644 --- a/Source/Foundation/NSUserDefaults.swift +++ b/Source/Foundation/UserDefaults.swift @@ -9,21 +9,20 @@ import ReactiveCocoa import enum Result.NoError -extension NSUserDefaults { +extension UserDefaults { /// Sends value of `key` whenever it changes. Attempts to filter out repeats /// by casting to NSObject and checking for equality. If the values aren't /// convertible this will generate events whenever _any_ value in NSUserDefaults /// changes. - @warn_unused_result(message="Did you forget to call `start` on the producer?") - public func rex_valueForKey(key: String) -> SignalProducer { - let center = NSNotificationCenter.defaultCenter() - let initial = objectForKey(key) + public func rex_valueForKey(_ key: String) -> SignalProducer { + let center = NotificationCenter.default + let initial = object(forKey: key) - let changes = center.rac_notifications(NSUserDefaultsDidChangeNotification) + let changes = center.rac_notifications(forName: UserDefaults.didChangeNotification) .map { _ in // The notification doesn't provide what changed so we have to look // it up every time - self.objectForKey(key) + self.object(forKey: key) } return SignalProducer(value: initial) diff --git a/Source/Property.swift b/Source/Property.swift index 230d0ab6d7..c743c4e746 100644 --- a/Source/Property.swift +++ b/Source/Property.swift @@ -9,40 +9,35 @@ import ReactiveCocoa import enum Result.NoError -extension PropertyType where Value == Bool { +extension PropertyProtocol where Value == Bool { /// The conjunction of `self` and `other`. - @warn_unused_result(message="Did you forget to use the property?") - public func and(other: P) -> AndProperty { + public func and(_ other: P) -> AndProperty { return AndProperty(terms: [AnyProperty(self), AnyProperty(other)]) } /// The conjunction of `self` and `other`. - @warn_unused_result(message="Did you forget to use the property?") - public func and(other: AnyProperty) -> AndProperty { + public func and(_ other: AnyProperty) -> AndProperty { return AndProperty(terms: [AnyProperty(self), other]) } /// The disjunction of `self` and `other`. - @warn_unused_result(message="Did you forget to use the property?") - public func or(other: P) -> OrProperty { + public func or(_ other: P) -> OrProperty { return OrProperty(terms: [AnyProperty(self), AnyProperty(other)]) } /// The disjunction of `self` and `other`. - @warn_unused_result(message="Did you forget to use the property?") - public func or(other: AnyProperty) -> OrProperty { + public func or(_ other: AnyProperty) -> OrProperty { return OrProperty(terms: [AnyProperty(self), other]) } /// A negated property of `self`. - @warn_unused_result(message="Did you forget to use the property?") public func not() -> NotProperty { return NotProperty(source: AnyProperty(self), invert: true) } } /// Specialized `PropertyType` for the conjuction of a set of boolean properties. -public struct AndProperty: PropertyType { +public struct AndProperty: PropertyProtocol { public let terms: [AnyProperty] public var value: Bool { @@ -51,27 +46,25 @@ public struct AndProperty: PropertyType { public var producer: SignalProducer { let producers = terms.map { $0.producer } - return combineLatest(producers).map { values in + return SignalProducer.combineLatest(producers).map { values in return values.reduce(true) { $0 && $1 } } } public var signal: Signal { let signals = terms.map { $0.signal } - return combineLatest(signals).map { values in + return Signal.combineLatest(signals).map { values in return values.reduce(true) { $0 && $1 } } } /// Creates a new property with an additional conjunctive term. - @warn_unused_result(message="Did you forget to use the property?") - public func and

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

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

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

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

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

(_ other: P) -> OrProperty { - return OrProperty(terms: terms + [AnyProperty(other)]) + return OrProperty(terms: terms + [Property(other)]) } /// Creates a new property with an additional disjunctive term. - public func or(_ other: AnyProperty) -> OrProperty { + public func or(_ other: Property) -> OrProperty { return OrProperty(terms: terms + [other]) } - private init(terms: [AnyProperty]) { + private init(terms: [Property]) { self.terms = terms } } /// Specialized `PropertyType` for the negation of a boolean property. -public struct NotProperty: PropertyProtocol { - private let source: AnyProperty +public class NotProperty: PropertyProtocol { + private let source: Property private let invert: Bool public var value: Bool { @@ -132,7 +132,7 @@ public struct NotProperty: PropertyProtocol { return NotProperty(source: source, invert: !invert) } - private init(source: AnyProperty, invert: Bool) { + private init(source: Property, invert: Bool) { self.source = source self.invert = invert } From 15dca561b97e71c263c339847e8a1270ca4adc1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastia=CC=81n=20Varela?= Date: Fri, 5 Aug 2016 13:28:16 +0200 Subject: [PATCH 0317/1028] Partial support for Xcode8b4 --- Cartfile.resolved | 4 ++-- Deprecations+Removals.swift | 2 +- Rex.xcodeproj/project.pbxproj | 6 +++++ Source/Foundation/Association.swift | 10 ++++---- Source/Foundation/Data.swift | 2 +- Source/SignalProducer.swift | 16 ++++++------- Source/UIKit/UIActivityIndicatorView.swift | 3 +-- Source/UIKit/UILabel.swift | 2 +- Tests/ActionTests.swift | 2 +- Tests/Foundation/NSObjectTests.swift | 4 ++-- Tests/SignalTests.swift | 2 +- .../UIKit/UIActivityIndicatorViewTests.swift | 4 ++-- Tests/UIKit/UIDatePickerTests.swift | 4 ++-- Tests/UIKit/UILabelTests.swift | 6 ++--- Tests/UIKit/UITextFieldTests.swift | 4 ++-- Tests/UIKit/UITextViewTests.swift | 4 ++-- Tests/UIKit/UIViewControllerTests.swift | 24 +++++++++---------- 17 files changed, 52 insertions(+), 47 deletions(-) diff --git a/Cartfile.resolved b/Cartfile.resolved index 11be7c674d..8251c3bf4d 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ -github "antitypical/Result" "8f1affd40bc2b10a15f56ff6cb7b00639e29cb35" -github "ReactiveCocoa/ReactiveCocoa" "8213f2e47c3228ad1895bf74c9b6dba541a45aa7" +github "jameshurst/Result" "2401fa7584183da17f40335816073bf511fb34b8" +github "ReactiveCocoa/ReactiveCocoa" "b4d757433574f85519bb3253a631955da937106a" diff --git a/Deprecations+Removals.swift b/Deprecations+Removals.swift index 5be18a9292..a6de0129d9 100644 --- a/Deprecations+Removals.swift +++ b/Deprecations+Removals.swift @@ -39,7 +39,7 @@ extension Data { public static func rex_dataWithContentsOfURL(_ url: URL, options: Data.ReadingOptions = []) -> SignalProducer { fatalError() } } -extension NSData { +extension Data { @available(*, unavailable, renamed:"rex_data(contentsOf:options:)") public static func rex_dataWithContentsOfURL(_ url: URL, options: NSData.ReadingOptions = []) -> SignalProducer { fatalError() } } diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index efb29c157f..62cea14b70 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -740,9 +740,11 @@ }; D83457131AFEE44E0070616A = { CreatedOnToolsVersion = 6.3; + LastSwiftMigration = 0800; }; D834571D1AFEE44E0070616A = { CreatedOnToolsVersion = 6.3; + LastSwiftMigration = 0800; }; D8715D931C210F97005F4191 = { CreatedOnToolsVersion = 7.2; @@ -1221,6 +1223,7 @@ PRODUCT_NAME = "$(PROJECT_NAME)"; SDKROOT = iphoneos; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -1248,6 +1251,7 @@ SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -1273,6 +1277,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "me.neilpa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -1292,6 +1297,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 3.0; VALIDATE_PRODUCT = YES; }; name = Release; diff --git a/Source/Foundation/Association.swift b/Source/Foundation/Association.swift index 8be4d03066..56c492c588 100644 --- a/Source/Foundation/Association.swift +++ b/Source/Foundation/Association.swift @@ -15,14 +15,14 @@ import ReactiveCocoa /// /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. -public func associatedProperty(_ host: AnyObject, keyPath: StaticString) -> MutableProperty { +public func associatedProperty(host: AnyObject, keyPath: StaticString) -> MutableProperty { let initial: (AnyObject) -> String = { host in host.value(forKeyPath: String(keyPath)) as? String ?? "" } let setter: (AnyObject, String) -> () = { host, newValue in host.setValue(newValue, forKeyPath: String(keyPath)) } - return associatedProperty(host, key: keyPath.utf8Start, initial: initial, setter: setter) + return associatedProperty(host: host, key: keyPath.utf8Start, initial: initial, setter: setter) } /// Attaches a `MutableProperty` value to the `host` object using KVC to get the initial @@ -32,11 +32,11 @@ public func associatedProperty(_ host: AnyObject, keyPath: StaticString) -> Muta /// /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. -public func associatedProperty(_ host: AnyObject, keyPath: StaticString, placeholder: @noescape () -> T) -> MutableProperty { +public func associatedProperty(host: AnyObject, keyPath: StaticString, placeholder: @noescape () -> T) -> MutableProperty { let setter: (AnyObject, T) -> () = { host, newValue in host.setValue(newValue, forKeyPath: String(keyPath)) } - return associatedProperty(host, key: keyPath.utf8Start, initial: { host in + return associatedProperty(host: host, key: keyPath.utf8Start, initial: { host in host.value(forKeyPath: String(keyPath)) as? T ?? placeholder() }, setter: setter) } @@ -47,7 +47,7 @@ public func associatedProperty(_ host: AnyObject, keyPath: StaticS /// /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. -public func associatedProperty(_ host: Host, key: UnsafePointer<()>, initial: @noescape (Host) -> T, setter: (Host, T) -> (), setUp: @noescape (MutableProperty) -> () = { _ in }) -> MutableProperty { +public func associatedProperty(host: Host, key: UnsafePointer<()>, initial: @noescape (Host) -> T, setter: (Host, T) -> (), setUp: @noescape (MutableProperty) -> () = { _ in }) -> MutableProperty { return associatedObject(host, key: key) { host in let property = MutableProperty(initial(host)) diff --git a/Source/Foundation/Data.swift b/Source/Foundation/Data.swift index dc5bf7136e..8f9527ce87 100644 --- a/Source/Foundation/Data.swift +++ b/Source/Foundation/Data.swift @@ -23,7 +23,7 @@ extension Data { } } -extension NSData { +extension Data { /// Read the data at the URL, sending the result or an error. public static func rex_data(contentsOf url: URL, options: NSData.ReadingOptions = []) -> SignalProducer { return Data.rex_data(contentsOf: url, options: options).map { $0 as NSData } diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index 69dbd79edb..7d02de4129 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -18,7 +18,7 @@ extension SignalProducerProtocol { return SignalProducer<(Key, SignalProducer), Error> { observer, disposable in var groups: [Key: Signal.Observer] = [:] - let lock = RecursiveLock() + let lock = NSRecursiveLock() lock.name = "me.neilpa.rex.groupBy" self.start { event in @@ -96,7 +96,7 @@ extension SignalProducerProtocol { } /// Delays retrying on failure by `interval` up to `count` attempts. - public func deferredRetry(interval: TimeInterval, on scheduler: DateSchedulerProtocol, count: Int = .max) -> SignalProducer { + public func deferredRetry(_ interval: TimeInterval, on scheduler: DateSchedulerProtocol, count: Int = .max) -> SignalProducer { precondition(count >= 0) if count == 0 { @@ -205,25 +205,25 @@ private final class ReplayBuffer { private var values: [Value] = [] } -private struct BufferState { +private struct BufferState { /// All values in the buffer. - var values: [Value] = [] + var values: [V] = [] /// Any terminating event sent to the buffer. /// /// This will be nil if termination has not occurred. - var terminationEvent: Event? + var terminationEvent: Event? /// The observers currently attached to the buffered producer, or nil if the /// producer was terminated. - var observers: Bag.Observer>? = Bag() + var observers: Bag.Observer>? = Bag() /// The set of unused replay token identifiers. - var replayBuffers: Bag> = Bag() + var replayBuffers: Bag> = Bag() /// Appends a new value to the buffer, trimming it down to the given capacity /// if necessary. - mutating func add(_ value: Value, upTo capacity: Int) { + mutating func add(_ value: V, upTo capacity: Int) { precondition(capacity >= 0) for buffer in replayBuffers { diff --git a/Source/UIKit/UIActivityIndicatorView.swift b/Source/UIKit/UIActivityIndicatorView.swift index 179b795090..6833ef6946 100644 --- a/Source/UIKit/UIActivityIndicatorView.swift +++ b/Source/UIKit/UIActivityIndicatorView.swift @@ -15,7 +15,7 @@ extension UIActivityIndicatorView { /// Setting a new value to the property would call `startAnimating()` or /// `stopAnimating()` depending on the value. public var rex_animating: MutableProperty { - return associatedProperty(self, key: &animatingKey, initial: { $0.isAnimating() }, setter: { host, animating in + return associatedProperty(host: self, key: &animatingKey, initial: { $0.isAnimating() }, setter: { host, animating in if animating { host.startAnimating() } else { @@ -23,7 +23,6 @@ extension UIActivityIndicatorView { } }) } - } private var animatingKey: UInt8 = 0 diff --git a/Source/UIKit/UILabel.swift b/Source/UIKit/UILabel.swift index 0078d7cd0e..c47746f326 100644 --- a/Source/UIKit/UILabel.swift +++ b/Source/UIKit/UILabel.swift @@ -16,7 +16,7 @@ extension UILabel { } /// Wraps a label's `attributedText` value in a bindable property. - public var rex_attributedText: MutableProperty { + public var rex_attributedText: MutableProperty { return associatedProperty(self, key: &attributedTextKey, initial: { $0.attributedText }, setter: { $0.attributedText = $1 }) } diff --git a/Tests/ActionTests.swift b/Tests/ActionTests.swift index b6d7338851..2c4673a5b4 100644 --- a/Tests/ActionTests.swift +++ b/Tests/ActionTests.swift @@ -13,7 +13,7 @@ import enum Result.NoError final class ActionTests: XCTestCase { - enum TestError: ErrorProtocol { + enum TestError: Error { case unknown } diff --git a/Tests/Foundation/NSObjectTests.swift b/Tests/Foundation/NSObjectTests.swift index 83cee3ae2f..38ba1de051 100644 --- a/Tests/Foundation/NSObjectTests.swift +++ b/Tests/Foundation/NSObjectTests.swift @@ -25,8 +25,8 @@ final class NSObjectTests: XCTestCase { func testObjectsWillBeDeallocatedSignal() { - let expectation = self.expectation(withDescription: "Expected timer to send `completed` event when object deallocates") - defer { self.waitForExpectations(withTimeout: 2, handler: nil) } + let expectation = self.expectation(description: "Expected timer to send `completed` event when object deallocates") + defer { self.waitForExpectations(timeout: 2, handler: nil) } let object = Object() diff --git a/Tests/SignalTests.swift b/Tests/SignalTests.swift index 350e98039c..26860472b5 100644 --- a/Tests/SignalTests.swift +++ b/Tests/SignalTests.swift @@ -186,6 +186,6 @@ final class SignalTests: XCTestCase { } } -enum TestError: ErrorProtocol { +enum TestError: Error { case `default` } diff --git a/Tests/UIKit/UIActivityIndicatorViewTests.swift b/Tests/UIKit/UIActivityIndicatorViewTests.swift index ece8aef930..df5051d349 100644 --- a/Tests/UIKit/UIActivityIndicatorViewTests.swift +++ b/Tests/UIKit/UIActivityIndicatorViewTests.swift @@ -27,8 +27,8 @@ class UIActivityIndicatorTests: XCTestCase { indicatorView.rex_animating <~ SignalProducer(signal: pipeSignal) observer.sendNext(true) - XCTAssertTrue(indicatorView.isAnimating()) + XCTAssertTrue(indicatorView.isAnimating) observer.sendNext(false) - XCTAssertFalse(indicatorView.isAnimating()) + XCTAssertFalse(indicatorView.isAnimating) } } diff --git a/Tests/UIKit/UIDatePickerTests.swift b/Tests/UIKit/UIDatePickerTests.swift index 20ac37e360..55d9362631 100644 --- a/Tests/UIKit/UIDatePickerTests.swift +++ b/Tests/UIKit/UIDatePickerTests.swift @@ -31,8 +31,8 @@ class UIDatePickerTests: XCTestCase { } func testUpdatePropertyFromPicker() { - let expectation = self.expectation(withDescription: "Expected rex_date to send an event when picker's date value is changed by a UI event") - defer { self.waitForExpectations(withTimeout: 2, handler: nil) } + let expectation = self.expectation(description: "Expected rex_date to send an event when picker's date value is changed by a UI event") + defer { self.waitForExpectations(timeout: 2, handler: nil) } picker.rex_date.signal.observeNext { changedDate in XCTAssertEqual(changedDate, self.date) diff --git a/Tests/UIKit/UILabelTests.swift b/Tests/UIKit/UILabelTests.swift index ded0e7d765..3e9fcf500a 100644 --- a/Tests/UIKit/UILabelTests.swift +++ b/Tests/UIKit/UILabelTests.swift @@ -71,13 +71,13 @@ class UILabelTests: XCTestCase { } func testTextColorProperty() { - let firstChange = UIColor.red() - let secondChange = UIColor.black() + let firstChange = UIColor.red + let secondChange = UIColor.black let label = UILabel(frame: CGRect.zero) let (pipeSignal, observer) = Signal.pipe() - label.textColor = UIColor.black() + label.textColor = UIColor.black label.rex_textColor <~ SignalProducer(signal: pipeSignal) observer.sendNext(firstChange) diff --git a/Tests/UIKit/UITextFieldTests.swift b/Tests/UIKit/UITextFieldTests.swift index 4bc48b7657..8ce8d830d1 100644 --- a/Tests/UIKit/UITextFieldTests.swift +++ b/Tests/UIKit/UITextFieldTests.swift @@ -13,8 +13,8 @@ import XCTest class UITextFieldTests: XCTestCase { func testTextProperty() { - let expectation = self.expectation(withDescription: "Expected `rex_text`'s value to equal to the textField's text") - defer { self.waitForExpectations(withTimeout: 2, handler: nil) } + let expectation = self.expectation(description: "Expected `rex_text`'s value to equal to the textField's text") + defer { self.waitForExpectations(timeout: 2, handler: nil) } let textField = UITextField(frame: CGRect.zero) textField.text = "Test" diff --git a/Tests/UIKit/UITextViewTests.swift b/Tests/UIKit/UITextViewTests.swift index 6374fe58bc..d99f0e9a9f 100644 --- a/Tests/UIKit/UITextViewTests.swift +++ b/Tests/UIKit/UITextViewTests.swift @@ -13,8 +13,8 @@ import XCTest class UITextViewTests: XCTestCase { func testTextProperty() { - let expectation = self.expectation(withDescription: "Expected `rex_text`'s value to equal to the textViews's text") - defer { self.waitForExpectations(withTimeout: 2, handler: nil) } + let expectation = self.expectation(description: "Expected `rex_text`'s value to equal to the textViews's text") + defer { self.waitForExpectations(timeout: 2, handler: nil) } let textView = UITextView(frame: CGRect.zero) textView.text = "Test" diff --git a/Tests/UIKit/UIViewControllerTests.swift b/Tests/UIKit/UIViewControllerTests.swift index 9d371e2d83..315b723d3e 100644 --- a/Tests/UIKit/UIViewControllerTests.swift +++ b/Tests/UIKit/UIViewControllerTests.swift @@ -22,8 +22,8 @@ class UIViewControllerTests: XCTestCase { func testViewDidDisappear() { - let expectation = self.expectation(withDescription: "Expected rex_viewDidDisappear to be triggered") - defer { self.waitForExpectations(withTimeout: 2, handler: nil) } + let expectation = self.expectation(description: "Expected rex_viewDidDisappear to be triggered") + defer { self.waitForExpectations(timeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController @@ -37,8 +37,8 @@ class UIViewControllerTests: XCTestCase { func testViewWillDisappear() { - let expectation = self.expectation(withDescription: "Expected rex_viewWillDisappear to be triggered") - defer { self.waitForExpectations(withTimeout: 2, handler: nil) } + let expectation = self.expectation(description: "Expected rex_viewWillDisappear to be triggered") + defer { self.waitForExpectations(timeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController @@ -52,8 +52,8 @@ class UIViewControllerTests: XCTestCase { func testViewDidAppear() { - let expectation = self.expectation(withDescription: "Expected rex_viewDidAppear to be triggered") - defer { self.waitForExpectations(withTimeout: 2, handler: nil) } + let expectation = self.expectation(description: "Expected rex_viewDidAppear to be triggered") + defer { self.waitForExpectations(timeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController @@ -67,8 +67,8 @@ class UIViewControllerTests: XCTestCase { func testViewWillAppear() { - let expectation = self.expectation(withDescription: "Expected rex_viewWillAppear to be triggered") - defer { self.waitForExpectations(withTimeout: 2, handler: nil) } + let expectation = self.expectation(description: "Expected rex_viewWillAppear to be triggered") + defer { self.waitForExpectations(timeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController @@ -82,8 +82,8 @@ class UIViewControllerTests: XCTestCase { func testDismissViewController_via_property() { - let expectation = self.expectation(withDescription: "Expected rex_dismissModally to be triggered") - defer { self.waitForExpectations(withTimeout: 2, handler: nil) } + let expectation = self.expectation(description: "Expected rex_dismissModally to be triggered") + defer { self.waitForExpectations(timeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController @@ -97,8 +97,8 @@ class UIViewControllerTests: XCTestCase { func testDismissViewController_via_cocoaDismiss() { - let expectation = self.expectation(withDescription: "Expected rex_dismissModally to be triggered") - defer { self.waitForExpectations(withTimeout: 2, handler: nil) } + let expectation = self.expectation(description: "Expected rex_dismissModally to be triggered") + defer { self.waitForExpectations(timeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController From c2404eb4c0f4e5eacebaede8aa76ba7275422179 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastia=CC=81n=20Varela?= Date: Fri, 5 Aug 2016 13:35:56 +0200 Subject: [PATCH 0318/1028] Fix Build on Xcode8b4 --- Source/Foundation/Association.swift | 10 +++++----- Source/UIKit/UIActivityIndicatorView.swift | 18 +++++++++--------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Source/Foundation/Association.swift b/Source/Foundation/Association.swift index 56c492c588..8be4d03066 100644 --- a/Source/Foundation/Association.swift +++ b/Source/Foundation/Association.swift @@ -15,14 +15,14 @@ import ReactiveCocoa /// /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. -public func associatedProperty(host: AnyObject, keyPath: StaticString) -> MutableProperty { +public func associatedProperty(_ host: AnyObject, keyPath: StaticString) -> MutableProperty { let initial: (AnyObject) -> String = { host in host.value(forKeyPath: String(keyPath)) as? String ?? "" } let setter: (AnyObject, String) -> () = { host, newValue in host.setValue(newValue, forKeyPath: String(keyPath)) } - return associatedProperty(host: host, key: keyPath.utf8Start, initial: initial, setter: setter) + return associatedProperty(host, key: keyPath.utf8Start, initial: initial, setter: setter) } /// Attaches a `MutableProperty` value to the `host` object using KVC to get the initial @@ -32,11 +32,11 @@ public func associatedProperty(host: AnyObject, keyPath: StaticString) -> Mutabl /// /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. -public func associatedProperty(host: AnyObject, keyPath: StaticString, placeholder: @noescape () -> T) -> MutableProperty { +public func associatedProperty(_ host: AnyObject, keyPath: StaticString, placeholder: @noescape () -> T) -> MutableProperty { let setter: (AnyObject, T) -> () = { host, newValue in host.setValue(newValue, forKeyPath: String(keyPath)) } - return associatedProperty(host: host, key: keyPath.utf8Start, initial: { host in + return associatedProperty(host, key: keyPath.utf8Start, initial: { host in host.value(forKeyPath: String(keyPath)) as? T ?? placeholder() }, setter: setter) } @@ -47,7 +47,7 @@ public func associatedProperty(host: AnyObject, keyPath: StaticStr /// /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. -public func associatedProperty(host: Host, key: UnsafePointer<()>, initial: @noescape (Host) -> T, setter: (Host, T) -> (), setUp: @noescape (MutableProperty) -> () = { _ in }) -> MutableProperty { +public func associatedProperty(_ host: Host, key: UnsafePointer<()>, initial: @noescape (Host) -> T, setter: (Host, T) -> (), setUp: @noescape (MutableProperty) -> () = { _ in }) -> MutableProperty { return associatedObject(host, key: key) { host in let property = MutableProperty(initial(host)) diff --git a/Source/UIKit/UIActivityIndicatorView.swift b/Source/UIKit/UIActivityIndicatorView.swift index 6833ef6946..b2579b1427 100644 --- a/Source/UIKit/UIActivityIndicatorView.swift +++ b/Source/UIKit/UIActivityIndicatorView.swift @@ -14,15 +14,15 @@ extension UIActivityIndicatorView { /// Wraps an indicator's `isAnimating()` state in a bindable property. /// Setting a new value to the property would call `startAnimating()` or /// `stopAnimating()` depending on the value. - public var rex_animating: MutableProperty { - return associatedProperty(host: self, key: &animatingKey, initial: { $0.isAnimating() }, setter: { host, animating in - if animating { - host.startAnimating() - } else { - host.stopAnimating() - } - }) - } +// public var rex_animating: MutableProperty { +// return associatedProperty(self, key: &animatingKey, initial: { $0.isAnimating() }, setter: { host, animating in +// if animating { +// host.startAnimating() +// } else { +// host.stopAnimating() +// } +// }) +// } } private var animatingKey: UInt8 = 0 From db07e80d7e99888dfc10ac6a2ece9b93ad190c36 Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Fri, 5 Aug 2016 09:52:26 -0400 Subject: [PATCH 0319/1028] Move swizzling helper out of framework target UIControl+EnableSendActionsForControlEvents.swift is located in the `Tests/Helpers/` directory, but was added to the framework target instead of the test bundle. --- Rex.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index a043fc43de..8408246ffc 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -12,9 +12,9 @@ 45CED4701D27C1E400788BDC /* UIActivityIndicatorViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CED46D1D27C1D400788BDC /* UIActivityIndicatorViewTests.swift */; }; 45CED4711D27C1EB00788BDC /* UIActivityIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CED46B1D27BB8700788BDC /* UIActivityIndicatorView.swift */; }; 45CED4721D27C1EC00788BDC /* UIActivityIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CED46B1D27BB8700788BDC /* UIActivityIndicatorView.swift */; }; + 4A8EDADB1D54D12400A1734C /* UIControl+EnableSendActionsForControlEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D0DABA91CCC381F00B6CD2B /* UIControl+EnableSendActionsForControlEvents.swift */; }; 5B7F81E31D0842AD0014B06D /* UISegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1C882D1D0715CE000B888F /* UISegmentedControl.swift */; }; 5B7F81E41D0842B50014B06D /* UISegmentedControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1C882F1D071639000B888F /* UISegmentedControlTests.swift */; }; - 7D0DABAA1CCC381F00B6CD2B /* UIControl+EnableSendActionsForControlEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D0DABA91CCC381F00B6CD2B /* UIControl+EnableSendActionsForControlEvents.swift */; }; 7D2AA99B1CB6EFEB008AB5C9 /* UISwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D2AA99A1CB6EFEB008AB5C9 /* UISwitch.swift */; }; 7D2AA99D1CB6F275008AB5C9 /* UISwitchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D2AA99C1CB6F275008AB5C9 /* UISwitchTests.swift */; }; 7D5FE3081CD4B04E00834675 /* UITextFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E851C4B420A00086F3C /* UITextFieldTests.swift */; }; @@ -882,7 +882,6 @@ D8F0973F1B17F31E002E15BA /* NSData.swift in Sources */, D86FFBDD1B34B691001A89B3 /* UIButton.swift in Sources */, 45CED4711D27C1EB00788BDC /* UIActivityIndicatorView.swift in Sources */, - 7D0DABAA1CCC381F00B6CD2B /* UIControl+EnableSendActionsForControlEvents.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -906,6 +905,7 @@ C7945F141CC1DFBE00DC9E37 /* UIViewControllerTests.swift in Sources */, 7DC3257F1CC6FD1E00746D88 /* UITableViewCellTests.swift in Sources */, 9DA915A61CA63046003723B9 /* UIDatePickerTests.swift in Sources */, + 4A8EDADB1D54D12400A1734C /* UIControl+EnableSendActionsForControlEvents.swift in Sources */, D83457301AFEE45E0070616A /* SignalProducerTests.swift in Sources */, D83457411AFEE6050070616A /* SignalTests.swift in Sources */, 8295FD8D1B87374A007C9000 /* UIBarButtonItemTests.swift in Sources */, From 28f983107c4a1a5ead8ca3065b9158f851646868 Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Mon, 25 Jul 2016 16:58:43 -0600 Subject: [PATCH 0320/1028] Use SWIFT_VERSION = 2.3 and record Xcode migration --- Rex.xcodeproj/project.pbxproj | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 8408246ffc..fdd1660dd6 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -724,24 +724,31 @@ TargetAttributes = { D8003E8D1AFEC3D400D7D3C5 = { CreatedOnToolsVersion = 6.3; + LastSwiftMigration = 0800; }; D8003E981AFEC3D400D7D3C5 = { CreatedOnToolsVersion = 6.3; + LastSwiftMigration = 0800; }; D83457131AFEE44E0070616A = { CreatedOnToolsVersion = 6.3; + LastSwiftMigration = 0800; }; D834571D1AFEE44E0070616A = { CreatedOnToolsVersion = 6.3; + LastSwiftMigration = 0800; }; D8715D931C210F97005F4191 = { CreatedOnToolsVersion = 7.2; + LastSwiftMigration = 0800; }; D8715DAF1C21123E005F4191 = { CreatedOnToolsVersion = 7.2; + LastSwiftMigration = 0800; }; D8715DD01C21160A005F4191 = { CreatedOnToolsVersion = 7.2; + LastSwiftMigration = 0800; }; }; }; @@ -1048,6 +1055,7 @@ ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 2.3; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -1086,6 +1094,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; + SWIFT_VERSION = 2.3; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; From f143797efba91341a2b26443ca6f7234a883b476 Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Mon, 25 Jul 2016 17:00:20 -0600 Subject: [PATCH 0321/1028] Automatic Xcode project file changes --- Rex.xcodeproj/project.pbxproj | 54 +++++++++++++++++------------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index fdd1660dd6..ed0a7f56a0 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -56,8 +56,8 @@ CC02C18B1CCA704F0025CC04 /* ActionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC02C1881CCA704C0025CC04 /* ActionTests.swift */; }; CC02C18C1CCA704F0025CC04 /* ActionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC02C1881CCA704C0025CC04 /* ActionTests.swift */; }; D8003E941AFEC3D400D7D3C5 /* Rex.h in Headers */ = {isa = PBXBuildFile; fileRef = D8003E931AFEC3D400D7D3C5 /* Rex.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in (null) */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - D8003EB51AFEC6B000D7D3C5 /* Result.framework in (null) */ = {isa = PBXBuildFile; fileRef = D8003EAE1AFEC68A00D7D3C5 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in 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, ); }; }; D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */; }; D8003EBD1AFED01000D7D3C5 /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBC1AFED01000D7D3C5 /* Signal.swift */; }; D8003EC21AFED30F00D7D3C5 /* SignalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */; }; @@ -71,8 +71,8 @@ D83457331AFEE4930070616A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAE1AFEC68A00D7D3C5 /* Result.framework */; }; D83457351AFEE4B20070616A /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */; }; D83457361AFEE4B20070616A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */; }; - D83457391AFEE4BE0070616A /* ReactiveCocoa.framework in (null) */ = {isa = PBXBuildFile; fileRef = D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */; }; - D834573A1AFEE4BE0070616A /* Result.framework in (null) */ = {isa = PBXBuildFile; fileRef = D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */; }; + D83457391AFEE4BE0070616A /* ReactiveCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */; }; + D834573A1AFEE4BE0070616A /* Result.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */; }; D834573C1AFEE57E0070616A /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */; }; D834573D1AFEE57E0070616A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */; }; D83457411AFEE6050070616A /* SignalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */; }; @@ -125,8 +125,8 @@ D8715DE51C211643005F4191 /* UIViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */; }; D8715DE61C21170C005F4191 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8715DC21C211310005F4191 /* ReactiveCocoa.framework */; }; D8715DE71C21170C005F4191 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8715DC31C211310005F4191 /* Result.framework */; }; - D8715DE91C211739005F4191 /* ReactiveCocoa.framework in (null) */ = {isa = PBXBuildFile; fileRef = D8715DC21C211310005F4191 /* ReactiveCocoa.framework */; }; - D8715DEA1C211739005F4191 /* Result.framework in (null) */ = {isa = PBXBuildFile; fileRef = D8715DC31C211310005F4191 /* Result.framework */; }; + D8715DE91C211739005F4191 /* ReactiveCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8715DC21C211310005F4191 /* ReactiveCocoa.framework */; }; + D8715DEA1C211739005F4191 /* Result.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8715DC31C211310005F4191 /* Result.framework */; }; D8A454061BD26A1A00C9E790 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Property.swift */; }; D8A454071BD26A1A00C9E790 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Property.swift */; }; D8A454091BD2772700C9E790 /* PropertyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454081BD2772700C9E790 /* PropertyTests.swift */; }; @@ -140,12 +140,12 @@ D8F0973F1B17F31E002E15BA /* NSData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973A1B17F2F7002E15BA /* NSData.swift */; }; D8F097441B17F3C8002E15BA /* NSObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097431B17F3C8002E15BA /* NSObject.swift */; }; D8F097451B17F3C8002E15BA /* NSObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097431B17F3C8002E15BA /* NSObject.swift */; }; + D8F0974A1B17F5E1002E15BA /* NSObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097471B17F5DD002E15BA /* NSObjectTests.swift */; }; + D8F0974B1B17F5E2002E15BA /* NSObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097471B17F5DD002E15BA /* NSObjectTests.swift */; }; E6933BEA1CD9C335006F7CE7 /* UIProgressViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6933BE81CD9C1F1006F7CE7 /* UIProgressViewTests.swift */; }; E6933BEB1CD9C363006F7CE7 /* UIProgressViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6933BE81CD9C1F1006F7CE7 /* UIProgressViewTests.swift */; }; E6933BEC1CD9C37D006F7CE7 /* UIProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6933BE61CD9C0B2006F7CE7 /* UIProgressView.swift */; }; E6933BED1CD9C37D006F7CE7 /* UIProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6933BE61CD9C0B2006F7CE7 /* UIProgressView.swift */; }; - D8F0974A1B17F5E1002E15BA /* NSObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097471B17F5DD002E15BA /* NSObjectTests.swift */; }; - D8F0974B1B17F5E2002E15BA /* NSObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097471B17F5DD002E15BA /* NSObjectTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -173,49 +173,49 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ - D8003EB21AFEC6A800D7D3C5 /* (null) */ = { + D8003EB21AFEC6A800D7D3C5 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 16; files = ( - D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in (null) */, - D8003EB51AFEC6B000D7D3C5 /* Result.framework in (null) */, + D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in CopyFiles */, + D8003EB51AFEC6B000D7D3C5 /* Result.framework in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; - D83457371AFEE4B80070616A /* (null) */ = { + D83457371AFEE4B80070616A /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 16; files = ( - D83457391AFEE4BE0070616A /* ReactiveCocoa.framework in (null) */, - D834573A1AFEE4BE0070616A /* Result.framework in (null) */, + D83457391AFEE4BE0070616A /* ReactiveCocoa.framework in CopyFiles */, + D834573A1AFEE4BE0070616A /* Result.framework in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; - D8715DE81C21172A005F4191 /* (null) */ = { + D8715DE81C21172A005F4191 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 16; files = ( - D8715DE91C211739005F4191 /* ReactiveCocoa.framework in (null) */, - D8715DEA1C211739005F4191 /* Result.framework in (null) */, + D8715DE91C211739005F4191 /* ReactiveCocoa.framework in CopyFiles */, + D8715DEA1C211739005F4191 /* Result.framework in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 4238D5951B4D5950008534C0 /* NSTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; name = NSTextField.swift; path = AppKit/NSTextField.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 45CED46B1D27BB8700788BDC /* UIActivityIndicatorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorView.swift; sourceTree = ""; }; 45CED46D1D27C1D400788BDC /* UIActivityIndicatorViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorViewTests.swift; sourceTree = ""; }; - 4238D5951B4D5950008534C0 /* NSTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; name = NSTextField.swift; path = AppKit/NSTextField.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 5B1C882D1D0715CE000B888F /* UISegmentedControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISegmentedControl.swift; sourceTree = ""; }; - 5B1C882F1D071639000B888F /* UISegmentedControlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISegmentedControlTests.swift; sourceTree = ""; }; 5173EBC51B625A2600C9B48E /* UIBarItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIBarItem.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIBarButtonItem.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 5B1C882D1D0715CE000B888F /* UISegmentedControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISegmentedControl.swift; sourceTree = ""; }; + 5B1C882F1D071639000B888F /* UISegmentedControlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISegmentedControlTests.swift; sourceTree = ""; }; 7D0DABA91CCC381F00B6CD2B /* UIControl+EnableSendActionsForControlEvents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIControl+EnableSendActionsForControlEvents.swift"; sourceTree = ""; }; 7D2AA99A1CB6EFEB008AB5C9 /* UISwitch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UISwitch.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 7D2AA99C1CB6F275008AB5C9 /* UISwitchTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UISwitchTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; @@ -274,11 +274,11 @@ D8A454081BD2772700C9E790 /* PropertyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = PropertyTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; D8F073141B861B3A0047D546 /* UILabelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UILabelTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; D8F0973A1B17F2F7002E15BA /* NSData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = NSData.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - E6933BE61CD9C0B2006F7CE7 /* UIProgressView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIProgressView.swift; sourceTree = ""; }; - E6933BE81CD9C1F1006F7CE7 /* UIProgressViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIProgressViewTests.swift; sourceTree = ""; }; D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = NSUserDefaults.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; D8F097431B17F3C8002E15BA /* NSObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = NSObject.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; D8F097471B17F5DD002E15BA /* NSObjectTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = NSObjectTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + E6933BE61CD9C0B2006F7CE7 /* UIProgressView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIProgressView.swift; sourceTree = ""; }; + E6933BE81CD9C1F1006F7CE7 /* UIProgressViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIProgressViewTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -608,7 +608,7 @@ D8003E951AFEC3D400D7D3C5 /* Sources */, D8003E961AFEC3D400D7D3C5 /* Frameworks */, D8003E971AFEC3D400D7D3C5 /* Resources */, - D8003EB21AFEC6A800D7D3C5 /* (null) */, + D8003EB21AFEC6A800D7D3C5 /* CopyFiles */, ); buildRules = ( ); @@ -645,7 +645,7 @@ D834571A1AFEE44E0070616A /* Sources */, D834571B1AFEE44E0070616A /* Frameworks */, D834571C1AFEE44E0070616A /* Resources */, - D83457371AFEE4B80070616A /* (null) */, + D83457371AFEE4B80070616A /* CopyFiles */, ); buildRules = ( ); @@ -700,7 +700,7 @@ D8715DCD1C21160A005F4191 /* Sources */, D8715DCE1C21160A005F4191 /* Frameworks */, D8715DCF1C21160A005F4191 /* Resources */, - D8715DE81C21172A005F4191 /* (null) */, + D8715DE81C21172A005F4191 /* CopyFiles */, ); buildRules = ( ); @@ -752,7 +752,7 @@ }; }; }; - buildConfigurationList = D8003E881AFEC3D400D7D3C5 /* Build configuration list for PBXProject "Rex-Mac" */; + buildConfigurationList = D8003E881AFEC3D400D7D3C5 /* Build configuration list for PBXProject "Rex" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; @@ -1421,7 +1421,7 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - D8003E881AFEC3D400D7D3C5 /* Build configuration list for PBXProject "Rex-Mac" */ = { + D8003E881AFEC3D400D7D3C5 /* Build configuration list for PBXProject "Rex" */ = { isa = XCConfigurationList; buildConfigurations = ( D8003EA21AFEC3D400D7D3C5 /* Debug */, From 68c7ece2359d6b0ee64cc5ee1333ed5946f1daca Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Mon, 25 Jul 2016 17:06:20 -0600 Subject: [PATCH 0322/1028] Update to ReactiveCocoa 4.2.2 --- Cartfile | 2 +- Cartfile.resolved | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cartfile b/Cartfile index fb56d4ce5f..c46e12521c 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveCocoa" ~> 4.2.1 +github "ReactiveCocoa/ReactiveCocoa" ~> 4.2.2 diff --git a/Cartfile.resolved b/Cartfile.resolved index 421a6de515..7267e9d02d 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ -github "antitypical/Result" "2.1.1" -github "ReactiveCocoa/ReactiveCocoa" "v4.2.1" +github "antitypical/Result" "2.1.3" +github "ReactiveCocoa/ReactiveCocoa" "v4.2.2" From ac0c21be5ba28dc9085960b8cccf84c9c442a493 Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Mon, 25 Jul 2016 17:28:46 -0600 Subject: [PATCH 0323/1028] Add Xcode 8 to Travis build matrix --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 47dd0d1ea8..f6c913acb0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,7 @@ language: objective-c matrix: include: - osx_image: xcode7.3 + - osx_image: xcode8 env: global: From 0032984665249a83e0265e5ec6e205d5af8bc8d0 Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Fri, 29 Jul 2016 11:11:38 -0600 Subject: [PATCH 0324/1028] Allow warnings when validating the podspec --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f6c913acb0..4b8c6818d9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ script: - xcodebuild test -scheme Rex-iOS -sdk iphonesimulator -destination "platform=iOS Simulator,name=iPhone 6s" | xcpretty -c - xcodebuild test -scheme Rex-tvOS -sdk appletvsimulator -destination "platform=tvOS Simulator,name=Apple TV 1080p" | xcpretty -c - xcodebuild build -scheme Rex-watchOS -sdk watchsimulator -destination "platform=watchOS Simulator,name=Apple Watch - 38mm" | xcpretty -c - - pod lib lint + - pod lib lint --allow-warnings notifications: email: false From 5d22a84c8718dd053e185d8ca71cf77085f6e052 Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Fri, 29 Jul 2016 11:14:28 -0600 Subject: [PATCH 0325/1028] Use build-for-testing and test-without-building for CI on Xcode 8 This should stabilise testing on certain platforms: https://github.com/Quick/Quick/pull/565/commits/ed9c17b8c39a5a36d0244a2e1ff4450a4b971a30 --- .travis.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4b8c6818d9..4114de376a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,11 @@ language: objective-c matrix: include: - osx_image: xcode7.3 + env: + - XCODEBUILD_TEST_ACTION="test" - osx_image: xcode8 + env: + - XCODEBUILD_TEST_ACTION="build-for-testing test-without-building" env: global: @@ -14,9 +18,9 @@ git: script: - set -o pipefail - carthage bootstrap - - xcodebuild test -scheme Rex-Mac | xcpretty -c - - xcodebuild test -scheme Rex-iOS -sdk iphonesimulator -destination "platform=iOS Simulator,name=iPhone 6s" | xcpretty -c - - xcodebuild test -scheme Rex-tvOS -sdk appletvsimulator -destination "platform=tvOS Simulator,name=Apple TV 1080p" | xcpretty -c + - xcodebuild $XCODEBUILD_TEST_ACTION -scheme Rex-Mac | xcpretty -c + - xcodebuild $XCODEBUILD_TEST_ACTION -scheme Rex-iOS -sdk iphonesimulator -destination "platform=iOS Simulator,name=iPhone 6s" | xcpretty -c + - xcodebuild $XCODEBUILD_TEST_ACTION -scheme Rex-tvOS -sdk appletvsimulator -destination "platform=tvOS Simulator,name=Apple TV 1080p" | xcpretty -c - xcodebuild build -scheme Rex-watchOS -sdk watchsimulator -destination "platform=watchOS Simulator,name=Apple Watch - 38mm" | xcpretty -c - pod lib lint --allow-warnings From 4a3f1052d0d7bd343ac07c32100d664c34325266 Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Fri, 29 Jul 2016 11:24:11 -0600 Subject: [PATCH 0326/1028] Don't use Carthage binaries on CI Now that we're testing on two different Xcode versions, prebuilt binaries will always fail on one of them. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4114de376a..805c6cf215 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ git: script: - set -o pipefail - - carthage bootstrap + - carthage bootstrap --no-use-binaries - xcodebuild $XCODEBUILD_TEST_ACTION -scheme Rex-Mac | xcpretty -c - xcodebuild $XCODEBUILD_TEST_ACTION -scheme Rex-iOS -sdk iphonesimulator -destination "platform=iOS Simulator,name=iPhone 6s" | xcpretty -c - xcodebuild $XCODEBUILD_TEST_ACTION -scheme Rex-tvOS -sdk appletvsimulator -destination "platform=tvOS Simulator,name=Apple TV 1080p" | xcpretty -c From 1f4497b11a92aed2906c2588fd5475b0db83d9c4 Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Fri, 5 Aug 2016 16:38:49 -0400 Subject: [PATCH 0327/1028] Use `pod lib lint --quick` on CI --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 805c6cf215..9778f1f8f8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,7 +22,7 @@ script: - xcodebuild $XCODEBUILD_TEST_ACTION -scheme Rex-iOS -sdk iphonesimulator -destination "platform=iOS Simulator,name=iPhone 6s" | xcpretty -c - xcodebuild $XCODEBUILD_TEST_ACTION -scheme Rex-tvOS -sdk appletvsimulator -destination "platform=tvOS Simulator,name=Apple TV 1080p" | xcpretty -c - xcodebuild build -scheme Rex-watchOS -sdk watchsimulator -destination "platform=watchOS Simulator,name=Apple Watch - 38mm" | xcpretty -c - - pod lib lint --allow-warnings + - pod lib lint --quick --allow-warnings notifications: email: false From d8596a7b7f3e44e85b7e3746ce67095588aaf92b Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Sat, 6 Aug 2016 22:14:25 -0400 Subject: [PATCH 0328/1028] Start a migration guide --- Documentation/RAC5MigrationGuide.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 Documentation/RAC5MigrationGuide.txt diff --git a/Documentation/RAC5MigrationGuide.txt b/Documentation/RAC5MigrationGuide.txt new file mode 100644 index 0000000000..4e5b802f38 --- /dev/null +++ b/Documentation/RAC5MigrationGuide.txt @@ -0,0 +1 @@ +# RAC 5.0 Migration Guide From 24f7000b9c2658ebf7136714265c88922f030ca8 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Sat, 6 Aug 2016 22:14:46 -0400 Subject: [PATCH 0329/1028] Write about the repository split --- Documentation/RAC5MigrationGuide.txt | 39 ++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/Documentation/RAC5MigrationGuide.txt b/Documentation/RAC5MigrationGuide.txt index 4e5b802f38..1089c76193 100644 --- a/Documentation/RAC5MigrationGuide.txt +++ b/Documentation/RAC5MigrationGuide.txt @@ -1 +1,40 @@ # RAC 5.0 Migration Guide + +## Repository Split +As part of RAC 5.0, ReactiveCocoa has been split into multiple repositories. _The rationale for this change is explained below._ + +**If you’re using only the Swift APIs**, you can continue to include ReactiveCocoa. You will also need to link against [ReactiveSwift][], which is now a dependency of ReactiveCocoa. + +**If you’re using only the Objective-C APIs**, you can switch to using [ReactiveObjC][]. It has all the Obj-C code from RAC 2. + +**If you’re using both the Swift and Objective-C APIs**, you will most likely want to use both ReactiveCocoa and [ReactiveObjCBridge][], which depend on [ReactiveSwift][] and [ReactiveObjC][]. + +### ReactiveCocoa +The ReactiveCocoa library is newly focused on Swift and the UI layers of Apple’s platforms, building on the work of [Rex](https://github.com/neilpa/Rex). + +Reactive programming provides significant benefit in UI programming. RAC 3 and 4 focused on building out the new core Swift API. But we feel that those APIs have matured and it’s time for RAC-friendly extensions to AppKit and UIKit. + +### ReactiveSwift +The core, platform-independent Swift APIs have been extracted to a new framework, [ReactiveSwift][]. + +As Swift continues to grow as a language and a platform, we hope that it will expand beyond Cocoa and Apple’s platforms. Separating the Swift code makes it possible to use the reactive paradigm on other platforms. + +[ReactiveSwift]: https://github.com/ReactiveCocoa/ReactiveSwift + +### ReactiveObjC +RAC 3/4 included the Objective-C code from RAC 2. That code has been moved to [ReactiveObjC][] because: + + 1. It’s independent of the Swift code + 2. It has a separate user base + 3. It has a separate group of maintainers + +We hope that this move will enable continued support of ReactiveObjC. + +[ReactiveObjC]: https://github.com/ReactiveCocoa/ReactiveObjC + +### ReactiveObjCBridge +Moving the Swift and Objective-C APIs to separate repositories meant that a new home was need for the bridging layer between the two. + +This value will remain an important tool for anyone who’s heavily invested in the Objective-C RAC APIs as they (hopefully) move gradually to Swift. + +[ReactiveObjCBridge]: https://github.com/ReactiveCocoa/ReactiveObjCBridge From b890d8d6ab95bf63fd6cc0222c0e5ce1bcd14045 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Sat, 6 Aug 2016 22:19:17 -0400 Subject: [PATCH 0330/1028] Fix file extension --- Documentation/{RAC5MigrationGuide.txt => RAC5MigrationGuide.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Documentation/{RAC5MigrationGuide.txt => RAC5MigrationGuide.md} (100%) diff --git a/Documentation/RAC5MigrationGuide.txt b/Documentation/RAC5MigrationGuide.md similarity index 100% rename from Documentation/RAC5MigrationGuide.txt rename to Documentation/RAC5MigrationGuide.md From f22eba108143bfa5aff9057b8066a46a4a1c18c6 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Sat, 6 Aug 2016 22:21:12 -0400 Subject: [PATCH 0331/1028] Add a TOC --- Documentation/RAC5MigrationGuide.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/RAC5MigrationGuide.md b/Documentation/RAC5MigrationGuide.md index 1089c76193..85aae8ef00 100644 --- a/Documentation/RAC5MigrationGuide.md +++ b/Documentation/RAC5MigrationGuide.md @@ -1,5 +1,11 @@ # RAC 5.0 Migration Guide +* [Repository Split](#repository-split) + * [ReactiveCocoa](#reactivecocoa) + * [ReactiveSwift](#reactiveswift) + * [ReactiveObjC](#reactiveobjc) + * [ReactiveObjCBridge](#reactiveobjcbridge) + ## Repository Split As part of RAC 5.0, ReactiveCocoa has been split into multiple repositories. _The rationale for this change is explained below._ From 84e16d79489ead0e773cf527c40de55315d01537 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Sat, 6 Aug 2016 22:27:06 -0400 Subject: [PATCH 0332/1028] Start outlining the guide --- Documentation/RAC5MigrationGuide.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Documentation/RAC5MigrationGuide.md b/Documentation/RAC5MigrationGuide.md index 85aae8ef00..739305aea9 100644 --- a/Documentation/RAC5MigrationGuide.md +++ b/Documentation/RAC5MigrationGuide.md @@ -5,6 +5,12 @@ * [ReactiveSwift](#reactiveswift) * [ReactiveObjC](#reactiveobjc) * [ReactiveObjCBridge](#reactiveobjcbridge) +* [API Names](#api-names) +* [Signal](#signal) + * [Lifetime Semantics](#lifetime-semantics) +* [SignalProducer](#signalproducer) +* [Properties](#properties) +* [Atomic](#atomic) ## Repository Split As part of RAC 5.0, ReactiveCocoa has been split into multiple repositories. _The rationale for this change is explained below._ @@ -44,3 +50,14 @@ Moving the Swift and Objective-C APIs to separate repositories meant that a new This value will remain an important tool for anyone who’s heavily invested in the Objective-C RAC APIs as they (hopefully) move gradually to Swift. [ReactiveObjCBridge]: https://github.com/ReactiveCocoa/ReactiveObjCBridge + +## API Names + +## Signal +### Lifetime Semantics + +## SignalProducer + +## Properties + +## Atomic From b3a207ea7ebe9906d4ba7405a6b7d6c5d963de43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastia=CC=81n=20Varela?= Date: Mon, 8 Aug 2016 14:45:33 +0200 Subject: [PATCH 0333/1028] Update ReactiveCocoa Dependency (damm branch fuckers) --- Cartfile | 2 +- Cartfile.resolved | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cartfile b/Cartfile index 167b0e00b5..38f3bb3b35 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveCocoa" "RAC5" +github "ReactiveCocoa/ReactiveCocoa" "master" diff --git a/Cartfile.resolved b/Cartfile.resolved index 8251c3bf4d..006ec05a8b 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ -github "jameshurst/Result" "2401fa7584183da17f40335816073bf511fb34b8" -github "ReactiveCocoa/ReactiveCocoa" "b4d757433574f85519bb3253a631955da937106a" +github "antitypical/Result" "3.0.0-alpha.2" +github "ReactiveCocoa/ReactiveCocoa" "d98489ae97b020ccdd9a8c8ccad2b67429817669" From 012e5195b31bd0dea02b7735cedd3858410e730a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastia=CC=81n=20Varela?= Date: Tue, 9 Aug 2016 10:47:32 +0200 Subject: [PATCH 0334/1028] fix remaining warning on Xcode8b4 --- Source/Foundation/UserDefaults.swift | 2 +- Source/UIKit/UIActivityIndicatorView.swift | 18 +++++++++--------- Tests/PropertyTests.swift | 4 ++-- Tests/SignalProducerTests.swift | 4 ++-- Tests/UIKit/UILabelTests.swift | 10 +++++----- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Source/Foundation/UserDefaults.swift b/Source/Foundation/UserDefaults.swift index c0a9e0b6f7..abc1393646 100644 --- a/Source/Foundation/UserDefaults.swift +++ b/Source/Foundation/UserDefaults.swift @@ -28,7 +28,7 @@ extension UserDefaults { return SignalProducer(value: initial) .concat(changes) .skipRepeats { previous, next in - if let previous = previous as? NSObject, next = next as? NSObject { + if let previous = previous as? NSObject, let next = next as? NSObject { return previous == next } return false diff --git a/Source/UIKit/UIActivityIndicatorView.swift b/Source/UIKit/UIActivityIndicatorView.swift index b2579b1427..85d5848f90 100644 --- a/Source/UIKit/UIActivityIndicatorView.swift +++ b/Source/UIKit/UIActivityIndicatorView.swift @@ -14,15 +14,15 @@ extension UIActivityIndicatorView { /// Wraps an indicator's `isAnimating()` state in a bindable property. /// Setting a new value to the property would call `startAnimating()` or /// `stopAnimating()` depending on the value. -// public var rex_animating: MutableProperty { -// return associatedProperty(self, key: &animatingKey, initial: { $0.isAnimating() }, setter: { host, animating in -// if animating { -// host.startAnimating() -// } else { -// host.stopAnimating() -// } -// }) -// } + public var rex_animating: MutableProperty { + return associatedProperty(self, key: &animatingKey, initial: { $0.isAnimating }, setter: { host, animating in + if animating { + host.startAnimating() + } else { + host.stopAnimating() + } + }) + } } private var animatingKey: UInt8 = 0 diff --git a/Tests/PropertyTests.swift b/Tests/PropertyTests.swift index 6faedac8fa..7ee93128f1 100644 --- a/Tests/PropertyTests.swift +++ b/Tests/PropertyTests.swift @@ -32,7 +32,7 @@ final class PropertyTests: XCTestCase { XCTAssertTrue(current!) let (signal, pipe) = Signal.pipe() - let and2 = and.and(AnyProperty(initial: false, then: signal)) + let and2 = and.and(Property(initial: false, then: signal)) and2.producer.startWithNext { current = $0 } XCTAssertFalse(and2.value) @@ -62,7 +62,7 @@ final class PropertyTests: XCTestCase { XCTAssertFalse(current!) let (signal, pipe) = Signal.pipe() - let or2 = or.or(AnyProperty(initial: true, then: signal)) + let or2 = or.or(Property(initial: true, then: signal)) or2.producer.startWithNext { current = $0 } XCTAssertTrue(or2.value) diff --git a/Tests/SignalProducerTests.swift b/Tests/SignalProducerTests.swift index 997ad37e4a..f2cbff6b8c 100644 --- a/Tests/SignalProducerTests.swift +++ b/Tests/SignalProducerTests.swift @@ -98,7 +98,7 @@ final class SignalProducerTests: XCTestCase { var value = -1 var completed = false producer - .deferredRetry(interval: 1, on: scheduler) + .deferredRetry(1, on: scheduler) .start(Observer( next: { value = $0 }, completed: { completed = true } @@ -136,7 +136,7 @@ final class SignalProducerTests: XCTestCase { var value = -1 var failed = false producer - .deferredRetry(interval: 1, on: scheduler, count: 2) + .deferredRetry(1, on: scheduler, count: 2) .start(Observer( next: { value = $0 }, failed: { _ in failed = true } diff --git a/Tests/UIKit/UILabelTests.swift b/Tests/UIKit/UILabelTests.swift index 3e9fcf500a..056d1ec60c 100644 --- a/Tests/UIKit/UILabelTests.swift +++ b/Tests/UIKit/UILabelTests.swift @@ -50,18 +50,18 @@ class UILabelTests: XCTestCase { let label = UILabel(frame: CGRect.zero) _label = label - label.rex_attributedText <~ SignalProducer(value: AttributedString(string: "Test")) + label.rex_attributedText <~ SignalProducer(value: NSAttributedString(string: "Test")) XCTAssert(_label?.attributedText?.string == "Test") } func testAttributedTextProperty() { - let firstChange = AttributedString(string: "first") - let secondChange = AttributedString(string: "second") + let firstChange = NSAttributedString(string: "first") + let secondChange = NSAttributedString(string: "second") let label = UILabel(frame: CGRect.zero) - label.attributedText = AttributedString(string: "") + label.attributedText = NSAttributedString(string: "") - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() label.rex_attributedText <~ SignalProducer(signal: pipeSignal) observer.sendNext(firstChange) From bfcb48de59d3625510b2b38c9526470eb552a6c2 Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Wed, 10 Aug 2016 10:56:48 -0400 Subject: [PATCH 0335/1028] Construct CI build matrix from platform & OS X image The new build matrix should have 10 items: 2 Xcodes * 5 platforms (Mac, iOS, tvOS, watchOS, CocoaPods). --- .travis.yml | 31 +++++++++++++++++++------------ script/ci | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 12 deletions(-) create mode 100755 script/ci diff --git a/.travis.yml b/.travis.yml index 9778f1f8f8..ca2289c390 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,11 +2,25 @@ language: objective-c matrix: include: - osx_image: xcode7.3 - env: - - XCODEBUILD_TEST_ACTION="test" + 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 + env: PLATFORM=iOS TEST_ACTION="build-for-testing test-without-building" + - osx_image: xcode8 + env: PLATFORM=tvOS TEST_ACTION="build-for-testing test-without-building" + - osx_image: xcode8 + env: PLATFORM=watchOS TEST_ACTION="build-for-testing test-without-building" - osx_image: xcode8 - env: - - XCODEBUILD_TEST_ACTION="build-for-testing test-without-building" + env: PLATFORM=CocoaPods env: global: @@ -15,14 +29,7 @@ env: git: submodules: false -script: - - set -o pipefail - - carthage bootstrap --no-use-binaries - - xcodebuild $XCODEBUILD_TEST_ACTION -scheme Rex-Mac | xcpretty -c - - xcodebuild $XCODEBUILD_TEST_ACTION -scheme Rex-iOS -sdk iphonesimulator -destination "platform=iOS Simulator,name=iPhone 6s" | xcpretty -c - - xcodebuild $XCODEBUILD_TEST_ACTION -scheme Rex-tvOS -sdk appletvsimulator -destination "platform=tvOS Simulator,name=Apple TV 1080p" | xcpretty -c - - xcodebuild build -scheme Rex-watchOS -sdk watchsimulator -destination "platform=watchOS Simulator,name=Apple Watch - 38mm" | xcpretty -c - - pod lib lint --quick --allow-warnings +script: script/ci "$PLATFORM" notifications: email: false diff --git a/script/ci b/script/ci new file mode 100755 index 0000000000..3ed40d744d --- /dev/null +++ b/script/ci @@ -0,0 +1,40 @@ +#!/bin/sh + +set -e -o pipefail + +bootstrap() { + case "$1" in + CocoaPods) + ;; + *) + carthage bootstrap --no-use-binaries --platform "$1" + ;; + esac +} + +build() { + case "$1" in + Mac) + xcodebuild ${TEST_ACTION:-test} -scheme Rex-Mac | xcpretty -c + ;; + iOS) + xcodebuild ${TEST_ACTION:-test} -scheme Rex-iOS -destination "platform=iOS Simulator,name=iPhone 6s" | xcpretty -c + ;; + tvOS) + xcodebuild ${TEST_ACTION:-test} -scheme Rex-tvOS -destination "platform=tvOS Simulator,name=Apple TV 1080p" | xcpretty -c + ;; + watchOS) + xcodebuild build -scheme Rex-watchOS -destination "platform=watchOS Simulator,name=Apple Watch - 42mm" | xcpretty -c + ;; + CocoaPods) + pod lib lint --quick --allow-warnings + ;; + *) + echo 2>&1 "Usage: $0 " + exit 1 + ;; + esac +} + +bootstrap "$@" +build "$@" From a8c816987156c41c447ad028cd12ebefae7fcd56 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Tue, 16 Aug 2016 09:34:37 -0400 Subject: [PATCH 0336/1028] Create ReactiveSwift --- .travis.yml | 21 + .../contents.xcworkspacedata | 3 - .../Sandbox.xcplaygroundpage/Contents.swift | 12 +- .../Signal.xcplaygroundpage/Contents.swift | 16 +- .../Contents.swift | 16 +- .../Sources/PlaygroundUtility.swift | 0 .../contents.xcplayground | 2 +- ReactiveSwift.xcodeproj/project.pbxproj | 1595 ++++++++++++ .../xcschemes/ReactiveSwift-iOS.xcscheme | 156 ++ .../xcschemes/ReactiveSwift-macOS.xcscheme | 156 ++ .../xcschemes/ReactiveSwift-tvOS.xcscheme | 156 ++ .../xcschemes/ReactiveSwift-watchOS.xcscheme | 71 + ReactiveSwift/Action.swift | 199 ++ ReactiveSwift/Atomic.swift | 169 ++ ReactiveSwift/Bag.swift | 110 + ReactiveSwift/Deprecations+Removals.swift | 255 ++ ReactiveSwift/Disposable.swift | 353 +++ ReactiveSwift/Event.swift | 165 ++ ReactiveSwift/EventLogger.swift | 132 + ReactiveSwift/Flatten.swift | 918 +++++++ ReactiveSwift/FoundationExtensions.swift | 80 + ReactiveSwift/Info.plist | 28 + ReactiveSwift/Lifetime.swift | 50 + ReactiveSwift/Observer.swift | 104 + ReactiveSwift/Optional.swift | 42 + ReactiveSwift/Property.swift | 878 +++++++ ReactiveSwift/ReactiveSwift.h | 15 + ReactiveSwift/Scheduler.swift | 493 ++++ ReactiveSwift/Signal.swift | 1838 +++++++++++++ ReactiveSwift/SignalProducer.swift | 1907 ++++++++++++++ ReactiveSwift/TupleExtensions.swift | 42 + ReactiveSwiftTests/ActionSpec.swift | 142 ++ ReactiveSwiftTests/AtomicSpec.swift | 44 + ReactiveSwiftTests/BagSpec.swift | 54 + ReactiveSwiftTests/DisposableSpec.swift | 143 ++ ReactiveSwiftTests/FlattenSpec.swift | 963 +++++++ .../FoundationExtensionsSpec.swift | 59 + ReactiveSwiftTests/Info.plist | 24 + ReactiveSwiftTests/LifetimeSpec.swift | 83 + ReactiveSwiftTests/PropertySpec.swift | 1560 ++++++++++++ ReactiveSwiftTests/SchedulerSpec.swift | 298 +++ ReactiveSwiftTests/SignalLifetimeSpec.swift | 414 +++ .../SignalProducerLiftingSpec.swift | 1536 +++++++++++ .../SignalProducerNimbleMatchers.swift | 57 + ReactiveSwiftTests/SignalProducerSpec.swift | 2257 ++++++++++++++++ ReactiveSwiftTests/SignalSpec.swift | 2269 +++++++++++++++++ ReactiveSwiftTests/TestError.swift | 43 + ReactiveSwiftTests/TestLogger.swift | 25 + 48 files changed, 19927 insertions(+), 26 deletions(-) rename {ReactiveCocoa.playground => ReactiveSwift.playground}/Pages/Sandbox.xcplaygroundpage/Contents.swift (58%) rename {ReactiveCocoa.playground => ReactiveSwift.playground}/Pages/Signal.xcplaygroundpage/Contents.swift (93%) rename {ReactiveCocoa.playground => ReactiveSwift.playground}/Pages/SignalProducer.xcplaygroundpage/Contents.swift (97%) rename {ReactiveCocoa.playground => ReactiveSwift.playground}/Sources/PlaygroundUtility.swift (100%) rename {ReactiveCocoa.playground => ReactiveSwift.playground}/contents.xcplayground (72%) create mode 100644 ReactiveSwift.xcodeproj/project.pbxproj create mode 100644 ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-iOS.xcscheme create mode 100644 ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-macOS.xcscheme create mode 100644 ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-tvOS.xcscheme create mode 100644 ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-watchOS.xcscheme create mode 100644 ReactiveSwift/Action.swift create mode 100644 ReactiveSwift/Atomic.swift create mode 100644 ReactiveSwift/Bag.swift create mode 100644 ReactiveSwift/Deprecations+Removals.swift create mode 100644 ReactiveSwift/Disposable.swift create mode 100644 ReactiveSwift/Event.swift create mode 100644 ReactiveSwift/EventLogger.swift create mode 100644 ReactiveSwift/Flatten.swift create mode 100644 ReactiveSwift/FoundationExtensions.swift create mode 100644 ReactiveSwift/Info.plist create mode 100644 ReactiveSwift/Lifetime.swift create mode 100644 ReactiveSwift/Observer.swift create mode 100644 ReactiveSwift/Optional.swift create mode 100644 ReactiveSwift/Property.swift create mode 100644 ReactiveSwift/ReactiveSwift.h create mode 100644 ReactiveSwift/Scheduler.swift create mode 100644 ReactiveSwift/Signal.swift create mode 100644 ReactiveSwift/SignalProducer.swift create mode 100644 ReactiveSwift/TupleExtensions.swift create mode 100755 ReactiveSwiftTests/ActionSpec.swift create mode 100644 ReactiveSwiftTests/AtomicSpec.swift create mode 100644 ReactiveSwiftTests/BagSpec.swift create mode 100644 ReactiveSwiftTests/DisposableSpec.swift create mode 100644 ReactiveSwiftTests/FlattenSpec.swift create mode 100644 ReactiveSwiftTests/FoundationExtensionsSpec.swift create mode 100644 ReactiveSwiftTests/Info.plist create mode 100644 ReactiveSwiftTests/LifetimeSpec.swift create mode 100644 ReactiveSwiftTests/PropertySpec.swift create mode 100644 ReactiveSwiftTests/SchedulerSpec.swift create mode 100644 ReactiveSwiftTests/SignalLifetimeSpec.swift create mode 100644 ReactiveSwiftTests/SignalProducerLiftingSpec.swift create mode 100644 ReactiveSwiftTests/SignalProducerNimbleMatchers.swift create mode 100644 ReactiveSwiftTests/SignalProducerSpec.swift create mode 100755 ReactiveSwiftTests/SignalSpec.swift create mode 100644 ReactiveSwiftTests/TestError.swift create mode 100644 ReactiveSwiftTests/TestLogger.swift diff --git a/.travis.yml b/.travis.yml index 85e315aaed..51ed833049 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,6 +32,27 @@ matrix: - XCODE_SDK=watchsimulator - XCODE_ACTION=build - XCODE_DESTINATION="platform=watchOS Simulator,name=Apple Watch - 38mm" + - xcode_scheme: ReactiveSwift-macOS + env: + - XCODE_SDK=macosx + - XCODE_ACTION="build test" + - XCODE_DESTINATION="arch=x86_64" + - XCODE_PLAYGROUND_TARGET="x86_64-apple-macosx10.10" + - xcode_scheme: ReactiveSwift-macOS + env: + - XCODE_SDK=iphonesimulator + - XCODE_ACTION="build-for-testing test-without-building" + - XCODE_DESTINATION="platform=iOS Simulator,name=iPhone 6s" + - xcode_scheme: ReactiveSwift-macOS + env: + - XCODE_SDK=appletvsimulator + - XCODE_ACTION="build-for-testing test-without-building" + - XCODE_DESTINATION="platform=tvOS Simulator,name=Apple TV 1080p" + - xcode_scheme: ReactiveSwift-macOS + env: + - XCODE_SDK=watchsimulator + - XCODE_ACTION=build + - XCODE_DESTINATION="platform=watchOS Simulator,name=Apple Watch - 38mm" - script: - brew update - brew outdated carthage || brew upgrade carthage diff --git a/ReactiveCocoa.xcworkspace/contents.xcworkspacedata b/ReactiveCocoa.xcworkspace/contents.xcworkspacedata index 9dc8faab27..4761de5116 100644 --- a/ReactiveCocoa.xcworkspace/contents.xcworkspacedata +++ b/ReactiveCocoa.xcworkspace/contents.xcworkspacedata @@ -1,9 +1,6 @@ - - diff --git a/ReactiveCocoa.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift b/ReactiveSwift.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift similarity index 58% rename from ReactiveCocoa.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift rename to ReactiveSwift.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift index afe8ad4d77..c2de48c890 100644 --- a/ReactiveCocoa.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift +++ b/ReactiveSwift.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift @@ -1,19 +1,19 @@ /*: - > # IMPORTANT: To use `ReactiveCocoa.playground`, please: + > # IMPORTANT: To use `ReactiveSwift.playground`, please: - 1. Retrieve the project dependencies using one of the following terminal commands from the ReactiveCocoa project root directory: + 1. Retrieve the project dependencies using one of the following terminal commands from the ReactiveSwift project root directory: - `script/bootstrap` **OR**, if you have [Carthage](https://github.com/Carthage/Carthage) installed - `carthage checkout` - 1. Open `ReactiveCocoa.xcworkspace` + 1. Open `ReactiveSwift.xcworkspace` 1. Build `Result-Mac` scheme - 1. Build `ReactiveCocoa-Mac` scheme - 1. Finally open the `ReactiveCocoa.playground` + 1. Build `ReactiveSwift-macOS` scheme + 1. Finally open the `ReactiveSwift.playground` 1. Choose `View > Show Debug Area` */ import Result -import ReactiveCocoa +import ReactiveSwift import Foundation /*: diff --git a/ReactiveCocoa.playground/Pages/Signal.xcplaygroundpage/Contents.swift b/ReactiveSwift.playground/Pages/Signal.xcplaygroundpage/Contents.swift similarity index 93% rename from ReactiveCocoa.playground/Pages/Signal.xcplaygroundpage/Contents.swift rename to ReactiveSwift.playground/Pages/Signal.xcplaygroundpage/Contents.swift index 6a310cd20f..385659b078 100644 --- a/ReactiveCocoa.playground/Pages/Signal.xcplaygroundpage/Contents.swift +++ b/ReactiveSwift.playground/Pages/Signal.xcplaygroundpage/Contents.swift @@ -1,25 +1,25 @@ /*: -> # IMPORTANT: To use `ReactiveCocoa.playground`, please: +> # IMPORTANT: To use `ReactiveSwift.playground`, please: -1. Retrieve the project dependencies using one of the following terminal commands from the ReactiveCocoa project root directory: +1. Retrieve the project dependencies using one of the following terminal commands from the ReactiveSwift project root directory: - `script/bootstrap` **OR**, if you have [Carthage](https://github.com/Carthage/Carthage) installed - `carthage checkout` -1. Open `ReactiveCocoa.xcworkspace` +1. Open `ReactiveSwift.xcworkspace` 1. Build `Result-Mac` scheme -1. Build `ReactiveCocoa-Mac` scheme -1. Finally open the `ReactiveCocoa.playground` +1. Build `ReactiveSwift-macOS` scheme +1. Finally open the `ReactiveSwift.playground` 1. Choose `View > Show Debug Area` */ import Result -import ReactiveCocoa +import ReactiveSwift import Foundation /*: ## Signal -A **signal**, represented by the [`Signal`](https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/ReactiveCocoa/Swift/Signal.swift) type, is any series of [`Event`](https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/ReactiveCocoa/Swift/Event.swift) values +A **signal**, represented by the [`Signal`](https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/ReactiveSwift/Signal.swift) type, is any series of [`Event`](https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/ReactiveSwift/Event.swift) values over time that can be observed. Signals are generally used to represent event streams that are already “in progress”, @@ -34,7 +34,7 @@ cannot have any effect on their lifetime. While observing a signal, the user can only evaluate the events in the same order as they are sent on the signal. There is no random access to values of a signal. -Signals can be manipulated by applying [primitives](https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/Documentation/BasicOperators.md) to them. +Signals can be manipulated by applying [primitives](https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/BasicOperators.md) to them. Typical primitives to manipulate a single signal like `filter`, `map` and `reduce` are available, as well as primitives to manipulate multiple signals at once (`zip`). Primitives operate only on the `Next` events of a signal. diff --git a/ReactiveCocoa.playground/Pages/SignalProducer.xcplaygroundpage/Contents.swift b/ReactiveSwift.playground/Pages/SignalProducer.xcplaygroundpage/Contents.swift similarity index 97% rename from ReactiveCocoa.playground/Pages/SignalProducer.xcplaygroundpage/Contents.swift rename to ReactiveSwift.playground/Pages/SignalProducer.xcplaygroundpage/Contents.swift index 9cd61b63c5..0bc7a9dfbf 100644 --- a/ReactiveCocoa.playground/Pages/SignalProducer.xcplaygroundpage/Contents.swift +++ b/ReactiveSwift.playground/Pages/SignalProducer.xcplaygroundpage/Contents.swift @@ -1,26 +1,26 @@ /*: -> # IMPORTANT: To use `ReactiveCocoa.playground`, please: +> # IMPORTANT: To use `ReactiveSwift.playground`, please: -1. Retrieve the project dependencies using one of the following terminal commands from the ReactiveCocoa project root directory: +1. Retrieve the project dependencies using one of the following terminal commands from the ReactiveSwift project root directory: - `script/bootstrap` **OR**, if you have [Carthage](https://github.com/Carthage/Carthage) installed - `carthage checkout` -1. Open `ReactiveCocoa.xcworkspace` +1. Open `ReactiveSwift.xcworkspace` 1. Build `Result-Mac` scheme -1. Build `ReactiveCocoa-Mac` scheme -1. Finally open the `ReactiveCocoa.playground` +1. Build `ReactiveSwift-macOS` scheme +1. Finally open the `ReactiveSwift.playground` 1. Choose `View > Show Debug Area` */ import Result -import ReactiveCocoa +import ReactiveSwift import Foundation /*: ## SignalProducer -A **signal producer**, represented by the [`SignalProducer`](https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/ReactiveCocoa/Swift/SignalProducer.swift) type, creates -[signals](https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/ReactiveCocoa/Swift/Signal.swift) and performs side effects. +A **signal producer**, represented by the [`SignalProducer`](https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/ReactiveSwift/SignalProducer.swift) type, creates +[signals](https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/ReactiveSwift/Signal.swift) and performs side effects. They can be used to represent operations or tasks, like network requests, where each invocation of `start()` will create a new underlying diff --git a/ReactiveCocoa.playground/Sources/PlaygroundUtility.swift b/ReactiveSwift.playground/Sources/PlaygroundUtility.swift similarity index 100% rename from ReactiveCocoa.playground/Sources/PlaygroundUtility.swift rename to ReactiveSwift.playground/Sources/PlaygroundUtility.swift diff --git a/ReactiveCocoa.playground/contents.xcplayground b/ReactiveSwift.playground/contents.xcplayground similarity index 72% rename from ReactiveCocoa.playground/contents.xcplayground rename to ReactiveSwift.playground/contents.xcplayground index fa86d24d80..43399d296e 100644 --- a/ReactiveCocoa.playground/contents.xcplayground +++ b/ReactiveSwift.playground/contents.xcplayground @@ -1,5 +1,5 @@ - + diff --git a/ReactiveSwift.xcodeproj/project.pbxproj b/ReactiveSwift.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..3baddc24d0 --- /dev/null +++ b/ReactiveSwift.xcodeproj/project.pbxproj @@ -0,0 +1,1595 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 02D2602A1C1D6DAF003ACC61 /* SignalLifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02D260291C1D6DAF003ACC61 /* SignalLifetimeSpec.swift */; }; + 02D2602B1C1D6DB8003ACC61 /* SignalLifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02D260291C1D6DAF003ACC61 /* SignalLifetimeSpec.swift */; }; + 4A0E10FF1D2A92720065D310 /* Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* Lifetime.swift */; }; + 4A0E11001D2A92720065D310 /* Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* Lifetime.swift */; }; + 4A0E11011D2A92720065D310 /* Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* Lifetime.swift */; }; + 4A0E11021D2A92720065D310 /* Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* Lifetime.swift */; }; + 4A0E11041D2A95200065D310 /* LifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E11031D2A95200065D310 /* LifetimeSpec.swift */; }; + 4A0E11051D2A95200065D310 /* LifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E11031D2A95200065D310 /* LifetimeSpec.swift */; }; + 4A0E11061D2A95200065D310 /* LifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E11031D2A95200065D310 /* LifetimeSpec.swift */; }; + 579504331BB8A34200A5E482 /* BagSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312EF19EF2A7700984962 /* BagSpec.swift */; }; + 579504341BB8A34300A5E482 /* BagSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312EF19EF2A7700984962 /* BagSpec.swift */; }; + 57A4D1B11BA13D7A00F7D4B1 /* Optional.swift in Sources */ = {isa = PBXBuildFile; fileRef = D871D69E1B3B29A40070F16C /* Optional.swift */; }; + 57A4D1B41BA13D7A00F7D4B1 /* Disposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BE19EF2A5800984962 /* Disposable.swift */; }; + 57A4D1B61BA13D7A00F7D4B1 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B51A69A3DB00AD8286 /* Event.swift */; }; + 57A4D1B81BA13D7A00F7D4B1 /* Scheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312C819EF2A5800984962 /* Scheduler.swift */; }; + 57A4D1B91BA13D7A00F7D4B1 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54AF1A69A2AC00AD8286 /* Action.swift */; }; + 57A4D1BA1BA13D7A00F7D4B1 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B01A69A2AC00AD8286 /* Property.swift */; }; + 57A4D1BB1BA13D7A00F7D4B1 /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B11A69A2AC00AD8286 /* Signal.swift */; }; + 57A4D1BC1BA13D7A00F7D4B1 /* SignalProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B21A69A2AC00AD8286 /* SignalProducer.swift */; }; + 57A4D1BD1BA13D7A00F7D4B1 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BB19EF2A5800984962 /* Atomic.swift */; }; + 57A4D1BE1BA13D7A00F7D4B1 /* Bag.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BC19EF2A5800984962 /* Bag.swift */; }; + 57A4D1BF1BA13D7A00F7D4B1 /* TupleExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00004081A46864E000E7D41 /* TupleExtensions.swift */; }; + 57A4D1C01BA13D7A00F7D4B1 /* FoundationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B4A3C19F4C39A009E02AC /* FoundationExtensions.swift */; }; + 57A4D2081BA13D7A00F7D4B1 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; + 57A4D20A1BA13D7A00F7D4B1 /* ReactiveSwift.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveSwift.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7DFBED081CDB8C9500EE435B /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57A4D2411BA13D7A00F7D4B1 /* ReactiveSwift.framework */; }; + 7DFBED1E1CDB8D7000EE435B /* ReactiveSwift.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 57A4D2411BA13D7A00F7D4B1 /* ReactiveSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 7DFBED1F1CDB8D7800EE435B /* Quick.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 7DFBED201CDB8D7D00EE435B /* Nimble.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 7DFBED211CDB8D8300EE435B /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 7DFBED221CDB8DE300EE435B /* ActionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D021671C1A6CD50500987861 /* ActionSpec.swift */; }; + 7DFBED231CDB8DE300EE435B /* AtomicSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312EE19EF2A7700984962 /* AtomicSpec.swift */; }; + 7DFBED241CDB8DE300EE435B /* BagSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312EF19EF2A7700984962 /* BagSpec.swift */; }; + 7DFBED251CDB8DE300EE435B /* DisposableSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312F019EF2A7700984962 /* DisposableSpec.swift */; }; + 7DFBED261CDB8DE300EE435B /* FoundationExtensionsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8170FC01B100EBC004192AD /* FoundationExtensionsSpec.swift */; }; + 7DFBED281CDB8DE300EE435B /* PropertySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260D1A72F16D00D33B74 /* PropertySpec.swift */; }; + 7DFBED291CDB8DE300EE435B /* SchedulerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312F219EF2A7700984962 /* SchedulerSpec.swift */; }; + 7DFBED2A1CDB8DE300EE435B /* SignalLifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02D260291C1D6DAF003ACC61 /* SignalLifetimeSpec.swift */; }; + 7DFBED2B1CDB8DE300EE435B /* SignalProducerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260A1A72E6C500D33B74 /* SignalProducerSpec.swift */; }; + 7DFBED2C1CDB8DE300EE435B /* SignalProducerLiftingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8024DB11B2E1BB0005E6B9A /* SignalProducerLiftingSpec.swift */; }; + 7DFBED2D1CDB8DE300EE435B /* SignalSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A226071A72E0E900D33B74 /* SignalSpec.swift */; }; + 7DFBED2E1CDB8DE300EE435B /* FlattenSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA6F284F1C52626B001879D2 /* FlattenSpec.swift */; }; + 7DFBED2F1CDB8DE300EE435B /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B696FB801A7640C00075236D /* TestError.swift */; }; + 7DFBED301CDB8DE300EE435B /* TestLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79B64731CD38B2B003F2376 /* TestLogger.swift */; }; + 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */; }; + 9ABCB1851D2A5B5A00BCA243 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ABCB1841D2A5B5A00BCA243 /* Deprecations+Removals.swift */; }; + 9ABCB1861D2A5B5A00BCA243 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ABCB1841D2A5B5A00BCA243 /* Deprecations+Removals.swift */; }; + 9ABCB1871D2A5B5A00BCA243 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ABCB1841D2A5B5A00BCA243 /* Deprecations+Removals.swift */; }; + 9ABCB1881D2A5B5A00BCA243 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ABCB1841D2A5B5A00BCA243 /* Deprecations+Removals.swift */; }; + A9B315BC1B3940810001CB9C /* Disposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BE19EF2A5800984962 /* Disposable.swift */; }; + A9B315BE1B3940810001CB9C /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B51A69A3DB00AD8286 /* Event.swift */; }; + A9B315C01B3940810001CB9C /* Scheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312C819EF2A5800984962 /* Scheduler.swift */; }; + A9B315C11B3940810001CB9C /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54AF1A69A2AC00AD8286 /* Action.swift */; }; + A9B315C21B3940810001CB9C /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B01A69A2AC00AD8286 /* Property.swift */; }; + A9B315C31B3940810001CB9C /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B11A69A2AC00AD8286 /* Signal.swift */; }; + A9B315C41B3940810001CB9C /* SignalProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B21A69A2AC00AD8286 /* SignalProducer.swift */; }; + A9B315C51B3940810001CB9C /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BB19EF2A5800984962 /* Atomic.swift */; }; + A9B315C61B3940810001CB9C /* Bag.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BC19EF2A5800984962 /* Bag.swift */; }; + A9B315C71B3940810001CB9C /* TupleExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00004081A46864E000E7D41 /* TupleExtensions.swift */; }; + A9B315C81B3940810001CB9C /* FoundationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B4A3C19F4C39A009E02AC /* FoundationExtensions.swift */; }; + A9B315C91B3940980001CB9C /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; + A9B315CA1B3940AB0001CB9C /* ReactiveSwift.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveSwift.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9F793341B60D0140026BCBA /* Optional.swift in Sources */ = {isa = PBXBuildFile; fileRef = D871D69E1B3B29A40070F16C /* Optional.swift */; }; + B696FB811A7640C00075236D /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B696FB801A7640C00075236D /* TestError.swift */; }; + B696FB821A7640C00075236D /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B696FB801A7640C00075236D /* TestError.swift */; }; + BFA6B94D1A7604D400C846D1 /* SignalProducerNimbleMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */; }; + BFA6B94E1A7604D500C846D1 /* SignalProducerNimbleMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */; }; + C79B64741CD38B2B003F2376 /* TestLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79B64731CD38B2B003F2376 /* TestLogger.swift */; }; + C79B64751CD38B2B003F2376 /* TestLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79B64731CD38B2B003F2376 /* TestLogger.swift */; }; + C79B647C1CD52E23003F2376 /* EventLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79B647B1CD52E23003F2376 /* EventLogger.swift */; }; + C79B647D1CD52E4A003F2376 /* EventLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79B647B1CD52E23003F2376 /* EventLogger.swift */; }; + C79B647F1CD52E4D003F2376 /* EventLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79B647B1CD52E23003F2376 /* EventLogger.swift */; }; + C79B64801CD52E4E003F2376 /* EventLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79B647B1CD52E23003F2376 /* EventLogger.swift */; }; + CA6F28501C52626B001879D2 /* FlattenSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA6F284F1C52626B001879D2 /* FlattenSpec.swift */; }; + CA6F28511C52626B001879D2 /* FlattenSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA6F284F1C52626B001879D2 /* FlattenSpec.swift */; }; + CDC42E2F1AE7AB8B00965373 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; + CDC42E301AE7AB8B00965373 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; + CDC42E311AE7AB8B00965373 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; + CDC42E331AE7AC6D00965373 /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + CDCD247A1C277EEC00710AEE /* AtomicSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312EE19EF2A7700984962 /* AtomicSpec.swift */; }; + CDCD247B1C277EED00710AEE /* AtomicSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312EE19EF2A7700984962 /* AtomicSpec.swift */; }; + CDF066CA1CDC1CA200199626 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; }; + CDF066CB1CDC1CA200199626 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; }; + D00004091A46864E000E7D41 /* TupleExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00004081A46864E000E7D41 /* TupleExtensions.swift */; }; + D000040A1A46864E000E7D41 /* TupleExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00004081A46864E000E7D41 /* TupleExtensions.swift */; }; + D01B7B6219EDD8FE00D26E01 /* Nimble.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D01B7B6319EDD8FE00D26E01 /* Quick.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D01B7B6419EDD94B00D26E01 /* ReactiveSwift.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D047260C19E49F82006002AA /* ReactiveSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D021671D1A6CD50500987861 /* ActionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D021671C1A6CD50500987861 /* ActionSpec.swift */; }; + D021671E1A6CD50500987861 /* ActionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D021671C1A6CD50500987861 /* ActionSpec.swift */; }; + D037666419EDA43C00A782A9 /* ReactiveSwift.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveSwift.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037672D19EDA75D00A782A9 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; }; + D037672F19EDA78B00A782A9 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; }; + D03B4A3D19F4C39A009E02AC /* FoundationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B4A3C19F4C39A009E02AC /* FoundationExtensions.swift */; }; + D03B4A3E19F4C39A009E02AC /* FoundationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B4A3C19F4C39A009E02AC /* FoundationExtensions.swift */; }; + D04725F019E49ED7006002AA /* ReactiveSwift.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveSwift.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D04725F619E49ED7006002AA /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D04725EA19E49ED7006002AA /* ReactiveSwift.framework */; }; + D047261719E49F82006002AA /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D047260C19E49F82006002AA /* ReactiveSwift.framework */; }; + D05E662519EDD82000904ACA /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; }; + D05E662619EDD83000904ACA /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; }; + D08C54B31A69A2AE00AD8286 /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B11A69A2AC00AD8286 /* Signal.swift */; }; + D08C54B41A69A2AF00AD8286 /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B11A69A2AC00AD8286 /* Signal.swift */; }; + D08C54B61A69A3DB00AD8286 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B51A69A3DB00AD8286 /* Event.swift */; }; + D08C54B71A69A3DB00AD8286 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B51A69A3DB00AD8286 /* Event.swift */; }; + D08C54B81A69A9D000AD8286 /* SignalProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B21A69A2AC00AD8286 /* SignalProducer.swift */; }; + D08C54B91A69A9D100AD8286 /* SignalProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B21A69A2AC00AD8286 /* SignalProducer.swift */; }; + D08C54BA1A69C54300AD8286 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B01A69A2AC00AD8286 /* Property.swift */; }; + D08C54BB1A69C54400AD8286 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B01A69A2AC00AD8286 /* Property.swift */; }; + D0A226081A72E0E900D33B74 /* SignalSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A226071A72E0E900D33B74 /* SignalSpec.swift */; }; + D0A226091A72E0E900D33B74 /* SignalSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A226071A72E0E900D33B74 /* SignalSpec.swift */; }; + D0A2260B1A72E6C500D33B74 /* SignalProducerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260A1A72E6C500D33B74 /* SignalProducerSpec.swift */; }; + D0A2260C1A72E6C500D33B74 /* SignalProducerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260A1A72E6C500D33B74 /* SignalProducerSpec.swift */; }; + D0A2260E1A72F16D00D33B74 /* PropertySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260D1A72F16D00D33B74 /* PropertySpec.swift */; }; + D0A2260F1A72F16D00D33B74 /* PropertySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260D1A72F16D00D33B74 /* PropertySpec.swift */; }; + D0C312CD19EF2A5800984962 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BB19EF2A5800984962 /* Atomic.swift */; }; + D0C312CE19EF2A5800984962 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BB19EF2A5800984962 /* Atomic.swift */; }; + D0C312CF19EF2A5800984962 /* Bag.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BC19EF2A5800984962 /* Bag.swift */; }; + D0C312D019EF2A5800984962 /* Bag.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BC19EF2A5800984962 /* Bag.swift */; }; + D0C312D319EF2A5800984962 /* Disposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BE19EF2A5800984962 /* Disposable.swift */; }; + D0C312D419EF2A5800984962 /* Disposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BE19EF2A5800984962 /* Disposable.swift */; }; + D0C312E719EF2A5800984962 /* Scheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312C819EF2A5800984962 /* Scheduler.swift */; }; + D0C312E819EF2A5800984962 /* Scheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312C819EF2A5800984962 /* Scheduler.swift */; }; + D0C3130C19EF2B1F00984962 /* DisposableSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312F019EF2A7700984962 /* DisposableSpec.swift */; }; + D0C3130E19EF2B1F00984962 /* SchedulerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312F219EF2A7700984962 /* SchedulerSpec.swift */; }; + D0C3131219EF2B2000984962 /* DisposableSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312F019EF2A7700984962 /* DisposableSpec.swift */; }; + D0C3131419EF2B2000984962 /* SchedulerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312F219EF2A7700984962 /* SchedulerSpec.swift */; }; + D0D11AB91A6AE87700C1F8B1 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54AF1A69A2AC00AD8286 /* Action.swift */; }; + D0D11ABA1A6AE87700C1F8B1 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54AF1A69A2AC00AD8286 /* Action.swift */; }; + D8024DB21B2E1BB0005E6B9A /* SignalProducerLiftingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8024DB11B2E1BB0005E6B9A /* SignalProducerLiftingSpec.swift */; }; + D8024DB31B2E1BB0005E6B9A /* SignalProducerLiftingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8024DB11B2E1BB0005E6B9A /* SignalProducerLiftingSpec.swift */; }; + D8170FC11B100EBC004192AD /* FoundationExtensionsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8170FC01B100EBC004192AD /* FoundationExtensionsSpec.swift */; }; + D8170FC21B100EBC004192AD /* FoundationExtensionsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8170FC01B100EBC004192AD /* FoundationExtensionsSpec.swift */; }; + D85C652A1C0D84C7005A77AD /* Flatten.swift in Sources */ = {isa = PBXBuildFile; fileRef = D85C65291C0D84C7005A77AD /* Flatten.swift */; }; + D85C652B1C0E70E3005A77AD /* Flatten.swift in Sources */ = {isa = PBXBuildFile; fileRef = D85C65291C0D84C7005A77AD /* Flatten.swift */; }; + D85C652C1C0E70E4005A77AD /* Flatten.swift in Sources */ = {isa = PBXBuildFile; fileRef = D85C65291C0D84C7005A77AD /* Flatten.swift */; }; + D85C652D1C0E70E5005A77AD /* Flatten.swift in Sources */ = {isa = PBXBuildFile; fileRef = D85C65291C0D84C7005A77AD /* Flatten.swift */; }; + D871D69F1B3B29A40070F16C /* Optional.swift in Sources */ = {isa = PBXBuildFile; fileRef = D871D69E1B3B29A40070F16C /* Optional.swift */; }; + D8E84A671B3B32FB00C3E831 /* Optional.swift in Sources */ = {isa = PBXBuildFile; fileRef = D871D69E1B3B29A40070F16C /* Optional.swift */; }; + EBCC7DBC1BBF010C00A2AE92 /* Observer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBCC7DBB1BBF010C00A2AE92 /* Observer.swift */; }; + EBCC7DBD1BBF01E100A2AE92 /* Observer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBCC7DBB1BBF010C00A2AE92 /* Observer.swift */; }; + EBCC7DBE1BBF01E200A2AE92 /* Observer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBCC7DBB1BBF010C00A2AE92 /* Observer.swift */; }; + EBCC7DBF1BBF01E200A2AE92 /* Observer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBCC7DBB1BBF010C00A2AE92 /* Observer.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 7DFBED091CDB8C9500EE435B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D04725E119E49ED7006002AA /* Project object */; + proxyType = 1; + remoteGlobalIDString = 57A4D1AF1BA13D7A00F7D4B1; + remoteInfo = "ReactiveCocoa-tvOS"; + }; + D04725F719E49ED7006002AA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D04725E119E49ED7006002AA /* Project object */; + proxyType = 1; + remoteGlobalIDString = D04725E919E49ED7006002AA; + remoteInfo = ReactiveCocoa; + }; + D047261819E49F82006002AA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D04725E119E49ED7006002AA /* Project object */; + proxyType = 1; + remoteGlobalIDString = D047260B19E49F82006002AA; + remoteInfo = ReactiveCocoa; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 7DFBED151CDB8CEC00EE435B /* Copy Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 7DFBED211CDB8D8300EE435B /* Result.framework in Copy Frameworks */, + 7DFBED201CDB8D7D00EE435B /* Nimble.framework in Copy Frameworks */, + 7DFBED1F1CDB8D7800EE435B /* Quick.framework in Copy Frameworks */, + 7DFBED1E1CDB8D7000EE435B /* ReactiveSwift.framework in Copy Frameworks */, + ); + name = "Copy Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; + D01B7B6119EDD8F600D26E01 /* Copy Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + CDC42E331AE7AC6D00965373 /* Result.framework in Copy Frameworks */, + D01B7B6219EDD8FE00D26E01 /* Nimble.framework in Copy Frameworks */, + D01B7B6319EDD8FE00D26E01 /* Quick.framework in Copy Frameworks */, + D01B7B6419EDD94B00D26E01 /* ReactiveSwift.framework in Copy Frameworks */, + ); + name = "Copy Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 02D260291C1D6DAF003ACC61 /* SignalLifetimeSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalLifetimeSpec.swift; sourceTree = ""; }; + 4A0E10FE1D2A92720065D310 /* Lifetime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Lifetime.swift; path = Lifetime.swift; sourceTree = ""; }; + 4A0E11031D2A95200065D310 /* LifetimeSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LifetimeSpec.swift; sourceTree = ""; }; + 57A4D2411BA13D7A00F7D4B1 /* ReactiveSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Application.xcconfig"; sourceTree = ""; }; + 57A4D2451BA13F9700F7D4B1 /* tvOS-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Base.xcconfig"; sourceTree = ""; }; + 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Framework.xcconfig"; sourceTree = ""; }; + 57A4D2471BA13F9700F7D4B1 /* tvOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-StaticLibrary.xcconfig"; sourceTree = ""; }; + 7DFBED031CDB8C9500EE435B /* ReactiveSwiftTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveSwiftTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 9ABCB1841D2A5B5A00BCA243 /* Deprecations+Removals.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deprecations+Removals.swift"; sourceTree = ""; }; + A97451331B3A935E00F48E55 /* watchOS-Application.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Application.xcconfig"; sourceTree = ""; }; + A97451341B3A935E00F48E55 /* watchOS-Base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Base.xcconfig"; sourceTree = ""; }; + A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Framework.xcconfig"; sourceTree = ""; }; + A97451361B3A935E00F48E55 /* watchOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-StaticLibrary.xcconfig"; sourceTree = ""; }; + A9B315541B3940610001CB9C /* ReactiveSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B696FB801A7640C00075236D /* TestError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestError.swift; sourceTree = ""; }; + BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalProducerNimbleMatchers.swift; sourceTree = ""; }; + C79B64731CD38B2B003F2376 /* TestLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestLogger.swift; sourceTree = ""; }; + C79B647B1CD52E23003F2376 /* EventLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventLogger.swift; sourceTree = ""; }; + CA6F284F1C52626B001879D2 /* FlattenSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlattenSpec.swift; sourceTree = ""; }; + CDC42E2E1AE7AB8B00965373 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D00004081A46864E000E7D41 /* TupleExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TupleExtensions.swift; path = TupleExtensions.swift; sourceTree = ""; }; + D021671C1A6CD50500987861 /* ActionSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ActionSpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + D037672B19EDA75D00A782A9 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Quick.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D03B4A3C19F4C39A009E02AC /* FoundationExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FoundationExtensions.swift; path = FoundationExtensions.swift; sourceTree = ""; }; + D04725EA19E49ED7006002AA /* ReactiveSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D04725EE19E49ED7006002AA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D04725EF19E49ED7006002AA /* ReactiveSwift.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ReactiveSwift.h; sourceTree = ""; }; + D04725F519E49ED7006002AA /* ReactiveSwiftTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveSwiftTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + D04725FB19E49ED7006002AA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D047260C19E49F82006002AA /* ReactiveSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D047261619E49F82006002AA /* ReactiveSwiftTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveSwiftTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + D047262719E49FE8006002AA /* Common.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Common.xcconfig; sourceTree = ""; }; + D047262919E49FE8006002AA /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + D047262A19E49FE8006002AA /* Profile.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Profile.xcconfig; sourceTree = ""; }; + D047262B19E49FE8006002AA /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + D047262C19E49FE8006002AA /* Test.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Test.xcconfig; sourceTree = ""; }; + D047262E19E49FE8006002AA /* Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Application.xcconfig; sourceTree = ""; }; + D047262F19E49FE8006002AA /* Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Framework.xcconfig; sourceTree = ""; }; + D047263019E49FE8006002AA /* StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = StaticLibrary.xcconfig; sourceTree = ""; }; + D047263219E49FE8006002AA /* iOS-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-Application.xcconfig"; sourceTree = ""; }; + D047263319E49FE8006002AA /* iOS-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-Base.xcconfig"; sourceTree = ""; }; + D047263419E49FE8006002AA /* iOS-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-Framework.xcconfig"; sourceTree = ""; }; + D047263519E49FE8006002AA /* iOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-StaticLibrary.xcconfig"; sourceTree = ""; }; + D047263719E49FE8006002AA /* Mac-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Application.xcconfig"; sourceTree = ""; }; + D047263819E49FE8006002AA /* Mac-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Base.xcconfig"; sourceTree = ""; }; + D047263919E49FE8006002AA /* Mac-DynamicLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-DynamicLibrary.xcconfig"; sourceTree = ""; }; + D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Framework.xcconfig"; sourceTree = ""; }; + D047263B19E49FE8006002AA /* Mac-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-StaticLibrary.xcconfig"; sourceTree = ""; }; + D047263C19E49FE8006002AA /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + D05E662419EDD82000904ACA /* Nimble.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Nimble.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D08C54AF1A69A2AC00AD8286 /* Action.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Action.swift; path = Action.swift; sourceTree = ""; }; + D08C54B01A69A2AC00AD8286 /* Property.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Property.swift; path = Property.swift; sourceTree = ""; }; + D08C54B11A69A2AC00AD8286 /* Signal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Signal.swift; path = Signal.swift; sourceTree = ""; }; + D08C54B21A69A2AC00AD8286 /* SignalProducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SignalProducer.swift; path = SignalProducer.swift; sourceTree = ""; }; + D08C54B51A69A3DB00AD8286 /* Event.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Event.swift; sourceTree = ""; }; + D0A226071A72E0E900D33B74 /* SignalSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SignalSpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + D0A2260A1A72E6C500D33B74 /* SignalProducerSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SignalProducerSpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + D0A2260D1A72F16D00D33B74 /* PropertySpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = PropertySpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + D0C312BB19EF2A5800984962 /* Atomic.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Atomic.swift; sourceTree = ""; }; + D0C312BC19EF2A5800984962 /* Bag.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Bag.swift; sourceTree = ""; }; + D0C312BE19EF2A5800984962 /* Disposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Disposable.swift; sourceTree = ""; }; + D0C312C819EF2A5800984962 /* Scheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Scheduler.swift; sourceTree = ""; }; + D0C312EE19EF2A7700984962 /* AtomicSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AtomicSpec.swift; sourceTree = ""; }; + D0C312EF19EF2A7700984962 /* BagSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BagSpec.swift; sourceTree = ""; }; + D0C312F019EF2A7700984962 /* DisposableSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisposableSpec.swift; sourceTree = ""; }; + D0C312F219EF2A7700984962 /* SchedulerSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchedulerSpec.swift; sourceTree = ""; }; + D8024DB11B2E1BB0005E6B9A /* SignalProducerLiftingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SignalProducerLiftingSpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + D8170FC01B100EBC004192AD /* FoundationExtensionsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoundationExtensionsSpec.swift; sourceTree = ""; }; + D85C65291C0D84C7005A77AD /* Flatten.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Flatten.swift; path = Flatten.swift; sourceTree = ""; }; + D871D69E1B3B29A40070F16C /* Optional.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Optional.swift; sourceTree = ""; }; + EBCC7DBB1BBF010C00A2AE92 /* Observer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Observer.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 57A4D2071BA13D7A00F7D4B1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 57A4D2081BA13D7A00F7D4B1 /* Result.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7DFBED001CDB8C9500EE435B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + CDF066CA1CDC1CA200199626 /* Nimble.framework in Frameworks */, + CDF066CB1CDC1CA200199626 /* Quick.framework in Frameworks */, + 7DFBED081CDB8C9500EE435B /* ReactiveSwift.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A9B315501B3940610001CB9C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A9B315C91B3940980001CB9C /* Result.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D04725E619E49ED7006002AA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + CDC42E2F1AE7AB8B00965373 /* Result.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D04725F219E49ED7006002AA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + CDC42E301AE7AB8B00965373 /* Result.framework in Frameworks */, + D05E662519EDD82000904ACA /* Nimble.framework in Frameworks */, + D037672D19EDA75D00A782A9 /* Quick.framework in Frameworks */, + D04725F619E49ED7006002AA /* ReactiveSwift.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D047260819E49F82006002AA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + CDC42E311AE7AB8B00965373 /* Result.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D047261319E49F82006002AA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D05E662619EDD83000904ACA /* Nimble.framework in Frameworks */, + D037672F19EDA78B00A782A9 /* Quick.framework in Frameworks */, + D047261719E49F82006002AA /* ReactiveSwift.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 57A4D2431BA13F9700F7D4B1 /* tvOS */ = { + isa = PBXGroup; + children = ( + 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */, + 57A4D2451BA13F9700F7D4B1 /* tvOS-Base.xcconfig */, + 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */, + 57A4D2471BA13F9700F7D4B1 /* tvOS-StaticLibrary.xcconfig */, + ); + path = tvOS; + sourceTree = ""; + }; + A97451321B3A935E00F48E55 /* watchOS */ = { + isa = PBXGroup; + children = ( + A97451331B3A935E00F48E55 /* watchOS-Application.xcconfig */, + A97451341B3A935E00F48E55 /* watchOS-Base.xcconfig */, + A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */, + A97451361B3A935E00F48E55 /* watchOS-StaticLibrary.xcconfig */, + ); + path = watchOS; + sourceTree = ""; + }; + D03B4A3919F4C25F009E02AC /* Signals */ = { + isa = PBXGroup; + children = ( + D08C54AF1A69A2AC00AD8286 /* Action.swift */, + D85C65291C0D84C7005A77AD /* Flatten.swift */, + 4A0E10FE1D2A92720065D310 /* Lifetime.swift */, + D08C54B01A69A2AC00AD8286 /* Property.swift */, + D08C54B11A69A2AC00AD8286 /* Signal.swift */, + D08C54B21A69A2AC00AD8286 /* SignalProducer.swift */, + ); + name = Signals; + path = ""; + sourceTree = ""; + }; + D03B4A3A19F4C26D009E02AC /* Internal Utilities */ = { + isa = PBXGroup; + children = ( + D00004081A46864E000E7D41 /* TupleExtensions.swift */, + ); + name = "Internal Utilities"; + path = ""; + sourceTree = ""; + }; + D03B4A3B19F4C281009E02AC /* Extensions */ = { + isa = PBXGroup; + children = ( + D03B4A3C19F4C39A009E02AC /* FoundationExtensions.swift */, + ); + name = Extensions; + path = ""; + sourceTree = ""; + }; + D04725E019E49ED7006002AA = { + isa = PBXGroup; + children = ( + D04725EC19E49ED7006002AA /* ReactiveSwift */, + D04725F919E49ED7006002AA /* ReactiveCocoaTests */, + D047262519E49FE8006002AA /* Configuration */, + D04725EB19E49ED7006002AA /* Products */, + ); + sourceTree = ""; + usesTabs = 1; + }; + D04725EB19E49ED7006002AA /* Products */ = { + isa = PBXGroup; + children = ( + D04725EA19E49ED7006002AA /* ReactiveSwift.framework */, + D04725F519E49ED7006002AA /* ReactiveSwiftTests.xctest */, + D047260C19E49F82006002AA /* ReactiveSwift.framework */, + D047261619E49F82006002AA /* ReactiveSwiftTests.xctest */, + A9B315541B3940610001CB9C /* ReactiveSwift.framework */, + 57A4D2411BA13D7A00F7D4B1 /* ReactiveSwift.framework */, + 7DFBED031CDB8C9500EE435B /* ReactiveSwiftTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + D04725EC19E49ED7006002AA /* ReactiveSwift */ = { + isa = PBXGroup; + children = ( + D04725EF19E49ED7006002AA /* ReactiveSwift.h */, + D0C312BB19EF2A5800984962 /* Atomic.swift */, + D0C312BC19EF2A5800984962 /* Bag.swift */, + D0C312BE19EF2A5800984962 /* Disposable.swift */, + D08C54B51A69A3DB00AD8286 /* Event.swift */, + EBCC7DBB1BBF010C00A2AE92 /* Observer.swift */, + D871D69E1B3B29A40070F16C /* Optional.swift */, + D0C312C819EF2A5800984962 /* Scheduler.swift */, + C79B647B1CD52E23003F2376 /* EventLogger.swift */, + D03B4A3919F4C25F009E02AC /* Signals */, + D03B4A3A19F4C26D009E02AC /* Internal Utilities */, + D03B4A3B19F4C281009E02AC /* Extensions */, + 9ABCB1841D2A5B5A00BCA243 /* Deprecations+Removals.swift */, + D04725ED19E49ED7006002AA /* Supporting Files */, + ); + path = ReactiveSwift; + sourceTree = ""; + }; + D04725ED19E49ED7006002AA /* Supporting Files */ = { + isa = PBXGroup; + children = ( + CDC42E2E1AE7AB8B00965373 /* Result.framework */, + D04725EE19E49ED7006002AA /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + D04725F919E49ED7006002AA /* ReactiveCocoaTests */ = { + isa = PBXGroup; + children = ( + D021671C1A6CD50500987861 /* ActionSpec.swift */, + D0C312EE19EF2A7700984962 /* AtomicSpec.swift */, + D0C312EF19EF2A7700984962 /* BagSpec.swift */, + D0C312F019EF2A7700984962 /* DisposableSpec.swift */, + CA6F284F1C52626B001879D2 /* FlattenSpec.swift */, + D8170FC01B100EBC004192AD /* FoundationExtensionsSpec.swift */, + 4A0E11031D2A95200065D310 /* LifetimeSpec.swift */, + D0A2260D1A72F16D00D33B74 /* PropertySpec.swift */, + D0C312F219EF2A7700984962 /* SchedulerSpec.swift */, + 02D260291C1D6DAF003ACC61 /* SignalLifetimeSpec.swift */, + D8024DB11B2E1BB0005E6B9A /* SignalProducerLiftingSpec.swift */, + D0A2260A1A72E6C500D33B74 /* SignalProducerSpec.swift */, + D0A226071A72E0E900D33B74 /* SignalSpec.swift */, + B696FB801A7640C00075236D /* TestError.swift */, + C79B64731CD38B2B003F2376 /* TestLogger.swift */, + D04725FA19E49ED7006002AA /* Supporting Files */, + ); + name = ReactiveCocoaTests; + path = ReactiveSwiftTests; + sourceTree = ""; + }; + D04725FA19E49ED7006002AA /* Supporting Files */ = { + isa = PBXGroup; + children = ( + D05E662419EDD82000904ACA /* Nimble.framework */, + D037672B19EDA75D00A782A9 /* Quick.framework */, + BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */, + D04725FB19E49ED7006002AA /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + D047262519E49FE8006002AA /* Configuration */ = { + isa = PBXGroup; + children = ( + D047262619E49FE8006002AA /* Base */, + D047263119E49FE8006002AA /* iOS */, + D047263619E49FE8006002AA /* Mac OS X */, + A97451321B3A935E00F48E55 /* watchOS */, + 57A4D2431BA13F9700F7D4B1 /* tvOS */, + D047263C19E49FE8006002AA /* README.md */, + ); + name = Configuration; + path = Carthage/Checkouts/xcconfigs; + sourceTree = ""; + }; + D047262619E49FE8006002AA /* Base */ = { + isa = PBXGroup; + children = ( + D047262719E49FE8006002AA /* Common.xcconfig */, + D047262819E49FE8006002AA /* Configurations */, + D047262D19E49FE8006002AA /* Targets */, + ); + path = Base; + sourceTree = ""; + }; + D047262819E49FE8006002AA /* Configurations */ = { + isa = PBXGroup; + children = ( + D047262919E49FE8006002AA /* Debug.xcconfig */, + D047262A19E49FE8006002AA /* Profile.xcconfig */, + D047262B19E49FE8006002AA /* Release.xcconfig */, + D047262C19E49FE8006002AA /* Test.xcconfig */, + ); + path = Configurations; + sourceTree = ""; + }; + D047262D19E49FE8006002AA /* Targets */ = { + isa = PBXGroup; + children = ( + D047262E19E49FE8006002AA /* Application.xcconfig */, + D047262F19E49FE8006002AA /* Framework.xcconfig */, + D047263019E49FE8006002AA /* StaticLibrary.xcconfig */, + ); + path = Targets; + sourceTree = ""; + }; + D047263119E49FE8006002AA /* iOS */ = { + isa = PBXGroup; + children = ( + D047263219E49FE8006002AA /* iOS-Application.xcconfig */, + D047263319E49FE8006002AA /* iOS-Base.xcconfig */, + D047263419E49FE8006002AA /* iOS-Framework.xcconfig */, + D047263519E49FE8006002AA /* iOS-StaticLibrary.xcconfig */, + ); + path = iOS; + sourceTree = ""; + }; + D047263619E49FE8006002AA /* Mac OS X */ = { + isa = PBXGroup; + children = ( + D047263719E49FE8006002AA /* Mac-Application.xcconfig */, + D047263819E49FE8006002AA /* Mac-Base.xcconfig */, + D047263919E49FE8006002AA /* Mac-DynamicLibrary.xcconfig */, + D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */, + D047263B19E49FE8006002AA /* Mac-StaticLibrary.xcconfig */, + ); + path = "Mac OS X"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 57A4D2091BA13D7A00F7D4B1 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 57A4D20A1BA13D7A00F7D4B1 /* ReactiveSwift.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A9B315511B3940610001CB9C /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + A9B315CA1B3940AB0001CB9C /* ReactiveSwift.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D04725E719E49ED7006002AA /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + D04725F019E49ED7006002AA /* ReactiveSwift.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D047260919E49F82006002AA /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + D037666419EDA43C00A782A9 /* ReactiveSwift.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 57A4D1AF1BA13D7A00F7D4B1 /* ReactiveSwift-tvOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 57A4D23C1BA13D7A00F7D4B1 /* Build configuration list for PBXNativeTarget "ReactiveSwift-tvOS" */; + buildPhases = ( + 57A4D1B01BA13D7A00F7D4B1 /* Sources */, + 57A4D2071BA13D7A00F7D4B1 /* Frameworks */, + 57A4D2091BA13D7A00F7D4B1 /* Headers */, + 57A4D23B1BA13D7A00F7D4B1 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "ReactiveSwift-tvOS"; + productName = ReactiveCocoa; + productReference = 57A4D2411BA13D7A00F7D4B1 /* ReactiveSwift.framework */; + productType = "com.apple.product-type.framework"; + }; + 7DFBED021CDB8C9500EE435B /* ReactiveSwift-tvOSTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7DFBED0F1CDB8C9500EE435B /* Build configuration list for PBXNativeTarget "ReactiveSwift-tvOSTests" */; + buildPhases = ( + 7DFBECFF1CDB8C9500EE435B /* Sources */, + 7DFBED001CDB8C9500EE435B /* Frameworks */, + 7DFBED011CDB8C9500EE435B /* Resources */, + 7DFBED151CDB8CEC00EE435B /* Copy Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 7DFBED0A1CDB8C9500EE435B /* PBXTargetDependency */, + ); + name = "ReactiveSwift-tvOSTests"; + productName = "ReactiveCocoa-tvOSTests"; + productReference = 7DFBED031CDB8C9500EE435B /* ReactiveSwiftTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + A9B315531B3940610001CB9C /* ReactiveSwift-watchOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = A9B3155D1B3940610001CB9C /* Build configuration list for PBXNativeTarget "ReactiveSwift-watchOS" */; + buildPhases = ( + A9B3154F1B3940610001CB9C /* Sources */, + A9B315501B3940610001CB9C /* Frameworks */, + A9B315511B3940610001CB9C /* Headers */, + A9B315521B3940610001CB9C /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "ReactiveSwift-watchOS"; + productName = ReactiveCocoa; + productReference = A9B315541B3940610001CB9C /* ReactiveSwift.framework */; + productType = "com.apple.product-type.framework"; + }; + D04725E919E49ED7006002AA /* ReactiveSwift-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = D047260019E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveSwift-macOS" */; + buildPhases = ( + D04725E519E49ED7006002AA /* Sources */, + D04725E619E49ED7006002AA /* Frameworks */, + D04725E719E49ED7006002AA /* Headers */, + D04725E819E49ED7006002AA /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "ReactiveSwift-macOS"; + productName = ReactiveCocoa; + productReference = D04725EA19E49ED7006002AA /* ReactiveSwift.framework */; + productType = "com.apple.product-type.framework"; + }; + D04725F419E49ED7006002AA /* ReactiveSwift-macOSTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = D047260319E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveSwift-macOSTests" */; + buildPhases = ( + D04725F119E49ED7006002AA /* Sources */, + D04725F219E49ED7006002AA /* Frameworks */, + D04725F319E49ED7006002AA /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + D04725F819E49ED7006002AA /* PBXTargetDependency */, + ); + name = "ReactiveSwift-macOSTests"; + productName = ReactiveCocoaTests; + productReference = D04725F519E49ED7006002AA /* ReactiveSwiftTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + D047260B19E49F82006002AA /* ReactiveSwift-iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = D047261F19E49F82006002AA /* Build configuration list for PBXNativeTarget "ReactiveSwift-iOS" */; + buildPhases = ( + D047260719E49F82006002AA /* Sources */, + D047260819E49F82006002AA /* Frameworks */, + D047260919E49F82006002AA /* Headers */, + D047260A19E49F82006002AA /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "ReactiveSwift-iOS"; + productName = ReactiveCocoa; + productReference = D047260C19E49F82006002AA /* ReactiveSwift.framework */; + productType = "com.apple.product-type.framework"; + }; + D047261519E49F82006002AA /* ReactiveSwift-iOSTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = D047262219E49F82006002AA /* Build configuration list for PBXNativeTarget "ReactiveSwift-iOSTests" */; + buildPhases = ( + D047261219E49F82006002AA /* Sources */, + D047261319E49F82006002AA /* Frameworks */, + D047261419E49F82006002AA /* Resources */, + D01B7B6119EDD8F600D26E01 /* Copy Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + D047261919E49F82006002AA /* PBXTargetDependency */, + ); + name = "ReactiveSwift-iOSTests"; + productName = ReactiveCocoaTests; + productReference = D047261619E49F82006002AA /* ReactiveSwiftTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + D04725E119E49ED7006002AA /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0730; + LastUpgradeCheck = 0710; + ORGANIZATIONNAME = GitHub; + TargetAttributes = { + 57A4D1AF1BA13D7A00F7D4B1 = { + LastSwiftMigration = 0800; + }; + 7DFBED021CDB8C9500EE435B = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 0800; + }; + A9B315531B3940610001CB9C = { + CreatedOnToolsVersion = 7.0; + LastSwiftMigration = 0800; + }; + D04725E919E49ED7006002AA = { + CreatedOnToolsVersion = 6.1; + LastSwiftMigration = 0800; + }; + D04725F419E49ED7006002AA = { + CreatedOnToolsVersion = 6.1; + LastSwiftMigration = 0800; + }; + D047260B19E49F82006002AA = { + CreatedOnToolsVersion = 6.1; + LastSwiftMigration = 0800; + }; + D047261519E49F82006002AA = { + CreatedOnToolsVersion = 6.1; + LastSwiftMigration = 0800; + }; + }; + }; + buildConfigurationList = D04725E419E49ED7006002AA /* Build configuration list for PBXProject "ReactiveSwift" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = D04725E019E49ED7006002AA; + productRefGroup = D04725EB19E49ED7006002AA /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + D04725E919E49ED7006002AA /* ReactiveSwift-macOS */, + D04725F419E49ED7006002AA /* ReactiveSwift-macOSTests */, + D047260B19E49F82006002AA /* ReactiveSwift-iOS */, + D047261519E49F82006002AA /* ReactiveSwift-iOSTests */, + A9B315531B3940610001CB9C /* ReactiveSwift-watchOS */, + 57A4D1AF1BA13D7A00F7D4B1 /* ReactiveSwift-tvOS */, + 7DFBED021CDB8C9500EE435B /* ReactiveSwift-tvOSTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 57A4D23B1BA13D7A00F7D4B1 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7DFBED011CDB8C9500EE435B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A9B315521B3940610001CB9C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D04725E819E49ED7006002AA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D04725F319E49ED7006002AA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D047260A19E49F82006002AA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D047261419E49F82006002AA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 57A4D1B01BA13D7A00F7D4B1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 57A4D1B11BA13D7A00F7D4B1 /* Optional.swift in Sources */, + 57A4D1B41BA13D7A00F7D4B1 /* Disposable.swift in Sources */, + 57A4D1B61BA13D7A00F7D4B1 /* Event.swift in Sources */, + 57A4D1B81BA13D7A00F7D4B1 /* Scheduler.swift in Sources */, + 57A4D1B91BA13D7A00F7D4B1 /* Action.swift in Sources */, + 57A4D1BA1BA13D7A00F7D4B1 /* Property.swift in Sources */, + 57A4D1BB1BA13D7A00F7D4B1 /* Signal.swift in Sources */, + 57A4D1BC1BA13D7A00F7D4B1 /* SignalProducer.swift in Sources */, + 57A4D1BD1BA13D7A00F7D4B1 /* Atomic.swift in Sources */, + 57A4D1BE1BA13D7A00F7D4B1 /* Bag.swift in Sources */, + 57A4D1BF1BA13D7A00F7D4B1 /* TupleExtensions.swift in Sources */, + 57A4D1C01BA13D7A00F7D4B1 /* FoundationExtensions.swift in Sources */, + D85C652D1C0E70E5005A77AD /* Flatten.swift in Sources */, + 9ABCB1881D2A5B5A00BCA243 /* Deprecations+Removals.swift in Sources */, + EBCC7DBF1BBF01E200A2AE92 /* Observer.swift in Sources */, + C79B64801CD52E4E003F2376 /* EventLogger.swift in Sources */, + 4A0E11021D2A92720065D310 /* Lifetime.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7DFBECFF1CDB8C9500EE435B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7DFBED221CDB8DE300EE435B /* ActionSpec.swift in Sources */, + 7DFBED231CDB8DE300EE435B /* AtomicSpec.swift in Sources */, + 7DFBED241CDB8DE300EE435B /* BagSpec.swift in Sources */, + 7DFBED251CDB8DE300EE435B /* DisposableSpec.swift in Sources */, + 7DFBED261CDB8DE300EE435B /* FoundationExtensionsSpec.swift in Sources */, + 7DFBED281CDB8DE300EE435B /* PropertySpec.swift in Sources */, + 7DFBED291CDB8DE300EE435B /* SchedulerSpec.swift in Sources */, + 7DFBED2A1CDB8DE300EE435B /* SignalLifetimeSpec.swift in Sources */, + 7DFBED2B1CDB8DE300EE435B /* SignalProducerSpec.swift in Sources */, + 7DFBED2C1CDB8DE300EE435B /* SignalProducerLiftingSpec.swift in Sources */, + 7DFBED2D1CDB8DE300EE435B /* SignalSpec.swift in Sources */, + 7DFBED2E1CDB8DE300EE435B /* FlattenSpec.swift in Sources */, + 7DFBED2F1CDB8DE300EE435B /* TestError.swift in Sources */, + 7DFBED301CDB8DE300EE435B /* TestLogger.swift in Sources */, + 4A0E11061D2A95200065D310 /* LifetimeSpec.swift in Sources */, + 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A9B3154F1B3940610001CB9C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A9F793341B60D0140026BCBA /* Optional.swift in Sources */, + A9B315BC1B3940810001CB9C /* Disposable.swift in Sources */, + A9B315BE1B3940810001CB9C /* Event.swift in Sources */, + A9B315C01B3940810001CB9C /* Scheduler.swift in Sources */, + A9B315C11B3940810001CB9C /* Action.swift in Sources */, + A9B315C21B3940810001CB9C /* Property.swift in Sources */, + A9B315C31B3940810001CB9C /* Signal.swift in Sources */, + A9B315C41B3940810001CB9C /* SignalProducer.swift in Sources */, + A9B315C51B3940810001CB9C /* Atomic.swift in Sources */, + A9B315C61B3940810001CB9C /* Bag.swift in Sources */, + A9B315C71B3940810001CB9C /* TupleExtensions.swift in Sources */, + A9B315C81B3940810001CB9C /* FoundationExtensions.swift in Sources */, + D85C652C1C0E70E4005A77AD /* Flatten.swift in Sources */, + 9ABCB1871D2A5B5A00BCA243 /* Deprecations+Removals.swift in Sources */, + EBCC7DBE1BBF01E200A2AE92 /* Observer.swift in Sources */, + C79B647F1CD52E4D003F2376 /* EventLogger.swift in Sources */, + 4A0E11011D2A92720065D310 /* Lifetime.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D04725E519E49ED7006002AA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D00004091A46864E000E7D41 /* TupleExtensions.swift in Sources */, + D871D69F1B3B29A40070F16C /* Optional.swift in Sources */, + D08C54B61A69A3DB00AD8286 /* Event.swift in Sources */, + D0C312D319EF2A5800984962 /* Disposable.swift in Sources */, + EBCC7DBC1BBF010C00A2AE92 /* Observer.swift in Sources */, + D03B4A3D19F4C39A009E02AC /* FoundationExtensions.swift in Sources */, + D08C54B31A69A2AE00AD8286 /* Signal.swift in Sources */, + D85C652A1C0D84C7005A77AD /* Flatten.swift in Sources */, + D0C312CF19EF2A5800984962 /* Bag.swift in Sources */, + 4A0E10FF1D2A92720065D310 /* Lifetime.swift in Sources */, + D0C312E719EF2A5800984962 /* Scheduler.swift in Sources */, + D0C312CD19EF2A5800984962 /* Atomic.swift in Sources */, + D08C54BA1A69C54300AD8286 /* Property.swift in Sources */, + D0D11AB91A6AE87700C1F8B1 /* Action.swift in Sources */, + C79B647C1CD52E23003F2376 /* EventLogger.swift in Sources */, + 9ABCB1851D2A5B5A00BCA243 /* Deprecations+Removals.swift in Sources */, + D08C54B81A69A9D000AD8286 /* SignalProducer.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D04725F119E49ED7006002AA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D0A2260E1A72F16D00D33B74 /* PropertySpec.swift in Sources */, + B696FB811A7640C00075236D /* TestError.swift in Sources */, + D021671D1A6CD50500987861 /* ActionSpec.swift in Sources */, + D0C3130E19EF2B1F00984962 /* SchedulerSpec.swift in Sources */, + BFA6B94D1A7604D400C846D1 /* SignalProducerNimbleMatchers.swift in Sources */, + D8170FC11B100EBC004192AD /* FoundationExtensionsSpec.swift in Sources */, + C79B64741CD38B2B003F2376 /* TestLogger.swift in Sources */, + CA6F28501C52626B001879D2 /* FlattenSpec.swift in Sources */, + 4A0E11041D2A95200065D310 /* LifetimeSpec.swift in Sources */, + CDCD247A1C277EEC00710AEE /* AtomicSpec.swift in Sources */, + 579504331BB8A34200A5E482 /* BagSpec.swift in Sources */, + D0A226081A72E0E900D33B74 /* SignalSpec.swift in Sources */, + 02D2602B1C1D6DB8003ACC61 /* SignalLifetimeSpec.swift in Sources */, + D0C3130C19EF2B1F00984962 /* DisposableSpec.swift in Sources */, + D0A2260B1A72E6C500D33B74 /* SignalProducerSpec.swift in Sources */, + D8024DB21B2E1BB0005E6B9A /* SignalProducerLiftingSpec.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D047260719E49F82006002AA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D08C54B41A69A2AF00AD8286 /* Signal.swift in Sources */, + D8E84A671B3B32FB00C3E831 /* Optional.swift in Sources */, + D0C312D419EF2A5800984962 /* Disposable.swift in Sources */, + D08C54B91A69A9D100AD8286 /* SignalProducer.swift in Sources */, + 9ABCB1861D2A5B5A00BCA243 /* Deprecations+Removals.swift in Sources */, + EBCC7DBD1BBF01E100A2AE92 /* Observer.swift in Sources */, + D85C652B1C0E70E3005A77AD /* Flatten.swift in Sources */, + 4A0E11001D2A92720065D310 /* Lifetime.swift in Sources */, + D08C54BB1A69C54400AD8286 /* Property.swift in Sources */, + D03B4A3E19F4C39A009E02AC /* FoundationExtensions.swift in Sources */, + D000040A1A46864E000E7D41 /* TupleExtensions.swift in Sources */, + D08C54B71A69A3DB00AD8286 /* Event.swift in Sources */, + C79B647D1CD52E4A003F2376 /* EventLogger.swift in Sources */, + D0C312CE19EF2A5800984962 /* Atomic.swift in Sources */, + D0C312E819EF2A5800984962 /* Scheduler.swift in Sources */, + D0C312D019EF2A5800984962 /* Bag.swift in Sources */, + D0D11ABA1A6AE87700C1F8B1 /* Action.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D047261219E49F82006002AA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D0A2260C1A72E6C500D33B74 /* SignalProducerSpec.swift in Sources */, + D0A2260F1A72F16D00D33B74 /* PropertySpec.swift in Sources */, + D0A226091A72E0E900D33B74 /* SignalSpec.swift in Sources */, + CDCD247B1C277EED00710AEE /* AtomicSpec.swift in Sources */, + D021671E1A6CD50500987861 /* ActionSpec.swift in Sources */, + D8024DB31B2E1BB0005E6B9A /* SignalProducerLiftingSpec.swift in Sources */, + BFA6B94E1A7604D500C846D1 /* SignalProducerNimbleMatchers.swift in Sources */, + B696FB821A7640C00075236D /* TestError.swift in Sources */, + D8170FC21B100EBC004192AD /* FoundationExtensionsSpec.swift in Sources */, + D0C3131419EF2B2000984962 /* SchedulerSpec.swift in Sources */, + C79B64751CD38B2B003F2376 /* TestLogger.swift in Sources */, + D0C3131219EF2B2000984962 /* DisposableSpec.swift in Sources */, + CA6F28511C52626B001879D2 /* FlattenSpec.swift in Sources */, + 579504341BB8A34300A5E482 /* BagSpec.swift in Sources */, + 4A0E11051D2A95200065D310 /* LifetimeSpec.swift in Sources */, + 02D2602A1C1D6DAF003ACC61 /* SignalLifetimeSpec.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 7DFBED0A1CDB8C9500EE435B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 57A4D1AF1BA13D7A00F7D4B1 /* ReactiveSwift-tvOS */; + targetProxy = 7DFBED091CDB8C9500EE435B /* PBXContainerItemProxy */; + }; + D04725F819E49ED7006002AA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D04725E919E49ED7006002AA /* ReactiveSwift-macOS */; + targetProxy = D04725F719E49ED7006002AA /* PBXContainerItemProxy */; + }; + D047261919E49F82006002AA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D047260B19E49F82006002AA /* ReactiveSwift-iOS */; + targetProxy = D047261819E49F82006002AA /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 57A4D23D1BA13D7A00F7D4B1 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DTRACE_PROBES_DISABLED=1", + ); + INFOPLIST_FILE = ReactiveSwift/Info.plist; + }; + name = Debug; + }; + 57A4D23E1BA13D7A00F7D4B1 /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DTRACE_PROBES_DISABLED=1", + ); + INFOPLIST_FILE = ReactiveSwift/Info.plist; + }; + name = Test; + }; + 57A4D23F1BA13D7A00F7D4B1 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DTRACE_PROBES_DISABLED=1", + ); + INFOPLIST_FILE = ReactiveSwift/Info.plist; + }; + name = Release; + }; + 57A4D2401BA13D7A00F7D4B1 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DTRACE_PROBES_DISABLED=1", + ); + INFOPLIST_FILE = ReactiveSwift/Info.plist; + }; + name = Profile; + }; + 7DFBED0B1CDB8C9500EE435B /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + INFOPLIST_FILE = ReactiveSwiftTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Debug; + }; + 7DFBED0C1CDB8C9500EE435B /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + INFOPLIST_FILE = ReactiveSwiftTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Test; + }; + 7DFBED0D1CDB8C9500EE435B /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + INFOPLIST_FILE = ReactiveSwiftTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Release; + }; + 7DFBED0E1CDB8C9500EE435B /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + INFOPLIST_FILE = ReactiveSwiftTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Profile; + }; + A9B315591B3940610001CB9C /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DTRACE_PROBES_DISABLED=1", + ); + INFOPLIST_FILE = ReactiveSwift/Info.plist; + }; + name = Debug; + }; + A9B3155A1B3940610001CB9C /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DTRACE_PROBES_DISABLED=1", + ); + INFOPLIST_FILE = ReactiveSwift/Info.plist; + }; + name = Test; + }; + A9B3155B1B3940610001CB9C /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DTRACE_PROBES_DISABLED=1", + ); + INFOPLIST_FILE = ReactiveSwift/Info.plist; + }; + name = Release; + }; + A9B3155C1B3940610001CB9C /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DTRACE_PROBES_DISABLED=1", + ); + INFOPLIST_FILE = ReactiveSwift/Info.plist; + }; + name = Profile; + }; + D04725FE19E49ED7006002AA /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047262919E49FE8006002AA /* Debug.xcconfig */; + buildSettings = { + BITCODE_GENERATION_MODE = bitcode; + CODE_SIGNING_REQUIRED = NO; + CURRENT_PROJECT_VERSION = 1; + ENABLE_BITCODE = NO; + ENABLE_TESTABILITY = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.9; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(PROJECT_NAME)"; + SWIFT_VERSION = 3.0; + TVOS_DEPLOYMENT_TARGET = 9.0; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + WATCHOS_DEPLOYMENT_TARGET = 2.0; + }; + name = Debug; + }; + D04725FF19E49ED7006002AA /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047262B19E49FE8006002AA /* Release.xcconfig */; + buildSettings = { + BITCODE_GENERATION_MODE = bitcode; + CODE_SIGNING_REQUIRED = NO; + CURRENT_PROJECT_VERSION = 1; + ENABLE_BITCODE = NO; + GCC_OPTIMIZATION_LEVEL = 0; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.9; + PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(PROJECT_NAME)"; + SWIFT_VERSION = 3.0; + TVOS_DEPLOYMENT_TARGET = 9.0; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + WATCHOS_DEPLOYMENT_TARGET = 2.0; + }; + name = Release; + }; + D047260119E49ED7006002AA /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = ReactiveSwift/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + }; + name = Debug; + }; + D047260219E49ED7006002AA /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = ReactiveSwift/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + }; + name = Release; + }; + D047260419E49ED7006002AA /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + INFOPLIST_FILE = ReactiveSwiftTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Debug; + }; + D047260519E49ED7006002AA /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + INFOPLIST_FILE = ReactiveSwiftTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Release; + }; + D047262019E49F82006002AA /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + INFOPLIST_FILE = ReactiveSwift/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + }; + name = Debug; + }; + D047262119E49F82006002AA /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + INFOPLIST_FILE = ReactiveSwift/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + }; + name = Release; + }; + D047262319E49F82006002AA /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + INFOPLIST_FILE = ReactiveSwiftTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Debug; + }; + D047262419E49F82006002AA /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + INFOPLIST_FILE = ReactiveSwiftTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Release; + }; + D047263D19E4A008006002AA /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047262A19E49FE8006002AA /* Profile.xcconfig */; + buildSettings = { + BITCODE_GENERATION_MODE = bitcode; + CODE_SIGNING_REQUIRED = NO; + CURRENT_PROJECT_VERSION = 1; + ENABLE_BITCODE = NO; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.9; + PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(PROJECT_NAME)"; + SWIFT_VERSION = 3.0; + TVOS_DEPLOYMENT_TARGET = 9.0; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + WATCHOS_DEPLOYMENT_TARGET = 2.0; + }; + name = Profile; + }; + D047263E19E4A008006002AA /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = ReactiveSwift/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + }; + name = Profile; + }; + D047263F19E4A008006002AA /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + INFOPLIST_FILE = ReactiveSwiftTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Profile; + }; + D047264019E4A008006002AA /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + INFOPLIST_FILE = ReactiveSwift/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + }; + name = Profile; + }; + D047264119E4A008006002AA /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + INFOPLIST_FILE = ReactiveSwiftTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Profile; + }; + D047264219E4A00B006002AA /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047262C19E49FE8006002AA /* Test.xcconfig */; + buildSettings = { + BITCODE_GENERATION_MODE = bitcode; + CODE_SIGNING_REQUIRED = NO; + CURRENT_PROJECT_VERSION = 1; + ENABLE_BITCODE = NO; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.9; + PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)-Tests"; + PRODUCT_NAME = "$(PROJECT_NAME)"; + SWIFT_VERSION = 3.0; + TVOS_DEPLOYMENT_TARGET = 9.0; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + WATCHOS_DEPLOYMENT_TARGET = 2.0; + }; + name = Test; + }; + D047264319E4A00B006002AA /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = ReactiveSwift/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + }; + name = Test; + }; + D047264419E4A00B006002AA /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + INFOPLIST_FILE = ReactiveSwiftTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Test; + }; + D047264519E4A00B006002AA /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + INFOPLIST_FILE = ReactiveSwift/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + }; + name = Test; + }; + D047264619E4A00B006002AA /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + INFOPLIST_FILE = ReactiveSwiftTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Test; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 57A4D23C1BA13D7A00F7D4B1 /* Build configuration list for PBXNativeTarget "ReactiveSwift-tvOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 57A4D23D1BA13D7A00F7D4B1 /* Debug */, + 57A4D23E1BA13D7A00F7D4B1 /* Test */, + 57A4D23F1BA13D7A00F7D4B1 /* Release */, + 57A4D2401BA13D7A00F7D4B1 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7DFBED0F1CDB8C9500EE435B /* Build configuration list for PBXNativeTarget "ReactiveSwift-tvOSTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7DFBED0B1CDB8C9500EE435B /* Debug */, + 7DFBED0C1CDB8C9500EE435B /* Test */, + 7DFBED0D1CDB8C9500EE435B /* Release */, + 7DFBED0E1CDB8C9500EE435B /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A9B3155D1B3940610001CB9C /* Build configuration list for PBXNativeTarget "ReactiveSwift-watchOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A9B315591B3940610001CB9C /* Debug */, + A9B3155A1B3940610001CB9C /* Test */, + A9B3155B1B3940610001CB9C /* Release */, + A9B3155C1B3940610001CB9C /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D04725E419E49ED7006002AA /* Build configuration list for PBXProject "ReactiveSwift" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D04725FE19E49ED7006002AA /* Debug */, + D047264219E4A00B006002AA /* Test */, + D04725FF19E49ED7006002AA /* Release */, + D047263D19E4A008006002AA /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D047260019E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveSwift-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D047260119E49ED7006002AA /* Debug */, + D047264319E4A00B006002AA /* Test */, + D047260219E49ED7006002AA /* Release */, + D047263E19E4A008006002AA /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D047260319E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveSwift-macOSTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D047260419E49ED7006002AA /* Debug */, + D047264419E4A00B006002AA /* Test */, + D047260519E49ED7006002AA /* Release */, + D047263F19E4A008006002AA /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D047261F19E49F82006002AA /* Build configuration list for PBXNativeTarget "ReactiveSwift-iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D047262019E49F82006002AA /* Debug */, + D047264519E4A00B006002AA /* Test */, + D047262119E49F82006002AA /* Release */, + D047264019E4A008006002AA /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D047262219E49F82006002AA /* Build configuration list for PBXNativeTarget "ReactiveSwift-iOSTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D047262319E49F82006002AA /* Debug */, + D047264619E4A00B006002AA /* Test */, + D047262419E49F82006002AA /* Release */, + D047264119E4A008006002AA /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = D04725E119E49ED7006002AA /* Project object */; +} diff --git a/ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-iOS.xcscheme b/ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-iOS.xcscheme new file mode 100644 index 0000000000..749a8751b4 --- /dev/null +++ b/ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-iOS.xcscheme @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-macOS.xcscheme b/ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-macOS.xcscheme new file mode 100644 index 0000000000..c989ea24f3 --- /dev/null +++ b/ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-macOS.xcscheme @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-tvOS.xcscheme b/ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-tvOS.xcscheme new file mode 100644 index 0000000000..863741d9ed --- /dev/null +++ b/ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-tvOS.xcscheme @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-watchOS.xcscheme b/ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-watchOS.xcscheme new file mode 100644 index 0000000000..2bdc6d5ca2 --- /dev/null +++ b/ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-watchOS.xcscheme @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ReactiveSwift/Action.swift b/ReactiveSwift/Action.swift new file mode 100644 index 0000000000..0ad813a89e --- /dev/null +++ b/ReactiveSwift/Action.swift @@ -0,0 +1,199 @@ +import Foundation +import enum Result.NoError + +/// Represents an action that will do some work when executed with a value of +/// type `Input`, then return zero or more values of type `Output` and/or fail +/// with an error of type `Error`. If no failure should be possible, NoError can +/// be specified for the `Error` parameter. +/// +/// Actions enforce serial execution. Any attempt to execute an action multiple +/// times concurrently will return an error. +public final class Action { + private let executeClosure: (Input) -> SignalProducer + private let eventsObserver: Signal, NoError>.Observer + + /// A signal of all events generated from applications of the Action. + /// + /// In other words, this will send every `Event` from every signal generated + /// by each SignalProducer returned from apply(). + public let events: Signal, NoError> + + /// A signal of all values generated from applications of the Action. + /// + /// In other words, this will send every value from every signal generated + /// by each SignalProducer returned from apply(). + public let values: Signal + + /// A signal of all errors generated from applications of the Action. + /// + /// In other words, this will send errors from every signal generated by + /// each SignalProducer returned from apply(). + public let errors: Signal + + /// Whether the action is currently executing. + public var isExecuting: Property { + return Property(_isExecuting) + } + + private let _isExecuting: MutableProperty = MutableProperty(false) + + /// Whether the action is currently enabled. + public var isEnabled: Property { + return Property(_isEnabled) + } + + private let _isEnabled: MutableProperty = MutableProperty(false) + + /// Whether the instantiator of this action wants it to be enabled. + private let isUserEnabled: Property + + /// This queue is used for read-modify-write operations on the `_executing` + /// property. + private let executingQueue = DispatchQueue( + label: "org.reactivecocoa.ReactiveSwift.Action.executingQueue", + attributes: [] + ) + + /// Whether the action should be enabled for the given combination of user + /// enabledness and executing status. + private static func shouldBeEnabled(userEnabled: Bool, executing: Bool) -> Bool { + return userEnabled && !executing + } + + /// Initializes an action that will be conditionally enabled, and creates a + /// SignalProducer for each input. + /// + /// - parameters: + /// - enabledIf: Boolean property that shows whether the action is + /// enabled. + /// - execute: A closure that returns the signal producer returned by + /// calling `apply(Input)` on the action. + public init(enabledIf property: P, _ execute: (Input) -> SignalProducer) { + executeClosure = execute + isUserEnabled = Property(property) + + (events, eventsObserver) = Signal, NoError>.pipe() + + values = events.map { $0.value }.skipNil() + errors = events.map { $0.error }.skipNil() + + _isEnabled <~ property.producer + .combineLatest(with: isExecuting.producer) + .map(Action.shouldBeEnabled) + } + + /// Initializes an action that will be enabled by default, and creates a + /// SignalProducer for each input. + /// + /// - parameters: + /// - execute: A closure that returns the signal producer returned by + /// calling `apply(Input)` on the action. + public convenience init(_ execute: (Input) -> SignalProducer) { + self.init(enabledIf: Property(value: true), execute) + } + + deinit { + eventsObserver.sendCompleted() + } + + /// Creates a SignalProducer that, when started, will execute the action + /// with the given input, then forward the results upon the produced Signal. + /// + /// - note: If the action is disabled when the returned SignalProducer is + /// started, the produced signal will send `ActionError.NotEnabled`, + /// and nothing will be sent upon `values` or `errors` for that + /// particular signal. + /// + /// - parameters: + /// - input: A value that will be passed to the closure creating the signal + /// producer. + public func apply(_ input: Input) -> SignalProducer> { + return SignalProducer { observer, disposable in + var startedExecuting = false + + self.executingQueue.sync { + if self._isEnabled.value { + self._isExecuting.value = true + startedExecuting = true + } + } + + if !startedExecuting { + observer.sendFailed(.disabled) + return + } + + self.executeClosure(input).startWithSignal { signal, signalDisposable in + disposable += signalDisposable + + signal.observe { event in + observer.action(event.mapError(ActionError.producerFailed)) + self.eventsObserver.sendNext(event) + } + } + + disposable += { + self._isExecuting.value = false + } + } + } +} + +public protocol ActionProtocol { + /// The type of argument to apply the action to. + associatedtype Input + /// The type of values returned by the action. + associatedtype Output + /// The type of error when the action fails. If errors aren't possible then + /// `NoError` can be used. + associatedtype Error: Swift.Error + + /// Whether the action is currently enabled. + var isEnabled: Property { get } + + /// Extracts an action from the receiver. + var action: Action { get } + + /// Creates a SignalProducer that, when started, will execute the action + /// with the given input, then forward the results upon the produced Signal. + /// + /// - note: If the action is disabled when the returned SignalProducer is + /// started, the produced signal will send `ActionError.NotEnabled`, + /// and nothing will be sent upon `values` or `errors` for that + /// particular signal. + /// + /// - parameters: + /// - input: A value that will be passed to the closure creating the signal + /// producer. + func apply(_ input: Input) -> SignalProducer> +} + +extension Action: ActionProtocol { + public var action: Action { + return self + } +} + +/// The type of error that can occur from Action.apply, where `Error` is the +/// type of error that can be generated by the specific Action instance. +public enum ActionError: Swift.Error { + /// The producer returned from apply() was started while the Action was + /// disabled. + case disabled + + /// The producer returned from apply() sent the given error. + case producerFailed(Error) +} + +public func == (lhs: ActionError, rhs: ActionError) -> Bool { + switch (lhs, rhs) { + case (.disabled, .disabled): + return true + + case let (.producerFailed(left), .producerFailed(right)): + return left == right + + default: + return false + } +} diff --git a/ReactiveSwift/Atomic.swift b/ReactiveSwift/Atomic.swift new file mode 100644 index 0000000000..db24e684e5 --- /dev/null +++ b/ReactiveSwift/Atomic.swift @@ -0,0 +1,169 @@ +// +// Atomic.swift +// ReactiveSwift +// +// Created by Justin Spahr-Summers on 2014-06-10. +// Copyright (c) 2014 GitHub. All rights reserved. +// + +import Foundation + +final class PosixThreadMutex: NSLocking { + private var mutex = pthread_mutex_t() + + init() { + let result = pthread_mutex_init(&mutex, nil) + precondition(result == 0, "Failed to initialize mutex with error \(result).") + } + + deinit { + let result = pthread_mutex_destroy(&mutex) + precondition(result == 0, "Failed to destroy mutex with error \(result).") + } + + func lock() { + let result = pthread_mutex_lock(&mutex) + precondition(result == 0, "Failed to lock \(self) with error \(result).") + } + + func unlock() { + let result = pthread_mutex_unlock(&mutex) + precondition(result == 0, "Failed to unlock \(self) with error \(result).") + } +} + +/// An atomic variable. +public final class Atomic: AtomicProtocol { + private let lock: PosixThreadMutex + private var _value: Value + + /// Initialize the variable with the given initial value. + /// + /// - parameters: + /// - value: Initial value for `self`. + public init(_ value: Value) { + _value = value + lock = PosixThreadMutex() + } + + /// Atomically modifies the variable. + /// + /// - parameters: + /// - action: A closure that takes the current value. + /// + /// - returns: The result of the action. + @discardableResult + public func modify(_ action: @noescape (inout Value) throws -> Result) rethrows -> Result { + lock.lock() + defer { lock.unlock() } + + return try action(&_value) + } + + /// Atomically perform an arbitrary action using the current value of the + /// variable. + /// + /// - parameters: + /// - action: A closure that takes the current value. + /// + /// - returns: The result of the action. + @discardableResult + public func withValue(_ action: @noescape (Value) throws -> Result) rethrows -> Result { + lock.lock() + defer { lock.unlock() } + + return try action(_value) + } +} + + +/// An atomic variable which uses a recursive lock. +internal final class RecursiveAtomic: AtomicProtocol { + private let lock: NSRecursiveLock + private var _value: Value + private let didSetObserver: ((Value) -> Void)? + + /// Initialize the variable with the given initial value. + /// + /// - parameters: + /// - value: Initial value for `self`. + /// - name: An optional name used to create the recursive lock. + /// - action: An optional closure which would be invoked every time the + /// value of `self` is mutated. + internal init(_ value: Value, name: StaticString? = nil, didSet action: ((Value) -> Void)? = nil) { + _value = value + lock = NSRecursiveLock() + lock.name = name.map(String.init(_:)) + didSetObserver = action + } + + /// Atomically modifies the variable. + /// + /// - parameters: + /// - action: A closure that takes the current value. + /// + /// - returns: The result of the action. + @discardableResult + func modify(_ action: @noescape (inout Value) throws -> Result) rethrows -> Result { + lock.lock() + defer { + didSetObserver?(_value) + lock.unlock() + } + + return try action(&_value) + } + + /// Atomically perform an arbitrary action using the current value of the + /// variable. + /// + /// - parameters: + /// - action: A closure that takes the current value. + /// + /// - returns: The result of the action. + @discardableResult + func withValue(_ action: @noescape (Value) throws -> Result) rethrows -> Result { + lock.lock() + defer { lock.unlock() } + + return try action(_value) + } +} + +public protocol AtomicProtocol: class { + associatedtype Value + + @discardableResult + func withValue(_ action: @noescape (Value) throws -> Result) rethrows -> Result + + @discardableResult + func modify(_ action: @noescape (inout Value) throws -> Result) rethrows -> Result +} + +extension AtomicProtocol { + /// Atomically get or set the value of the variable. + public var value: Value { + get { + return withValue { $0 } + } + + set(newValue) { + swap(newValue) + } + } + + /// Atomically replace the contents of the variable. + /// + /// - parameters: + /// - newValue: A new value for the variable. + /// + /// - returns: The old value. + @discardableResult + public func swap(_ newValue: Value) -> Value { + return modify { (value: inout Value) in + let oldValue = value + value = newValue + return oldValue + } + } +} diff --git a/ReactiveSwift/Bag.swift b/ReactiveSwift/Bag.swift new file mode 100644 index 0000000000..c7099938b7 --- /dev/null +++ b/ReactiveSwift/Bag.swift @@ -0,0 +1,110 @@ +// +// Bag.swift +// ReactiveSwift +// +// Created by Justin Spahr-Summers on 2014-07-10. +// Copyright (c) 2014 GitHub. All rights reserved. +// + +/// A uniquely identifying token for removing a value that was inserted into a +/// Bag. +public final class RemovalToken { + private var identifier: UInt? + + private init(identifier: UInt) { + self.identifier = identifier + } +} + +/// An unordered, non-unique collection of values of type `Element`. +public struct Bag { + private var elements: [BagElement] = [] + private var currentIdentifier: UInt = 0 + + public init() { + } + + /// Insert the given value into `self`, and return a token that can + /// later be passed to `removeValueForToken()`. + /// + /// - parameters: + /// - value: A value that will be inserted. + @discardableResult + public mutating func insert(_ value: Element) -> RemovalToken { + let (nextIdentifier, overflow) = UInt.addWithOverflow(currentIdentifier, 1) + if overflow { + reindex() + } + + let token = RemovalToken(identifier: currentIdentifier) + let element = BagElement(value: value, identifier: currentIdentifier, token: token) + + elements.append(element) + currentIdentifier = nextIdentifier + + return token + } + + /// Remove a value, given the token returned from `insert()`. + /// + /// - note: If the value has already been removed, nothing happens. + /// + /// - parameters: + /// - token: A token returned from a call to `insert()`. + public mutating func remove(using token: RemovalToken) { + if let identifier = token.identifier { + // Removal is more likely for recent objects than old ones. + for i in elements.indices.reversed() { + if elements[i].identifier == identifier { + elements.remove(at: i) + token.identifier = nil + break + } + } + } + } + + /// In the event of an identifier overflow (highly, highly unlikely), reset + /// all current identifiers to reclaim a contiguous set of available + /// identifiers for the future. + private mutating func reindex() { + for i in elements.indices { + currentIdentifier = UInt(i) + + elements[i].identifier = currentIdentifier + elements[i].token.identifier = currentIdentifier + } + } +} + +extension Bag: Collection { + public typealias Index = Array.Index + + public var startIndex: Index { + return elements.startIndex + } + + public var endIndex: Index { + return elements.endIndex + } + + public subscript(index: Index) -> Element { + return elements[index].value + } + + public func index(after i: Index) -> Index { + return i + 1 + } +} + +private struct BagElement { + let value: Value + var identifier: UInt + let token: RemovalToken +} + +extension BagElement: CustomStringConvertible { + var description: String { + return "BagElement(\(value))" + } +} diff --git a/ReactiveSwift/Deprecations+Removals.swift b/ReactiveSwift/Deprecations+Removals.swift new file mode 100644 index 0000000000..53899b6245 --- /dev/null +++ b/ReactiveSwift/Deprecations+Removals.swift @@ -0,0 +1,255 @@ +import Foundation +import enum Result.NoError + +// MARK: Removed Types and APIs in ReactiveCocoa 5.0. + +// Renamed Protocols +@available(*, unavailable, renamed:"ActionProtocol") +public enum ActionType {} + +@available(*, unavailable, renamed:"SignalProtocol") +public enum SignalType {} + +@available(*, unavailable, renamed:"SignalProducerProtocol") +public enum SignalProducerType {} + +@available(*, unavailable, renamed:"PropertyProtocol") +public enum PropertyType {} + +@available(*, unavailable, renamed:"MutablePropertyProtocol") +public enum MutablePropertyType {} + +@available(*, unavailable, renamed:"ObserverProtocol") +public enum ObserverType {} + +@available(*, unavailable, renamed:"SchedulerProtocol") +public enum SchedulerType {} + +@available(*, unavailable, renamed:"DateSchedulerProtocol") +public enum DateSchedulerType {} + +@available(*, unavailable, renamed:"OptionalProtocol") +public enum OptionalType {} + +@available(*, unavailable, renamed:"EventLoggerProtocol") +public enum EventLoggerType {} + +@available(*, unavailable, renamed:"EventProtocol") +public enum EventType {} + +// Renamed and Removed Types + +@available(*, unavailable, renamed:"Property") +public struct AnyProperty {} + +@available(*, unavailable, message:"Use 'Property(value:)' to create a constant property instead. 'ConstantProperty' is removed in RAC 5.0.") +public struct ConstantProperty {} + +// Renamed Properties + +extension Disposable { + @available(*, unavailable, renamed:"isDisposed") + public var disposed: Bool { fatalError() } +} + +extension ActionProtocol { + @available(*, unavailable, renamed:"isEnabled") + public var enabled: Bool { fatalError() } + + @available(*, unavailable, renamed:"isExecuting") + public var executing: Bool { fatalError() } +} + +// Renamed Enum cases + +extension Event { + @available(*, unavailable, renamed:"next") + public static var Next: Event { fatalError() } + + @available(*, unavailable, renamed:"failed") + public static var Failed: Event { fatalError() } + + @available(*, unavailable, renamed:"completed") + public static var Completed: Event { fatalError() } + + @available(*, unavailable, renamed:"interrupted") + public static var Interrupted: Event { fatalError() } +} + +extension ActionError { + @available(*, unavailable, renamed:"producerFailed") + public static var ProducerError: ActionError { fatalError() } + + @available(*, unavailable, renamed:"disabled") + public static var NotEnabled: ActionError { fatalError() } +} + +extension FlattenStrategy { + @available(*, unavailable, renamed:"latest") + public static var Latest: FlattenStrategy { fatalError() } + + @available(*, unavailable, renamed:"concat") + public static var Concat: FlattenStrategy { fatalError() } + + @available(*, unavailable, renamed:"merge") + public static var Merge: FlattenStrategy { fatalError() } +} + +// Methods + +extension Bag { + @available(*, unavailable, renamed:"remove(using:)") + public func removeValueForToken(_ token: RemovalToken) { fatalError() } +} + +extension CompositeDisposable { + @available(*, unavailable, renamed:"add(_:)") + public func addDisposable(_ d: Disposable) -> DisposableHandle { fatalError() } +} + +extension SignalProtocol { + @available(*, unavailable, renamed:"take(first:)") + public func take(_ count: Int) -> Signal { fatalError() } + + @available(*, unavailable, renamed:"take(last:)") + public func takeLast(_ count: Int) -> Signal { fatalError() } + + @available(*, unavailable, renamed:"skip(first:)") + public func skip(_ count: Int) -> Signal { fatalError() } + + @available(*, unavailable, renamed:"observe(on:)") + public func observeOn(_ scheduler: UIScheduler) -> Signal { fatalError() } + + @available(*, unavailable, renamed:"combineLatest(with:)") + public func combineLatestWith(_ otherSignal: S) -> Signal<(Value, S.Value), Error> { fatalError() } + + @available(*, unavailable, renamed:"zip(with:)") + public func zipWith(_ otherSignal: S) -> Signal<(Value, S.Value), Error> { fatalError() } + + @available(*, unavailable, renamed:"take(until:)") + public func takeUntil(_ trigger: Signal<(), NoError>) -> Signal { fatalError() } + + @available(*, unavailable, renamed:"take(untilReplacement:)") + public func takeUntilReplacement(_ replacement: Signal) -> Signal { fatalError() } + + @available(*, unavailable, renamed:"skip(until:)") + public func skipUntil(_ trigger: Signal<(), NoError>) -> Signal { fatalError() } + + @available(*, unavailable, renamed:"skip(while:)") + public func skipWhile(_ predicate: (Value) -> Bool) -> Signal { fatalError() } + + @available(*, unavailable, renamed:"take(while:)") + public func takeWhile(_ predicate: (Value) -> Bool) -> Signal { fatalError() } + + @available(*, unavailable, renamed:"timeout(after:raising:on:)") + public func timeoutWithError(_ error: Error, afterInterval: TimeInterval, onScheduler: SchedulerProtocol) -> Signal { fatalError() } + + @available(*, unavailable, message: "This Signal may emit errors which must be handled explicitly, or observed using `observeResult(_:)`") + public func observeNext(_ next: (Value) -> Void) -> Disposable? { fatalError() } +} + +extension SignalProtocol where Value: OptionalProtocol { + @available(*, unavailable, renamed:"skipNil()") + public func ignoreNil() -> SignalProducer { fatalError() } +} + +extension SignalProducerProtocol { + @available(*, unavailable, renamed:"take(first:)") + public func take(_ count: Int) -> SignalProducer { fatalError() } + + @available(*, unavailable, renamed:"take(last:)") + public func takeLast(_ count: Int) -> SignalProducer { fatalError() } + + @available(*, unavailable, renamed:"skip(first:)") + public func skip(_ count: Int) -> SignalProducer { fatalError() } + + @available(*, unavailable, renamed:"observe(on:)") + public func observeOn(_ scheduler: UIScheduler) -> SignalProducer { fatalError() } + + @available(*, unavailable, renamed:"start(on:)") + public func startOn(_ scheduler: UIScheduler) -> SignalProducer { fatalError() } + + @available(*, unavailable, renamed:"combineLatest(with:)") + public func combineLatestWith(_ otherProducer: SignalProducer) -> SignalProducer<(Value, U), Error> { fatalError() } + + @available(*, unavailable, renamed:"combineLatest(with:)") + public func combineLatestWith(_ otherSignal: Signal) -> SignalProducer<(Value, U), Error> { fatalError() } + + @available(*, unavailable, renamed:"zip(with:)") + public func zipWith(_ otherProducer: SignalProducer) -> SignalProducer<(Value, U), Error> { fatalError() } + + @available(*, unavailable, renamed:"zip(with:)") + public func zipWith(_ otherSignal: Signal) -> SignalProducer<(Value, U), Error> { fatalError() } + + @available(*, unavailable, renamed:"take(until:)") + public func takeUntil(_ trigger: Signal<(), NoError>) -> SignalProducer { fatalError() } + + @available(*, unavailable, renamed:"take(until:)") + public func takeUntil(_ trigger: SignalProducer<(), NoError>) -> SignalProducer { fatalError() } + + @available(*, unavailable, renamed:"take(untilReplacement:)") + public func takeUntilReplacement(_ replacement: Signal) -> SignalProducer { fatalError() } + + @available(*, unavailable, renamed:"take(untilReplacement:)") + public func takeUntilReplacement(_ replacement: SignalProducer) -> SignalProducer { fatalError() } + + @available(*, unavailable, renamed:"skip(until:)") + public func skipUntil(_ trigger: Signal<(), NoError>) -> SignalProducer { fatalError() } + + @available(*, unavailable, renamed:"skip(until:)") + public func skipUntil(_ trigger: SignalProducer<(), NoError>) -> SignalProducer { fatalError() } + + @available(*, unavailable, renamed:"skip(while:)") + public func skipWhile(_ predicate: (Value) -> Bool) -> SignalProducer { fatalError() } + + @available(*, unavailable, renamed:"take(while:)") + public func takeWhile(_ predicate: (Value) -> Bool) -> SignalProducer { fatalError() } + + @available(*, unavailable, renamed:"timeout(after:raising:on:)") + public func timeoutWithError(_ error: Error, afterInterval: TimeInterval, onScheduler: SchedulerProtocol) -> SignalProducer { fatalError() } + + @available(*, unavailable, message:"This SignalProducer may emit errors which must be handled explicitly, or observed using `startWithResult(_:)`.") + public func startWithNext(_ next: (Value) -> Void) -> Disposable { fatalError() } +} + +extension SignalProducerProtocol where Value: OptionalProtocol { + @available(*, unavailable, renamed:"skipNil()") + public func ignoreNil() -> SignalProducer { fatalError() } +} + +extension SignalProducer { + @available(*, unavailable, message:"Use properties instead. `buffer(_:)` is removed in RAC 5.0.") + public static func buffer(_ capacity: Int) -> (SignalProducer, Signal.Observer) { fatalError() } +} + +extension PropertyProtocol { + @available(*, unavailable, renamed:"combineLatest(with:)") + public func combineLatestWith(_ otherProperty: P) -> Property<(Value, P.Value)> { fatalError() } + + @available(*, unavailable, renamed:"zip(with:)") + public func zipWith(_ otherProperty: P) -> Property<(Value, P.Value)> { fatalError() } +} + +extension Property { + @available(*, unavailable, renamed:"AnyProperty(initial:then:)") + public convenience init(initialValue: Value, producer: SignalProducer) { fatalError() } + + @available(*, unavailable, renamed:"AnyProperty(initial:then:)") + public convenience init(initialValue: Value, signal: Signal) { fatalError() } +} + +extension DateSchedulerProtocol { + @available(*, unavailable, renamed:"schedule(after:action:)") + func scheduleAfter(date: Date, _ action: () -> Void) -> Disposable? { fatalError() } + + @available(*, unavailable, renamed:"schedule(after:interval:leeway:)") + func scheduleAfter(date: Date, repeatingEvery: TimeInterval, withLeeway: TimeInterval, action: () -> Void) -> Disposable? { fatalError() } +} + +extension TestScheduler { + @available(*, unavailable, renamed:"advanced(by:)") + public func advanceByInterval(_ interval: TimeInterval) { fatalError() } + + @available(*, unavailable, renamed:"advanced(to:)") + public func advanceToDate(_ date: Date) { fatalError() } +} diff --git a/ReactiveSwift/Disposable.swift b/ReactiveSwift/Disposable.swift new file mode 100644 index 0000000000..9a7614b815 --- /dev/null +++ b/ReactiveSwift/Disposable.swift @@ -0,0 +1,353 @@ +// +// Disposable.swift +// ReactiveSwift +// +// Created by Justin Spahr-Summers on 2014-06-02. +// Copyright (c) 2014 GitHub. All rights reserved. +// + +/// Represents something that can be “disposed”, usually associated with freeing +/// resources or canceling work. +public protocol Disposable: class { + /// Whether this disposable has been disposed already. + var isDisposed: Bool { get } + + /// Method for disposing of resources when appropriate. + func dispose() +} + +/// A type-erased disposable that forwards operations to an underlying disposable. +public final class AnyDisposable: Disposable { + private let disposable: Disposable + + public var isDisposed: Bool { + return disposable.isDisposed + } + + public init(_ disposable: Disposable) { + self.disposable = disposable + } + + public func dispose() { + disposable.dispose() + } +} + +/// A disposable that only flips `isDisposed` upon disposal, and performs no other +/// work. +public final class SimpleDisposable: Disposable { + private let _isDisposed = Atomic(false) + + public var isDisposed: Bool { + return _isDisposed.value + } + + public init() {} + + public func dispose() { + _isDisposed.value = true + } +} + +/// A disposable that will run an action upon disposal. +public final class ActionDisposable: Disposable { + private let action: Atomic<(() -> Void)?> + + public var isDisposed: Bool { + return action.value == nil + } + + /// Initialize the disposable to run the given action upon disposal. + /// + /// - parameters: + /// - action: A closure to run when calling `dispose()`. + public init(action: () -> Void) { + self.action = Atomic(action) + } + + public func dispose() { + let oldAction = action.swap(nil) + oldAction?() + } +} + +/// A disposable that will dispose of any number of other disposables. +public final class CompositeDisposable: Disposable { + private let disposables: Atomic?> + + /// Represents a handle to a disposable previously added to a + /// CompositeDisposable. + public final class DisposableHandle { + private let bagToken: Atomic + private weak var disposable: CompositeDisposable? + + private static let empty = DisposableHandle() + + private init() { + self.bagToken = Atomic(nil) + } + + private init(bagToken: RemovalToken, disposable: CompositeDisposable) { + self.bagToken = Atomic(bagToken) + self.disposable = disposable + } + + /// Remove the pointed-to disposable from its `CompositeDisposable`. + /// + /// - note: This is useful to minimize memory growth, by removing + /// disposables that are no longer needed. + public func remove() { + if let token = bagToken.swap(nil) { + _ = disposable?.disposables.modify { + $0?.remove(using: token) + } + } + } + } + + public var isDisposed: Bool { + return disposables.value == nil + } + + /// Initialize a `CompositeDisposable` containing the given sequence of + /// disposables. + /// + /// - parameters: + /// - disposables: A collection of objects conforming to the `Disposable` + /// protocol + public init(_ disposables: S) { + var bag: Bag = Bag() + + for disposable in disposables { + bag.insert(disposable) + } + + self.disposables = Atomic(bag) + } + + /// Initialize a `CompositeDisposable` containing the given sequence of + /// disposables. + /// + /// - parameters: + /// - disposables: A collection of objects conforming to the `Disposable` + /// protocol + public convenience init(_ disposables: S) { + self.init(disposables.flatMap { $0 }) + } + + /// Initializes an empty `CompositeDisposable`. + public convenience init() { + self.init([Disposable]()) + } + + public func dispose() { + if let ds = disposables.swap(nil) { + for d in ds.reversed() { + d.dispose() + } + } + } + + /// Add the given disposable to the list, then return a handle which can + /// be used to opaquely remove the disposable later (if desired). + /// + /// - parameters: + /// - d: Optional disposable. + /// + /// - returns: An instance of `DisposableHandle` that can be used to + /// opaquely remove the disposable later (if desired). + @discardableResult + public func add(_ d: Disposable?) -> DisposableHandle { + guard let d = d else { + return DisposableHandle.empty + } + + let handle: DisposableHandle? = disposables.modify { + return ($0?.insert(d)).map { DisposableHandle(bagToken: $0, disposable: self) } + } + + if let handle = handle { + return handle + } else { + d.dispose() + return DisposableHandle.empty + } + } + + /// Add an ActionDisposable to the list. + /// + /// - parameters: + /// - action: A closure that will be invoked when `dispose()` is called. + /// + /// - returns: An instance of `DisposableHandle` that can be used to + /// opaquely remove the disposable later (if desired). + public func add(_ action: () -> Void) -> DisposableHandle { + return add(ActionDisposable(action: action)) + } +} + +/// A disposable that, upon deinitialization, will automatically dispose of +/// another disposable. +public final class ScopedDisposable: Disposable { + /// The disposable which will be disposed when the ScopedDisposable + /// deinitializes. + public let innerDisposable: InnerDisposable + + public var isDisposed: Bool { + return innerDisposable.isDisposed + } + + /// Initialize the receiver to dispose of the argument upon + /// deinitialization. + /// + /// - parameters: + /// - disposable: A disposable to dispose of when deinitializing. + public init(_ disposable: InnerDisposable) { + innerDisposable = disposable + } + + deinit { + dispose() + } + + public func dispose() { + innerDisposable.dispose() + } +} + +extension ScopedDisposable where InnerDisposable: AnyDisposable { + /// Initialize the receiver to dispose of the argument upon + /// deinitialization. + /// + /// - parameters: + /// - disposable: A disposable to dispose of when deinitializing, which + /// will be wrapped in an `AnyDisposable`. + public convenience init(_ disposable: Disposable) { + self.init(AnyDisposable(disposable)) + } +} + +/// A disposable that will optionally dispose of another disposable. +public final class SerialDisposable: Disposable { + private struct State { + var innerDisposable: Disposable? = nil + var isDisposed = false + } + + private let state = Atomic(State()) + + public var isDisposed: Bool { + return state.value.isDisposed + } + + /// The inner disposable to dispose of. + /// + /// Whenever this property is set (even to the same value!), the previous + /// disposable is automatically disposed. + public var innerDisposable: Disposable? { + get { + return state.value.innerDisposable + } + + set(d) { + let oldState: State = state.modify { state in + defer { state.innerDisposable = d } + return state + } + + oldState.innerDisposable?.dispose() + if oldState.isDisposed { + d?.dispose() + } + } + } + + /// Initializes the receiver to dispose of the argument when the + /// SerialDisposable is disposed. + /// + /// - parameters: + /// - disposable: Optional disposable. + public init(_ disposable: Disposable? = nil) { + innerDisposable = disposable + } + + public func dispose() { + let orig = state.swap(State(innerDisposable: nil, isDisposed: true)) + orig.innerDisposable?.dispose() + } +} + +/// Adds the right-hand-side disposable to the left-hand-side +/// `CompositeDisposable`. +/// +/// ```` +/// disposable += producer +/// .filter { ... } +/// .map { ... } +/// .start(observer) +/// ```` +/// +/// - parameters: +/// - lhs: Disposable to add to. +/// - rhs: Disposable to add. +/// +/// - returns: An instance of `DisposableHandle` that can be used to opaquely +/// remove the disposable later (if desired). +@discardableResult +public func +=(lhs: CompositeDisposable, rhs: Disposable?) -> CompositeDisposable.DisposableHandle { + return lhs.add(rhs) +} + +/// Adds the right-hand-side `ActionDisposable` to the left-hand-side +/// `CompositeDisposable`. +/// +/// ```` +/// disposable += { ... } +/// ```` +/// +/// - parameters: +/// - lhs: Disposable to add to. +/// - rhs: Closure to add as a disposable. +/// +/// - returns: An instance of `DisposableHandle` that can be used to opaquely +/// remove the disposable later (if desired). +@discardableResult +public func +=(lhs: CompositeDisposable, rhs: () -> ()) -> CompositeDisposable.DisposableHandle { + return lhs.add(rhs) +} + +/// Adds the right-hand-side disposable to the left-hand-side +/// `ScopedDisposable`. +/// +/// ```` +/// disposable += { ... } +/// ```` +/// +/// - parameters: +/// - lhs: Disposable to add to. +/// - rhs: Disposable to add. +/// +/// - returns: An instance of `DisposableHandle` that can be used to opaquely +/// remove the disposable later (if desired). +@discardableResult +public func +=(lhs: ScopedDisposable, rhs: Disposable?) -> CompositeDisposable.DisposableHandle { + return lhs.innerDisposable.add(rhs) +} + +/// Adds the right-hand-side disposable to the left-hand-side +/// `ScopedDisposable`. +/// +/// ```` +/// disposable += { ... } +/// ```` +/// +/// - parameters: +/// - lhs: Disposable to add to. +/// - rhs: Closure to add as a disposable. +/// +/// - returns: An instance of `DisposableHandle` that can be used to opaquely +/// remove the disposable later (if desired). +@discardableResult +public func +=(lhs: ScopedDisposable, rhs: () -> ()) -> CompositeDisposable.DisposableHandle { + return lhs.innerDisposable.add(rhs) +} diff --git a/ReactiveSwift/Event.swift b/ReactiveSwift/Event.swift new file mode 100644 index 0000000000..4547483161 --- /dev/null +++ b/ReactiveSwift/Event.swift @@ -0,0 +1,165 @@ +// +// Event.swift +// ReactiveSwift +// +// Created by Justin Spahr-Summers on 2015-01-16. +// Copyright (c) 2015 GitHub. All rights reserved. +// + +/// Represents a signal event. +/// +/// Signals must conform to the grammar: +/// `Next* (Failed | Completed | Interrupted)?` +public enum Event { + /// A value provided by the signal. + case next(Value) + + /// The signal terminated because of an error. No further events will be + /// received. + case failed(Error) + + /// The signal successfully terminated. No further events will be received. + case completed + + /// Event production on the signal has been interrupted. No further events + /// will be received. + /// + /// - important: This event does not signify the successful or failed + /// completion of the signal. + case interrupted + + /// Whether this event indicates signal termination (i.e., that no further + /// events will be received). + public var isTerminating: Bool { + switch self { + case .next: + return false + + case .failed, .completed, .interrupted: + return true + } + } + + /// Lift the given closure over the event's value. + /// + /// - important: The closure is called only on `next` type events. + /// + /// - parameters: + /// - f: A closure that accepts a value and returns a new value + /// + /// - returns: An event with function applied to a value in case `self` is a + /// `next` type of event. + public func map(_ f: (Value) -> U) -> Event { + switch self { + case let .next(value): + return .next(f(value)) + + case let .failed(error): + return .failed(error) + + case .completed: + return .completed + + case .interrupted: + return .interrupted + } + } + + /// Lift the given closure over the event's error. + /// + /// - important: The closure is called only on failed type event. + /// + /// - parameters: + /// - f: A closure that accepts an error object and returns + /// a new error object + /// + /// - returns: An event with function applied to an error object in case + /// `self` is a `.Failed` type of event. + public func mapError(_ f: (Error) -> F) -> Event { + switch self { + case let .next(value): + return .next(value) + + case let .failed(error): + return .failed(f(error)) + + case .completed: + return .completed + + case .interrupted: + return .interrupted + } + } + + /// Unwrap the contained `next` value. + public var value: Value? { + if case let .next(value) = self { + return value + } else { + return nil + } + } + + /// Unwrap the contained `Error` value. + public var error: Error? { + if case let .failed(error) = self { + return error + } else { + return nil + } + } +} + +public func == (lhs: Event, rhs: Event) -> Bool { + switch (lhs, rhs) { + case let (.next(left), .next(right)): + return left == right + + case let (.failed(left), .failed(right)): + return left == right + + case (.completed, .completed): + return true + + case (.interrupted, .interrupted): + return true + + default: + return false + } +} + +extension Event: CustomStringConvertible { + public var description: String { + switch self { + case let .next(value): + return "NEXT \(value)" + + case let .failed(error): + return "FAILED \(error)" + + case .completed: + return "COMPLETED" + + case .interrupted: + return "INTERRUPTED" + } + } +} + +/// Event protocol for constraining signal extensions +public protocol EventProtocol { + /// The value type of an event. + associatedtype Value + /// The error type of an event. If errors aren't possible then `NoError` can + /// be used. + associatedtype Error: Swift.Error + /// Extracts the event from the receiver. + var event: Event { get } +} + +extension Event: EventProtocol { + public var event: Event { + return self + } +} diff --git a/ReactiveSwift/EventLogger.swift b/ReactiveSwift/EventLogger.swift new file mode 100644 index 0000000000..c6181b3479 --- /dev/null +++ b/ReactiveSwift/EventLogger.swift @@ -0,0 +1,132 @@ +// +// EventLogger.swift +// ReactiveSwift +// +// Created by Rui Peres on 30/04/2016. +// Copyright © 2016 GitHub. All rights reserved. +// + +import Foundation + +/// A namespace for logging event types. +public enum LoggingEvent { + public enum Signal: String { + case next, completed, failed, terminated, disposed, interrupted + + public static let allEvents: Set = [ + .next, .completed, .failed, .terminated, .disposed, .interrupted, + ] + } + + public enum SignalProducer: String { + case started, next, completed, failed, terminated, disposed, interrupted + + public static let allEvents: Set = [ + .started, .next, .completed, .failed, .terminated, .disposed, .interrupted, + ] + } +} + +private func defaultEventLog(identifier: String, event: String, fileName: String, functionName: String, lineNumber: Int) { + print("[\(identifier)] \(event) fileName: \(fileName), functionName: \(functionName), lineNumber: \(lineNumber)") +} + +/// A type that represents an event logging function. +public typealias EventLogger = (identifier: String, event: String, fileName: String, functionName: String, lineNumber: Int) -> Void + +extension SignalProtocol { + /// Logs all events that the receiver sends. By default, it will print to + /// the standard output. + /// + /// - parameters: + /// - identifier: a string to identify the Signal firing events. + /// - events: Types of events to log. + /// - fileName: Name of the file containing the code which fired the + /// event. + /// - functionName: Function where event was fired. + /// - lineNumber: Line number where event was fired. + /// - logger: Logger that logs the events. + /// + /// - returns: Signal that, when observed, logs the fired events. + public func logEvents(identifier: String = "", events: Set = LoggingEvent.Signal.allEvents, fileName: String = #file, functionName: String = #function, lineNumber: Int = #line, logger: EventLogger = defaultEventLog) -> Signal { + func log(_ event: LoggingEvent.Signal) -> ((T) -> Void)? { + return event.logIfNeeded(events: events) { event in + logger(identifier: identifier, event: event, fileName: fileName, functionName: functionName, lineNumber: lineNumber) + } + } + + return self.on( + failed: log(.failed), + completed: log(.completed), + interrupted: log(.interrupted), + terminated: log(.terminated), + disposed: log(.disposed), + next: log(.next) + ) + } +} + +extension SignalProducerProtocol { + /// Logs all events that the receiver sends. By default, it will print to + /// the standard output. + /// + /// - parameters: + /// - identifier: a string to identify the SignalProducer firing events. + /// - events: Types of events to log. + /// - fileName: Name of the file containing the code which fired the + /// event. + /// - functionName: Function where event was fired. + /// - lineNumber: Line number where event was fired. + /// - logger: Logger that logs the events. + /// + /// - returns: Signal producer that, when started, logs the fired events. + public func logEvents(identifier: String = "", + events: Set = LoggingEvent.SignalProducer.allEvents, + fileName: String = #file, + functionName: String = #function, + lineNumber: Int = #line, + logger: EventLogger = defaultEventLog + ) -> SignalProducer { + func log(_ event: LoggingEvent.SignalProducer) -> ((T) -> Void)? { + return event.logIfNeeded(events: events) { event in + logger( + identifier: identifier, + event: event, + fileName: fileName, + functionName: functionName, + lineNumber: lineNumber + ) + } + } + + return self.on( + started: log(.started), + next: log(.next), + failed: log(.failed), + completed: log(.completed), + interrupted: log(.interrupted), + terminated: log(.terminated), + disposed: log(.disposed) + ) + } +} + +private protocol LoggingEventProtocol: Hashable, RawRepresentable {} +extension LoggingEvent.Signal: LoggingEventProtocol {} +extension LoggingEvent.SignalProducer: LoggingEventProtocol {} + +private extension LoggingEventProtocol { + func logIfNeeded(events: Set, logger: (String) -> Void) -> ((T) -> Void)? { + guard events.contains(self) else { + return nil + } + + return { value in + if value is Void { + logger("\(self.rawValue)") + } else { + logger("\(self.rawValue) \(value)") + } + } + } +} diff --git a/ReactiveSwift/Flatten.swift b/ReactiveSwift/Flatten.swift new file mode 100644 index 0000000000..804621338f --- /dev/null +++ b/ReactiveSwift/Flatten.swift @@ -0,0 +1,918 @@ +// +// Flatten.swift +// ReactiveSwift +// +// Created by Neil Pankey on 11/30/15. +// Copyright © 2015 GitHub. All rights reserved. +// + +import enum Result.NoError + +/// Describes how multiple producers should be joined together. +public enum FlattenStrategy: Equatable { + /// The producers should be merged, so that any value received on any of the + /// input producers will be forwarded immediately to the output producer. + /// + /// The resulting producer will complete only when all inputs have + /// completed. + case merge + + /// The producers should be concatenated, so that their values are sent in + /// the order of the producers themselves. + /// + /// The resulting producer will complete only when all inputs have + /// completed. + case concat + + /// Only the events from the latest input producer should be considered for + /// the output. Any producers received before that point will be disposed + /// of. + /// + /// The resulting producer will complete only when the producer-of-producers + /// and the latest producer has completed. + case latest +} + + +extension SignalProtocol where Value: SignalProducerProtocol, Error == Value.Error { + /// Flattens the inner producers sent upon `signal` (into a single signal of + /// values), according to the semantics of the given strategy. + /// + /// - note: If `signal` or an active inner producer fails, the returned + /// signal will forward that failure immediately. + /// + /// - note: `interrupted` events on inner producers will be treated like + /// `Completed events on inner producers. + public func flatten(_ strategy: FlattenStrategy) -> Signal { + switch strategy { + case .merge: + return self.merge() + + case .concat: + return self.concat() + + case .latest: + return self.switchToLatest() + } + } +} + +extension SignalProtocol where Value: SignalProducerProtocol, Error == NoError { + /// Flattens the inner producers sent upon `signal` (into a single signal of + /// values), according to the semantics of the given strategy. + /// + /// - note: If an active inner producer fails, the returned signal will + /// forward that failure immediately. + /// + /// - warning: `interrupted` events on inner producers will be treated like + /// `completed` events on inner producers. + /// + /// - parameters: + /// - strategy: Strategy used when flattening signals. + public func flatten(_ strategy: FlattenStrategy) -> Signal { + return self + .promoteErrors(Value.Error.self) + .flatten(strategy) + } +} + +extension SignalProtocol where Value: SignalProducerProtocol, Error == NoError, Value.Error == NoError { + /// Flattens the inner producers sent upon `signal` (into a single signal of + /// values), according to the semantics of the given strategy. + /// + /// - warning: `interrupted` events on inner producers will be treated like + /// `completed` events on inner producers. + /// + /// - parameters: + /// - strategy: Strategy used when flattening signals. + public func flatten(_ strategy: FlattenStrategy) -> Signal { + switch strategy { + case .merge: + return self.merge() + + case .concat: + return self.concat() + + case .latest: + return self.switchToLatest() + } + } +} + +extension SignalProtocol where Value: SignalProducerProtocol, Value.Error == NoError { + /// Flattens the inner producers sent upon `signal` (into a single signal of + /// values), according to the semantics of the given strategy. + /// + /// - note: If `signal` fails, the returned signal will forward that failure + /// immediately. + /// + /// - warning: `interrupted` events on inner producers will be treated like + /// `completed` events on inner producers. + public func flatten(_ strategy: FlattenStrategy) -> Signal { + return self.flatMap(strategy) { $0.promoteErrors(Error.self) } + } +} + +extension SignalProducerProtocol where Value: SignalProducerProtocol, Error == Value.Error { + /// Flattens the inner producers sent upon `producer` (into a single + /// producer of values), according to the semantics of the given strategy. + /// + /// - note: If `producer` or an active inner producer fails, the returned + /// producer will forward that failure immediately. + /// + /// - warning: `interrupted` events on inner producers will be treated like + /// `completed` events on inner producers. + public func flatten(_ strategy: FlattenStrategy) -> SignalProducer { + switch strategy { + case .merge: + return self.merge() + + case .concat: + return self.concat() + + case .latest: + return self.switchToLatest() + } + } +} + +extension SignalProducerProtocol where Value: SignalProducerProtocol, Error == NoError { + /// Flattens the inner producers sent upon `producer` (into a single + /// producer of values), according to the semantics of the given strategy. + /// + /// - note: If an active inner producer fails, the returned producer will + /// forward that failure immediately. + /// + /// - warning: `interrupted` events on inner producers will be treated like + /// `completed` events on inner producers. + public func flatten(_ strategy: FlattenStrategy) -> SignalProducer { + return self + .promoteErrors(Value.Error.self) + .flatten(strategy) + } +} + +extension SignalProducerProtocol where Value: SignalProducerProtocol, Error == NoError, Value.Error == NoError { + /// Flattens the inner producers sent upon `producer` (into a single + /// producer of values), according to the semantics of the given strategy. + /// + /// - warning: `interrupted` events on inner producers will be treated like + /// `completed` events on inner producers. + public func flatten(_ strategy: FlattenStrategy) -> SignalProducer { + switch strategy { + case .merge: + return self.merge() + + case .concat: + return self.concat() + + case .latest: + return self.switchToLatest() + } + } +} + +extension SignalProducerProtocol where Value: SignalProducerProtocol, Value.Error == NoError { + /// Flattens the inner producers sent upon `signal` (into a single signal of + /// values), according to the semantics of the given strategy. + /// + /// - note: If `signal` fails, the returned signal will forward that failure + /// immediately. + /// + /// - warning: `interrupted` events on inner producers will be treated like + /// `completed` events on inner producers. + public func flatten(_ strategy: FlattenStrategy) -> SignalProducer { + return self.flatMap(strategy) { $0.promoteErrors(Error.self) } + } +} + +extension SignalProtocol where Value: SignalProtocol, Error == Value.Error { + /// Flattens the inner signals sent upon `signal` (into a single signal of + /// values), according to the semantics of the given strategy. + /// + /// - note: If `signal` or an active inner signal emits an error, the + /// returned signal will forward that error immediately. + /// + /// - warning: `interrupted` events on inner signals will be treated like + /// `completed` events on inner signals. + public func flatten(_ strategy: FlattenStrategy) -> Signal { + return self + .map(SignalProducer.init) + .flatten(strategy) + } +} + +extension SignalProtocol where Value: SignalProtocol, Error == NoError { + /// Flattens the inner signals sent upon `signal` (into a single signal of + /// values), according to the semantics of the given strategy. + /// + /// - note: If an active inner signal emits an error, the returned signal + /// will forward that error immediately. + /// + /// - warning: `interrupted` events on inner signals will be treated like + /// `completed` events on inner signals. + public func flatten(_ strategy: FlattenStrategy) -> Signal { + return self + .promoteErrors(Value.Error.self) + .flatten(strategy) + } +} + +extension SignalProtocol where Value: SignalProtocol, Error == NoError, Value.Error == NoError { + /// Flattens the inner signals sent upon `signal` (into a single signal of + /// values), according to the semantics of the given strategy. + /// + /// - warning: `interrupted` events on inner signals will be treated like + /// `completed` events on inner signals. + public func flatten(_ strategy: FlattenStrategy) -> Signal { + return self + .map(SignalProducer.init) + .flatten(strategy) + } +} + +extension SignalProtocol where Value: SignalProtocol, Value.Error == NoError { + /// Flattens the inner signals sent upon `signal` (into a single signal of + /// values), according to the semantics of the given strategy. + /// + /// - note: If `signal` emits an error, the returned signal will forward + /// that error immediately. + /// + /// - warning: `interrupted` events on inner signals will be treated like + /// `completed` events on inner signals. + public func flatten(_ strategy: FlattenStrategy) -> Signal { + return self.flatMap(strategy) { $0.promoteErrors(Error.self) } + } +} + +extension SignalProtocol where Value: Sequence, Error == NoError { + /// Flattens the `sequence` value sent by `signal` according to + /// the semantics of the given strategy. + public func flatten(_ strategy: FlattenStrategy) -> Signal { + return self.flatMap(strategy) { .init(values: $0) } + } +} + +extension SignalProducerProtocol where Value: SignalProtocol, Error == Value.Error { + /// Flattens the inner signals sent upon `producer` (into a single producer + /// of values), according to the semantics of the given strategy. + /// + /// - note: If `producer` or an active inner signal emits an error, the + /// returned producer will forward that error immediately. + /// + /// - warning: `interrupted` events on inner signals will be treated like + /// `completed` events on inner signals. + public func flatten(_ strategy: FlattenStrategy) -> SignalProducer { + return self + .map(SignalProducer.init) + .flatten(strategy) + } +} + +extension SignalProducerProtocol where Value: SignalProtocol, Error == NoError { + /// Flattens the inner signals sent upon `producer` (into a single producer + /// of values), according to the semantics of the given strategy. + /// + /// - note: If an active inner signal emits an error, the returned producer + /// will forward that error immediately. + /// + /// - warning: `interrupted` events on inner signals will be treated like + /// `completed` events on inner signals. + public func flatten(_ strategy: FlattenStrategy) -> SignalProducer { + return self + .promoteErrors(Value.Error.self) + .flatten(strategy) + } +} + +extension SignalProducerProtocol where Value: SignalProtocol, Error == NoError, Value.Error == NoError { + /// Flattens the inner signals sent upon `producer` (into a single producer + /// of values), according to the semantics of the given strategy. + /// + /// - warning: `interrupted` events on inner signals will be treated like + /// `completed` events on inner signals. + public func flatten(_ strategy: FlattenStrategy) -> SignalProducer { + return self + .map(SignalProducer.init) + .flatten(strategy) + } +} + +extension SignalProducerProtocol where Value: SignalProtocol, Value.Error == NoError { + /// Flattens the inner signals sent upon `producer` (into a single producer + /// of values), according to the semantics of the given strategy. + /// + /// - note: If `producer` emits an error, the returned producer will forward + /// that error immediately. + /// + /// - warning: `interrupted` events on inner signals will be treated like + /// `completed` events on inner signals. + public func flatten(_ strategy: FlattenStrategy) -> SignalProducer { + return self.flatMap(strategy) { $0.promoteErrors(Error.self) } + } +} + +extension SignalProducerProtocol where Value: Sequence, Error == NoError { + /// Flattens the `sequence` value sent by `producer` according to + /// the semantics of the given strategy. + public func flatten(_ strategy: FlattenStrategy) -> SignalProducer { + return self.flatMap(strategy) { .init(values: $0) } + } +} + +extension SignalProtocol where Value: SignalProducerProtocol, Error == Value.Error { + /// Returns a signal which sends all the values from producer signal emitted + /// from `signal`, waiting until each inner producer completes before + /// beginning to send the values from the next inner producer. + /// + /// - note: If any of the inner producers fail, the returned signal will + /// forward that failure immediately + /// + /// - note: The returned signal completes only when `signal` and all + /// producers emitted from `signal` complete. + private func concat() -> Signal { + return Signal { relayObserver in + let disposable = CompositeDisposable() + let relayDisposable = CompositeDisposable() + + disposable += relayDisposable + disposable += self.observeConcat(relayObserver, relayDisposable) + + return disposable + } + } + + private func observeConcat(_ observer: Observer, _ disposable: CompositeDisposable? = nil) -> Disposable? { + let state = ConcatState(observer: observer, disposable: disposable) + + return self.observe { event in + switch event { + case let .next(value): + state.enqueueSignalProducer(value.producer) + + case let .failed(error): + observer.sendFailed(error) + + case .completed: + // Add one last producer to the queue, whose sole job is to + // "turn out the lights" by completing `observer`. + state.enqueueSignalProducer(SignalProducer.empty.on(completed: { + observer.sendCompleted() + })) + + case .interrupted: + observer.sendInterrupted() + } + } + } +} + +extension SignalProducerProtocol where Value: SignalProducerProtocol, Error == Value.Error { + /// Returns a producer which sends all the values from each producer emitted + /// from `producer`, waiting until each inner producer completes before + /// beginning to send the values from the next inner producer. + /// + /// - note: If any of the inner producers emit an error, the returned + /// producer will emit that error. + /// + /// - note: The returned producer completes only when `producer` and all + /// producers emitted from `producer` complete. + private func concat() -> SignalProducer { + return SignalProducer { observer, disposable in + self.startWithSignal { signal, signalDisposable in + disposable += signalDisposable + _ = signal.observeConcat(observer, disposable) + } + } + } +} + +extension SignalProducerProtocol { + /// `concat`s `next` onto `self`. + public func concat(_ next: SignalProducer) -> SignalProducer { + return SignalProducer, Error>(values: [ self.producer, next ]).flatten(.concat) + } + + /// `concat`s `value` onto `self`. + public func concat(value: Value) -> SignalProducer { + return self.concat(SignalProducer(value: value)) + } + + /// `concat`s `self` onto initial `previous`. + public func prefix(_ previous: P) -> SignalProducer { + return previous.concat(self.producer) + } + + /// `concat`s `self` onto initial `value`. + public func prefix(value: Value) -> SignalProducer { + return self.prefix(SignalProducer(value: value)) + } +} + +private final class ConcatState { + /// The observer of a started `concat` producer. + let observer: Observer + + /// The top level disposable of a started `concat` producer. + let disposable: CompositeDisposable? + + /// The active producer, if any, and the producers waiting to be started. + let queuedSignalProducers: Atomic<[SignalProducer]> = Atomic([]) + + init(observer: Signal.Observer, disposable: CompositeDisposable?) { + self.observer = observer + self.disposable = disposable + } + + func enqueueSignalProducer(_ producer: SignalProducer) { + if let d = disposable, d.isDisposed { + return + } + + let shouldStart: Bool = queuedSignalProducers.modify { queue in + // An empty queue means the concat is idle, ready & waiting to start + // the next producer. + defer { queue.append(producer) } + return queue.isEmpty + } + + if shouldStart { + startNextSignalProducer(producer) + } + } + + func dequeueSignalProducer() -> SignalProducer? { + if let d = disposable, d.isDisposed { + return nil + } + + return queuedSignalProducers.modify { queue in + // Active producers remain in the queue until completed. Since + // dequeueing happens at completion of the active producer, the + // first producer in the queue can be removed. + if !queue.isEmpty { queue.remove(at: 0) } + return queue.first + } + } + + /// Subscribes to the given signal producer. + func startNextSignalProducer(_ signalProducer: SignalProducer) { + signalProducer.startWithSignal { signal, disposable in + let handle = self.disposable?.add(disposable) ?? nil + + signal.observe { event in + switch event { + case .completed, .interrupted: + handle?.remove() + + if let nextSignalProducer = self.dequeueSignalProducer() { + self.startNextSignalProducer(nextSignalProducer) + } + + case .next, .failed: + self.observer.action(event) + } + } + } + } +} + +extension SignalProtocol where Value: SignalProducerProtocol, Error == Value.Error { + /// Merges a `signal` of SignalProducers down into a single signal, biased + /// toward the producer added earlier. Returns a Signal that will forward + /// events from the inner producers as they arrive. + private func merge() -> Signal { + return Signal { relayObserver in + let disposable = CompositeDisposable() + let relayDisposable = CompositeDisposable() + + disposable += relayDisposable + disposable += self.observeMerge(relayObserver, relayDisposable) + + return disposable + } + } + + private func observeMerge(_ observer: Observer, _ disposable: CompositeDisposable) -> Disposable? { + let inFlight = Atomic(1) + let decrementInFlight = { + let shouldComplete: Bool = inFlight.modify { + $0 -= 1 + return $0 == 0 + } + + if shouldComplete { + observer.sendCompleted() + } + } + + return self.observe { event in + switch event { + case let .next(producer): + producer.startWithSignal { innerSignal, innerDisposable in + inFlight.modify { $0 += 1 } + let handle = disposable.add(innerDisposable) + + innerSignal.observe { event in + switch event { + case .completed, .interrupted: + handle.remove() + decrementInFlight() + + case .next, .failed: + observer.action(event) + } + } + } + + case let .failed(error): + observer.sendFailed(error) + + case .completed: + decrementInFlight() + + case .interrupted: + observer.sendInterrupted() + } + } + } +} + +extension SignalProducerProtocol where Value: SignalProducerProtocol, Error == Value.Error { + /// Merges a `signal` of SignalProducers down into a single signal, biased + /// toward the producer added earlier. Returns a Signal that will forward + /// events from the inner producers as they arrive. + private func merge() -> SignalProducer { + return SignalProducer { relayObserver, disposable in + self.startWithSignal { signal, signalDisposable in + disposable += signalDisposable + + _ = signal.observeMerge(relayObserver, disposable) + } + + } + } +} + +extension SignalProtocol { + /// Merges the given signals into a single `Signal` that will emit all + /// values from each of them, and complete when all of them have completed. + public static func merge(_ signals: Seq) -> Signal { + let producer = SignalProducer(values: signals) + var result: Signal! + + producer.startWithSignal { signal, _ in + result = signal.flatten(.merge) + } + + return result + } + + /// Merges the given signals into a single `Signal` that will emit all + /// values from each of them, and complete when all of them have completed. + public static func merge(_ signals: S...) -> Signal { + return Signal.merge(signals) + } +} + +extension SignalProducerProtocol { + /// Merges the given producers into a single `SignalProducer` that will emit + /// all values from each of them, and complete when all of them have + /// completed. + public static func merge(_ producers: Seq) -> SignalProducer { + return SignalProducer(values: producers).flatten(.merge) + } + + /// Merges the given producers into a single `SignalProducer` that will emit + /// all values from each of them, and complete when all of them have + /// completed. + public static func merge(_ producers: S...) -> SignalProducer { + return SignalProducer.merge(producers) + } +} + +extension SignalProtocol where Value: SignalProducerProtocol, Error == Value.Error { + /// Returns a signal that forwards values from the latest signal sent on + /// `signal`, ignoring values sent on previous inner signal. + /// + /// An error sent on `signal` or the latest inner signal will be sent on the + /// returned signal. + /// + /// The returned signal completes when `signal` and the latest inner + /// signal have both completed. + private func switchToLatest() -> Signal { + return Signal { observer in + let composite = CompositeDisposable() + let serial = SerialDisposable() + + composite += serial + composite += self.observeSwitchToLatest(observer, serial) + + return composite + } + } + + private func observeSwitchToLatest(_ observer: Observer, _ latestInnerDisposable: SerialDisposable) -> Disposable? { + let state = Atomic(LatestState()) + + return self.observe { event in + switch event { + case let .next(innerProducer): + innerProducer.startWithSignal { innerSignal, innerDisposable in + state.modify { + // When we replace the disposable below, this prevents + // the generated Interrupted event from doing any work. + $0.replacingInnerSignal = true + } + + latestInnerDisposable.innerDisposable = innerDisposable + + state.modify { + $0.replacingInnerSignal = false + $0.innerSignalComplete = false + } + + innerSignal.observe { event in + switch event { + case .interrupted: + // If interruption occurred as a result of a new + // producer arriving, we don't want to notify our + // observer. + let shouldComplete: Bool = state.modify { state in + if !state.replacingInnerSignal { + state.innerSignalComplete = true + } + return !state.replacingInnerSignal && state.outerSignalComplete + } + + if shouldComplete { + observer.sendCompleted() + } + + case .completed: + let shouldComplete: Bool = state.modify { + $0.innerSignalComplete = true + return $0.outerSignalComplete + } + + if shouldComplete { + observer.sendCompleted() + } + + case .next, .failed: + observer.action(event) + } + } + } + + case let .failed(error): + observer.sendFailed(error) + + case .completed: + let shouldComplete: Bool = state.modify { + $0.outerSignalComplete = true + return $0.innerSignalComplete + } + + if shouldComplete { + observer.sendCompleted() + } + + case .interrupted: + observer.sendInterrupted() + } + } + } +} + +extension SignalProducerProtocol where Value: SignalProducerProtocol, Error == Value.Error { + /// Returns a signal that forwards values from the latest signal sent on + /// `signal`, ignoring values sent on previous inner signal. + /// + /// An error sent on `signal` or the latest inner signal will be sent on the + /// returned signal. + /// + /// The returned signal completes when `signal` and the latest inner + /// signal have both completed. + private func switchToLatest() -> SignalProducer { + return SignalProducer { observer, disposable in + let latestInnerDisposable = SerialDisposable() + disposable += latestInnerDisposable + + self.startWithSignal { signal, signalDisposable in + disposable += signalDisposable + disposable += signal.observeSwitchToLatest(observer, latestInnerDisposable) + } + } + } +} + +private struct LatestState { + var outerSignalComplete: Bool = false + var innerSignalComplete: Bool = true + + var replacingInnerSignal: Bool = false +} + + +extension SignalProtocol { + /// Maps each event from `signal` to a new signal, then flattens the + /// resulting producers (into a signal of values), according to the + /// semantics of the given strategy. + /// + /// If `signal` or any of the created producers fail, the returned signal + /// will forward that failure immediately. + public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> SignalProducer) -> Signal { + return map(transform).flatten(strategy) + } + + /// Maps each event from `signal` to a new signal, then flattens the + /// resulting producers (into a signal of values), according to the + /// semantics of the given strategy. + /// + /// If `signal` fails, the returned signal will forward that failure + /// immediately. + public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> SignalProducer) -> Signal { + return map(transform).flatten(strategy) + } + + /// Maps each event from `signal` to a new signal, then flattens the + /// resulting signals (into a signal of values), according to the + /// semantics of the given strategy. + /// + /// If `signal` or any of the created signals emit an error, the returned + /// signal will forward that error immediately. + public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> Signal) -> Signal { + return map(transform).flatten(strategy) + } + + /// Maps each event from `signal` to a new signal, then flattens the + /// resulting signals (into a signal of values), according to the + /// semantics of the given strategy. + /// + /// If `signal` emits an error, the returned signal will forward that + /// error immediately. + public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> Signal) -> Signal { + return map(transform).flatten(strategy) + } +} + +extension SignalProtocol where Error == NoError { + /// Maps each event from `signal` to a new signal, then flattens the + /// resulting signals (into a signal of values), according to the + /// semantics of the given strategy. + /// + /// If any of the created signals emit an error, the returned signal + /// will forward that error immediately. + public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> SignalProducer) -> Signal { + return map(transform).flatten(strategy) + } + + /// Maps each event from `signal` to a new signal, then flattens the + /// resulting signals (into a signal of values), according to the + /// semantics of the given strategy. + public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> SignalProducer) -> Signal { + return map(transform).flatten(strategy) + } + + /// Maps each event from `signal` to a new signal, then flattens the + /// resulting signals (into a signal of values), according to the + /// semantics of the given strategy. + /// + /// If any of the created signals emit an error, the returned signal + /// will forward that error immediately. + public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> Signal) -> Signal { + return map(transform).flatten(strategy) + } + + /// Maps each event from `signal` to a new signal, then flattens the + /// resulting signals (into a signal of values), according to the + /// semantics of the given strategy. + public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> Signal) -> Signal { + return map(transform).flatten(strategy) + } +} + +extension SignalProducerProtocol { + /// Maps each event from `self` to a new producer, then flattens the + /// resulting producers (into a producer of values), according to the + /// semantics of the given strategy. + /// + /// If `self` or any of the created producers fail, the returned producer + /// will forward that failure immediately. + public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> SignalProducer) -> SignalProducer { + return map(transform).flatten(strategy) + } + + /// Maps each event from `self` to a new producer, then flattens the + /// resulting producers (into a producer of values), according to the + /// semantics of the given strategy. + /// + /// If `self` fails, the returned producer will forward that failure + /// immediately. + public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> SignalProducer) -> SignalProducer { + return map(transform).flatten(strategy) + } + + /// Maps each event from `self` to a new producer, then flattens the + /// resulting signals (into a producer of values), according to the + /// semantics of the given strategy. + /// + /// If `self` or any of the created signals emit an error, the returned + /// producer will forward that error immediately. + public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> Signal) -> SignalProducer { + return map(transform).flatten(strategy) + } + + /// Maps each event from `self` to a new producer, then flattens the + /// resulting signals (into a producer of values), according to the + /// semantics of the given strategy. + /// + /// If `self` emits an error, the returned producer will forward that + /// error immediately. + public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> Signal) -> SignalProducer { + return map(transform).flatten(strategy) + } +} + +extension SignalProducerProtocol where Error == NoError { + /// Maps each event from `self` to a new producer, then flattens the + /// resulting producers (into a producer of values), according to the + /// semantics of the given strategy. + /// + /// If any of the created producers fail, the returned producer will + /// forward that failure immediately. + public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> SignalProducer) -> SignalProducer { + return map(transform).flatten(strategy) + } + + /// Maps each event from `self` to a new producer, then flattens the + /// resulting producers (into a producer of values), according to the + /// semantics of the given strategy. + public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> SignalProducer) -> SignalProducer { + return map(transform).flatten(strategy) + } + + /// Maps each event from `self` to a new producer, then flattens the + /// resulting signals (into a producer of values), according to the + /// semantics of the given strategy. + /// + /// If any of the created signals emit an error, the returned + /// producer will forward that error immediately. + public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> Signal) -> SignalProducer { + return map(transform).flatten(strategy) + } + + /// Maps each event from `self` to a new producer, then flattens the + /// resulting signals (into a producer of values), according to the + /// semantics of the given strategy. + public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> Signal) -> SignalProducer { + return map(transform).flatten(strategy) + } +} + + +extension SignalProtocol { + /// Catches any failure that may occur on the input signal, mapping to a new + /// producer that starts in its place. + public func flatMapError(_ handler: (Error) -> SignalProducer) -> Signal { + return Signal { observer in + self.observeFlatMapError(handler, observer, SerialDisposable()) + } + } + + private func observeFlatMapError(_ handler: (Error) -> SignalProducer, _ observer: Observer, _ serialDisposable: SerialDisposable) -> Disposable? { + return self.observe { event in + switch event { + case let .next(value): + observer.sendNext(value) + case let .failed(error): + handler(error).startWithSignal { signal, disposable in + serialDisposable.innerDisposable = disposable + signal.observe(observer) + } + case .completed: + observer.sendCompleted() + case .interrupted: + observer.sendInterrupted() + } + } + } +} + +extension SignalProducerProtocol { + /// Catches any failure that may occur on the input producer, mapping to a + /// new producer that starts in its place. + public func flatMapError(_ handler: (Error) -> SignalProducer) -> SignalProducer { + return SignalProducer { observer, disposable in + let serialDisposable = SerialDisposable() + disposable += serialDisposable + + self.startWithSignal { signal, signalDisposable in + serialDisposable.innerDisposable = signalDisposable + + _ = signal.observeFlatMapError(handler, observer, serialDisposable) + } + } + } +} diff --git a/ReactiveSwift/FoundationExtensions.swift b/ReactiveSwift/FoundationExtensions.swift new file mode 100644 index 0000000000..d5f0ef19da --- /dev/null +++ b/ReactiveSwift/FoundationExtensions.swift @@ -0,0 +1,80 @@ +// +// FoundationExtensions.swift +// ReactiveSwift +// +// Created by Justin Spahr-Summers on 2014-10-19. +// Copyright (c) 2014 GitHub. All rights reserved. +// + +import Foundation +import enum Result.NoError + +extension NotificationCenter { + /// Returns a SignalProducer to observe posting of the specified + /// notification. + /// + /// - parameters: + /// - name: name of the notification to observe + /// - object: an instance which sends the notifications + /// + /// - returns: A SignalProducer of notifications posted that match the given + /// criteria. + /// + /// - note: If the `object` is deallocated before starting the producer, it + /// will terminate immediately with an `interrupted` event. + /// Otherwise, the producer will not terminate naturally, so it must + /// be explicitly disposed to avoid leaks. + public func rac_notifications(forName name: Notification.Name?, object: AnyObject? = nil) -> SignalProducer { + // We're weakly capturing an optional reference here, which makes destructuring awkward. + let objectWasNil = (object == nil) + return SignalProducer { [weak object] observer, disposable in + guard object != nil || objectWasNil else { + observer.sendInterrupted() + return + } + + let notificationObserver = self.addObserver(forName: name, object: object, queue: nil) { notification in + observer.sendNext(notification) + } + + disposable += { + self.removeObserver(notificationObserver) + } + } + } +} + +private let defaultSessionError = NSError(domain: "org.reactivecocoa.ReactiveSwift.rac_dataWithRequest", code: 1, userInfo: nil) + +extension URLSession { + /// Returns a SignalProducer which performs the work associated with an + /// `NSURLSession` + /// + /// - parameters: + /// - request: A request that will be performed when the producer is + /// started + /// + /// - returns: A producer that will execute the given request once for each + /// invocation of `start()`. + /// + /// - note: This method will not send an error event in the case of a server + /// side error (i.e. when a response with status code other than + /// 200...299 is received). + public func rac_data(with request: URLRequest) -> SignalProducer<(Data, URLResponse), NSError> { + return SignalProducer { observer, disposable in + let task = self.dataTask(with: request) { data, response, error in + if let data = data, let response = response { + observer.sendNext((data, response)) + observer.sendCompleted() + } else { + observer.sendFailed(error ?? defaultSessionError) + } + } + + disposable += { + task.cancel() + } + task.resume() + } + } +} diff --git a/ReactiveSwift/Info.plist b/ReactiveSwift/Info.plist new file mode 100644 index 0000000000..9a11afdc43 --- /dev/null +++ b/ReactiveSwift/Info.plist @@ -0,0 +1,28 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSHumanReadableCopyright + Copyright © 2014 GitHub. All rights reserved. + NSPrincipalClass + + + diff --git a/ReactiveSwift/Lifetime.swift b/ReactiveSwift/Lifetime.swift new file mode 100644 index 0000000000..5ab129e6db --- /dev/null +++ b/ReactiveSwift/Lifetime.swift @@ -0,0 +1,50 @@ +import Foundation +import enum Result.NoError + +/// Represents the lifetime of an object, and provides a hook to observe when +/// the object deinitializes. +public final class Lifetime { + /// A signal that sends a Completed event when the lifetime ends. + public let ended: Signal<(), NoError> + + /// Initialize a `Lifetime` from a lifetime token, which is expected to be + /// associated with an object. + /// + /// - important: The resulting lifetime object does not retain the lifetime + /// token. + /// + /// - parameters: + /// - token: A lifetime token for detecting the deinitialization of the + /// associated object. + public init(_ token: Token) { + ended = token.ended + } + + /// A token object which completes its signal when it deinitializes. + /// + /// It is generally used in conjuncion with `Lifetime` as a private + /// deinitialization trigger. + /// + /// ``` + /// class MyController { + /// private let token = Lifetime.Token() + /// public var lifetime: Lifetime { + /// return Lifetime(token) + /// } + /// } + /// ``` + public final class Token { + /// A signal that sends a Completed event when the lifetime ends. + private let ended: Signal<(), NoError> + + private let endedObserver: Signal<(), NoError>.Observer + + public init() { + (ended, endedObserver) = Signal.pipe() + } + + deinit { + endedObserver.sendCompleted() + } + } +} diff --git a/ReactiveSwift/Observer.swift b/ReactiveSwift/Observer.swift new file mode 100644 index 0000000000..938df641cb --- /dev/null +++ b/ReactiveSwift/Observer.swift @@ -0,0 +1,104 @@ +// +// Observer.swift +// ReactiveSwift +// +// Created by Andy Matuschak on 10/2/15. +// Copyright © 2015 GitHub. All rights reserved. +// + +/// A protocol for type-constrained extensions of `Observer`. +public protocol ObserverProtocol { + associatedtype Value + associatedtype Error: Swift.Error + + /// Puts a `next` event into `self`. + func sendNext(_ value: Value) + + /// Puts a failed event into `self`. + func sendFailed(_ error: Error) + + /// Puts a `completed` event into `self`. + func sendCompleted() + + /// Puts an `interrupted` event into `self`. + func sendInterrupted() +} + +/// An Observer is a simple wrapper around a function which can receive Events +/// (typically from a Signal). +public final class Observer { + public typealias Action = (Event) -> Void + + /// An action that will be performed upon arrival of the event. + public let action: Action + + /// An initializer that accepts a closure accepting an event for the + /// observer. + /// + /// - parameters: + /// - action: A closure to lift over received event. + public init(_ action: Action) { + self.action = action + } + + /// An initializer that accepts closures for different event types. + /// + /// - parameters: + /// - next: Optional closure executed when a `next` event is observed. + /// - failed: Optional closure that accepts an `Error` parameter when a + /// failed event is observed. + /// - completed: Optional closure executed when a `completed` event is + /// observed. + /// - interruped: Optional closure executed when an `interrupted` event is + /// observed. + public convenience init( + next: ((Value) -> Void)? = nil, + failed: ((Error) -> Void)? = nil, + completed: (() -> Void)? = nil, + interrupted: (() -> Void)? = nil + ) { + self.init { event in + switch event { + case let .next(value): + next?(value) + + case let .failed(error): + failed?(error) + + case .completed: + completed?() + + case .interrupted: + interrupted?() + } + } + } +} + +extension Observer: ObserverProtocol { + /// Puts a `next` event into `self`. + /// + /// - parameters: + /// - value: A value sent with the `next` event. + public func sendNext(_ value: Value) { + action(.next(value)) + } + + /// Puts a failed event into `self`. + /// + /// - parameters: + /// - error: An error object sent with failed event. + public func sendFailed(_ error: Error) { + action(.failed(error)) + } + + /// Puts a `completed` event into `self`. + public func sendCompleted() { + action(.completed) + } + + /// Puts an `interrupted` event into `self`. + public func sendInterrupted() { + action(.interrupted) + } +} diff --git a/ReactiveSwift/Optional.swift b/ReactiveSwift/Optional.swift new file mode 100644 index 0000000000..c5446bd733 --- /dev/null +++ b/ReactiveSwift/Optional.swift @@ -0,0 +1,42 @@ +// +// Optional.swift +// ReactiveSwift +// +// Created by Neil Pankey on 6/24/15. +// Copyright (c) 2015 GitHub. All rights reserved. +// + +/// An optional protocol for use in type constraints. +public protocol OptionalProtocol { + /// The type contained in the otpional. + associatedtype Wrapped + + init(reconstructing value: Wrapped?) + + /// Extracts an optional from the receiver. + var optional: Wrapped? { get } +} + +extension Optional: OptionalProtocol { + public var optional: Wrapped? { + return self + } + + public init(reconstructing value: Wrapped?) { + self = value + } +} + +extension SignalProtocol { + /// Turns each value into an Optional. + internal func optionalize() -> Signal { + return map(Optional.init) + } +} + +extension SignalProducerProtocol { + /// Turns each value into an Optional. + internal func optionalize() -> SignalProducer { + return lift { $0.optionalize() } + } +} diff --git a/ReactiveSwift/Property.swift b/ReactiveSwift/Property.swift new file mode 100644 index 0000000000..0bf9a114f6 --- /dev/null +++ b/ReactiveSwift/Property.swift @@ -0,0 +1,878 @@ +import Foundation +import enum Result.NoError + +/// Represents a property that allows observation of its changes. +/// +/// Only classes can conform to this protocol, because having a signal +/// for changes over time implies the origin must have a unique identity. +public protocol PropertyProtocol: class { + associatedtype Value + + /// The current value of the property. + var value: Value { get } + + /// 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. + var producer: SignalProducer { get } + + /// A signal that will send the property's changes over time. It + /// completes when the property has deinitialized, or has no further + /// change. + var signal: Signal { get } +} + +/// Represents an observable property that can be mutated directly. +public protocol MutablePropertyProtocol: PropertyProtocol { + /// The current value of the property. + var value: Value { get set } +} + +/// Protocol composition operators +/// +/// The producer and the signal of transformed properties would complete +/// only when its source properties have deinitialized. +/// +/// A composed property would retain its ultimate source, but not +/// any intermediate property during the composition. +extension PropertyProtocol { + /// Lifts a unary SignalProducer operator to operate upon PropertyProtocol instead. + private func lift(_ transform: @noescape (SignalProducer) -> SignalProducer) -> Property { + return Property(self, transform: transform) + } + + /// Lifts a binary SignalProducer operator to operate upon PropertyProtocol instead. + private func lift(_ transform: @noescape (SignalProducer) -> (SignalProducer) -> SignalProducer) -> @noescape (P) -> Property { + return { otherProperty in + return Property(self, otherProperty, transform: transform) + } + } + + /// Maps the current value and all subsequent values to a new property. + /// + /// - parameters: + /// - transform: A closure that will map the current `value` of this + /// `Property` to a new value. + /// + /// - returns: A new instance of `AnyProperty` who's holds a mapped value + /// from `self`. + public func map(_ transform: (Value) -> U) -> Property { + return lift { $0.map(transform) } + } + + /// Combines the current value and the subsequent values of two `Property`s in + /// the manner described by `Signal.combineLatestWith:`. + /// + /// - parameters: + /// - other: A property to combine `self`'s value with. + /// + /// - returns: A property that holds a tuple containing values of `self` and + /// the given property. + public func combineLatest(with other: P) -> Property<(Value, P.Value)> { + return lift(SignalProducer.combineLatest(with:))(other) + } + + /// Zips the current value and the subsequent values of two `Property`s in + /// the manner described by `Signal.zipWith`. + /// + /// - parameters: + /// - other: A property to zip `self`'s value with. + /// + /// - returns: A property that holds a tuple containing values of `self` and + /// the given property. + public func zip(with other: P) -> Property<(Value, P.Value)> { + return lift(SignalProducer.zip(with:))(other) + } + + /// Forward events from `self` with history: values of the returned property + /// are a tuple whose first member is the previous value and whose second + /// member is the current value. `initial` is supplied as the first member + /// when `self` sends its first value. + /// + /// - parameters: + /// - initial: A value that will be combined with the first value sent by + /// `self`. + /// + /// - returns: A property that holds tuples that contain previous and + /// current values of `self`. + public func combinePrevious(_ initial: Value) -> Property<(Value, Value)> { + return lift { $0.combinePrevious(initial) } + } + + /// Forward only those values from `self` which do not pass `isRepeat` with + /// respect to the previous value. + /// + /// - parameters: + /// - isRepeat: A predicate to determine if the two given values are equal. + /// + /// - returns: A property that does not emit events for two equal values + /// sequentially. + public func skipRepeats(_ isRepeat: (Value, Value) -> Bool) -> Property { + return lift { $0.skipRepeats(isRepeat) } + } +} + +extension PropertyProtocol where Value: Equatable { + /// Forward only those values from `self` which do not pass `isRepeat` with + /// respect to the previous value. + /// + /// - returns: A property that does not emit events for two equal values + /// sequentially. + public func skipRepeats() -> Property { + return lift { $0.skipRepeats() } + } +} + +extension PropertyProtocol where Value: PropertyProtocol { + /// Flattens the inner property held by `self` (into a single property of + /// values), according to the semantics of the given strategy. + /// + /// - parameters: + /// - strategy: The preferred flatten strategy. + /// + /// - returns: A property that sends the values of its inner properties. + public func flatten(_ strategy: FlattenStrategy) -> Property { + return lift { $0.flatMap(strategy) { $0.producer } } + } +} + +extension PropertyProtocol { + /// Maps each property from `self` to a new property, then flattens the + /// resulting properties (into a single property), according to the + /// semantics of the given strategy. + /// + /// - parameters: + /// - strategy: The preferred flatten strategy. + /// - transform: The transform to be applied on `self` before flattening. + /// + /// - returns: A property that sends the values of its inner properties. + public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> P) -> Property { + return lift { $0.flatMap(strategy) { transform($0).producer } } + } + + /// Forward only those values from `self` that have unique identities across + /// the set of all values that have been held. + /// + /// - note: This causes the identities to be retained to check for + /// uniqueness. + /// + /// - parameters: + /// - transform: A closure that accepts a value and returns identity + /// value. + /// + /// - returns: A property that sends unique values during its lifetime. + public func uniqueValues(_ transform: (Value) -> Identity) -> Property { + return lift { $0.uniqueValues(transform) } + } +} + +extension PropertyProtocol where Value: Hashable { + /// Forwards only those values from `self` that are unique across the set of + /// all values that have been seen. + /// + /// - note: This causes the identities to be retained to check for uniqueness. + /// Providing a function that returns a unique value for each sent + /// value can help you reduce the memory footprint. + /// + /// - returns: A property that sends unique values during its lifetime. + public func uniqueValues() -> Property { + return lift { $0.uniqueValues() } + } +} + +extension PropertyProtocol { + /// Combines the values of all the given properties, in the manner described + /// by `combineLatest(with:)`. + public static func combineLatest(_ a: A, _ b: B) -> Property<(A.Value, B.Value)> { + return a.combineLatest(with: b) + } + + /// Combines the values of all the given properties, in the manner described + /// by `combineLatest(with:)`. + public static func combineLatest(_ a: A, _ b: B, _ c: C) -> Property<(A.Value, B.Value, C.Value)> { + return combineLatest(a, b) + .combineLatest(with: c) + .map(repack) + } + + /// Combines the values of all the given properties, in the manner described + /// by `combineLatest(with:)`. + public static func combineLatest(_ a: A, _ b: B, _ c: C, _ d: D) -> Property<(A.Value, B.Value, C.Value, D.Value)> { + return combineLatest(a, b, c) + .combineLatest(with: d) + .map(repack) + } + + /// Combines the values of all the given properties, in the manner described + /// by `combineLatest(with:)`. + public static func combineLatest(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value)> { + return combineLatest(a, b, c, d) + .combineLatest(with: e) + .map(repack) + } + + /// Combines the values of all the given properties, in the manner described + /// by `combineLatest(with:)`. + public static func combineLatest(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value)> { + return combineLatest(a, b, c, d, e) + .combineLatest(with: f) + .map(repack) + } + + /// Combines the values of all the given properties, in the manner described + /// by `combineLatest(with:)`. + public static func combineLatest(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value)> { + return combineLatest(a, b, c, d, e, f) + .combineLatest(with: g) + .map(repack) + } + + /// Combines the values of all the given properties, in the manner described + /// by `combineLatest(with:)`. + public static func combineLatest(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G, _ h: H) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value, H.Value)> { + return combineLatest(a, b, c, d, e, f, g) + .combineLatest(with: h) + .map(repack) + } + + /// Combines the values of all the given properties, in the manner described + /// by `combineLatest(with:)`. + public static func combineLatest(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G, _ h: H, _ i: I) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value, H.Value, I.Value)> { + return combineLatest(a, b, c, d, e, f, g, h) + .combineLatest(with: i) + .map(repack) + } + + /// Combines the values of all the given properties, in the manner described + /// by `combineLatest(with:)`. + public static func combineLatest(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G, _ h: H, _ i: I, _ j: J) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value, H.Value, I.Value, J.Value)> { + return combineLatest(a, b, c, d, e, f, g, h, i) + .combineLatest(with: j) + .map(repack) + } + + /// Combines the values of all the given producers, in the manner described by + /// `combineLatest(with:)`. Returns nil if the sequence is empty. + public static func combineLatest(_ properties: S) -> Property<[S.Iterator.Element.Value]>? { + var generator = properties.makeIterator() + if let first = generator.next() { + let initial = first.map { [$0] } + return IteratorSequence(generator).reduce(initial) { property, next in + property.combineLatest(with: next).map { $0.0 + [$0.1] } + } + } + + return nil + } + + /// Zips the values of all the given properties, in the manner described by + /// `zip(with:)`. + public static func zip(_ a: A, _ b: B) -> Property<(A.Value, B.Value)> { + return a.zip(with: b) + } + + /// Zips the values of all the given properties, in the manner described by + /// `zip(with:)`. + public static func zip(_ a: A, _ b: B, _ c: C) -> Property<(A.Value, B.Value, C.Value)> { + return zip(a, b) + .zip(with: c) + .map(repack) + } + + /// Zips the values of all the given properties, in the manner described by + /// `zip(with:)`. + public static func zip(_ a: A, _ b: B, _ c: C, _ d: D) -> Property<(A.Value, B.Value, C.Value, D.Value)> { + return zip(a, b, c) + .zip(with: d) + .map(repack) + } + + /// Zips the values of all the given properties, in the manner described by + /// `zip(with:)`. + public static func zip(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value)> { + return zip(a, b, c, d) + .zip(with: e) + .map(repack) + } + + /// Zips the values of all the given properties, in the manner described by + /// `zip(with:)`. + public static func zip(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value)> { + return zip(a, b, c, d, e) + .zip(with: f) + .map(repack) + } + + /// Zips the values of all the given properties, in the manner described by + /// `zip(with:)`. + public static func zip(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value)> { + return zip(a, b, c, d, e, f) + .zip(with: g) + .map(repack) + } + + /// Zips the values of all the given properties, in the manner described by + /// `zip(with:)`. + public static func zip(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G, _ h: H) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value, H.Value)> { + return zip(a, b, c, d, e, f, g) + .zip(with: h) + .map(repack) + } + + /// Zips the values of all the given properties, in the manner described by + /// `zip(with:)`. + public static func zip(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G, _ h: H, _ i: I) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value, H.Value, I.Value)> { + return zip(a, b, c, d, e, f, g, h) + .zip(with: i) + .map(repack) + } + + /// Zips the values of all the given properties, in the manner described by + /// `zip(with:)`. + public static func zip(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G, _ h: H, _ i: I, _ j: J) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value, H.Value, I.Value, J.Value)> { + return zip(a, b, c, d, e, f, g, h, i) + .zip(with: j) + .map(repack) + } + + /// Zips the values of all the given properties, in the manner described by + /// `zip(with:)`. Returns nil if the sequence is empty. + public static func zip(_ properties: S) -> Property<[S.Iterator.Element.Value]>? { + var generator = properties.makeIterator() + if let first = generator.next() { + let initial = first.map { [$0] } + return IteratorSequence(generator).reduce(initial) { property, next in + property.zip(with: next).map { $0.0 + [$0.1] } + } + } + + return nil + } +} + +/// A read-only property that can be observed for its changes over time. There are +/// three categories of read-only property: +/// +/// # Constant property +/// Created by `Property(value:)`, the producer and signal of a constant +/// property would complete immediately when it is initialized. +/// +/// # Existential property +/// Created by `Property(_:)`, an existential property passes through the +/// behavior of the wrapped property. +/// +/// # Composed property +/// Created by either the compositional operators in `PropertyProtocol`, or +/// `Property(initial:followingBy:)`, a composed property presents a +/// composed view of its source, which can be a set of properties, +/// a producer, or a signal. +/// +/// A composed property respects the lifetime of its source rather than its own. +/// In other words, its producer and signal can outlive the property itself, if +/// its source outlives it too. +public final class Property: PropertyProtocol { + private let sources: [AnyObject] + + private let _value: () -> Value + private let _producer: () -> SignalProducer + private let _signal: () -> Signal + + /// The current value of the property. + public var value: Value { + return _value() + } + + /// A producer for Signals that will send the property's current + /// value, followed by all changes over time, then complete when the + /// property has deinitialized or has no further changes. + public var producer: SignalProducer { + return _producer() + } + + /// A signal that will send the property's changes over time, then + /// complete when the property has deinitialized or has no further changes. + public var signal: Signal { + return _signal() + } + + /// Initializes a constant property. + /// + /// - parameters: + /// - property: A value of the constant property. + public init(value: Value) { + sources = [] + _value = { value } + _producer = { SignalProducer(value: value) } + _signal = { Signal.empty } + } + + /// Initializes an existential property which wraps the given property. + /// + /// - parameters: + /// - property: A property to be wrapped. + public init(_ property: P) { + sources = Property.capture(property) + _value = { property.value } + _producer = { property.producer } + _signal = { property.signal } + } + + /// Initializes a composed property that first takes on `initial`, then each + /// value sent on a signal created by `producer`. + /// + /// - parameters: + /// - initial: Starting value for the property. + /// - producer: A producer that will start immediately and send values to + /// the property. + public convenience init(initial: Value, then producer: SignalProducer) { + self.init(unsafeProducer: producer.prefix(value: initial), + capturing: []) + } + + /// Initialize a composed property that first takes on `initial`, then each + /// value sent on `signal`. + /// + /// - parameters: + /// - initialValue: Starting value for the property. + /// - signal: A signal that will send values to the property. + public convenience init(initial: Value, then signal: Signal) { + self.init(unsafeProducer: SignalProducer(signal: signal).prefix(value: initial), + capturing: []) + } + + /// Initialize a composed property by applying the unary `SignalProducer` + /// transform on `property`. + /// + /// - parameters: + /// - property: The source property. + /// - transform: A unary `SignalProducer` transform to be applied on + /// `property`. + private convenience init(_ property: P, transform: @noescape (SignalProducer) -> SignalProducer) { + self.init(unsafeProducer: transform(property.producer), + capturing: Property.capture(property)) + } + + /// Initialize a composed property by applying the binary `SignalProducer` + /// transform on `firstProperty` and `secondProperty`. + /// + /// - parameters: + /// - firstProperty: The first source property. + /// - secondProperty: The first source property. + /// - transform: A binary `SignalProducer` transform to be applied on + /// `firstProperty` and `secondProperty`. + private convenience init(_ firstProperty: P1, _ secondProperty: P2, transform: @noescape (SignalProducer) -> (SignalProducer) -> SignalProducer) { + self.init(unsafeProducer: transform(firstProperty.producer)(secondProperty.producer), + capturing: Property.capture(firstProperty) + Property.capture(secondProperty)) + } + + /// Initialize a composed property from a producer that promises to send + /// at least one value synchronously in its start handler before sending any + /// subsequent event. + /// + /// - important: The producer and the signal of the created property would + /// complete only when the `unsafeProducer` completes. + /// + /// - warning: If the producer fails its promise, a fatal error would be + /// raised. + /// + /// - parameters: + /// - unsafeProducer: The composed producer for creating the property. + /// - sources: The property sources to be captured. + private init(unsafeProducer: SignalProducer, capturing sources: [AnyObject]) { + // Share a replayed producer with `self.producer` and `self.signal` so + // they see a consistent view of the `self.value`. + // https://github.com/ReactiveCocoa/ReactiveCocoa/pull/3042 + let producer = unsafeProducer.replayLazily(upTo: 1) + + // Verify that an initial is sent. This is friendlier than deadlocking + // in the event that one isn't. + var value: Value? = nil + let disposable = producer.start { event in + switch event { + case let .next(newValue): + value = newValue + + case .completed, .interrupted: + break + + case let .failed(error): + fatalError("Receive unexpected error from a producer of `NoError` type: \(error)") + } + } + guard value != nil else { + fatalError("A producer promised to send at least one value. Received none.") + } + disposable.dispose() + + self.sources = sources + _value = { producer.take(first: 1).single()!.value! } + _producer = { producer } + _signal = { + var extractedSignal: Signal! + producer.startWithSignal { signal, _ in extractedSignal = signal } + return extractedSignal + } + } + + /// Inspect if `property` is an `AnyProperty` and has already captured its + /// sources using a closure. Returns that closure if it does. Otherwise, + /// returns a closure which captures `property`. + /// + /// - parameters: + /// - property: The property to be insepcted. + private static func capture(_ property: P) -> [AnyObject] { + if let property = property as? Property { + return property.sources + } else { + return [property] + } + } +} + +/// A mutable property of type `Value` that allows observation of its changes. +/// +/// Instances of this class are thread-safe. +public final class MutableProperty: MutablePropertyProtocol { + private let observer: Signal.Observer + + private let atomic: RecursiveAtomic + + /// The current value of the property. + /// + /// Setting this to a new value will notify all observers of `signal`, or + /// signals created using `producer`. + public var value: Value { + get { + return atomic.withValue { $0 } + } + + set { + swap(newValue) + } + } + + /// A signal that will send the property's changes over time, + /// then complete when the property has deinitialized. + public let signal: Signal + + /// A producer for Signals that will send the property's current value, + /// followed by all changes over time, then complete when the property has + /// deinitialized. + public var producer: SignalProducer { + return SignalProducer { [atomic, weak self] producerObserver, producerDisposable in + atomic.withValue { value in + if let strongSelf = self { + producerObserver.sendNext(value) + producerDisposable += strongSelf.signal.observe(producerObserver) + } else { + producerObserver.sendNext(value) + producerObserver.sendCompleted() + } + } + } + } + + /// Initializes a mutable property that first takes on `initialValue` + /// + /// - parameters: + /// - initialValue: Starting value for the mutable property. + public init(_ initialValue: Value) { + (signal, observer) = Signal.pipe() + + /// Need a recursive lock around `value` to allow recursive access to + /// `value`. Note that recursive sets will still deadlock because the + /// underlying producer prevents sending recursive events. + atomic = RecursiveAtomic(initialValue, + name: "org.reactivecocoa.ReactiveSwift.MutableProperty", + didSet: observer.sendNext) + } + + /// Atomically replaces the contents of the variable. + /// + /// - parameters: + /// - newValue: New property value. + /// + /// - returns: The previous property value. + @discardableResult + public func swap(_ newValue: Value) -> Value { + return atomic.swap(newValue) + } + + /// Atomically modifies the variable. + /// + /// - parameters: + /// - action: A closure that accepts old property value and returns a new + /// property value. + /// + /// - returns: The result of the action. + @discardableResult + public func modify(_ action: @noescape (inout Value) throws -> Result) rethrows -> Result { + return try atomic.modify(action) + } + + /// Atomically performs an arbitrary action using the current value of the + /// variable. + /// + /// - parameters: + /// - action: A closure that accepts current property value. + /// + /// - returns: the result of the action. + @discardableResult + public func withValue(action: @noescape (Value) throws -> Result) rethrows -> Result { + return try atomic.withValue(action) + } + + deinit { + observer.sendCompleted() + } +} + +private class Box { + var value: Value + + init(_ value: Value) { + self.value = value + } +} + +infix operator <~ { + associativity right + + // Binds tighter than assignment but looser than everything else + precedence 93 +} + +/// Binds a signal to a property, updating the property's value to the latest +/// value sent by the signal. +/// +/// - note: The binding will automatically terminate when the property is +/// deinitialized, or when the signal sends a `completed` event. +/// +/// ```` +/// let property = MutableProperty(0) +/// let signal = Signal({ /* do some work after some time */ }) +/// property <~ signal +/// ```` +/// +/// ```` +/// let property = MutableProperty(0) +/// let signal = Signal({ /* do some work after some time */ }) +/// let disposable = property <~ signal +/// ... +/// // Terminates binding before property dealloc or signal's +/// // `completed` event. +/// disposable.dispose() +/// ```` +/// +/// - parameters: +/// - property: A property to bind to. +/// - signal: A signal to bind. +/// +/// - returns: A disposable that can be used to terminate binding before the +/// deinitialization of property or signal's `completed` event. +@discardableResult +public func <~ (property: P, signal: Signal) -> Disposable { + let disposable = CompositeDisposable() + disposable += property.producer.startWithCompleted { + disposable.dispose() + } + + disposable += signal.observe { [weak property] event in + switch event { + case let .next(value): + property?.value = value + case .completed: + disposable.dispose() + case .failed, .interrupted: + break + } + } + + return disposable +} + +/// Creates a signal from the given producer, which will be immediately bound to +/// the given property, updating the property's value to the latest value sent +/// by the signal. +/// +/// ```` +/// let property = MutableProperty(0) +/// let producer = SignalProducer(value: 1) +/// property <~ producer +/// print(property.value) // prints `1` +/// ```` +/// +/// ```` +/// let property = MutableProperty(0) +/// let producer = SignalProducer({ /* do some work after some time */ }) +/// let disposable = (property <~ producer) +/// ... +/// // Terminates binding before property dealloc or +/// // signal's `completed` event. +/// disposable.dispose() +/// ```` +/// +/// - note: The binding will automatically terminate when the property is +/// deinitialized, or when the created producer sends a `completed` +/// event. +/// +/// - parameters: +/// - property: A property to bind to. +/// - producer: A producer to bind. +/// +/// - returns: A disposable that can be used to terminate binding before the +/// deinitialization of property or producer's `completed` event. +@discardableResult +public func <~ (property: P, producer: SignalProducer) -> Disposable { + let disposable = CompositeDisposable() + + producer + .on(completed: { disposable.dispose() }) + .startWithSignal { signal, signalDisposable in + disposable += property <~ signal + disposable += signalDisposable + + disposable += property.producer.startWithCompleted { + disposable.dispose() + } + } + + return disposable +} + +/// Binds a signal to a property, updating the property's value to the latest +/// value sent by the signal. +/// +/// - note: The binding will automatically terminate when the property is +/// deinitialized, or when the signal sends a `completed` event. +/// +/// ```` +/// let property = MutableProperty(0) +/// let signal = Signal({ /* do some work after some time */ }) +/// property <~ signal +/// ```` +/// +/// ```` +/// let property = MutableProperty(0) +/// let signal = Signal({ /* do some work after some time */ }) +/// let disposable = property <~ signal +/// ... +/// // Terminates binding before property dealloc or signal's +/// // `completed` event. +/// disposable.dispose() +/// ```` +/// +/// - parameters: +/// - property: A property to bind to. +/// - signal: A signal to bind. +/// +/// - returns: A disposable that can be used to terminate binding before the +/// deinitialization of property or signal's `completed` event. +@discardableResult +public func <~ (property: P, signal: S) -> Disposable { + return property <~ signal.optionalize() +} + +/// Creates a signal from the given producer, which will be immediately bound to +/// the given property, updating the property's value to the latest value sent +/// by the signal. +/// +/// ```` +/// let property = MutableProperty(0) +/// let producer = SignalProducer(value: 1) +/// property <~ producer +/// print(property.value) // prints `1` +/// ```` +/// +/// ```` +/// let property = MutableProperty(0) +/// let producer = SignalProducer({ /* do some work after some time */ }) +/// let disposable = (property <~ producer) +/// ... +/// // Terminates binding before property dealloc or +/// // signal's `completed` event. +/// disposable.dispose() +/// ```` +/// +/// - note: The binding will automatically terminate when the property is +/// deinitialized, or when the created producer sends a `completed` +/// event. +/// +/// - parameters: +/// - property: A property to bind to. +/// - producer: A producer to bind. +/// +/// - returns: A disposable that can be used to terminate binding before the +/// deinitialization of property or producer's `completed` event. +@discardableResult +public func <~ (property: P, producer: S) -> Disposable { + return property <~ producer.optionalize() +} + +/// Binds `destinationProperty` to the latest values of `sourceProperty`. +/// +/// ```` +/// let dstProperty = MutableProperty(0) +/// let srcProperty = ConstantProperty(10) +/// dstProperty <~ srcProperty +/// print(dstProperty.value) // prints 10 +/// ```` +/// +/// ```` +/// let dstProperty = MutableProperty(0) +/// let srcProperty = ConstantProperty(10) +/// let disposable = (dstProperty <~ srcProperty) +/// ... +/// disposable.dispose() // terminate the binding earlier if +/// // needed +/// ```` +/// +/// - note: The binding will automatically terminate when either property is +/// deinitialized. +/// +/// - parameters: +/// - destinationProperty: A property to bind to. +/// - sourceProperty: A property to bind. +/// +/// - returns: A disposable that can be used to terminate binding before the +/// deinitialization of destination property or source property +/// producer's `completed` event. +@discardableResult +public func <~ (destinationProperty: Destination, sourceProperty: Source) -> Disposable { + return destinationProperty <~ sourceProperty.producer +} + +/// Binds `destinationProperty` to the latest values of `sourceProperty`. +/// +/// ```` +/// let dstProperty = MutableProperty(0) +/// let srcProperty = ConstantProperty(10) +/// dstProperty <~ srcProperty +/// print(dstProperty.value) // prints 10 +/// ```` +/// +/// ```` +/// let dstProperty = MutableProperty(0) +/// let srcProperty = ConstantProperty(10) +/// let disposable = (dstProperty <~ srcProperty) +/// ... +/// disposable.dispose() // terminate the binding earlier if +/// // needed +/// ```` +/// +/// - note: The binding will automatically terminate when either property is +/// deinitialized. +/// +/// - parameters: +/// - destinationProperty: A property to bind to. +/// - sourceProperty: A property to bind. +/// +/// - returns: A disposable that can be used to terminate binding before the +/// deinitialization of destination property or source property +/// producer's `completed` event. +@discardableResult +public func <~ (destinationProperty: Destination, sourceProperty: Source) -> Disposable { + return destinationProperty <~ sourceProperty.producer +} diff --git a/ReactiveSwift/ReactiveSwift.h b/ReactiveSwift/ReactiveSwift.h new file mode 100644 index 0000000000..dde845ccc0 --- /dev/null +++ b/ReactiveSwift/ReactiveSwift.h @@ -0,0 +1,15 @@ +// +// ReactiveSwift.h +// ReactiveSwift +// +// Created by Matt Diephouse on 8/15/16. +// Copyright (c) 2016 the ReactiveSwift contributors. All rights reserved. +// + +#import + +//! Project version number for ReactiveSwift. +FOUNDATION_EXPORT double ReactiveSwiftVersionNumber; + +//! Project version string for ReactiveSwift. +FOUNDATION_EXPORT const unsigned char ReactiveSwiftVersionString[]; diff --git a/ReactiveSwift/Scheduler.swift b/ReactiveSwift/Scheduler.swift new file mode 100644 index 0000000000..daa3cb429c --- /dev/null +++ b/ReactiveSwift/Scheduler.swift @@ -0,0 +1,493 @@ +// +// Scheduler.swift +// ReactiveSwift +// +// Created by Justin Spahr-Summers on 2014-06-02. +// Copyright (c) 2014 GitHub. All rights reserved. +// + +import Foundation + +/// Represents a serial queue of work items. +public protocol SchedulerProtocol { + /// Enqueues an action on the scheduler. + /// + /// When the work is executed depends on the scheduler in use. + /// + /// - returns: Optional `Disposable` that can be used to cancel the work + /// before it begins. + @discardableResult + func schedule(_ action: () -> Void) -> Disposable? +} + +/// A particular kind of scheduler that supports enqueuing actions at future +/// dates. +public protocol DateSchedulerProtocol: SchedulerProtocol { + /// The current date, as determined by this scheduler. + /// + /// This can be implemented to deterministically return a known date (e.g., + /// for testing purposes). + var currentDate: Date { get } + + /// Schedules an action for execution at or after the given date. + /// + /// - parameters: + /// - date: Starting time. + /// - action: Closure of the action to perform. + /// + /// - returns: Optional `Disposable` that can be used to cancel the work + /// before it begins. + @discardableResult + func schedule(after date: Date, action: () -> Void) -> Disposable? + + /// Schedules a recurring action at the given interval, beginning at the + /// given date. + /// + /// - parameters: + /// - date: Starting time. + /// - repeatingEvery: Repetition interval. + /// - withLeeway: Some delta for repetition. + /// - action: Closure of the action to perform. + /// + /// - returns: Optional `Disposable` that can be used to cancel the work + /// before it begins. + @discardableResult + func schedule(after date: Date, interval: TimeInterval, leeway: TimeInterval, action: () -> Void) -> Disposable? +} + +/// A scheduler that performs all work synchronously. +public final class ImmediateScheduler: SchedulerProtocol { + public init() {} + + /// Immediately calls passed in `action`. + /// + /// - parameters: + /// - action: Closure of the action to perform. + /// + /// - returns: `nil`. + @discardableResult + public func schedule(_ action: () -> Void) -> Disposable? { + action() + return nil + } +} + +/// A scheduler that performs all work on the main queue, as soon as possible. +/// +/// If the caller is already running on the main queue when an action is +/// scheduled, it may be run synchronously. However, ordering between actions +/// will always be preserved. +public final class UIScheduler: SchedulerProtocol { + private static let dispatchSpecificKey = DispatchSpecificKey() + private static let dispatchSpecificValue = UInt8.max + private static var __once: () = { + DispatchQueue.main.setSpecific(key: UIScheduler.dispatchSpecificKey, + value: dispatchSpecificValue) + }() + + private var queueLength: Int32 = 0 + + /// Initializes `UIScheduler` + public init() { + /// This call is to ensure the main queue has been setup appropriately + /// for `UIScheduler`. It is only called once during the application + /// lifetime, since Swift has a `dispatch_once` like mechanism to + /// lazily initialize global variables and static variables. + _ = UIScheduler.__once + } + + /// Queues an action to be performed on main queue. If the action is called + /// on the main thread and no work is queued, no scheduling takes place and + /// the action is called instantly. + /// + /// - parameters: + /// - action: Closure of the action to perform on the main thread. + /// + /// - returns: `Disposable` that can be used to cancel the work before it + /// begins. + @discardableResult + public func schedule(_ action: () -> Void) -> Disposable? { + let disposable = SimpleDisposable() + let actionAndDecrement = { + if !disposable.isDisposed { + action() + } + + OSAtomicDecrement32(&self.queueLength) + } + + let queued = OSAtomicIncrement32(&queueLength) + + // If we're already running on the main queue, and there isn't work + // already enqueued, we can skip scheduling and just execute directly. + if queued == 1 && DispatchQueue.getSpecific(key: UIScheduler.dispatchSpecificKey) == UIScheduler.dispatchSpecificValue { + actionAndDecrement() + } else { + DispatchQueue.main.async(execute: actionAndDecrement) + } + + return disposable + } +} + +/// A scheduler backed by a serial GCD queue. +public final class QueueScheduler: DateSchedulerProtocol { + /// A singleton `QueueScheduler` that always targets the main thread's GCD + /// queue. + /// + /// - note: Unlike `UIScheduler`, this scheduler supports scheduling for a + /// future date, and will always schedule asynchronously (even if + /// already running on the main thread). + public static let main = QueueScheduler(internalQueue: DispatchQueue.main) + + public var currentDate: Date { + return Date() + } + + internal let queue: DispatchQueue + + internal init(internalQueue: DispatchQueue) { + queue = internalQueue + } + + /// Initializes a scheduler that will target the given queue with its + /// work. + /// + /// - note: Even if the queue is concurrent, all work items enqueued with + /// the `QueueScheduler` will be serial with respect to each other. + /// + /// - warning: Obsoleted in OS X 10.11 + @available(OSX, deprecated:10.10, obsoleted:10.11, message:"Use init(qos:, name:) instead") + @available(iOS, deprecated:8.0, obsoleted:9.0, message:"Use init(qos:, name:) instead.") + public convenience init(queue: DispatchQueue, name: String = "org.reactivecocoa.ReactiveSwift.QueueScheduler") { + self.init(internalQueue: DispatchQueue(label: name, attributes: [], target: queue)) + } + + /// Initializes a scheduler that will target a new serial queue with the + /// given quality of service class. + /// + /// - parameters: + /// - qos: Dispatch queue's QoS value. + /// - name: Name for the queue in the form of reverse domain. + @available(OSX 10.10, *) + public convenience init( + qos: DispatchQoS = .default, + name: String = "org.reactivecocoa.ReactiveSwift.QueueScheduler" + ) { + self.init(internalQueue: DispatchQueue( + label: name, + qos: qos + )) + } + + /// Schedules action for dispatch on internal queue + /// + /// - parameters: + /// - action: Closure of the action to schedule. + /// + /// - returns: `Disposable` that can be used to cancel the work before it + /// begins. + @discardableResult + public func schedule(_ action: () -> Void) -> Disposable? { + let d = SimpleDisposable() + + queue.async { + if !d.isDisposed { + action() + } + } + + return d + } + + private func wallTime(with date: Date) -> DispatchWallTime { + let (seconds, frac) = modf(date.timeIntervalSince1970) + + let nsec: Double = frac * Double(NSEC_PER_SEC) + let walltime = timespec(tv_sec: Int(seconds), tv_nsec: Int(nsec)) + + return DispatchWallTime(timespec: walltime) + } + + /// Schedules an action for execution at or after the given date. + /// + /// - parameters: + /// - date: Starting time. + /// - action: Closure of the action to perform. + /// + /// - returns: Optional `Disposable` that can be used to cancel the work + /// before it begins. + @discardableResult + public func schedule(after date: Date, action: () -> Void) -> Disposable? { + let d = SimpleDisposable() + + queue.asyncAfter(wallDeadline: wallTime(with: date)) { + if !d.isDisposed { + action() + } + } + + return d + } + + /// Schedules a recurring action at the given interval and beginning at the + /// given start time. A reasonable default timer interval leeway is + /// provided. + /// + /// - parameters: + /// - date: Date to schedule the first action for. + /// - repeatingEvery: Repetition interval. + /// - action: Closure of the action to repeat. + /// + /// - returns: Optional disposable that can be used to cancel the work + /// before it begins. + @discardableResult + public func schedule(after date: Date, interval: TimeInterval, action: () -> Void) -> Disposable? { + // Apple's "Power Efficiency Guide for Mac Apps" recommends a leeway of + // at least 10% of the timer interval. + return schedule(after: date, interval: interval, leeway: interval * 0.1, action: action) + } + + /// Schedules a recurring action at the given interval with provided leeway, + /// beginning at the given start time. + /// + /// - parameters: + /// - date: Date to schedule the first action for. + /// - repeatingEvery: Repetition interval. + /// - leeway: Some delta for repetition interval. + /// - action: Closure of the action to repeat. + /// + /// - returns: Optional `Disposable` that can be used to cancel the work + /// before it begins. + @discardableResult + public func schedule(after date: Date, interval: TimeInterval, leeway: TimeInterval, action: () -> Void) -> Disposable? { + precondition(interval >= 0) + precondition(leeway >= 0) + + let nsecInterval = interval * Double(NSEC_PER_SEC) + let nsecLeeway = leeway * Double(NSEC_PER_SEC) + + let timer = DispatchSource.makeTimerSource( + flags: DispatchSource.TimerFlags(rawValue: UInt(0)), + queue: queue + ) + timer.scheduleRepeating(wallDeadline: wallTime(with: date), + interval: .nanoseconds(Int(nsecInterval)), + leeway: .nanoseconds(Int(nsecLeeway))) + timer.setEventHandler(handler: action) + timer.resume() + + return ActionDisposable { + timer.cancel() + } + } +} + +/// A scheduler that implements virtualized time, for use in testing. +public final class TestScheduler: DateSchedulerProtocol { + private final class ScheduledAction { + let date: Date + let action: () -> Void + + init(date: Date, action: () -> Void) { + self.date = date + self.action = action + } + + func less(_ rhs: ScheduledAction) -> Bool { + return date.compare(rhs.date) == .orderedAscending + } + } + + private let lock = NSRecursiveLock() + private var _currentDate: Date + + /// The virtual date that the scheduler is currently at. + public var currentDate: Date { + let d: Date + + lock.lock() + d = _currentDate + lock.unlock() + + return d + } + + private var scheduledActions: [ScheduledAction] = [] + + /// Initializes a TestScheduler with the given start date. + /// + /// - parameters: + /// - startDate: The start date of the scheduler. + public init(startDate: Date = Date(timeIntervalSinceReferenceDate: 0)) { + lock.name = "org.reactivecocoa.ReactiveSwift.TestScheduler" + _currentDate = startDate + } + + private func schedule(_ action: ScheduledAction) -> Disposable { + lock.lock() + scheduledActions.append(action) + scheduledActions.sort { $0.less($1) } + lock.unlock() + + return ActionDisposable { + self.lock.lock() + self.scheduledActions = self.scheduledActions.filter { $0 !== action } + self.lock.unlock() + } + } + + /// Enqueues an action on the scheduler. + /// + /// - note: The work is executed on `currentDate` as it is understood by the + /// scheduler. + /// + /// - parameters: + /// - action: An action that will be performed on scheduler's + /// `currentDate`. + /// + /// - returns: Optional `Disposable` that can be used to cancel the work + /// before it begins. + @discardableResult + public func schedule(_ action: () -> Void) -> Disposable? { + return schedule(ScheduledAction(date: currentDate, action: action)) + } + + /// Schedules an action for execution at or after the given date. + /// + /// - parameters: + /// - date: Starting date. + /// - action: Closure of the action to perform. + /// + /// - returns: Optional disposable that can be used to cancel the work + /// before it begins. + @discardableResult + public func schedule(after delay: TimeInterval, action: () -> Void) -> Disposable? { + return schedule(after: currentDate.addingTimeInterval(delay), action: action) + } + + @discardableResult + public func schedule(after date: Date, action: () -> Void) -> Disposable? { + return schedule(ScheduledAction(date: date, action: action)) + } + + /// Schedules a recurring action at the given interval, beginning at the + /// given start time + /// + /// - parameters: + /// - date: Date to schedule the first action for. + /// - repeatingEvery: Repetition interval. + /// - action: Closure of the action to repeat. + /// + /// - returns: Optional `Disposable` that can be used to cancel the work + /// before it begins. + private func schedule(after date: Date, interval: TimeInterval, disposable: SerialDisposable, action: () -> Void) { + precondition(interval >= 0) + + disposable.innerDisposable = schedule(after: date) { [unowned self] in + action() + self.schedule(after: date.addingTimeInterval(interval), interval: interval, disposable: disposable, action: action) + } + } + + /// Schedules a recurring action at the given interval, beginning at the + /// given interval (counted from `currentDate`). + /// + /// - parameters: + /// - interval: Interval to add to `currentDate`. + /// - repeatingEvery: Repetition interval. + /// - leeway: Some delta for repetition interval. + /// - action: Closure of the action to repeat. + /// + /// - returns: Optional `Disposable` that can be used to cancel the work + /// before it begins. + @discardableResult + public func schedule(after delay: TimeInterval, interval: TimeInterval, leeway: TimeInterval = 0, action: () -> Void) -> Disposable? { + return schedule(after: currentDate.addingTimeInterval(delay), interval: interval, leeway: leeway, action: action) + } + + /// Schedules a recurring action at the given interval with + /// provided leeway, beginning at the given start time. + /// + /// - parameters: + /// - date: Date to schedule the first action for. + /// - repeatingEvery: Repetition interval. + /// - leeway: Some delta for repetition interval. + /// - action: Closure of the action to repeat. + /// + /// - returns: Optional `Disposable` that can be used to cancel the work + /// before it begins. + public func schedule(after date: Date, interval: TimeInterval, leeway: TimeInterval = 0, action: () -> Void) -> Disposable? { + let disposable = SerialDisposable() + schedule(after: date, interval: interval, disposable: disposable, action: action) + return disposable + } + + /// Advances the virtualized clock by an extremely tiny interval, dequeuing + /// and executing any actions along the way. + /// + /// This is intended to be used as a way to execute actions that have been + /// scheduled to run as soon as possible. + public func advance() { + advance(by: DBL_EPSILON) + } + + /// Advances the virtualized clock by the given interval, dequeuing and + /// executing any actions along the way. + /// + /// - parameters: + /// - interval: Interval by which the current date will be advanced. + public func advance(by interval: TimeInterval) { + lock.lock() + advance(to: currentDate.addingTimeInterval(interval)) + lock.unlock() + } + + /// Advances the virtualized clock to the given future date, dequeuing and + /// executing any actions up until that point. + /// + /// - parameters: + /// - newDate: Future date to which the virtual clock will be advanced. + public func advance(to newDate: Date) { + lock.lock() + + assert(currentDate.compare(newDate) != .orderedDescending) + + while scheduledActions.count > 0 { + if newDate.compare(scheduledActions[0].date) == .orderedAscending { + break + } + + _currentDate = scheduledActions[0].date + + let scheduledAction = scheduledActions.remove(at: 0) + scheduledAction.action() + } + + _currentDate = newDate + + lock.unlock() + } + + /// Dequeues and executes all scheduled actions, leaving the scheduler's + /// date at `NSDate.distantFuture()`. + public func run() { + advance(to: Date.distantFuture) + } + + /// Rewinds the virtualized clock by the given interval. + /// This simulates that user changes device date. + /// + /// - parameters: + /// - interval: Interval by which the current date will be retreated. + public func rewind(by interval: TimeInterval) { + lock.lock() + + let newDate = currentDate.addingTimeInterval(-interval) + assert(currentDate.compare(newDate) != .orderedAscending) + _currentDate = newDate + + lock.unlock() + + } +} diff --git a/ReactiveSwift/Signal.swift b/ReactiveSwift/Signal.swift new file mode 100644 index 0000000000..6fef3d0525 --- /dev/null +++ b/ReactiveSwift/Signal.swift @@ -0,0 +1,1838 @@ +import Foundation +import Result + +/// A push-driven stream that sends Events over time, parameterized by the type +/// of values being sent (`Value`) and the type of failure that can occur +/// (`Error`). If no failures should be possible, NoError can be specified for +/// `Error`. +/// +/// An observer of a Signal will see the exact same sequence of events as all +/// other observers. In other words, events will be sent to all observers at the +/// same time. +/// +/// Signals are generally used to represent event streams that are already “in +/// progress,” like notifications, user input, etc. To represent streams that +/// must first be _started_, see the SignalProducer type. +/// +/// A Signal is kept alive until either of the following happens: +/// 1. its input observer receives a terminating event; or +/// 2. it has no active observers, and is not being retained. +public final class Signal { + public typealias Observer = ReactiveSwift.Observer + + /// The disposable returned by the signal generator. It would be disposed of + /// when the signal terminates. + private var generatorDisposable: Disposable? + + /// The state of the signal. `nil` if the signal has terminated. + private let state: Atomic?> + + /// Initialize a Signal that will immediately invoke the given generator, + /// then forward events sent to the given observer. + /// + /// - note: The disposable returned from the closure will be automatically + /// disposed if a terminating event is sent to the observer. The + /// Signal itself will remain alive until the observer is released. + /// + /// - parameters: + /// - generator: A closure that accepts an implicitly created observer + /// that will act as an event emitter for the signal. + public init(_ generator: @noescape (Observer) -> Disposable?) { + state = Atomic(SignalState()) + + /// Used to ensure that events are serialized during delivery to observers. + let sendLock = NSLock() + sendLock.name = "org.reactivecocoa.ReactiveSwift.Signal" + + /// When set to `true`, the Signal should interrupt as soon as possible. + let interrupted = Atomic(false) + + let observer = Observer { [weak self] event in + guard let signal = self else { + return + } + + func interrupt() { + if let state = signal.state.swap(nil) { + for observer in state.observers { + observer.sendInterrupted() + } + } + } + + if case .interrupted = event { + // Normally we disallow recursive events, but `interrupted` is + // kind of a special snowflake, since it can inadvertently be + // sent by downstream consumers. + // + // So we'll flag Interrupted events specially, and if it + // happened to occur while we're sending something else, we'll + // wait to deliver it. + interrupted.value = true + + if sendLock.try() { + interrupt() + sendLock.unlock() + + signal.generatorDisposable?.dispose() + } + } else { + if let state = (event.isTerminating ? signal.state.swap(nil) : signal.state.value) { + sendLock.lock() + + for observer in state.observers { + observer.action(event) + } + + let shouldInterrupt = !event.isTerminating && interrupted.value + if shouldInterrupt { + interrupt() + } + + sendLock.unlock() + + if event.isTerminating || shouldInterrupt { + // Dispose only after notifying observers, so disposal + // logic is consistently the last thing to run. + signal.generatorDisposable?.dispose() + } + } + } + } + + generatorDisposable = generator(observer) + } + + deinit { + if state.swap(nil) != nil { + // As the signal can deinitialize only when it has no observers attached, + // only the generator disposable has to be disposed of at this point. + generatorDisposable?.dispose() + } + } + + /// A Signal that never sends any events to its observers. + public static var never: Signal { + return self.init { _ in nil } + } + + /// A Signal that completes immediately without emitting any value. + public static var empty: Signal { + return self.init { observer in + observer.sendCompleted() + return nil + } + } + + /// Create a Signal that will be controlled by sending events to the given + /// observer. + /// + /// - note: The Signal will remain alive until a terminating event is sent + /// to the observer. + /// + /// - returns: A tuple made of signal and observer. + public static func pipe() -> (Signal, Observer) { + var observer: Observer! + let signal = self.init { innerObserver in + observer = innerObserver + return nil + } + + return (signal, observer) + } + + /// Observe the Signal by sending any future events to the given observer. + /// + /// - note: If the Signal has already terminated, the observer will + /// immediately receive an `interrupted` event. + /// + /// - parameters: + /// - observer: An observer to forward the events to. + /// + /// - returns: An optional `Disposable` which can be used to disconnect the + /// observer. + @discardableResult + public func observe(_ observer: Observer) -> Disposable? { + var token: RemovalToken? + state.modify { + $0?.retainedSignal = self + token = $0?.observers.insert(observer) + } + + if let token = token { + return ActionDisposable { [weak self] in + if let strongSelf = self { + strongSelf.state.modify { state in + state?.observers.remove(using: token) + if state?.observers.isEmpty ?? false { + state!.retainedSignal = nil + } + } + } + } + } else { + observer.sendInterrupted() + return nil + } + } +} + +private struct SignalState { + var observers: Bag.Observer> = Bag() + var retainedSignal: Signal? +} + +public protocol SignalProtocol { + /// The type of values being sent on the signal. + associatedtype Value + + /// The type of error that can occur on the signal. If errors aren't + /// possible then `NoError` can be used. + associatedtype Error: Swift.Error + + /// Extracts a signal from the receiver. + var signal: Signal { get } + + /// Observes the Signal by sending any future events to the given observer. + @discardableResult + func observe(_ observer: Signal.Observer) -> Disposable? +} + +extension Signal: SignalProtocol { + public var signal: Signal { + return self + } +} + +extension SignalProtocol { + /// Convenience override for observe(_:) to allow trailing-closure style + /// invocations. + /// + /// - parameters: + /// - action: A closure that will accept an event of the signal + /// + /// - returns: An optional `Disposable` which can be used to stop the + /// invocation of the callback. Disposing of the Disposable will + /// have no effect on the Signal itself. + @discardableResult + public func observe(_ action: Signal.Observer.Action) -> Disposable? { + return observe(Observer(action)) + } + + /// Observe the `Signal` by invoking the given callback when `next` or + /// `failed` event are received. + /// + /// - parameters: + /// - result: A closure that accepts instance of `Result` + /// enum that contains either a `Success(Value)` or + /// `Failure` case. + /// + /// - returns: An optional `Disposable` which can be used to stop the + /// invocation of the callback. Disposing of the Disposable will + /// have no effect on the Signal itself. + @discardableResult + public func observeResult(_ result: (Result) -> Void) -> Disposable? { + return observe( + Observer( + next: { result(.success($0)) }, + failed: { result(.failure($0)) } + ) + ) + } + + /// Observe the `Signal` by invoking the given callback when a `completed` + /// event is received. + /// + /// - parameters: + /// - completed: A closure that is called when `completed` event is + /// received. + /// + /// - returns: An optional `Disposable` which can be used to stop the + /// invocation of the callback. Disposing of the Disposable will + /// have no effect on the Signal itself. + @discardableResult + public func observeCompleted(_ completed: () -> Void) -> Disposable? { + return observe(Observer(completed: completed)) + } + + /// Observe the `Signal` by invoking the given callback when a `failed` + /// event is received. + /// + /// - parameters: + /// - error: A closure that is called when failed event is received. It + /// accepts an error parameter. + /// + /// Returns a Disposable which can be used to stop the invocation of the + /// callback. Disposing of the Disposable will have no effect on the Signal + /// itself. + @discardableResult + public func observeFailed(_ error: (Error) -> Void) -> Disposable? { + return observe(Observer(failed: error)) + } + + /// Observe the `Signal` by invoking the given callback when an + /// `interrupted` event is received. If the Signal has already terminated, + /// the callback will be invoked immediately. + /// + /// - parameters: + /// - interrupted: A closure that is invoked when `interrupted` event is + /// received + /// + /// - returns: An optional `Disposable` which can be used to stop the + /// invocation of the callback. Disposing of the Disposable will + /// have no effect on the Signal itself. + @discardableResult + public func observeInterrupted(_ interrupted: () -> Void) -> Disposable? { + return observe(Observer(interrupted: interrupted)) + } +} + +extension SignalProtocol where Error == NoError { + /// Observe the Signal by invoking the given callback when `next` events are + /// received. + /// + /// - parameters: + /// - next: A closure that accepts a value when `next` event is received. + /// + /// - returns: An optional `Disposable` which can be used to stop the + /// invocation of the callback. Disposing of the Disposable will + /// have no effect on the Signal itself. + @discardableResult + public func observeNext(_ next: (Value) -> Void) -> Disposable? { + return observe(Observer(next: next)) + } +} + +extension SignalProtocol { + /// Map each value in the signal to a new value. + /// + /// - parameters: + /// - transform: A closure that accepts a value from the `next` event and + /// returns a new value. + /// + /// - returns: A signal that will send new values. + public func map(_ transform: (Value) -> U) -> Signal { + return Signal { observer in + return self.observe { event in + observer.action(event.map(transform)) + } + } + } + + /// Map errors in the signal to a new error. + /// + /// - parameters: + /// - transform: A closure that accepts current error object and returns + /// a new type of error object. + /// + /// - returns: A signal that will send new type of errors. + public func mapError(_ transform: (Error) -> F) -> Signal { + return Signal { observer in + return self.observe { event in + observer.action(event.mapError(transform)) + } + } + } + + /// Preserve only the values of the signal that pass the given predicate. + /// + /// - parameters: + /// - predicate: A closure that accepts value and returns `Bool` denoting + /// whether value has passed the test. + /// + /// - returns: A signal that will send only the values passing the given + /// predicate. + public func filter(_ predicate: (Value) -> Bool) -> Signal { + return Signal { observer in + return self.observe { (event: Event) -> Void in + guard let value = event.value else { + observer.action(event) + return + } + + if predicate(value) { + observer.sendNext(value) + } + } + } + } +} + +extension SignalProtocol where Value: OptionalProtocol { + /// Unwrap non-`nil` values and forward them on the returned signal, `nil` + /// values are dropped. + /// + /// - returns: A signal that sends only non-nil values. + public func skipNil() -> Signal { + return filter { $0.optional != nil }.map { $0.optional! } + } +} + +extension SignalProtocol { + /// Take up to `n` values from the signal and then complete. + /// + /// - precondition: `count` must be non-negative number. + /// + /// - parameters: + /// - count: A number of values to take from the signal. + /// + /// - returns: A signal that will yield the first `count` values from `self` + public func take(first count: Int) -> Signal { + precondition(count >= 0) + + return Signal { observer in + if count == 0 { + observer.sendCompleted() + return nil + } + + var taken = 0 + + return self.observe { event in + guard let value = event.value else { + observer.action(event) + return + } + + if taken < count { + taken += 1 + observer.sendNext(value) + } + + if taken == count { + observer.sendCompleted() + } + } + } + } +} + +/// A reference type which wraps an array to auxiliate the collection of values +/// for `collect` operator. +private final class CollectState { + var values: [Value] = [] + + /// Collects a new value. + func append(_ value: Value) { + values.append(value) + } + + /// Check if there are any items remaining. + /// + /// - note: This method also checks if there weren't collected any values + /// and, in that case, it means an empty array should be sent as the + /// result of collect. + var isEmpty: Bool { + /// We use capacity being zero to determine if we haven't collected any + /// value since we're keeping the capacity of the array to avoid + /// unnecessary and expensive allocations). This also guarantees + /// retro-compatibility around the original `collect()` operator. + return values.isEmpty && values.capacity > 0 + } + + /// Removes all values previously collected if any. + func flush() { + // Minor optimization to avoid consecutive allocations. Can + // be useful for sequences of regular or similar size and to + // track if any value was ever collected. + values.removeAll(keepingCapacity: true) + } +} + +extension SignalProtocol { + /// Collect all values sent by the signal then forward them as a single + /// array and complete. + /// + /// - note: When `self` completes without collecting any value, it will send + /// an empty array of values. + /// + /// - returns: A signal that will yield an array of values when `self` + /// completes. + public func collect() -> Signal<[Value], Error> { + return collect { _,_ in false } + } + + /// Collect at most `count` values from `self`, forward them as a single + /// array and complete. + /// + /// - note: When the count is reached the array is sent and the signal + /// starts over yielding a new array of values. + /// + /// - note: When `self` completes any remaining values will be sent, the + /// last array may not have `count` values. Alternatively, if were + /// not collected any values will sent an empty array of values. + /// + /// - precondition: `count` should be greater than zero. + /// + public func collect(count: Int) -> Signal<[Value], Error> { + precondition(count > 0) + return collect { values in values.count == count } + } + + /// Collect values that pass the given predicate then forward them as a + /// single array and complete. + /// + /// - note: When `self` completes any remaining values will be sent, the + /// last array may not match `predicate`. Alternatively, if were not + /// collected any values will sent an empty array of values. + /// + /// ```` + /// let (signal, observer) = Signal.pipe() + /// + /// signal + /// .collect { values in values.reduce(0, combine: +) == 8 } + /// .observeNext { print($0) } + /// + /// observer.sendNext(1) + /// observer.sendNext(3) + /// observer.sendNext(4) + /// observer.sendNext(7) + /// observer.sendNext(1) + /// observer.sendNext(5) + /// observer.sendNext(6) + /// observer.sendCompleted() + /// + /// // Output: + /// // [1, 3, 4] + /// // [7, 1] + /// // [5, 6] + /// ```` + /// + /// - parameters: + /// - predicate: Predicate to match when values should be sent (returning + /// `true`) or alternatively when they should be collected + /// (where it should return `false`). The most recent value + /// (`next`) is included in `values` and will be the end of + /// the current array of values if the predicate returns + /// `true`. + /// + /// - returns: A signal that collects values passing the predicate and, when + /// `self` completes, forwards them as a single array and + /// complets. + public func collect(_ predicate: (values: [Value]) -> Bool) -> Signal<[Value], Error> { + return Signal { observer in + let state = CollectState() + + return self.observe { event in + switch event { + case let .next(value): + state.append(value) + if predicate(values: state.values) { + observer.sendNext(state.values) + state.flush() + } + case .completed: + if !state.isEmpty { + observer.sendNext(state.values) + } + observer.sendCompleted() + case let .failed(error): + observer.sendFailed(error) + case .interrupted: + observer.sendInterrupted() + } + } + } + } + + /// Repeatedly collect an array of values up to a matching `next` value. + /// Then forward them as single array and wait for next events. + /// + /// - note: When `self` completes any remaining values will be sent, the + /// last array may not match `predicate`. Alternatively, if no + /// values were collected an empty array will be sent. + /// + /// ```` + /// let (signal, observer) = Signal.pipe() + /// + /// signal + /// .collect { values, next in next == 7 } + /// .observeNext { print($0) } + /// + /// observer.sendNext(1) + /// observer.sendNext(1) + /// observer.sendNext(7) + /// observer.sendNext(7) + /// observer.sendNext(5) + /// observer.sendNext(6) + /// observer.sendCompleted() + /// + /// // Output: + /// // [1, 1] + /// // [7] + /// // [7, 5, 6] + /// ```` + /// + /// - parameters: + /// - predicate: Predicate to match when values should be sent (returning + /// `true`) or alternatively when they should be collected + /// (where it should return `false`). The most recent value + /// (`next`) is not included in `values` and will be the + /// start of the next array of values if the predicate + /// returns `true`. + /// + /// - returns: A signal that will yield an array of values based on a + /// predicate which matches the values collected and the next + /// value. + public func collect(_ predicate: (values: [Value], next: Value) -> Bool) -> Signal<[Value], Error> { + return Signal { observer in + let state = CollectState() + + return self.observe { event in + switch event { + case let .next(value): + if predicate(values: state.values, next: value) { + observer.sendNext(state.values) + state.flush() + } + state.append(value) + case .completed: + if !state.isEmpty { + observer.sendNext(state.values) + } + observer.sendCompleted() + case let .failed(error): + observer.sendFailed(error) + case .interrupted: + observer.sendInterrupted() + } + } + } + } + + /// Forward all events onto the given scheduler, instead of whichever + /// scheduler they originally arrived upon. + /// + /// - parameters: + /// - scheduler: A scheduler to deliver events on. + /// + /// - returns: A signal that will yield `self` values on provided scheduler. + public func observe(on scheduler: SchedulerProtocol) -> Signal { + return Signal { observer in + return self.observe { event in + scheduler.schedule { + observer.action(event) + } + } + } + } +} + +private final class CombineLatestState { + var latestValue: Value? + var isCompleted = false +} + +extension SignalProtocol { + private func observeWithStates(_ signalState: CombineLatestState, _ otherState: CombineLatestState, _ lock: NSLock, _ observer: Signal<(), Error>.Observer) -> Disposable? { + return self.observe { event in + switch event { + case let .next(value): + lock.lock() + + signalState.latestValue = value + if otherState.latestValue != nil { + observer.sendNext() + } + + lock.unlock() + + case let .failed(error): + observer.sendFailed(error) + + case .completed: + lock.lock() + + signalState.isCompleted = true + if otherState.isCompleted { + observer.sendCompleted() + } + + lock.unlock() + + case .interrupted: + observer.sendInterrupted() + } + } + } + + /// Combine the latest value of the receiver with the latest value from the + /// given signal. + /// + /// - note: The returned signal will not send a value until both inputs have + /// sent at least one value each. + /// + /// - note: If either signal is interrupted, the returned signal will also + /// be interrupted. + /// + /// - parameters: + /// - otherSignal: A signal to combine `self`'s value with. + /// + /// - returns: A signal that will yield a tuple containing values of `self` + /// and given signal. + public func combineLatest(with other: Signal) -> Signal<(Value, U), Error> { + return Signal { observer in + let lock = NSLock() + lock.name = "org.reactivecocoa.ReactiveSwift.combineLatestWith" + + let signalState = CombineLatestState() + let otherState = CombineLatestState() + + let onBothNext = { + observer.sendNext((signalState.latestValue!, otherState.latestValue!)) + } + + let observer = Signal<(), Error>.Observer(next: onBothNext, failed: observer.sendFailed, completed: observer.sendCompleted, interrupted: observer.sendInterrupted) + + let disposable = CompositeDisposable() + disposable += self.observeWithStates(signalState, otherState, lock, observer) + disposable += other.observeWithStates(otherState, signalState, lock, observer) + + return disposable + } + } + + /// Delay `next` and `completed` events by the given interval, forwarding + /// them on the given scheduler. + /// + /// - note: failed and `interrupted` events are always scheduled + /// immediately. + /// + /// - parameters: + /// - interval: Interval to delay `next` and `completed` events by. + /// - scheduler: A scheduler to deliver delayed events on. + /// + /// - returns: A signal that will delay `next` and `completed` events and + /// will yield them on given scheduler. + public func delay(_ interval: TimeInterval, on scheduler: DateSchedulerProtocol) -> Signal { + precondition(interval >= 0) + + return Signal { observer in + return self.observe { event in + switch event { + case .failed, .interrupted: + scheduler.schedule { + observer.action(event) + } + + case .next, .completed: + let date = scheduler.currentDate.addingTimeInterval(interval) + scheduler.schedule(after: date) { + observer.action(event) + } + } + } + } + } + + /// Skip first `count` number of values then act as usual. + /// + /// - parameters: + /// - count: A number of values to skip. + /// + /// - returns: A signal that will skip the first `count` values, then + /// forward everything afterward. + public func skip(first count: Int) -> Signal { + precondition(count >= 0) + + if count == 0 { + return signal + } + + return Signal { observer in + var skipped = 0 + + return self.observe { event in + if case .next = event, skipped < count { + skipped += 1 + } else { + observer.action(event) + } + } + } + } + + /// Treat all Events from `self` as plain values, allowing them to be + /// manipulated just like any other value. + /// + /// In other words, this brings Events “into the monad”. + /// + /// - note: When a Completed or Failed event is received, the resulting + /// signal will send the Event itself and then complete. When an + /// Interrupted event is received, the resulting signal will send + /// the Event itself and then interrupt. + /// + /// - returns: A signal that sends events as its values. + public func materialize() -> Signal, NoError> { + return Signal { observer in + return self.observe { event in + observer.sendNext(event) + + switch event { + case .interrupted: + observer.sendInterrupted() + + case .completed, .failed: + observer.sendCompleted() + + case .next: + break + } + } + } + } +} + +extension SignalProtocol where Value: EventProtocol, Error == NoError { + /// Translate a signal of `Event` _values_ into a signal of those events + /// themselves. + /// + /// - returns: A signal that sends values carried by `self` events. + public func dematerialize() -> Signal { + return Signal { observer in + return self.observe { event in + switch event { + case let .next(innerEvent): + observer.action(innerEvent.event) + + case .failed: + fatalError("NoError is impossible to construct") + + case .completed: + observer.sendCompleted() + + case .interrupted: + observer.sendInterrupted() + } + } + } + } +} + +extension SignalProtocol { + /// Inject side effects to be performed upon the specified signal events. + /// + /// - parameters: + /// - event: A closure that accepts an event and is invoked on every + /// received event. + /// - next: A closure that accepts a value from `next` event. + /// - failed: A closure that accepts error object and is invoked for + /// failed event. + /// - completed: A closure that is invoked for `completed` event. + /// - interrupted: A closure that is invoked for `interrupted` event. + /// - terminated: A closure that is invoked for any terminating event. + /// - disposed: A closure added as disposable when signal completes. + /// + /// - returns: A signal with attached side-effects for given event cases. + public func on( + event: ((Event) -> Void)? = nil, + failed: ((Error) -> Void)? = nil, + completed: (() -> Void)? = nil, + interrupted: (() -> Void)? = nil, + terminated: (() -> Void)? = nil, + disposed: (() -> Void)? = nil, + next: ((Value) -> Void)? = nil + ) -> Signal { + return Signal { observer in + let disposable = CompositeDisposable() + + _ = disposed.map(disposable.add) + + disposable += signal.observe { receivedEvent in + event?(receivedEvent) + + switch receivedEvent { + case let .next(value): + next?(value) + + case let .failed(error): + failed?(error) + + case .completed: + completed?() + + case .interrupted: + interrupted?() + } + + if receivedEvent.isTerminating { + terminated?() + } + + observer.action(receivedEvent) + } + + return disposable + } + } +} + +private struct SampleState { + var latestValue: Value? = nil + var isSignalCompleted: Bool = false + var isSamplerCompleted: Bool = false +} + +extension SignalProtocol { + /// Forward the latest value from `self` with the value from `sampler` as a + /// tuple, only when`sampler` sends a `next` event. + /// + /// - note: If `sampler` fires before a value has been observed on `self`, + /// nothing happens. + /// + /// - parameters: + /// - sampler: A signal that will trigger the delivery of `next` event + /// from `self`. + /// + /// - returns: A signal that will send values from `self` and `sampler`, + /// sampled (possibly multiple times) by `sampler`, then complete + /// once both input signals have completed, or interrupt if + /// either input signal is interrupted. + public func sample(with sampler: Signal) -> Signal<(Value, T), Error> { + return Signal { observer in + let state = Atomic(SampleState()) + let disposable = CompositeDisposable() + + disposable += self.observe { event in + switch event { + case let .next(value): + state.modify { + $0.latestValue = value + } + + case let .failed(error): + observer.sendFailed(error) + + case .completed: + let shouldComplete: Bool = state.modify { + $0.isSignalCompleted = true + return $0.isSamplerCompleted + } + + if shouldComplete { + observer.sendCompleted() + } + + case .interrupted: + observer.sendInterrupted() + } + } + + disposable += sampler.observe { event in + switch event { + case .next(let samplerValue): + if let value = state.value.latestValue { + observer.sendNext((value, samplerValue)) + } + + case .completed: + let shouldComplete: Bool = state.modify { + $0.isSamplerCompleted = true + return $0.isSignalCompleted + } + + if shouldComplete { + observer.sendCompleted() + } + + case .interrupted: + observer.sendInterrupted() + + case .failed: + break + } + } + + return disposable + } + } + + /// Forward the latest value from `self` whenever `sampler` sends a `next` + /// event. + /// + /// - note: If `sampler` fires before a value has been observed on `self`, + /// nothing happens. + /// + /// - parameters: + /// - sampler: A signal that will trigger the delivery of `next` event + /// from `self`. + /// + /// - returns: A signal that will send values from `self`, sampled (possibly + /// multiple times) by `sampler`, then complete once both input + /// signals have completed, or interrupt if either input signal + /// is interrupted. + public func sample(on sampler: Signal<(), NoError>) -> Signal { + return sample(with: sampler) + .map { $0.0 } + } + + /// Forwards events from `self` until `lifetime` ends, at which point the + /// returned signal will complete. + /// + /// - parameters: + /// - lifetime: A lifetime whose `ended` signal will cause the returned + /// signal to complete. + /// + /// - returns: A signal that will deliver events until `lifetime` ends. + public func take(during lifetime: Lifetime) -> Signal { + return take(until: lifetime.ended) + } + + /// Forward events from `self` until `trigger` sends a `next` or + /// `completed` event, at which point the returned signal will complete. + /// + /// - parameters: + /// - trigger: A signal whose `next` or `completed` events will stop the + /// delivery of `next` events from `self`. + /// + /// - returns: A signal that will deliver events until `trigger` sends + /// `next` or `completed` events. + public func take(until trigger: Signal<(), NoError>) -> Signal { + return Signal { observer in + let disposable = CompositeDisposable() + disposable += self.observe(observer) + + disposable += trigger.observe { event in + switch event { + case .next, .completed: + observer.sendCompleted() + + case .failed, .interrupted: + break + } + } + + return disposable + } + } + + /// Do not forward any values from `self` until `trigger` sends a `next` or + /// `completed` event, at which point the returned signal behaves exactly + /// like `signal`. + /// + /// - parameters: + /// - trigger: A signal whose `next` or `completed` events will start the + /// deliver of events on `self`. + /// + /// - returns: A signal that will deliver events once the `trigger` sends + /// `next` or `completed` events. + public func skip(until trigger: Signal<(), NoError>) -> Signal { + return Signal { observer in + let disposable = SerialDisposable() + + disposable.innerDisposable = trigger.observe { event in + switch event { + case .next, .completed: + disposable.innerDisposable = self.observe(observer) + + case .failed, .interrupted: + break + } + } + + return disposable + } + } + + /// Forward events from `self` with history: values of the returned signal + /// are a tuples whose first member is the previous value and whose second member + /// is the current value. `initial` is supplied as the first member when `self` + /// sends its first value. + /// + /// - parameters: + /// - initial: A value that will be combined with the first value sent by + /// `self`. + /// + /// - returns: A signal that sends tuples that contain previous and current + /// sent values of `self`. + public func combinePrevious(_ initial: Value) -> Signal<(Value, Value), Error> { + return scan((initial, initial)) { previousCombinedValues, newValue in + return (previousCombinedValues.1, newValue) + } + } + + + /// Send only the final value and then immediately completes. + /// + /// - parameters: + /// - initial: Initial value for the accumulator. + /// - combine: A closure that accepts accumulator and sent value of + /// `self`. + /// + /// - returns: A signal that sends accumulated value after `self` completes. + public func reduce(_ initial: U, _ combine: (U, Value) -> U) -> Signal { + // We need to handle the special case in which `signal` sends no values. + // We'll do that by sending `initial` on the output signal (before + // taking the last value). + let (scannedSignalWithInitialValue, outputSignalObserver) = Signal.pipe() + let outputSignal = scannedSignalWithInitialValue.take(last: 1) + + // Now that we've got takeLast() listening to the piped signal, send + // that initial value. + outputSignalObserver.sendNext(initial) + + // Pipe the scanned input signal into the output signal. + scan(initial, combine).observe(outputSignalObserver) + + return outputSignal + } + + /// Aggregate values into a single combined value. When `self` emits its + /// first value, `combine` is invoked with `initial` as the first argument + /// and that emitted value as the second argument. The result is emitted + /// from the signal returned from `scan`. That result is then passed to + /// `combine` as the first argument when the next value is emitted, and so + /// on. + /// + /// - parameters: + /// - initial: Initial value for the accumulator. + /// - combine: A closure that accepts accumulator and sent value of + /// `self`. + /// + /// - returns: A signal that sends accumulated value each time `self` emits + /// own value. + public func scan(_ initial: U, _ combine: (U, Value) -> U) -> Signal { + return Signal { observer in + var accumulator = initial + + return self.observe { event in + observer.action(event.map { value in + accumulator = combine(accumulator, value) + return accumulator + }) + } + } + } +} + +extension SignalProtocol where Value: Equatable { + /// Forward only those values from `self` which are not duplicates of the + /// immedately preceding value. + /// + /// - note: The first value is always forwarded. + /// + /// - returns: A signal that does not send two equal values sequentially. + public func skipRepeats() -> Signal { + return skipRepeats(==) + } +} + +extension SignalProtocol { + /// Forward only those values from `self` which do not pass `isRepeat` with + /// respect to the previous value. + /// + /// - note: The first value is always forwarded. + /// + /// - parameters: + /// - isRepeate: A closure that accepts previous and current values of + /// `self` and returns `Bool` whether these values are + /// repeating. + /// + /// - returns: A signal that forwards only those values that fail given + /// `isRepeat` predicate. + public func skipRepeats(_ isRepeat: (Value, Value) -> Bool) -> Signal { + return self + .scan((nil, false)) { (accumulated: (Value?, Bool), next: Value) -> (value: Value?, repeated: Bool) in + switch accumulated.0 { + case nil: + return (next, false) + case let prev? where isRepeat(prev, next): + return (prev, true) + case _?: + return (Optional(next), false) + } + } + .filter { !$0.repeated } + .map { $0.value } + .skipNil() + } + + /// Do not forward any values from `self` until `predicate` returns false, + /// at which point the returned signal behaves exactly like `signal`. + /// + /// - parameters: + /// - predicate: A closure that accepts a value and returns whether `self` + /// should still not forward that value to a `signal`. + /// + /// - returns: A signal that sends only forwarded values from `self`. + public func skip(while predicate: (Value) -> Bool) -> Signal { + return Signal { observer in + var shouldSkip = true + + return self.observe { event in + switch event { + case let .next(value): + shouldSkip = shouldSkip && predicate(value) + if !shouldSkip { + fallthrough + } + + case .failed, .completed, .interrupted: + observer.action(event) + } + } + } + } + + /// Forward events from `self` until `replacement` begins sending events. + /// + /// - parameters: + /// - replacement: A signal to wait to wait for values from and start + /// sending them as a replacement to `self`'s values. + /// + /// - returns: A signal which passes through `next`, failed, and + /// `interrupted` events from `self` until `replacement` sends + /// an event, at which point the returned signal will send that + /// event and switch to passing through events from `replacement` + /// instead, regardless of whether `self` has sent events + /// already. + public func take(untilReplacement signal: Signal) -> Signal { + return Signal { observer in + let disposable = CompositeDisposable() + + let signalDisposable = self.observe { event in + switch event { + case .completed: + break + + case .next, .failed, .interrupted: + observer.action(event) + } + } + + disposable += signalDisposable + disposable += signal.observe { event in + signalDisposable?.dispose() + observer.action(event) + } + + return disposable + } + } + + /// Wait until `self` completes and then forward the final `count` values + /// on the returned signal. + /// + /// - parameters: + /// - count: Number of last events to send after `self` completes. + /// + /// - returns: A signal that receives up to `count` values from `self` + /// after `self` completes. + public func take(last count: Int) -> Signal { + return Signal { observer in + var buffer: [Value] = [] + buffer.reserveCapacity(count) + + return self.observe { event in + switch event { + case let .next(value): + // To avoid exceeding the reserved capacity of the buffer, + // we remove then add. Remove elements until we have room to + // add one more. + while (buffer.count + 1) > count { + buffer.remove(at: 0) + } + + buffer.append(value) + case let .failed(error): + observer.sendFailed(error) + case .completed: + buffer.forEach(observer.sendNext) + + observer.sendCompleted() + case .interrupted: + observer.sendInterrupted() + } + } + } + } + + /// Forward any values from `self` until `predicate` returns false, at which + /// point the returned signal will complete. + /// + /// - parameters: + /// - predicate: A closure that accepts value and returns `Bool` value + /// whether `self` should forward it to `signal` and continue + /// sending other events. + /// + /// - returns: A signal that sends events until the values sent by `self` + /// pass the given `predicate`. + public func take(while predicate: (Value) -> Bool) -> Signal { + return Signal { observer in + return self.observe { event in + if let value = event.value, !predicate(value) { + observer.sendCompleted() + } else { + observer.action(event) + } + } + } + } +} + +private struct ZipState { + var values: (left: [Left], right: [Right]) = ([], []) + var isCompleted: (left: Bool, right: Bool) = (false, false) + + var isFinished: Bool { + return (isCompleted.left && values.left.isEmpty) || (isCompleted.right && values.right.isEmpty) + } +} + +extension SignalProtocol { + /// Zip elements of two signals into pairs. The elements of any Nth pair + /// are the Nth elements of the two input signals. + /// + /// - parameters: + /// - otherSignal: A signal to zip values with. + /// + /// - returns: A signal that sends tuples of `self` and `otherSignal`. + public func zip(with other: Signal) -> Signal<(Value, U), Error> { + return Signal { observer in + let state = Atomic(ZipState()) + let disposable = CompositeDisposable() + + let flush = { + var tuple: (Value, U)? + var isFinished = false + + state.modify { state in + guard !state.values.left.isEmpty && !state.values.right.isEmpty else { + isFinished = state.isFinished + return + } + + tuple = (state.values.left.removeFirst(), state.values.right.removeFirst()) + isFinished = state.isFinished + } + + if let tuple = tuple { + observer.sendNext(tuple) + } + + if isFinished { + observer.sendCompleted() + } + } + + let onFailed = observer.sendFailed + let onInterrupted = observer.sendInterrupted + + disposable += self.observe { event in + switch event { + case let .next(value): + state.modify { + $0.values.left.append(value) + } + flush() + + case let .failed(error): + onFailed(error) + + case .completed: + state.modify { + $0.isCompleted.left = true + } + flush() + + case .interrupted: + onInterrupted() + } + } + + disposable += other.observe { event in + switch event { + case let .next(value): + state.modify { + $0.values.right.append(value) + } + flush() + + case let .failed(error): + onFailed(error) + + case .completed: + state.modify { + $0.isCompleted.right = true + } + flush() + + case .interrupted: + onInterrupted() + } + } + + return disposable + } + } + + /// Apply `operation` to values from `self` with `Success`ful results + /// forwarded on the returned signal and `Failure`s sent as failed events. + /// + /// - parameters: + /// - operation: A closure that accepts a value and returns a `Result`. + /// + /// - returns: A signal that receives `Success`ful `Result` as `next` event + /// and `Failure` as failed event. + public func attempt(_ operation: (Value) -> Result<(), Error>) -> Signal { + return attemptMap { value in + return operation(value).map { + return value + } + } + } + + /// Apply `operation` to values from `self` with `Success`ful results mapped + /// on the returned signal and `Failure`s sent as failed events. + /// + /// - parameters: + /// - operation: A closure that accepts a value and returns a result of + /// a mapped value as `Success`. + /// + /// - returns: A signal that sends mapped values from `self` if returned + /// `Result` is `Success`ful, failed events otherwise. + public func attemptMap(_ operation: (Value) -> Result) -> Signal { + return Signal { observer in + self.observe { event in + switch event { + case let .next(value): + operation(value).analysis( + ifSuccess: observer.sendNext, + ifFailure: observer.sendFailed + ) + case let .failed(error): + observer.sendFailed(error) + case .completed: + observer.sendCompleted() + case .interrupted: + observer.sendInterrupted() + } + } + } + } + + /// Throttle values sent by the receiver, so that at least `interval` + /// seconds pass between each, then forwards them on the given scheduler. + /// + /// - note: If multiple values are received before the interval has elapsed, + /// the latest value is the one that will be passed on. + /// + /// - note: If the input signal terminates while a value is being throttled, + /// that value will be discarded and the returned signal will + /// terminate immediately. + /// + /// - note: If the device time changed backwords before previous date while + /// a value is being throttled, and if there is a new value sent, + /// the new value will be passed anyway. + /// + /// - parameters: + /// - interval: Number of seconds to wait between sent values. + /// - scheduler: A scheduler to deliver events on. + /// + /// - returns: A signal that sends values at least `interval` seconds + /// appart on a given scheduler. + public func throttle(_ interval: TimeInterval, on scheduler: DateSchedulerProtocol) -> Signal { + precondition(interval >= 0) + + return Signal { observer in + let state: Atomic> = Atomic(ThrottleState()) + let schedulerDisposable = SerialDisposable() + + let disposable = CompositeDisposable() + disposable += schedulerDisposable + + disposable += self.observe { event in + guard let value = event.value else { + schedulerDisposable.innerDisposable = scheduler.schedule { + observer.action(event) + } + return + } + + var scheduleDate: Date! + state.modify { + $0.pendingValue = value + + let proposedScheduleDate: Date + if let previousDate = $0.previousDate, previousDate.compare(scheduler.currentDate) != .orderedDescending { + proposedScheduleDate = previousDate.addingTimeInterval(interval) + } else { + proposedScheduleDate = scheduler.currentDate + } + + switch proposedScheduleDate.compare(scheduler.currentDate) { + case .orderedAscending: + scheduleDate = scheduler.currentDate + + case .orderedSame: fallthrough + case .orderedDescending: + scheduleDate = proposedScheduleDate + } + } + + schedulerDisposable.innerDisposable = scheduler.schedule(after: scheduleDate) { + let pendingValue: Value? = state.modify { state in + defer { + if state.pendingValue != nil { + state.pendingValue = nil + state.previousDate = scheduleDate + } + } + return state.pendingValue + } + + if let pendingValue = pendingValue { + observer.sendNext(pendingValue) + } + } + } + + return disposable + } + } + + /// Debounce values sent by the receiver, such that at least `interval` + /// seconds pass after the receiver has last sent a value, then forward the + /// latest value on the given scheduler. + /// + /// - note: If multiple values are received before the interval has elapsed, + /// the latest value is the one that will be passed on. + /// + /// - note: If the input signal terminates while a value is being debounced, + /// that value will be discarded and the returned signal will + /// terminate immediately. + /// + /// - parameters: + /// - interval: A number of seconds to wait before sending a value. + /// - scheduler: A scheduler to send values on. + /// + /// - returns: A signal that sends values that are sent from `self` at least + /// `interval` seconds apart. + public func debounce(_ interval: TimeInterval, on scheduler: DateSchedulerProtocol) -> Signal { + precondition(interval >= 0) + + return self + .materialize() + .flatMap(.latest) { event -> SignalProducer, NoError> in + if event.isTerminating { + return SignalProducer(value: event).observe(on: scheduler) + } else { + return SignalProducer(value: event).delay(interval, on: scheduler) + } + } + .dematerialize() + } +} + +extension SignalProtocol { + /// Forward only those values from `self` that have unique identities across + /// the set of all values that have been seen. + /// + /// - note: This causes the identities to be retained to check for + /// uniqueness. + /// + /// - parameters: + /// - transform: A closure that accepts a value and returns identity + /// value. + /// + /// - returns: A signal that sends unique values during its lifetime. + public func uniqueValues(_ transform: (Value) -> Identity) -> Signal { + return Signal { observer in + var seenValues: Set = [] + + return self + .observe { event in + switch event { + case let .next(value): + let identity = transform(value) + if !seenValues.contains(identity) { + seenValues.insert(identity) + fallthrough + } + + case .failed, .completed, .interrupted: + observer.action(event) + } + } + } + } +} + +extension SignalProtocol where Value: Hashable { + /// Forward only those values from `self` that are unique across the set of + /// all values that have been seen. + /// + /// - note: This causes the values to be retained to check for uniqueness. + /// Providing a function that returns a unique value for each sent + /// value can help you reduce the memory footprint. + /// + /// - returns: A signal that sends unique values during its lifetime. + public func uniqueValues() -> Signal { + return uniqueValues { $0 } + } +} + +private struct ThrottleState { + var previousDate: Date? = nil + var pendingValue: Value? = nil +} + +extension SignalProtocol { + /// Combines the values of all the given signals, in the manner described by + /// `combineLatestWith`. + public static func combineLatest(_ a: Signal, _ b: Signal) -> Signal<(Value, B), Error> { + return a.combineLatest(with: b) + } + + /// Combines the values of all the given signals, in the manner described by + /// `combineLatestWith`. + public static func combineLatest(_ a: Signal, _ b: Signal, _ c: Signal) -> Signal<(Value, B, C), Error> { + return combineLatest(a, b) + .combineLatest(with: c) + .map(repack) + } + + /// Combines the values of all the given signals, in the manner described by + /// `combineLatestWith`. + public static func combineLatest(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal) -> Signal<(Value, B, C, D), Error> { + return combineLatest(a, b, c) + .combineLatest(with: d) + .map(repack) + } + + /// Combines the values of all the given signals, in the manner described by + /// `combineLatestWith`. + public static func combineLatest(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal) -> Signal<(Value, B, C, D, E), Error> { + return combineLatest(a, b, c, d) + .combineLatest(with: e) + .map(repack) + } + + /// Combines the values of all the given signals, in the manner described by + /// `combineLatestWith`. + public static func combineLatest(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal) -> Signal<(Value, B, C, D, E, F), Error> { + return combineLatest(a, b, c, d, e) + .combineLatest(with: f) + .map(repack) + } + + /// Combines the values of all the given signals, in the manner described by + /// `combineLatestWith`. + public static func combineLatest(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal, _ g: Signal) -> Signal<(Value, B, C, D, E, F, G), Error> { + return combineLatest(a, b, c, d, e, f) + .combineLatest(with: g) + .map(repack) + } + + /// Combines the values of all the given signals, in the manner described by + /// `combineLatestWith`. + public static func combineLatest(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal, _ g: Signal, _ h: Signal) -> Signal<(Value, B, C, D, E, F, G, H), Error> { + return combineLatest(a, b, c, d, e, f, g) + .combineLatest(with: h) + .map(repack) + } + + /// Combines the values of all the given signals, in the manner described by + /// `combineLatestWith`. + public static func combineLatest(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal, _ g: Signal, _ h: Signal, _ i: Signal) -> Signal<(Value, B, C, D, E, F, G, H, I), Error> { + return combineLatest(a, b, c, d, e, f, g, h) + .combineLatest(with: i) + .map(repack) + } + + /// Combines the values of all the given signals, in the manner described by + /// `combineLatestWith`. + public static func combineLatest(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal, _ g: Signal, _ h: Signal, _ i: Signal, _ j: Signal) -> Signal<(Value, B, C, D, E, F, G, H, I, J), Error> { + return combineLatest(a, b, c, d, e, f, g, h, i) + .combineLatest(with: j) + .map(repack) + } + + /// Combines the values of all the given signals, in the manner described by + /// `combineLatestWith`. No events will be sent if the sequence is empty. + public static func combineLatest>(_ signals: S) -> Signal<[Value], Error> { + var generator = signals.makeIterator() + if let first = generator.next() { + let initial = first.map { [$0] } + return IteratorSequence(generator).reduce(initial) { signal, next in + signal.combineLatest(with: next).map { $0.0 + [$0.1] } + } + } + + return .never + } + + /// Zips the values of all the given signals, in the manner described by + /// `zipWith`. + public static func zip(_ a: Signal, _ b: Signal) -> Signal<(Value, B), Error> { + return a.zip(with: b) + } + + /// Zips the values of all the given signals, in the manner described by + /// `zipWith`. + public static func zip(_ a: Signal, _ b: Signal, _ c: Signal) -> Signal<(Value, B, C), Error> { + return zip(a, b) + .zip(with: c) + .map(repack) + } + + /// Zips the values of all the given signals, in the manner described by + /// `zipWith`. + public static func zip(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal) -> Signal<(Value, B, C, D), Error> { + return zip(a, b, c) + .zip(with: d) + .map(repack) + } + + /// Zips the values of all the given signals, in the manner described by + /// `zipWith`. + public static func zip(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal) -> Signal<(Value, B, C, D, E), Error> { + return zip(a, b, c, d) + .zip(with: e) + .map(repack) + } + + /// Zips the values of all the given signals, in the manner described by + /// `zipWith`. + public static func zip(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal) -> Signal<(Value, B, C, D, E, F), Error> { + return zip(a, b, c, d, e) + .zip(with: f) + .map(repack) + } + + /// Zips the values of all the given signals, in the manner described by + /// `zipWith`. + public static func zip(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal, _ g: Signal) -> Signal<(Value, B, C, D, E, F, G), Error> { + return zip(a, b, c, d, e, f) + .zip(with: g) + .map(repack) + } + + /// Zips the values of all the given signals, in the manner described by + /// `zipWith`. + public static func zip(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal, _ g: Signal, _ h: Signal) -> Signal<(Value, B, C, D, E, F, G, H), Error> { + return zip(a, b, c, d, e, f, g) + .zip(with: h) + .map(repack) + } + + /// Zips the values of all the given signals, in the manner described by + /// `zipWith`. + public static func zip(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal, _ g: Signal, _ h: Signal, _ i: Signal) -> Signal<(Value, B, C, D, E, F, G, H, I), Error> { + return zip(a, b, c, d, e, f, g, h) + .zip(with: i) + .map(repack) + } + + /// Zips the values of all the given signals, in the manner described by + /// `zipWith`. + public static func zip(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal, _ g: Signal, _ h: Signal, _ i: Signal, _ j: Signal) -> Signal<(Value, B, C, D, E, F, G, H, I, J), Error> { + return zip(a, b, c, d, e, f, g, h, i) + .zip(with: j) + .map(repack) + } + + /// Zips the values of all the given signals, in the manner described by + /// `zipWith`. No events will be sent if the sequence is empty. + public static func zip>(_ signals: S) -> Signal<[Value], Error> { + var generator = signals.makeIterator() + if let first = generator.next() { + let initial = first.map { [$0] } + return IteratorSequence(generator).reduce(initial) { signal, next in + signal.zip(with: next).map { $0.0 + [$0.1] } + } + } + + return .never + } +} + +extension SignalProtocol { + /// Forward events from `self` until `interval`. Then if signal isn't + /// completed yet, fails with `error` on `scheduler`. + /// + /// - note: 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. + /// + /// - parameters: + /// - error: Error to send with failed event if `self` is not completed + /// when `interval` passes. + /// - interval: Number of seconds to wait for `self` to complete. + /// - scheudler: A scheduler to deliver error on. + /// + /// - returns: A signal that sends events for at most `interval` seconds, + /// then, if not `completed` - sends `error` with failed event + /// on `scheduler`. + public func timeout(after interval: TimeInterval, raising error: Error, on scheduler: DateSchedulerProtocol) -> Signal { + precondition(interval >= 0) + + return Signal { observer in + let disposable = CompositeDisposable() + let date = scheduler.currentDate.addingTimeInterval(interval) + + disposable += scheduler.schedule(after: date) { + observer.sendFailed(error) + } + + disposable += self.observe(observer) + return disposable + } + } +} + +extension SignalProtocol where Error == NoError { + /// Promote a signal that does not generate failures into one that can. + /// + /// - note: This does not actually cause failures to be generated for the + /// given signal, but makes it easier to combine with other signals + /// that may fail; for example, with operators like + /// `combineLatestWith`, `zipWith`, `flatten`, etc. + /// + /// - parameters: + /// - _ An `ErrorType`. + /// + /// - returns: A signal that has an instantiatable `ErrorType`. + public func promoteErrors(_: F.Type) -> Signal { + return Signal { observer in + return self.observe { event in + switch event { + case let .next(value): + observer.sendNext(value) + case .failed: + fatalError("NoError is impossible to construct") + case .completed: + observer.sendCompleted() + case .interrupted: + observer.sendInterrupted() + } + } + } + } + + /// Forward events from `self` until `interval`. Then if signal isn't + /// completed yet, fails with `error` on `scheduler`. + /// + /// - note: 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. + /// + /// - parameters: + /// - interval: Number of seconds to wait for `self` to complete. + /// - error: Error to send with `failed` event if `self` is not completed + /// when `interval` passes. + /// - scheudler: A scheduler to deliver error on. + /// + /// - returns: A signal that sends events for at most `interval` seconds, + /// then, if not `completed` - sends `error` with `failed` event + /// on `scheduler`. + public func timeout( + after interval: TimeInterval, + raising error: NewError, + on scheduler: DateSchedulerProtocol + ) -> Signal { + return self + .promoteErrors(NewError.self) + .timeout(after: interval, raising: error, on: scheduler) + } +} diff --git a/ReactiveSwift/SignalProducer.swift b/ReactiveSwift/SignalProducer.swift new file mode 100644 index 0000000000..a3030992f6 --- /dev/null +++ b/ReactiveSwift/SignalProducer.swift @@ -0,0 +1,1907 @@ +import Foundation +import Result + +/// A SignalProducer creates Signals that can produce values of type `Value` +/// and/or fail with errors of type `Error`. If no failure should be possible, +/// `NoError` can be specified for `Error`. +/// +/// SignalProducers can be used to represent operations or tasks, like network +/// requests, where each invocation of `start()` will create a new underlying +/// operation. This ensures that consumers will receive the results, versus a +/// plain Signal, where the results might be sent before any observers are +/// attached. +/// +/// Because of the behavior of `start()`, different Signals created from the +/// producer may see a different version of Events. The Events may arrive in a +/// different order between Signals, or the stream might be completely +/// different! +public struct SignalProducer { + public typealias ProducedSignal = Signal + + private let startHandler: (Signal.Observer, CompositeDisposable) -> Void + + /// Initializes a `SignalProducer` that will emit the same events as the + /// given signal. + /// + /// If the Disposable returned from `start()` is disposed or a terminating + /// event is sent to the observer, the given signal will be disposed. + /// + /// - parameters: + /// - signal: A signal to observe after starting the producer. + public init(signal: S) { + self.init { observer, disposable in + disposable += signal.observe(observer) + } + } + + /// Initializes a SignalProducer that will invoke the given closure once for + /// each invocation of `start()`. + /// + /// The events that the closure puts into the given observer will become + /// the events sent by the started `Signal` to its observers. + /// + /// - note: If the `Disposable` returned from `start()` is disposed or a + /// terminating event is sent to the observer, the given + /// `CompositeDisposable` will be disposed, at which point work + /// should be interrupted and any temporary resources cleaned up. + /// + /// - parameters: + /// - startHandler: A closure that accepts observer and a disposable. + public init(_ startHandler: (Signal.Observer, CompositeDisposable) -> Void) { + self.startHandler = startHandler + } + + /// Creates a producer for a `Signal` that will immediately send one value + /// then complete. + /// + /// - parameters: + /// - value: A value that should be sent by the `Signal` in a `next` + /// event. + public init(value: Value) { + self.init { observer, disposable in + observer.sendNext(value) + observer.sendCompleted() + } + } + + /// Creates a producer for a `Signal` that will immediately fail with the + /// given error. + /// + /// - parameters: + /// - error: An error that should be sent by the `Signal` in a `failed` + /// event. + public init(error: Error) { + self.init { observer, disposable in + observer.sendFailed(error) + } + } + + /// Creates a producer for a Signal that will immediately send one value + /// then complete, or immediately fail, depending on the given Result. + /// + /// - parameters: + /// - result: A `Result` instance that will send either `next` event if + /// `result` is `Success`ful or `failed` event if `result` is a + /// `Failure`. + public init(result: Result) { + switch result { + case let .success(value): + self.init(value: value) + + case let .failure(error): + self.init(error: error) + } + } + + /// Creates a producer for a Signal that will immediately send the values + /// from the given sequence, then complete. + /// + /// - parameters: + /// - values: A sequence of values that a `Signal` will send as separate + /// `next` events and then complete. + public init(values: S) { + self.init { observer, disposable in + for value in values { + observer.sendNext(value) + + if disposable.isDisposed { + break + } + } + + observer.sendCompleted() + } + } + + /// Creates a producer for a Signal that will immediately send the values + /// from the given sequence, then complete. + /// + /// - parameters: + /// - first: First value for the `Signal` to send. + /// - second: Second value for the `Signal` to send. + /// - tail: Rest of the values to be sent by the `Signal`. + public init(values first: Value, _ second: Value, _ tail: Value...) { + self.init(values: [ first, second ] + tail) + } + + /// A producer for a Signal that will immediately complete without sending + /// any values. + public static var empty: SignalProducer { + return self.init { observer, disposable in + observer.sendCompleted() + } + } + + /// A producer for a Signal that never sends any events to its observers. + public static var never: SignalProducer { + return self.init { _ in return } + } + + /// Create a `SignalProducer` that will attempt the given operation once for + /// each invocation of `start()`. + /// + /// Upon success, the started signal will send the resulting value then + /// complete. Upon failure, the started signal will fail with the error that + /// occurred. + /// + /// - parameters: + /// - operation: A closure that returns instance of `Result`. + /// + /// - returns: A `SignalProducer` that will forward `Success`ful `result` as + /// `next` event and then complete or `failed` event if `result` + /// is a `Failure`. + public static func attempt(_ operation: () -> Result) -> SignalProducer { + return self.init { observer, disposable in + operation().analysis(ifSuccess: { value in + observer.sendNext(value) + observer.sendCompleted() + }, ifFailure: { error in + observer.sendFailed(error) + }) + } + } + + /// Create a Signal from the producer, pass it into the given closure, + /// then start sending events on the Signal when the closure has returned. + /// + /// The closure will also receive a disposable which can be used to + /// interrupt the work associated with the signal and immediately send an + /// `interrupted` event. + /// + /// - parameters: + /// - setUp: A closure that accepts a `signal` and `interrupter`. + public func startWithSignal(_ setup: @noescape (signal: Signal, interrupter: Disposable) -> Void) { + let (signal, observer) = Signal.pipe() + + // Disposes of the work associated with the SignalProducer and any + // upstream producers. + let producerDisposable = CompositeDisposable() + + // Directly disposed of when `start()` or `startWithSignal()` is + // disposed. + let cancelDisposable = ActionDisposable { + observer.sendInterrupted() + producerDisposable.dispose() + } + + setup(signal: signal, interrupter: cancelDisposable) + + if cancelDisposable.isDisposed { + return + } + + let wrapperObserver: Signal.Observer = Observer { event in + observer.action(event) + + if event.isTerminating { + // Dispose only after notifying the Signal, so disposal + // logic is consistently the last thing to run. + producerDisposable.dispose() + } + } + + startHandler(wrapperObserver, producerDisposable) + } +} + +public protocol SignalProducerProtocol { + /// The type of values being sent on the producer + associatedtype Value + /// The type of error that can occur on the producer. If errors aren't possible + /// then `NoError` can be used. + associatedtype Error: Swift.Error + + /// Extracts a signal producer from the receiver. + var producer: SignalProducer { get } + + /// Initialize a signal + init(_ startHandler: (Signal.Observer, CompositeDisposable) -> Void) + + /// Creates a Signal from the producer, passes it into the given closure, + /// then starts sending events on the Signal when the closure has returned. + func startWithSignal(_ setup: @noescape (signal: Signal, interrupter: Disposable) -> Void) +} + +extension SignalProducer: SignalProducerProtocol { + public var producer: SignalProducer { + return self + } +} + +extension SignalProducerProtocol { + /// Create a Signal from the producer, then attach the given observer to + /// the `Signal` as an observer. + /// + /// - parameters: + /// - observer: An observer to attach to produced signal. + /// + /// - returns: A `Disposable` which can be used to interrupt the work + /// associated with the signal and immediately send an + /// `interrupted` event. + @discardableResult + public func start(_ observer: Signal.Observer = Signal.Observer()) -> Disposable { + var disposable: Disposable! + + startWithSignal { signal, innerDisposable in + signal.observe(observer) + disposable = innerDisposable + } + + return disposable + } + + /// Convenience override for start(_:) to allow trailing-closure style + /// invocations. + /// + /// - parameters: + /// - observerAction: A closure that accepts `Event` sent by the produced + /// signal. + /// + /// - returns: A `Disposable` which can be used to interrupt the work + /// associated with the signal and immediately send an + /// `interrupted` event. + @discardableResult + public func start(_ observerAction: Signal.Observer.Action) -> Disposable { + return start(Observer(observerAction)) + } + + /// Create a Signal from the producer, then add an observer to the `Signal`, + /// which will invoke the given callback when `next` or `failed` events are + /// received. + /// + /// - parameters: + /// - result: A closure that accepts a `result` that contains a `Success` + /// case for `next` events or `Failure` case for `failed` event. + /// + /// - returns: A Disposable which can be used to interrupt the work + /// associated with the Signal, and prevent any future callbacks + /// from being invoked. + @discardableResult + public func startWithResult(_ result: (Result) -> Void) -> Disposable { + return start( + Observer( + next: { result(.success($0)) }, + failed: { result(.failure($0)) } + ) + ) + } + + /// Create a Signal from the producer, then add exactly one observer to the + /// Signal, which will invoke the given callback when a `completed` event is + /// received. + /// + /// - parameters: + /// - completed: A closure that will be envoked when produced signal sends + /// `completed` event. + /// + /// - returns: A `Disposable` which can be used to interrupt the work + /// associated with the signal. + @discardableResult + public func startWithCompleted(_ completed: () -> Void) -> Disposable { + return start(Observer(completed: completed)) + } + + /// Creates a Signal from the producer, then adds exactly one observer to + /// the Signal, which will invoke the given callback when a `failed` event + /// is received. + /// + /// - parameters: + /// - failed: A closure that accepts an error object. + /// + /// - returns: A `Disposable` which can be used to interrupt the work + /// associated with the signal. + @discardableResult + public func startWithFailed(_ failed: (Error) -> Void) -> Disposable { + return start(Observer(failed: failed)) + } + + /// Creates a Signal from the producer, then adds exactly one observer to + /// the Signal, which will invoke the given callback when an `interrupted` + /// event is received. + /// + /// - parameters: + /// - interrupted: A closure that is invoked when `interrupted` event is + /// received. + /// + /// - returns: A `Disposable` which can be used to interrupt the work + /// associated with the signal. + @discardableResult + public func startWithInterrupted(_ interrupted: () -> Void) -> Disposable { + return start(Observer(interrupted: interrupted)) + } +} + +extension SignalProducerProtocol where Error == NoError { + /// Create a Signal from the producer, then add exactly one observer to + /// the Signal, which will invoke the given callback when `next` events are + /// received. + /// + /// - parameters: + /// - next: A closure that accepts a value carried by `next` event. + /// + /// - returns: A `Disposable` which can be used to interrupt the work + /// associated with the Signal, and prevent any future callbacks + /// from being invoked. + @discardableResult + public func startWithNext(_ next: (Value) -> Void) -> Disposable { + return start(Observer(next: next)) + } +} + +extension SignalProducerProtocol { + /// Lift an unary Signal operator to operate upon SignalProducers instead. + /// + /// In other words, this will create a new `SignalProducer` which will apply + /// the given `Signal` operator to _every_ created `Signal`, just as if the + /// operator had been applied to each `Signal` yielded from `start()`. + /// + /// - parameters: + /// - transform: An unary operator to lift. + /// + /// - returns: A signal producer that applies signal's operator to every + /// created signal. + public func lift(_ transform: (Signal) -> Signal) -> SignalProducer { + return SignalProducer { observer, outerDisposable in + self.startWithSignal { signal, innerDisposable in + outerDisposable += innerDisposable + + transform(signal).observe(observer) + } + } + } + + + /// Lift a binary Signal operator to operate upon SignalProducers instead. + /// + /// In other words, this will create a new `SignalProducer` which will apply + /// the given `Signal` operator to _every_ `Signal` created from the two + /// producers, just as if the operator had been applied to each `Signal` + /// yielded from `start()`. + /// + /// - note: starting the returned producer will start the receiver of the + /// operator, which may not be adviseable for some operators. + /// + /// - parameters: + /// - transform: A binary operator to lift. + /// + /// - returns: A binary operator that operates on two signal producers. + public func lift(_ transform: (Signal) -> (Signal) -> Signal) -> (SignalProducer) -> SignalProducer { + return liftRight(transform) + } + + /// Right-associative lifting of a binary signal operator over producers. + /// That is, the argument producer will be started before the receiver. When + /// both producers are synchronous this order can be important depending on + /// the operator to generate correct results. + private func liftRight(_ transform: (Signal) -> (Signal) -> Signal) -> (SignalProducer) -> SignalProducer { + return { otherProducer in + return SignalProducer { observer, outerDisposable in + self.startWithSignal { signal, disposable in + outerDisposable.add(disposable) + + otherProducer.startWithSignal { otherSignal, otherDisposable in + outerDisposable += otherDisposable + + transform(signal)(otherSignal).observe(observer) + } + } + } + } + } + + /// Left-associative lifting of a binary signal operator over producers. + /// That is, the receiver will be started before the argument producer. When + /// both producers are synchronous this order can be important depending on + /// the operator to generate correct results. + private func liftLeft(_ transform: (Signal) -> (Signal) -> Signal) -> (SignalProducer) -> SignalProducer { + return { otherProducer in + return SignalProducer { observer, outerDisposable in + otherProducer.startWithSignal { otherSignal, otherDisposable in + outerDisposable += otherDisposable + + self.startWithSignal { signal, disposable in + outerDisposable.add(disposable) + + transform(signal)(otherSignal).observe(observer) + } + } + } + } + } + + + /// Lift a binary Signal operator to operate upon a Signal and a + /// SignalProducer instead. + /// + /// In other words, this will create a new `SignalProducer` which will apply + /// the given `Signal` operator to _every_ `Signal` created from the two + /// producers, just as if the operator had been applied to each `Signal` + /// yielded from `start()`. + /// + /// - parameters: + /// - transform: A binary operator to lift. + /// + /// - returns: A binary operator that works on `Signal` and returns + /// `SignalProducer`. + public func lift(_ transform: (Signal) -> (Signal) -> Signal) -> (Signal) -> SignalProducer { + return { otherSignal in + return SignalProducer { observer, outerDisposable in + let (wrapperSignal, otherSignalObserver) = Signal.pipe() + + // Avoid memory leak caused by the direct use of the given + // signal. + // + // See https://github.com/ReactiveCocoa/ReactiveCocoa/pull/2758 + // for the details. + outerDisposable += ActionDisposable { + otherSignalObserver.sendInterrupted() + } + outerDisposable += otherSignal.observe(otherSignalObserver) + + self.startWithSignal { signal, disposable in + outerDisposable += disposable + outerDisposable += transform(signal)(wrapperSignal).observe(observer) + } + } + } + } + + + /// Map each value in the producer to a new value. + /// + /// - parameters: + /// - transform: A closure that accepts a value and returns a different + /// value. + /// + /// - returns: A signal producer that, when started, will send a mapped + /// value of `self.` + public func map(_ transform: (Value) -> U) -> SignalProducer { + return lift { $0.map(transform) } + } + + /// Map errors in the producer to a new error. + /// + /// - parameters: + /// - transform: A closure that accepts an error object and returns a + /// different error. + /// + /// - returns: A producer that emits errors of new type. + public func mapError(_ transform: (Error) -> F) -> SignalProducer { + return lift { $0.mapError(transform) } + } + + /// Preserve only the values of the producer that pass the given predicate. + /// + /// - parameters: + /// - predicate: A closure that accepts value and returns `Bool` denoting + /// whether value has passed the test. + /// + /// - returns: A producer that, when started, will send only the values + /// passing the given predicate. + public func filter(_ predicate: (Value) -> Bool) -> SignalProducer { + return lift { $0.filter(predicate) } + } + + /// Yield the first `count` values from the input producer. + /// + /// - precondition: `count` must be non-negative number. + /// + /// - parameters: + /// - count: A number of values to take from the signal. + /// + /// - returns: A producer that, when started, will yield the first `count` + /// values from `self`. + public func take(first count: Int) -> SignalProducer { + return lift { $0.take(first: count) } + } + + /// Yield an array of values when `self` completes. + /// + /// - note: When `self` completes without collecting any value, it will send + /// an empty array of values. + /// + /// - returns: A producer that, when started, will yield an array of values + /// when `self` completes. + public func collect() -> SignalProducer<[Value], Error> { + return lift { $0.collect() } + } + + /// Yield an array of values until it reaches a certain count. + /// + /// - precondition: `count` should be greater than zero. + /// + /// - note: When the count is reached the array is sent and the signal + /// starts over yielding a new array of values. + /// + /// - note: When `self` completes any remaining values will be sent, the + /// last array may not have `count` values. Alternatively, if were + /// not collected any values will sent an empty array of values. + /// + /// - returns: A producer that, when started, collects at most `count` + /// values from `self`, forwards them as a single array and + /// completes. + public func collect(count: Int) -> SignalProducer<[Value], Error> { + precondition(count > 0) + return lift { $0.collect(count: count) } + } + + /// Yield an array of values based on a predicate which matches the values + /// collected. + /// + /// - note: When `self` completes any remaining values will be sent, the + /// last array may not match `predicate`. Alternatively, if were not + /// collected any values will sent an empty array of values. + /// + /// ```` + /// let (producer, observer) = SignalProducer.buffer(1) + /// + /// producer + /// .collect { values in values.reduce(0, combine: +) == 8 } + /// .startWithNext { print($0) } + /// + /// observer.sendNext(1) + /// observer.sendNext(3) + /// observer.sendNext(4) + /// observer.sendNext(7) + /// observer.sendNext(1) + /// observer.sendNext(5) + /// observer.sendNext(6) + /// observer.sendCompleted() + /// + /// // Output: + /// // [1, 3, 4] + /// // [7, 1] + /// // [5, 6] + /// ```` + /// + /// - parameters: + /// - predicate: Predicate to match when values should be sent (returning + /// `true`) or alternatively when they should be collected + /// (where it should return `false`). The most recent value + /// (`next`) is included in `values` and will be the end of + /// the current array of values if the predicate returns + /// `true`. + /// + /// - returns: A producer that, when started, collects values passing the + /// predicate and, when `self` completes, forwards them as a + /// single array and complets. + public func collect(_ predicate: (values: [Value]) -> Bool) -> SignalProducer<[Value], Error> { + return lift { $0.collect(predicate) } + } + + /// Yield an array of values based on a predicate which matches the values + /// collected and the next value. + /// + /// - note: When `self` completes any remaining values will be sent, the + /// last array may not match `predicate`. Alternatively, if no + /// values were collected an empty array will be sent. + /// + /// ```` + /// let (producer, observer) = SignalProducer.buffer(1) + /// + /// producer + /// .collect { values, next in next == 7 } + /// .startWithNext { print($0) } + /// + /// observer.sendNext(1) + /// observer.sendNext(1) + /// observer.sendNext(7) + /// observer.sendNext(7) + /// observer.sendNext(5) + /// observer.sendNext(6) + /// observer.sendCompleted() + /// + /// // Output: + /// // [1, 1] + /// // [7] + /// // [7, 5, 6] + /// ```` + /// + /// - parameters: + /// - predicate: Predicate to match when values should be sent (returning + /// `true`) or alternatively when they should be collected + /// (where it should return `false`). The most recent value + /// (`next`) is not included in `values` and will be the + /// start of the next array of values if the predicate + /// returns `true`. + /// + /// - returns: A signal that will yield an array of values based on a + /// predicate which matches the values collected and the next + /// value. + public func collect(_ predicate: (values: [Value], next: Value) -> Bool) -> SignalProducer<[Value], Error> { + return lift { $0.collect(predicate) } + } + + /// Forward all events onto the given scheduler, instead of whichever + /// scheduler they originally arrived upon. + /// + /// - parameters: + /// - scheduler: A scheduler to deliver events on. + /// + /// - returns: A producer that, when started, will yield `self` values on + /// provided scheduler. + public func observe(on scheduler: SchedulerProtocol) -> SignalProducer { + return lift { $0.observe(on: scheduler) } + } + + /// Combine the latest value of the receiver with the latest value from the + /// given producer. + /// + /// - note: The returned producer will not send a value until both inputs + /// have sent at least one value each. + /// + /// - note: If either producer is interrupted, the returned producer will + /// also be interrupted. + /// + /// - parameters: + /// - other: A producer to combine `self`'s value with. + /// + /// - returns: A producer that, when started, will yield a tuple containing + /// values of `self` and given producer. + public func combineLatest(with other: SignalProducer) -> SignalProducer<(Value, U), Error> { + return liftLeft(Signal.combineLatest)(other) + } + + /// Combine the latest value of the receiver with the latest value from + /// the given signal. + /// + /// - note: The returned producer will not send a value until both inputs + /// have sent at least one value each. + /// + /// - note: If either input is interrupted, the returned producer will also + /// be interrupted. + /// + /// - parameters: + /// - other: A signal to combine `self`'s value with. + /// + /// - returns: A producer that, when started, will yield a tuple containing + /// values of `self` and given signal. + public func combineLatest(with other: Signal) -> SignalProducer<(Value, U), Error> { + return lift(Signal.combineLatest(with:))(other) + } + + /// Delay `next` and `completed` events by the given interval, forwarding + /// them on the given scheduler. + /// + /// - note: `failed` and `interrupted` events are always scheduled + /// immediately. + /// + /// - parameters: + /// - interval: Interval to delay `next` and `completed` events by. + /// - scheduler: A scheduler to deliver delayed events on. + /// + /// - returns: A producer that, when started, will delay `next` and + /// `completed` events and will yield them on given scheduler. + public func delay(_ interval: TimeInterval, on scheduler: DateSchedulerProtocol) -> SignalProducer { + return lift { $0.delay(interval, on: scheduler) } + } + + /// Skip the first `count` values, then forward everything afterward. + /// + /// - parameters: + /// - count: A number of values to skip. + /// + /// - returns: A producer that, when started, will skip the first `count` + /// values, then forward everything afterward. + public func skip(first count: Int) -> SignalProducer { + return lift { $0.skip(first: count) } + } + + /// Treats all Events from the input producer as plain values, allowing them + /// to be manipulated just like any other value. + /// + /// In other words, this brings Events “into the monad.” + /// + /// - note: When a Completed or Failed event is received, the resulting + /// producer will send the Event itself and then complete. When an + /// `interrupted` event is received, the resulting producer will + /// send the `Event` itself and then interrupt. + /// + /// - returns: A producer that sends events as its values. + public func materialize() -> SignalProducer, NoError> { + return lift { $0.materialize() } + } + + /// Forward the latest value from `self` with the value from `sampler` as a + /// tuple, only when `sampler` sends a `next` event. + /// + /// - note: If `sampler` fires before a value has been observed on `self`, + /// nothing happens. + /// + /// - parameters: + /// - sampler: A producer that will trigger the delivery of `next` event + /// from `self`. + /// + /// - returns: A producer that will send values from `self` and `sampler`, + /// sampled (possibly multiple times) by `sampler`, then complete + /// once both input producers have completed, or interrupt if + /// either input producer is interrupted. + public func sample(with sampler: SignalProducer) -> SignalProducer<(Value, T), Error> { + return liftLeft(Signal.sample(with:))(sampler) + } + + /// Forward the latest value from `self` with the value from `sampler` as a + /// tuple, only when `sampler` sends a `next` event. + /// + /// - note: If `sampler` fires before a value has been observed on `self`, + /// nothing happens. + /// + /// - parameters: + /// - sampler: A signal that will trigger the delivery of `next` event + /// from `self`. + /// + /// - returns: A producer that, when started, will send values from `self` + /// and `sampler`, sampled (possibly multiple times) by + /// `sampler`, then complete once both input producers have + /// completed, or interrupt if either input producer is + /// interrupted. + public func sample(with sampler: Signal) -> SignalProducer<(Value, T), Error> { + return lift(Signal.sample(with:))(sampler) + } + + /// Forward the latest value from `self` whenever `sampler` sends a `next` + /// event. + /// + /// - note: If `sampler` fires before a value has been observed on `self`, + /// nothing happens. + /// + /// - parameters: + /// - sampler: A producer that will trigger the delivery of `next` event + /// from `self`. + /// + /// - returns: A producer that, when started, will send values from `self`, + /// sampled (possibly multiple times) by `sampler`, then complete + /// once both input producers have completed, or interrupt if + /// either input producer is interrupted. + public func sample(on sampler: SignalProducer<(), NoError>) -> SignalProducer { + return liftLeft(Signal.sample(on:))(sampler) + } + + /// Forward the latest value from `self` whenever `sampler` sends a `next` + /// event. + /// + /// - note: If `sampler` fires before a value has been observed on `self`, + /// nothing happens. + /// + /// - parameters: + /// - trigger: A signal whose `next` or `completed` events will start the + /// deliver of events on `self`. + /// + /// - returns: A producer that will send values from `self`, sampled + /// (possibly multiple times) by `sampler`, then complete once + /// both inputs have completed, or interrupt if either input is + /// interrupted. + public func sample(on sampler: Signal<(), NoError>) -> SignalProducer { + return lift(Signal.sample(on:))(sampler) + } + + /// Forwards events from `self` until `lifetime` ends, at which point the + /// returned producer will complete. + /// + /// - parameters: + /// - lifetime: A lifetime whose `ended` signal will cause the returned + /// producer to complete. + /// + /// - returns: A producer that will deliver events until `lifetime` ends. + public func take(during lifetime: Lifetime) -> SignalProducer { + return take(until: lifetime.ended) + } + + /// Forward events from `self` until `trigger` sends a `next` or `completed` + /// event, at which point the returned producer will complete. + /// + /// - parameters: + /// - trigger: A producer whose `next` or `completed` events will stop the + /// delivery of `next` events from `self`. + /// + /// - returns: A producer that will deliver events until `trigger` sends + /// `next` or `completed` events. + public func take(until trigger: SignalProducer<(), NoError>) -> SignalProducer { + // This should be the implementation of this method: + // return liftRight(Signal.takeUntil)(trigger) + // + // However, due to a Swift miscompilation (with `-O`) we need to inline + // `liftRight` here. + // + // See https://github.com/ReactiveCocoa/ReactiveCocoa/issues/2751 for + // more details. + // + // This can be reverted once tests with -O work correctly. + return SignalProducer { observer, outerDisposable in + self.startWithSignal { signal, disposable in + outerDisposable.add(disposable) + + trigger.startWithSignal { triggerSignal, triggerDisposable in + outerDisposable += triggerDisposable + + signal.take(until: triggerSignal).observe(observer) + } + } + } + } + + /// Forward events from `self` until `trigger` sends a Next or Completed + /// event, at which point the returned producer will complete. + /// + /// - parameters: + /// - trigger: A signal whose `next` or `completed` events will stop the + /// delivery of `next` events from `self`. + /// + /// - returns: A producer that will deliver events until `trigger` sends + /// `next` or `completed` events. + public func take(until trigger: Signal<(), NoError>) -> SignalProducer { + return lift(Signal.take(until:))(trigger) + } + + /// Do not forward any values from `self` until `trigger` sends a `next` + /// or `completed`, at which point the returned producer behaves exactly + /// like `producer`. + /// + /// - parameters: + /// - trigger: A producer whose `next` or `completed` events will start + /// the deliver of events on `self`. + /// + /// - returns: A producer that will deliver events once the `trigger` sends + /// `next` or `completed` events. + public func skip(until trigger: SignalProducer<(), NoError>) -> SignalProducer { + return liftRight(Signal.skip(until:))(trigger) + } + + /// Do not forward any values from `self` until `trigger` sends a `next` + /// or `completed`, at which point the returned signal behaves exactly like + /// `signal`. + /// + /// - parameters: + /// - trigger: A signal whose `next` or `completed` events will start the + /// deliver of events on `self`. + /// + /// - returns: A producer that will deliver events once the `trigger` sends + /// `next` or `completed` events. + public func skip(until trigger: Signal<(), NoError>) -> SignalProducer { + return lift(Signal.skip(until:))(trigger) + } + + /// Forward events from `self` with history: values of the returned producer + /// are a tuple whose first member is the previous value and whose second + /// member is the current value. `initial` is supplied as the first member + /// when `self` sends its first value. + /// + /// - parameters: + /// - initial: A value that will be combined with the first value sent by + /// `self`. + /// + /// - returns: A producer that sends tuples that contain previous and + /// current sent values of `self`. + public func combinePrevious(_ initial: Value) -> SignalProducer<(Value, Value), Error> { + return lift { $0.combinePrevious(initial) } + } + + /// Send only the final value and then immediately completes. + /// + /// - parameters: + /// - initial: Initial value for the accumulator. + /// - combine: A closure that accepts accumulator and sent value of + /// `self`. + /// + /// - returns: A producer that sends accumulated value after `self` + /// completes. + public func reduce(_ initial: U, _ combine: (U, Value) -> U) -> SignalProducer { + return lift { $0.reduce(initial, combine) } + } + + /// Aggregate `self`'s values into a single combined value. When `self` + /// emits its first value, `combine` is invoked with `initial` as the first + /// argument and that emitted value as the second argument. The result is + /// emitted from the producer returned from `scan`. That result is then + /// passed to `combine` as the first argument when the next value is + /// emitted, and so on. + /// + /// - parameters: + /// - initial: Initial value for the accumulator. + /// - combine: A closure that accepts accumulator and sent value of + /// `self`. + /// + /// - returns: A producer that sends accumulated value each time `self` + /// emits own value. + public func scan(_ initial: U, _ combine: (U, Value) -> U) -> SignalProducer { + return lift { $0.scan(initial, combine) } + } + + /// Forward only those values from `self` which do not pass `isRepeat` with + /// respect to the previous value. + /// + /// - note: The first value is always forwarded. + /// + /// - returns: A producer that does not send two equal values sequentially. + public func skipRepeats(_ isRepeat: (Value, Value) -> Bool) -> SignalProducer { + return lift { $0.skipRepeats(isRepeat) } + } + + /// Do not forward any values from `self` until `predicate` returns false, + /// at which point the returned producer behaves exactly like `self`. + /// + /// - parameters: + /// - predicate: A closure that accepts a value and returns whether `self` + /// should still not forward that value to a `producer`. + /// + /// - returns: A producer that sends only forwarded values from `self`. + public func skip(while predicate: (Value) -> Bool) -> SignalProducer { + return lift { $0.skip(while: predicate) } + } + + /// Forward events from `self` until `replacement` begins sending events. + /// + /// - parameters: + /// - replacement: A producer to wait to wait for values from and start + /// sending them as a replacement to `self`'s values. + /// + /// - returns: A producer which passes through `next`, `failed`, and + /// `interrupted` events from `self` until `replacement` sends an + /// event, at which point the returned producer will send that + /// event and switch to passing through events from `replacement` + /// instead, regardless of whether `self` has sent events + /// already. + public func take(untilReplacement signal: SignalProducer) -> SignalProducer { + return liftRight(Signal.take(untilReplacement:))(signal) + } + + /// Forwards events from `self` until `replacement` begins sending events. + /// + /// - parameters: + /// - replacement: A signal to wait to wait for values from and start + /// sending them as a replacement to `self`'s values. + /// + /// - returns: A producer which passes through `next`, `failed`, and + /// `interrupted` events from `self` until `replacement` sends an + /// event, at which point the returned producer will send that + /// event and switch to passing through events from `replacement` + /// instead, regardless of whether `self` has sent events + /// already. + public func take(untilReplacement signal: Signal) -> SignalProducer { + return lift(Signal.take(untilReplacement:))(signal) + } + + /// Wait until `self` completes and then forward the final `count` values + /// on the returned producer. + /// + /// - parameters: + /// - count: Number of last events to send after `self` completes. + /// + /// - returns: A producer that receives up to `count` values from `self` + /// after `self` completes. + public func take(last count: Int) -> SignalProducer { + return lift { $0.take(last: count) } + } + + /// Forward any values from `self` until `predicate` returns false, at which + /// point the returned producer will complete. + /// + /// - parameters: + /// - predicate: A closure that accepts value and returns `Bool` value + /// whether `self` should forward it to `signal` and continue + /// sending other events. + /// + /// - returns: A producer that sends events until the values sent by `self` + /// pass the given `predicate`. + public func take(while predicate: (Value) -> Bool) -> SignalProducer { + return lift { $0.take(while: predicate) } + } + + /// Zip elements of two producers into pairs. The elements of any Nth pair + /// are the Nth elements of the two input producers. + /// + /// - parameters: + /// - other: A producer to zip values with. + /// + /// - returns: A producer that sends tuples of `self` and `otherProducer`. + public func zip(with other: SignalProducer) -> SignalProducer<(Value, U), Error> { + return liftLeft(Signal.zip(with:))(other) + } + + /// Zip elements of this producer and a signal into pairs. The elements of + /// any Nth pair are the Nth elements of the two. + /// + /// - parameters: + /// - other: A signal to zip values with. + /// + /// - returns: A producer that sends tuples of `self` and `otherSignal`. + public func zip(with other: Signal) -> SignalProducer<(Value, U), Error> { + return lift(Signal.zip(with:))(other) + } + + /// Apply `operation` to values from `self` with `Success`ful results + /// forwarded on the returned producer and `Failure`s sent as `failed` + /// events. + /// + /// - parameters: + /// - operation: A closure that accepts a value and returns a `Result`. + /// + /// - returns: A producer that receives `Success`ful `Result` as `next` + /// event and `Failure` as `failed` event. + public func attempt(operation: (Value) -> Result<(), Error>) -> SignalProducer { + return lift { $0.attempt(operation) } + } + + /// Apply `operation` to values from `self` with `Success`ful results + /// mapped on the returned producer and `Failure`s sent as `failed` events. + /// + /// - parameters: + /// - operation: A closure that accepts a value and returns a result of + /// a mapped value as `Success`. + /// + /// - returns: A producer that sends mapped values from `self` if returned + /// `Result` is `Success`ful, `failed` events otherwise. + public func attemptMap(_ operation: (Value) -> Result) -> SignalProducer { + return lift { $0.attemptMap(operation) } + } + + /// Throttle values sent by the receiver, so that at least `interval` + /// seconds pass between each, then forwards them on the given scheduler. + /// + /// - note: If multiple values are received before the interval has elapsed, + /// the latest value is the one that will be passed on. + /// + /// - norw: If `self` terminates while a value is being throttled, that + /// value will be discarded and the returned producer will terminate + /// immediately. + /// + /// - parameters: + /// - interval: Number of seconds to wait between sent values. + /// - scheduler: A scheduler to deliver events on. + /// + /// - returns: A producer that sends values at least `interval` seconds + /// appart on a given scheduler. + public func throttle(_ interval: TimeInterval, on scheduler: DateSchedulerProtocol) -> SignalProducer { + return lift { $0.throttle(interval, on: scheduler) } + } + + /// Debounce values sent by the receiver, such that at least `interval` + /// seconds pass after the receiver has last sent a value, then + /// forward the latest value on the given scheduler. + /// + /// - note: If multiple values are received before the interval has elapsed, + /// the latest value is the one that will be passed on. + /// + /// - note: If `self` terminates while a value is being debounced, + /// that value will be discarded and the returned producer will + /// terminate immediately. + /// + /// - parameters: + /// - interval: A number of seconds to wait before sending a value. + /// - scheduler: A scheduler to send values on. + /// + /// - returns: A producer that sends values that are sent from `self` at + /// least `interval` seconds apart. + public func debounce(_ interval: TimeInterval, on scheduler: DateSchedulerProtocol) -> SignalProducer { + return lift { $0.debounce(interval, on: scheduler) } + } + + /// Forward events from `self` until `interval`. Then if producer isn't + /// completed yet, fails with `error` on `scheduler`. + /// + /// - note: 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. + /// + /// - parameters: + /// - interval: Number of seconds to wait for `self` to complete. + /// - error: Error to send with `failed` event if `self` is not completed + /// when `interval` passes. + /// - scheduler: A scheduler to deliver error on. + /// + /// - returns: A producer that sends events for at most `interval` seconds, + /// then, if not `completed` - sends `error` with `failed` event + /// on `scheduler`. + public func timeout(after interval: TimeInterval, raising error: Error, on scheduler: DateSchedulerProtocol) -> SignalProducer { + return lift { $0.timeout(after: interval, raising: error, on: scheduler) } + } +} + +extension SignalProducerProtocol where Value: OptionalProtocol { + /// Unwraps non-`nil` values and forwards them on the returned signal, `nil` + /// values are dropped. + /// + /// - returns: A producer that sends only non-nil values. + public func skipNil() -> SignalProducer { + return lift { $0.skipNil() } + } +} + +extension SignalProducerProtocol where Value: EventProtocol, Error == NoError { + /// The inverse of materialize(), this will translate a producer of `Event` + /// _values_ into a producer of those events themselves. + /// + /// - returns: A producer that sends values carried by `self` events. + public func dematerialize() -> SignalProducer { + return lift { $0.dematerialize() } + } +} + +extension SignalProducerProtocol where Error == NoError { + /// Promote a producer that does not generate failures into one that can. + /// + /// - note: This does not actually cause failers to be generated for the + /// given producer, but makes it easier to combine with other + /// producers that may fail; for example, with operators like + /// `combineLatestWith`, `zipWith`, `flatten`, etc. + /// + /// - parameters: + /// - _ An `ErrorType`. + /// + /// - returns: A producer that has an instantiatable `ErrorType`. + public func promoteErrors(_: F.Type) -> SignalProducer { + return lift { $0.promoteErrors(F.self) } + } + + /// Forward events from `self` until `interval`. Then if producer isn't + /// completed yet, fails with `error` on `scheduler`. + /// + /// - note: 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. + /// + /// - parameters: + /// - interval: Number of seconds to wait for `self` to complete. + /// - error: Error to send with `failed` event if `self` is not completed + /// when `interval` passes. + /// - scheudler: A scheduler to deliver error on. + /// + /// - returns: A producer that sends events for at most `interval` seconds, + /// then, if not `completed` - sends `error` with `failed` event + /// on `scheduler`. + public func timeout( + after interval: TimeInterval, + raising error: NewError, + on scheduler: DateSchedulerProtocol + ) -> SignalProducer { + return lift { $0.timeout(after: interval, raising: error, on: scheduler) } + } +} + +extension SignalProducerProtocol where Value: Equatable { + /// Forward only those values from `self` which are not duplicates of the + /// immedately preceding value. + /// + /// - note: The first value is always forwarded. + /// + /// - returns: A producer that does not send two equal values sequentially. + public func skipRepeats() -> SignalProducer { + return lift { $0.skipRepeats() } + } +} + +extension SignalProducerProtocol { + /// Forward only those values from `self` that have unique identities across + /// the set of all values that have been seen. + /// + /// - note: This causes the identities to be retained to check for + /// uniqueness. + /// + /// - parameters: + /// - transform: A closure that accepts a value and returns identity + /// value. + /// + /// - returns: A producer that sends unique values during its lifetime. + public func uniqueValues(_ transform: (Value) -> Identity) -> SignalProducer { + return lift { $0.uniqueValues(transform) } + } +} + +extension SignalProducerProtocol where Value: Hashable { + /// Forward only those values from `self` that are unique across the set of + /// all values that have been seen. + /// + /// - note: This causes the values to be retained to check for uniqueness. + /// Providing a function that returns a unique value for each sent + /// value can help you reduce the memory footprint. + /// + /// - returns: A producer that sends unique values during its lifetime. + public func uniqueValues() -> SignalProducer { + return lift { $0.uniqueValues() } + } +} + +extension SignalProducerProtocol { + /// Injects side effects to be performed upon the specified producer events. + /// + /// - note: In a composed producer, `starting` is invoked in the reverse + /// direction of the flow of events. + /// + /// - parameters: + /// - starting: A closure that is invoked before the producer is started. + /// - started: A closure that is invoked after the producer is started. + /// - event: A closure that accepts an event and is invoked on every + /// received event. + /// - next: A closure that accepts a value from `next` event. + /// - failed: A closure that accepts error object and is invoked for + /// `failed` event. + /// - completed: A closure that is invoked for `completed` event. + /// - interrupted: A closure that is invoked for `interrupted` event. + /// - terminated: A closure that is invoked for any terminating event. + /// - disposed: A closure added as disposable when signal completes. + /// + /// - returns: A producer with attached side-effects for given event cases. + public func on( + starting: (() -> Void)? = nil, + started: (() -> Void)? = nil, + event: ((Event) -> Void)? = nil, + next: ((Value) -> Void)? = nil, + failed: ((Error) -> Void)? = nil, + completed: (() -> Void)? = nil, + interrupted: (() -> Void)? = nil, + terminated: (() -> Void)? = nil, + disposed: (() -> Void)? = nil + ) -> SignalProducer { + return SignalProducer { observer, compositeDisposable in + starting?() + defer { started?() } + + self.startWithSignal { signal, disposable in + compositeDisposable += disposable + compositeDisposable += signal + .on( + event: event, + failed: failed, + completed: completed, + interrupted: interrupted, + terminated: terminated, + disposed: disposed, + next: next + ) + .observe(observer) + } + } + } + + /// Start the returned producer on the given `Scheduler`. + /// + /// - note: This implies that any side effects embedded in the producer will + /// be performed on the given scheduler as well. + /// + /// - note: Events may still be sent upon other schedulers — this merely + /// affects where the `start()` method is run. + /// + /// - parameters: + /// - scheduler: A scheduler to deliver events on. + /// + /// - returns: A producer that will deliver events on given `scheduler` when + /// started. + public func start(on scheduler: SchedulerProtocol) -> SignalProducer { + return SignalProducer { observer, compositeDisposable in + compositeDisposable += scheduler.schedule { + self.startWithSignal { signal, signalDisposable in + compositeDisposable += signalDisposable + signal.observe(observer) + } + } + } + } +} + +extension SignalProducerProtocol { + /// Combines the values of all the given producers, in the manner described by + /// `combineLatestWith`. + public static func combineLatest(_ a: SignalProducer, _ b: SignalProducer) -> SignalProducer<(Value, B), Error> { + return a.combineLatest(with: b) + } + + /// Combines the values of all the given producers, in the manner described by + /// `combineLatestWith`. + public static func combineLatest(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer) -> SignalProducer<(Value, B, C), Error> { + return combineLatest(a, b) + .combineLatest(with: c) + .map(repack) + } + + /// Combines the values of all the given producers, in the manner described by + /// `combineLatestWith`. + public static func combineLatest(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer) -> SignalProducer<(Value, B, C, D), Error> { + return combineLatest(a, b, c) + .combineLatest(with: d) + .map(repack) + } + + /// Combines the values of all the given producers, in the manner described by + /// `combineLatestWith`. + public static func combineLatest(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer) -> SignalProducer<(Value, B, C, D, E), Error> { + return combineLatest(a, b, c, d) + .combineLatest(with: e) + .map(repack) + } + + /// Combines the values of all the given producers, in the manner described by + /// `combineLatestWith`. + public static func combineLatest(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F), Error> { + return combineLatest(a, b, c, d, e) + .combineLatest(with: f) + .map(repack) + } + + /// Combines the values of all the given producers, in the manner described by + /// `combineLatestWith`. + public static func combineLatest(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer, _ g: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F, G), Error> { + return combineLatest(a, b, c, d, e, f) + .combineLatest(with: g) + .map(repack) + } + + /// Combines the values of all the given producers, in the manner described by + /// `combineLatestWith`. + public static func combineLatest(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer, _ g: SignalProducer, _ h: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F, G, H), Error> { + return combineLatest(a, b, c, d, e, f, g) + .combineLatest(with: h) + .map(repack) + } + + /// Combines the values of all the given producers, in the manner described by + /// `combineLatestWith`. + public static func combineLatest(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer, _ g: SignalProducer, _ h: SignalProducer, _ i: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F, G, H, I), Error> { + return combineLatest(a, b, c, d, e, f, g, h) + .combineLatest(with: i) + .map(repack) + } + + /// Combines the values of all the given producers, in the manner described by + /// `combineLatestWith`. + public static func combineLatest(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer, _ g: SignalProducer, _ h: SignalProducer, _ i: SignalProducer, _ j: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F, G, H, I, J), Error> { + return combineLatest(a, b, c, d, e, f, g, h, i) + .combineLatest(with: j) + .map(repack) + } + + /// Combines the values of all the given producers, in the manner described by + /// `combineLatestWith`. Will return an empty `SignalProducer` if the sequence is empty. + public static func combineLatest>(_ producers: S) -> SignalProducer<[Value], Error> { + var generator = producers.makeIterator() + if let first = generator.next() { + let initial = first.map { [$0] } + return IteratorSequence(generator).reduce(initial) { producer, next in + producer.combineLatest(with: next).map { $0.0 + [$0.1] } + } + } + + return .empty + } + + /// Zips the values of all the given producers, in the manner described by + /// `zipWith`. + public static func zip(_ a: SignalProducer, _ b: SignalProducer) -> SignalProducer<(Value, B), Error> { + return a.zip(with: b) + } + + /// Zips the values of all the given producers, in the manner described by + /// `zipWith`. + public static func zip(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer) -> SignalProducer<(Value, B, C), Error> { + return zip(a, b) + .zip(with: c) + .map(repack) + } + + /// Zips the values of all the given producers, in the manner described by + /// `zipWith`. + public static func zip(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer) -> SignalProducer<(Value, B, C, D), Error> { + return zip(a, b, c) + .zip(with: d) + .map(repack) + } + + /// Zips the values of all the given producers, in the manner described by + /// `zipWith`. + public static func zip(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer) -> SignalProducer<(Value, B, C, D, E), Error> { + return zip(a, b, c, d) + .zip(with: e) + .map(repack) + } + + /// Zips the values of all the given producers, in the manner described by + /// `zipWith`. + public static func zip(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F), Error> { + return zip(a, b, c, d, e) + .zip(with: f) + .map(repack) + } + + /// Zips the values of all the given producers, in the manner described by + /// `zipWith`. + public static func zip(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer, _ g: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F, G), Error> { + return zip(a, b, c, d, e, f) + .zip(with: g) + .map(repack) + } + + /// Zips the values of all the given producers, in the manner described by + /// `zipWith`. + public static func zip(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer, _ g: SignalProducer, _ h: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F, G, H), Error> { + return zip(a, b, c, d, e, f, g) + .zip(with: h) + .map(repack) + } + + /// Zips the values of all the given producers, in the manner described by + /// `zipWith`. + public static func zip(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer, _ g: SignalProducer, _ h: SignalProducer, _ i: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F, G, H, I), Error> { + return zip(a, b, c, d, e, f, g, h) + .zip(with: i) + .map(repack) + } + + /// Zips the values of all the given producers, in the manner described by + /// `zipWith`. + public static func zip(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer, _ g: SignalProducer, _ h: SignalProducer, _ i: SignalProducer, _ j: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F, G, H, I, J), Error> { + return zip(a, b, c, d, e, f, g, h, i) + .zip(with: j) + .map(repack) + } + + /// Zips the values of all the given producers, in the manner described by + /// `zipWith`. Will return an empty `SignalProducer` if the sequence is empty. + public static func zip>(_ producers: S) -> SignalProducer<[Value], Error> { + var generator = producers.makeIterator() + if let first = generator.next() { + let initial = first.map { [$0] } + return IteratorSequence(generator).reduce(initial) { producer, next in + producer.zip(with: next).map { $0.0 + [$0.1] } + } + } + + return .empty + } +} + +extension SignalProducerProtocol { + /// Repeat `self` a total of `count` times. In other words, start producer + /// `count` number of times, each one after previously started producer + /// completes. + /// + /// - note: Repeating `1` time results in an equivalent signal producer. + /// + /// - note: Repeating `0` times results in a producer that instantly + /// completes. + /// + /// - parameters: + /// - count: Number of repetitions. + /// + /// - returns: A signal producer start sequentially starts `self` after + /// previously started producer completes. + public func times(_ count: Int) -> SignalProducer { + precondition(count >= 0) + + if count == 0 { + return .empty + } else if count == 1 { + return producer + } + + return SignalProducer { observer, disposable in + let serialDisposable = SerialDisposable() + disposable += serialDisposable + + func iterate(_ current: Int) { + self.startWithSignal { signal, signalDisposable in + serialDisposable.innerDisposable = signalDisposable + + signal.observe { event in + if case .completed = event { + let remainingTimes = current - 1 + if remainingTimes > 0 { + iterate(remainingTimes) + } else { + observer.sendCompleted() + } + } else { + observer.action(event) + } + } + } + } + + iterate(count) + } + } + + /// Ignore failures up to `count` times. + /// + /// - precondition: `count` must be non-negative integer. + /// + /// - parameters: + /// - count: Number of retries. + /// + /// - returns: A signal producer that restarts up to `count` times. + public func retry(upTo count: Int) -> SignalProducer { + precondition(count >= 0) + + if count == 0 { + return producer + } else { + return flatMapError { _ in + self.retry(upTo: count - 1) + } + } + } + + /// Wait for completion of `self`, *then* forward all events from + /// `replacement`. Any failure or interruption sent from `self` is + /// forwarded immediately, in which case `replacement` will not be started, + /// and none of its events will be be forwarded. + /// + /// - note: All values sent from `self` are ignored. + /// + /// - parameters: + /// - replacement: A producer to start when `self` completes. + /// + /// - returns: A producer that sends events from `self` and then from + /// `replacement` when `self` completes. + public func then(_ replacement: SignalProducer) -> SignalProducer { + return SignalProducer { observer, observerDisposable in + self.startWithSignal { signal, signalDisposable in + observerDisposable += signalDisposable + + signal.observe { event in + switch event { + case let .failed(error): + observer.sendFailed(error) + case .completed: + observerDisposable += replacement.start(observer) + case .interrupted: + observer.sendInterrupted() + case .next: + break + } + } + } + } + } + + /// Start the producer, then block, waiting for the first value. + /// + /// When a single value or error is sent, the returned `Result` will + /// represent those cases. However, when no values are sent, `nil` will be + /// returned. + /// + /// - returns: Result when single `next` or `failed` event is received. + /// `nil` when no events are received. + public func first() -> Result? { + return take(first: 1).single() + } + + /// Start the producer, then block, waiting for events: Next and + /// Completed. + /// + /// When a single value or error is sent, the returned `Result` will + /// represent those cases. However, when no values are sent, or when more + /// than one value is sent, `nil` will be returned. + /// + /// - returns: Result when single `next` or `failed` event is received. + /// `nil` when 0 or more than 1 events are received. + public func single() -> Result? { + let semaphore = DispatchSemaphore(value: 0) + var result: Result? + + take(first: 2).start { event in + switch event { + case let .next(value): + if result != nil { + // Move into failure state after recieving another value. + result = nil + return + } + result = .success(value) + case let .failed(error): + result = .failure(error) + semaphore.signal() + case .completed, .interrupted: + semaphore.signal() + } + } + + semaphore.wait() + return result + } + + /// Start the producer, then block, waiting for the last value. + /// + /// When a single value or error is sent, the returned `Result` will + /// represent those cases. However, when no values are sent, `nil` will be + /// returned. + /// + /// - returns: Result when single `next` or `failed` event is received. + /// `nil` when no events are received. + public func last() -> Result? { + return take(last: 1).single() + } + + /// Starts the producer, then blocks, waiting for completion. + /// + /// When a completion or error is sent, the returned `Result` will represent + /// those cases. + /// + /// - returns: Result when single `Completion` or `failed` event is + /// received. + public func wait() -> Result<(), Error> { + return then(SignalProducer<(), Error>(value: ())).last() ?? .success(()) + } + + /// Creates a new `SignalProducer` that will multicast values emitted by + /// the underlying producer, up to `capacity`. + /// This means that all clients of this `SignalProducer` will see the same + /// version of the emitted values/errors. + /// + /// The underlying `SignalProducer` will not be started until `self` is + /// started for the first time. When subscribing to this producer, all + /// previous values (up to `capacity`) will be emitted, followed by any new + /// values. + /// + /// If you find yourself needing *the current value* (the last buffered + /// value) you should consider using `PropertyType` instead, which, unlike + /// this operator, will guarantee at compile time that there's always a + /// buffered value. This operator is not recommended in most cases, as it + /// will introduce an implicit relationship between the original client and + /// the rest, so consider alternatives like `PropertyType`, or representing + /// your stream using a `Signal` instead. + /// + /// This operator is only recommended when you absolutely need to introduce + /// a layer of caching in front of another `SignalProducer`. + /// + /// - precondtion: `capacity` must be non-negative integer. + /// + /// - parameters: + /// - capcity: Number of values to hold. + /// + /// - returns: A caching producer that will hold up to last `capacity` + /// values. + public func replayLazily(upTo capacity: Int) -> SignalProducer { + precondition(capacity >= 0, "Invalid capacity: \(capacity)") + + // This will go "out of scope" when the returned `SignalProducer` goes + // out of scope. This lets us know when we're supposed to dispose the + // underlying producer. This is necessary because `struct`s don't have + // `deinit`. + let lifetimeToken = Lifetime.Token() + let lifetime = Lifetime(lifetimeToken) + + let state = Atomic(ReplayState(upTo: capacity)) + + let start: Atomic<(() -> Void)?> = Atomic { + // Start the underlying producer. + self + .take(during: lifetime) + .start { event in + let observers: Bag.Observer>? = state.modify { state in + defer { state.enqueue(event) } + return state.observers + } + observers?.forEach { $0.action(event) } + } + } + + return SignalProducer { observer, disposable in + // Don't dispose of the original producer until all observers + // have terminated. + disposable += { _ = lifetimeToken } + + while true { + var result: Result>! + state.modify { + result = $0.observe(observer) + } + + switch result! { + case let .success(token): + if let token = token { + disposable += { + state.modify { + $0.removeObserver(using: token) + } + } + } + + // Start the underlying producer if it has never been started. + start.swap(nil)?() + + // Terminate the replay loop. + return + + case let .failure(error): + error.values.forEach(observer.sendNext) + } + } + } + } +} + +/// Represents a recoverable error of an observer not being ready for an +/// attachment to a `ReplayState`, and the observer should replay the supplied +/// values before attempting to observe again. +private struct ReplayError: Error { + /// The values that should be replayed by the observer. + let values: [Value] +} + +private struct ReplayState { + let capacity: Int + + /// All cached values. + var values: [Value] = [] + + /// A termination event emitted by the underlying producer. + /// + /// This will be nil if termination has not occurred. + var terminationEvent: Event? + + /// The observers currently attached to the caching producer, or `nil` if the + /// caching producer was terminated. + var observers: Bag.Observer>? = Bag() + + /// The set of in-flight replay buffers. + var replayBuffers: [ObjectIdentifier: [Value]] = [:] + + /// Initialize the replay state. + /// + /// - parameters: + /// - capacity: The maximum amount of values which can be cached by the + /// replay state. + init(upTo capacity: Int) { + self.capacity = capacity + } + + /// Attempt to observe the replay state. + /// + /// - warning: Repeatedly observing the replay state with the same observer + /// should be avoided. + /// + /// - parameters: + /// - observer: The observer to be registered. + /// + /// - returns: + /// If the observer is successfully attached, a `Result.success` with the + /// corresponding removal token would be returned. Otherwise, a + /// `Result.failure` with a `ReplayError` would be returned. + mutating func observe(_ observer: Signal.Observer) -> Result> { + // Since the only use case is `replayLazily`, which always creates a unique + // `Observer` for every produced signal, we can use the ObjectIdentifier of + // the `Observer` to track them directly. + let id = ObjectIdentifier(observer) + + switch replayBuffers[id] { + case .none where !values.isEmpty: + // No in-flight replay buffers was found, but the `ReplayState` has one or + // more cached values in the `ReplayState`. The observer should replay + // them before attempting to observe again. + replayBuffers[id] = [] + return .failure(ReplayError(values: values)) + + case let .some(buffer) where !buffer.isEmpty: + // An in-flight replay buffer was found with one or more buffered values. + // The observer should replay them before attempting to observe again. + defer { replayBuffers[id] = [] } + return .failure(ReplayError(values: buffer)) + + case let .some(buffer) where buffer.isEmpty: + // Since an in-flight but empty replay buffer was found, the observer is + // ready to be attached to the `ReplayState`. + replayBuffers.removeValue(forKey: id) + + default: + // No values has to be replayed. The observer is ready to be attached to + // the `ReplayState`. + break + } + + if let event = terminationEvent { + observer.action(event) + } + + return .success(observers?.insert(observer)) + } + + /// Enqueue the supplied event to the replay state. + /// + /// - parameter: + /// - event: The event to be cached. + mutating func enqueue(_ event: Event) { + switch event { + case let .next(value): + for key in replayBuffers.keys { + replayBuffers[key]!.append(value) + } + + switch capacity { + case 0: + // With a capacity of zero, `state.values` can never be filled. + break + + case 1: + values = [value] + + default: + values.append(value) + + let overflow = values.count - capacity + if overflow > 0 { + values.removeFirst(overflow) + } + } + + case .completed, .failed, .interrupted: + // Disconnect all observers and prevent future attachments. + terminationEvent = event + observers = nil + } + } + + /// Remove the observer represented by the supplied token. + /// + /// - parameters: + /// - token: The token of the observer to be removed. + mutating func removeObserver(using token: RemovalToken) { + observers?.remove(using: token) + } +} + +/// Create a repeating timer of the given interval, with a reasonable default +/// leeway, sending updates on the given scheduler. +/// +/// - note: This timer will never complete naturally, so all invocations of +/// `start()` must be disposed to avoid leaks. +/// +/// - precondition: Interval must be non-negative number. +/// +/// - parameters: +/// - interval: An interval between invocations. +/// - scheduler: A scheduler to deliver events on. +/// +/// - returns: A producer that sends `NSDate` values every `interval` seconds. +public func timer(interval: TimeInterval, on scheduler: DateSchedulerProtocol) -> SignalProducer { + // Apple's "Power Efficiency Guide for Mac Apps" recommends a leeway of + // at least 10% of the timer interval. + return timer(interval: interval, on: scheduler, leeway: interval * 0.1) +} + +/// Creates a repeating timer of the given interval, sending updates on the +/// given scheduler. +/// +/// - note: This timer will never complete naturally, so all invocations of +/// `start()` must be disposed to avoid leaks. +/// +/// - precondition: Interval must be non-negative number. +/// +/// - precondition: Leeway must be non-negative number. +/// +/// - parameters: +/// - interval: An interval between invocations. +/// - scheduler: A scheduler to deliver events on. +/// - leeway: Interval leeway. Apple's "Power Efficiency Guide for Mac Apps" +/// recommends a leeway of at least 10% of the timer interval. +/// +/// - returns: A producer that sends `NSDate` values every `interval` seconds. +public func timer(interval: TimeInterval, on scheduler: DateSchedulerProtocol, leeway: TimeInterval) -> SignalProducer { + precondition(interval >= 0) + precondition(leeway >= 0) + + return SignalProducer { observer, compositeDisposable in + compositeDisposable += scheduler.schedule(after: scheduler.currentDate.addingTimeInterval(interval), + interval: interval, + leeway: leeway, + action: { observer.sendNext(scheduler.currentDate) }) + } +} diff --git a/ReactiveSwift/TupleExtensions.swift b/ReactiveSwift/TupleExtensions.swift new file mode 100644 index 0000000000..f976369328 --- /dev/null +++ b/ReactiveSwift/TupleExtensions.swift @@ -0,0 +1,42 @@ +// +// TupleExtensions.swift +// ReactiveSwift +// +// Created by Justin Spahr-Summers on 2014-12-20. +// Copyright (c) 2014 GitHub. All rights reserved. +// + +/// Adds a value into an N-tuple, returning an (N+1)-tuple. +/// +/// Supports creating tuples up to 10 elements long. +internal func repack(_ t: (A, B), value: C) -> (A, B, C) { + return (t.0, t.1, value) +} + +internal func repack(_ t: (A, B, C), value: D) -> (A, B, C, D) { + return (t.0, t.1, t.2, value) +} + +internal func repack(_ t: (A, B, C, D), value: E) -> (A, B, C, D, E) { + return (t.0, t.1, t.2, t.3, value) +} + +internal func repack(_ t: (A, B, C, D, E), value: F) -> (A, B, C, D, E, F) { + return (t.0, t.1, t.2, t.3, t.4, value) +} + +internal func repack(_ t: (A, B, C, D, E, F), value: G) -> (A, B, C, D, E, F, G) { + return (t.0, t.1, t.2, t.3, t.4, t.5, value) +} + +internal func repack(_ t: (A, B, C, D, E, F, G), value: H) -> (A, B, C, D, E, F, G, H) { + return (t.0, t.1, t.2, t.3, t.4, t.5, t.6, value) +} + +internal func repack(_ t: (A, B, C, D, E, F, G, H), value: I) -> (A, B, C, D, E, F, G, H, I) { + return (t.0, t.1, t.2, t.3, t.4, t.5, t.6, t.7, value) +} + +internal func repack(_ t: (A, B, C, D, E, F, G, H, I), value: J) -> (A, B, C, D, E, F, G, H, I, J) { + return (t.0, t.1, t.2, t.3, t.4, t.5, t.6, t.7, t.8, value) +} diff --git a/ReactiveSwiftTests/ActionSpec.swift b/ReactiveSwiftTests/ActionSpec.swift new file mode 100755 index 0000000000..7a56cd5501 --- /dev/null +++ b/ReactiveSwiftTests/ActionSpec.swift @@ -0,0 +1,142 @@ +// +// ActionSpec.swift +// ReactiveSwift +// +// Created by Justin Spahr-Summers on 2014-12-11. +// Copyright (c) 2014 GitHub. All rights reserved. +// + +import Result +import Nimble +import Quick +import ReactiveSwift + +class ActionSpec: QuickSpec { + override func spec() { + describe("Action") { + var action: Action! + var enabled: MutableProperty! + + var executionCount = 0 + var values: [String] = [] + var errors: [NSError] = [] + + var scheduler: TestScheduler! + let testError = NSError(domain: "ActionSpec", code: 1, userInfo: nil) + + beforeEach { + executionCount = 0 + values = [] + errors = [] + enabled = MutableProperty(false) + + scheduler = TestScheduler() + action = Action(enabledIf: enabled) { number in + return SignalProducer { observer, disposable in + executionCount += 1 + + if number % 2 == 0 { + observer.sendNext("\(number)") + observer.sendNext("\(number)\(number)") + + scheduler.schedule { + observer.sendCompleted() + } + } else { + scheduler.schedule { + observer.sendFailed(testError) + } + } + } + } + + action.values.observeNext { values.append($0) } + action.errors.observeNext { errors.append($0) } + } + + it("should be disabled and not executing after initialization") { + expect(action.isEnabled.value) == false + expect(action.isExecuting.value) == false + } + + it("should error if executed while disabled") { + var receivedError: ActionError? + action.apply(0).startWithFailed { + receivedError = $0 + } + + expect(receivedError).notTo(beNil()) + if let error = receivedError { + let expectedError = ActionError.disabled + expect(error == expectedError) == true + } + } + + it("should enable and disable based on the given property") { + enabled.value = true + expect(action.isEnabled.value) == true + expect(action.isExecuting.value) == false + + enabled.value = false + expect(action.isEnabled.value) == false + expect(action.isExecuting.value) == false + } + + describe("execution") { + beforeEach { + enabled.value = true + } + + it("should execute successfully") { + var receivedValue: String? + + action.apply(0) + .assumeNoErrors() + .startWithNext { + receivedValue = $0 + } + + expect(executionCount) == 1 + expect(action.isExecuting.value) == true + expect(action.isEnabled.value) == false + + expect(receivedValue) == "00" + expect(values) == [ "0", "00" ] + expect(errors) == [] + + scheduler.run() + expect(action.isExecuting.value) == false + expect(action.isEnabled.value) == true + + expect(values) == [ "0", "00" ] + expect(errors) == [] + } + + it("should execute with an error") { + var receivedError: ActionError? + + action.apply(1).startWithFailed { + receivedError = $0 + } + + expect(executionCount) == 1 + expect(action.isExecuting.value) == true + expect(action.isEnabled.value) == false + + scheduler.run() + expect(action.isExecuting.value) == false + expect(action.isEnabled.value) == true + + expect(receivedError).notTo(beNil()) + if let error = receivedError { + let expectedError = ActionError.producerFailed(testError) + expect(error == expectedError) == true + } + + expect(values) == [] + expect(errors) == [ testError ] + } + } + } + } +} diff --git a/ReactiveSwiftTests/AtomicSpec.swift b/ReactiveSwiftTests/AtomicSpec.swift new file mode 100644 index 0000000000..8234f5b9f4 --- /dev/null +++ b/ReactiveSwiftTests/AtomicSpec.swift @@ -0,0 +1,44 @@ +// +// AtomicSpec.swift +// ReactiveSwift +// +// Created by Justin Spahr-Summers on 2014-07-13. +// Copyright (c) 2014 GitHub. All rights reserved. +// + +import Nimble +import Quick +import ReactiveSwift + +class AtomicSpec: QuickSpec { + override func spec() { + var atomic: Atomic! + + beforeEach { + atomic = Atomic(1) + } + + it("should read and write the value directly") { + expect(atomic.value) == 1 + + atomic.value = 2 + expect(atomic.value) == 2 + } + + it("should swap the value atomically") { + expect(atomic.swap(2)) == 1 + expect(atomic.value) == 2 + } + + it("should modify the value atomically") { + atomic.modify { $0 += 1 } + expect(atomic.value) == 2 + } + + it("should perform an action with the value") { + let result: Bool = atomic.withValue { $0 == 1 } + expect(result) == true + expect(atomic.value) == 1 + } + } +} diff --git a/ReactiveSwiftTests/BagSpec.swift b/ReactiveSwiftTests/BagSpec.swift new file mode 100644 index 0000000000..706e781a07 --- /dev/null +++ b/ReactiveSwiftTests/BagSpec.swift @@ -0,0 +1,54 @@ +// +// BagSpec.swift +// ReactiveSwift +// +// Created by Justin Spahr-Summers on 2014-07-13. +// Copyright (c) 2014 GitHub. All rights reserved. +// + +import Nimble +import Quick +import ReactiveSwift + +class BagSpec: QuickSpec { + override func spec() { + var bag = Bag() + + beforeEach { + bag = Bag() + } + + it("should insert values") { + bag.insert("foo") + bag.insert("bar") + bag.insert("buzz") + + expect(bag).to(contain("foo")) + expect(bag).to(contain("bar")) + expect(bag).to(contain("buzz")) + expect(bag).toNot(contain("fuzz")) + expect(bag).toNot(contain("foobar")) + } + + it("should remove values given the token from insertion") { + let a = bag.insert("foo") + let b = bag.insert("bar") + let c = bag.insert("buzz") + + bag.remove(using: b) + expect(bag).to(contain("foo")) + expect(bag).toNot(contain("bar")) + expect(bag).to(contain("buzz")) + + bag.remove(using: a) + expect(bag).toNot(contain("foo")) + expect(bag).toNot(contain("bar")) + expect(bag).to(contain("buzz")) + + bag.remove(using: c) + expect(bag).toNot(contain("foo")) + expect(bag).toNot(contain("bar")) + expect(bag).toNot(contain("buzz")) + } + } +} diff --git a/ReactiveSwiftTests/DisposableSpec.swift b/ReactiveSwiftTests/DisposableSpec.swift new file mode 100644 index 0000000000..b8ac11cf9c --- /dev/null +++ b/ReactiveSwiftTests/DisposableSpec.swift @@ -0,0 +1,143 @@ +// +// DisposableSpec.swift +// ReactiveSwift +// +// Created by Justin Spahr-Summers on 2014-07-13. +// Copyright (c) 2014 GitHub. All rights reserved. +// + +import Nimble +import Quick +import ReactiveSwift + +class DisposableSpec: QuickSpec { + override func spec() { + describe("SimpleDisposable") { + it("should set disposed to true") { + let disposable = SimpleDisposable() + expect(disposable.isDisposed) == false + + disposable.dispose() + expect(disposable.isDisposed) == true + } + } + + describe("ActionDisposable") { + it("should run the given action upon disposal") { + var didDispose = false + let disposable = ActionDisposable { + didDispose = true + } + + expect(didDispose) == false + expect(disposable.isDisposed) == false + + disposable.dispose() + expect(didDispose) == true + expect(disposable.isDisposed) == true + } + } + + describe("CompositeDisposable") { + var disposable = CompositeDisposable() + + beforeEach { + disposable = CompositeDisposable() + } + + it("should ignore the addition of nil") { + disposable.add(nil) + return + } + + it("should dispose of added disposables") { + let simpleDisposable = SimpleDisposable() + disposable += simpleDisposable + + var didDispose = false + disposable += { + didDispose = true + } + + expect(simpleDisposable.isDisposed) == false + expect(didDispose) == false + expect(disposable.isDisposed) == false + + disposable.dispose() + expect(simpleDisposable.isDisposed) == true + expect(didDispose) == true + expect(disposable.isDisposed) == true + } + + it("should not dispose of removed disposables") { + let simpleDisposable = SimpleDisposable() + let handle = disposable += simpleDisposable + + // We should be allowed to call this any number of times. + handle.remove() + handle.remove() + expect(simpleDisposable.isDisposed) == false + + disposable.dispose() + expect(simpleDisposable.isDisposed) == false + } + } + + describe("ScopedDisposable") { + it("should dispose of the inner disposable upon deinitialization") { + let simpleDisposable = SimpleDisposable() + + func runScoped() { + let scopedDisposable = ScopedDisposable(simpleDisposable) + expect(simpleDisposable.isDisposed) == false + expect(scopedDisposable.isDisposed) == false + } + + expect(simpleDisposable.isDisposed) == false + + runScoped() + expect(simpleDisposable.isDisposed) == true + } + } + + describe("SerialDisposable") { + var disposable: SerialDisposable! + + beforeEach { + disposable = SerialDisposable() + } + + it("should dispose of the inner disposable") { + let simpleDisposable = SimpleDisposable() + disposable.innerDisposable = simpleDisposable + + expect(disposable.innerDisposable).notTo(beNil()) + expect(simpleDisposable.isDisposed) == false + expect(disposable.isDisposed) == false + + disposable.dispose() + expect(disposable.innerDisposable).to(beNil()) + expect(simpleDisposable.isDisposed) == true + expect(disposable.isDisposed) == true + } + + it("should dispose of the previous disposable when swapping innerDisposable") { + let oldDisposable = SimpleDisposable() + let newDisposable = SimpleDisposable() + + disposable.innerDisposable = oldDisposable + expect(oldDisposable.isDisposed) == false + expect(newDisposable.isDisposed) == false + + disposable.innerDisposable = newDisposable + expect(oldDisposable.isDisposed) == true + expect(newDisposable.isDisposed) == false + expect(disposable.isDisposed) == false + + disposable.innerDisposable = nil + expect(newDisposable.isDisposed) == true + expect(disposable.isDisposed) == false + } + } + } +} diff --git a/ReactiveSwiftTests/FlattenSpec.swift b/ReactiveSwiftTests/FlattenSpec.swift new file mode 100644 index 0000000000..bf19fdfa2f --- /dev/null +++ b/ReactiveSwiftTests/FlattenSpec.swift @@ -0,0 +1,963 @@ +// +// FlattenSpec.swift +// ReactiveSwift +// +// Created by Oleg Shnitko on 1/22/16. +// Copyright © 2016 GitHub. All rights reserved. +// + +import Result +import Nimble +import Quick +import ReactiveSwift + +private extension SignalProtocol { + typealias Pipe = (signal: Signal, observer: Observer) +} + +private typealias Pipe = Signal, TestError>.Pipe + +class FlattenSpec: QuickSpec { + override func spec() { + func describeSignalFlattenDisposal(_ flattenStrategy: FlattenStrategy, name: String) { + describe(name) { + var pipe: Pipe! + var disposable: Disposable? + + beforeEach { + pipe = Signal.pipe() + disposable = pipe.signal + .flatten(flattenStrategy) + .observe { _ in } + } + + afterEach { + disposable?.dispose() + } + + context("disposal") { + var disposed = false + + beforeEach { + disposed = false + pipe.observer.sendNext(SignalProducer { _, disposable in + disposable += ActionDisposable { + disposed = true + } + }) + } + + it("should dispose inner signals when outer signal interrupted") { + pipe.observer.sendInterrupted() + expect(disposed) == true + } + + it("should dispose inner signals when outer signal failed") { + pipe.observer.sendFailed(.default) + expect(disposed) == true + } + + it("should not dispose inner signals when outer signal completed") { + pipe.observer.sendCompleted() + expect(disposed) == false + } + } + } + } + + context("Signal") { + describeSignalFlattenDisposal(.latest, name: "switchToLatest") + describeSignalFlattenDisposal(.merge, name: "merge") + describeSignalFlattenDisposal(.concat, name: "concat") + } + + func describeSignalProducerFlattenDisposal(_ flattenStrategy: FlattenStrategy, name: String) { + describe(name) { + it("disposes original signal when result signal interrupted") { + var disposed = false + + let disposable = SignalProducer, NoError> { _, disposable in + disposable += ActionDisposable { + disposed = true + } + } + .flatten(flattenStrategy) + .start() + + disposable.dispose() + expect(disposed) == true + } + } + } + + context("SignalProducer") { + describeSignalProducerFlattenDisposal(.latest, name: "switchToLatest") + describeSignalProducerFlattenDisposal(.merge, name: "merge") + describeSignalProducerFlattenDisposal(.concat, name: "concat") + } + + describe("Signal.flatten()") { + it("works with TestError and a TestError Signal") { + typealias Inner = Signal + typealias Outer = Signal + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatten(.latest) + .assumeNoErrors() + .observeNext { value in + observed = value + } + + outerObserver.sendNext(inner) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with NoError and a TestError Signal") { + typealias Inner = Signal + typealias Outer = Signal + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatten(.latest) + .assumeNoErrors() + .observeNext { value in + observed = value + } + + outerObserver.sendNext(inner) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with NoError and a NoError Signal") { + typealias Inner = Signal + typealias Outer = Signal + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatten(.latest) + .observeNext { value in + observed = value + } + + outerObserver.sendNext(inner) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with TestError and a NoError Signal") { + typealias Inner = Signal + typealias Outer = Signal + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatten(.latest) + .assumeNoErrors() + .observeNext { value in + observed = value + } + + outerObserver.sendNext(inner) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with TestError and a TestError SignalProducer") { + typealias Inner = SignalProducer + typealias Outer = Signal + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatten(.latest) + .assumeNoErrors() + .observeNext { value in + observed = value + } + + outerObserver.sendNext(inner) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with NoError and a TestError SignalProducer") { + typealias Inner = SignalProducer + typealias Outer = Signal + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatten(.latest) + .assumeNoErrors() + .observeNext { value in + observed = value + } + + outerObserver.sendNext(inner) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with NoError and a NoError SignalProducer") { + typealias Inner = SignalProducer + typealias Outer = Signal + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatten(.latest) + .observeNext { value in + observed = value + } + + outerObserver.sendNext(inner) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with TestError and a NoError SignalProducer") { + typealias Inner = SignalProducer + typealias Outer = Signal + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatten(.latest) + .assumeNoErrors() + .observeNext { value in + observed = value + } + + outerObserver.sendNext(inner) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with SequenceType as a value") { + let (signal, innerObserver) = Signal<[Int], NoError>.pipe() + let sequence = [1, 2, 3] + var observedValues = [Int]() + + signal + .flatten(.concat) + .observeNext { value in + observedValues.append(value) + } + + innerObserver.sendNext(sequence) + expect(observedValues) == sequence + } + } + + describe("SignalProducer.flatten()") { + it("works with TestError and a TestError Signal") { + typealias Inner = Signal + typealias Outer = SignalProducer + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatten(.latest) + .assumeNoErrors() + .startWithNext { value in + observed = value + } + + outerObserver.sendNext(inner) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with NoError and a TestError Signal") { + typealias Inner = Signal + typealias Outer = SignalProducer + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatten(.latest) + .assumeNoErrors() + .startWithNext { value in + observed = value + } + + outerObserver.sendNext(inner) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with NoError and a NoError Signal") { + typealias Inner = Signal + typealias Outer = SignalProducer + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatten(.latest) + .startWithNext { value in + observed = value + } + + outerObserver.sendNext(inner) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with TestError and a NoError Signal") { + typealias Inner = Signal + typealias Outer = SignalProducer + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatten(.latest) + .assumeNoErrors() + .startWithNext { value in + observed = value + } + + outerObserver.sendNext(inner) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with TestError and a TestError SignalProducer") { + typealias Inner = SignalProducer + typealias Outer = SignalProducer + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatten(.latest) + .assumeNoErrors() + .startWithNext { value in + observed = value + } + + outerObserver.sendNext(inner) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with NoError and a TestError SignalProducer") { + typealias Inner = SignalProducer + typealias Outer = SignalProducer + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatten(.latest) + .assumeNoErrors() + .startWithNext { value in + observed = value + } + + outerObserver.sendNext(inner) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with NoError and a NoError SignalProducer") { + typealias Inner = SignalProducer + typealias Outer = SignalProducer + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatten(.latest) + .startWithNext { value in + observed = value + } + + outerObserver.sendNext(inner) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with TestError and a NoError SignalProducer") { + typealias Inner = SignalProducer + typealias Outer = SignalProducer + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatten(.latest) + .assumeNoErrors() + .startWithNext { value in + observed = value + } + + outerObserver.sendNext(inner) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with SequenceType as a value") { + let sequence = [1, 2, 3] + var observedValues = [Int]() + + let producer = SignalProducer<[Int], NoError>(value: sequence) + producer + .flatten(.latest) + .startWithNext { value in + observedValues.append(value) + } + + expect(observedValues) == sequence + } + } + + describe("Signal.flatMap()") { + it("works with TestError and a TestError Signal") { + typealias Inner = Signal + typealias Outer = Signal + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatMap(.latest) { _ in inner } + .assumeNoErrors() + .observeNext { value in + observed = value + } + + outerObserver.sendNext(4) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with NoError and a TestError Signal") { + typealias Inner = Signal + typealias Outer = Signal + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatMap(.latest) { _ in inner } + .assumeNoErrors() + .observeNext { value in + observed = value + } + + outerObserver.sendNext(4) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with NoError and a NoError Signal") { + typealias Inner = Signal + typealias Outer = Signal + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatMap(.latest) { _ in inner } + .observeNext { value in + observed = value + } + + outerObserver.sendNext(4) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with TestError and a NoError Signal") { + typealias Inner = Signal + typealias Outer = Signal + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatMap(.latest) { _ in inner } + .assumeNoErrors() + .observeNext { value in + observed = value + } + + outerObserver.sendNext(4) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with TestError and a TestError SignalProducer") { + typealias Inner = SignalProducer + typealias Outer = Signal + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatMap(.latest) { _ in inner } + .assumeNoErrors() + .observeNext { value in + observed = value + } + + outerObserver.sendNext(4) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with NoError and a TestError SignalProducer") { + typealias Inner = SignalProducer + typealias Outer = Signal + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatMap(.latest) { _ in inner } + .assumeNoErrors() + .observeNext { value in + observed = value + } + + outerObserver.sendNext(4) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with NoError and a NoError SignalProducer") { + typealias Inner = SignalProducer + typealias Outer = Signal + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatMap(.latest) { _ in inner } + .observeNext { value in + observed = value + } + + outerObserver.sendNext(4) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with TestError and a NoError SignalProducer") { + typealias Inner = SignalProducer + typealias Outer = Signal + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatMap(.latest) { _ in inner } + .assumeNoErrors() + .observeNext { value in + observed = value + } + + outerObserver.sendNext(4) + innerObserver.sendNext(4) + expect(observed) == 4 + } + } + + describe("SignalProducer.flatMap()") { + it("works with TestError and a TestError Signal") { + typealias Inner = Signal + typealias Outer = SignalProducer + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatMap(.latest) { _ in inner } + .assumeNoErrors() + .startWithNext { value in + observed = value + } + + outerObserver.sendNext(4) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with NoError and a TestError Signal") { + typealias Inner = Signal + typealias Outer = SignalProducer + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatMap(.latest) { _ in inner } + .assumeNoErrors() + .startWithNext { value in + observed = value + } + + outerObserver.sendNext(4) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with NoError and a NoError Signal") { + typealias Inner = Signal + typealias Outer = SignalProducer + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatMap(.latest) { _ in inner } + .startWithNext { value in + observed = value + } + + outerObserver.sendNext(4) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with TestError and a NoError Signal") { + typealias Inner = Signal + typealias Outer = SignalProducer + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatMap(.latest) { _ in inner } + .assumeNoErrors() + .startWithNext { value in + observed = value + } + + outerObserver.sendNext(4) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with TestError and a TestError SignalProducer") { + typealias Inner = SignalProducer + typealias Outer = SignalProducer + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatMap(.latest) { _ in inner } + .assumeNoErrors() + .startWithNext { value in + observed = value + } + + outerObserver.sendNext(4) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with NoError and a TestError SignalProducer") { + typealias Inner = SignalProducer + typealias Outer = SignalProducer + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatMap(.latest) { _ in inner } + .assumeNoErrors() + .startWithNext { value in + observed = value + } + + outerObserver.sendNext(4) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with NoError and a NoError SignalProducer") { + typealias Inner = SignalProducer + typealias Outer = SignalProducer + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatMap(.latest) { _ in inner } + .startWithNext { value in + observed = value + } + + outerObserver.sendNext(4) + innerObserver.sendNext(4) + expect(observed) == 4 + } + + it("works with TestError and a NoError SignalProducer") { + typealias Inner = SignalProducer + typealias Outer = SignalProducer + + let (inner, innerObserver) = Inner.pipe() + let (outer, outerObserver) = Outer.pipe() + + var observed: Int? = nil + outer + .flatMap(.latest) { _ in inner } + .assumeNoErrors() + .startWithNext { value in + observed = value + } + + outerObserver.sendNext(4) + innerObserver.sendNext(4) + expect(observed) == 4 + } + } + + describe("Signal.merge()") { + it("should emit values from all signals") { + let (signal1, observer1) = Signal.pipe() + let (signal2, observer2) = Signal.pipe() + + let mergedSignals = Signal.merge([signal1, signal2]) + + var lastValue: Int? + mergedSignals.observeNext { lastValue = $0 } + + expect(lastValue).to(beNil()) + + observer1.sendNext(1) + expect(lastValue) == 1 + + observer2.sendNext(2) + expect(lastValue) == 2 + + observer1.sendNext(3) + expect(lastValue) == 3 + } + + it("should not stop when one signal completes") { + let (signal1, observer1) = Signal.pipe() + let (signal2, observer2) = Signal.pipe() + + let mergedSignals = Signal.merge([signal1, signal2]) + + var lastValue: Int? + mergedSignals.observeNext { lastValue = $0 } + + expect(lastValue).to(beNil()) + + observer1.sendNext(1) + expect(lastValue) == 1 + + observer1.sendCompleted() + expect(lastValue) == 1 + + observer2.sendNext(2) + expect(lastValue) == 2 + } + + it("should complete when all signals complete") { + let (signal1, observer1) = Signal.pipe() + let (signal2, observer2) = Signal.pipe() + + let mergedSignals = Signal.merge([signal1, signal2]) + + var completed = false + mergedSignals.observeCompleted { completed = true } + + expect(completed) == false + + observer1.sendNext(1) + expect(completed) == false + + observer1.sendCompleted() + expect(completed) == false + + observer2.sendCompleted() + expect(completed) == true + } + } + + describe("SignalProducer.merge()") { + it("should emit values from all producers") { + let (signal1, observer1) = SignalProducer.pipe() + let (signal2, observer2) = SignalProducer.pipe() + + let mergedSignals = SignalProducer.merge([signal1, signal2]) + + var lastValue: Int? + mergedSignals.startWithNext { lastValue = $0 } + + expect(lastValue).to(beNil()) + + observer1.sendNext(1) + expect(lastValue) == 1 + + observer2.sendNext(2) + expect(lastValue) == 2 + + observer1.sendNext(3) + expect(lastValue) == 3 + } + + it("should not stop when one producer completes") { + let (signal1, observer1) = SignalProducer.pipe() + let (signal2, observer2) = SignalProducer.pipe() + + let mergedSignals = SignalProducer.merge([signal1, signal2]) + + var lastValue: Int? + mergedSignals.startWithNext { lastValue = $0 } + + expect(lastValue).to(beNil()) + + observer1.sendNext(1) + expect(lastValue) == 1 + + observer1.sendCompleted() + expect(lastValue) == 1 + + observer2.sendNext(2) + expect(lastValue) == 2 + } + + it("should complete when all producers complete") { + let (signal1, observer1) = SignalProducer.pipe() + let (signal2, observer2) = SignalProducer.pipe() + + let mergedSignals = SignalProducer.merge([signal1, signal2]) + + var completed = false + mergedSignals.startWithCompleted { completed = true } + + expect(completed) == false + + observer1.sendNext(1) + expect(completed) == false + + observer1.sendCompleted() + expect(completed) == false + + observer2.sendCompleted() + expect(completed) == true + } + } + + describe("SignalProducer.prefix()") { + it("should emit initial value") { + let (signal, observer) = SignalProducer.pipe() + + let mergedSignals = signal.prefix(value: 0) + + var lastValue: Int? + mergedSignals.startWithNext { lastValue = $0 } + + expect(lastValue) == 0 + + observer.sendNext(1) + expect(lastValue) == 1 + + observer.sendNext(2) + expect(lastValue) == 2 + + observer.sendNext(3) + expect(lastValue) == 3 + } + + it("should emit initial value") { + let (signal, observer) = SignalProducer.pipe() + + let mergedSignals = signal.prefix(SignalProducer(value: 0)) + + var lastValue: Int? + mergedSignals.startWithNext { lastValue = $0 } + + expect(lastValue) == 0 + + observer.sendNext(1) + expect(lastValue) == 1 + + observer.sendNext(2) + expect(lastValue) == 2 + + observer.sendNext(3) + expect(lastValue) == 3 + } + } + + describe("SignalProducer.concat(value:)") { + it("should emit final value") { + let (signal, observer) = SignalProducer.pipe() + + let mergedSignals = signal.concat(value: 4) + + var lastValue: Int? + mergedSignals.startWithNext { lastValue = $0 } + + observer.sendNext(1) + expect(lastValue) == 1 + + observer.sendNext(2) + expect(lastValue) == 2 + + observer.sendNext(3) + expect(lastValue) == 3 + + observer.sendCompleted() + expect(lastValue) == 4 + } + } + } +} diff --git a/ReactiveSwiftTests/FoundationExtensionsSpec.swift b/ReactiveSwiftTests/FoundationExtensionsSpec.swift new file mode 100644 index 0000000000..249eb44a8f --- /dev/null +++ b/ReactiveSwiftTests/FoundationExtensionsSpec.swift @@ -0,0 +1,59 @@ +// +// FoundationExtensionsSpec.swift +// ReactiveSwift +// +// Created by Neil Pankey on 5/22/15. +// Copyright (c) 2015 GitHub. All rights reserved. +// + +import Result +import Nimble +import Quick +import ReactiveSwift + +extension Notification.Name { + static let racFirst = Notification.Name(rawValue: "rac_notifications_test") + static let racAnother = Notification.Name(rawValue: "rac_notifications_another") +} + +class FoundationExtensionsSpec: QuickSpec { + override func spec() { + describe("NSNotificationCenter.rac_notifications") { + let center = NotificationCenter.default + + it("should send notifications on the producer") { + let producer = center.rac_notifications(forName: .racFirst) + + var notif: Notification? = nil + let disposable = producer.startWithNext { notif = $0 } + + center.post(name: .racAnother, object: nil) + expect(notif).to(beNil()) + + center.post(name: .racFirst, object: nil) + expect(notif?.name) == .racFirst + + notif = nil + disposable.dispose() + + center.post(name: .racFirst, object: nil) + expect(notif).to(beNil()) + } + + it("should send Interrupted when the observed object is freed") { + var observedObject: AnyObject? = NSObject() + let producer = center.rac_notifications(forName: nil, object: observedObject) + observedObject = nil + + var interrupted = false + let disposable = producer.startWithInterrupted { + interrupted = true + } + expect(interrupted) == true + + disposable.dispose() + } + + } + } +} diff --git a/ReactiveSwiftTests/Info.plist b/ReactiveSwiftTests/Info.plist new file mode 100644 index 0000000000..ba72822e87 --- /dev/null +++ b/ReactiveSwiftTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/ReactiveSwiftTests/LifetimeSpec.swift b/ReactiveSwiftTests/LifetimeSpec.swift new file mode 100644 index 0000000000..4a23a9c259 --- /dev/null +++ b/ReactiveSwiftTests/LifetimeSpec.swift @@ -0,0 +1,83 @@ +import Quick +import Nimble +import ReactiveSwift +import Result + +final class LifetimeSpec: QuickSpec { + override func spec() { + describe("SignalProducerProtocol.takeDuring") { + it("completes a signal when the lifetime ends") { + let (signal, observer) = Signal.pipe() + let object = MutableReference(TestObject()) + + let output = signal.take(during: object.value!.lifetime) + + var results: [Int] = [] + output.observeNext { results.append($0) } + + observer.sendNext(1) + observer.sendNext(2) + object.value = nil + observer.sendNext(3) + + expect(results) == [1, 2] + } + + it("completes a signal producer when the lifetime ends") { + let (producer, observer) = Signal.pipe() + let object = MutableReference(TestObject()) + + let output = producer.take(during: object.value!.lifetime) + + var results: [Int] = [] + output.observeNext { results.append($0) } + + observer.sendNext(1) + observer.sendNext(2) + object.value = nil + observer.sendNext(3) + + expect(results) == [1, 2] + } + } + + describe("NSObject") { + it("should complete its lifetime ended signal when the it deinitializes") { + let object = MutableReference(TestObject()) + + var isCompleted = false + + object.value!.lifetime.ended.observeCompleted { isCompleted = true } + expect(isCompleted) == false + + object.value = nil + expect(isCompleted) == true + } + + it("should complete its lifetime ended signal even if the lifetime object is being retained") { + let object = MutableReference(TestObject()) + let lifetime = object.value!.lifetime + + var isCompleted = false + + lifetime.ended.observeCompleted { isCompleted = true } + expect(isCompleted) == false + + object.value = nil + expect(isCompleted) == true + } + } + } +} + +private final class MutableReference { + var value: Value? + init(_ value: Value?) { + self.value = value + } +} + +private final class TestObject { + private let token = Lifetime.Token() + var lifetime: Lifetime { return Lifetime(token) } +} diff --git a/ReactiveSwiftTests/PropertySpec.swift b/ReactiveSwiftTests/PropertySpec.swift new file mode 100644 index 0000000000..e32c9126a6 --- /dev/null +++ b/ReactiveSwiftTests/PropertySpec.swift @@ -0,0 +1,1560 @@ +// +// PropertySpec.swift +// ReactiveSwift +// +// Created by Justin Spahr-Summers on 2015-01-23. +// Copyright (c) 2015 GitHub. All rights reserved. +// + +import Result +import Nimble +import Quick +import ReactiveSwift + +private let initialPropertyValue = "InitialValue" +private let subsequentPropertyValue = "SubsequentValue" +private let finalPropertyValue = "FinalValue" + +private let initialOtherPropertyValue = "InitialOtherValue" +private let subsequentOtherPropertyValue = "SubsequentOtherValue" +private let finalOtherPropertyValue = "FinalOtherValue" + +class PropertySpec: QuickSpec { + override func spec() { + describe("MutableProperty") { + it("should have the value given at initialization") { + let mutableProperty = MutableProperty(initialPropertyValue) + + expect(mutableProperty.value) == initialPropertyValue + } + + it("should yield a producer that sends the current value then all changes") { + let mutableProperty = MutableProperty(initialPropertyValue) + var sentValue: String? + + mutableProperty.producer.startWithNext { sentValue = $0 } + + expect(sentValue) == initialPropertyValue + + mutableProperty.value = subsequentPropertyValue + expect(sentValue) == subsequentPropertyValue + + mutableProperty.value = finalPropertyValue + expect(sentValue) == finalPropertyValue + } + + it("should yield a producer that sends the current value then all changes, even if the value actually remains unchanged") { + let mutableProperty = MutableProperty(initialPropertyValue) + var count = 0 + + mutableProperty.producer.startWithNext { _ in count = count + 1 } + + expect(count) == 1 + + mutableProperty.value = initialPropertyValue + expect(count) == 2 + + mutableProperty.value = initialPropertyValue + expect(count) == 3 + } + + it("should yield a signal that emits subsequent changes to the value") { + let mutableProperty = MutableProperty(initialPropertyValue) + var sentValue: String? + + mutableProperty.signal.observeNext { sentValue = $0 } + + expect(sentValue).to(beNil()) + + mutableProperty.value = subsequentPropertyValue + expect(sentValue) == subsequentPropertyValue + + mutableProperty.value = finalPropertyValue + expect(sentValue) == finalPropertyValue + } + + it("should yield a signal that emits subsequent changes to the value, even if the value actually remains unchanged") { + let mutableProperty = MutableProperty(initialPropertyValue) + var count = 0 + + mutableProperty.signal.observeNext { _ in count = count + 1 } + + expect(count) == 0 + + mutableProperty.value = initialPropertyValue + expect(count) == 1 + + mutableProperty.value = initialPropertyValue + expect(count) == 2 + } + + it("should complete its producer when deallocated") { + var mutableProperty: MutableProperty? = MutableProperty(initialPropertyValue) + var producerCompleted = false + + mutableProperty!.producer.startWithCompleted { producerCompleted = true } + + mutableProperty = nil + expect(producerCompleted) == true + } + + it("should complete its signal when deallocated") { + var mutableProperty: MutableProperty? = MutableProperty(initialPropertyValue) + var signalCompleted = false + + mutableProperty!.signal.observeCompleted { signalCompleted = true } + + mutableProperty = nil + expect(signalCompleted) == true + } + + it("should yield a producer which emits the latest value and complete even if the property is deallocated") { + var mutableProperty: MutableProperty? = MutableProperty(initialPropertyValue) + let producer = mutableProperty!.producer + + var producerCompleted = false + var hasUnanticipatedEvent = false + var latestValue = mutableProperty?.value + + mutableProperty!.value = subsequentPropertyValue + mutableProperty = nil + + producer.start { event in + switch event { + case let .next(value): + latestValue = value + case .completed: + producerCompleted = true + case .interrupted, .failed: + hasUnanticipatedEvent = true + } + } + + expect(hasUnanticipatedEvent) == false + expect(producerCompleted) == true + expect(latestValue) == subsequentPropertyValue + } + + it("should modify the value atomically") { + let property = MutableProperty(initialPropertyValue) + + property.modify { $0 = subsequentPropertyValue } + expect(property.value) == subsequentPropertyValue + } + + it("should modify the value atomically and subsquently send out a Next event with the new value") { + let property = MutableProperty(initialPropertyValue) + var value: String? + + property.producer.startWithNext { + value = $0 + } + + expect(value) == initialPropertyValue + + property.modify { $0 = subsequentPropertyValue } + expect(property.value) == subsequentPropertyValue + expect(value) == subsequentPropertyValue + } + + it("should swap the value atomically") { + let property = MutableProperty(initialPropertyValue) + + expect(property.swap(subsequentPropertyValue)) == initialPropertyValue + expect(property.value) == subsequentPropertyValue + } + + it("should swap the value atomically and subsquently send out a Next event with the new value") { + let property = MutableProperty(initialPropertyValue) + var value: String? + + property.producer.startWithNext { + value = $0 + } + + expect(value) == initialPropertyValue + expect(property.swap(subsequentPropertyValue)) == initialPropertyValue + + expect(property.value) == subsequentPropertyValue + expect(value) == subsequentPropertyValue + } + + it("should perform an action with the value") { + let property = MutableProperty(initialPropertyValue) + + let result: Bool = property.withValue { $0.isEmpty } + + expect(result) == false + expect(property.value) == initialPropertyValue + } + + it("should not deadlock on recursive value access") { + let (producer, observer) = SignalProducer.pipe() + let property = MutableProperty(0) + var value: Int? + + property <~ producer + property.producer.startWithNext { _ in + value = property.value + } + + observer.sendNext(10) + expect(value) == 10 + } + + it("should not deadlock on recursive value access with a closure") { + let (producer, observer) = SignalProducer.pipe() + let property = MutableProperty(0) + var value: Int? + + property <~ producer + property.producer.startWithNext { _ in + value = property.withValue { $0 + 1 } + } + + observer.sendNext(10) + expect(value) == 11 + } + + it("should not deadlock on recursive observation") { + let property = MutableProperty(0) + + var value: Int? + property.producer.startWithNext { _ in + property.producer.startWithNext { x in value = x } + } + + expect(value) == 0 + + property.value = 1 + expect(value) == 1 + } + + it("should not deadlock on recursive ABA observation") { + let propertyA = MutableProperty(0) + let propertyB = MutableProperty(0) + + var value: Int? + propertyA.producer.startWithNext { _ in + propertyB.producer.startWithNext { _ in + propertyA.producer.startWithNext { x in value = x } + } + } + + expect(value) == 0 + + propertyA.value = 1 + expect(value) == 1 + } + } + + describe("Property") { + describe("from a value") { + it("should have the value given at initialization") { + let constantProperty = Property(value: initialPropertyValue) + + expect(constantProperty.value) == initialPropertyValue + } + + it("should yield a signal that interrupts observers without emitting any value.") { + let constantProperty = Property(value: initialPropertyValue) + + var signalInterrupted = false + var hasUnexpectedEventsEmitted = false + + constantProperty.signal.observe { event in + switch event { + case .interrupted: + signalInterrupted = true + case .next, .failed, .completed: + hasUnexpectedEventsEmitted = true + } + } + + expect(signalInterrupted) == true + expect(hasUnexpectedEventsEmitted) == false + } + + it("should yield a producer that sends the current value then completes") { + let constantProperty = Property(value: initialPropertyValue) + + var sentValue: String? + var signalCompleted = false + + constantProperty.producer.start { event in + switch event { + case let .next(value): + sentValue = value + case .completed: + signalCompleted = true + case .failed, .interrupted: + break + } + } + + expect(sentValue) == initialPropertyValue + expect(signalCompleted) == true + } + } + + describe("from a PropertyProtocol") { + it("should pass through behaviors of the input property") { + let constantProperty = Property(value: initialPropertyValue) + let property = Property(constantProperty) + + var sentValue: String? + var signalSentValue: String? + var producerCompleted = false + var signalInterrupted = false + + property.producer.start { event in + switch event { + case let .next(value): + sentValue = value + case .completed: + producerCompleted = true + case .failed, .interrupted: + break + } + } + + property.signal.observe { event in + switch event { + case let .next(value): + signalSentValue = value + case .interrupted: + signalInterrupted = true + case .failed, .completed: + break + } + } + + expect(sentValue) == initialPropertyValue + expect(signalSentValue).to(beNil()) + expect(producerCompleted) == true + expect(signalInterrupted) == true + } + + describe("composed properties") { + it("should have the latest value available before sending any value") { + var latestValue: Int! + + let property = MutableProperty(1) + let mappedProperty = property.map { $0 + 1 } + mappedProperty.producer.startWithNext { _ in latestValue = mappedProperty.value } + + expect(latestValue) == 2 + + property.value = 2 + expect(latestValue) == 3 + + property.value = 3 + expect(latestValue) == 4 + } + + it("should retain its source property") { + var property = Optional(MutableProperty(1)) + weak var weakProperty = property + + var firstMappedProperty = Optional(property!.map { $0 + 1 }) + var secondMappedProperty = Optional(firstMappedProperty!.map { $0 + 2 }) + + // Suppress the "written to but never read" warning on `secondMappedProperty`. + _ = secondMappedProperty + + property = nil + expect(weakProperty).toNot(beNil()) + + firstMappedProperty = nil + expect(weakProperty).toNot(beNil()) + + secondMappedProperty = nil + expect(weakProperty).to(beNil()) + } + + it("should transform property from a property that has a terminated producer") { + let property = Property(value: 1) + let transformedProperty = property.map { $0 + 1 } + + expect(transformedProperty.value) == 2 + } + + describe("signal lifetime and producer lifetime") { + it("should return a producer and a signal which respect the lifetime of the source property instead of the read-only view itself") { + var signalCompleted = 0 + var producerCompleted = 0 + + var property = Optional(MutableProperty(1)) + var firstMappedProperty = Optional(property!.map { $0 + 1 }) + var secondMappedProperty = Optional(firstMappedProperty!.map { $0 + 2 }) + var thirdMappedProperty = Optional(secondMappedProperty!.map { $0 + 2 }) + + firstMappedProperty!.signal.observeCompleted { signalCompleted += 1 } + secondMappedProperty!.signal.observeCompleted { signalCompleted += 1 } + thirdMappedProperty!.signal.observeCompleted { signalCompleted += 1 } + + firstMappedProperty!.producer.startWithCompleted { producerCompleted += 1 } + secondMappedProperty!.producer.startWithCompleted { producerCompleted += 1 } + thirdMappedProperty!.producer.startWithCompleted { producerCompleted += 1 } + + firstMappedProperty = nil + expect(signalCompleted) == 0 + expect(producerCompleted) == 0 + + secondMappedProperty = nil + expect(signalCompleted) == 0 + expect(producerCompleted) == 0 + + property = nil + expect(signalCompleted) == 0 + expect(producerCompleted) == 0 + + thirdMappedProperty = nil + expect(signalCompleted) == 3 + expect(producerCompleted) == 3 + } + } + } + + describe("Property.capture") { + it("should not capture intermediate properties but only the ultimate sources") { + func increment(input: Int) -> Int { + return input + 1 + } + + weak var weakSourceProperty: MutableProperty? + weak var weakPropertyA: Property? + weak var weakPropertyB: Property? + weak var weakPropertyC: Property? + + var finalProperty: Property! + + func scope() { + let property = MutableProperty(1) + weakSourceProperty = property + + let propertyA = property.map(increment) + weakPropertyA = propertyA + + let propertyB = propertyA.map(increment) + weakPropertyB = propertyB + + let propertyC = propertyB.map(increment) + weakPropertyC = propertyC + + finalProperty = propertyC.map(increment) + } + + scope() + + expect(finalProperty.value) == 5 + expect(weakSourceProperty).toNot(beNil()) + expect(weakPropertyA).to(beNil()) + expect(weakPropertyB).to(beNil()) + expect(weakPropertyC).to(beNil()) + } + } + } + + describe("from a value and SignalProducer") { + it("should initially take on the supplied value") { + let property = Property(initial: initialPropertyValue, + then: SignalProducer.never) + + expect(property.value) == initialPropertyValue + } + + it("should take on each value sent on the producer") { + let property = Property(initial: initialPropertyValue, + then: SignalProducer(value: subsequentPropertyValue)) + + expect(property.value) == subsequentPropertyValue + } + + it("should return a producer and a signal that respect the lifetime of its ultimate source") { + var signalCompleted = false + var producerCompleted = false + var signalInterrupted = false + + let (signal, observer) = Signal.pipe() + var property: Property? = Property(initial: 1, + then: SignalProducer(signal: signal)) + let propertySignal = property!.signal + + propertySignal.observeCompleted { signalCompleted = true } + property!.producer.startWithCompleted { producerCompleted = true } + + expect(property!.value) == 1 + + observer.sendNext(2) + expect(property!.value) == 2 + expect(producerCompleted) == false + expect(signalCompleted) == false + + property = nil + expect(producerCompleted) == false + expect(signalCompleted) == false + + observer.sendCompleted() + expect(producerCompleted) == true + expect(signalCompleted) == true + + propertySignal.observeInterrupted { signalInterrupted = true } + expect(signalInterrupted) == true + } + } + + describe("from a value and Signal") { + it("should initially take on the supplied value, then values sent on the signal") { + let (signal, observer) = Signal.pipe() + + let property = Property(initial: initialPropertyValue, + then: signal) + + expect(property.value) == initialPropertyValue + + observer.sendNext(subsequentPropertyValue) + + expect(property.value) == subsequentPropertyValue + } + + + it("should return a producer and a signal that respect the lifetime of its ultimate source") { + var signalCompleted = false + var producerCompleted = false + var signalInterrupted = false + + let (signal, observer) = Signal.pipe() + var property: Property? = Property(initial: 1, + then: signal) + let propertySignal = property!.signal + + propertySignal.observeCompleted { signalCompleted = true } + property!.producer.startWithCompleted { producerCompleted = true } + + expect(property!.value) == 1 + + observer.sendNext(2) + expect(property!.value) == 2 + expect(producerCompleted) == false + expect(signalCompleted) == false + + property = nil + expect(producerCompleted) == false + expect(signalCompleted) == false + + observer.sendCompleted() + expect(producerCompleted) == true + expect(signalCompleted) == true + + propertySignal.observeInterrupted { signalInterrupted = true } + expect(signalInterrupted) == true + } + } + } + + describe("PropertyProtocol") { + describe("map") { + it("should transform the current value and all subsequent values") { + let property = MutableProperty(1) + let mappedProperty = property + .map { $0 + 1 } + expect(mappedProperty.value) == 2 + + property.value = 2 + expect(mappedProperty.value) == 3 + } + } + + describe("combineLatest") { + var property: MutableProperty! + var otherProperty: MutableProperty! + + beforeEach { + property = MutableProperty(initialPropertyValue) + otherProperty = MutableProperty(initialOtherPropertyValue) + } + + it("should forward the latest values from both inputs") { + let combinedProperty = property.combineLatest(with: otherProperty) + var latest: (String, String)? + combinedProperty.signal.observeNext { latest = $0 } + + property.value = subsequentPropertyValue + expect(latest?.0) == subsequentPropertyValue + expect(latest?.1) == initialOtherPropertyValue + + // is there a better way to test tuples? + otherProperty.value = subsequentOtherPropertyValue + expect(latest?.0) == subsequentPropertyValue + expect(latest?.1) == subsequentOtherPropertyValue + + property.value = finalPropertyValue + expect(latest?.0) == finalPropertyValue + expect(latest?.1) == subsequentOtherPropertyValue + } + + it("should complete when the source properties are deinitialized") { + var completed = false + + var combinedProperty = Optional(property.combineLatest(with: otherProperty)) + combinedProperty!.signal.observeCompleted { completed = true } + + combinedProperty = nil + expect(completed) == false + + property = nil + expect(completed) == false + + otherProperty = nil + expect(completed) == true + } + + it("should be consistent between its cached value and its values producer") { + var firstResult: String! + var secondResult: String! + + let combined = property.combineLatest(with: otherProperty) + combined.producer.startWithNext { (left, right) in firstResult = left + right } + + func getValue() -> String { + return combined.value.0 + combined.value.1 + } + + expect(getValue()) == initialPropertyValue + initialOtherPropertyValue + expect(firstResult) == initialPropertyValue + initialOtherPropertyValue + + property.value = subsequentPropertyValue + expect(getValue()) == subsequentPropertyValue + initialOtherPropertyValue + expect(firstResult) == subsequentPropertyValue + initialOtherPropertyValue + + combined.producer.startWithNext { (left, right) in secondResult = left + right } + expect(secondResult) == subsequentPropertyValue + initialOtherPropertyValue + + otherProperty.value = subsequentOtherPropertyValue + expect(getValue()) == subsequentPropertyValue + subsequentOtherPropertyValue + expect(firstResult) == subsequentPropertyValue + subsequentOtherPropertyValue + expect(secondResult) == subsequentPropertyValue + subsequentOtherPropertyValue + } + + it("should be consistent between nested combined properties") { + let A = MutableProperty(1) + let B = MutableProperty(100) + let C = MutableProperty(10000) + + var firstResult: Int! + + let combined = A.combineLatest(with: B) + combined.producer.startWithNext { (left, right) in firstResult = left + right } + + func getValue() -> Int { + return combined.value.0 + combined.value.1 + } + + /// Initial states + expect(getValue()) == 101 + expect(firstResult) == 101 + + A.value = 2 + expect(getValue()) == 102 + expect(firstResult) == 102 + + B.value = 200 + expect(getValue()) == 202 + expect(firstResult) == 202 + + /// Setup + A.value = 3 + expect(getValue()) == 203 + expect(firstResult) == 203 + + /// Zip another property now. + var secondResult: Int! + let anotherCombined = combined.combineLatest(with: C) + anotherCombined.producer.startWithNext { (left, right) in secondResult = (left.0 + left.1) + right } + + func getAnotherValue() -> Int { + return (anotherCombined.value.0.0 + anotherCombined.value.0.1) + anotherCombined.value.1 + } + + expect(getAnotherValue()) == 10203 + + A.value = 4 + expect(getValue()) == 204 + expect(getAnotherValue()) == 10204 + } + } + + describe("zip") { + var property: MutableProperty! + var otherProperty: MutableProperty! + + beforeEach { + property = MutableProperty(initialPropertyValue) + otherProperty = MutableProperty(initialOtherPropertyValue) + } + + it("should combine pairs") { + var result: [String] = [] + + let zippedProperty = property.zip(with: otherProperty) + zippedProperty.producer.startWithNext { (left, right) in result.append("\(left)\(right)") } + + let firstResult = [ "\(initialPropertyValue)\(initialOtherPropertyValue)" ] + let secondResult = firstResult + [ "\(subsequentPropertyValue)\(subsequentOtherPropertyValue)" ] + let thirdResult = secondResult + [ "\(finalPropertyValue)\(finalOtherPropertyValue)" ] + let finalResult = thirdResult + [ "\(initialPropertyValue)\(initialOtherPropertyValue)" ] + + expect(result) == firstResult + + property.value = subsequentPropertyValue + expect(result) == firstResult + + otherProperty.value = subsequentOtherPropertyValue + expect(result) == secondResult + + property.value = finalPropertyValue + otherProperty.value = finalOtherPropertyValue + expect(result) == thirdResult + + property.value = initialPropertyValue + expect(result) == thirdResult + + property.value = subsequentPropertyValue + expect(result) == thirdResult + + otherProperty.value = initialOtherPropertyValue + expect(result) == finalResult + } + + it("should be consistent between its cached value and its values producer") { + var firstResult: String! + var secondResult: String! + + let zippedProperty = property.zip(with: otherProperty) + zippedProperty.producer.startWithNext { (left, right) in firstResult = left + right } + + func getValue() -> String { + return zippedProperty.value.0 + zippedProperty.value.1 + } + + expect(getValue()) == initialPropertyValue + initialOtherPropertyValue + expect(firstResult) == initialPropertyValue + initialOtherPropertyValue + + property.value = subsequentPropertyValue + expect(getValue()) == initialPropertyValue + initialOtherPropertyValue + expect(firstResult) == initialPropertyValue + initialOtherPropertyValue + + // It should still be the tuple with initial property values, + // since `otherProperty` isn't changed yet. + zippedProperty.producer.startWithNext { (left, right) in secondResult = left + right } + expect(secondResult) == initialPropertyValue + initialOtherPropertyValue + + otherProperty.value = subsequentOtherPropertyValue + expect(getValue()) == subsequentPropertyValue + subsequentOtherPropertyValue + expect(firstResult) == subsequentPropertyValue + subsequentOtherPropertyValue + expect(secondResult) == subsequentPropertyValue + subsequentOtherPropertyValue + } + + it("should be consistent between nested zipped properties") { + let A = MutableProperty(1) + let B = MutableProperty(100) + let C = MutableProperty(10000) + + var firstResult: Int! + + let zipped = A.zip(with: B) + zipped.producer.startWithNext { (left, right) in firstResult = left + right } + + func getValue() -> Int { + return zipped.value.0 + zipped.value.1 + } + + /// Initial states + expect(getValue()) == 101 + expect(firstResult) == 101 + + A.value = 2 + expect(getValue()) == 101 + expect(firstResult) == 101 + + B.value = 200 + expect(getValue()) == 202 + expect(firstResult) == 202 + + /// Setup + A.value = 3 + expect(getValue()) == 202 + expect(firstResult) == 202 + + /// Zip another property now. + var secondResult: Int! + let anotherZipped = zipped.zip(with: C) + anotherZipped.producer.startWithNext { (left, right) in secondResult = (left.0 + left.1) + right } + + func getAnotherValue() -> Int { + return (anotherZipped.value.0.0 + anotherZipped.value.0.1) + anotherZipped.value.1 + } + + /// Since `zipped` is 202 now, and `C` is 10000, + /// shouldn't this be 10202? + + /// Verify `zipped` again. + expect(getValue()) == 202 + expect(firstResult) == 202 + + /// Then... well, no. Surprise! (Only before #3042) + /// We get 10203 here. + /// + /// https://github.com/ReactiveCocoa/ReactiveCocoa/pull/3042 + expect(getAnotherValue()) == 10202 + } + + it("should be consistent between combined and nested zipped properties") { + let A = MutableProperty(1) + let B = MutableProperty(100) + let C = MutableProperty(10000) + let D = MutableProperty(1000000) + + var firstResult: Int! + + let zipped = A.zip(with: B) + zipped.producer.startWithNext { (left, right) in firstResult = left + right } + + func getValue() -> Int { + return zipped.value.0 + zipped.value.1 + } + + /// Initial states + expect(getValue()) == 101 + expect(firstResult) == 101 + + A.value = 2 + expect(getValue()) == 101 + expect(firstResult) == 101 + + B.value = 200 + expect(getValue()) == 202 + expect(firstResult) == 202 + + /// Setup + A.value = 3 + expect(getValue()) == 202 + expect(firstResult) == 202 + + /// Zip another property now. + var secondResult: Int! + let anotherZipped = zipped.zip(with: C) + anotherZipped.producer.startWithNext { (left, right) in secondResult = (left.0 + left.1) + right } + + func getAnotherValue() -> Int { + return (anotherZipped.value.0.0 + anotherZipped.value.0.1) + anotherZipped.value.1 + } + + /// Verify `zipped` again. + expect(getValue()) == 202 + expect(firstResult) == 202 + + expect(getAnotherValue()) == 10202 + + /// Zip `D` with `anotherZipped`. + let yetAnotherZipped = anotherZipped.zip(with: D) + + /// Combine with another property. + /// (((Int, Int), Int), (((Int, Int), Int), Int)) + let combined = anotherZipped.combineLatest(with: yetAnotherZipped) + + var thirdResult: Int! + combined.producer.startWithNext { (left, right) in + let leftResult = left.0.0 + left.0.1 + left.1 + let rightResult = right.0.0.0 + right.0.0.1 + right.0.1 + right.1 + thirdResult = leftResult + rightResult + } + + expect(thirdResult) == 1020404 + } + + it("should complete its producer only when the source properties are deinitialized") { + var result: [String] = [] + var completed = false + + var zippedProperty = Optional(property.zip(with: otherProperty)) + zippedProperty!.producer.start { event in + switch event { + case let .next(left, right): + result.append("\(left)\(right)") + case .completed: + completed = true + default: + break + } + } + + expect(completed) == false + expect(result) == [ "\(initialPropertyValue)\(initialOtherPropertyValue)" ] + + property.value = subsequentPropertyValue + expect(result) == [ "\(initialPropertyValue)\(initialOtherPropertyValue)" ] + + zippedProperty = nil + expect(completed) == false + + property = nil + otherProperty = nil + expect(completed) == true + } + } + + describe("unary operators") { + var property: MutableProperty! + + beforeEach { + property = MutableProperty(initialPropertyValue) + } + + describe("combinePrevious") { + it("should pack the current value and the previous value a tuple") { + let transformedProperty = property.combinePrevious(initialPropertyValue) + + expect(transformedProperty.value.0) == initialPropertyValue + expect(transformedProperty.value.1) == initialPropertyValue + + property.value = subsequentPropertyValue + + expect(transformedProperty.value.0) == initialPropertyValue + expect(transformedProperty.value.1) == subsequentPropertyValue + + property.value = finalPropertyValue + + expect(transformedProperty.value.0) == subsequentPropertyValue + expect(transformedProperty.value.1) == finalPropertyValue + } + + it("should complete its producer only when the source property is deinitialized") { + var result: (String, String)? + var completed = false + + var transformedProperty = Optional(property.combinePrevious(initialPropertyValue)) + transformedProperty!.producer.start { event in + switch event { + case let .next(tuple): + result = tuple + case .completed: + completed = true + default: + break + } + } + + expect(result?.0) == initialPropertyValue + expect(result?.1) == initialPropertyValue + + property.value = subsequentPropertyValue + + expect(result?.0) == initialPropertyValue + expect(result?.1) == subsequentPropertyValue + + transformedProperty = nil + expect(completed) == false + + property = nil + expect(completed) == true + } + } + + describe("skipRepeats") { + it("should not emit events for subsequent equatable values that are the same as the current value") { + let transformedProperty = property.skipRepeats() + + var counter = 0 + transformedProperty.signal.observeNext { _ in + counter += 1 + } + + property.value = initialPropertyValue + property.value = initialPropertyValue + property.value = initialPropertyValue + + expect(counter) == 0 + + property.value = subsequentPropertyValue + property.value = subsequentPropertyValue + property.value = subsequentPropertyValue + + expect(counter) == 1 + + property.value = finalPropertyValue + property.value = initialPropertyValue + property.value = subsequentPropertyValue + + expect(counter) == 4 + } + + it("should not emit events for subsequent values that are regarded as the same as the current value by the supplied closure") { + var counter = 0 + let transformedProperty = property.skipRepeats { _, newValue in newValue == initialPropertyValue } + + transformedProperty.signal.observeNext { _ in + counter += 1 + } + + property.value = initialPropertyValue + expect(counter) == 0 + + property.value = subsequentPropertyValue + expect(counter) == 1 + + property.value = finalPropertyValue + expect(counter) == 2 + + property.value = initialPropertyValue + expect(counter) == 2 + } + + it("should complete its producer only when the source property is deinitialized") { + var counter = 0 + var completed = false + + var transformedProperty = Optional(property.skipRepeats()) + transformedProperty!.producer.start { event in + switch event { + case .next: + counter += 1 + case .completed: + completed = true + default: + break + } + } + + expect(counter) == 1 + + property.value = initialPropertyValue + expect(counter) == 1 + + transformedProperty = nil + expect(completed) == false + + property = nil + expect(completed) == true + } + } + + describe("uniqueValues") { + it("should emit hashable values that have not been emited before") { + let transformedProperty = property.uniqueValues() + + var counter = 0 + transformedProperty.signal.observeNext { _ in + counter += 1 + } + + property.value = initialPropertyValue + expect(counter) == 0 + + property.value = subsequentPropertyValue + property.value = subsequentPropertyValue + + expect(counter) == 1 + + property.value = finalPropertyValue + property.value = initialPropertyValue + property.value = subsequentPropertyValue + + expect(counter) == 2 + } + + it("should emit only the values of which the computed identity have not been captured before") { + let transformedProperty = property.uniqueValues { _ in 0 } + + var counter = 0 + transformedProperty.signal.observeNext { _ in + counter += 1 + } + + property.value = initialPropertyValue + property.value = subsequentPropertyValue + property.value = finalPropertyValue + expect(counter) == 0 + } + + it("should complete its producer only when the source property is deinitialized") { + var counter = 0 + var completed = false + + var transformedProperty = Optional(property.uniqueValues()) + transformedProperty!.producer.start { event in + switch event { + case .next: + counter += 1 + case .completed: + completed = true + default: + break + } + } + + expect(counter) == 1 + + property.value = initialPropertyValue + expect(counter) == 1 + + transformedProperty = nil + expect(completed) == false + + property = nil + expect(completed) == true + } + } + } + + describe("flattening") { + describe("flatten") { + describe("FlattenStrategy.concat") { + it("should concatenate the values as the inner property is replaced and deinitialized") { + var firstProperty = Optional(MutableProperty(0)) + var secondProperty = Optional(MutableProperty(10)) + var thirdProperty = Optional(MutableProperty(20)) + + var outerProperty = Optional(MutableProperty(firstProperty!)) + + var receivedValues: [Int] = [] + var errored = false + var completed = false + + var flattenedProperty = Optional(outerProperty!.flatten(.concat)) + + flattenedProperty!.producer.start { event in + switch event { + case let .next(value): + receivedValues.append(value) + case .completed: + completed = true + case .failed: + errored = true + case .interrupted: + break + } + } + + expect(receivedValues) == [ 0 ] + + outerProperty!.value = secondProperty! + secondProperty!.value = 11 + outerProperty!.value = thirdProperty! + thirdProperty!.value = 21 + + expect(receivedValues) == [ 0 ] + expect(completed) == false + + secondProperty!.value = 12 + thirdProperty!.value = 22 + + expect(receivedValues) == [ 0 ] + expect(completed) == false + + firstProperty = nil + + expect(receivedValues) == [ 0, 12 ] + expect(completed) == false + + secondProperty = nil + + expect(receivedValues) == [ 0, 12, 22 ] + expect(completed) == false + + outerProperty = nil + expect(completed) == false + + thirdProperty = nil + expect(completed) == false + + flattenedProperty = nil + expect(completed) == true + expect(errored) == false + } + } + + describe("FlattenStrategy.merge") { + it("should merge the values of all inner properties") { + var firstProperty = Optional(MutableProperty(0)) + var secondProperty = Optional(MutableProperty(10)) + var thirdProperty = Optional(MutableProperty(20)) + + var outerProperty = Optional(MutableProperty(firstProperty!)) + + var receivedValues: [Int] = [] + var errored = false + var completed = false + + var flattenedProperty = Optional(outerProperty!.flatten(.merge)) + + flattenedProperty!.producer.start { event in + switch event { + case let .next(value): + receivedValues.append(value) + case .completed: + completed = true + case .failed: + errored = true + case .interrupted: + break + } + } + + expect(receivedValues) == [ 0 ] + + outerProperty!.value = secondProperty! + secondProperty!.value = 11 + outerProperty!.value = thirdProperty! + thirdProperty!.value = 21 + + expect(receivedValues) == [ 0, 10, 11, 20, 21 ] + expect(completed) == false + + secondProperty!.value = 12 + thirdProperty!.value = 22 + + expect(receivedValues) == [ 0, 10, 11, 20, 21, 12, 22 ] + expect(completed) == false + + firstProperty = nil + + expect(receivedValues) == [ 0, 10, 11, 20, 21, 12, 22 ] + expect(completed) == false + + secondProperty = nil + + expect(receivedValues) == [ 0, 10, 11, 20, 21, 12, 22 ] + expect(completed) == false + + outerProperty = nil + expect(completed) == false + + thirdProperty = nil + expect(completed) == false + + flattenedProperty = nil + expect(completed) == true + expect(errored) == false + } + } + + describe("FlattenStrategy.latest") { + it("should forward values from the latest inner property") { + let firstProperty = Optional(MutableProperty(0)) + var secondProperty = Optional(MutableProperty(10)) + var thirdProperty = Optional(MutableProperty(20)) + + var outerProperty = Optional(MutableProperty(firstProperty!)) + + var receivedValues: [Int] = [] + var errored = false + var completed = false + + outerProperty!.flatten(.latest).producer.start { event in + switch event { + case let .next(value): + receivedValues.append(value) + case .completed: + completed = true + case .failed: + errored = true + case .interrupted: + break + } + } + + expect(receivedValues) == [ 0 ] + + outerProperty!.value = secondProperty! + secondProperty!.value = 11 + outerProperty!.value = thirdProperty! + thirdProperty!.value = 21 + + expect(receivedValues) == [ 0, 10, 11, 20, 21 ] + expect(errored) == false + expect(completed) == false + + secondProperty!.value = 12 + secondProperty = nil + thirdProperty!.value = 22 + thirdProperty = nil + + expect(receivedValues) == [ 0, 10, 11, 20, 21, 22 ] + expect(errored) == false + expect(completed) == false + + outerProperty = nil + expect(errored) == false + expect(completed) == true + } + + it("should release the old properties when switched or deallocated") { + var firstProperty = Optional(MutableProperty(0)) + var secondProperty = Optional(MutableProperty(10)) + var thirdProperty = Optional(MutableProperty(20)) + + weak var weakFirstProperty = firstProperty + weak var weakSecondProperty = secondProperty + weak var weakThirdProperty = thirdProperty + + var outerProperty = Optional(MutableProperty(firstProperty!)) + var flattened = Optional(outerProperty!.flatten(.latest)) + + var errored = false + var completed = false + + flattened!.producer.start { event in + switch event { + case .completed: + completed = true + case .failed: + errored = true + case .interrupted, .next: + break + } + } + + firstProperty = nil + outerProperty!.value = secondProperty! + expect(weakFirstProperty).to(beNil()) + + secondProperty = nil + outerProperty!.value = thirdProperty! + expect(weakSecondProperty).to(beNil()) + + thirdProperty = nil + outerProperty = nil + flattened = nil + expect(weakThirdProperty).to(beNil()) + expect(errored) == false + expect(completed) == true + } + } + } + + describe("flatMap") { + describe("PropertyFlattenStrategy.latest") { + it("should forward values from the latest inner transformed property") { + let firstProperty = Optional(MutableProperty(0)) + var secondProperty = Optional(MutableProperty(10)) + var thirdProperty = Optional(MutableProperty(20)) + + var outerProperty = Optional(MutableProperty(firstProperty!)) + + var receivedValues: [String] = [] + var errored = false + var completed = false + + outerProperty!.flatMap(.latest) { $0.map { "\($0)" } }.producer.start { event in + switch event { + case let .next(value): + receivedValues.append(value) + case .completed: + completed = true + case .failed: + errored = true + case .interrupted: + break + } + } + + expect(receivedValues) == [ "0" ] + + outerProperty!.value = secondProperty! + secondProperty!.value = 11 + outerProperty!.value = thirdProperty! + thirdProperty!.value = 21 + + expect(receivedValues) == [ "0", "10", "11", "20", "21" ] + expect(errored) == false + expect(completed) == false + + secondProperty!.value = 12 + secondProperty = nil + thirdProperty!.value = 22 + thirdProperty = nil + + expect(receivedValues) == [ "0", "10", "11", "20", "21", "22" ] + expect(errored) == false + expect(completed) == false + + outerProperty = nil + expect(errored) == false + expect(completed) == true + } + } + } + } + } + + describe("binding") { + describe("from a Signal") { + it("should update the property with values sent from the signal") { + let (signal, observer) = Signal.pipe() + + let mutableProperty = MutableProperty(initialPropertyValue) + + mutableProperty <~ signal + + // Verify that the binding hasn't changed the property value: + expect(mutableProperty.value) == initialPropertyValue + + observer.sendNext(subsequentPropertyValue) + expect(mutableProperty.value) == subsequentPropertyValue + } + + it("should tear down the binding when disposed") { + let (signal, observer) = Signal.pipe() + + let mutableProperty = MutableProperty(initialPropertyValue) + + let bindingDisposable = mutableProperty <~ signal + bindingDisposable.dispose() + + observer.sendNext(subsequentPropertyValue) + expect(mutableProperty.value) == initialPropertyValue + } + + it("should tear down the binding when bound signal is completed") { + let (signal, observer) = Signal.pipe() + + let mutableProperty = MutableProperty(initialPropertyValue) + + let bindingDisposable = mutableProperty <~ signal + + expect(bindingDisposable.isDisposed) == false + observer.sendCompleted() + expect(bindingDisposable.isDisposed) == true + } + + it("should tear down the binding when the property deallocates") { + let (signal, _) = Signal.pipe() + + var mutableProperty: MutableProperty? = MutableProperty(initialPropertyValue) + + let bindingDisposable = mutableProperty! <~ signal + + mutableProperty = nil + expect(bindingDisposable.isDisposed) == true + } + } + + describe("from a SignalProducer") { + it("should start a signal and update the property with its values") { + let signalValues = [initialPropertyValue, subsequentPropertyValue] + let signalProducer = SignalProducer(values: signalValues) + + let mutableProperty = MutableProperty(initialPropertyValue) + + mutableProperty <~ signalProducer + + expect(mutableProperty.value) == signalValues.last! + } + + it("should tear down the binding when disposed") { + let (signalProducer, observer) = SignalProducer.pipe() + + let mutableProperty = MutableProperty(initialPropertyValue) + let disposable = mutableProperty <~ signalProducer + + disposable.dispose() + + observer.sendNext(subsequentPropertyValue) + expect(mutableProperty.value) == initialPropertyValue + } + + it("should tear down the binding when bound signal is completed") { + let (signalProducer, observer) = SignalProducer.pipe() + + let mutableProperty = MutableProperty(initialPropertyValue) + let disposable = mutableProperty <~ signalProducer + + observer.sendCompleted() + + expect(disposable.isDisposed) == true + } + + it("should tear down the binding when the property deallocates") { + let signalValues = [initialPropertyValue, subsequentPropertyValue] + let signalProducer = SignalProducer(values: signalValues) + + var mutableProperty: MutableProperty? = MutableProperty(initialPropertyValue) + let disposable = mutableProperty! <~ signalProducer + + mutableProperty = nil + expect(disposable.isDisposed) == true + } + } + + describe("from another property") { + it("should take the source property's current value") { + let sourceProperty = Property(value: initialPropertyValue) + + let destinationProperty = MutableProperty("") + + destinationProperty <~ sourceProperty.producer + + expect(destinationProperty.value) == initialPropertyValue + } + + it("should update with changes to the source property's value") { + let sourceProperty = MutableProperty(initialPropertyValue) + + let destinationProperty = MutableProperty("") + + destinationProperty <~ sourceProperty.producer + + sourceProperty.value = subsequentPropertyValue + expect(destinationProperty.value) == subsequentPropertyValue + } + + it("should tear down the binding when disposed") { + let sourceProperty = MutableProperty(initialPropertyValue) + + let destinationProperty = MutableProperty("") + + let bindingDisposable = destinationProperty <~ sourceProperty.producer + bindingDisposable.dispose() + + sourceProperty.value = subsequentPropertyValue + + expect(destinationProperty.value) == initialPropertyValue + } + + it("should tear down the binding when the source property deallocates") { + var sourceProperty: MutableProperty? = MutableProperty(initialPropertyValue) + + let destinationProperty = MutableProperty("") + destinationProperty <~ sourceProperty!.producer + + sourceProperty = nil + // TODO: Assert binding was torn down? + } + + it("should tear down the binding when the destination property deallocates") { + let sourceProperty = MutableProperty(initialPropertyValue) + var destinationProperty: MutableProperty? = MutableProperty("") + + let bindingDisposable = destinationProperty! <~ sourceProperty.producer + destinationProperty = nil + + expect(bindingDisposable.isDisposed) == true + } + } + } + } +} + +private class ObservableObject: NSObject { + dynamic var rac_value: Int = 0 + dynamic var rac_reference: UnbridgedObject = UnbridgedObject("") +} + +private class UnbridgedObject: NSObject { + let value: String + init(_ value: String) { + self.value = value + } +} diff --git a/ReactiveSwiftTests/SchedulerSpec.swift b/ReactiveSwiftTests/SchedulerSpec.swift new file mode 100644 index 0000000000..9daba6d9f0 --- /dev/null +++ b/ReactiveSwiftTests/SchedulerSpec.swift @@ -0,0 +1,298 @@ +// +// SchedulerSpec.swift +// ReactiveSwift +// +// Created by Justin Spahr-Summers on 2014-07-13. +// Copyright (c) 2014 GitHub. All rights reserved. +// + +import Foundation +import Nimble +import Quick +@testable +import ReactiveSwift + +class SchedulerSpec: QuickSpec { + override func spec() { + describe("ImmediateScheduler") { + it("should run enqueued actions immediately") { + var didRun = false + ImmediateScheduler().schedule { + didRun = true + } + + expect(didRun) == true + } + } + + describe("UIScheduler") { + func dispatchSyncInBackground(_ action: () -> Void) { + let group = DispatchGroup() + + let globalQueue: DispatchQueue + if #available(*, OSX 10.10) { + globalQueue = DispatchQueue.global() + } else { + globalQueue = DispatchQueue.global(priority: .default) + } + + globalQueue.async(group: group, execute: action) + group.wait() + } + + it("should run actions immediately when on the main thread") { + let scheduler = UIScheduler() + var values: [Int] = [] + expect(Thread.isMainThread) == true + + scheduler.schedule { + values.append(0) + } + + expect(values) == [ 0 ] + + scheduler.schedule { + values.append(1) + } + + scheduler.schedule { + values.append(2) + } + + expect(values) == [ 0, 1, 2 ] + } + + it("should enqueue actions scheduled from the background") { + let scheduler = UIScheduler() + var values: [Int] = [] + + dispatchSyncInBackground { + scheduler.schedule { + expect(Thread.isMainThread) == true + values.append(0) + } + + return + } + + expect(values) == [] + expect(values).toEventually(equal([ 0 ])) + + dispatchSyncInBackground { + scheduler.schedule { + expect(Thread.isMainThread) == true + values.append(1) + } + + scheduler.schedule { + expect(Thread.isMainThread) == true + values.append(2) + } + + return + } + + expect(values) == [ 0 ] + expect(values).toEventually(equal([ 0, 1, 2 ])) + } + + it("should run actions enqueued from the main thread after those from the background") { + let scheduler = UIScheduler() + var values: [Int] = [] + + dispatchSyncInBackground { + scheduler.schedule { + expect(Thread.isMainThread) == true + values.append(0) + } + + return + } + + scheduler.schedule { + expect(Thread.isMainThread) == true + values.append(1) + } + + scheduler.schedule { + expect(Thread.isMainThread) == true + values.append(2) + } + + expect(values) == [] + expect(values).toEventually(equal([ 0, 1, 2 ])) + } + } + + describe("QueueScheduler") { + it("should run enqueued actions on a global queue") { + var didRun = false + + let scheduler: QueueScheduler + if #available(OSX 10.10, *) { + scheduler = QueueScheduler(qos: .default, name: "\(#file):\(#line)") + } else { + scheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) + } + + scheduler.schedule { + didRun = true + expect(Thread.isMainThread) == false + } + + expect{didRun}.toEventually(beTruthy()) + } + + describe("on a given queue") { + var scheduler: QueueScheduler! + + beforeEach { + if #available(OSX 10.10, *) { + scheduler = QueueScheduler(qos: .default, name: "\(#file):\(#line)") + } else { + scheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) + } + scheduler.queue.suspend() + } + + it("should run enqueued actions serially on the given queue") { + var value = 0 + + for _ in 0..<5 { + scheduler.schedule { + expect(Thread.isMainThread) == false + value += 1 + } + } + + expect(value) == 0 + + scheduler.queue.resume() + expect{value}.toEventually(equal(5)) + } + + it("should run enqueued actions after a given date") { + var didRun = false + scheduler.schedule(after: Date()) { + didRun = true + expect(Thread.isMainThread) == false + } + + expect(didRun) == false + + scheduler.queue.resume() + expect{didRun}.toEventually(beTruthy()) + } + + it("should repeatedly run actions after a given date") { + let disposable = SerialDisposable() + + var count = 0 + let timesToRun = 3 + + disposable.innerDisposable = scheduler.schedule(after: Date(), interval: 0.01, leeway: 0) { + expect(Thread.isMainThread) == false + + count += 1 + + if count == timesToRun { + disposable.dispose() + } + } + + expect(count) == 0 + + scheduler.queue.resume() + expect{count}.toEventually(equal(timesToRun)) + } + } + } + + describe("TestScheduler") { + var scheduler: TestScheduler! + var startDate: Date! + + // How much dates are allowed to differ when they should be "equal." + let dateComparisonDelta = 0.00001 + + beforeEach { + startDate = Date() + + scheduler = TestScheduler(startDate: startDate) + expect(scheduler.currentDate) == startDate + } + + it("should run immediately enqueued actions upon advancement") { + var string = "" + + scheduler.schedule { + string += "foo" + expect(Thread.isMainThread) == true + } + + scheduler.schedule { + string += "bar" + expect(Thread.isMainThread) == true + } + + expect(string) == "" + + scheduler.advance() + expect(scheduler.currentDate).to(beCloseTo(startDate)) + + expect(string) == "foobar" + } + + it("should run actions when advanced past the target date") { + var string = "" + + scheduler.schedule(after: 15) { [weak scheduler] in + string += "bar" + expect(Thread.isMainThread) == true + expect(scheduler?.currentDate).to(beCloseTo(startDate.addingTimeInterval(15), within: dateComparisonDelta)) + } + + scheduler.schedule(after: 5) { [weak scheduler] in + string += "foo" + expect(Thread.isMainThread) == true + expect(scheduler?.currentDate).to(beCloseTo(startDate.addingTimeInterval(5), within: dateComparisonDelta)) + } + + expect(string) == "" + + scheduler.advance(by: 10) + expect(scheduler.currentDate).to(beCloseTo(startDate.addingTimeInterval(10), within: TimeInterval(dateComparisonDelta))) + expect(string) == "foo" + + scheduler.advance(by: 10) + expect(scheduler.currentDate).to(beCloseTo(startDate.addingTimeInterval(20), within: dateComparisonDelta)) + expect(string) == "foobar" + } + + it("should run all remaining actions in order") { + var string = "" + + scheduler.schedule(after: 15) { + string += "bar" + expect(Thread.isMainThread) == true + } + + scheduler.schedule(after: 5) { + string += "foo" + expect(Thread.isMainThread) == true + } + + scheduler.schedule { + string += "fuzzbuzz" + expect(Thread.isMainThread) == true + } + + expect(string) == "" + + scheduler.run() + expect(scheduler.currentDate) == NSDate.distantFuture + expect(string) == "fuzzbuzzfoobar" + } + } + } +} diff --git a/ReactiveSwiftTests/SignalLifetimeSpec.swift b/ReactiveSwiftTests/SignalLifetimeSpec.swift new file mode 100644 index 0000000000..a1ea79ea5d --- /dev/null +++ b/ReactiveSwiftTests/SignalLifetimeSpec.swift @@ -0,0 +1,414 @@ +// +// SignalLifetimeSpec.swift +// ReactiveSwift +// +// Created by Vadim Yelagin on 2015-12-13. +// Copyright (c) 2015 GitHub. All rights reserved. +// + +import Result +import Nimble +import Quick +import ReactiveSwift + +class SignalLifetimeSpec: QuickSpec { + override func spec() { + describe("init") { + var testScheduler: TestScheduler! + + beforeEach { + testScheduler = TestScheduler() + } + + it("should deallocate") { + weak var signal: Signal? = Signal { _ in nil } + + expect(signal).to(beNil()) + } + + it("should deallocate if it does not have any observers") { + weak var signal: Signal? = { + let signal: Signal = Signal { _ in nil } + return signal + }() + expect(signal).to(beNil()) + } + + it("should deallocate if no one retains it") { + var signal: Signal? = Signal { _ in nil } + weak var weakSignal = signal + + expect(weakSignal).toNot(beNil()) + + var reference = signal + signal = nil + expect(weakSignal).toNot(beNil()) + + reference = nil + expect(weakSignal).to(beNil()) + } + + it("should deallocate even if the generator observer is retained") { + var observer: Signal.Observer? + + weak var signal: Signal? = { + let signal: Signal = Signal { innerObserver in + observer = innerObserver + return nil + } + return signal + }() + expect(observer).toNot(beNil()) + expect(signal).to(beNil()) + } + + it("should not deallocate if it has at least one observer") { + var disposable: Disposable? = nil + weak var signal: Signal? = { + let signal: Signal = Signal { _ in nil } + disposable = signal.observe(Observer()) + return signal + }() + expect(signal).toNot(beNil()) + disposable?.dispose() + expect(signal).to(beNil()) + } + + it("should be alive until erroring if it has at least one observer, despite not being explicitly retained") { + var errored = false + + weak var signal: Signal? = { + let signal = Signal { observer in + testScheduler.schedule { + observer.sendFailed(TestError.default) + } + return nil + } + signal.observeFailed { _ in errored = true } + return signal + }() + + expect(errored) == false + expect(signal).toNot(beNil()) + + testScheduler.run() + + expect(errored) == true + expect(signal).to(beNil()) + } + + it("should be alive until completion if it has at least one observer, despite not being explicitly retained") { + var completed = false + + weak var signal: Signal? = { + let signal = Signal { observer in + testScheduler.schedule { + observer.sendCompleted() + } + return nil + } + signal.observeCompleted { completed = true } + return signal + }() + + expect(completed) == false + expect(signal).toNot(beNil()) + + testScheduler.run() + + expect(completed) == true + expect(signal).to(beNil()) + } + + it("should be alive until interruption if it has at least one observer, despite not being explicitly retained") { + var interrupted = false + + weak var signal: Signal? = { + let signal = Signal { observer in + testScheduler.schedule { + observer.sendInterrupted() + } + + return nil + } + signal.observeInterrupted { interrupted = true } + return signal + }() + + expect(interrupted) == false + expect(signal).toNot(beNil()) + + testScheduler.run() + + expect(interrupted) == true + expect(signal).to(beNil()) + } + } + + describe("Signal.pipe") { + it("should deallocate") { + weak var signal = Signal<(), NoError>.pipe().0 + + expect(signal).to(beNil()) + } + + it("should be alive until erroring if it has at least one observer, despite not being explicitly retained") { + let testScheduler = TestScheduler() + var errored = false + weak var weakSignal: Signal<(), TestError>? + + // Use an inner closure to help ARC deallocate things as we + // expect. + let test = { + let (signal, observer) = Signal<(), TestError>.pipe() + weakSignal = signal + testScheduler.schedule { + // Note that the input observer has a weak reference to the signal. + observer.sendFailed(TestError.default) + } + signal.observeFailed { _ in errored = true } + } + test() + + expect(weakSignal).toNot(beNil()) + expect(errored) == false + + testScheduler.run() + expect(weakSignal).to(beNil()) + expect(errored) == true + } + + it("should be alive until completion if it has at least one observer, despite not being explicitly retained") { + let testScheduler = TestScheduler() + var completed = false + weak var weakSignal: Signal<(), TestError>? + + // Use an inner closure to help ARC deallocate things as we + // expect. + let test = { + let (signal, observer) = Signal<(), TestError>.pipe() + weakSignal = signal + testScheduler.schedule { + // Note that the input observer has a weak reference to the signal. + observer.sendCompleted() + } + signal.observeCompleted { completed = true } + } + test() + + expect(weakSignal).toNot(beNil()) + expect(completed) == false + + testScheduler.run() + expect(weakSignal).to(beNil()) + expect(completed) == true + } + + it("should be alive until interruption if it has at least one observer, despite not being explicitly retained") { + let testScheduler = TestScheduler() + var interrupted = false + weak var weakSignal: Signal<(), NoError>? + + let test = { + let (signal, observer) = Signal<(), NoError>.pipe() + weakSignal = signal + + testScheduler.schedule { + // Note that the input observer has a weak reference to the signal. + observer.sendInterrupted() + } + + signal.observeInterrupted { interrupted = true } + } + + test() + expect(weakSignal).toNot(beNil()) + expect(interrupted) == false + + testScheduler.run() + expect(weakSignal).to(beNil()) + expect(interrupted) == true + } + } + + describe("testTransform") { + it("should deallocate") { + weak var signal: Signal? = Signal { _ in nil }.testTransform() + + expect(signal).to(beNil()) + } + + it("should not deallocate if it has at least one observer, despite not being explicitly retained") { + weak var signal: Signal? = { + let signal: Signal = Signal { _ in nil }.testTransform() + signal.observe(Observer()) + return signal + }() + expect(signal).toNot(beNil()) + } + + it("should not deallocate if it has at least one observer, despite not being explicitly retained") { + var disposable: Disposable? = nil + weak var signal: Signal? = { + let signal: Signal = Signal { _ in nil }.testTransform() + disposable = signal.observe(Observer()) + return signal + }() + expect(signal).toNot(beNil()) + disposable?.dispose() + expect(signal).to(beNil()) + } + + it("should deallocate if it is unreachable and has no observer") { + let (sourceSignal, sourceObserver) = Signal.pipe() + + var firstCounter = 0 + var secondCounter = 0 + var thirdCounter = 0 + + func run() { + _ = sourceSignal + .map { value -> Int in + firstCounter += 1 + return value + } + .map { value -> Int in + secondCounter += 1 + return value + } + .map { value -> Int in + thirdCounter += 1 + return value + } + } + + run() + + sourceObserver.sendNext(1) + expect(firstCounter) == 0 + expect(secondCounter) == 0 + expect(thirdCounter) == 0 + + sourceObserver.sendNext(2) + expect(firstCounter) == 0 + expect(secondCounter) == 0 + expect(thirdCounter) == 0 + } + + it("should not deallocate if it is unreachable but still has at least one observer") { + let (sourceSignal, sourceObserver) = Signal.pipe() + + var firstCounter = 0 + var secondCounter = 0 + var thirdCounter = 0 + + var disposable: Disposable? + + func run() { + disposable = sourceSignal + .map { value -> Int in + firstCounter += 1 + return value + } + .map { value -> Int in + secondCounter += 1 + return value + } + .map { value -> Int in + thirdCounter += 1 + return value + } + .observe { _ in } + } + + run() + + sourceObserver.sendNext(1) + expect(firstCounter) == 1 + expect(secondCounter) == 1 + expect(thirdCounter) == 1 + + sourceObserver.sendNext(2) + expect(firstCounter) == 2 + expect(secondCounter) == 2 + expect(thirdCounter) == 2 + + disposable?.dispose() + + sourceObserver.sendNext(3) + expect(firstCounter) == 2 + expect(secondCounter) == 2 + expect(thirdCounter) == 2 + } + } + + describe("observe") { + var signal: Signal! + var observer: Signal.Observer! + + var token: NSObject? = nil + weak var weakToken: NSObject? + + func expectTokenNotDeallocated() { + expect(weakToken).toNot(beNil()) + } + + func expectTokenDeallocated() { + expect(weakToken).to(beNil()) + } + + beforeEach { + let (signalTemp, observerTemp) = Signal.pipe() + signal = signalTemp + observer = observerTemp + + token = NSObject() + weakToken = token + + signal.observe { [token = token] _ in + _ = token!.description + } + } + + it("should deallocate observe handler when signal completes") { + expectTokenNotDeallocated() + + observer.sendNext(1) + expectTokenNotDeallocated() + + token = nil + expectTokenNotDeallocated() + + observer.sendNext(2) + expectTokenNotDeallocated() + + observer.sendCompleted() + expectTokenDeallocated() + } + + it("should deallocate observe handler when signal fails") { + expectTokenNotDeallocated() + + observer.sendNext(1) + expectTokenNotDeallocated() + + token = nil + expectTokenNotDeallocated() + + observer.sendNext(2) + expectTokenNotDeallocated() + + observer.sendFailed(.default) + expectTokenDeallocated() + } + } + } +} + +private extension SignalProtocol { + func testTransform() -> Signal { + return Signal { observer in + return self.observe(observer.action) + } + } +} diff --git a/ReactiveSwiftTests/SignalProducerLiftingSpec.swift b/ReactiveSwiftTests/SignalProducerLiftingSpec.swift new file mode 100644 index 0000000000..17637ea83e --- /dev/null +++ b/ReactiveSwiftTests/SignalProducerLiftingSpec.swift @@ -0,0 +1,1536 @@ +// +// SignalProducerLiftingSpec.swift +// ReactiveSwift +// +// Created by Neil Pankey on 6/14/15. +// Copyright © 2015 GitHub. All rights reserved. +// + +import Result +import Nimble +import Quick +import ReactiveSwift + +class SignalProducerLiftingSpec: QuickSpec { + override func spec() { + describe("map") { + it("should transform the values of the signal") { + let (producer, observer) = SignalProducer.pipe() + let mappedProducer = producer.map { String($0 + 1) } + + var lastValue: String? + + mappedProducer.startWithNext { + lastValue = $0 + return + } + + expect(lastValue).to(beNil()) + + observer.sendNext(0) + expect(lastValue) == "1" + + observer.sendNext(1) + expect(lastValue) == "2" + } + } + + describe("mapError") { + it("should transform the errors of the signal") { + let (producer, observer) = SignalProducer.pipe() + let producerError = NSError(domain: "com.reactivecocoa.errordomain", code: 100, userInfo: nil) + var error: NSError? + + producer + .mapError { _ in producerError } + .startWithFailed { error = $0 } + + expect(error).to(beNil()) + + observer.sendFailed(TestError.default) + expect(error) == producerError + } + } + + describe("filter") { + it("should omit values from the producer") { + let (producer, observer) = SignalProducer.pipe() + let mappedProducer = producer.filter { $0 % 2 == 0 } + + var lastValue: Int? + + mappedProducer.startWithNext { lastValue = $0 } + + expect(lastValue).to(beNil()) + + observer.sendNext(0) + expect(lastValue) == 0 + + observer.sendNext(1) + expect(lastValue) == 0 + + observer.sendNext(2) + expect(lastValue) == 2 + } + } + + describe("skipNil") { + it("should forward only non-nil values") { + let (producer, observer) = SignalProducer.pipe() + let mappedProducer = producer.skipNil() + + var lastValue: Int? + + mappedProducer.startWithNext { lastValue = $0 } + expect(lastValue).to(beNil()) + + observer.sendNext(nil) + expect(lastValue).to(beNil()) + + observer.sendNext(1) + expect(lastValue) == 1 + + observer.sendNext(nil) + expect(lastValue) == 1 + + observer.sendNext(2) + expect(lastValue) == 2 + } + } + + describe("scan") { + it("should incrementally accumulate a value") { + let (baseProducer, observer) = SignalProducer.pipe() + let producer = baseProducer.scan("", +) + + var lastValue: String? + + producer.startWithNext { lastValue = $0 } + + expect(lastValue).to(beNil()) + + observer.sendNext("a") + expect(lastValue) == "a" + + observer.sendNext("bb") + expect(lastValue) == "abb" + } + } + + describe("reduce") { + it("should accumulate one value") { + let (baseProducer, observer) = SignalProducer.pipe() + let producer = baseProducer.reduce(1, +) + + var lastValue: Int? + var completed = false + + producer.start { event in + switch event { + case let .next(value): + lastValue = value + case .completed: + completed = true + case .failed, .interrupted: + break + } + } + + expect(lastValue).to(beNil()) + + observer.sendNext(1) + expect(lastValue).to(beNil()) + + observer.sendNext(2) + expect(lastValue).to(beNil()) + + expect(completed) == false + observer.sendCompleted() + expect(completed) == true + + expect(lastValue) == 4 + } + + it("should send the initial value if none are received") { + let (baseProducer, observer) = SignalProducer.pipe() + let producer = baseProducer.reduce(1, +) + + var lastValue: Int? + var completed = false + + producer.start { event in + switch event { + case let .next(value): + lastValue = value + case .completed: + completed = true + case .failed, .interrupted: + break + } + } + + expect(lastValue).to(beNil()) + expect(completed) == false + + observer.sendCompleted() + + expect(lastValue) == 1 + expect(completed) == true + } + } + + describe("skip") { + it("should skip initial values") { + let (baseProducer, observer) = SignalProducer.pipe() + let producer = baseProducer.skip(first: 1) + + var lastValue: Int? + producer.startWithNext { lastValue = $0 } + + expect(lastValue).to(beNil()) + + observer.sendNext(1) + expect(lastValue).to(beNil()) + + observer.sendNext(2) + expect(lastValue) == 2 + } + + it("should not skip any values when 0") { + let (baseProducer, observer) = SignalProducer.pipe() + let producer = baseProducer.skip(first: 0) + + var lastValue: Int? + producer.startWithNext { lastValue = $0 } + + expect(lastValue).to(beNil()) + + observer.sendNext(1) + expect(lastValue) == 1 + + observer.sendNext(2) + expect(lastValue) == 2 + } + } + + describe("skipRepeats") { + it("should skip duplicate Equatable values") { + let (baseProducer, observer) = SignalProducer.pipe() + let producer = baseProducer.skipRepeats() + + var values: [Bool] = [] + producer.startWithNext { values.append($0) } + + expect(values) == [] + + observer.sendNext(true) + expect(values) == [ true ] + + observer.sendNext(true) + expect(values) == [ true ] + + observer.sendNext(false) + expect(values) == [ true, false ] + + observer.sendNext(true) + expect(values) == [ true, false, true ] + } + + it("should skip values according to a predicate") { + let (baseProducer, observer) = SignalProducer.pipe() + let producer = baseProducer.skipRepeats { $0.characters.count == $1.characters.count } + + var values: [String] = [] + producer.startWithNext { values.append($0) } + + expect(values) == [] + + observer.sendNext("a") + expect(values) == [ "a" ] + + observer.sendNext("b") + expect(values) == [ "a" ] + + observer.sendNext("cc") + expect(values) == [ "a", "cc" ] + + observer.sendNext("d") + expect(values) == [ "a", "cc", "d" ] + } + } + + describe("skipWhile") { + var producer: SignalProducer! + var observer: Signal.Observer! + + var lastValue: Int? + + beforeEach { + let (baseProducer, incomingObserver) = SignalProducer.pipe() + + producer = baseProducer.skip { $0 < 2 } + observer = incomingObserver + lastValue = nil + + producer.startWithNext { lastValue = $0 } + } + + it("should skip while the predicate is true") { + expect(lastValue).to(beNil()) + + observer.sendNext(1) + expect(lastValue).to(beNil()) + + observer.sendNext(2) + expect(lastValue) == 2 + + observer.sendNext(0) + expect(lastValue) == 0 + } + + it("should not skip any values when the predicate starts false") { + expect(lastValue).to(beNil()) + + observer.sendNext(3) + expect(lastValue) == 3 + + observer.sendNext(1) + expect(lastValue) == 1 + } + } + + describe("skipUntil") { + var producer: SignalProducer! + var observer: Signal.Observer! + var triggerObserver: Signal<(), NoError>.Observer! + + var lastValue: Int? = nil + + beforeEach { + let (baseProducer, baseIncomingObserver) = SignalProducer.pipe() + let (triggerProducer, incomingTriggerObserver) = SignalProducer<(), NoError>.pipe() + + producer = baseProducer.skip(until: triggerProducer) + observer = baseIncomingObserver + triggerObserver = incomingTriggerObserver + + lastValue = nil + + producer.start { event in + switch event { + case let .next(value): + lastValue = value + case .failed, .completed, .interrupted: + break + } + } + } + + it("should skip values until the trigger fires") { + expect(lastValue).to(beNil()) + + observer.sendNext(1) + expect(lastValue).to(beNil()) + + observer.sendNext(2) + expect(lastValue).to(beNil()) + + triggerObserver.sendNext(()) + observer.sendNext(0) + expect(lastValue) == 0 + } + + it("should skip values until the trigger completes") { + expect(lastValue).to(beNil()) + + observer.sendNext(1) + expect(lastValue).to(beNil()) + + observer.sendNext(2) + expect(lastValue).to(beNil()) + + triggerObserver.sendCompleted() + observer.sendNext(0) + expect(lastValue) == 0 + } + } + + describe("take") { + it("should take initial values") { + let (baseProducer, observer) = SignalProducer.pipe() + let producer = baseProducer.take(first: 2) + + var lastValue: Int? + var completed = false + producer.start { event in + switch event { + case let .next(value): + lastValue = value + case .completed: + completed = true + case .failed, .interrupted: + break + } + } + + expect(lastValue).to(beNil()) + expect(completed) == false + + observer.sendNext(1) + expect(lastValue) == 1 + expect(completed) == false + + observer.sendNext(2) + expect(lastValue) == 2 + expect(completed) == true + } + + it("should complete immediately after taking given number of values") { + let numbers = [ 1, 2, 4, 4, 5 ] + let testScheduler = TestScheduler() + + let producer: SignalProducer = SignalProducer { observer, _ in + // workaround `Class declaration cannot close over value 'observer' defined in outer scope` + let observer = observer + + testScheduler.schedule { + for number in numbers { + observer.sendNext(number) + } + } + } + + var completed = false + + producer + .take(first: numbers.count) + .startWithCompleted { completed = true } + + expect(completed) == false + testScheduler.run() + expect(completed) == true + } + + it("should interrupt when 0") { + let numbers = [ 1, 2, 4, 4, 5 ] + let testScheduler = TestScheduler() + + let producer: SignalProducer = SignalProducer { observer, _ in + // workaround `Class declaration cannot close over value 'observer' defined in outer scope` + let observer = observer + + testScheduler.schedule { + for number in numbers { + observer.sendNext(number) + } + } + } + + var result: [Int] = [] + var interrupted = false + + producer + .take(first: 0) + .start { event in + switch event { + case let .next(number): + result.append(number) + case .interrupted: + interrupted = true + case .failed, .completed: + break + } + } + + expect(interrupted) == true + + testScheduler.run() + expect(result).to(beEmpty()) + } + } + + describe("collect") { + it("should collect all values") { + let (original, observer) = SignalProducer.pipe() + let producer = original.collect() + let expectedResult = [ 1, 2, 3 ] + + var result: [Int]? + + producer.startWithNext { value in + expect(result).to(beNil()) + result = value + } + + for number in expectedResult { + observer.sendNext(number) + } + + expect(result).to(beNil()) + observer.sendCompleted() + expect(result) == expectedResult + } + + it("should complete with an empty array if there are no values") { + let (original, observer) = SignalProducer.pipe() + let producer = original.collect() + + var result: [Int]? + + producer.startWithNext { result = $0 } + + expect(result).to(beNil()) + observer.sendCompleted() + expect(result) == [] + } + + it("should forward errors") { + let (original, observer) = SignalProducer.pipe() + let producer = original.collect() + + var error: TestError? + + producer.startWithFailed { error = $0 } + + expect(error).to(beNil()) + observer.sendFailed(.default) + expect(error) == TestError.default + } + + it("should collect an exact count of values") { + let (original, observer) = SignalProducer.pipe() + + let producer = original.collect(count: 3) + + var observedValues: [[Int]] = [] + + producer.startWithNext { value in + observedValues.append(value) + } + + var expectation: [[Int]] = [] + + for i in 1...7 { + + observer.sendNext(i) + + if i % 3 == 0 { + expectation.append([Int]((i - 2)...i)) + expect(observedValues) == expectation + } else { + expect(observedValues) == expectation + } + } + + observer.sendCompleted() + + expectation.append([7]) + expect(observedValues) == expectation + } + + it("should collect values until it matches a certain value") { + let (original, observer) = SignalProducer.pipe() + + let producer = original.collect { _, next in next != 5 } + + var expectedValues = [ + [5, 5], + [42, 5] + ] + + producer.startWithNext { value in + expect(value) == expectedValues.removeFirst() + } + + producer.startWithCompleted { + expect(expectedValues) == [] + } + + expectedValues + .flatMap { $0 } + .forEach(observer.sendNext) + + observer.sendCompleted() + } + + it("should collect values until it matches a certain condition on values") { + let (original, observer) = SignalProducer.pipe() + + let producer = original.collect { values in values.reduce(0, +) == 10 } + + var expectedValues = [ + [1, 2, 3, 4], + [5, 6, 7, 8, 9] + ] + + producer.startWithNext { value in + expect(value) == expectedValues.removeFirst() + } + + producer.startWithCompleted { + expect(expectedValues) == [] + } + + expectedValues + .flatMap { $0 } + .forEach(observer.sendNext) + + observer.sendCompleted() + } + + } + + describe("takeUntil") { + var producer: SignalProducer! + var observer: Signal.Observer! + var triggerObserver: Signal<(), NoError>.Observer! + + var lastValue: Int? = nil + var completed: Bool = false + + beforeEach { + let (baseProducer, baseIncomingObserver) = SignalProducer.pipe() + let (triggerProducer, incomingTriggerObserver) = SignalProducer<(), NoError>.pipe() + + producer = baseProducer.take(until: triggerProducer) + observer = baseIncomingObserver + triggerObserver = incomingTriggerObserver + + lastValue = nil + completed = false + + producer.start { event in + switch event { + case let .next(value): + lastValue = value + case .completed: + completed = true + case .failed, .interrupted: + break + } + } + } + + it("should take values until the trigger fires") { + expect(lastValue).to(beNil()) + + observer.sendNext(1) + expect(lastValue) == 1 + + observer.sendNext(2) + expect(lastValue) == 2 + + expect(completed) == false + triggerObserver.sendNext(()) + expect(completed) == true + } + + it("should take values until the trigger completes") { + expect(lastValue).to(beNil()) + + observer.sendNext(1) + expect(lastValue) == 1 + + observer.sendNext(2) + expect(lastValue) == 2 + + expect(completed) == false + triggerObserver.sendCompleted() + expect(completed) == true + } + + it("should complete if the trigger fires immediately") { + expect(lastValue).to(beNil()) + expect(completed) == false + + triggerObserver.sendNext(()) + + expect(completed) == true + expect(lastValue).to(beNil()) + } + } + + describe("takeUntilReplacement") { + var producer: SignalProducer! + var observer: Signal.Observer! + var replacementObserver: Signal.Observer! + + var lastValue: Int? = nil + var completed: Bool = false + + beforeEach { + let (baseProducer, incomingObserver) = SignalProducer.pipe() + let (replacementProducer, incomingReplacementObserver) = SignalProducer.pipe() + + producer = baseProducer.take(untilReplacement: replacementProducer) + observer = incomingObserver + replacementObserver = incomingReplacementObserver + + lastValue = nil + completed = false + + producer.start { event in + switch event { + case let .next(value): + lastValue = value + case .completed: + completed = true + case .failed, .interrupted: + break + } + } + } + + it("should take values from the original then the replacement") { + expect(lastValue).to(beNil()) + expect(completed) == false + + observer.sendNext(1) + expect(lastValue) == 1 + + observer.sendNext(2) + expect(lastValue) == 2 + + replacementObserver.sendNext(3) + + expect(lastValue) == 3 + expect(completed) == false + + observer.sendNext(4) + + expect(lastValue) == 3 + expect(completed) == false + + replacementObserver.sendNext(5) + expect(lastValue) == 5 + + expect(completed) == false + replacementObserver.sendCompleted() + expect(completed) == true + } + } + + describe("takeWhile") { + var producer: SignalProducer! + var observer: Signal.Observer! + + beforeEach { + let (baseProducer, incomingObserver) = SignalProducer.pipe() + producer = baseProducer.take { $0 <= 4 } + observer = incomingObserver + } + + it("should take while the predicate is true") { + var latestValue: Int! + var completed = false + + producer.start { event in + switch event { + case let .next(value): + latestValue = value + case .completed: + completed = true + case .failed, .interrupted: + break + } + } + + for value in -1...4 { + observer.sendNext(value) + expect(latestValue) == value + expect(completed) == false + } + + observer.sendNext(5) + expect(latestValue) == 4 + expect(completed) == true + } + + it("should complete if the predicate starts false") { + var latestValue: Int? + var completed = false + + producer.start { event in + switch event { + case let .next(value): + latestValue = value + case .completed: + completed = true + case .failed, .interrupted: + break + } + } + + observer.sendNext(5) + expect(latestValue).to(beNil()) + expect(completed) == true + } + } + + describe("observeOn") { + it("should send events on the given scheduler") { + let testScheduler = TestScheduler() + let (producer, observer) = SignalProducer.pipe() + + var result: [Int] = [] + + producer + .observe(on: testScheduler) + .startWithNext { result.append($0) } + + observer.sendNext(1) + observer.sendNext(2) + expect(result).to(beEmpty()) + + testScheduler.run() + expect(result) == [ 1, 2 ] + } + } + + describe("delay") { + it("should send events on the given scheduler after the interval") { + let testScheduler = TestScheduler() + let producer: SignalProducer = SignalProducer { observer, _ in + testScheduler.schedule { + observer.sendNext(1) + } + testScheduler.schedule(after: 5) { + observer.sendNext(2) + observer.sendCompleted() + } + } + + var result: [Int] = [] + var completed = false + + producer + .delay(10, on: testScheduler) + .start { event in + switch event { + case let .next(number): + result.append(number) + case .completed: + completed = true + case .failed, .interrupted: + break + } + } + + testScheduler.advance(by: 4) // send initial value + expect(result).to(beEmpty()) + + testScheduler.advance(by: 10) // send second value and receive first + expect(result) == [ 1 ] + expect(completed) == false + + testScheduler.advance(by: 10) // send second value and receive first + expect(result) == [ 1, 2 ] + expect(completed) == true + } + + it("should schedule errors immediately") { + let testScheduler = TestScheduler() + let producer: SignalProducer = SignalProducer { observer, _ in + // workaround `Class declaration cannot close over value 'observer' defined in outer scope` + let observer = observer + + testScheduler.schedule { + observer.sendFailed(TestError.default) + } + } + + var errored = false + + producer + .delay(10, on: testScheduler) + .startWithFailed { _ in errored = true } + + testScheduler.advance() + expect(errored) == true + } + } + + describe("throttle") { + var scheduler: TestScheduler! + var observer: Signal.Observer! + var producer: SignalProducer! + + beforeEach { + scheduler = TestScheduler() + + let (baseProducer, baseObserver) = SignalProducer.pipe() + observer = baseObserver + + producer = baseProducer.throttle(1, on: scheduler) + } + + it("should send values on the given scheduler at no less than the interval") { + var values: [Int] = [] + producer.startWithNext { value in + values.append(value) + } + + expect(values) == [] + + observer.sendNext(0) + expect(values) == [] + + scheduler.advance() + expect(values) == [ 0 ] + + observer.sendNext(1) + observer.sendNext(2) + expect(values) == [ 0 ] + + scheduler.advance(by: 1.5) + expect(values) == [ 0, 2 ] + + scheduler.advance(by: 3) + expect(values) == [ 0, 2 ] + + observer.sendNext(3) + expect(values) == [ 0, 2 ] + + scheduler.advance() + expect(values) == [ 0, 2, 3 ] + + observer.sendNext(4) + observer.sendNext(5) + scheduler.advance() + expect(values) == [ 0, 2, 3 ] + + scheduler.rewind(by: 2) + expect(values) == [ 0, 2, 3 ] + + observer.sendNext(6) + scheduler.advance() + expect(values) == [ 0, 2, 3, 6 ] + + observer.sendNext(7) + observer.sendNext(8) + scheduler.advance() + expect(values) == [ 0, 2, 3, 6 ] + + scheduler.run() + expect(values) == [ 0, 2, 3, 6, 8 ] + } + + it("should schedule completion immediately") { + var values: [Int] = [] + var completed = false + + producer.start { event in + switch event { + case let .next(value): + values.append(value) + case .completed: + completed = true + case .failed, .interrupted: + break + } + } + + observer.sendNext(0) + scheduler.advance() + expect(values) == [ 0 ] + + observer.sendNext(1) + observer.sendCompleted() + expect(completed) == false + + scheduler.run() + expect(values) == [ 0 ] + expect(completed) == true + } + } + + describe("sampleWith") { + var sampledProducer: SignalProducer<(Int, String), NoError>! + var observer: Signal.Observer! + var samplerObserver: Signal.Observer! + + beforeEach { + let (producer, incomingObserver) = SignalProducer.pipe() + let (sampler, incomingSamplerObserver) = SignalProducer.pipe() + sampledProducer = producer.sample(with: sampler) + observer = incomingObserver + samplerObserver = incomingSamplerObserver + } + + it("should forward the latest value when the sampler fires") { + var result: [String] = [] + sampledProducer.startWithNext { (left, right) in result.append("\(left)\(right)") } + + observer.sendNext(1) + observer.sendNext(2) + samplerObserver.sendNext("a") + expect(result) == [ "2a" ] + } + + it("should do nothing if sampler fires before signal receives value") { + var result: [String] = [] + sampledProducer.startWithNext { (left, right) in result.append("\(left)\(right)") } + + samplerObserver.sendNext("a") + expect(result).to(beEmpty()) + } + + it("should send lates value multiple times when sampler fires multiple times") { + var result: [String] = [] + sampledProducer.startWithNext { (left, right) in result.append("\(left)\(right)") } + + observer.sendNext(1) + samplerObserver.sendNext("a") + samplerObserver.sendNext("b") + expect(result) == [ "1a", "1b" ] + } + + it("should complete when both inputs have completed") { + var completed = false + sampledProducer.startWithCompleted { completed = true } + + observer.sendCompleted() + expect(completed) == false + + samplerObserver.sendCompleted() + expect(completed) == true + } + + it("should emit an initial value if the sampler is a synchronous SignalProducer") { + let producer = SignalProducer(values: [1]) + let sampler = SignalProducer(value: "a") + + let result = producer.sample(with: sampler) + + var valueReceived: String? + result.startWithNext { (left, right) in valueReceived = "\(left)\(right)" } + + expect(valueReceived) == "1a" + } + } + + describe("sampleOn") { + var sampledProducer: SignalProducer! + var observer: Signal.Observer! + var samplerObserver: Signal<(), NoError>.Observer! + + beforeEach { + let (producer, incomingObserver) = SignalProducer.pipe() + let (sampler, incomingSamplerObserver) = SignalProducer<(), NoError>.pipe() + sampledProducer = producer.sample(on: sampler) + observer = incomingObserver + samplerObserver = incomingSamplerObserver + } + + it("should forward the latest value when the sampler fires") { + var result: [Int] = [] + sampledProducer.startWithNext { result.append($0) } + + observer.sendNext(1) + observer.sendNext(2) + samplerObserver.sendNext(()) + expect(result) == [ 2 ] + } + + it("should do nothing if sampler fires before signal receives value") { + var result: [Int] = [] + sampledProducer.startWithNext { result.append($0) } + + samplerObserver.sendNext(()) + expect(result).to(beEmpty()) + } + + it("should send lates value multiple times when sampler fires multiple times") { + var result: [Int] = [] + sampledProducer.startWithNext { result.append($0) } + + observer.sendNext(1) + samplerObserver.sendNext(()) + samplerObserver.sendNext(()) + expect(result) == [ 1, 1 ] + } + + it("should complete when both inputs have completed") { + var completed = false + sampledProducer.startWithCompleted { completed = true } + + observer.sendCompleted() + expect(completed) == false + + samplerObserver.sendCompleted() + expect(completed) == true + } + + it("should emit an initial value if the sampler is a synchronous SignalProducer") { + let producer = SignalProducer(values: [1]) + let sampler = SignalProducer<(), NoError>(value: ()) + + let result = producer.sample(on: sampler) + + var valueReceived: Int? + result.startWithNext { valueReceived = $0 } + + expect(valueReceived) == 1 + } + + describe("memory") { + class Payload { + let action: () -> Void + + init(onDeinit action: () -> Void) { + self.action = action + } + + deinit { + action() + } + } + + var sampledProducer: SignalProducer! + var observer: Signal.Observer! + + beforeEach { + let (producer, incomingObserver) = SignalProducer.pipe() + let (sampler, _) = Signal<(), NoError>.pipe() + sampledProducer = producer.sample(on: sampler) + observer = incomingObserver + } + + it("should free payload when interrupted after complete of incoming producer") { + var payloadFreed = false + + let disposable = sampledProducer.start() + + observer.sendNext(Payload { payloadFreed = true }) + observer.sendCompleted() + + expect(payloadFreed) == false + + disposable.dispose() + expect(payloadFreed) == true + } + } + } + + describe("combineLatestWith") { + var combinedProducer: SignalProducer<(Int, Double), NoError>! + var observer: Signal.Observer! + var otherObserver: Signal.Observer! + + beforeEach { + let (producer, incomingObserver) = SignalProducer.pipe() + let (otherSignal, incomingOtherObserver) = SignalProducer.pipe() + combinedProducer = producer.combineLatest(with: otherSignal) + observer = incomingObserver + otherObserver = incomingOtherObserver + } + + it("should forward the latest values from both inputs") { + var latest: (Int, Double)? + combinedProducer.startWithNext { latest = $0 } + + observer.sendNext(1) + expect(latest).to(beNil()) + + // is there a better way to test tuples? + otherObserver.sendNext(1.5) + expect(latest?.0) == 1 + expect(latest?.1) == 1.5 + + observer.sendNext(2) + expect(latest?.0) == 2 + expect(latest?.1) == 1.5 + } + + it("should complete when both inputs have completed") { + var completed = false + combinedProducer.startWithCompleted { completed = true } + + observer.sendCompleted() + expect(completed) == false + + otherObserver.sendCompleted() + expect(completed) == true + } + } + + describe("zipWith") { + var leftObserver: Signal.Observer! + var rightObserver: Signal.Observer! + var zipped: SignalProducer<(Int, String), NoError>! + + beforeEach { + let (leftProducer, incomingLeftObserver) = SignalProducer.pipe() + let (rightProducer, incomingRightObserver) = SignalProducer.pipe() + + leftObserver = incomingLeftObserver + rightObserver = incomingRightObserver + zipped = leftProducer.zip(with: rightProducer) + } + + it("should combine pairs") { + var result: [String] = [] + zipped.startWithNext { (left, right) in result.append("\(left)\(right)") } + + leftObserver.sendNext(1) + leftObserver.sendNext(2) + expect(result) == [] + + rightObserver.sendNext("foo") + expect(result) == [ "1foo" ] + + leftObserver.sendNext(3) + rightObserver.sendNext("bar") + expect(result) == [ "1foo", "2bar" ] + + rightObserver.sendNext("buzz") + expect(result) == [ "1foo", "2bar", "3buzz" ] + + rightObserver.sendNext("fuzz") + expect(result) == [ "1foo", "2bar", "3buzz" ] + + leftObserver.sendNext(4) + expect(result) == [ "1foo", "2bar", "3buzz", "4fuzz" ] + } + + it("should complete when the shorter signal has completed") { + var result: [String] = [] + var completed = false + + zipped.start { event in + switch event { + case let .next(left, right): + result.append("\(left)\(right)") + case .completed: + completed = true + case .failed, .interrupted: + break + } + } + + expect(completed) == false + + leftObserver.sendNext(0) + leftObserver.sendCompleted() + expect(completed) == false + expect(result) == [] + + rightObserver.sendNext("foo") + expect(completed) == true + expect(result) == [ "0foo" ] + } + } + + describe("materialize") { + it("should reify events from the signal") { + let (producer, observer) = SignalProducer.pipe() + var latestEvent: Event? + producer + .materialize() + .startWithNext { latestEvent = $0 } + + observer.sendNext(2) + + expect(latestEvent).toNot(beNil()) + if let latestEvent = latestEvent { + switch latestEvent { + case let .next(value): + expect(value) == 2 + case .failed, .completed, .interrupted: + fail() + } + } + + observer.sendFailed(TestError.default) + if let latestEvent = latestEvent { + switch latestEvent { + case .failed: + break + case .next, .completed, .interrupted: + fail() + } + } + } + } + + describe("dematerialize") { + typealias IntEvent = Event + var observer: Signal.Observer! + var dematerialized: SignalProducer! + + beforeEach { + let (producer, incomingObserver) = SignalProducer.pipe() + observer = incomingObserver + dematerialized = producer.dematerialize() + } + + it("should send values for Next events") { + var result: [Int] = [] + dematerialized + .assumeNoErrors() + .startWithNext { result.append($0) } + + expect(result).to(beEmpty()) + + observer.sendNext(.next(2)) + expect(result) == [ 2 ] + + observer.sendNext(.next(4)) + expect(result) == [ 2, 4 ] + } + + it("should error out for Error events") { + var errored = false + dematerialized.startWithFailed { _ in errored = true } + + expect(errored) == false + + observer.sendNext(.failed(TestError.default)) + expect(errored) == true + } + + it("should complete early for Completed events") { + var completed = false + dematerialized.startWithCompleted { completed = true } + + expect(completed) == false + observer.sendNext(IntEvent.completed) + expect(completed) == true + } + } + + describe("takeLast") { + var observer: Signal.Observer! + var lastThree: SignalProducer! + + beforeEach { + let (producer, incomingObserver) = SignalProducer.pipe() + observer = incomingObserver + lastThree = producer.take(last: 3) + } + + it("should send the last N values upon completion") { + var result: [Int] = [] + lastThree + .assumeNoErrors() + .startWithNext { result.append($0) } + + observer.sendNext(1) + observer.sendNext(2) + observer.sendNext(3) + observer.sendNext(4) + expect(result).to(beEmpty()) + + observer.sendCompleted() + expect(result) == [ 2, 3, 4 ] + } + + it("should send less than N values if not enough were received") { + var result: [Int] = [] + lastThree + .assumeNoErrors() + .startWithNext { result.append($0) } + + observer.sendNext(1) + observer.sendNext(2) + observer.sendCompleted() + expect(result) == [ 1, 2 ] + } + + it("should send nothing when errors") { + var result: [Int] = [] + var errored = false + lastThree.start { event in + switch event { + case let .next(value): + result.append(value) + case .failed: + errored = true + case .completed, .interrupted: + break + } + } + + observer.sendNext(1) + observer.sendNext(2) + observer.sendNext(3) + expect(errored) == false + + observer.sendFailed(TestError.default) + expect(errored) == true + expect(result).to(beEmpty()) + } + } + + describe("timeoutWithError") { + var testScheduler: TestScheduler! + var producer: SignalProducer! + var observer: Signal.Observer! + + beforeEach { + testScheduler = TestScheduler() + let (baseProducer, incomingObserver) = SignalProducer.pipe() + producer = baseProducer.timeout(after: 2, raising: TestError.default, on: testScheduler) + observer = incomingObserver + } + + it("should complete if within the interval") { + var completed = false + var errored = false + producer.start { event in + switch event { + case .completed: + completed = true + case .failed: + errored = true + case .next, .interrupted: + break + } + } + + testScheduler.schedule(after: 1) { + observer.sendCompleted() + } + + expect(completed) == false + expect(errored) == false + + testScheduler.run() + expect(completed) == true + expect(errored) == false + } + + it("should error if not completed before the interval has elapsed") { + var completed = false + var errored = false + producer.start { event in + switch event { + case .completed: + completed = true + case .failed: + errored = true + case .next, .interrupted: + break + } + } + + testScheduler.schedule(after: 3) { + observer.sendCompleted() + } + + expect(completed) == false + expect(errored) == false + + testScheduler.run() + expect(completed) == false + expect(errored) == true + } + + it("should be available for NoError") { + let producer: SignalProducer = SignalProducer.never + .timeout(after: 2, raising: TestError.default, on: testScheduler) + + _ = producer + } + } + + describe("attempt") { + it("should forward original values upon success") { + let (baseProducer, observer) = SignalProducer.pipe() + let producer = baseProducer.attempt { _ in + return .success() + } + + var current: Int? + producer + .assumeNoErrors() + .startWithNext { value in + current = value + } + + for value in 1...5 { + observer.sendNext(value) + expect(current) == value + } + } + + it("should error if an attempt fails") { + let (baseProducer, observer) = SignalProducer.pipe() + let producer = baseProducer.attempt { _ in + return .failure(.default) + } + + var error: TestError? + producer.startWithFailed { err in + error = err + } + + observer.sendNext(42) + expect(error) == TestError.default + } + } + + describe("attemptMap") { + it("should forward mapped values upon success") { + let (baseProducer, observer) = SignalProducer.pipe() + let producer = baseProducer.attemptMap { num -> Result in + return .success(num % 2 == 0) + } + + var even: Bool? + producer + .assumeNoErrors() + .startWithNext { value in + even = value + } + + observer.sendNext(1) + expect(even) == false + + observer.sendNext(2) + expect(even) == true + } + + it("should error if a mapping fails") { + let (baseProducer, observer) = SignalProducer.pipe() + let producer = baseProducer.attemptMap { _ -> Result in + return .failure(.default) + } + + var error: TestError? + producer.startWithFailed { err in + error = err + } + + observer.sendNext(42) + expect(error) == TestError.default + } + } + + describe("combinePrevious") { + var observer: Signal.Observer! + let initialValue: Int = 0 + var latestValues: (Int, Int)? + + beforeEach { + latestValues = nil + + let (signal, baseObserver) = SignalProducer.pipe() + observer = baseObserver + signal.combinePrevious(initialValue).startWithNext { latestValues = $0 } + } + + it("should forward the latest value with previous value") { + expect(latestValues).to(beNil()) + + observer.sendNext(1) + expect(latestValues?.0) == initialValue + expect(latestValues?.1) == 1 + + observer.sendNext(2) + expect(latestValues?.0) == 1 + expect(latestValues?.1) == 2 + } + } + } +} diff --git a/ReactiveSwiftTests/SignalProducerNimbleMatchers.swift b/ReactiveSwiftTests/SignalProducerNimbleMatchers.swift new file mode 100644 index 0000000000..bc9654a3e4 --- /dev/null +++ b/ReactiveSwiftTests/SignalProducerNimbleMatchers.swift @@ -0,0 +1,57 @@ +// +// SignalProducerNimbleMatchers.swift +// ReactiveSwift +// +// Created by Javier Soto on 1/25/15. +// Copyright (c) 2015 GitHub. All rights reserved. +// + +import Foundation + +import ReactiveSwift +import Nimble + +public func sendValue(_ value: T?, sendError: E?, complete: Bool) -> NonNilMatcherFunc> { + return sendValues(value.map { [$0] } ?? [], sendError: sendError, complete: complete) +} + +public func sendValues(_ values: [T], sendError maybeSendError: E?, complete: Bool) -> NonNilMatcherFunc> { + return NonNilMatcherFunc { actualExpression, failureMessage in + precondition(maybeSendError == nil || !complete, "Signals can't both send an error and complete") + + failureMessage.postfixMessage = "Send values \(values). Send error \(maybeSendError). Complete: \(complete)" + let maybeProducer = try actualExpression.evaluate() + + if let signalProducer = maybeProducer { + var sentValues: [T] = [] + var sentError: E? + var signalCompleted = false + + signalProducer.start { event in + switch event { + case let .next(value): + sentValues.append(value) + case .completed: + signalCompleted = true + case let .failed(error): + sentError = error + default: + break + } + } + + if sentValues != values { + return false + } + + if sentError != maybeSendError { + return false + } + + return signalCompleted == complete + } + else { + return false + } + } +} diff --git a/ReactiveSwiftTests/SignalProducerSpec.swift b/ReactiveSwiftTests/SignalProducerSpec.swift new file mode 100644 index 0000000000..1435d12f23 --- /dev/null +++ b/ReactiveSwiftTests/SignalProducerSpec.swift @@ -0,0 +1,2257 @@ +// +// SignalProducerSpec.swift +// ReactiveSwift +// +// Created by Justin Spahr-Summers on 2015-01-23. +// Copyright (c) 2015 GitHub. All rights reserved. +// + +import Foundation + +import Result +import Nimble +import Quick +import ReactiveSwift + +class SignalProducerSpec: QuickSpec { + override func spec() { + describe("init") { + it("should run the handler once per start()") { + var handlerCalledTimes = 0 + let signalProducer = SignalProducer() { observer, disposable in + handlerCalledTimes += 1 + + return + } + + signalProducer.start() + signalProducer.start() + + expect(handlerCalledTimes) == 2 + } + + it("should not release signal observers when given disposable is disposed") { + var disposable: Disposable! + + let producer = SignalProducer { observer, innerDisposable in + disposable = innerDisposable + + innerDisposable += { + // This is necessary to keep the observer long enough to + // even test the memory management. + observer.sendNext(0) + } + } + + weak var objectRetainedByObserver: NSObject? + producer.startWithSignal { signal, _ in + let object = NSObject() + objectRetainedByObserver = object + signal.observeNext { _ in _ = object } + } + + expect(objectRetainedByObserver).toNot(beNil()) + + disposable.dispose() + + // https://github.com/ReactiveCocoa/ReactiveCocoa/pull/2959 + // + // Before #2959, this would be `nil` as the input observer is not + // retained, and observers would not retain the signal. + // + // After #2959, the object is still retained, since the observation + // keeps the signal alive. + expect(objectRetainedByObserver).toNot(beNil()) + } + + it("should dispose of added disposables upon completion") { + let addedDisposable = SimpleDisposable() + var observer: Signal<(), NoError>.Observer! + + let producer = SignalProducer<(), NoError>() { incomingObserver, disposable in + disposable += addedDisposable + observer = incomingObserver + } + + producer.start() + expect(addedDisposable.isDisposed) == false + + observer.sendCompleted() + expect(addedDisposable.isDisposed) == true + } + + it("should dispose of added disposables upon error") { + let addedDisposable = SimpleDisposable() + var observer: Signal<(), TestError>.Observer! + + let producer = SignalProducer<(), TestError>() { incomingObserver, disposable in + disposable += addedDisposable + observer = incomingObserver + } + + producer.start() + expect(addedDisposable.isDisposed) == false + + observer.sendFailed(.default) + expect(addedDisposable.isDisposed) == true + } + + it("should dispose of added disposables upon interruption") { + let addedDisposable = SimpleDisposable() + var observer: Signal<(), NoError>.Observer! + + let producer = SignalProducer<(), NoError>() { incomingObserver, disposable in + disposable += addedDisposable + observer = incomingObserver + } + + producer.start() + expect(addedDisposable.isDisposed) == false + + observer.sendInterrupted() + expect(addedDisposable.isDisposed) == true + } + + it("should dispose of added disposables upon start() disposal") { + let addedDisposable = SimpleDisposable() + + let producer = SignalProducer<(), TestError>() { _, disposable in + disposable += addedDisposable + return + } + + let startDisposable = producer.start() + expect(addedDisposable.isDisposed) == false + + startDisposable.dispose() + expect(addedDisposable.isDisposed) == true + } + } + + describe("init(signal:)") { + var signal: Signal! + var observer: Signal.Observer! + + beforeEach { + // Cannot directly assign due to compiler crash on Xcode 7.0.1 + let (signalTemp, observerTemp) = Signal.pipe() + signal = signalTemp + observer = observerTemp + } + + it("should emit values then complete") { + let producer = SignalProducer(signal: signal) + + var values: [Int] = [] + var error: TestError? + var completed = false + producer.start { event in + switch event { + case let .next(value): + values.append(value) + case let .failed(err): + error = err + case .completed: + completed = true + default: + break + } + } + + expect(values) == [] + expect(error).to(beNil()) + expect(completed) == false + + observer.sendNext(1) + expect(values) == [ 1 ] + observer.sendNext(2) + observer.sendNext(3) + expect(values) == [ 1, 2, 3 ] + + observer.sendCompleted() + expect(completed) == true + } + + it("should emit error") { + let producer = SignalProducer(signal: signal) + + var error: TestError? + let sentError = TestError.default + + producer.start { event in + switch event { + case let .failed(err): + error = err + default: + break + } + } + + expect(error).to(beNil()) + + observer.sendFailed(sentError) + expect(error) == sentError + } + } + + describe("init(value:)") { + it("should immediately send the value then complete") { + let producerValue = "StringValue" + let signalProducer = SignalProducer(value: producerValue) + + expect(signalProducer).to(sendValue(producerValue, sendError: nil, complete: true)) + } + } + + describe("init(error:)") { + it("should immediately send the error") { + let producerError = NSError(domain: "com.reactivecocoa.errordomain", code: 4815, userInfo: nil) + let signalProducer = SignalProducer(error: producerError) + + expect(signalProducer).to(sendValue(nil, sendError: producerError, complete: false)) + } + } + + describe("init(result:)") { + it("should immediately send the value then complete") { + let producerValue = "StringValue" + let producerResult = .success(producerValue) as Result + let signalProducer = SignalProducer(result: producerResult) + + expect(signalProducer).to(sendValue(producerValue, sendError: nil, complete: true)) + } + + it("should immediately send the error") { + let producerError = NSError(domain: "com.reactivecocoa.errordomain", code: 4815, userInfo: nil) + let producerResult = .failure(producerError) as Result + let signalProducer = SignalProducer(result: producerResult) + + expect(signalProducer).to(sendValue(nil, sendError: producerError, complete: false)) + } + } + + describe("init(values:)") { + it("should immediately send the sequence of values") { + let sequenceValues = [1, 2, 3] + let signalProducer = SignalProducer(values: sequenceValues) + + expect(signalProducer).to(sendValues(sequenceValues, sendError: nil, complete: true)) + } + } + + describe("SignalProducer.empty") { + it("should immediately complete") { + let signalProducer = SignalProducer.empty + + expect(signalProducer).to(sendValue(nil, sendError: nil, complete: true)) + } + } + + describe("SignalProducer.never") { + it("should not send any events") { + let signalProducer = SignalProducer.never + + expect(signalProducer).to(sendValue(nil, sendError: nil, complete: false)) + } + } + + describe("trailing closure") { + it("receives next values") { + let (producer, observer) = SignalProducer.pipe() + + var values = [Int]() + producer.startWithNext { next in + values.append(next) + } + + observer.sendNext(1) + expect(values) == [1] + } + } + + describe("SignalProducer.attempt") { + it("should run the operation once per start()") { + var operationRunTimes = 0 + let operation: () -> Result = { + operationRunTimes += 1 + + return .success("OperationValue") + } + + SignalProducer.attempt(operation).start() + SignalProducer.attempt(operation).start() + + expect(operationRunTimes) == 2 + } + + it("should send the value then complete") { + let operationReturnValue = "OperationValue" + let operation: () -> Result = { + return .success(operationReturnValue) + } + + let signalProducer = SignalProducer.attempt(operation) + + expect(signalProducer).to(sendValue(operationReturnValue, sendError: nil, complete: true)) + } + + it("should send the error") { + let operationError = NSError(domain: "com.reactivecocoa.errordomain", code: 4815, userInfo: nil) + let operation: () -> Result = { + return .failure(operationError) + } + + let signalProducer = SignalProducer.attempt(operation) + + expect(signalProducer).to(sendValue(nil, sendError: operationError, complete: false)) + } + } + + describe("startWithSignal") { + it("should invoke the closure before any effects or events") { + var started = false + var value: Int? + + SignalProducer(value: 42) + .on(started: { + started = true + }, next: { + value = $0 + }) + .startWithSignal { _ in + expect(started) == false + expect(value).to(beNil()) + } + + expect(started) == true + expect(value) == 42 + } + + it("should dispose of added disposables if disposed") { + let addedDisposable = SimpleDisposable() + var disposable: Disposable! + + let producer = SignalProducer() { _, disposable in + disposable += addedDisposable + return + } + + producer.startWithSignal { _, innerDisposable in + disposable = innerDisposable + } + + expect(addedDisposable.isDisposed) == false + + disposable.dispose() + expect(addedDisposable.isDisposed) == true + } + + it("should send interrupted if disposed") { + var interrupted = false + var disposable: Disposable! + + SignalProducer(value: 42) + .start(on: TestScheduler()) + .startWithSignal { signal, innerDisposable in + signal.observeInterrupted { + interrupted = true + } + + disposable = innerDisposable + } + + expect(interrupted) == false + + disposable.dispose() + expect(interrupted) == true + } + + it("should release signal observers if disposed") { + weak var objectRetainedByObserver: NSObject? + var disposable: Disposable! + + let producer = SignalProducer.never + producer.startWithSignal { signal, innerDisposable in + let object = NSObject() + objectRetainedByObserver = object + signal.observeNext { _ in _ = object.description } + disposable = innerDisposable + } + + expect(objectRetainedByObserver).toNot(beNil()) + + disposable.dispose() + expect(objectRetainedByObserver).to(beNil()) + } + + it("should not trigger effects if disposed before closure return") { + var started = false + var value: Int? + + SignalProducer(value: 42) + .on(started: { + started = true + }, next: { + value = $0 + }) + .startWithSignal { _, disposable in + expect(started) == false + expect(value).to(beNil()) + + disposable.dispose() + } + + expect(started) == false + expect(value).to(beNil()) + } + + it("should send interrupted if disposed before closure return") { + var interrupted = false + + SignalProducer(value: 42) + .startWithSignal { signal, disposable in + expect(interrupted) == false + + signal.observeInterrupted { + interrupted = true + } + + disposable.dispose() + } + + expect(interrupted) == true + } + + it("should dispose of added disposables upon completion") { + let addedDisposable = SimpleDisposable() + var observer: Signal.Observer! + + let producer = SignalProducer() { incomingObserver, disposable in + disposable += addedDisposable + observer = incomingObserver + } + + producer.startWithSignal { _ in } + expect(addedDisposable.isDisposed) == false + + observer.sendCompleted() + expect(addedDisposable.isDisposed) == true + } + + it("should dispose of added disposables upon error") { + let addedDisposable = SimpleDisposable() + var observer: Signal.Observer! + + let producer = SignalProducer() { incomingObserver, disposable in + disposable += addedDisposable + observer = incomingObserver + } + + producer.startWithSignal { _ in } + expect(addedDisposable.isDisposed) == false + + observer.sendFailed(.default) + expect(addedDisposable.isDisposed) == true + } + } + + describe("start") { + it("should immediately begin sending events") { + let producer = SignalProducer(values: [1, 2]) + + var values: [Int] = [] + var completed = false + producer.start { event in + switch event { + case let .next(value): + values.append(value) + case .completed: + completed = true + default: + break + } + } + + expect(values) == [1, 2] + expect(completed) == true + } + + it("should send interrupted if disposed") { + let producer = SignalProducer<(), NoError>.never + + var interrupted = false + let disposable = producer.startWithInterrupted { + interrupted = true + } + + expect(interrupted) == false + + disposable.dispose() + expect(interrupted) == true + } + + it("should release observer when disposed") { + weak var objectRetainedByObserver: NSObject? + var disposable: Disposable! + let test = { + let producer = SignalProducer.never + let object = NSObject() + objectRetainedByObserver = object + disposable = producer.startWithNext { _ in _ = object } + } + + test() + expect(objectRetainedByObserver).toNot(beNil()) + + disposable.dispose() + expect(objectRetainedByObserver).to(beNil()) + } + + describe("trailing closure") { + it("receives next values") { + let (producer, observer) = SignalProducer.pipe() + + var values = [Int]() + producer.startWithNext { next in + values.append(next) + } + + observer.sendNext(1) + observer.sendNext(2) + observer.sendNext(3) + + observer.sendCompleted() + + expect(values) == [1, 2, 3] + } + + it("receives results") { + let (producer, observer) = SignalProducer.pipe() + + var results: [Result] = [] + producer.startWithResult { results.append($0) } + + observer.sendNext(1) + observer.sendNext(2) + observer.sendNext(3) + observer.sendFailed(.default) + + observer.sendCompleted() + + expect(results).to(haveCount(4)) + expect(results[0].value) == 1 + expect(results[1].value) == 2 + expect(results[2].value) == 3 + expect(results[3].error) == .default + } + } + } + + describe("lift") { + describe("over unary operators") { + it("should invoke transformation once per started signal") { + let baseProducer = SignalProducer(values: [1, 2]) + + var counter = 0 + let transform = { (signal: Signal) -> Signal in + counter += 1 + return signal + } + + let producer = baseProducer.lift(transform) + expect(counter) == 0 + + producer.start() + expect(counter) == 1 + + producer.start() + expect(counter) == 2 + } + + it("should not miss any events") { + let baseProducer = SignalProducer(values: [1, 2, 3, 4]) + + let producer = baseProducer.lift { signal in + return signal.map { $0 * $0 } + } + let result = producer.collect().single() + + expect(result?.value) == [1, 4, 9, 16] + } + } + + describe("over binary operators") { + it("should invoke transformation once per started signal") { + let baseProducer = SignalProducer(values: [1, 2]) + let otherProducer = SignalProducer(values: [3, 4]) + + var counter = 0 + let transform = { (signal: Signal) -> (Signal) -> Signal<(Int, Int), NoError> in + return { otherSignal in + counter += 1 + return Signal.zip(signal, otherSignal) + } + } + + let producer = baseProducer.lift(transform)(otherProducer) + expect(counter) == 0 + + producer.start() + expect(counter) == 1 + + producer.start() + expect(counter) == 2 + } + + it("should not miss any events") { + let baseProducer = SignalProducer(values: [1, 2, 3]) + let otherProducer = SignalProducer(values: [4, 5, 6]) + + let transform = { (signal: Signal) -> (Signal) -> Signal in + return { otherSignal in + return Signal.zip(signal, otherSignal).map { first, second in first + second } + } + } + + let producer = baseProducer.lift(transform)(otherProducer) + let result = producer.collect().single() + + expect(result?.value) == [5, 7, 9] + } + } + + describe("over binary operators with signal") { + it("should invoke transformation once per started signal") { + let baseProducer = SignalProducer(values: [1, 2]) + let (otherSignal, otherSignalObserver) = Signal.pipe() + + var counter = 0 + let transform = { (signal: Signal) -> (Signal) -> Signal<(Int, Int), NoError> in + return { otherSignal in + counter += 1 + return Signal.zip(signal, otherSignal) + } + } + + let producer = baseProducer.lift(transform)(otherSignal) + expect(counter) == 0 + + producer.start() + otherSignalObserver.sendNext(1) + expect(counter) == 1 + + producer.start() + otherSignalObserver.sendNext(2) + expect(counter) == 2 + } + + it("should not miss any events") { + let baseProducer = SignalProducer(values: [ 1, 2, 3 ]) + let (otherSignal, otherSignalObserver) = Signal.pipe() + + let transform = { (signal: Signal) -> (Signal) -> Signal in + return { otherSignal in + return Signal.zip(signal, otherSignal).map(+) + } + } + + let producer = baseProducer.lift(transform)(otherSignal) + var result: [Int] = [] + var completed: Bool = false + + producer.start { event in + switch event { + case .next(let value): result.append(value) + case .completed: completed = true + default: break + } + } + + otherSignalObserver.sendNext(4) + expect(result) == [ 5 ] + + otherSignalObserver.sendNext(5) + expect(result) == [ 5, 7 ] + + otherSignalObserver.sendNext(6) + expect(result) == [ 5, 7, 9 ] + expect(completed) == true + } + } + } + + describe("combineLatest") { + it("should combine the events to one array") { + let (producerA, observerA) = SignalProducer.pipe() + let (producerB, observerB) = SignalProducer.pipe() + + let producer = SignalProducer.combineLatest([producerA, producerB]) + + var values = [[Int]]() + producer.startWithNext { next in + values.append(next) + } + + observerA.sendNext(1) + observerB.sendNext(2) + observerA.sendNext(3) + observerA.sendCompleted() + observerB.sendCompleted() + + expect(values) == [[1, 2], [3, 2]] + } + + it("should start signal producers in order as defined") { + var ids = [Int]() + let createProducer = { (id: Int) -> SignalProducer in + return SignalProducer { observer, disposable in + ids.append(id) + + observer.sendNext(id) + observer.sendCompleted() + } + } + + let producerA = createProducer(1) + let producerB = createProducer(2) + + let producer = SignalProducer.combineLatest([producerA, producerB]) + + var values = [[Int]]() + producer.startWithNext { next in + values.append(next) + } + + expect(ids) == [1, 2] + expect(values) == [[1, 2]] + } + } + + describe("zip") { + it("should zip the events to one array") { + let producerA = SignalProducer(values: [ 1, 2 ]) + let producerB = SignalProducer(values: [ 3, 4 ]) + + let producer = SignalProducer.zip([producerA, producerB]) + let result = producer.collect().single() + + expect(result?.value) == [[1, 3], [2, 4]] + } + + it("should start signal producers in order as defined") { + var ids = [Int]() + let createProducer = { (id: Int) -> SignalProducer in + return SignalProducer { observer, disposable in + ids.append(id) + + observer.sendNext(id) + observer.sendCompleted() + } + } + + let producerA = createProducer(1) + let producerB = createProducer(2) + + let producer = SignalProducer.zip([producerA, producerB]) + + var values = [[Int]]() + producer.startWithNext { next in + values.append(next) + } + + expect(ids) == [1, 2] + expect(values) == [[1, 2]] + } + } + + describe("timer") { + it("should send the current date at the given interval") { + let scheduler = TestScheduler() + let producer = timer(interval: 1, on: scheduler, leeway: 0) + + let startDate = scheduler.currentDate + let tick1 = startDate.addingTimeInterval(1) + let tick2 = startDate.addingTimeInterval(2) + let tick3 = startDate.addingTimeInterval(3) + + var dates: [NSDate] = [] + producer.startWithNext { dates.append($0) } + + scheduler.advance(by: 0.9) + expect(dates) == [] + + scheduler.advance(by: 1) + expect(dates) == [tick1] + + scheduler.advance() + expect(dates) == [tick1] + + scheduler.advance(by: 0.2) + expect(dates) == [tick1, tick2] + + scheduler.advance(by: 1) + expect(dates) == [tick1, tick2, tick3] + } + + it("should release the signal when disposed") { + let scheduler = TestScheduler() + let producer = timer(interval: 1, on: scheduler, leeway: 0) + var interrupted = false + + weak var weakSignal: Signal? + producer.startWithSignal { signal, disposable in + weakSignal = signal + scheduler.schedule { + disposable.dispose() + } + signal.observeInterrupted { interrupted = true } + } + + expect(weakSignal).toNot(beNil()) + expect(interrupted) == false + + scheduler.run() + expect(weakSignal).to(beNil()) + expect(interrupted) == true + } + } + + describe("on") { + it("should attach event handlers to each started signal") { + let (baseProducer, observer) = SignalProducer.pipe() + + var starting = 0 + var started = 0 + var event = 0 + var next = 0 + var completed = 0 + var terminated = 0 + + let producer = baseProducer + .on(starting: { + starting += 1 + }, started: { + started += 1 + }, event: { e in + event += 1 + }, next: { n in + next += 1 + }, completed: { + completed += 1 + }, terminated: { + terminated += 1 + }) + + producer.start() + expect(starting) == 1 + expect(started) == 1 + + producer.start() + expect(starting) == 2 + expect(started) == 2 + + observer.sendNext(1) + expect(event) == 2 + expect(next) == 2 + + observer.sendCompleted() + expect(event) == 4 + expect(completed) == 2 + expect(terminated) == 2 + } + + it("should attach event handlers for disposal") { + let (baseProducer, _) = SignalProducer.pipe() + + var disposed: Bool = false + + let producer = baseProducer + .on(disposed: { disposed = true }) + + let disposable = producer.start() + + expect(disposed) == false + disposable.dispose() + expect(disposed) == true + } + + it("should invoke the `started` action of the inner producer first") { + let (baseProducer, _) = SignalProducer.pipe() + + var numbers = [Int]() + + _ = baseProducer + .on(started: { numbers.append(1) }) + .on(started: { numbers.append(2) }) + .on(started: { numbers.append(3) }) + .start() + + expect(numbers) == [1, 2, 3] + } + + it("should invoke the `starting` action of the outer producer first") { + let (baseProducer, _) = SignalProducer.pipe() + + var numbers = [Int]() + + _ = baseProducer + .on(starting: { numbers.append(1) }) + .on(starting: { numbers.append(2) }) + .on(starting: { numbers.append(3) }) + .start() + + expect(numbers) == [3, 2, 1] + } + } + + describe("startOn") { + it("should invoke effects on the given scheduler") { + let scheduler = TestScheduler() + var invoked = false + + let producer = SignalProducer() { _ in + invoked = true + } + + producer.start(on: scheduler).start() + expect(invoked) == false + + scheduler.advance() + expect(invoked) == true + } + + it("should forward events on their original scheduler") { + let startScheduler = TestScheduler() + let testScheduler = TestScheduler() + + let producer = timer(interval: 2, on: testScheduler, leeway: 0) + + var next: NSDate? + producer.start(on: startScheduler).startWithNext { next = $0 } + + startScheduler.advance(by: 2) + expect(next).to(beNil()) + + testScheduler.advance(by: 1) + expect(next).to(beNil()) + + testScheduler.advance(by: 1) + expect(next) == testScheduler.currentDate + } + } + + describe("flatMapError") { + it("should invoke the handler and start new producer for an error") { + let (baseProducer, baseObserver) = SignalProducer.pipe() + + var values: [Int] = [] + var completed = false + + baseProducer + .flatMapError { (error: TestError) -> SignalProducer in + expect(error) == TestError.default + expect(values) == [1] + + return .init(value: 2) + } + .start { event in + switch event { + case let .next(value): + values.append(value) + case .completed: + completed = true + default: + break + } + } + + baseObserver.sendNext(1) + baseObserver.sendFailed(.default) + + expect(values) == [1, 2] + expect(completed) == true + } + + it("should interrupt the replaced producer on disposal") { + let (baseProducer, baseObserver) = SignalProducer.pipe() + + var (disposed, interrupted) = (false, false) + let disposable = baseProducer + .flatMapError { (error: TestError) -> SignalProducer in + return SignalProducer { _, disposable in + disposable += ActionDisposable { disposed = true } + } + } + .startWithInterrupted { interrupted = true } + + baseObserver.sendFailed(.default) + disposable.dispose() + + expect(interrupted) == true + expect(disposed) == true + } + } + + describe("flatten") { + describe("FlattenStrategy.concat") { + describe("sequencing") { + var completePrevious: (() -> Void)! + var sendSubsequent: (() -> Void)! + var completeOuter: (() -> Void)! + + var subsequentStarted = false + + beforeEach { + let (outerProducer, outerObserver) = SignalProducer, NoError>.pipe() + let (previousProducer, previousObserver) = SignalProducer.pipe() + + subsequentStarted = false + let subsequentProducer = SignalProducer { _ in + subsequentStarted = true + } + + completePrevious = { previousObserver.sendCompleted() } + sendSubsequent = { outerObserver.sendNext(subsequentProducer) } + completeOuter = { outerObserver.sendCompleted() } + + outerProducer.flatten(.concat).start() + outerObserver.sendNext(previousProducer) + } + + it("should immediately start subsequent inner producer if previous inner producer has already completed") { + completePrevious() + sendSubsequent() + expect(subsequentStarted) == true + } + + context("with queued producers") { + beforeEach { + // Place the subsequent producer into `concat`'s queue. + sendSubsequent() + expect(subsequentStarted) == false + } + + it("should start subsequent inner producer upon completion of previous inner producer") { + completePrevious() + expect(subsequentStarted) == true + } + + it("should start subsequent inner producer upon completion of previous inner producer and completion of outer producer") { + completeOuter() + completePrevious() + expect(subsequentStarted) == true + } + } + } + + it("should forward an error from an inner producer") { + let errorProducer = SignalProducer(error: TestError.default) + let outerProducer = SignalProducer, TestError>(value: errorProducer) + + var error: TestError? + (outerProducer.flatten(.concat)).startWithFailed { e in + error = e + } + + expect(error) == TestError.default + } + + it("should forward an error from the outer producer") { + let (outerProducer, outerObserver) = SignalProducer, TestError>.pipe() + + var error: TestError? + outerProducer.flatten(.concat).startWithFailed { e in + error = e + } + + outerObserver.sendFailed(TestError.default) + expect(error) == TestError.default + } + + describe("completion") { + var completeOuter: (() -> Void)! + var completeInner: (() -> Void)! + + var completed = false + + beforeEach { + let (outerProducer, outerObserver) = SignalProducer, NoError>.pipe() + let (innerProducer, innerObserver) = SignalProducer.pipe() + + completeOuter = { outerObserver.sendCompleted() } + completeInner = { innerObserver.sendCompleted() } + + completed = false + outerProducer.flatten(.concat).startWithCompleted { + completed = true + } + + outerObserver.sendNext(innerProducer) + } + + it("should complete when inner producers complete, then outer producer completes") { + completeInner() + expect(completed) == false + + completeOuter() + expect(completed) == true + } + + it("should complete when outer producers completes, then inner producers complete") { + completeOuter() + expect(completed) == false + + completeInner() + expect(completed) == true + } + } + } + + describe("FlattenStrategy.merge") { + describe("behavior") { + var completeA: (() -> Void)! + var sendA: (() -> Void)! + var completeB: (() -> Void)! + var sendB: (() -> Void)! + + var outerCompleted = false + + var recv = [Int]() + + beforeEach { + let (outerProducer, outerObserver) = SignalProducer, NoError>.pipe() + let (producerA, observerA) = SignalProducer.pipe() + let (producerB, observerB) = SignalProducer.pipe() + + completeA = { observerA.sendCompleted() } + completeB = { observerB.sendCompleted() } + + var a = 0 + sendA = { observerA.sendNext(a); a += 1 } + + var b = 100 + sendB = { observerB.sendNext(b); b += 1 } + + outerProducer.flatten(.merge).start { event in + switch event { + case let .next(i): + recv.append(i) + case .completed: + outerCompleted = true + default: + break + } + } + + outerObserver.sendNext(producerA) + outerObserver.sendNext(producerB) + + outerObserver.sendCompleted() + } + + it("should forward values from any inner signals") { + sendA() + sendA() + sendB() + sendA() + sendB() + expect(recv) == [0, 1, 100, 2, 101] + } + + it("should complete when all signals have completed") { + completeA() + expect(outerCompleted) == false + completeB() + expect(outerCompleted) == true + } + } + + describe("error handling") { + it("should forward an error from an inner signal") { + let errorProducer = SignalProducer(error: TestError.default) + let outerProducer = SignalProducer, TestError>(value: errorProducer) + + var error: TestError? + outerProducer.flatten(.merge).startWithFailed { e in + error = e + } + expect(error) == TestError.default + } + + it("should forward an error from the outer signal") { + let (outerProducer, outerObserver) = SignalProducer, TestError>.pipe() + + var error: TestError? + outerProducer.flatten(.merge).startWithFailed { e in + error = e + } + + outerObserver.sendFailed(TestError.default) + expect(error) == TestError.default + } + } + } + + describe("FlattenStrategy.latest") { + it("should forward values from the latest inner signal") { + let (outer, outerObserver) = SignalProducer, TestError>.pipe() + let (firstInner, firstInnerObserver) = SignalProducer.pipe() + let (secondInner, secondInnerObserver) = SignalProducer.pipe() + + var receivedValues: [Int] = [] + var errored = false + var completed = false + + outer.flatten(.latest).start { event in + switch event { + case let .next(value): + receivedValues.append(value) + case .completed: + completed = true + case .failed: + errored = true + case .interrupted: + break + } + } + + outerObserver.sendNext(SignalProducer(value: 0)) + outerObserver.sendNext(firstInner) + firstInnerObserver.sendNext(1) + outerObserver.sendNext(secondInner) + secondInnerObserver.sendNext(2) + outerObserver.sendCompleted() + + expect(receivedValues) == [ 0, 1, 2 ] + expect(errored) == false + expect(completed) == false + + firstInnerObserver.sendNext(3) + firstInnerObserver.sendCompleted() + secondInnerObserver.sendNext(4) + secondInnerObserver.sendCompleted() + + expect(receivedValues) == [ 0, 1, 2, 4 ] + expect(errored) == false + expect(completed) == true + } + + it("should forward an error from an inner signal") { + let inner = SignalProducer(error: .default) + let outer = SignalProducer, TestError>(value: inner) + + let result = outer.flatten(.latest).first() + expect(result?.error) == TestError.default + } + + it("should forward an error from the outer signal") { + let outer = SignalProducer, TestError>(error: .default) + + let result = outer.flatten(.latest).first() + expect(result?.error) == TestError.default + } + + it("should complete when the original and latest signals have completed") { + let inner = SignalProducer.empty + let outer = SignalProducer, TestError>(value: inner) + + var completed = false + outer.flatten(.latest).startWithCompleted { + completed = true + } + + expect(completed) == true + } + + it("should complete when the outer signal completes before sending any signals") { + let outer = SignalProducer, TestError>.empty + + var completed = false + outer.flatten(.latest).startWithCompleted { + completed = true + } + + expect(completed) == true + } + + it("should not deadlock") { + let producer = SignalProducer(value: 1) + .flatMap(.latest) { _ in SignalProducer(value: 10) } + + let result = producer.take(first: 1).last() + expect(result?.value) == 10 + } + } + + describe("interruption") { + var innerObserver: Signal<(), NoError>.Observer! + var outerObserver: Signal, NoError>.Observer! + var execute: ((FlattenStrategy) -> Void)! + + var interrupted = false + var completed = false + + beforeEach { + let (innerProducer, incomingInnerObserver) = SignalProducer<(), NoError>.pipe() + let (outerProducer, incomingOuterObserver) = SignalProducer, NoError>.pipe() + + innerObserver = incomingInnerObserver + outerObserver = incomingOuterObserver + + execute = { strategy in + interrupted = false + completed = false + + outerProducer + .flatten(strategy) + .start { event in + switch event { + case .interrupted: + interrupted = true + case .completed: + completed = true + default: + break + } + } + } + + incomingOuterObserver.sendNext(innerProducer) + } + + describe("Concat") { + it("should drop interrupted from an inner producer") { + execute(.concat) + + innerObserver.sendInterrupted() + expect(interrupted) == false + expect(completed) == false + + outerObserver.sendCompleted() + expect(completed) == true + } + + it("should forward interrupted from the outer producer") { + execute(.concat) + outerObserver.sendInterrupted() + expect(interrupted) == true + } + } + + describe("Latest") { + it("should drop interrupted from an inner producer") { + execute(.latest) + + innerObserver.sendInterrupted() + expect(interrupted) == false + expect(completed) == false + + outerObserver.sendCompleted() + expect(completed) == true + } + + it("should forward interrupted from the outer producer") { + execute(.latest) + outerObserver.sendInterrupted() + expect(interrupted) == true + } + } + + describe("Merge") { + it("should drop interrupted from an inner producer") { + execute(.merge) + + innerObserver.sendInterrupted() + expect(interrupted) == false + expect(completed) == false + + outerObserver.sendCompleted() + expect(completed) == true + } + + it("should forward interrupted from the outer producer") { + execute(.merge) + outerObserver.sendInterrupted() + expect(interrupted) == true + } + } + } + + describe("disposal") { + var completeOuter: (() -> Void)! + var disposeOuter: (() -> Void)! + var execute: ((FlattenStrategy) -> Void)! + + var innerDisposable = SimpleDisposable() + var interrupted = false + + beforeEach { + execute = { strategy in + let (outerProducer, outerObserver) = SignalProducer, NoError>.pipe() + + innerDisposable = SimpleDisposable() + let innerProducer = SignalProducer { $1.add(innerDisposable) } + + interrupted = false + let outerDisposable = outerProducer.flatten(strategy).startWithInterrupted { + interrupted = true + } + + completeOuter = outerObserver.sendCompleted + disposeOuter = outerDisposable.dispose + + outerObserver.sendNext(innerProducer) + } + } + + describe("Concat") { + it("should cancel inner work when disposed before the outer producer completes") { + execute(.concat) + + expect(innerDisposable.isDisposed) == false + expect(interrupted) == false + disposeOuter() + + expect(innerDisposable.isDisposed) == true + expect(interrupted) == true + } + + it("should cancel inner work when disposed after the outer producer completes") { + execute(.concat) + + completeOuter() + + expect(innerDisposable.isDisposed) == false + expect(interrupted) == false + disposeOuter() + + expect(innerDisposable.isDisposed) == true + expect(interrupted) == true + } + } + + describe("Latest") { + it("should cancel inner work when disposed before the outer producer completes") { + execute(.latest) + + expect(innerDisposable.isDisposed) == false + expect(interrupted) == false + disposeOuter() + + expect(innerDisposable.isDisposed) == true + expect(interrupted) == true + } + + it("should cancel inner work when disposed after the outer producer completes") { + execute(.latest) + + completeOuter() + + expect(innerDisposable.isDisposed) == false + expect(interrupted) == false + disposeOuter() + + expect(innerDisposable.isDisposed) == true + expect(interrupted) == true + } + } + + describe("Merge") { + it("should cancel inner work when disposed before the outer producer completes") { + execute(.merge) + + expect(innerDisposable.isDisposed) == false + expect(interrupted) == false + disposeOuter() + + expect(innerDisposable.isDisposed) == true + expect(interrupted) == true + } + + it("should cancel inner work when disposed after the outer producer completes") { + execute(.merge) + + completeOuter() + + expect(innerDisposable.isDisposed) == false + expect(interrupted) == false + disposeOuter() + + expect(innerDisposable.isDisposed) == true + expect(interrupted) == true + } + } + } + } + + describe("times") { + it("should start a signal N times upon completion") { + let original = SignalProducer(values: [ 1, 2, 3 ]) + let producer = original.times(3) + + let result = producer.collect().single() + expect(result?.value) == [ 1, 2, 3, 1, 2, 3, 1, 2, 3 ] + } + + it("should produce an equivalent signal producer if count is 1") { + let original = SignalProducer(value: 1) + let producer = original.times(1) + + let result = producer.collect().single() + expect(result?.value) == [ 1 ] + } + + it("should produce an empty signal if count is 0") { + let original = SignalProducer(value: 1) + let producer = original.times(0) + + let result = producer.first() + expect(result).to(beNil()) + } + + it("should not repeat upon error") { + let results: [Result] = [ + .success(1), + .success(2), + .failure(.default) + ] + + let original = SignalProducer.attemptWithResults(results) + let producer = original.times(3) + + let events = producer + .materialize() + .collect() + .single() + let result = events?.value + + let expectedEvents: [Event] = [ + .next(1), + .next(2), + .failed(.default) + ] + + // TODO: if let result = result where result.count == expectedEvents.count + if result?.count != expectedEvents.count { + fail("Invalid result: \(result)") + } else { + // Can't test for equality because Array is not Equatable, + // and neither is Event. + expect(result![0] == expectedEvents[0]) == true + expect(result![1] == expectedEvents[1]) == true + expect(result![2] == expectedEvents[2]) == true + } + } + + it("should evaluate lazily") { + let original = SignalProducer(value: 1) + let producer = original.times(Int.max) + + let result = producer.take(first: 1).single() + expect(result?.value) == 1 + } + } + + describe("retry") { + it("should start a signal N times upon error") { + let results: [Result] = [ + .failure(.error1), + .failure(.error2), + .success(1) + ] + + let original = SignalProducer.attemptWithResults(results) + let producer = original.retry(upTo: 2) + + let result = producer.single() + + expect(result?.value) == 1 + } + + it("should forward errors that occur after all retries") { + let results: [Result] = [ + .failure(.default), + .failure(.error1), + .failure(.error2), + ] + + let original = SignalProducer.attemptWithResults(results) + let producer = original.retry(upTo: 2) + + let result = producer.single() + + expect(result?.error) == TestError.error2 + } + + it("should not retry upon completion") { + let results: [Result] = [ + .success(1), + .success(2), + .success(3) + ] + + let original = SignalProducer.attemptWithResults(results) + let producer = original.retry(upTo: 2) + + let result = producer.single() + expect(result?.value) == 1 + } + } + + describe("then") { + it("should start the subsequent producer after the completion of the original") { + let (original, observer) = SignalProducer.pipe() + + var subsequentStarted = false + let subsequent = SignalProducer { observer, _ in + subsequentStarted = true + } + + let producer = original.then(subsequent) + producer.start() + expect(subsequentStarted) == false + + observer.sendCompleted() + expect(subsequentStarted) == true + } + + it("should forward errors from the original producer") { + let original = SignalProducer(error: .default) + let subsequent = SignalProducer.empty + + let result = original.then(subsequent).first() + expect(result?.error) == TestError.default + } + + it("should forward errors from the subsequent producer") { + let original = SignalProducer.empty + let subsequent = SignalProducer(error: .default) + + let result = original.then(subsequent).first() + expect(result?.error) == TestError.default + } + + it("should forward interruptions from the original producer") { + let (original, observer) = SignalProducer.pipe() + + var subsequentStarted = false + let subsequent = SignalProducer { observer, _ in + subsequentStarted = true + } + + var interrupted = false + let producer = original.then(subsequent) + producer.startWithInterrupted { + interrupted = true + } + expect(subsequentStarted) == false + + observer.sendInterrupted() + expect(interrupted) == true + } + + it("should complete when both inputs have completed") { + let (original, originalObserver) = SignalProducer.pipe() + let (subsequent, subsequentObserver) = SignalProducer.pipe() + + let producer = original.then(subsequent) + + var completed = false + producer.startWithCompleted { + completed = true + } + + originalObserver.sendCompleted() + expect(completed) == false + + subsequentObserver.sendCompleted() + expect(completed) == true + } + } + + describe("first") { + it("should start a signal then block on the first value") { + let (_signal, observer) = Signal.pipe() + + let forwardingScheduler: QueueScheduler + + if #available(OSX 10.10, *) { + forwardingScheduler = QueueScheduler(qos: .default, name: "\(#file):\(#line)") + } else { + forwardingScheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) + } + + let producer = SignalProducer(signal: _signal.delay(0.1, on: forwardingScheduler)) + + let observingScheduler: QueueScheduler + + if #available(OSX 10.10, *) { + observingScheduler = QueueScheduler(qos: .default, name: "\(#file):\(#line)") + } else { + observingScheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) + } + + var result: Int? + + observingScheduler.schedule { + result = producer.first()?.value + } + + expect(result).to(beNil()) + + observer.sendNext(1) + expect(result).toEventually(be(1), timeout: 5.0) + } + + it("should return a nil result if no values are sent before completion") { + let result = SignalProducer.empty.first() + expect(result).to(beNil()) + } + + it("should return the first value if more than one value is sent") { + let result = SignalProducer(values: [ 1, 2 ]).first() + expect(result?.value) == 1 + } + + it("should return an error if one occurs before the first value") { + let result = SignalProducer(error: .default).first() + expect(result?.error) == TestError.default + } + } + + describe("single") { + it("should start a signal then block until completion") { + let (_signal, observer) = Signal.pipe() + let forwardingScheduler: QueueScheduler + + if #available(OSX 10.10, *) { + forwardingScheduler = QueueScheduler(qos: .default, name: "\(#file):\(#line)") + } else { + forwardingScheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) + } + + let producer = SignalProducer(signal: _signal.delay(0.1, on: forwardingScheduler)) + + let observingScheduler: QueueScheduler + + if #available(OSX 10.10, *) { + observingScheduler = QueueScheduler(qos: .default, name: "\(#file):\(#line)") + } else { + observingScheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) + } + + var result: Int? + + observingScheduler.schedule { + result = producer.single()?.value + } + expect(result).to(beNil()) + + observer.sendNext(1) + + Thread.sleep(forTimeInterval: 3.0) + expect(result).to(beNil()) + + observer.sendCompleted() + expect(result).toEventually(be(1)) + } + + it("should return a nil result if no values are sent before completion") { + let result = SignalProducer.empty.single() + expect(result).to(beNil()) + } + + it("should return a nil result if more than one value is sent before completion") { + let result = SignalProducer(values: [ 1, 2 ]).single() + expect(result).to(beNil()) + } + + it("should return an error if one occurs") { + let result = SignalProducer(error: .default).single() + expect(result?.error) == TestError.default + } + } + + describe("last") { + it("should start a signal then block until completion") { + let (_signal, observer) = Signal.pipe() + let scheduler: QueueScheduler + + if #available(*, OSX 10.10) { + scheduler = QueueScheduler(name: "\(#file):\(#line)") + } else { + scheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) + } + let producer = SignalProducer(signal: _signal.delay(0.1, on: scheduler)) + + var result: Result? + + let group = DispatchGroup() + + let globalQueue: DispatchQueue + if #available(*, OSX 10.10) { + globalQueue = DispatchQueue.global() + } else { + globalQueue = DispatchQueue.global(priority: .default) + } + + globalQueue.async(group: group, flags: []) { + result = producer.last() + } + expect(result).to(beNil()) + + observer.sendNext(1) + observer.sendNext(2) + expect(result).to(beNil()) + + observer.sendCompleted() + group.wait() + + expect(result?.value) == 2 + } + + it("should return a nil result if no values are sent before completion") { + let result = SignalProducer.empty.last() + expect(result).to(beNil()) + } + + it("should return the last value if more than one value is sent") { + let result = SignalProducer(values: [ 1, 2 ]).last() + expect(result?.value) == 2 + } + + it("should return an error if one occurs") { + let result = SignalProducer(error: .default).last() + expect(result?.error) == TestError.default + } + } + + describe("wait") { + it("should start a signal then block until completion") { + let (_signal, observer) = Signal.pipe() + let scheduler: QueueScheduler + if #available(*, OSX 10.10) { + scheduler = QueueScheduler(name: "\(#file):\(#line)") + } else { + scheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) + } + let producer = SignalProducer(signal: _signal.delay(0.1, on: scheduler)) + + var result: Result<(), NoError>? + + let group = DispatchGroup() + + let globalQueue: DispatchQueue + if #available(*, OSX 10.10) { + globalQueue = DispatchQueue.global() + } else { + globalQueue = DispatchQueue.global(priority: .default) + } + + globalQueue.async(group: group, flags: []) { + result = producer.wait() + } + + expect(result).to(beNil()) + + observer.sendCompleted() + group.wait() + + expect(result?.value).toNot(beNil()) + } + + it("should return an error if one occurs") { + let result = SignalProducer(error: .default).wait() + expect(result.error) == TestError.default + } + } + + describe("observeOn") { + it("should immediately cancel upstream producer's work when disposed") { + var upstreamDisposable: Disposable! + let producer = SignalProducer<(), NoError>{ _, innerDisposable in + upstreamDisposable = innerDisposable + } + + var downstreamDisposable: Disposable! + producer + .observe(on: TestScheduler()) + .startWithSignal { signal, innerDisposable in + downstreamDisposable = innerDisposable + } + + expect(upstreamDisposable.isDisposed) == false + + downstreamDisposable.dispose() + expect(upstreamDisposable.isDisposed) == true + } + } + + describe("take") { + it("Should not start concat'ed producer if the first one sends a value when using take(1)") { + let scheduler: QueueScheduler + if #available(OSX 10.10, *) { + scheduler = QueueScheduler(name: "\(#file):\(#line)") + } else { + scheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) + } + + // Delaying producer1 from sending a value to test whether producer2 is started in the mean-time. + let producer1 = SignalProducer() { handler, _ in + handler.sendNext(1) + handler.sendCompleted() + }.start(on: scheduler) + + var started = false + let producer2 = SignalProducer() { handler, _ in + started = true + handler.sendNext(2) + handler.sendCompleted() + } + + let result = producer1.concat(producer2).take(first: 1).collect().first() + + expect(result?.value) == [1] + expect(started) == false + } + } + + describe("replayLazily") { + var producer: SignalProducer! + var observer: SignalProducer.ProducedSignal.Observer! + + var replayedProducer: SignalProducer! + + beforeEach { + let (producerTemp, observerTemp) = SignalProducer.pipe() + producer = producerTemp + observer = observerTemp + + replayedProducer = producer.replayLazily(upTo: 2) + } + + context("subscribing to underlying producer") { + it("emits new values") { + var last: Int? + + replayedProducer + .assumeNoErrors() + .startWithNext { last = $0 } + + expect(last).to(beNil()) + + observer.sendNext(1) + expect(last) == 1 + + observer.sendNext(2) + expect(last) == 2 + } + + it("emits errors") { + var error: TestError? + + replayedProducer.startWithFailed { error = $0 } + expect(error).to(beNil()) + + observer.sendFailed(.default) + expect(error) == TestError.default + } + } + + context("buffers past values") { + it("emits last value upon subscription") { + let disposable = replayedProducer + .start() + + observer.sendNext(1) + disposable.dispose() + + var last: Int? + + replayedProducer + .assumeNoErrors() + .startWithNext { last = $0 } + expect(last) == 1 + } + + it("emits previous failure upon subscription") { + let disposable = replayedProducer + .start() + + observer.sendFailed(.default) + disposable.dispose() + + var error: TestError? + + replayedProducer + .startWithFailed { error = $0 } + expect(error) == TestError.default + } + + it("emits last n values upon subscription") { + var disposable = replayedProducer + .start() + + observer.sendNext(1) + observer.sendNext(2) + observer.sendNext(3) + observer.sendNext(4) + disposable.dispose() + + var values: [Int] = [] + + disposable = replayedProducer + .assumeNoErrors() + .startWithNext { values.append($0) } + expect(values) == [ 3, 4 ] + + observer.sendNext(5) + expect(values) == [ 3, 4, 5 ] + + disposable.dispose() + values = [] + + replayedProducer + .assumeNoErrors() + .startWithNext { values.append($0) } + expect(values) == [ 4, 5 ] + } + } + + context("starting underying producer") { + it("starts lazily") { + var started = false + + let producer = SignalProducer(value: 0) + .on(started: { started = true }) + expect(started) == false + + let replayedProducer = producer + .replayLazily(upTo: 1) + expect(started) == false + + replayedProducer.start() + expect(started) == true + } + + it("shares a single subscription") { + var startedTimes = 0 + + let producer = SignalProducer.never + .on(started: { startedTimes += 1 }) + expect(startedTimes) == 0 + + let replayedProducer = producer + .replayLazily(upTo: 1) + expect(startedTimes) == 0 + + replayedProducer.start() + expect(startedTimes) == 1 + + replayedProducer.start() + expect(startedTimes) == 1 + } + + it("does not start multiple times when subscribing multiple times") { + var startedTimes = 0 + + let producer = SignalProducer(value: 0) + .on(started: { startedTimes += 1 }) + + let replayedProducer = producer + .replayLazily(upTo: 1) + + expect(startedTimes) == 0 + replayedProducer.start().dispose() + expect(startedTimes) == 1 + replayedProducer.start().dispose() + expect(startedTimes) == 1 + } + + it("does not start again if it finished") { + var startedTimes = 0 + + let producer = SignalProducer.empty + .on(started: { startedTimes += 1 }) + expect(startedTimes) == 0 + + let replayedProducer = producer + .replayLazily(upTo: 1) + expect(startedTimes) == 0 + + replayedProducer.start() + expect(startedTimes) == 1 + + replayedProducer.start() + expect(startedTimes) == 1 + } + } + + context("lifetime") { + it("does not dispose underlying subscription if the replayed producer is still in memory") { + var disposed = false + + let producer = SignalProducer.never + .on(disposed: { disposed = true }) + + let replayedProducer = producer + .replayLazily(upTo: 1) + + expect(disposed) == false + let disposable = replayedProducer.start() + expect(disposed) == false + + disposable.dispose() + expect(disposed) == false + } + + it("does not dispose if it has active subscriptions") { + var disposed = false + + let producer = SignalProducer.never + .on(disposed: { disposed = true }) + + var replayedProducer = ImplicitlyUnwrappedOptional(producer.replayLazily(upTo: 1)) + + expect(disposed) == false + let disposable1 = replayedProducer?.start() + let disposable2 = replayedProducer?.start() + expect(disposed) == false + + replayedProducer = nil + expect(disposed) == false + + disposable1?.dispose() + expect(disposed) == false + + disposable2?.dispose() + expect(disposed) == true + } + + it("disposes underlying producer when the producer is deallocated") { + var disposed = false + + let producer = SignalProducer.never + .on(disposed: { disposed = true }) + + var replayedProducer = ImplicitlyUnwrappedOptional(producer.replayLazily(upTo: 1)) + + expect(disposed) == false + let disposable = replayedProducer?.start() + expect(disposed) == false + + disposable?.dispose() + expect(disposed) == false + + replayedProducer = nil + expect(disposed) == true + } + + it("does not leak buffered values") { + final class Value { + private let deinitBlock: () -> Void + + init(deinitBlock: () -> Void) { + self.deinitBlock = deinitBlock + } + + deinit { + self.deinitBlock() + } + } + + var deinitValues = 0 + + var producer: SignalProducer! = SignalProducer(value: Value { + deinitValues += 1 + }) + expect(deinitValues) == 0 + + var replayedProducer: SignalProducer! = producer + .replayLazily(upTo: 1) + + let disposable = replayedProducer + .start() + + disposable.dispose() + expect(deinitValues) == 0 + + producer = nil + expect(deinitValues) == 0 + + replayedProducer = nil + expect(deinitValues) == 1 + } + } + + describe("log events") { + it("should output the correct event") { + let expectations: [(String) -> Void] = [ + { event in expect(event) == "[] started" }, + { event in expect(event) == "[] next 1" }, + { event in expect(event) == "[] completed" }, + { event in expect(event) == "[] terminated" }, + { event in expect(event) == "[] disposed" } + ] + + let logger = TestLogger(expectations: expectations) + + let (producer, observer) = SignalProducer.pipe() + producer + .logEvents(logger: logger.logEvent) + .start() + + observer.sendNext(1) + observer.sendCompleted() + } + } + + describe("init(values) ambiguity") { + it("should not be a SignalProducer, NoError>") { + + let producer1: SignalProducer = SignalProducer.empty + let producer2: SignalProducer = SignalProducer.empty + + // This expression verifies at compile time that the type is as expected. + let _: SignalProducer = SignalProducer(values: [producer1, producer2]) + .flatten(.merge) + } + } + } + } +} + +// MARK: - Helpers + +extension SignalProducer { + internal static func pipe() -> (SignalProducer, ProducedSignal.Observer) { + let (signal, observer) = ProducedSignal.pipe() + let producer = SignalProducer(signal: signal) + return (producer, observer) + } + + /// Creates a producer that can be started as many times as elements in `results`. + /// Each signal will immediately send either a value or an error. + private static func attemptWithResults, C.IndexDistance == C.Index, C.Index == Int>(_ results: C) -> SignalProducer { + let resultCount = results.count + var operationIndex = 0 + + precondition(resultCount > 0) + + let operation: () -> Result = { + if operationIndex < resultCount { + defer { + operationIndex += 1 + } + + return results[results.index(results.startIndex, offsetBy: operationIndex)] + } else { + fail("Operation started too many times") + + return results[results.startIndex] + } + } + + return SignalProducer.attempt(operation) + } +} diff --git a/ReactiveSwiftTests/SignalSpec.swift b/ReactiveSwiftTests/SignalSpec.swift new file mode 100755 index 0000000000..39f70a2c7d --- /dev/null +++ b/ReactiveSwiftTests/SignalSpec.swift @@ -0,0 +1,2269 @@ +// +// SignalSpec.swift +// ReactiveSwift +// +// Created by Justin Spahr-Summers on 2015-01-23. +// Copyright (c) 2015 GitHub. All rights reserved. +// + +import Result +import Nimble +import Quick +import ReactiveSwift + +class SignalSpec: QuickSpec { + override func spec() { + describe("init") { + var testScheduler: TestScheduler! + + beforeEach { + testScheduler = TestScheduler() + } + + it("should run the generator immediately") { + var didRunGenerator = false + _ = Signal { observer in + didRunGenerator = true + return nil + } + + expect(didRunGenerator) == true + } + + it("should forward events to observers") { + let numbers = [ 1, 2, 5 ] + + let signal: Signal = Signal { observer in + testScheduler.schedule { + for number in numbers { + observer.sendNext(number) + } + observer.sendCompleted() + } + return nil + } + + var fromSignal: [Int] = [] + var completed = false + + signal.observe { event in + switch event { + case let .next(number): + fromSignal.append(number) + case .completed: + completed = true + default: + break + } + } + + expect(completed) == false + expect(fromSignal).to(beEmpty()) + + testScheduler.run() + + expect(completed) == true + expect(fromSignal) == numbers + } + + it("should dispose of returned disposable upon error") { + let disposable = SimpleDisposable() + + let signal: Signal = Signal { observer in + testScheduler.schedule { + observer.sendFailed(TestError.default) + } + return disposable + } + + var errored = false + + signal.observeFailed { _ in errored = true } + + expect(errored) == false + expect(disposable.isDisposed) == false + + testScheduler.run() + + expect(errored) == true + expect(disposable.isDisposed) == true + } + + it("should dispose of returned disposable upon completion") { + let disposable = SimpleDisposable() + + let signal: Signal = Signal { observer in + testScheduler.schedule { + observer.sendCompleted() + } + return disposable + } + + var completed = false + + signal.observeCompleted { completed = true } + + expect(completed) == false + expect(disposable.isDisposed) == false + + testScheduler.run() + + expect(completed) == true + expect(disposable.isDisposed) == true + } + + it("should dispose of returned disposable upon interrupted") { + let disposable = SimpleDisposable() + + let signal: Signal = Signal { observer in + testScheduler.schedule { + observer.sendInterrupted() + } + return disposable + } + + var interrupted = false + signal.observeInterrupted { + interrupted = true + } + + expect(interrupted) == false + expect(disposable.isDisposed) == false + + testScheduler.run() + + expect(interrupted) == true + expect(disposable.isDisposed) == true + } + } + + describe("Signal.empty") { + it("should interrupt its observers without emitting any value") { + let signal = Signal<(), NoError>.empty + + var hasUnexpectedEventsEmitted = false + var signalInterrupted = false + + signal.observe { event in + switch event { + case .next, .failed, .completed: + hasUnexpectedEventsEmitted = true + case .interrupted: + signalInterrupted = true + } + } + + expect(hasUnexpectedEventsEmitted) == false + expect(signalInterrupted) == true + } + } + + describe("Signal.pipe") { + it("should forward events to observers") { + let (signal, observer) = Signal.pipe() + + var fromSignal: [Int] = [] + var completed = false + + signal.observe { event in + switch event { + case let .next(number): + fromSignal.append(number) + case .completed: + completed = true + default: + break + } + } + + expect(fromSignal).to(beEmpty()) + expect(completed) == false + + observer.sendNext(1) + expect(fromSignal) == [ 1 ] + + observer.sendNext(2) + expect(fromSignal) == [ 1, 2 ] + + expect(completed) == false + observer.sendCompleted() + expect(completed) == true + } + + context("memory") { + it("should not crash allocating memory with a few observers") { + let (signal, _) = Signal.pipe() + + for _ in 0..<50 { + autoreleasepool { + let disposable = signal.observe { _ in } + + disposable!.dispose() + } + } + } + } + } + + describe("observe") { + var testScheduler: TestScheduler! + + beforeEach { + testScheduler = TestScheduler() + } + + it("should stop forwarding events when disposed") { + let disposable = SimpleDisposable() + + let signal: Signal = Signal { observer in + testScheduler.schedule { + for number in [ 1, 2 ] { + observer.sendNext(number) + } + observer.sendCompleted() + observer.sendNext(4) + } + return disposable + } + + var fromSignal: [Int] = [] + signal.observeNext { number in + fromSignal.append(number) + } + + expect(disposable.isDisposed) == false + expect(fromSignal).to(beEmpty()) + + testScheduler.run() + + expect(disposable.isDisposed) == true + expect(fromSignal) == [ 1, 2 ] + } + + it("should not trigger side effects") { + var runCount = 0 + let signal: Signal<(), NoError> = Signal { observer in + runCount += 1 + return nil + } + + expect(runCount) == 1 + + signal.observe(Observer<(), NoError>()) + expect(runCount) == 1 + } + + it("should release observer after termination") { + weak var testStr: NSMutableString? + let (signal, observer) = Signal.pipe() + + let test = { + let innerStr: NSMutableString = NSMutableString() + signal.observeNext { value in + innerStr.append("\(value)") + } + testStr = innerStr + } + test() + + observer.sendNext(1) + expect(testStr) == "1" + observer.sendNext(2) + expect(testStr) == "12" + + observer.sendCompleted() + expect(testStr).to(beNil()) + } + + it("should release observer after interruption") { + weak var testStr: NSMutableString? + let (signal, observer) = Signal.pipe() + + let test = { + let innerStr: NSMutableString = NSMutableString() + signal.observeNext { value in + innerStr.append("\(value)") + } + + testStr = innerStr + } + + test() + + observer.sendNext(1) + expect(testStr) == "1" + + observer.sendNext(2) + expect(testStr) == "12" + + observer.sendInterrupted() + expect(testStr).to(beNil()) + } + } + + describe("trailing closure") { + it("receives next values") { + var values = [Int]() + let (signal, observer) = Signal.pipe() + + signal.observeNext { next in + values.append(next) + } + + observer.sendNext(1) + expect(values) == [1] + } + + it("receives results") { + let (signal, observer) = Signal.pipe() + + var results: [Result] = [] + signal.observeResult { results.append($0) } + + observer.sendNext(1) + observer.sendNext(2) + observer.sendNext(3) + observer.sendFailed(.default) + + observer.sendCompleted() + + expect(results).to(haveCount(4)) + expect(results[0].value) == 1 + expect(results[1].value) == 2 + expect(results[2].value) == 3 + expect(results[3].error) == .default + } + } + + describe("map") { + it("should transform the values of the signal") { + let (signal, observer) = Signal.pipe() + let mappedSignal = signal.map { String($0 + 1) } + + var lastValue: String? + + mappedSignal.observeNext { + lastValue = $0 + return + } + + expect(lastValue).to(beNil()) + + observer.sendNext(0) + expect(lastValue) == "1" + + observer.sendNext(1) + expect(lastValue) == "2" + } + } + + + describe("mapError") { + it("should transform the errors of the signal") { + let (signal, observer) = Signal.pipe() + let producerError = NSError(domain: "com.reactivecocoa.errordomain", code: 100, userInfo: nil) + var error: NSError? + + signal + .mapError { _ in producerError } + .observeFailed { err in error = err } + + expect(error).to(beNil()) + + observer.sendFailed(TestError.default) + expect(error) == producerError + } + } + + describe("filter") { + it("should omit values from the signal") { + let (signal, observer) = Signal.pipe() + let mappedSignal = signal.filter { $0 % 2 == 0 } + + var lastValue: Int? + + mappedSignal.observeNext { lastValue = $0 } + + expect(lastValue).to(beNil()) + + observer.sendNext(0) + expect(lastValue) == 0 + + observer.sendNext(1) + expect(lastValue) == 0 + + observer.sendNext(2) + expect(lastValue) == 2 + } + } + + describe("skipNil") { + it("should forward only non-nil values") { + let (signal, observer) = Signal.pipe() + let mappedSignal = signal.skipNil() + + var lastValue: Int? + + mappedSignal.observeNext { lastValue = $0 } + expect(lastValue).to(beNil()) + + observer.sendNext(nil) + expect(lastValue).to(beNil()) + + observer.sendNext(1) + expect(lastValue) == 1 + + observer.sendNext(nil) + expect(lastValue) == 1 + + observer.sendNext(2) + expect(lastValue) == 2 + } + } + + describe("scan") { + it("should incrementally accumulate a value") { + let (baseSignal, observer) = Signal.pipe() + let signal = baseSignal.scan("", +) + + var lastValue: String? + + signal.observeNext { lastValue = $0 } + + expect(lastValue).to(beNil()) + + observer.sendNext("a") + expect(lastValue) == "a" + + observer.sendNext("bb") + expect(lastValue) == "abb" + } + } + + describe("reduce") { + it("should accumulate one value") { + let (baseSignal, observer) = Signal.pipe() + let signal = baseSignal.reduce(1, +) + + var lastValue: Int? + var completed = false + + signal.observe { event in + switch event { + case let .next(value): + lastValue = value + case .completed: + completed = true + default: + break + } + } + + expect(lastValue).to(beNil()) + + observer.sendNext(1) + expect(lastValue).to(beNil()) + + observer.sendNext(2) + expect(lastValue).to(beNil()) + + expect(completed) == false + observer.sendCompleted() + expect(completed) == true + + expect(lastValue) == 4 + } + + it("should send the initial value if none are received") { + let (baseSignal, observer) = Signal.pipe() + let signal = baseSignal.reduce(1, +) + + var lastValue: Int? + var completed = false + + signal.observe { event in + switch event { + case let .next(value): + lastValue = value + case .completed: + completed = true + default: + break + } + } + + expect(lastValue).to(beNil()) + expect(completed) == false + + observer.sendCompleted() + + expect(lastValue) == 1 + expect(completed) == true + } + } + + describe("skip") { + it("should skip initial values") { + let (baseSignal, observer) = Signal.pipe() + let signal = baseSignal.skip(first: 1) + + var lastValue: Int? + signal.observeNext { lastValue = $0 } + + expect(lastValue).to(beNil()) + + observer.sendNext(1) + expect(lastValue).to(beNil()) + + observer.sendNext(2) + expect(lastValue) == 2 + } + + it("should not skip any values when 0") { + let (baseSignal, observer) = Signal.pipe() + let signal = baseSignal.skip(first: 0) + + var lastValue: Int? + signal.observeNext { lastValue = $0 } + + expect(lastValue).to(beNil()) + + observer.sendNext(1) + expect(lastValue) == 1 + + observer.sendNext(2) + expect(lastValue) == 2 + } + } + + describe("skipRepeats") { + it("should skip duplicate Equatable values") { + let (baseSignal, observer) = Signal.pipe() + let signal = baseSignal.skipRepeats() + + var values: [Bool] = [] + signal.observeNext { values.append($0) } + + expect(values) == [] + + observer.sendNext(true) + expect(values) == [ true ] + + observer.sendNext(true) + expect(values) == [ true ] + + observer.sendNext(false) + expect(values) == [ true, false ] + + observer.sendNext(true) + expect(values) == [ true, false, true ] + } + + it("should skip values according to a predicate") { + let (baseSignal, observer) = Signal.pipe() + let signal = baseSignal.skipRepeats { $0.characters.count == $1.characters.count } + + var values: [String] = [] + signal.observeNext { values.append($0) } + + expect(values) == [] + + observer.sendNext("a") + expect(values) == [ "a" ] + + observer.sendNext("b") + expect(values) == [ "a" ] + + observer.sendNext("cc") + expect(values) == [ "a", "cc" ] + + observer.sendNext("d") + expect(values) == [ "a", "cc", "d" ] + } + + it("should not store strong reference to previously passed items") { + var disposedItems: [Bool] = [] + + struct Item { + let payload: Bool + let disposable: ScopedDisposable + } + + func item(_ payload: Bool) -> Item { + return Item( + payload: payload, + disposable: ScopedDisposable(ActionDisposable { disposedItems.append(payload) }) + ) + } + + let (baseSignal, observer) = Signal.pipe() + baseSignal.skipRepeats { $0.payload == $1.payload }.observeNext { _ in } + + observer.sendNext(item(true)) + expect(disposedItems) == [] + + observer.sendNext(item(false)) + expect(disposedItems) == [ true ] + + observer.sendNext(item(false)) + expect(disposedItems) == [ true, false ] + + observer.sendNext(item(true)) + expect(disposedItems) == [ true, false, false ] + + observer.sendCompleted() + expect(disposedItems) == [ true, false, false, true ] + } + } + + describe("uniqueValues") { + it("should skip values that have been already seen") { + let (baseSignal, observer) = Signal.pipe() + let signal = baseSignal.uniqueValues() + + var values: [String] = [] + signal.observeNext { values.append($0) } + + expect(values) == [] + + observer.sendNext("a") + expect(values) == [ "a" ] + + observer.sendNext("b") + expect(values) == [ "a", "b" ] + + observer.sendNext("a") + expect(values) == [ "a", "b" ] + + observer.sendNext("b") + expect(values) == [ "a", "b" ] + + observer.sendNext("c") + expect(values) == [ "a", "b", "c" ] + + observer.sendCompleted() + expect(values) == [ "a", "b", "c" ] + } + } + + describe("skipWhile") { + var signal: Signal! + var observer: Signal.Observer! + + var lastValue: Int? + + beforeEach { + let (baseSignal, incomingObserver) = Signal.pipe() + + signal = baseSignal.skip { $0 < 2 } + observer = incomingObserver + lastValue = nil + + signal.observeNext { lastValue = $0 } + } + + it("should skip while the predicate is true") { + expect(lastValue).to(beNil()) + + observer.sendNext(1) + expect(lastValue).to(beNil()) + + observer.sendNext(2) + expect(lastValue) == 2 + + observer.sendNext(0) + expect(lastValue) == 0 + } + + it("should not skip any values when the predicate starts false") { + expect(lastValue).to(beNil()) + + observer.sendNext(3) + expect(lastValue) == 3 + + observer.sendNext(1) + expect(lastValue) == 1 + } + } + + describe("skipUntil") { + var signal: Signal! + var observer: Signal.Observer! + var triggerObserver: Signal<(), NoError>.Observer! + + var lastValue: Int? = nil + + beforeEach { + let (baseSignal, incomingObserver) = Signal.pipe() + let (triggerSignal, incomingTriggerObserver) = Signal<(), NoError>.pipe() + + signal = baseSignal.skip(until: triggerSignal) + observer = incomingObserver + triggerObserver = incomingTriggerObserver + + lastValue = nil + + signal.observe { event in + switch event { + case let .next(value): + lastValue = value + default: + break + } + } + } + + it("should skip values until the trigger fires") { + expect(lastValue).to(beNil()) + + observer.sendNext(1) + expect(lastValue).to(beNil()) + + observer.sendNext(2) + expect(lastValue).to(beNil()) + + triggerObserver.sendNext(()) + observer.sendNext(0) + expect(lastValue) == 0 + } + + it("should skip values until the trigger completes") { + expect(lastValue).to(beNil()) + + observer.sendNext(1) + expect(lastValue).to(beNil()) + + observer.sendNext(2) + expect(lastValue).to(beNil()) + + triggerObserver.sendCompleted() + observer.sendNext(0) + expect(lastValue) == 0 + } + } + + describe("take") { + it("should take initial values") { + let (baseSignal, observer) = Signal.pipe() + let signal = baseSignal.take(first: 2) + + var lastValue: Int? + var completed = false + signal.observe { event in + switch event { + case let .next(value): + lastValue = value + case .completed: + completed = true + default: + break + } + } + + expect(lastValue).to(beNil()) + expect(completed) == false + + observer.sendNext(1) + expect(lastValue) == 1 + expect(completed) == false + + observer.sendNext(2) + expect(lastValue) == 2 + expect(completed) == true + } + + it("should complete immediately after taking given number of values") { + let numbers = [ 1, 2, 4, 4, 5 ] + let testScheduler = TestScheduler() + + var signal: Signal = Signal { observer in + testScheduler.schedule { + for number in numbers { + observer.sendNext(number) + } + } + return nil + } + + var completed = false + + signal = signal.take(first: numbers.count) + signal.observeCompleted { completed = true } + + expect(completed) == false + testScheduler.run() + expect(completed) == true + } + + it("should interrupt when 0") { + let numbers = [ 1, 2, 4, 4, 5 ] + let testScheduler = TestScheduler() + + let signal: Signal = Signal { observer in + testScheduler.schedule { + for number in numbers { + observer.sendNext(number) + } + } + return nil + } + + var result: [Int] = [] + var interrupted = false + + signal + .take(first: 0) + .observe { event in + switch event { + case let .next(number): + result.append(number) + case .interrupted: + interrupted = true + default: + break + } + } + + expect(interrupted) == true + + testScheduler.run() + expect(result).to(beEmpty()) + } + } + + describe("collect") { + it("should collect all values") { + let (original, observer) = Signal.pipe() + let signal = original.collect() + let expectedResult = [ 1, 2, 3 ] + + var result: [Int]? + + signal.observeNext { value in + expect(result).to(beNil()) + result = value + } + + for number in expectedResult { + observer.sendNext(number) + } + + expect(result).to(beNil()) + observer.sendCompleted() + expect(result) == expectedResult + } + + it("should complete with an empty array if there are no values") { + let (original, observer) = Signal.pipe() + let signal = original.collect() + + var result: [Int]? + + signal.observeNext { result = $0 } + + expect(result).to(beNil()) + observer.sendCompleted() + expect(result) == [] + } + + it("should forward errors") { + let (original, observer) = Signal.pipe() + let signal = original.collect() + + var error: TestError? + + signal.observeFailed { error = $0 } + + expect(error).to(beNil()) + observer.sendFailed(.default) + expect(error) == TestError.default + } + + it("should collect an exact count of values") { + let (original, observer) = Signal.pipe() + + let signal = original.collect(count: 3) + + var observedValues: [[Int]] = [] + + signal.observeNext { value in + observedValues.append(value) + } + + var expectation: [[Int]] = [] + + for i in 1...7 { + + observer.sendNext(i) + + if i % 3 == 0 { + expectation.append([Int]((i - 2)...i)) + expect(observedValues) == expectation + } else { + expect(observedValues) == expectation + } + } + + observer.sendCompleted() + + expectation.append([7]) + expect(observedValues) == expectation + } + + it("should collect values until it matches a certain value") { + let (original, observer) = Signal.pipe() + + let signal = original.collect { _, next in next != 5 } + + var expectedValues = [ + [5, 5], + [42, 5] + ] + + signal.observeNext { value in + expect(value) == expectedValues.removeFirst() + } + + signal.observeCompleted { + expect(expectedValues) == [] + } + + expectedValues + .flatMap { $0 } + .forEach(observer.sendNext) + + observer.sendCompleted() + } + + it("should collect values until it matches a certain condition on values") { + let (original, observer) = Signal.pipe() + + let signal = original.collect { values in values.reduce(0, +) == 10 } + + var expectedValues = [ + [1, 2, 3, 4], + [5, 6, 7, 8, 9] + ] + + signal.observeNext { value in + expect(value) == expectedValues.removeFirst() + } + + signal.observeCompleted { + expect(expectedValues) == [] + } + + expectedValues + .flatMap { $0 } + .forEach(observer.sendNext) + + observer.sendCompleted() + } + } + + describe("takeUntil") { + var signal: Signal! + var observer: Signal.Observer! + var triggerObserver: Signal<(), NoError>.Observer! + + var lastValue: Int? = nil + var completed: Bool = false + + beforeEach { + let (baseSignal, incomingObserver) = Signal.pipe() + let (triggerSignal, incomingTriggerObserver) = Signal<(), NoError>.pipe() + + signal = baseSignal.take(until: triggerSignal) + observer = incomingObserver + triggerObserver = incomingTriggerObserver + + lastValue = nil + completed = false + + signal.observe { event in + switch event { + case let .next(value): + lastValue = value + case .completed: + completed = true + default: + break + } + } + } + + it("should take values until the trigger fires") { + expect(lastValue).to(beNil()) + + observer.sendNext(1) + expect(lastValue) == 1 + + observer.sendNext(2) + expect(lastValue) == 2 + + expect(completed) == false + triggerObserver.sendNext(()) + expect(completed) == true + } + + it("should take values until the trigger completes") { + expect(lastValue).to(beNil()) + + observer.sendNext(1) + expect(lastValue) == 1 + + observer.sendNext(2) + expect(lastValue) == 2 + + expect(completed) == false + triggerObserver.sendCompleted() + expect(completed) == true + } + + it("should complete if the trigger fires immediately") { + expect(lastValue).to(beNil()) + expect(completed) == false + + triggerObserver.sendNext(()) + + expect(completed) == true + expect(lastValue).to(beNil()) + } + } + + describe("takeUntilReplacement") { + var signal: Signal! + var observer: Signal.Observer! + var replacementObserver: Signal.Observer! + + var lastValue: Int? = nil + var completed: Bool = false + + beforeEach { + let (baseSignal, incomingObserver) = Signal.pipe() + let (replacementSignal, incomingReplacementObserver) = Signal.pipe() + + signal = baseSignal.take(untilReplacement: replacementSignal) + observer = incomingObserver + replacementObserver = incomingReplacementObserver + + lastValue = nil + completed = false + + signal.observe { event in + switch event { + case let .next(value): + lastValue = value + case .completed: + completed = true + default: + break + } + } + } + + it("should take values from the original then the replacement") { + expect(lastValue).to(beNil()) + expect(completed) == false + + observer.sendNext(1) + expect(lastValue) == 1 + + observer.sendNext(2) + expect(lastValue) == 2 + + replacementObserver.sendNext(3) + + expect(lastValue) == 3 + expect(completed) == false + + observer.sendNext(4) + + expect(lastValue) == 3 + expect(completed) == false + + replacementObserver.sendNext(5) + expect(lastValue) == 5 + + expect(completed) == false + replacementObserver.sendCompleted() + expect(completed) == true + } + } + + describe("takeWhile") { + var signal: Signal! + var observer: Signal.Observer! + + beforeEach { + let (baseSignal, incomingObserver) = Signal.pipe() + signal = baseSignal.take { $0 <= 4 } + observer = incomingObserver + } + + it("should take while the predicate is true") { + var latestValue: Int! + var completed = false + + signal.observe { event in + switch event { + case let .next(value): + latestValue = value + case .completed: + completed = true + default: + break + } + } + + for value in -1...4 { + observer.sendNext(value) + expect(latestValue) == value + expect(completed) == false + } + + observer.sendNext(5) + expect(latestValue) == 4 + expect(completed) == true + } + + it("should complete if the predicate starts false") { + var latestValue: Int? + var completed = false + + signal.observe { event in + switch event { + case let .next(value): + latestValue = value + case .completed: + completed = true + default: + break + } + } + + observer.sendNext(5) + expect(latestValue).to(beNil()) + expect(completed) == true + } + } + + describe("observeOn") { + it("should send events on the given scheduler") { + let testScheduler = TestScheduler() + let (signal, observer) = Signal.pipe() + + var result: [Int] = [] + + signal + .observe(on: testScheduler) + .observeNext { result.append($0) } + + observer.sendNext(1) + observer.sendNext(2) + expect(result).to(beEmpty()) + + testScheduler.run() + expect(result) == [ 1, 2 ] + } + } + + describe("delay") { + it("should send events on the given scheduler after the interval") { + let testScheduler = TestScheduler() + let signal: Signal = Signal { observer in + testScheduler.schedule { + observer.sendNext(1) + } + testScheduler.schedule(after: 5) { + observer.sendNext(2) + observer.sendCompleted() + } + return nil + } + + var result: [Int] = [] + var completed = false + + signal + .delay(10, on: testScheduler) + .observe { event in + switch event { + case let .next(number): + result.append(number) + case .completed: + completed = true + default: + break + } + } + + testScheduler.advance(by: 4) // send initial value + expect(result).to(beEmpty()) + + testScheduler.advance(by: 10) // send second value and receive first + expect(result) == [ 1 ] + expect(completed) == false + + testScheduler.advance(by: 10) // send second value and receive first + expect(result) == [ 1, 2 ] + expect(completed) == true + } + + it("should schedule errors immediately") { + let testScheduler = TestScheduler() + let signal: Signal = Signal { observer in + testScheduler.schedule { + observer.sendFailed(TestError.default) + } + return nil + } + + var errored = false + + signal + .delay(10, on: testScheduler) + .observeFailed { _ in errored = true } + + testScheduler.advance() + expect(errored) == true + } + } + + describe("throttle") { + var scheduler: TestScheduler! + var observer: Signal.Observer! + var signal: Signal! + + beforeEach { + scheduler = TestScheduler() + + let (baseSignal, baseObserver) = Signal.pipe() + observer = baseObserver + + signal = baseSignal.throttle(1, on: scheduler) + expect(signal).notTo(beNil()) + } + + it("should send values on the given scheduler at no less than the interval") { + var values: [Int] = [] + signal.observeNext { value in + values.append(value) + } + + expect(values) == [] + + observer.sendNext(0) + expect(values) == [] + + scheduler.advance() + expect(values) == [ 0 ] + + observer.sendNext(1) + observer.sendNext(2) + expect(values) == [ 0 ] + + scheduler.advance(by: 1.5) + expect(values) == [ 0, 2 ] + + scheduler.advance(by: 3) + expect(values) == [ 0, 2 ] + + observer.sendNext(3) + expect(values) == [ 0, 2 ] + + scheduler.advance() + expect(values) == [ 0, 2, 3 ] + + observer.sendNext(4) + observer.sendNext(5) + scheduler.advance() + expect(values) == [ 0, 2, 3 ] + + scheduler.rewind(by: 2) + expect(values) == [ 0, 2, 3 ] + + observer.sendNext(6) + scheduler.advance() + expect(values) == [ 0, 2, 3, 6 ] + + observer.sendNext(7) + observer.sendNext(8) + scheduler.advance() + expect(values) == [ 0, 2, 3, 6 ] + + scheduler.run() + expect(values) == [ 0, 2, 3, 6, 8 ] + } + + it("should schedule completion immediately") { + var values: [Int] = [] + var completed = false + + signal.observe { event in + switch event { + case let .next(value): + values.append(value) + case .completed: + completed = true + default: + break + } + } + + observer.sendNext(0) + scheduler.advance() + expect(values) == [ 0 ] + + observer.sendNext(1) + observer.sendCompleted() + expect(completed) == false + + scheduler.advance() + expect(values) == [ 0 ] + expect(completed) == true + + scheduler.run() + expect(values) == [ 0 ] + expect(completed) == true + } + } + + describe("debounce") { + var scheduler: TestScheduler! + var observer: Signal.Observer! + var signal: Signal! + + beforeEach { + scheduler = TestScheduler() + + let (baseSignal, baseObserver) = Signal.pipe() + observer = baseObserver + + signal = baseSignal.debounce(1, on: scheduler) + expect(signal).notTo(beNil()) + } + + it("should send values on the given scheduler once the interval has passed since the last value was sent") { + var values: [Int] = [] + signal.observeNext { value in + values.append(value) + } + + expect(values) == [] + + observer.sendNext(0) + expect(values) == [] + + scheduler.advance() + expect(values) == [] + + observer.sendNext(1) + observer.sendNext(2) + expect(values) == [] + + scheduler.advance(by: 1.5) + expect(values) == [ 2 ] + + scheduler.advance(by: 3) + expect(values) == [ 2 ] + + observer.sendNext(3) + expect(values) == [ 2 ] + + scheduler.advance() + expect(values) == [ 2 ] + + observer.sendNext(4) + observer.sendNext(5) + scheduler.advance() + expect(values) == [ 2 ] + + scheduler.run() + expect(values) == [ 2, 5 ] + } + + it("should schedule completion immediately") { + var values: [Int] = [] + var completed = false + + signal.observe { event in + switch event { + case let .next(value): + values.append(value) + case .completed: + completed = true + default: + break + } + } + + observer.sendNext(0) + scheduler.advance() + expect(values) == [] + + observer.sendNext(1) + observer.sendCompleted() + expect(completed) == false + + scheduler.advance() + expect(values) == [] + expect(completed) == true + + scheduler.run() + expect(values) == [] + expect(completed) == true + } + } + + describe("sampleWith") { + var sampledSignal: Signal<(Int, String), NoError>! + var observer: Signal.Observer! + var samplerObserver: Signal.Observer! + + beforeEach { + let (signal, incomingObserver) = Signal.pipe() + let (sampler, incomingSamplerObserver) = Signal.pipe() + sampledSignal = signal.sample(with: sampler) + observer = incomingObserver + samplerObserver = incomingSamplerObserver + } + + it("should forward the latest value when the sampler fires") { + var result: [String] = [] + sampledSignal.observeNext { (left, right) in result.append("\(left)\(right)") } + + observer.sendNext(1) + observer.sendNext(2) + samplerObserver.sendNext("a") + expect(result) == [ "2a" ] + } + + it("should do nothing if sampler fires before signal receives value") { + var result: [String] = [] + sampledSignal.observeNext { (left, right) in result.append("\(left)\(right)") } + + samplerObserver.sendNext("a") + expect(result).to(beEmpty()) + } + + it("should send lates value with sampler value multiple times when sampler fires multiple times") { + var result: [String] = [] + sampledSignal.observeNext { (left, right) in result.append("\(left)\(right)") } + + observer.sendNext(1) + samplerObserver.sendNext("a") + samplerObserver.sendNext("b") + expect(result) == [ "1a", "1b" ] + } + + it("should complete when both inputs have completed") { + var completed = false + sampledSignal.observeCompleted { completed = true } + + observer.sendCompleted() + expect(completed) == false + + samplerObserver.sendCompleted() + expect(completed) == true + } + } + + describe("sampleOn") { + var sampledSignal: Signal! + var observer: Signal.Observer! + var samplerObserver: Signal<(), NoError>.Observer! + + beforeEach { + let (signal, incomingObserver) = Signal.pipe() + let (sampler, incomingSamplerObserver) = Signal<(), NoError>.pipe() + sampledSignal = signal.sample(on: sampler) + observer = incomingObserver + samplerObserver = incomingSamplerObserver + } + + it("should forward the latest value when the sampler fires") { + var result: [Int] = [] + sampledSignal.observeNext { result.append($0) } + + observer.sendNext(1) + observer.sendNext(2) + samplerObserver.sendNext(()) + expect(result) == [ 2 ] + } + + it("should do nothing if sampler fires before signal receives value") { + var result: [Int] = [] + sampledSignal.observeNext { result.append($0) } + + samplerObserver.sendNext(()) + expect(result).to(beEmpty()) + } + + it("should send lates value multiple times when sampler fires multiple times") { + var result: [Int] = [] + sampledSignal.observeNext { result.append($0) } + + observer.sendNext(1) + samplerObserver.sendNext(()) + samplerObserver.sendNext(()) + expect(result) == [ 1, 1 ] + } + + it("should complete when both inputs have completed") { + var completed = false + sampledSignal.observeCompleted { completed = true } + + observer.sendCompleted() + expect(completed) == false + + samplerObserver.sendCompleted() + expect(completed) == true + } + } + + describe("combineLatestWith") { + var combinedSignal: Signal<(Int, Double), NoError>! + var observer: Signal.Observer! + var otherObserver: Signal.Observer! + + beforeEach { + let (signal, incomingObserver) = Signal.pipe() + let (otherSignal, incomingOtherObserver) = Signal.pipe() + combinedSignal = signal.combineLatest(with: otherSignal) + observer = incomingObserver + otherObserver = incomingOtherObserver + } + + it("should forward the latest values from both inputs") { + var latest: (Int, Double)? + combinedSignal.observeNext { latest = $0 } + + observer.sendNext(1) + expect(latest).to(beNil()) + + // is there a better way to test tuples? + otherObserver.sendNext(1.5) + expect(latest?.0) == 1 + expect(latest?.1) == 1.5 + + observer.sendNext(2) + expect(latest?.0) == 2 + expect(latest?.1) == 1.5 + } + + it("should complete when both inputs have completed") { + var completed = false + combinedSignal.observeCompleted { completed = true } + + observer.sendCompleted() + expect(completed) == false + + otherObserver.sendCompleted() + expect(completed) == true + } + } + + describe("zipWith") { + var leftObserver: Signal.Observer! + var rightObserver: Signal.Observer! + var zipped: Signal<(Int, String), NoError>! + + beforeEach { + let (leftSignal, incomingLeftObserver) = Signal.pipe() + let (rightSignal, incomingRightObserver) = Signal.pipe() + + leftObserver = incomingLeftObserver + rightObserver = incomingRightObserver + zipped = leftSignal.zip(with: rightSignal) + } + + it("should combine pairs") { + var result: [String] = [] + zipped.observeNext { (left, right) in result.append("\(left)\(right)") } + + leftObserver.sendNext(1) + leftObserver.sendNext(2) + expect(result) == [] + + rightObserver.sendNext("foo") + expect(result) == [ "1foo" ] + + leftObserver.sendNext(3) + rightObserver.sendNext("bar") + expect(result) == [ "1foo", "2bar" ] + + rightObserver.sendNext("buzz") + expect(result) == [ "1foo", "2bar", "3buzz" ] + + rightObserver.sendNext("fuzz") + expect(result) == [ "1foo", "2bar", "3buzz" ] + + leftObserver.sendNext(4) + expect(result) == [ "1foo", "2bar", "3buzz", "4fuzz" ] + } + + it("should complete when the shorter signal has completed") { + var result: [String] = [] + var completed = false + + zipped.observe { event in + switch event { + case let .next(left, right): + result.append("\(left)\(right)") + case .completed: + completed = true + default: + break + } + } + + expect(completed) == false + + leftObserver.sendNext(0) + leftObserver.sendCompleted() + expect(completed) == false + expect(result) == [] + + rightObserver.sendNext("foo") + expect(completed) == true + expect(result) == [ "0foo" ] + } + + it("should complete when both signal have completed") { + var result: [String] = [] + var completed = false + + zipped.observe { event in + switch event { + case let .next(left, right): + result.append("\(left)\(right)") + case .completed: + completed = true + default: + break + } + } + + expect(completed) == false + + leftObserver.sendNext(0) + leftObserver.sendCompleted() + expect(completed) == false + expect(result) == [] + + rightObserver.sendCompleted() + expect(result) == [ ] + } + + it("should complete and drop unpaired pending values when both signal have completed") { + var result: [String] = [] + var completed = false + + zipped.observe { event in + switch event { + case let .next(left, right): + result.append("\(left)\(right)") + case .completed: + completed = true + default: + break + } + } + + expect(completed) == false + + leftObserver.sendNext(0) + leftObserver.sendNext(1) + leftObserver.sendNext(2) + leftObserver.sendNext(3) + leftObserver.sendCompleted() + expect(completed) == false + expect(result) == [] + + rightObserver.sendNext("foo") + rightObserver.sendNext("bar") + rightObserver.sendCompleted() + expect(result) == ["0foo", "1bar"] + } + } + + describe("materialize") { + it("should reify events from the signal") { + let (signal, observer) = Signal.pipe() + var latestEvent: Event? + signal + .materialize() + .observeNext { latestEvent = $0 } + + observer.sendNext(2) + + expect(latestEvent).toNot(beNil()) + if let latestEvent = latestEvent { + switch latestEvent { + case let .next(value): + expect(value) == 2 + default: + fail() + } + } + + observer.sendFailed(TestError.default) + if let latestEvent = latestEvent { + switch latestEvent { + case .failed: + () + default: + fail() + } + } + } + } + + describe("dematerialize") { + typealias IntEvent = Event + var observer: Signal.Observer! + var dematerialized: Signal! + + beforeEach { + let (signal, incomingObserver) = Signal.pipe() + observer = incomingObserver + dematerialized = signal.dematerialize() + } + + it("should send values for Next events") { + var result: [Int] = [] + dematerialized + .assumeNoErrors() + .observeNext { result.append($0) } + + expect(result).to(beEmpty()) + + observer.sendNext(.next(2)) + expect(result) == [ 2 ] + + observer.sendNext(.next(4)) + expect(result) == [ 2, 4 ] + } + + it("should error out for Error events") { + var errored = false + dematerialized.observeFailed { _ in errored = true } + + expect(errored) == false + + observer.sendNext(.failed(TestError.default)) + expect(errored) == true + } + + it("should complete early for Completed events") { + var completed = false + dematerialized.observeCompleted { completed = true } + + expect(completed) == false + observer.sendNext(IntEvent.completed) + expect(completed) == true + } + } + + describe("takeLast") { + var observer: Signal.Observer! + var lastThree: Signal! + + beforeEach { + let (signal, incomingObserver) = Signal.pipe() + observer = incomingObserver + lastThree = signal.take(last: 3) + } + + it("should send the last N values upon completion") { + var result: [Int] = [] + lastThree + .assumeNoErrors() + .observeNext { result.append($0) } + + observer.sendNext(1) + observer.sendNext(2) + observer.sendNext(3) + observer.sendNext(4) + expect(result).to(beEmpty()) + + observer.sendCompleted() + expect(result) == [ 2, 3, 4 ] + } + + it("should send less than N values if not enough were received") { + var result: [Int] = [] + lastThree + .assumeNoErrors() + .observeNext { result.append($0) } + + observer.sendNext(1) + observer.sendNext(2) + observer.sendCompleted() + expect(result) == [ 1, 2 ] + } + + it("should send nothing when errors") { + var result: [Int] = [] + var errored = false + lastThree.observe { event in + switch event { + case let .next(value): + result.append(value) + case .failed: + errored = true + default: + break + } + } + + observer.sendNext(1) + observer.sendNext(2) + observer.sendNext(3) + expect(errored) == false + + observer.sendFailed(TestError.default) + expect(errored) == true + expect(result).to(beEmpty()) + } + } + + describe("timeoutWithError") { + var testScheduler: TestScheduler! + var signal: Signal! + var observer: Signal.Observer! + + beforeEach { + testScheduler = TestScheduler() + let (baseSignal, incomingObserver) = Signal.pipe() + signal = baseSignal.timeout(after: 2, raising: TestError.default, on: testScheduler) + observer = incomingObserver + } + + it("should complete if within the interval") { + var completed = false + var errored = false + signal.observe { event in + switch event { + case .completed: + completed = true + case .failed: + errored = true + default: + break + } + } + + testScheduler.schedule(after: 1) { + observer.sendCompleted() + } + + expect(completed) == false + expect(errored) == false + + testScheduler.run() + expect(completed) == true + expect(errored) == false + } + + it("should error if not completed before the interval has elapsed") { + var completed = false + var errored = false + signal.observe { event in + switch event { + case .completed: + completed = true + case .failed: + errored = true + default: + break + } + } + + testScheduler.schedule(after: 3) { + observer.sendCompleted() + } + + expect(completed) == false + expect(errored) == false + + testScheduler.run() + expect(completed) == false + expect(errored) == true + } + + it("should be available for NoError") { + let signal: Signal = Signal.never + .timeout(after: 2, raising: TestError.default, on: testScheduler) + + _ = signal + } + } + + describe("attempt") { + it("should forward original values upon success") { + let (baseSignal, observer) = Signal.pipe() + let signal = baseSignal.attempt { _ in + return .success() + } + + var current: Int? + signal + .assumeNoErrors() + .observeNext { value in + current = value + } + + for value in 1...5 { + observer.sendNext(value) + expect(current) == value + } + } + + it("should error if an attempt fails") { + let (baseSignal, observer) = Signal.pipe() + let signal = baseSignal.attempt { _ in + return .failure(.default) + } + + var error: TestError? + signal.observeFailed { err in + error = err + } + + observer.sendNext(42) + expect(error) == TestError.default + } + } + + describe("attemptMap") { + it("should forward mapped values upon success") { + let (baseSignal, observer) = Signal.pipe() + let signal = baseSignal.attemptMap { num -> Result in + return .success(num % 2 == 0) + } + + var even: Bool? + signal + .assumeNoErrors() + .observeNext { value in + even = value + } + + observer.sendNext(1) + expect(even) == false + + observer.sendNext(2) + expect(even) == true + } + + it("should error if a mapping fails") { + let (baseSignal, observer) = Signal.pipe() + let signal = baseSignal.attemptMap { _ -> Result in + return .failure(.default) + } + + var error: TestError? + signal.observeFailed { err in + error = err + } + + observer.sendNext(42) + expect(error) == TestError.default + } + } + + describe("combinePrevious") { + var observer: Signal.Observer! + let initialValue: Int = 0 + var latestValues: (Int, Int)? + + beforeEach { + latestValues = nil + + let (signal, baseObserver) = Signal.pipe() + observer = baseObserver + signal.combinePrevious(initialValue).observeNext { latestValues = $0 } + } + + it("should forward the latest value with previous value") { + expect(latestValues).to(beNil()) + + observer.sendNext(1) + expect(latestValues?.0) == initialValue + expect(latestValues?.1) == 1 + + observer.sendNext(2) + expect(latestValues?.0) == 1 + expect(latestValues?.1) == 2 + } + } + + describe("combineLatest") { + var signalA: Signal! + var signalB: Signal! + var signalC: Signal! + var observerA: Signal.Observer! + var observerB: Signal.Observer! + var observerC: Signal.Observer! + + var combinedValues: [Int]? + var completed: Bool! + + beforeEach { + combinedValues = nil + completed = false + + let (baseSignalA, baseObserverA) = Signal.pipe() + let (baseSignalB, baseObserverB) = Signal.pipe() + let (baseSignalC, baseObserverC) = Signal.pipe() + + signalA = baseSignalA + signalB = baseSignalB + signalC = baseSignalC + + observerA = baseObserverA + observerB = baseObserverB + observerC = baseObserverC + } + + let combineLatestExampleName = "combineLatest examples" + sharedExamples(combineLatestExampleName) { + it("should forward the latest values from all inputs"){ + expect(combinedValues).to(beNil()) + + observerA.sendNext(0) + observerB.sendNext(1) + observerC.sendNext(2) + expect(combinedValues) == [0, 1, 2] + + observerA.sendNext(10) + expect(combinedValues) == [10, 1, 2] + } + + it("should not forward the latest values before all inputs"){ + expect(combinedValues).to(beNil()) + + observerA.sendNext(0) + expect(combinedValues).to(beNil()) + + observerB.sendNext(1) + expect(combinedValues).to(beNil()) + + observerC.sendNext(2) + expect(combinedValues) == [0, 1, 2] + } + + it("should complete when all inputs have completed"){ + expect(completed) == false + + observerA.sendCompleted() + observerB.sendCompleted() + expect(completed) == false + + observerC.sendCompleted() + expect(completed) == true + } + } + + describe("tuple") { + beforeEach { + Signal.combineLatest(signalA, signalB, signalC) + .observe { event in + switch event { + case let .next(value): + combinedValues = [value.0, value.1, value.2] + case .completed: + completed = true + default: + break + } + } + } + + itBehavesLike(combineLatestExampleName) + } + + describe("sequence") { + beforeEach { + Signal.combineLatest([signalA, signalB, signalC]) + .observe { event in + switch event { + case let .next(values): + combinedValues = values + case .completed: + completed = true + default: + break + } + } + } + + itBehavesLike(combineLatestExampleName) + } + } + + describe("zip") { + var signalA: Signal! + var signalB: Signal! + var signalC: Signal! + var observerA: Signal.Observer! + var observerB: Signal.Observer! + var observerC: Signal.Observer! + + var zippedValues: [Int]? + var completed: Bool! + + beforeEach { + zippedValues = nil + completed = false + + let (baseSignalA, baseObserverA) = Signal.pipe() + let (baseSignalB, baseObserverB) = Signal.pipe() + let (baseSignalC, baseObserverC) = Signal.pipe() + + signalA = baseSignalA + signalB = baseSignalB + signalC = baseSignalC + + observerA = baseObserverA + observerB = baseObserverB + observerC = baseObserverC + } + + let zipExampleName = "zip examples" + sharedExamples(zipExampleName) { + it("should combine all set"){ + expect(zippedValues).to(beNil()) + + observerA.sendNext(0) + expect(zippedValues).to(beNil()) + + observerB.sendNext(1) + expect(zippedValues).to(beNil()) + + observerC.sendNext(2) + expect(zippedValues) == [0, 1, 2] + + observerA.sendNext(10) + expect(zippedValues) == [0, 1, 2] + + observerA.sendNext(20) + expect(zippedValues) == [0, 1, 2] + + observerB.sendNext(11) + expect(zippedValues) == [0, 1, 2] + + observerC.sendNext(12) + expect(zippedValues) == [10, 11, 12] + } + + it("should complete when the shorter signal has completed"){ + expect(completed) == false + + observerB.sendNext(1) + observerC.sendNext(2) + observerB.sendCompleted() + observerC.sendCompleted() + expect(completed) == false + + observerA.sendNext(0) + expect(completed) == true + } + } + + describe("tuple") { + beforeEach { + Signal.zip(signalA, signalB, signalC) + .observe { event in + switch event { + case let .next(value): + zippedValues = [value.0, value.1, value.2] + case .completed: + completed = true + default: + break + } + } + } + + itBehavesLike(zipExampleName) + } + + describe("sequence") { + beforeEach { + Signal.zip([signalA, signalB, signalC]) + .observe { event in + switch event { + case let .next(values): + zippedValues = values + case .completed: + completed = true + default: + break + } + } + } + + itBehavesLike(zipExampleName) + } + + describe("log events") { + it("should output the correct event without identifier") { + let expectations: [(String) -> Void] = [ + { event in expect(event) == "[] next 1" }, + { event in expect(event) == "[] completed" }, + { event in expect(event) == "[] terminated" }, + { event in expect(event) == "[] disposed" }, + ] + + let logger = TestLogger(expectations: expectations) + + let (signal, observer) = Signal.pipe() + signal + .logEvents(logger: logger.logEvent) + .observe { _ in } + + observer.sendNext(1) + observer.sendCompleted() + } + + it("should output the correct event with identifier") { + let expectations: [(String) -> Void] = [ + { event in expect(event) == "[test.rac] next 1" }, + { event in expect(event) == "[test.rac] failed error1" }, + { event in expect(event) == "[test.rac] terminated" }, + { event in expect(event) == "[test.rac] disposed" }, + ] + + let logger = TestLogger(expectations: expectations) + + let (signal, observer) = Signal.pipe() + signal + .logEvents(identifier: "test.rac", logger: logger.logEvent) + .observe { _ in } + + observer.sendNext(1) + observer.sendFailed(.error1) + } + + it("should only output the events specified in the `events` parameter") { + let expectations: [(String) -> Void] = [ + { event in expect(event) == "[test.rac] failed error1" }, + ] + + let logger = TestLogger(expectations: expectations) + + let (signal, observer) = Signal.pipe() + signal + .logEvents(identifier: "test.rac", events: [.failed], logger: logger.logEvent) + .observe { _ in } + + observer.sendNext(1) + observer.sendFailed(.error1) + } + } + } + } +} diff --git a/ReactiveSwiftTests/TestError.swift b/ReactiveSwiftTests/TestError.swift new file mode 100644 index 0000000000..39125cdfed --- /dev/null +++ b/ReactiveSwiftTests/TestError.swift @@ -0,0 +1,43 @@ +// +// TestError.swift +// ReactiveSwift +// +// Created by Almas Sapargali on 1/26/15. +// Copyright (c) 2015 GitHub. All rights reserved. +// + +import ReactiveSwift +import Result + +enum TestError: Int { + case `default` = 0 + case error1 = 1 + case error2 = 2 +} + +extension TestError: Error { +} + + +internal extension SignalProducerProtocol { + /// Halts if an error is emitted in the receiver signal. + /// This is useful in tests to be able to just use `startWithNext` + /// in cases where we know that an error won't be emitted. + func assumeNoErrors() -> SignalProducer { + return self.lift { $0.assumeNoErrors() } + } +} + +internal extension SignalProtocol { + /// Halts if an error is emitted in the receiver signal. + /// This is useful in tests to be able to just use `startWithNext` + /// in cases where we know that an error won't be emitted. + func assumeNoErrors() -> Signal { + return self.mapError { error in + fatalError("Unexpected error: \(error)") + + () + } + } +} + diff --git a/ReactiveSwiftTests/TestLogger.swift b/ReactiveSwiftTests/TestLogger.swift new file mode 100644 index 0000000000..85a769070a --- /dev/null +++ b/ReactiveSwiftTests/TestLogger.swift @@ -0,0 +1,25 @@ +// +// TestLogger.swift +// ReactiveSwift +// +// Created by Rui Peres on 29/04/2016. +// Copyright © 2016 GitHub. All rights reserved. +// + +import Foundation +@testable import ReactiveSwift + +final class TestLogger { + private var expectations: [(String) -> Void] + + init(expectations: [(String) -> Void]) { + self.expectations = expectations + } +} + +extension TestLogger { + + func logEvent(_ identifier: String, event: String, fileName: String, functionName: String, lineNumber: Int) { + expectations.removeFirst()("[\(identifier)] \(event)") + } +} From 073031ed0c2f5260a4139250060dfe7580acb9db Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Tue, 16 Aug 2016 17:27:47 -0400 Subject: [PATCH 0337/1028] Use ReactiveSwift in ReactiveCocoa --- ReactiveCocoa.xcodeproj/project.pbxproj | 329 +-- .../xcschemes/ReactiveCocoa-Mac.xcscheme | 14 + .../xcschemes/ReactiveCocoa-iOS.xcscheme | 14 + .../xcschemes/ReactiveCocoa-tvOS.xcscheme | 14 + .../contents.xcworkspacedata | 3 + ReactiveCocoa/Swift/Action.swift | 199 -- ReactiveCocoa/Swift/Atomic.swift | 169 -- ReactiveCocoa/Swift/Bag.swift | 110 - ReactiveCocoa/Swift/CocoaAction.swift | 1 + .../Swift/Deprecations+Removals.swift | 263 -- ReactiveCocoa/Swift/Disposable.swift | 353 --- ReactiveCocoa/Swift/DynamicProperty.swift | 1 + ReactiveCocoa/Swift/Event.swift | 165 -- ReactiveCocoa/Swift/EventLogger.swift | 132 - ReactiveCocoa/Swift/Flatten.swift | 918 ------- .../Swift/FoundationExtensions.swift | 80 - ReactiveCocoa/Swift/Lifetime.swift | 49 +- .../Swift/NSObject+KeyValueObserving.swift | 1 + ReactiveCocoa/Swift/ObjectiveCBridging.swift | 16 + ReactiveCocoa/Swift/Observer.swift | 104 - ReactiveCocoa/Swift/Optional.swift | 42 - ReactiveCocoa/Swift/Property.swift | 878 ------- ReactiveCocoa/Swift/Scheduler.swift | 493 ---- ReactiveCocoa/Swift/Signal.swift | 1838 ------------- ReactiveCocoa/Swift/SignalProducer.swift | 1907 -------------- ReactiveCocoa/Swift/TupleExtensions.swift | 42 - ReactiveCocoaTests/Swift/ActionSpec.swift | 142 -- ReactiveCocoaTests/Swift/AtomicSpec.swift | 44 - ReactiveCocoaTests/Swift/BagSpec.swift | 54 - .../Swift/CocoaActionSpec.swift | 1 + ReactiveCocoaTests/Swift/DisposableSpec.swift | 143 -- .../Swift/DynamicPropertySpec.swift | 233 ++ ReactiveCocoaTests/Swift/FlattenSpec.swift | 963 ------- .../Swift/FoundationExtensionsSpec.swift | 59 - .../Swift/KeyValueObservingSpec.swift | 1 + ReactiveCocoaTests/Swift/LifetimeSpec.swift | 83 - .../Swift/ObjectiveCBridgingSpec.swift | 1 + ReactiveCocoaTests/Swift/PropertySpec.swift | 1754 ------------- ReactiveCocoaTests/Swift/SchedulerSpec.swift | 298 --- .../Swift/SignalLifetimeSpec.swift | 414 --- .../Swift/SignalProducerLiftingSpec.swift | 1536 ----------- .../Swift/SignalProducerNimbleMatchers.swift | 1 + .../Swift/SignalProducerSpec.swift | 2257 ---------------- ReactiveCocoaTests/Swift/SignalSpec.swift | 2269 ----------------- ReactiveCocoaTests/Swift/TestError.swift | 1 + ReactiveCocoaTests/Swift/TestLogger.swift | 25 - ReactiveSwift/Scheduler.swift | 2 +- 47 files changed, 350 insertions(+), 18066 deletions(-) delete mode 100644 ReactiveCocoa/Swift/Action.swift delete mode 100644 ReactiveCocoa/Swift/Atomic.swift delete mode 100644 ReactiveCocoa/Swift/Bag.swift delete mode 100644 ReactiveCocoa/Swift/Deprecations+Removals.swift delete mode 100644 ReactiveCocoa/Swift/Disposable.swift delete mode 100644 ReactiveCocoa/Swift/Event.swift delete mode 100644 ReactiveCocoa/Swift/EventLogger.swift delete mode 100644 ReactiveCocoa/Swift/Flatten.swift delete mode 100644 ReactiveCocoa/Swift/FoundationExtensions.swift delete mode 100644 ReactiveCocoa/Swift/Observer.swift delete mode 100644 ReactiveCocoa/Swift/Optional.swift delete mode 100644 ReactiveCocoa/Swift/Property.swift delete mode 100644 ReactiveCocoa/Swift/Scheduler.swift delete mode 100644 ReactiveCocoa/Swift/Signal.swift delete mode 100644 ReactiveCocoa/Swift/SignalProducer.swift delete mode 100644 ReactiveCocoa/Swift/TupleExtensions.swift delete mode 100755 ReactiveCocoaTests/Swift/ActionSpec.swift delete mode 100644 ReactiveCocoaTests/Swift/AtomicSpec.swift delete mode 100644 ReactiveCocoaTests/Swift/BagSpec.swift delete mode 100644 ReactiveCocoaTests/Swift/DisposableSpec.swift create mode 100644 ReactiveCocoaTests/Swift/DynamicPropertySpec.swift delete mode 100644 ReactiveCocoaTests/Swift/FlattenSpec.swift delete mode 100644 ReactiveCocoaTests/Swift/FoundationExtensionsSpec.swift delete mode 100644 ReactiveCocoaTests/Swift/LifetimeSpec.swift delete mode 100644 ReactiveCocoaTests/Swift/PropertySpec.swift delete mode 100644 ReactiveCocoaTests/Swift/SchedulerSpec.swift delete mode 100644 ReactiveCocoaTests/Swift/SignalLifetimeSpec.swift delete mode 100644 ReactiveCocoaTests/Swift/SignalProducerLiftingSpec.swift delete mode 100644 ReactiveCocoaTests/Swift/SignalProducerSpec.swift delete mode 100755 ReactiveCocoaTests/Swift/SignalSpec.swift delete mode 100644 ReactiveCocoaTests/Swift/TestLogger.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index a4cf19ff5f..132c1005d8 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -7,34 +7,15 @@ objects = { /* Begin PBXBuildFile section */ - 02D2602A1C1D6DAF003ACC61 /* SignalLifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02D260291C1D6DAF003ACC61 /* SignalLifetimeSpec.swift */; }; - 02D2602B1C1D6DB8003ACC61 /* SignalLifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02D260291C1D6DAF003ACC61 /* SignalLifetimeSpec.swift */; }; 314304171ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 314304151ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; 314304181ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 314304161ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.m */; }; 4A0E10FF1D2A92720065D310 /* Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* Lifetime.swift */; }; 4A0E11001D2A92720065D310 /* Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* Lifetime.swift */; }; 4A0E11011D2A92720065D310 /* Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* Lifetime.swift */; }; 4A0E11021D2A92720065D310 /* Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* Lifetime.swift */; }; - 4A0E11041D2A95200065D310 /* LifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E11031D2A95200065D310 /* LifetimeSpec.swift */; }; - 4A0E11051D2A95200065D310 /* LifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E11031D2A95200065D310 /* LifetimeSpec.swift */; }; - 4A0E11061D2A95200065D310 /* LifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E11031D2A95200065D310 /* LifetimeSpec.swift */; }; - 579504331BB8A34200A5E482 /* BagSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312EF19EF2A7700984962 /* BagSpec.swift */; }; - 579504341BB8A34300A5E482 /* BagSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312EF19EF2A7700984962 /* BagSpec.swift */; }; - 57A4D1B11BA13D7A00F7D4B1 /* Optional.swift in Sources */ = {isa = PBXBuildFile; fileRef = D871D69E1B3B29A40070F16C /* Optional.swift */; }; 57A4D1B21BA13D7A00F7D4B1 /* RACCompoundDisposableProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D037646A19EDA41200A782A9 /* RACCompoundDisposableProvider.d */; }; 57A4D1B31BA13D7A00F7D4B1 /* RACSignalProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D03764A319EDA41200A782A9 /* RACSignalProvider.d */; }; - 57A4D1B41BA13D7A00F7D4B1 /* Disposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BE19EF2A5800984962 /* Disposable.swift */; }; - 57A4D1B61BA13D7A00F7D4B1 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B51A69A3DB00AD8286 /* Event.swift */; }; 57A4D1B71BA13D7A00F7D4B1 /* ObjectiveCBridging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312C419EF2A5800984962 /* ObjectiveCBridging.swift */; }; - 57A4D1B81BA13D7A00F7D4B1 /* Scheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312C819EF2A5800984962 /* Scheduler.swift */; }; - 57A4D1B91BA13D7A00F7D4B1 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54AF1A69A2AC00AD8286 /* Action.swift */; }; - 57A4D1BA1BA13D7A00F7D4B1 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B01A69A2AC00AD8286 /* Property.swift */; }; - 57A4D1BB1BA13D7A00F7D4B1 /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B11A69A2AC00AD8286 /* Signal.swift */; }; - 57A4D1BC1BA13D7A00F7D4B1 /* SignalProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B21A69A2AC00AD8286 /* SignalProducer.swift */; }; - 57A4D1BD1BA13D7A00F7D4B1 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BB19EF2A5800984962 /* Atomic.swift */; }; - 57A4D1BE1BA13D7A00F7D4B1 /* Bag.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BC19EF2A5800984962 /* Bag.swift */; }; - 57A4D1BF1BA13D7A00F7D4B1 /* TupleExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00004081A46864E000E7D41 /* TupleExtensions.swift */; }; - 57A4D1C01BA13D7A00F7D4B1 /* FoundationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B4A3C19F4C39A009E02AC /* FoundationExtensions.swift */; }; 57A4D1C11BA13D7A00F7D4B1 /* EXTRuntimeExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037666819EDA57100A782A9 /* EXTRuntimeExtensions.m */; }; 57A4D1C21BA13D7A00F7D4B1 /* NSArray+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037642B19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m */; }; 57A4D1C31BA13D7A00F7D4B1 /* NSData+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643119EDA41200A782A9 /* NSData+RACSupport.m */; }; @@ -179,21 +160,8 @@ 7DFBED1F1CDB8D7800EE435B /* Quick.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 7DFBED201CDB8D7D00EE435B /* Nimble.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 7DFBED211CDB8D8300EE435B /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 7DFBED221CDB8DE300EE435B /* ActionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D021671C1A6CD50500987861 /* ActionSpec.swift */; }; - 7DFBED231CDB8DE300EE435B /* AtomicSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312EE19EF2A7700984962 /* AtomicSpec.swift */; }; - 7DFBED241CDB8DE300EE435B /* BagSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312EF19EF2A7700984962 /* BagSpec.swift */; }; - 7DFBED251CDB8DE300EE435B /* DisposableSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312F019EF2A7700984962 /* DisposableSpec.swift */; }; - 7DFBED261CDB8DE300EE435B /* FoundationExtensionsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8170FC01B100EBC004192AD /* FoundationExtensionsSpec.swift */; }; 7DFBED271CDB8DE300EE435B /* ObjectiveCBridgingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A226101A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift */; }; - 7DFBED281CDB8DE300EE435B /* PropertySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260D1A72F16D00D33B74 /* PropertySpec.swift */; }; - 7DFBED291CDB8DE300EE435B /* SchedulerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312F219EF2A7700984962 /* SchedulerSpec.swift */; }; - 7DFBED2A1CDB8DE300EE435B /* SignalLifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02D260291C1D6DAF003ACC61 /* SignalLifetimeSpec.swift */; }; - 7DFBED2B1CDB8DE300EE435B /* SignalProducerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260A1A72E6C500D33B74 /* SignalProducerSpec.swift */; }; - 7DFBED2C1CDB8DE300EE435B /* SignalProducerLiftingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8024DB11B2E1BB0005E6B9A /* SignalProducerLiftingSpec.swift */; }; - 7DFBED2D1CDB8DE300EE435B /* SignalSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A226071A72E0E900D33B74 /* SignalSpec.swift */; }; - 7DFBED2E1CDB8DE300EE435B /* FlattenSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA6F284F1C52626B001879D2 /* FlattenSpec.swift */; }; - 7DFBED2F1CDB8DE300EE435B /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B696FB801A7640C00075236D /* TestError.swift */; }; - 7DFBED301CDB8DE300EE435B /* TestLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79B64731CD38B2B003F2376 /* TestLogger.swift */; }; + 7DFBED281CDB8DE300EE435B /* DynamicPropertySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */; }; 7DFBED321CDB8DE300EE435B /* NSEnumeratorRACSequenceAdditionsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667819EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m */; }; 7DFBED331CDB8DE300EE435B /* NSNotificationCenterRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667919EDA60000A782A9 /* NSNotificationCenterRACSupportSpec.m */; }; 7DFBED351CDB8DE300EE435B /* NSObjectRACDeallocatingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667B19EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m */; }; @@ -240,10 +208,6 @@ 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */; }; 7DFBED6E1CDB918900EE435B /* UIBarButtonItem+RACCommandSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C719EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.m */; }; 7DFBED6F1CDB926400EE435B /* UIBarButtonItem+RACCommandSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764C619EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9ABCB1851D2A5B5A00BCA243 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ABCB1841D2A5B5A00BCA243 /* Deprecations+Removals.swift */; }; - 9ABCB1861D2A5B5A00BCA243 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ABCB1841D2A5B5A00BCA243 /* Deprecations+Removals.swift */; }; - 9ABCB1871D2A5B5A00BCA243 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ABCB1841D2A5B5A00BCA243 /* Deprecations+Removals.swift */; }; - 9ABCB1881D2A5B5A00BCA243 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ABCB1841D2A5B5A00BCA243 /* Deprecations+Removals.swift */; }; 9A1E72BA1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; 9A1E72BB1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; 9A1E72BC1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; @@ -322,18 +286,7 @@ A9B315A51B3940750001CB9C /* RACUnarySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BD19EDA41200A782A9 /* RACUnarySequence.m */; }; A9B315A61B3940750001CB9C /* RACUnit.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BF19EDA41200A782A9 /* RACUnit.m */; }; A9B315A71B3940750001CB9C /* RACValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C119EDA41200A782A9 /* RACValueTransformer.m */; }; - A9B315BC1B3940810001CB9C /* Disposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BE19EF2A5800984962 /* Disposable.swift */; }; - A9B315BE1B3940810001CB9C /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B51A69A3DB00AD8286 /* Event.swift */; }; A9B315BF1B3940810001CB9C /* ObjectiveCBridging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312C419EF2A5800984962 /* ObjectiveCBridging.swift */; }; - A9B315C01B3940810001CB9C /* Scheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312C819EF2A5800984962 /* Scheduler.swift */; }; - A9B315C11B3940810001CB9C /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54AF1A69A2AC00AD8286 /* Action.swift */; }; - A9B315C21B3940810001CB9C /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B01A69A2AC00AD8286 /* Property.swift */; }; - A9B315C31B3940810001CB9C /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B11A69A2AC00AD8286 /* Signal.swift */; }; - A9B315C41B3940810001CB9C /* SignalProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B21A69A2AC00AD8286 /* SignalProducer.swift */; }; - A9B315C51B3940810001CB9C /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BB19EF2A5800984962 /* Atomic.swift */; }; - A9B315C61B3940810001CB9C /* Bag.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BC19EF2A5800984962 /* Bag.swift */; }; - A9B315C71B3940810001CB9C /* TupleExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00004081A46864E000E7D41 /* TupleExtensions.swift */; }; - A9B315C81B3940810001CB9C /* FoundationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B4A3C19F4C39A009E02AC /* FoundationExtensions.swift */; }; A9B315C91B3940980001CB9C /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; A9B315CA1B3940AB0001CB9C /* ReactiveCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; }; A9B315CB1B3940AB0001CB9C /* EXTKeyPathCoding.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666619EDA57100A782A9 /* EXTKeyPathCoding.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -384,27 +337,26 @@ A9B3161C1B3940AF0001CB9C /* RACUnit.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764BE19EDA41200A782A9 /* RACUnit.h */; settings = {ATTRIBUTES = (Public, ); }; }; A9B316341B394C7F0001CB9C /* RACCompoundDisposableProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D037646A19EDA41200A782A9 /* RACCompoundDisposableProvider.d */; }; A9B316351B394C7F0001CB9C /* RACSignalProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D03764A319EDA41200A782A9 /* RACSignalProvider.d */; }; - A9F793341B60D0140026BCBA /* Optional.swift in Sources */ = {isa = PBXBuildFile; fileRef = D871D69E1B3B29A40070F16C /* Optional.swift */; }; B696FB811A7640C00075236D /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B696FB801A7640C00075236D /* TestError.swift */; }; B696FB821A7640C00075236D /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B696FB801A7640C00075236D /* TestError.swift */; }; + BE330A0F1D634F1E00806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A0E1D634F1E00806963 /* ReactiveSwift.framework */; }; + BE330A111D634F2900806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A101D634F2900806963 /* ReactiveSwift.framework */; }; + BE330A131D634F2E00806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A121D634F2E00806963 /* ReactiveSwift.framework */; }; + BE330A151D634F4000806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A141D634F4000806963 /* ReactiveSwift.framework */; }; + BE330A171D634F4E00806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A161D634F4E00806963 /* ReactiveSwift.framework */; }; + BE330A191D634F5900806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A181D634F5900806963 /* ReactiveSwift.framework */; }; + BE330A1B1D634F5F00806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A1A1D634F5F00806963 /* ReactiveSwift.framework */; }; BEBDD6E51CDC292D009A75A9 /* RACDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646B19EDA41200A782A9 /* RACDelegateProxy.h */; settings = {ATTRIBUTES = (Public, ); }; }; BEBDD6E61CDC292D009A75A9 /* RACDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646B19EDA41200A782A9 /* RACDelegateProxy.h */; settings = {ATTRIBUTES = (Public, ); }; }; BEBDD6E71CDC292E009A75A9 /* RACDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646B19EDA41200A782A9 /* RACDelegateProxy.h */; settings = {ATTRIBUTES = (Public, ); }; }; BEBDD6E81CDC292F009A75A9 /* RACDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646B19EDA41200A782A9 /* RACDelegateProxy.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BEE020661D637B0000DF261F /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B696FB801A7640C00075236D /* TestError.swift */; }; BFA6B94D1A7604D400C846D1 /* SignalProducerNimbleMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */; }; BFA6B94E1A7604D500C846D1 /* SignalProducerNimbleMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */; }; C7142DBC1CDEA167009F402D /* CocoaAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7142DBB1CDEA167009F402D /* CocoaAction.swift */; }; C7142DBD1CDEA194009F402D /* CocoaAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7142DBB1CDEA167009F402D /* CocoaAction.swift */; }; C7142DBE1CDEA194009F402D /* CocoaAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7142DBB1CDEA167009F402D /* CocoaAction.swift */; }; C7142DBF1CDEA195009F402D /* CocoaAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7142DBB1CDEA167009F402D /* CocoaAction.swift */; }; - C79B64741CD38B2B003F2376 /* TestLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79B64731CD38B2B003F2376 /* TestLogger.swift */; }; - C79B64751CD38B2B003F2376 /* TestLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79B64731CD38B2B003F2376 /* TestLogger.swift */; }; - C79B647C1CD52E23003F2376 /* EventLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79B647B1CD52E23003F2376 /* EventLogger.swift */; }; - C79B647D1CD52E4A003F2376 /* EventLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79B647B1CD52E23003F2376 /* EventLogger.swift */; }; - C79B647F1CD52E4D003F2376 /* EventLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79B647B1CD52E23003F2376 /* EventLogger.swift */; }; - C79B64801CD52E4E003F2376 /* EventLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79B647B1CD52E23003F2376 /* EventLogger.swift */; }; - CA6F28501C52626B001879D2 /* FlattenSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA6F284F1C52626B001879D2 /* FlattenSpec.swift */; }; - CA6F28511C52626B001879D2 /* FlattenSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA6F284F1C52626B001879D2 /* FlattenSpec.swift */; }; CD0C45DE1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */; }; CD0C45DF1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */; }; CD0C45E01CC9A288009F5BF0 /* DynamicProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */; }; @@ -416,17 +368,11 @@ CDC42E301AE7AB8B00965373 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; CDC42E311AE7AB8B00965373 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; CDC42E331AE7AC6D00965373 /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - CDCD247A1C277EEC00710AEE /* AtomicSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312EE19EF2A7700984962 /* AtomicSpec.swift */; }; - CDCD247B1C277EED00710AEE /* AtomicSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312EE19EF2A7700984962 /* AtomicSpec.swift */; }; CDF066CA1CDC1CA200199626 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; }; CDF066CB1CDC1CA200199626 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; }; - D00004091A46864E000E7D41 /* TupleExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00004081A46864E000E7D41 /* TupleExtensions.swift */; }; - D000040A1A46864E000E7D41 /* TupleExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00004081A46864E000E7D41 /* TupleExtensions.swift */; }; D01B7B6219EDD8FE00D26E01 /* Nimble.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; D01B7B6319EDD8FE00D26E01 /* Quick.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; D01B7B6419EDD94B00D26E01 /* ReactiveCocoa.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D047260C19E49F82006002AA /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - D021671D1A6CD50500987861 /* ActionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D021671C1A6CD50500987861 /* ActionSpec.swift */; }; - D021671E1A6CD50500987861 /* ActionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D021671C1A6CD50500987861 /* ActionSpec.swift */; }; D03764E819EDA41200A782A9 /* NSArray+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037642A19EDA41200A782A9 /* NSArray+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; D03764E919EDA41200A782A9 /* NSArray+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037642A19EDA41200A782A9 /* NSArray+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; D03764EA19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037642B19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m */; }; @@ -790,43 +736,17 @@ D037672819EDA63500A782A9 /* RACBehaviorSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646019EDA41200A782A9 /* RACBehaviorSubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; D037672D19EDA75D00A782A9 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; }; D037672F19EDA78B00A782A9 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; }; - D03B4A3D19F4C39A009E02AC /* FoundationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B4A3C19F4C39A009E02AC /* FoundationExtensions.swift */; }; - D03B4A3E19F4C39A009E02AC /* FoundationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B4A3C19F4C39A009E02AC /* FoundationExtensions.swift */; }; D04725F019E49ED7006002AA /* ReactiveCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; }; D04725F619E49ED7006002AA /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D04725EA19E49ED7006002AA /* ReactiveCocoa.framework */; }; D047261719E49F82006002AA /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D047260C19E49F82006002AA /* ReactiveCocoa.framework */; }; D05E662519EDD82000904ACA /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; }; D05E662619EDD83000904ACA /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; }; - D08C54B31A69A2AE00AD8286 /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B11A69A2AC00AD8286 /* Signal.swift */; }; - D08C54B41A69A2AF00AD8286 /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B11A69A2AC00AD8286 /* Signal.swift */; }; - D08C54B61A69A3DB00AD8286 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B51A69A3DB00AD8286 /* Event.swift */; }; - D08C54B71A69A3DB00AD8286 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B51A69A3DB00AD8286 /* Event.swift */; }; - D08C54B81A69A9D000AD8286 /* SignalProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B21A69A2AC00AD8286 /* SignalProducer.swift */; }; - D08C54B91A69A9D100AD8286 /* SignalProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B21A69A2AC00AD8286 /* SignalProducer.swift */; }; - D08C54BA1A69C54300AD8286 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B01A69A2AC00AD8286 /* Property.swift */; }; - D08C54BB1A69C54400AD8286 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B01A69A2AC00AD8286 /* Property.swift */; }; - D0A226081A72E0E900D33B74 /* SignalSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A226071A72E0E900D33B74 /* SignalSpec.swift */; }; - D0A226091A72E0E900D33B74 /* SignalSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A226071A72E0E900D33B74 /* SignalSpec.swift */; }; - D0A2260B1A72E6C500D33B74 /* SignalProducerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260A1A72E6C500D33B74 /* SignalProducerSpec.swift */; }; - D0A2260C1A72E6C500D33B74 /* SignalProducerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260A1A72E6C500D33B74 /* SignalProducerSpec.swift */; }; - D0A2260E1A72F16D00D33B74 /* PropertySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260D1A72F16D00D33B74 /* PropertySpec.swift */; }; - D0A2260F1A72F16D00D33B74 /* PropertySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260D1A72F16D00D33B74 /* PropertySpec.swift */; }; + D0A2260E1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */; }; + D0A2260F1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */; }; D0A226111A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A226101A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift */; }; D0A226121A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A226101A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift */; }; - D0C312CD19EF2A5800984962 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BB19EF2A5800984962 /* Atomic.swift */; }; - D0C312CE19EF2A5800984962 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BB19EF2A5800984962 /* Atomic.swift */; }; - D0C312CF19EF2A5800984962 /* Bag.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BC19EF2A5800984962 /* Bag.swift */; }; - D0C312D019EF2A5800984962 /* Bag.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BC19EF2A5800984962 /* Bag.swift */; }; - D0C312D319EF2A5800984962 /* Disposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BE19EF2A5800984962 /* Disposable.swift */; }; - D0C312D419EF2A5800984962 /* Disposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BE19EF2A5800984962 /* Disposable.swift */; }; D0C312DF19EF2A5800984962 /* ObjectiveCBridging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312C419EF2A5800984962 /* ObjectiveCBridging.swift */; }; D0C312E019EF2A5800984962 /* ObjectiveCBridging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312C419EF2A5800984962 /* ObjectiveCBridging.swift */; }; - D0C312E719EF2A5800984962 /* Scheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312C819EF2A5800984962 /* Scheduler.swift */; }; - D0C312E819EF2A5800984962 /* Scheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312C819EF2A5800984962 /* Scheduler.swift */; }; - D0C3130C19EF2B1F00984962 /* DisposableSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312F019EF2A7700984962 /* DisposableSpec.swift */; }; - D0C3130E19EF2B1F00984962 /* SchedulerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312F219EF2A7700984962 /* SchedulerSpec.swift */; }; - D0C3131219EF2B2000984962 /* DisposableSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312F019EF2A7700984962 /* DisposableSpec.swift */; }; - D0C3131419EF2B2000984962 /* SchedulerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312F219EF2A7700984962 /* SchedulerSpec.swift */; }; D0C3131E19EF2D9700984962 /* RACTestExampleScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131819EF2D9700984962 /* RACTestExampleScheduler.m */; }; D0C3131F19EF2D9700984962 /* RACTestExampleScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131819EF2D9700984962 /* RACTestExampleScheduler.m */; }; D0C3132019EF2D9700984962 /* RACTestObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131A19EF2D9700984962 /* RACTestObject.m */; }; @@ -834,22 +754,6 @@ D0C3132219EF2D9700984962 /* RACTestSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131B19EF2D9700984962 /* RACTestSchedulerSpec.m */; }; D0C3132319EF2D9700984962 /* RACTestSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131B19EF2D9700984962 /* RACTestSchedulerSpec.m */; }; D0C3132519EF2D9700984962 /* RACTestUIButton.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131D19EF2D9700984962 /* RACTestUIButton.m */; }; - D0D11AB91A6AE87700C1F8B1 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54AF1A69A2AC00AD8286 /* Action.swift */; }; - D0D11ABA1A6AE87700C1F8B1 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54AF1A69A2AC00AD8286 /* Action.swift */; }; - D8024DB21B2E1BB0005E6B9A /* SignalProducerLiftingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8024DB11B2E1BB0005E6B9A /* SignalProducerLiftingSpec.swift */; }; - D8024DB31B2E1BB0005E6B9A /* SignalProducerLiftingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8024DB11B2E1BB0005E6B9A /* SignalProducerLiftingSpec.swift */; }; - D8170FC11B100EBC004192AD /* FoundationExtensionsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8170FC01B100EBC004192AD /* FoundationExtensionsSpec.swift */; }; - D8170FC21B100EBC004192AD /* FoundationExtensionsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8170FC01B100EBC004192AD /* FoundationExtensionsSpec.swift */; }; - D85C652A1C0D84C7005A77AD /* Flatten.swift in Sources */ = {isa = PBXBuildFile; fileRef = D85C65291C0D84C7005A77AD /* Flatten.swift */; }; - D85C652B1C0E70E3005A77AD /* Flatten.swift in Sources */ = {isa = PBXBuildFile; fileRef = D85C65291C0D84C7005A77AD /* Flatten.swift */; }; - D85C652C1C0E70E4005A77AD /* Flatten.swift in Sources */ = {isa = PBXBuildFile; fileRef = D85C65291C0D84C7005A77AD /* Flatten.swift */; }; - D85C652D1C0E70E5005A77AD /* Flatten.swift in Sources */ = {isa = PBXBuildFile; fileRef = D85C65291C0D84C7005A77AD /* Flatten.swift */; }; - D871D69F1B3B29A40070F16C /* Optional.swift in Sources */ = {isa = PBXBuildFile; fileRef = D871D69E1B3B29A40070F16C /* Optional.swift */; }; - D8E84A671B3B32FB00C3E831 /* Optional.swift in Sources */ = {isa = PBXBuildFile; fileRef = D871D69E1B3B29A40070F16C /* Optional.swift */; }; - EBCC7DBC1BBF010C00A2AE92 /* Observer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBCC7DBB1BBF010C00A2AE92 /* Observer.swift */; }; - EBCC7DBD1BBF01E100A2AE92 /* Observer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBCC7DBB1BBF010C00A2AE92 /* Observer.swift */; }; - EBCC7DBE1BBF01E200A2AE92 /* Observer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBCC7DBB1BBF010C00A2AE92 /* Observer.swift */; }; - EBCC7DBF1BBF01E200A2AE92 /* Observer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBCC7DBB1BBF010C00A2AE92 /* Observer.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -908,11 +812,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 02D260291C1D6DAF003ACC61 /* SignalLifetimeSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalLifetimeSpec.swift; sourceTree = ""; }; 314304151ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MKAnnotationView+RACSignalSupport.h"; sourceTree = ""; }; 314304161ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MKAnnotationView+RACSignalSupport.m"; sourceTree = ""; }; 4A0E10FE1D2A92720065D310 /* Lifetime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Lifetime.swift; sourceTree = ""; }; - 4A0E11031D2A95200065D310 /* LifetimeSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LifetimeSpec.swift; sourceTree = ""; }; 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Application.xcconfig"; sourceTree = ""; }; 57A4D2451BA13F9700F7D4B1 /* tvOS-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Base.xcconfig"; sourceTree = ""; }; @@ -922,7 +824,6 @@ 7A70657E1A3F88B8001E8354 /* RACKVOProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACKVOProxy.m; sourceTree = ""; }; 7A7065831A3F8967001E8354 /* RACKVOProxySpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACKVOProxySpec.m; sourceTree = ""; }; 7DFBED031CDB8C9500EE435B /* ReactiveCocoaTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveCocoaTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 9ABCB1841D2A5B5A00BCA243 /* Deprecations+Removals.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deprecations+Removals.swift"; sourceTree = ""; }; 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyValueObservingSpec.swift; sourceTree = ""; }; 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+KeyValueObserving.swift"; sourceTree = ""; }; A97451331B3A935E00F48E55 /* watchOS-Application.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Application.xcconfig"; sourceTree = ""; }; @@ -931,16 +832,18 @@ A97451361B3A935E00F48E55 /* watchOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-StaticLibrary.xcconfig"; sourceTree = ""; }; A9B315541B3940610001CB9C /* ReactiveCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; B696FB801A7640C00075236D /* TestError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestError.swift; sourceTree = ""; }; + BE330A0E1D634F1E00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = build/Debug/ReactiveSwift.framework; sourceTree = ""; }; + BE330A101D634F2900806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = build/Debug/ReactiveSwift.framework; sourceTree = ""; }; + BE330A121D634F2E00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-iphoneos/ReactiveSwift.framework"; sourceTree = ""; }; + BE330A141D634F4000806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-iphoneos/ReactiveSwift.framework"; sourceTree = ""; }; + BE330A161D634F4E00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-watchos/ReactiveSwift.framework"; sourceTree = ""; }; + BE330A181D634F5900806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-appletvos/ReactiveSwift.framework"; sourceTree = ""; }; + BE330A1A1D634F5F00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-appletvos/ReactiveSwift.framework"; sourceTree = ""; }; BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SignalProducerNimbleMatchers.swift; path = Swift/SignalProducerNimbleMatchers.swift; sourceTree = ""; }; C7142DBB1CDEA167009F402D /* CocoaAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaAction.swift; sourceTree = ""; }; - C79B64731CD38B2B003F2376 /* TestLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestLogger.swift; sourceTree = ""; }; - C79B647B1CD52E23003F2376 /* EventLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventLogger.swift; sourceTree = ""; }; - CA6F284F1C52626B001879D2 /* FlattenSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlattenSpec.swift; sourceTree = ""; }; CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DynamicProperty.swift; sourceTree = ""; }; CD8401821CEE8ED7009F0ABF /* CocoaActionSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaActionSpec.swift; sourceTree = ""; }; CDC42E2E1AE7AB8B00965373 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D00004081A46864E000E7D41 /* TupleExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TupleExtensions.swift; sourceTree = ""; }; - D021671C1A6CD50500987861 /* ActionSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ActionSpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; D037642A19EDA41200A782A9 /* NSArray+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+RACSequenceAdditions.h"; sourceTree = ""; }; D037642B19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+RACSequenceAdditions.m"; sourceTree = ""; }; D037642C19EDA41200A782A9 /* NSControl+RACCommandSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSControl+RACCommandSupport.h"; sourceTree = ""; }; @@ -1186,7 +1089,6 @@ D03766B519EDA60000A782A9 /* UIButtonRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIButtonRACSupportSpec.m; sourceTree = ""; }; D03766B719EDA60000A782A9 /* UIImagePickerControllerRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIImagePickerControllerRACSupportSpec.m; sourceTree = ""; }; D037672B19EDA75D00A782A9 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Quick.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D03B4A3C19F4C39A009E02AC /* FoundationExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoundationExtensions.swift; sourceTree = ""; }; D04725EA19E49ED7006002AA /* ReactiveCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D04725EE19E49ED7006002AA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D04725EF19E49ED7006002AA /* ReactiveCocoa.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ReactiveCocoa.h; sourceTree = ""; }; @@ -1213,24 +1115,9 @@ D047263B19E49FE8006002AA /* Mac-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-StaticLibrary.xcconfig"; sourceTree = ""; }; D047263C19E49FE8006002AA /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; D05E662419EDD82000904ACA /* Nimble.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Nimble.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D08C54AF1A69A2AC00AD8286 /* Action.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Action.swift; sourceTree = ""; }; - D08C54B01A69A2AC00AD8286 /* Property.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Property.swift; sourceTree = ""; }; - D08C54B11A69A2AC00AD8286 /* Signal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Signal.swift; sourceTree = ""; }; - D08C54B21A69A2AC00AD8286 /* SignalProducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignalProducer.swift; sourceTree = ""; }; - D08C54B51A69A3DB00AD8286 /* Event.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Event.swift; sourceTree = ""; }; - D0A226071A72E0E900D33B74 /* SignalSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SignalSpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - D0A2260A1A72E6C500D33B74 /* SignalProducerSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SignalProducerSpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - D0A2260D1A72F16D00D33B74 /* PropertySpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = PropertySpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = DynamicPropertySpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; D0A226101A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ObjectiveCBridgingSpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - D0C312BB19EF2A5800984962 /* Atomic.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Atomic.swift; sourceTree = ""; }; - D0C312BC19EF2A5800984962 /* Bag.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Bag.swift; sourceTree = ""; }; - D0C312BE19EF2A5800984962 /* Disposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Disposable.swift; sourceTree = ""; }; D0C312C419EF2A5800984962 /* ObjectiveCBridging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjectiveCBridging.swift; sourceTree = ""; }; - D0C312C819EF2A5800984962 /* Scheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Scheduler.swift; sourceTree = ""; }; - D0C312EE19EF2A7700984962 /* AtomicSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AtomicSpec.swift; sourceTree = ""; }; - D0C312EF19EF2A7700984962 /* BagSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BagSpec.swift; sourceTree = ""; }; - D0C312F019EF2A7700984962 /* DisposableSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisposableSpec.swift; sourceTree = ""; }; - D0C312F219EF2A7700984962 /* SchedulerSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchedulerSpec.swift; sourceTree = ""; }; D0C3131719EF2D9700984962 /* RACTestExampleScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTestExampleScheduler.h; sourceTree = ""; }; D0C3131819EF2D9700984962 /* RACTestExampleScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTestExampleScheduler.m; sourceTree = ""; }; D0C3131919EF2D9700984962 /* RACTestObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTestObject.h; sourceTree = ""; }; @@ -1238,11 +1125,6 @@ D0C3131B19EF2D9700984962 /* RACTestSchedulerSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTestSchedulerSpec.m; sourceTree = ""; }; D0C3131C19EF2D9700984962 /* RACTestUIButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTestUIButton.h; sourceTree = ""; }; D0C3131D19EF2D9700984962 /* RACTestUIButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTestUIButton.m; sourceTree = ""; }; - D8024DB11B2E1BB0005E6B9A /* SignalProducerLiftingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SignalProducerLiftingSpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - D8170FC01B100EBC004192AD /* FoundationExtensionsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoundationExtensionsSpec.swift; sourceTree = ""; }; - D85C65291C0D84C7005A77AD /* Flatten.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Flatten.swift; sourceTree = ""; }; - D871D69E1B3B29A40070F16C /* Optional.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Optional.swift; sourceTree = ""; }; - EBCC7DBB1BBF010C00A2AE92 /* Observer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Observer.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1250,6 +1132,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + BE330A191D634F5900806963 /* ReactiveSwift.framework in Frameworks */, 57A4D2081BA13D7A00F7D4B1 /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1258,6 +1141,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + BE330A1B1D634F5F00806963 /* ReactiveSwift.framework in Frameworks */, CDF066CA1CDC1CA200199626 /* Nimble.framework in Frameworks */, CDF066CB1CDC1CA200199626 /* Quick.framework in Frameworks */, 7DFBED081CDB8C9500EE435B /* ReactiveCocoa.framework in Frameworks */, @@ -1268,6 +1152,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + BE330A171D634F4E00806963 /* ReactiveSwift.framework in Frameworks */, A9B315C91B3940980001CB9C /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1276,6 +1161,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + BE330A0F1D634F1E00806963 /* ReactiveSwift.framework in Frameworks */, CDC42E2F1AE7AB8B00965373 /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1284,6 +1170,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + BE330A111D634F2900806963 /* ReactiveSwift.framework in Frameworks */, CDC42E301AE7AB8B00965373 /* Result.framework in Frameworks */, D05E662519EDD82000904ACA /* Nimble.framework in Frameworks */, D037672D19EDA75D00A782A9 /* Quick.framework in Frameworks */, @@ -1295,6 +1182,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + BE330A131D634F2E00806963 /* ReactiveSwift.framework in Frameworks */, CDC42E311AE7AB8B00965373 /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1303,6 +1191,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + BE330A151D634F4000806963 /* ReactiveSwift.framework in Frameworks */, D05E662619EDD83000904ACA /* Nimble.framework in Frameworks */, D037672F19EDA78B00A782A9 /* Quick.framework in Frameworks */, D047261719E49F82006002AA /* ReactiveCocoa.framework in Frameworks */, @@ -1334,6 +1223,20 @@ path = watchOS; sourceTree = ""; }; + BE330A0D1D634F1E00806963 /* Frameworks */ = { + isa = PBXGroup; + children = ( + BE330A1A1D634F5F00806963 /* ReactiveSwift.framework */, + BE330A181D634F5900806963 /* ReactiveSwift.framework */, + BE330A161D634F4E00806963 /* ReactiveSwift.framework */, + BE330A141D634F4000806963 /* ReactiveSwift.framework */, + BE330A121D634F2E00806963 /* ReactiveSwift.framework */, + BE330A101D634F2900806963 /* ReactiveSwift.framework */, + BE330A0E1D634F1E00806963 /* ReactiveSwift.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; D037642919EDA3B600A782A9 /* Objective-C */ = { isa = PBXGroup; children = ( @@ -1614,30 +1517,16 @@ D03B4A3919F4C25F009E02AC /* Signals */ = { isa = PBXGroup; children = ( - D08C54AF1A69A2AC00AD8286 /* Action.swift */, C7142DBB1CDEA167009F402D /* CocoaAction.swift */, CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */, - D85C65291C0D84C7005A77AD /* Flatten.swift */, 4A0E10FE1D2A92720065D310 /* Lifetime.swift */, - D08C54B01A69A2AC00AD8286 /* Property.swift */, - D08C54B11A69A2AC00AD8286 /* Signal.swift */, - D08C54B21A69A2AC00AD8286 /* SignalProducer.swift */, ); name = Signals; sourceTree = ""; }; - D03B4A3A19F4C26D009E02AC /* Internal Utilities */ = { - isa = PBXGroup; - children = ( - D00004081A46864E000E7D41 /* TupleExtensions.swift */, - ); - name = "Internal Utilities"; - sourceTree = ""; - }; D03B4A3B19F4C281009E02AC /* Extensions */ = { isa = PBXGroup; children = ( - D03B4A3C19F4C39A009E02AC /* FoundationExtensions.swift */, 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */, ); name = Extensions; @@ -1650,6 +1539,7 @@ D04725F919E49ED7006002AA /* ReactiveCocoaTests */, D047262519E49FE8006002AA /* Configuration */, D04725EB19E49ED7006002AA /* Products */, + BE330A0D1D634F1E00806963 /* Frameworks */, ); sourceTree = ""; usesTabs = 1; @@ -1781,19 +1671,9 @@ D0C312B919EF2A3000984962 /* Swift */ = { isa = PBXGroup; children = ( - D0C312BB19EF2A5800984962 /* Atomic.swift */, - D0C312BC19EF2A5800984962 /* Bag.swift */, - D0C312BE19EF2A5800984962 /* Disposable.swift */, - D08C54B51A69A3DB00AD8286 /* Event.swift */, D0C312C419EF2A5800984962 /* ObjectiveCBridging.swift */, - EBCC7DBB1BBF010C00A2AE92 /* Observer.swift */, - D871D69E1B3B29A40070F16C /* Optional.swift */, - D0C312C819EF2A5800984962 /* Scheduler.swift */, - C79B647B1CD52E23003F2376 /* EventLogger.swift */, D03B4A3919F4C25F009E02AC /* Signals */, - D03B4A3A19F4C26D009E02AC /* Internal Utilities */, D03B4A3B19F4C281009E02AC /* Extensions */, - 9ABCB1841D2A5B5A00BCA243 /* Deprecations+Removals.swift */, ); path = Swift; sourceTree = ""; @@ -1801,23 +1681,10 @@ D0C312ED19EF2A6F00984962 /* Swift */ = { isa = PBXGroup; children = ( - D021671C1A6CD50500987861 /* ActionSpec.swift */, - D0C312EE19EF2A7700984962 /* AtomicSpec.swift */, - D0C312EF19EF2A7700984962 /* BagSpec.swift */, CD8401821CEE8ED7009F0ABF /* CocoaActionSpec.swift */, - D0C312F019EF2A7700984962 /* DisposableSpec.swift */, - CA6F284F1C52626B001879D2 /* FlattenSpec.swift */, - D8170FC01B100EBC004192AD /* FoundationExtensionsSpec.swift */, - 4A0E11031D2A95200065D310 /* LifetimeSpec.swift */, + D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */, D0A226101A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift */, - D0A2260D1A72F16D00D33B74 /* PropertySpec.swift */, - D0C312F219EF2A7700984962 /* SchedulerSpec.swift */, - 02D260291C1D6DAF003ACC61 /* SignalLifetimeSpec.swift */, - D8024DB11B2E1BB0005E6B9A /* SignalProducerLiftingSpec.swift */, - D0A2260A1A72E6C500D33B74 /* SignalProducerSpec.swift */, - D0A226071A72E0E900D33B74 /* SignalSpec.swift */, B696FB801A7640C00075236D /* TestError.swift */, - C79B64731CD38B2B003F2376 /* TestLogger.swift */, 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */, ); path = Swift; @@ -2338,29 +2205,16 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 57A4D1B11BA13D7A00F7D4B1 /* Optional.swift in Sources */, 57D476951C4206EC00EFE697 /* UITableViewCell+RACSignalSupport.m in Sources */, 57A4D1B21BA13D7A00F7D4B1 /* RACCompoundDisposableProvider.d in Sources */, 57D476901C4206D400EFE697 /* UIControl+RACSignalSupportPrivate.m in Sources */, 57A4D1B31BA13D7A00F7D4B1 /* RACSignalProvider.d in Sources */, - 57A4D1B41BA13D7A00F7D4B1 /* Disposable.swift in Sources */, - 57A4D1B61BA13D7A00F7D4B1 /* Event.swift in Sources */, 57A4D1B71BA13D7A00F7D4B1 /* ObjectiveCBridging.swift in Sources */, - 57A4D1B81BA13D7A00F7D4B1 /* Scheduler.swift in Sources */, - 57A4D1B91BA13D7A00F7D4B1 /* Action.swift in Sources */, - 57A4D1BA1BA13D7A00F7D4B1 /* Property.swift in Sources */, - 57A4D1BB1BA13D7A00F7D4B1 /* Signal.swift in Sources */, - 57A4D1BC1BA13D7A00F7D4B1 /* SignalProducer.swift in Sources */, - 57A4D1BD1BA13D7A00F7D4B1 /* Atomic.swift in Sources */, - 57A4D1BE1BA13D7A00F7D4B1 /* Bag.swift in Sources */, - 57A4D1BF1BA13D7A00F7D4B1 /* TupleExtensions.swift in Sources */, - 57A4D1C01BA13D7A00F7D4B1 /* FoundationExtensions.swift in Sources */, 57A4D1C11BA13D7A00F7D4B1 /* EXTRuntimeExtensions.m in Sources */, 57A4D1C21BA13D7A00F7D4B1 /* NSArray+RACSequenceAdditions.m in Sources */, 57A4D1C31BA13D7A00F7D4B1 /* NSData+RACSupport.m in Sources */, 57A4D1C41BA13D7A00F7D4B1 /* NSDictionary+RACSequenceAdditions.m in Sources */, 57A4D1C51BA13D7A00F7D4B1 /* NSEnumerator+RACSequenceAdditions.m in Sources */, - D85C652D1C0E70E5005A77AD /* Flatten.swift in Sources */, 57D476961C4206EC00EFE697 /* UITableViewHeaderFooterView+RACSignalSupport.m in Sources */, 57A4D1C61BA13D7A00F7D4B1 /* NSFileHandle+RACSupport.m in Sources */, 57A4D1C71BA13D7A00F7D4B1 /* NSIndexSet+RACSequenceAdditions.m in Sources */, @@ -2374,7 +2228,6 @@ 57A4D1CD1BA13D7A00F7D4B1 /* NSObject+RACLifting.m in Sources */, 57A4D1CE1BA13D7A00F7D4B1 /* NSObject+RACPropertySubscribing.m in Sources */, 57A4D1CF1BA13D7A00F7D4B1 /* NSObject+RACSelectorSignal.m in Sources */, - 9ABCB1881D2A5B5A00BCA243 /* Deprecations+Removals.swift in Sources */, 57D476981C4206EC00EFE697 /* UITextView+RACSignalSupport.m in Sources */, 57A4D1D01BA13D7A00F7D4B1 /* NSOrderedSet+RACSequenceAdditions.m in Sources */, 57A4D1D11BA13D7A00F7D4B1 /* NSSet+RACSequenceAdditions.m in Sources */, @@ -2393,12 +2246,10 @@ 57A4D1DD1BA13D7A00F7D4B1 /* RACDelegateProxy.m in Sources */, 57A4D1DE1BA13D7A00F7D4B1 /* RACDisposable.m in Sources */, 9AD0F06D1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, - EBCC7DBF1BBF01E200A2AE92 /* Observer.swift in Sources */, 57A4D1DF1BA13D7A00F7D4B1 /* RACDynamicSequence.m in Sources */, C7142DBF1CDEA195009F402D /* CocoaAction.swift in Sources */, 57A4D1E01BA13D7A00F7D4B1 /* RACDynamicSignal.m in Sources */, 57A4D1E11BA13D7A00F7D4B1 /* RACEagerSequence.m in Sources */, - C79B64801CD52E4E003F2376 /* EventLogger.swift in Sources */, 57D4768D1C42063C00EFE697 /* UIControl+RACSignalSupport.m in Sources */, 4A0E11021D2A92720065D310 /* Lifetime.swift in Sources */, 57A4D1E21BA13D7A00F7D4B1 /* RACEmptySequence.m in Sources */, @@ -2446,21 +2297,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 7DFBED221CDB8DE300EE435B /* ActionSpec.swift in Sources */, - 7DFBED231CDB8DE300EE435B /* AtomicSpec.swift in Sources */, - 7DFBED241CDB8DE300EE435B /* BagSpec.swift in Sources */, - 7DFBED251CDB8DE300EE435B /* DisposableSpec.swift in Sources */, - 7DFBED261CDB8DE300EE435B /* FoundationExtensionsSpec.swift in Sources */, 7DFBED271CDB8DE300EE435B /* ObjectiveCBridgingSpec.swift in Sources */, - 7DFBED281CDB8DE300EE435B /* PropertySpec.swift in Sources */, - 7DFBED291CDB8DE300EE435B /* SchedulerSpec.swift in Sources */, - 7DFBED2A1CDB8DE300EE435B /* SignalLifetimeSpec.swift in Sources */, - 7DFBED2B1CDB8DE300EE435B /* SignalProducerSpec.swift in Sources */, - 7DFBED2C1CDB8DE300EE435B /* SignalProducerLiftingSpec.swift in Sources */, - 7DFBED2D1CDB8DE300EE435B /* SignalSpec.swift in Sources */, - 7DFBED2E1CDB8DE300EE435B /* FlattenSpec.swift in Sources */, - 7DFBED2F1CDB8DE300EE435B /* TestError.swift in Sources */, - 7DFBED301CDB8DE300EE435B /* TestLogger.swift in Sources */, + 7DFBED281CDB8DE300EE435B /* DynamicPropertySpec.swift in Sources */, 7DFBED321CDB8DE300EE435B /* NSEnumeratorRACSequenceAdditionsSpec.m in Sources */, 7DFBED331CDB8DE300EE435B /* NSNotificationCenterRACSupportSpec.m in Sources */, 7DFBED351CDB8DE300EE435B /* NSObjectRACDeallocatingSpec.m in Sources */, @@ -2469,7 +2307,6 @@ 7DFBED391CDB8DE300EE435B /* NSObjectRACPropertySubscribingSpec.m in Sources */, 7DFBED3A1CDB8DE300EE435B /* NSObjectRACSelectorSignalSpec.m in Sources */, 7DFBED3B1CDB8DE300EE435B /* NSStringRACKeyPathUtilitiesSpec.m in Sources */, - 4A0E11061D2A95200065D310 /* LifetimeSpec.swift in Sources */, 7DFBED3D1CDB8DE300EE435B /* NSUserDefaultsRACSupportSpec.m in Sources */, 7DFBED3E1CDB8DE300EE435B /* RACBlockTrampolineSpec.m in Sources */, 7DFBED401CDB8DE300EE435B /* RACChannelExamples.m in Sources */, @@ -2507,6 +2344,7 @@ 7DFBED671CDB8DE300EE435B /* RACTestExampleScheduler.m in Sources */, 7DFBED691CDB8DE300EE435B /* RACTestObject.m in Sources */, 7DFBED6A1CDB8DE300EE435B /* RACTestSchedulerSpec.m in Sources */, + BEE020661D637B0000DF261F /* TestError.swift in Sources */, 7DFBED6C1CDB8DE300EE435B /* RACTestUIButton.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2515,32 +2353,18 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - A9F793341B60D0140026BCBA /* Optional.swift in Sources */, A9B316341B394C7F0001CB9C /* RACCompoundDisposableProvider.d in Sources */, A9B316351B394C7F0001CB9C /* RACSignalProvider.d in Sources */, - A9B315BC1B3940810001CB9C /* Disposable.swift in Sources */, - A9B315BE1B3940810001CB9C /* Event.swift in Sources */, A9B315BF1B3940810001CB9C /* ObjectiveCBridging.swift in Sources */, - A9B315C01B3940810001CB9C /* Scheduler.swift in Sources */, - A9B315C11B3940810001CB9C /* Action.swift in Sources */, - A9B315C21B3940810001CB9C /* Property.swift in Sources */, - A9B315C31B3940810001CB9C /* Signal.swift in Sources */, - A9B315C41B3940810001CB9C /* SignalProducer.swift in Sources */, - A9B315C51B3940810001CB9C /* Atomic.swift in Sources */, - A9B315C61B3940810001CB9C /* Bag.swift in Sources */, - A9B315C71B3940810001CB9C /* TupleExtensions.swift in Sources */, - A9B315C81B3940810001CB9C /* FoundationExtensions.swift in Sources */, A9B3155E1B3940750001CB9C /* EXTRuntimeExtensions.m in Sources */, A9B315601B3940750001CB9C /* NSArray+RACSequenceAdditions.m in Sources */, A9B315631B3940750001CB9C /* NSData+RACSupport.m in Sources */, A9B315641B3940750001CB9C /* NSDictionary+RACSequenceAdditions.m in Sources */, A9B315651B3940750001CB9C /* NSEnumerator+RACSequenceAdditions.m in Sources */, - D85C652C1C0E70E4005A77AD /* Flatten.swift in Sources */, A9B315661B3940750001CB9C /* NSFileHandle+RACSupport.m in Sources */, A9B315671B3940750001CB9C /* NSIndexSet+RACSequenceAdditions.m in Sources */, A9B315681B3940750001CB9C /* NSInvocation+RACTypeParsing.m in Sources */, A9B315691B3940750001CB9C /* NSNotificationCenter+RACSupport.m in Sources */, - 9ABCB1871D2A5B5A00BCA243 /* Deprecations+Removals.swift in Sources */, A9B3156B1B3940750001CB9C /* NSObject+RACDeallocating.m in Sources */, A9B3156C1B3940750001CB9C /* NSObject+RACDescription.m in Sources */, A9B3156D1B3940750001CB9C /* NSObject+RACKVOWrapper.m in Sources */, @@ -2561,8 +2385,6 @@ A9B3157E1B3940750001CB9C /* RACCompoundDisposable.m in Sources */, A9B3157F1B3940750001CB9C /* RACDelegateProxy.m in Sources */, A9B315801B3940750001CB9C /* RACDisposable.m in Sources */, - EBCC7DBE1BBF01E200A2AE92 /* Observer.swift in Sources */, - C79B647F1CD52E4D003F2376 /* EventLogger.swift in Sources */, A9B315811B3940750001CB9C /* RACDynamicSequence.m in Sources */, A9B315821B3940750001CB9C /* RACDynamicSignal.m in Sources */, A9B315831B3940750001CB9C /* RACEagerSequence.m in Sources */, @@ -2605,7 +2427,6 @@ A9B315A61B3940750001CB9C /* RACUnit.m in Sources */, A9B315A71B3940750001CB9C /* RACValueTransformer.m in Sources */, 9AD0F06C1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, - A9B315BB1B3940750001CB9C /* RACDynamicPropertySuperclass.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2618,12 +2439,9 @@ D03765C819EDA41200A782A9 /* RACScopedDisposable.m in Sources */, D03764FE19EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m in Sources */, D03764EA19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m in Sources */, - D00004091A46864E000E7D41 /* TupleExtensions.swift in Sources */, D03765C019EDA41200A782A9 /* RACScheduler.m in Sources */, D037659819EDA41200A782A9 /* RACIndexSetSequence.m in Sources */, D03765D819EDA41200A782A9 /* RACSignal+Operations.m in Sources */, - D871D69F1B3B29A40070F16C /* Optional.swift in Sources */, - D08C54B61A69A3DB00AD8286 /* Event.swift in Sources */, D03764F219EDA41200A782A9 /* NSControl+RACTextSignalSupport.m in Sources */, D037650219EDA41200A782A9 /* NSFileHandle+RACSupport.m in Sources */, D03765E219EDA41200A782A9 /* RACStream.m in Sources */, @@ -2632,19 +2450,15 @@ D03765B819EDA41200A782A9 /* RACReplaySubject.m in Sources */, D03765EC19EDA41200A782A9 /* RACSubject.m in Sources */, D03765D019EDA41200A782A9 /* RACSerialDisposable.m in Sources */, - D0C312D319EF2A5800984962 /* Disposable.swift in Sources */, D037666F19EDA57100A782A9 /* EXTRuntimeExtensions.m in Sources */, D037653E19EDA41200A782A9 /* NSString+RACSupport.m in Sources */, D037653619EDA41200A782A9 /* NSString+RACKeyPathUtilities.m in Sources */, D03764FA19EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m in Sources */, - EBCC7DBC1BBF010C00A2AE92 /* Observer.swift in Sources */, CD0C45DE1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, D037656819EDA41200A782A9 /* RACCompoundDisposableProvider.d in Sources */, - D03B4A3D19F4C39A009E02AC /* FoundationExtensions.swift in Sources */, D037653A19EDA41200A782A9 /* NSString+RACSequenceAdditions.m in Sources */, D03765E819EDA41200A782A9 /* RACStringSequence.m in Sources */, D03764EE19EDA41200A782A9 /* NSControl+RACCommandSupport.m in Sources */, - D08C54B31A69A2AE00AD8286 /* Signal.swift in Sources */, 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, D037660A19EDA41200A782A9 /* RACTupleSequence.m in Sources */, D03765D419EDA41200A782A9 /* RACSignal.m in Sources */, @@ -2655,8 +2469,6 @@ D037650619EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m in Sources */, D037650E19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m in Sources */, D03765FA19EDA41200A782A9 /* RACSubscriptionScheduler.m in Sources */, - D85C652A1C0D84C7005A77AD /* Flatten.swift in Sources */, - D0C312CF19EF2A5800984962 /* Bag.swift in Sources */, D037658019EDA41200A782A9 /* RACEmptySequence.m in Sources */, D037654A19EDA41200A782A9 /* NSUserDefaults+RACSupport.m in Sources */, D037660E19EDA41200A782A9 /* RACUnarySequence.m in Sources */, @@ -2680,8 +2492,6 @@ 7A7065811A3F88B8001E8354 /* RACKVOProxy.m in Sources */, D037651619EDA41200A782A9 /* NSObject+RACDeallocating.m in Sources */, 4A0E10FF1D2A92720065D310 /* Lifetime.swift in Sources */, - D0C312E719EF2A5800984962 /* Scheduler.swift in Sources */, - D0C312CD19EF2A5800984962 /* Atomic.swift in Sources */, D037658419EDA41200A782A9 /* RACEmptySignal.m in Sources */, D037654619EDA41200A782A9 /* NSURLConnection+RACSupport.m in Sources */, D03765F019EDA41200A782A9 /* RACSubscriber.m in Sources */, @@ -2689,21 +2499,16 @@ D037656219EDA41200A782A9 /* RACCommand.m in Sources */, D037658819EDA41200A782A9 /* RACErrorSignal.m in Sources */, D03765F619EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m in Sources */, - D08C54BA1A69C54300AD8286 /* Property.swift in Sources */, - D0D11AB91A6AE87700C1F8B1 /* Action.swift in Sources */, D037661219EDA41200A782A9 /* RACUnit.m in Sources */, D03765A019EDA41200A782A9 /* RACKVOTrampoline.m in Sources */, D037650A19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m in Sources */, D037660619EDA41200A782A9 /* RACTuple.m in Sources */, D037651E19EDA41200A782A9 /* NSObject+RACKVOWrapper.m in Sources */, D037661619EDA41200A782A9 /* RACValueTransformer.m in Sources */, - C79B647C1CD52E23003F2376 /* EventLogger.swift in Sources */, D03765CC19EDA41200A782A9 /* RACSequence.m in Sources */, - 9ABCB1851D2A5B5A00BCA243 /* Deprecations+Removals.swift in Sources */, D037652E19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m in Sources */, D037652619EDA41200A782A9 /* NSObject+RACPropertySubscribing.m in Sources */, D037658C19EDA41200A782A9 /* RACEvent.m in Sources */, - D08C54B81A69A9D000AD8286 /* SignalProducer.swift in Sources */, D03765B219EDA41200A782A9 /* RACQueueScheduler.m in Sources */, D037652A19EDA41200A782A9 /* NSObject+RACSelectorSignal.m in Sources */, D03765AE19EDA41200A782A9 /* RACPassthroughSubscriber.m in Sources */, @@ -2715,51 +2520,39 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D0A2260E1A72F16D00D33B74 /* PropertySpec.swift in Sources */, + D0A2260E1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */, D03766C719EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.m in Sources */, D03766E319EDA60000A782A9 /* RACDelegateProxySpec.m in Sources */, B696FB811A7640C00075236D /* TestError.swift in Sources */, - D021671D1A6CD50500987861 /* ActionSpec.swift in Sources */, D03766F919EDA60000A782A9 /* RACSerialDisposableSpec.m in Sources */, D0C3131E19EF2D9700984962 /* RACTestExampleScheduler.m in Sources */, D037670B19EDA60000A782A9 /* RACTargetQueueSchedulerSpec.m in Sources */, D03766DD19EDA60000A782A9 /* RACCommandSpec.m in Sources */, - D0C3130E19EF2B1F00984962 /* SchedulerSpec.swift in Sources */, D037670919EDA60000A782A9 /* RACSubscriptingAssignmentTrampolineSpec.m in Sources */, BFA6B94D1A7604D400C846D1 /* SignalProducerNimbleMatchers.swift in Sources */, D03766EB19EDA60000A782A9 /* RACKVOWrapperSpec.m in Sources */, D03766E719EDA60000A782A9 /* RACEventSpec.m in Sources */, D03766F719EDA60000A782A9 /* RACSequenceSpec.m in Sources */, - D8170FC11B100EBC004192AD /* FoundationExtensionsSpec.swift in Sources */, D03766C919EDA60000A782A9 /* NSObjectRACPropertySubscribingSpec.m in Sources */, D03766C319EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m in Sources */, - C79B64741CD38B2B003F2376 /* TestLogger.swift in Sources */, D03766BD19EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m in Sources */, - CA6F28501C52626B001879D2 /* FlattenSpec.swift in Sources */, D037670119EDA60000A782A9 /* RACSubclassObject.m in Sources */, D03766CD19EDA60000A782A9 /* NSStringRACKeyPathUtilitiesSpec.m in Sources */, - 4A0E11041D2A95200065D310 /* LifetimeSpec.swift in Sources */, D037671519EDA60000A782A9 /* RACTupleSpec.m in Sources */, D03766C519EDA60000A782A9 /* NSObjectRACLiftingSpec.m in Sources */, D03766D119EDA60000A782A9 /* NSURLConnectionRACSupportSpec.m in Sources */, D03766F319EDA60000A782A9 /* RACSequenceAdditionsSpec.m in Sources */, D03766ED19EDA60000A782A9 /* RACMulticastConnectionSpec.m in Sources */, - CDCD247A1C277EEC00710AEE /* AtomicSpec.swift in Sources */, D03766E919EDA60000A782A9 /* RACKVOChannelSpec.m in Sources */, D03766FB19EDA60000A782A9 /* RACSignalSpec.m in Sources */, 7A7065841A3F8967001E8354 /* RACKVOProxySpec.m in Sources */, - 579504331BB8A34200A5E482 /* BagSpec.swift in Sources */, D037670719EDA60000A782A9 /* RACSubscriberSpec.m in Sources */, CD8401831CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, D03766EF19EDA60000A782A9 /* RACPropertySignalExamples.m in Sources */, D037670519EDA60000A782A9 /* RACSubscriberExamples.m in Sources */, 9A1E72BA1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, - D0A226081A72E0E900D33B74 /* SignalSpec.swift in Sources */, D0C3132219EF2D9700984962 /* RACTestSchedulerSpec.m in Sources */, - 02D2602B1C1D6DB8003ACC61 /* SignalLifetimeSpec.swift in Sources */, - D0C3130C19EF2B1F00984962 /* DisposableSpec.swift in Sources */, D03766D719EDA60000A782A9 /* RACBlockTrampolineSpec.m in Sources */, - D0A2260B1A72E6C500D33B74 /* SignalProducerSpec.swift in Sources */, D03766FF19EDA60000A782A9 /* RACStreamExamples.m in Sources */, D03766CB19EDA60000A782A9 /* NSObjectRACSelectorSignalSpec.m in Sources */, D03766E119EDA60000A782A9 /* RACControlCommandExamples.m in Sources */, @@ -2775,7 +2568,6 @@ D0A226111A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift in Sources */, D03766D919EDA60000A782A9 /* RACChannelExamples.m in Sources */, D03766F519EDA60000A782A9 /* RACSequenceExamples.m in Sources */, - D8024DB21B2E1BB0005E6B9A /* SignalProducerLiftingSpec.swift in Sources */, D03766B919EDA60000A782A9 /* NSControllerRACSupportSpec.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2785,23 +2577,18 @@ buildActionMask = 2147483647; files = ( D037659D19EDA41200A782A9 /* RACKVOChannel.m in Sources */, - D08C54B41A69A2AF00AD8286 /* Signal.swift in Sources */, D037666319EDA41200A782A9 /* UITextView+RACSignalSupport.m in Sources */, D037662F19EDA41200A782A9 /* UIControl+RACSignalSupport.m in Sources */, D03765C919EDA41200A782A9 /* RACScopedDisposable.m in Sources */, D03764FF19EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m in Sources */, D037664719EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.m in Sources */, - D8E84A671B3B32FB00C3E831 /* Optional.swift in Sources */, D03764EB19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m in Sources */, - D0C312D419EF2A5800984962 /* Disposable.swift in Sources */, D03765C119EDA41200A782A9 /* RACScheduler.m in Sources */, D037662B19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.m in Sources */, D037659919EDA41200A782A9 /* RACIndexSetSequence.m in Sources */, D03765D919EDA41200A782A9 /* RACSignal+Operations.m in Sources */, D037661B19EDA41200A782A9 /* UIActionSheet+RACSignalSupport.m in Sources */, D037650319EDA41200A782A9 /* NSFileHandle+RACSupport.m in Sources */, - D08C54B91A69A9D100AD8286 /* SignalProducer.swift in Sources */, - 9ABCB1861D2A5B5A00BCA243 /* Deprecations+Removals.swift in Sources */, D03765E319EDA41200A782A9 /* RACStream.m in Sources */, D037655719EDA41200A782A9 /* RACBehaviorSubject.m in Sources */, D037663B19EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.m in Sources */, @@ -2810,7 +2597,6 @@ D03765ED19EDA41200A782A9 /* RACSubject.m in Sources */, D037664F19EDA41200A782A9 /* UIStepper+RACSignalSupport.m in Sources */, D03765D119EDA41200A782A9 /* RACSerialDisposable.m in Sources */, - EBCC7DBD1BBF01E100A2AE92 /* Observer.swift in Sources */, D037663F19EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.m in Sources */, D037653F19EDA41200A782A9 /* NSString+RACSupport.m in Sources */, D037653719EDA41200A782A9 /* NSString+RACKeyPathUtilities.m in Sources */, @@ -2826,7 +2612,6 @@ D037664319EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.m in Sources */, D037651B19EDA41200A782A9 /* NSObject+RACDescription.m in Sources */, D03765A519EDA41200A782A9 /* RACMulticastConnection.m in Sources */, - D85C652B1C0E70E3005A77AD /* Flatten.swift in Sources */, D037654F19EDA41200A782A9 /* RACArraySequence.m in Sources */, D037652319EDA41200A782A9 /* NSObject+RACLifting.m in Sources */, C7142DBD1CDEA194009F402D /* CocoaAction.swift in Sources */, @@ -2839,14 +2624,11 @@ D0C312E019EF2A5800984962 /* ObjectiveCBridging.swift in Sources */, D037654B19EDA41200A782A9 /* NSUserDefaults+RACSupport.m in Sources */, D037660F19EDA41200A782A9 /* RACUnarySequence.m in Sources */, - D08C54BB1A69C54400AD8286 /* Property.swift in Sources */, D03765FF19EDA41200A782A9 /* RACTargetQueueScheduler.m in Sources */, D03765DF19EDA41200A782A9 /* RACSignalSequence.m in Sources */, D037656D19EDA41200A782A9 /* RACDelegateProxy.m in Sources */, - D03B4A3E19F4C39A009E02AC /* FoundationExtensions.swift in Sources */, D037657519EDA41200A782A9 /* RACDynamicSequence.m in Sources */, D037657119EDA41200A782A9 /* RACDisposable.m in Sources */, - D000040A1A46864E000E7D41 /* TupleExtensions.swift in Sources */, D03765DB19EDA41200A782A9 /* RACSignalProvider.d in Sources */, D037653319EDA41200A782A9 /* NSSet+RACSequenceAdditions.m in Sources */, D037665319EDA41200A782A9 /* UISwitch+RACSignalSupport.m in Sources */, @@ -2861,13 +2643,9 @@ D037651719EDA41200A782A9 /* NSObject+RACDeallocating.m in Sources */, D037658519EDA41200A782A9 /* RACEmptySignal.m in Sources */, D037663719EDA41200A782A9 /* UIDatePicker+RACSignalSupport.m in Sources */, - D08C54B71A69A3DB00AD8286 /* Event.swift in Sources */, D037654719EDA41200A782A9 /* NSURLConnection+RACSupport.m in Sources */, D03765F119EDA41200A782A9 /* RACSubscriber.m in Sources */, D03764F719EDA41200A782A9 /* NSData+RACSupport.m in Sources */, - C79B647D1CD52E4A003F2376 /* EventLogger.swift in Sources */, - D0C312CE19EF2A5800984962 /* Atomic.swift in Sources */, - D0C312E819EF2A5800984962 /* Scheduler.swift in Sources */, D037656319EDA41200A782A9 /* RACCommand.m in Sources */, D037658919EDA41200A782A9 /* RACErrorSignal.m in Sources */, D03765F719EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m in Sources */, @@ -2875,8 +2653,6 @@ D037662319EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.m in Sources */, D03765A119EDA41200A782A9 /* RACKVOTrampoline.m in Sources */, D037665B19EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.m in Sources */, - D0C312D019EF2A5800984962 /* Bag.swift in Sources */, - D0D11ABA1A6AE87700C1F8B1 /* Action.swift in Sources */, D037650B19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m in Sources */, D037660719EDA41200A782A9 /* RACTuple.m in Sources */, D037667019EDA57100A782A9 /* EXTRuntimeExtensions.m in Sources */, @@ -2902,24 +2678,19 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D0A2260C1A72E6C500D33B74 /* SignalProducerSpec.swift in Sources */, D03766C819EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.m in Sources */, D037672419EDA60000A782A9 /* UIImagePickerControllerRACSupportSpec.m in Sources */, D03766E419EDA60000A782A9 /* RACDelegateProxySpec.m in Sources */, - D0A2260F1A72F16D00D33B74 /* PropertySpec.swift in Sources */, + D0A2260F1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */, D03766FA19EDA60000A782A9 /* RACSerialDisposableSpec.m in Sources */, - D0A226091A72E0E900D33B74 /* SignalSpec.swift in Sources */, - CDCD247B1C277EED00710AEE /* AtomicSpec.swift in Sources */, D037670C19EDA60000A782A9 /* RACTargetQueueSchedulerSpec.m in Sources */, D03766DE19EDA60000A782A9 /* RACCommandSpec.m in Sources */, D037670A19EDA60000A782A9 /* RACSubscriptingAssignmentTrampolineSpec.m in Sources */, D03766EC19EDA60000A782A9 /* RACKVOWrapperSpec.m in Sources */, - D021671E1A6CD50500987861 /* ActionSpec.swift in Sources */, D03766E819EDA60000A782A9 /* RACEventSpec.m in Sources */, D03766F819EDA60000A782A9 /* RACSequenceSpec.m in Sources */, D0A226121A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift in Sources */, D037671E19EDA60000A782A9 /* UIBarButtonItemRACSupportSpec.m in Sources */, - D8024DB31B2E1BB0005E6B9A /* SignalProducerLiftingSpec.swift in Sources */, BFA6B94E1A7604D500C846D1 /* SignalProducerNimbleMatchers.swift in Sources */, D03766CA19EDA60000A782A9 /* NSObjectRACPropertySubscribingSpec.m in Sources */, D0C3132319EF2D9700984962 /* RACTestSchedulerSpec.m in Sources */, @@ -2934,15 +2705,10 @@ D03766C619EDA60000A782A9 /* NSObjectRACLiftingSpec.m in Sources */, B696FB821A7640C00075236D /* TestError.swift in Sources */, D0C3131F19EF2D9700984962 /* RACTestExampleScheduler.m in Sources */, - D8170FC21B100EBC004192AD /* FoundationExtensionsSpec.swift in Sources */, D03766D219EDA60000A782A9 /* NSURLConnectionRACSupportSpec.m in Sources */, D03766F419EDA60000A782A9 /* RACSequenceAdditionsSpec.m in Sources */, - D0C3131419EF2B2000984962 /* SchedulerSpec.swift in Sources */, - C79B64751CD38B2B003F2376 /* TestLogger.swift in Sources */, - D0C3131219EF2B2000984962 /* DisposableSpec.swift in Sources */, D03766EE19EDA60000A782A9 /* RACMulticastConnectionSpec.m in Sources */, D03766EA19EDA60000A782A9 /* RACKVOChannelSpec.m in Sources */, - CA6F28511C52626B001879D2 /* FlattenSpec.swift in Sources */, D0C3132119EF2D9700984962 /* RACTestObject.m in Sources */, D03766FC19EDA60000A782A9 /* RACSignalSpec.m in Sources */, D037670819EDA60000A782A9 /* RACSubscriberSpec.m in Sources */, @@ -2961,13 +2727,10 @@ D03766E619EDA60000A782A9 /* RACDisposableSpec.m in Sources */, D03766D419EDA60000A782A9 /* NSUserDefaultsRACSupportSpec.m in Sources */, D03766DC19EDA60000A782A9 /* RACChannelSpec.m in Sources */, - 579504341BB8A34300A5E482 /* BagSpec.swift in Sources */, D037671A19EDA60000A782A9 /* UIActionSheetRACSupportSpec.m in Sources */, D03766DA19EDA60000A782A9 /* RACChannelExamples.m in Sources */, D03766F619EDA60000A782A9 /* RACSequenceExamples.m in Sources */, CD8401841CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, - 4A0E11051D2A95200065D310 /* LifetimeSpec.swift in Sources */, - 02D2602A1C1D6DAF003ACC61 /* SignalLifetimeSpec.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-Mac.xcscheme b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-Mac.xcscheme index 25ac5b0747..e26a5aa441 100644 --- a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-Mac.xcscheme +++ b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-Mac.xcscheme @@ -20,6 +20,20 @@ ReferencedContainer = "container:Carthage/Checkouts/Result/Result.xcodeproj"> + + + + + + + + + + + + + + diff --git a/ReactiveCocoa/Swift/Action.swift b/ReactiveCocoa/Swift/Action.swift deleted file mode 100644 index a8611ace3a..0000000000 --- a/ReactiveCocoa/Swift/Action.swift +++ /dev/null @@ -1,199 +0,0 @@ -import Foundation -import enum Result.NoError - -/// Represents an action that will do some work when executed with a value of -/// type `Input`, then return zero or more values of type `Output` and/or fail -/// with an error of type `Error`. If no failure should be possible, NoError can -/// be specified for the `Error` parameter. -/// -/// Actions enforce serial execution. Any attempt to execute an action multiple -/// times concurrently will return an error. -public final class Action { - private let executeClosure: (Input) -> SignalProducer - private let eventsObserver: Signal, NoError>.Observer - - /// A signal of all events generated from applications of the Action. - /// - /// In other words, this will send every `Event` from every signal generated - /// by each SignalProducer returned from apply(). - public let events: Signal, NoError> - - /// A signal of all values generated from applications of the Action. - /// - /// In other words, this will send every value from every signal generated - /// by each SignalProducer returned from apply(). - public let values: Signal - - /// A signal of all errors generated from applications of the Action. - /// - /// In other words, this will send errors from every signal generated by - /// each SignalProducer returned from apply(). - public let errors: Signal - - /// Whether the action is currently executing. - public var isExecuting: Property { - return Property(_isExecuting) - } - - private let _isExecuting: MutableProperty = MutableProperty(false) - - /// Whether the action is currently enabled. - public var isEnabled: Property { - return Property(_isEnabled) - } - - private let _isEnabled: MutableProperty = MutableProperty(false) - - /// Whether the instantiator of this action wants it to be enabled. - private let isUserEnabled: Property - - /// This queue is used for read-modify-write operations on the `_executing` - /// property. - private let executingQueue = DispatchQueue( - label: "org.reactivecocoa.ReactiveCocoa.Action.executingQueue", - attributes: [] - ) - - /// Whether the action should be enabled for the given combination of user - /// enabledness and executing status. - private static func shouldBeEnabled(userEnabled: Bool, executing: Bool) -> Bool { - return userEnabled && !executing - } - - /// Initializes an action that will be conditionally enabled, and creates a - /// SignalProducer for each input. - /// - /// - parameters: - /// - enabledIf: Boolean property that shows whether the action is - /// enabled. - /// - execute: A closure that returns the signal producer returned by - /// calling `apply(Input)` on the action. - public init(enabledIf property: P, _ execute: (Input) -> SignalProducer) { - executeClosure = execute - isUserEnabled = Property(property) - - (events, eventsObserver) = Signal, NoError>.pipe() - - values = events.map { $0.value }.skipNil() - errors = events.map { $0.error }.skipNil() - - _isEnabled <~ property.producer - .combineLatest(with: isExecuting.producer) - .map(Action.shouldBeEnabled) - } - - /// Initializes an action that will be enabled by default, and creates a - /// SignalProducer for each input. - /// - /// - parameters: - /// - execute: A closure that returns the signal producer returned by - /// calling `apply(Input)` on the action. - public convenience init(_ execute: (Input) -> SignalProducer) { - self.init(enabledIf: Property(value: true), execute) - } - - deinit { - eventsObserver.sendCompleted() - } - - /// Creates a SignalProducer that, when started, will execute the action - /// with the given input, then forward the results upon the produced Signal. - /// - /// - note: If the action is disabled when the returned SignalProducer is - /// started, the produced signal will send `ActionError.NotEnabled`, - /// and nothing will be sent upon `values` or `errors` for that - /// particular signal. - /// - /// - parameters: - /// - input: A value that will be passed to the closure creating the signal - /// producer. - public func apply(_ input: Input) -> SignalProducer> { - return SignalProducer { observer, disposable in - var startedExecuting = false - - self.executingQueue.sync { - if self._isEnabled.value { - self._isExecuting.value = true - startedExecuting = true - } - } - - if !startedExecuting { - observer.sendFailed(.disabled) - return - } - - self.executeClosure(input).startWithSignal { signal, signalDisposable in - disposable += signalDisposable - - signal.observe { event in - observer.action(event.mapError(ActionError.producerFailed)) - self.eventsObserver.sendNext(event) - } - } - - disposable += { - self._isExecuting.value = false - } - } - } -} - -public protocol ActionProtocol { - /// The type of argument to apply the action to. - associatedtype Input - /// The type of values returned by the action. - associatedtype Output - /// The type of error when the action fails. If errors aren't possible then - /// `NoError` can be used. - associatedtype Error: Swift.Error - - /// Whether the action is currently enabled. - var isEnabled: Property { get } - - /// Extracts an action from the receiver. - var action: Action { get } - - /// Creates a SignalProducer that, when started, will execute the action - /// with the given input, then forward the results upon the produced Signal. - /// - /// - note: If the action is disabled when the returned SignalProducer is - /// started, the produced signal will send `ActionError.NotEnabled`, - /// and nothing will be sent upon `values` or `errors` for that - /// particular signal. - /// - /// - parameters: - /// - input: A value that will be passed to the closure creating the signal - /// producer. - func apply(_ input: Input) -> SignalProducer> -} - -extension Action: ActionProtocol { - public var action: Action { - return self - } -} - -/// The type of error that can occur from Action.apply, where `Error` is the -/// type of error that can be generated by the specific Action instance. -public enum ActionError: Swift.Error { - /// The producer returned from apply() was started while the Action was - /// disabled. - case disabled - - /// The producer returned from apply() sent the given error. - case producerFailed(Error) -} - -public func == (lhs: ActionError, rhs: ActionError) -> Bool { - switch (lhs, rhs) { - case (.disabled, .disabled): - return true - - case let (.producerFailed(left), .producerFailed(right)): - return left == right - - default: - return false - } -} diff --git a/ReactiveCocoa/Swift/Atomic.swift b/ReactiveCocoa/Swift/Atomic.swift deleted file mode 100644 index c1a9bb0a73..0000000000 --- a/ReactiveCocoa/Swift/Atomic.swift +++ /dev/null @@ -1,169 +0,0 @@ -// -// Atomic.swift -// ReactiveCocoa -// -// Created by Justin Spahr-Summers on 2014-06-10. -// Copyright (c) 2014 GitHub. All rights reserved. -// - -import Foundation - -final class PosixThreadMutex: NSLocking { - private var mutex = pthread_mutex_t() - - init() { - let result = pthread_mutex_init(&mutex, nil) - precondition(result == 0, "Failed to initialize mutex with error \(result).") - } - - deinit { - let result = pthread_mutex_destroy(&mutex) - precondition(result == 0, "Failed to destroy mutex with error \(result).") - } - - func lock() { - let result = pthread_mutex_lock(&mutex) - precondition(result == 0, "Failed to lock \(self) with error \(result).") - } - - func unlock() { - let result = pthread_mutex_unlock(&mutex) - precondition(result == 0, "Failed to unlock \(self) with error \(result).") - } -} - -/// An atomic variable. -public final class Atomic: AtomicProtocol { - private let lock: PosixThreadMutex - private var _value: Value - - /// Initialize the variable with the given initial value. - /// - /// - parameters: - /// - value: Initial value for `self`. - public init(_ value: Value) { - _value = value - lock = PosixThreadMutex() - } - - /// Atomically modifies the variable. - /// - /// - parameters: - /// - action: A closure that takes the current value. - /// - /// - returns: The result of the action. - @discardableResult - public func modify(_ action: @noescape (inout Value) throws -> Result) rethrows -> Result { - lock.lock() - defer { lock.unlock() } - - return try action(&_value) - } - - /// Atomically perform an arbitrary action using the current value of the - /// variable. - /// - /// - parameters: - /// - action: A closure that takes the current value. - /// - /// - returns: The result of the action. - @discardableResult - public func withValue(_ action: @noescape (Value) throws -> Result) rethrows -> Result { - lock.lock() - defer { lock.unlock() } - - return try action(_value) - } -} - - -/// An atomic variable which uses a recursive lock. -internal final class RecursiveAtomic: AtomicProtocol { - private let lock: NSRecursiveLock - private var _value: Value - private let didSetObserver: ((Value) -> Void)? - - /// Initialize the variable with the given initial value. - /// - /// - parameters: - /// - value: Initial value for `self`. - /// - name: An optional name used to create the recursive lock. - /// - action: An optional closure which would be invoked every time the - /// value of `self` is mutated. - internal init(_ value: Value, name: StaticString? = nil, didSet action: ((Value) -> Void)? = nil) { - _value = value - lock = NSRecursiveLock() - lock.name = name.map(String.init(_:)) - didSetObserver = action - } - - /// Atomically modifies the variable. - /// - /// - parameters: - /// - action: A closure that takes the current value. - /// - /// - returns: The result of the action. - @discardableResult - func modify(_ action: @noescape (inout Value) throws -> Result) rethrows -> Result { - lock.lock() - defer { - didSetObserver?(_value) - lock.unlock() - } - - return try action(&_value) - } - - /// Atomically perform an arbitrary action using the current value of the - /// variable. - /// - /// - parameters: - /// - action: A closure that takes the current value. - /// - /// - returns: The result of the action. - @discardableResult - func withValue(_ action: @noescape (Value) throws -> Result) rethrows -> Result { - lock.lock() - defer { lock.unlock() } - - return try action(_value) - } -} - -public protocol AtomicProtocol: class { - associatedtype Value - - @discardableResult - func withValue(_ action: @noescape (Value) throws -> Result) rethrows -> Result - - @discardableResult - func modify(_ action: @noescape (inout Value) throws -> Result) rethrows -> Result -} - -extension AtomicProtocol { - /// Atomically get or set the value of the variable. - public var value: Value { - get { - return withValue { $0 } - } - - set(newValue) { - swap(newValue) - } - } - - /// Atomically replace the contents of the variable. - /// - /// - parameters: - /// - newValue: A new value for the variable. - /// - /// - returns: The old value. - @discardableResult - public func swap(_ newValue: Value) -> Value { - return modify { (value: inout Value) in - let oldValue = value - value = newValue - return oldValue - } - } -} diff --git a/ReactiveCocoa/Swift/Bag.swift b/ReactiveCocoa/Swift/Bag.swift deleted file mode 100644 index a4b10c97d0..0000000000 --- a/ReactiveCocoa/Swift/Bag.swift +++ /dev/null @@ -1,110 +0,0 @@ -// -// Bag.swift -// ReactiveCocoa -// -// Created by Justin Spahr-Summers on 2014-07-10. -// Copyright (c) 2014 GitHub. All rights reserved. -// - -/// A uniquely identifying token for removing a value that was inserted into a -/// Bag. -public final class RemovalToken { - private var identifier: UInt? - - private init(identifier: UInt) { - self.identifier = identifier - } -} - -/// An unordered, non-unique collection of values of type `Element`. -public struct Bag { - private var elements: [BagElement] = [] - private var currentIdentifier: UInt = 0 - - public init() { - } - - /// Insert the given value into `self`, and return a token that can - /// later be passed to `removeValueForToken()`. - /// - /// - parameters: - /// - value: A value that will be inserted. - @discardableResult - public mutating func insert(_ value: Element) -> RemovalToken { - let (nextIdentifier, overflow) = UInt.addWithOverflow(currentIdentifier, 1) - if overflow { - reindex() - } - - let token = RemovalToken(identifier: currentIdentifier) - let element = BagElement(value: value, identifier: currentIdentifier, token: token) - - elements.append(element) - currentIdentifier = nextIdentifier - - return token - } - - /// Remove a value, given the token returned from `insert()`. - /// - /// - note: If the value has already been removed, nothing happens. - /// - /// - parameters: - /// - token: A token returned from a call to `insert()`. - public mutating func remove(using token: RemovalToken) { - if let identifier = token.identifier { - // Removal is more likely for recent objects than old ones. - for i in elements.indices.reversed() { - if elements[i].identifier == identifier { - elements.remove(at: i) - token.identifier = nil - break - } - } - } - } - - /// In the event of an identifier overflow (highly, highly unlikely), reset - /// all current identifiers to reclaim a contiguous set of available - /// identifiers for the future. - private mutating func reindex() { - for i in elements.indices { - currentIdentifier = UInt(i) - - elements[i].identifier = currentIdentifier - elements[i].token.identifier = currentIdentifier - } - } -} - -extension Bag: Collection { - public typealias Index = Array.Index - - public var startIndex: Index { - return elements.startIndex - } - - public var endIndex: Index { - return elements.endIndex - } - - public subscript(index: Index) -> Element { - return elements[index].value - } - - public func index(after i: Index) -> Index { - return i + 1 - } -} - -private struct BagElement { - let value: Value - var identifier: UInt - let token: RemovalToken -} - -extension BagElement: CustomStringConvertible { - var description: String { - return "BagElement(\(value))" - } -} diff --git a/ReactiveCocoa/Swift/CocoaAction.swift b/ReactiveCocoa/Swift/CocoaAction.swift index 44d80d6e0d..8d54a422e8 100644 --- a/ReactiveCocoa/Swift/CocoaAction.swift +++ b/ReactiveCocoa/Swift/CocoaAction.swift @@ -1,4 +1,5 @@ import Foundation +import ReactiveSwift /// Wraps an Action for use by a GUI control (such as `NSControl` or /// `UIControl`), with KVO, or with Cocoa Bindings. diff --git a/ReactiveCocoa/Swift/Deprecations+Removals.swift b/ReactiveCocoa/Swift/Deprecations+Removals.swift deleted file mode 100644 index 13c460e809..0000000000 --- a/ReactiveCocoa/Swift/Deprecations+Removals.swift +++ /dev/null @@ -1,263 +0,0 @@ -import Foundation -import enum Result.NoError - -// MARK: Removed Types and APIs in ReactiveCocoa 5.0. - -// Renamed Protocols -@available(*, unavailable, renamed:"ActionProtocol") -public enum ActionType {} - -@available(*, unavailable, renamed:"SignalProtocol") -public enum SignalType {} - -@available(*, unavailable, renamed:"SignalProducerProtocol") -public enum SignalProducerType {} - -@available(*, unavailable, renamed:"PropertyProtocol") -public enum PropertyType {} - -@available(*, unavailable, renamed:"MutablePropertyProtocol") -public enum MutablePropertyType {} - -@available(*, unavailable, renamed:"ObserverProtocol") -public enum ObserverType {} - -@available(*, unavailable, renamed:"SchedulerProtocol") -public enum SchedulerType {} - -@available(*, unavailable, renamed:"DateSchedulerProtocol") -public enum DateSchedulerType {} - -@available(*, unavailable, renamed:"OptionalProtocol") -public enum OptionalType {} - -@available(*, unavailable, renamed:"EventLoggerProtocol") -public enum EventLoggerType {} - -@available(*, unavailable, renamed:"EventProtocol") -public enum EventType {} - -// Renamed and Removed Types - -@available(*, unavailable, renamed:"Property") -public struct AnyProperty {} - -@available(*, unavailable, message:"Use 'Property(value:)' to create a constant property instead. 'ConstantProperty' is removed in RAC 5.0.") -public struct ConstantProperty {} - -// Renamed Properties - -extension Disposable { - @available(*, unavailable, renamed:"isDisposed") - public var disposed: Bool { fatalError() } -} - -extension ActionProtocol { - @available(*, unavailable, renamed:"isEnabled") - public var enabled: Bool { fatalError() } - - @available(*, unavailable, renamed:"isExecuting") - public var executing: Bool { fatalError() } -} - -extension CocoaAction { - @available(*, unavailable, renamed:"isEnabled") - @nonobjc public var enabled: Bool { fatalError() } - - @available(*, unavailable, renamed:"isExecuting") - @nonobjc public var executing: Bool { fatalError() } -} - -// Renamed Enum cases - -extension Event { - @available(*, unavailable, renamed:"next") - public static var Next: Event { fatalError() } - - @available(*, unavailable, renamed:"failed") - public static var Failed: Event { fatalError() } - - @available(*, unavailable, renamed:"completed") - public static var Completed: Event { fatalError() } - - @available(*, unavailable, renamed:"interrupted") - public static var Interrupted: Event { fatalError() } -} - -extension ActionError { - @available(*, unavailable, renamed:"producerFailed") - public static var ProducerError: ActionError { fatalError() } - - @available(*, unavailable, renamed:"disabled") - public static var NotEnabled: ActionError { fatalError() } -} - -extension FlattenStrategy { - @available(*, unavailable, renamed:"latest") - public static var Latest: FlattenStrategy { fatalError() } - - @available(*, unavailable, renamed:"concat") - public static var Concat: FlattenStrategy { fatalError() } - - @available(*, unavailable, renamed:"merge") - public static var Merge: FlattenStrategy { fatalError() } -} - -// Methods - -extension Bag { - @available(*, unavailable, renamed:"remove(using:)") - public func removeValueForToken(_ token: RemovalToken) { fatalError() } -} - -extension CompositeDisposable { - @available(*, unavailable, renamed:"add(_:)") - public func addDisposable(_ d: Disposable) -> DisposableHandle { fatalError() } -} - -extension SignalProtocol { - @available(*, unavailable, renamed:"take(first:)") - public func take(_ count: Int) -> Signal { fatalError() } - - @available(*, unavailable, renamed:"take(last:)") - public func takeLast(_ count: Int) -> Signal { fatalError() } - - @available(*, unavailable, renamed:"skip(first:)") - public func skip(_ count: Int) -> Signal { fatalError() } - - @available(*, unavailable, renamed:"observe(on:)") - public func observeOn(_ scheduler: UIScheduler) -> Signal { fatalError() } - - @available(*, unavailable, renamed:"combineLatest(with:)") - public func combineLatestWith(_ otherSignal: S) -> Signal<(Value, S.Value), Error> { fatalError() } - - @available(*, unavailable, renamed:"zip(with:)") - public func zipWith(_ otherSignal: S) -> Signal<(Value, S.Value), Error> { fatalError() } - - @available(*, unavailable, renamed:"take(until:)") - public func takeUntil(_ trigger: Signal<(), NoError>) -> Signal { fatalError() } - - @available(*, unavailable, renamed:"take(untilReplacement:)") - public func takeUntilReplacement(_ replacement: Signal) -> Signal { fatalError() } - - @available(*, unavailable, renamed:"skip(until:)") - public func skipUntil(_ trigger: Signal<(), NoError>) -> Signal { fatalError() } - - @available(*, unavailable, renamed:"skip(while:)") - public func skipWhile(_ predicate: (Value) -> Bool) -> Signal { fatalError() } - - @available(*, unavailable, renamed:"take(while:)") - public func takeWhile(_ predicate: (Value) -> Bool) -> Signal { fatalError() } - - @available(*, unavailable, renamed:"timeout(after:raising:on:)") - public func timeoutWithError(_ error: Error, afterInterval: TimeInterval, onScheduler: SchedulerProtocol) -> Signal { fatalError() } - - @available(*, unavailable, message: "This Signal may emit errors which must be handled explicitly, or observed using `observeResult(_:)`") - public func observeNext(_ next: (Value) -> Void) -> Disposable? { fatalError() } -} - -extension SignalProtocol where Value: OptionalProtocol { - @available(*, unavailable, renamed:"skipNil()") - public func ignoreNil() -> SignalProducer { fatalError() } -} - -extension SignalProducerProtocol { - @available(*, unavailable, renamed:"take(first:)") - public func take(_ count: Int) -> SignalProducer { fatalError() } - - @available(*, unavailable, renamed:"take(last:)") - public func takeLast(_ count: Int) -> SignalProducer { fatalError() } - - @available(*, unavailable, renamed:"skip(first:)") - public func skip(_ count: Int) -> SignalProducer { fatalError() } - - @available(*, unavailable, renamed:"observe(on:)") - public func observeOn(_ scheduler: UIScheduler) -> SignalProducer { fatalError() } - - @available(*, unavailable, renamed:"start(on:)") - public func startOn(_ scheduler: UIScheduler) -> SignalProducer { fatalError() } - - @available(*, unavailable, renamed:"combineLatest(with:)") - public func combineLatestWith(_ otherProducer: SignalProducer) -> SignalProducer<(Value, U), Error> { fatalError() } - - @available(*, unavailable, renamed:"combineLatest(with:)") - public func combineLatestWith(_ otherSignal: Signal) -> SignalProducer<(Value, U), Error> { fatalError() } - - @available(*, unavailable, renamed:"zip(with:)") - public func zipWith(_ otherProducer: SignalProducer) -> SignalProducer<(Value, U), Error> { fatalError() } - - @available(*, unavailable, renamed:"zip(with:)") - public func zipWith(_ otherSignal: Signal) -> SignalProducer<(Value, U), Error> { fatalError() } - - @available(*, unavailable, renamed:"take(until:)") - public func takeUntil(_ trigger: Signal<(), NoError>) -> SignalProducer { fatalError() } - - @available(*, unavailable, renamed:"take(until:)") - public func takeUntil(_ trigger: SignalProducer<(), NoError>) -> SignalProducer { fatalError() } - - @available(*, unavailable, renamed:"take(untilReplacement:)") - public func takeUntilReplacement(_ replacement: Signal) -> SignalProducer { fatalError() } - - @available(*, unavailable, renamed:"take(untilReplacement:)") - public func takeUntilReplacement(_ replacement: SignalProducer) -> SignalProducer { fatalError() } - - @available(*, unavailable, renamed:"skip(until:)") - public func skipUntil(_ trigger: Signal<(), NoError>) -> SignalProducer { fatalError() } - - @available(*, unavailable, renamed:"skip(until:)") - public func skipUntil(_ trigger: SignalProducer<(), NoError>) -> SignalProducer { fatalError() } - - @available(*, unavailable, renamed:"skip(while:)") - public func skipWhile(_ predicate: (Value) -> Bool) -> SignalProducer { fatalError() } - - @available(*, unavailable, renamed:"take(while:)") - public func takeWhile(_ predicate: (Value) -> Bool) -> SignalProducer { fatalError() } - - @available(*, unavailable, renamed:"timeout(after:raising:on:)") - public func timeoutWithError(_ error: Error, afterInterval: TimeInterval, onScheduler: SchedulerProtocol) -> SignalProducer { fatalError() } - - @available(*, unavailable, message:"This SignalProducer may emit errors which must be handled explicitly, or observed using `startWithResult(_:)`.") - public func startWithNext(_ next: (Value) -> Void) -> Disposable { fatalError() } -} - -extension SignalProducerProtocol where Value: OptionalProtocol { - @available(*, unavailable, renamed:"skipNil()") - public func ignoreNil() -> SignalProducer { fatalError() } -} - -extension SignalProducer { - @available(*, unavailable, message:"Use properties instead. `buffer(_:)` is removed in RAC 5.0.") - public static func buffer(_ capacity: Int) -> (SignalProducer, Signal.Observer) { fatalError() } -} - -extension PropertyProtocol { - @available(*, unavailable, renamed:"combineLatest(with:)") - public func combineLatestWith(_ otherProperty: P) -> Property<(Value, P.Value)> { fatalError() } - - @available(*, unavailable, renamed:"zip(with:)") - public func zipWith(_ otherProperty: P) -> Property<(Value, P.Value)> { fatalError() } -} - -extension Property { - @available(*, unavailable, renamed:"AnyProperty(initial:then:)") - public convenience init(initialValue: Value, producer: SignalProducer) { fatalError() } - - @available(*, unavailable, renamed:"AnyProperty(initial:then:)") - public convenience init(initialValue: Value, signal: Signal) { fatalError() } -} - -extension DateSchedulerProtocol { - @available(*, unavailable, renamed:"schedule(after:action:)") - func scheduleAfter(date: Date, _ action: () -> Void) -> Disposable? { fatalError() } - - @available(*, unavailable, renamed:"schedule(after:interval:leeway:)") - func scheduleAfter(date: Date, repeatingEvery: TimeInterval, withLeeway: TimeInterval, action: () -> Void) -> Disposable? { fatalError() } -} - -extension TestScheduler { - @available(*, unavailable, renamed:"advanced(by:)") - public func advanceByInterval(_ interval: TimeInterval) { fatalError() } - - @available(*, unavailable, renamed:"advanced(to:)") - public func advanceToDate(_ date: Date) { fatalError() } -} diff --git a/ReactiveCocoa/Swift/Disposable.swift b/ReactiveCocoa/Swift/Disposable.swift deleted file mode 100644 index 757b80a737..0000000000 --- a/ReactiveCocoa/Swift/Disposable.swift +++ /dev/null @@ -1,353 +0,0 @@ -// -// Disposable.swift -// ReactiveCocoa -// -// Created by Justin Spahr-Summers on 2014-06-02. -// Copyright (c) 2014 GitHub. All rights reserved. -// - -/// Represents something that can be “disposed”, usually associated with freeing -/// resources or canceling work. -public protocol Disposable: class { - /// Whether this disposable has been disposed already. - var isDisposed: Bool { get } - - /// Method for disposing of resources when appropriate. - func dispose() -} - -/// A type-erased disposable that forwards operations to an underlying disposable. -public final class AnyDisposable: Disposable { - private let disposable: Disposable - - public var isDisposed: Bool { - return disposable.isDisposed - } - - public init(_ disposable: Disposable) { - self.disposable = disposable - } - - public func dispose() { - disposable.dispose() - } -} - -/// A disposable that only flips `isDisposed` upon disposal, and performs no other -/// work. -public final class SimpleDisposable: Disposable { - private let _isDisposed = Atomic(false) - - public var isDisposed: Bool { - return _isDisposed.value - } - - public init() {} - - public func dispose() { - _isDisposed.value = true - } -} - -/// A disposable that will run an action upon disposal. -public final class ActionDisposable: Disposable { - private let action: Atomic<(() -> Void)?> - - public var isDisposed: Bool { - return action.value == nil - } - - /// Initialize the disposable to run the given action upon disposal. - /// - /// - parameters: - /// - action: A closure to run when calling `dispose()`. - public init(action: () -> Void) { - self.action = Atomic(action) - } - - public func dispose() { - let oldAction = action.swap(nil) - oldAction?() - } -} - -/// A disposable that will dispose of any number of other disposables. -public final class CompositeDisposable: Disposable { - private let disposables: Atomic?> - - /// Represents a handle to a disposable previously added to a - /// CompositeDisposable. - public final class DisposableHandle { - private let bagToken: Atomic - private weak var disposable: CompositeDisposable? - - private static let empty = DisposableHandle() - - private init() { - self.bagToken = Atomic(nil) - } - - private init(bagToken: RemovalToken, disposable: CompositeDisposable) { - self.bagToken = Atomic(bagToken) - self.disposable = disposable - } - - /// Remove the pointed-to disposable from its `CompositeDisposable`. - /// - /// - note: This is useful to minimize memory growth, by removing - /// disposables that are no longer needed. - public func remove() { - if let token = bagToken.swap(nil) { - _ = disposable?.disposables.modify { - $0?.remove(using: token) - } - } - } - } - - public var isDisposed: Bool { - return disposables.value == nil - } - - /// Initialize a `CompositeDisposable` containing the given sequence of - /// disposables. - /// - /// - parameters: - /// - disposables: A collection of objects conforming to the `Disposable` - /// protocol - public init(_ disposables: S) { - var bag: Bag = Bag() - - for disposable in disposables { - bag.insert(disposable) - } - - self.disposables = Atomic(bag) - } - - /// Initialize a `CompositeDisposable` containing the given sequence of - /// disposables. - /// - /// - parameters: - /// - disposables: A collection of objects conforming to the `Disposable` - /// protocol - public convenience init(_ disposables: S) { - self.init(disposables.flatMap { $0 }) - } - - /// Initializes an empty `CompositeDisposable`. - public convenience init() { - self.init([Disposable]()) - } - - public func dispose() { - if let ds = disposables.swap(nil) { - for d in ds.reversed() { - d.dispose() - } - } - } - - /// Add the given disposable to the list, then return a handle which can - /// be used to opaquely remove the disposable later (if desired). - /// - /// - parameters: - /// - d: Optional disposable. - /// - /// - returns: An instance of `DisposableHandle` that can be used to - /// opaquely remove the disposable later (if desired). - @discardableResult - public func add(_ d: Disposable?) -> DisposableHandle { - guard let d = d else { - return DisposableHandle.empty - } - - let handle: DisposableHandle? = disposables.modify { - return ($0?.insert(d)).map { DisposableHandle(bagToken: $0, disposable: self) } - } - - if let handle = handle { - return handle - } else { - d.dispose() - return DisposableHandle.empty - } - } - - /// Add an ActionDisposable to the list. - /// - /// - parameters: - /// - action: A closure that will be invoked when `dispose()` is called. - /// - /// - returns: An instance of `DisposableHandle` that can be used to - /// opaquely remove the disposable later (if desired). - public func add(_ action: () -> Void) -> DisposableHandle { - return add(ActionDisposable(action: action)) - } -} - -/// A disposable that, upon deinitialization, will automatically dispose of -/// another disposable. -public final class ScopedDisposable: Disposable { - /// The disposable which will be disposed when the ScopedDisposable - /// deinitializes. - public let innerDisposable: InnerDisposable - - public var isDisposed: Bool { - return innerDisposable.isDisposed - } - - /// Initialize the receiver to dispose of the argument upon - /// deinitialization. - /// - /// - parameters: - /// - disposable: A disposable to dispose of when deinitializing. - public init(_ disposable: InnerDisposable) { - innerDisposable = disposable - } - - deinit { - dispose() - } - - public func dispose() { - innerDisposable.dispose() - } -} - -extension ScopedDisposable where InnerDisposable: AnyDisposable { - /// Initialize the receiver to dispose of the argument upon - /// deinitialization. - /// - /// - parameters: - /// - disposable: A disposable to dispose of when deinitializing, which - /// will be wrapped in an `AnyDisposable`. - public convenience init(_ disposable: Disposable) { - self.init(AnyDisposable(disposable)) - } -} - -/// A disposable that will optionally dispose of another disposable. -public final class SerialDisposable: Disposable { - private struct State { - var innerDisposable: Disposable? = nil - var isDisposed = false - } - - private let state = Atomic(State()) - - public var isDisposed: Bool { - return state.value.isDisposed - } - - /// The inner disposable to dispose of. - /// - /// Whenever this property is set (even to the same value!), the previous - /// disposable is automatically disposed. - public var innerDisposable: Disposable? { - get { - return state.value.innerDisposable - } - - set(d) { - let oldState: State = state.modify { state in - defer { state.innerDisposable = d } - return state - } - - oldState.innerDisposable?.dispose() - if oldState.isDisposed { - d?.dispose() - } - } - } - - /// Initializes the receiver to dispose of the argument when the - /// SerialDisposable is disposed. - /// - /// - parameters: - /// - disposable: Optional disposable. - public init(_ disposable: Disposable? = nil) { - innerDisposable = disposable - } - - public func dispose() { - let orig = state.swap(State(innerDisposable: nil, isDisposed: true)) - orig.innerDisposable?.dispose() - } -} - -/// Adds the right-hand-side disposable to the left-hand-side -/// `CompositeDisposable`. -/// -/// ```` -/// disposable += producer -/// .filter { ... } -/// .map { ... } -/// .start(observer) -/// ```` -/// -/// - parameters: -/// - lhs: Disposable to add to. -/// - rhs: Disposable to add. -/// -/// - returns: An instance of `DisposableHandle` that can be used to opaquely -/// remove the disposable later (if desired). -@discardableResult -public func +=(lhs: CompositeDisposable, rhs: Disposable?) -> CompositeDisposable.DisposableHandle { - return lhs.add(rhs) -} - -/// Adds the right-hand-side `ActionDisposable` to the left-hand-side -/// `CompositeDisposable`. -/// -/// ```` -/// disposable += { ... } -/// ```` -/// -/// - parameters: -/// - lhs: Disposable to add to. -/// - rhs: Closure to add as a disposable. -/// -/// - returns: An instance of `DisposableHandle` that can be used to opaquely -/// remove the disposable later (if desired). -@discardableResult -public func +=(lhs: CompositeDisposable, rhs: () -> ()) -> CompositeDisposable.DisposableHandle { - return lhs.add(rhs) -} - -/// Adds the right-hand-side disposable to the left-hand-side -/// `ScopedDisposable`. -/// -/// ```` -/// disposable += { ... } -/// ```` -/// -/// - parameters: -/// - lhs: Disposable to add to. -/// - rhs: Disposable to add. -/// -/// - returns: An instance of `DisposableHandle` that can be used to opaquely -/// remove the disposable later (if desired). -@discardableResult -public func +=(lhs: ScopedDisposable, rhs: Disposable?) -> CompositeDisposable.DisposableHandle { - return lhs.innerDisposable.add(rhs) -} - -/// Adds the right-hand-side disposable to the left-hand-side -/// `ScopedDisposable`. -/// -/// ```` -/// disposable += { ... } -/// ```` -/// -/// - parameters: -/// - lhs: Disposable to add to. -/// - rhs: Closure to add as a disposable. -/// -/// - returns: An instance of `DisposableHandle` that can be used to opaquely -/// remove the disposable later (if desired). -@discardableResult -public func +=(lhs: ScopedDisposable, rhs: () -> ()) -> CompositeDisposable.DisposableHandle { - return lhs.innerDisposable.add(rhs) -} diff --git a/ReactiveCocoa/Swift/DynamicProperty.swift b/ReactiveCocoa/Swift/DynamicProperty.swift index 857078f69a..309c81c879 100644 --- a/ReactiveCocoa/Swift/DynamicProperty.swift +++ b/ReactiveCocoa/Swift/DynamicProperty.swift @@ -1,4 +1,5 @@ import Foundation +import ReactiveSwift import enum Result.NoError /// Models types that can be represented in Objective-C (i.e., reference diff --git a/ReactiveCocoa/Swift/Event.swift b/ReactiveCocoa/Swift/Event.swift deleted file mode 100644 index 3d6d94f6ba..0000000000 --- a/ReactiveCocoa/Swift/Event.swift +++ /dev/null @@ -1,165 +0,0 @@ -// -// Event.swift -// ReactiveCocoa -// -// Created by Justin Spahr-Summers on 2015-01-16. -// Copyright (c) 2015 GitHub. All rights reserved. -// - -/// Represents a signal event. -/// -/// Signals must conform to the grammar: -/// `Next* (Failed | Completed | Interrupted)?` -public enum Event { - /// A value provided by the signal. - case next(Value) - - /// The signal terminated because of an error. No further events will be - /// received. - case failed(Error) - - /// The signal successfully terminated. No further events will be received. - case completed - - /// Event production on the signal has been interrupted. No further events - /// will be received. - /// - /// - important: This event does not signify the successful or failed - /// completion of the signal. - case interrupted - - /// Whether this event indicates signal termination (i.e., that no further - /// events will be received). - public var isTerminating: Bool { - switch self { - case .next: - return false - - case .failed, .completed, .interrupted: - return true - } - } - - /// Lift the given closure over the event's value. - /// - /// - important: The closure is called only on `next` type events. - /// - /// - parameters: - /// - f: A closure that accepts a value and returns a new value - /// - /// - returns: An event with function applied to a value in case `self` is a - /// `next` type of event. - public func map(_ f: (Value) -> U) -> Event { - switch self { - case let .next(value): - return .next(f(value)) - - case let .failed(error): - return .failed(error) - - case .completed: - return .completed - - case .interrupted: - return .interrupted - } - } - - /// Lift the given closure over the event's error. - /// - /// - important: The closure is called only on failed type event. - /// - /// - parameters: - /// - f: A closure that accepts an error object and returns - /// a new error object - /// - /// - returns: An event with function applied to an error object in case - /// `self` is a `.Failed` type of event. - public func mapError(_ f: (Error) -> F) -> Event { - switch self { - case let .next(value): - return .next(value) - - case let .failed(error): - return .failed(f(error)) - - case .completed: - return .completed - - case .interrupted: - return .interrupted - } - } - - /// Unwrap the contained `next` value. - public var value: Value? { - if case let .next(value) = self { - return value - } else { - return nil - } - } - - /// Unwrap the contained `Error` value. - public var error: Error? { - if case let .failed(error) = self { - return error - } else { - return nil - } - } -} - -public func == (lhs: Event, rhs: Event) -> Bool { - switch (lhs, rhs) { - case let (.next(left), .next(right)): - return left == right - - case let (.failed(left), .failed(right)): - return left == right - - case (.completed, .completed): - return true - - case (.interrupted, .interrupted): - return true - - default: - return false - } -} - -extension Event: CustomStringConvertible { - public var description: String { - switch self { - case let .next(value): - return "NEXT \(value)" - - case let .failed(error): - return "FAILED \(error)" - - case .completed: - return "COMPLETED" - - case .interrupted: - return "INTERRUPTED" - } - } -} - -/// Event protocol for constraining signal extensions -public protocol EventProtocol { - /// The value type of an event. - associatedtype Value - /// The error type of an event. If errors aren't possible then `NoError` can - /// be used. - associatedtype Error: Swift.Error - /// Extracts the event from the receiver. - var event: Event { get } -} - -extension Event: EventProtocol { - public var event: Event { - return self - } -} diff --git a/ReactiveCocoa/Swift/EventLogger.swift b/ReactiveCocoa/Swift/EventLogger.swift deleted file mode 100644 index 4b68c7fe1c..0000000000 --- a/ReactiveCocoa/Swift/EventLogger.swift +++ /dev/null @@ -1,132 +0,0 @@ -// -// EventLogger.swift -// ReactiveCocoa -// -// Created by Rui Peres on 30/04/2016. -// Copyright © 2016 GitHub. All rights reserved. -// - -import Foundation - -/// A namespace for logging event types. -public enum LoggingEvent { - public enum Signal: String { - case next, completed, failed, terminated, disposed, interrupted - - public static let allEvents: Set = [ - .next, .completed, .failed, .terminated, .disposed, .interrupted, - ] - } - - public enum SignalProducer: String { - case started, next, completed, failed, terminated, disposed, interrupted - - public static let allEvents: Set = [ - .started, .next, .completed, .failed, .terminated, .disposed, .interrupted, - ] - } -} - -private func defaultEventLog(identifier: String, event: String, fileName: String, functionName: String, lineNumber: Int) { - print("[\(identifier)] \(event) fileName: \(fileName), functionName: \(functionName), lineNumber: \(lineNumber)") -} - -/// A type that represents an event logging function. -public typealias EventLogger = (identifier: String, event: String, fileName: String, functionName: String, lineNumber: Int) -> Void - -extension SignalProtocol { - /// Logs all events that the receiver sends. By default, it will print to - /// the standard output. - /// - /// - parameters: - /// - identifier: a string to identify the Signal firing events. - /// - events: Types of events to log. - /// - fileName: Name of the file containing the code which fired the - /// event. - /// - functionName: Function where event was fired. - /// - lineNumber: Line number where event was fired. - /// - logger: Logger that logs the events. - /// - /// - returns: Signal that, when observed, logs the fired events. - public func logEvents(identifier: String = "", events: Set = LoggingEvent.Signal.allEvents, fileName: String = #file, functionName: String = #function, lineNumber: Int = #line, logger: EventLogger = defaultEventLog) -> Signal { - func log(_ event: LoggingEvent.Signal) -> ((T) -> Void)? { - return event.logIfNeeded(events: events) { event in - logger(identifier: identifier, event: event, fileName: fileName, functionName: functionName, lineNumber: lineNumber) - } - } - - return self.on( - failed: log(.failed), - completed: log(.completed), - interrupted: log(.interrupted), - terminated: log(.terminated), - disposed: log(.disposed), - next: log(.next) - ) - } -} - -extension SignalProducerProtocol { - /// Logs all events that the receiver sends. By default, it will print to - /// the standard output. - /// - /// - parameters: - /// - identifier: a string to identify the SignalProducer firing events. - /// - events: Types of events to log. - /// - fileName: Name of the file containing the code which fired the - /// event. - /// - functionName: Function where event was fired. - /// - lineNumber: Line number where event was fired. - /// - logger: Logger that logs the events. - /// - /// - returns: Signal producer that, when started, logs the fired events. - public func logEvents(identifier: String = "", - events: Set = LoggingEvent.SignalProducer.allEvents, - fileName: String = #file, - functionName: String = #function, - lineNumber: Int = #line, - logger: EventLogger = defaultEventLog - ) -> SignalProducer { - func log(_ event: LoggingEvent.SignalProducer) -> ((T) -> Void)? { - return event.logIfNeeded(events: events) { event in - logger( - identifier: identifier, - event: event, - fileName: fileName, - functionName: functionName, - lineNumber: lineNumber - ) - } - } - - return self.on( - started: log(.started), - next: log(.next), - failed: log(.failed), - completed: log(.completed), - interrupted: log(.interrupted), - terminated: log(.terminated), - disposed: log(.disposed) - ) - } -} - -private protocol LoggingEventProtocol: Hashable, RawRepresentable {} -extension LoggingEvent.Signal: LoggingEventProtocol {} -extension LoggingEvent.SignalProducer: LoggingEventProtocol {} - -private extension LoggingEventProtocol { - func logIfNeeded(events: Set, logger: (String) -> Void) -> ((T) -> Void)? { - guard events.contains(self) else { - return nil - } - - return { value in - if value is Void { - logger("\(self.rawValue)") - } else { - logger("\(self.rawValue) \(value)") - } - } - } -} diff --git a/ReactiveCocoa/Swift/Flatten.swift b/ReactiveCocoa/Swift/Flatten.swift deleted file mode 100644 index d01296644f..0000000000 --- a/ReactiveCocoa/Swift/Flatten.swift +++ /dev/null @@ -1,918 +0,0 @@ -// -// Flatten.swift -// ReactiveCocoa -// -// Created by Neil Pankey on 11/30/15. -// Copyright © 2015 GitHub. All rights reserved. -// - -import enum Result.NoError - -/// Describes how multiple producers should be joined together. -public enum FlattenStrategy: Equatable { - /// The producers should be merged, so that any value received on any of the - /// input producers will be forwarded immediately to the output producer. - /// - /// The resulting producer will complete only when all inputs have - /// completed. - case merge - - /// The producers should be concatenated, so that their values are sent in - /// the order of the producers themselves. - /// - /// The resulting producer will complete only when all inputs have - /// completed. - case concat - - /// Only the events from the latest input producer should be considered for - /// the output. Any producers received before that point will be disposed - /// of. - /// - /// The resulting producer will complete only when the producer-of-producers - /// and the latest producer has completed. - case latest -} - - -extension SignalProtocol where Value: SignalProducerProtocol, Error == Value.Error { - /// Flattens the inner producers sent upon `signal` (into a single signal of - /// values), according to the semantics of the given strategy. - /// - /// - note: If `signal` or an active inner producer fails, the returned - /// signal will forward that failure immediately. - /// - /// - note: `interrupted` events on inner producers will be treated like - /// `Completed events on inner producers. - public func flatten(_ strategy: FlattenStrategy) -> Signal { - switch strategy { - case .merge: - return self.merge() - - case .concat: - return self.concat() - - case .latest: - return self.switchToLatest() - } - } -} - -extension SignalProtocol where Value: SignalProducerProtocol, Error == NoError { - /// Flattens the inner producers sent upon `signal` (into a single signal of - /// values), according to the semantics of the given strategy. - /// - /// - note: If an active inner producer fails, the returned signal will - /// forward that failure immediately. - /// - /// - warning: `interrupted` events on inner producers will be treated like - /// `completed` events on inner producers. - /// - /// - parameters: - /// - strategy: Strategy used when flattening signals. - public func flatten(_ strategy: FlattenStrategy) -> Signal { - return self - .promoteErrors(Value.Error.self) - .flatten(strategy) - } -} - -extension SignalProtocol where Value: SignalProducerProtocol, Error == NoError, Value.Error == NoError { - /// Flattens the inner producers sent upon `signal` (into a single signal of - /// values), according to the semantics of the given strategy. - /// - /// - warning: `interrupted` events on inner producers will be treated like - /// `completed` events on inner producers. - /// - /// - parameters: - /// - strategy: Strategy used when flattening signals. - public func flatten(_ strategy: FlattenStrategy) -> Signal { - switch strategy { - case .merge: - return self.merge() - - case .concat: - return self.concat() - - case .latest: - return self.switchToLatest() - } - } -} - -extension SignalProtocol where Value: SignalProducerProtocol, Value.Error == NoError { - /// Flattens the inner producers sent upon `signal` (into a single signal of - /// values), according to the semantics of the given strategy. - /// - /// - note: If `signal` fails, the returned signal will forward that failure - /// immediately. - /// - /// - warning: `interrupted` events on inner producers will be treated like - /// `completed` events on inner producers. - public func flatten(_ strategy: FlattenStrategy) -> Signal { - return self.flatMap(strategy) { $0.promoteErrors(Error.self) } - } -} - -extension SignalProducerProtocol where Value: SignalProducerProtocol, Error == Value.Error { - /// Flattens the inner producers sent upon `producer` (into a single - /// producer of values), according to the semantics of the given strategy. - /// - /// - note: If `producer` or an active inner producer fails, the returned - /// producer will forward that failure immediately. - /// - /// - warning: `interrupted` events on inner producers will be treated like - /// `completed` events on inner producers. - public func flatten(_ strategy: FlattenStrategy) -> SignalProducer { - switch strategy { - case .merge: - return self.merge() - - case .concat: - return self.concat() - - case .latest: - return self.switchToLatest() - } - } -} - -extension SignalProducerProtocol where Value: SignalProducerProtocol, Error == NoError { - /// Flattens the inner producers sent upon `producer` (into a single - /// producer of values), according to the semantics of the given strategy. - /// - /// - note: If an active inner producer fails, the returned producer will - /// forward that failure immediately. - /// - /// - warning: `interrupted` events on inner producers will be treated like - /// `completed` events on inner producers. - public func flatten(_ strategy: FlattenStrategy) -> SignalProducer { - return self - .promoteErrors(Value.Error.self) - .flatten(strategy) - } -} - -extension SignalProducerProtocol where Value: SignalProducerProtocol, Error == NoError, Value.Error == NoError { - /// Flattens the inner producers sent upon `producer` (into a single - /// producer of values), according to the semantics of the given strategy. - /// - /// - warning: `interrupted` events on inner producers will be treated like - /// `completed` events on inner producers. - public func flatten(_ strategy: FlattenStrategy) -> SignalProducer { - switch strategy { - case .merge: - return self.merge() - - case .concat: - return self.concat() - - case .latest: - return self.switchToLatest() - } - } -} - -extension SignalProducerProtocol where Value: SignalProducerProtocol, Value.Error == NoError { - /// Flattens the inner producers sent upon `signal` (into a single signal of - /// values), according to the semantics of the given strategy. - /// - /// - note: If `signal` fails, the returned signal will forward that failure - /// immediately. - /// - /// - warning: `interrupted` events on inner producers will be treated like - /// `completed` events on inner producers. - public func flatten(_ strategy: FlattenStrategy) -> SignalProducer { - return self.flatMap(strategy) { $0.promoteErrors(Error.self) } - } -} - -extension SignalProtocol where Value: SignalProtocol, Error == Value.Error { - /// Flattens the inner signals sent upon `signal` (into a single signal of - /// values), according to the semantics of the given strategy. - /// - /// - note: If `signal` or an active inner signal emits an error, the - /// returned signal will forward that error immediately. - /// - /// - warning: `interrupted` events on inner signals will be treated like - /// `completed` events on inner signals. - public func flatten(_ strategy: FlattenStrategy) -> Signal { - return self - .map(SignalProducer.init) - .flatten(strategy) - } -} - -extension SignalProtocol where Value: SignalProtocol, Error == NoError { - /// Flattens the inner signals sent upon `signal` (into a single signal of - /// values), according to the semantics of the given strategy. - /// - /// - note: If an active inner signal emits an error, the returned signal - /// will forward that error immediately. - /// - /// - warning: `interrupted` events on inner signals will be treated like - /// `completed` events on inner signals. - public func flatten(_ strategy: FlattenStrategy) -> Signal { - return self - .promoteErrors(Value.Error.self) - .flatten(strategy) - } -} - -extension SignalProtocol where Value: SignalProtocol, Error == NoError, Value.Error == NoError { - /// Flattens the inner signals sent upon `signal` (into a single signal of - /// values), according to the semantics of the given strategy. - /// - /// - warning: `interrupted` events on inner signals will be treated like - /// `completed` events on inner signals. - public func flatten(_ strategy: FlattenStrategy) -> Signal { - return self - .map(SignalProducer.init) - .flatten(strategy) - } -} - -extension SignalProtocol where Value: SignalProtocol, Value.Error == NoError { - /// Flattens the inner signals sent upon `signal` (into a single signal of - /// values), according to the semantics of the given strategy. - /// - /// - note: If `signal` emits an error, the returned signal will forward - /// that error immediately. - /// - /// - warning: `interrupted` events on inner signals will be treated like - /// `completed` events on inner signals. - public func flatten(_ strategy: FlattenStrategy) -> Signal { - return self.flatMap(strategy) { $0.promoteErrors(Error.self) } - } -} - -extension SignalProtocol where Value: Sequence, Error == NoError { - /// Flattens the `sequence` value sent by `signal` according to - /// the semantics of the given strategy. - public func flatten(_ strategy: FlattenStrategy) -> Signal { - return self.flatMap(strategy) { .init(values: $0) } - } -} - -extension SignalProducerProtocol where Value: SignalProtocol, Error == Value.Error { - /// Flattens the inner signals sent upon `producer` (into a single producer - /// of values), according to the semantics of the given strategy. - /// - /// - note: If `producer` or an active inner signal emits an error, the - /// returned producer will forward that error immediately. - /// - /// - warning: `interrupted` events on inner signals will be treated like - /// `completed` events on inner signals. - public func flatten(_ strategy: FlattenStrategy) -> SignalProducer { - return self - .map(SignalProducer.init) - .flatten(strategy) - } -} - -extension SignalProducerProtocol where Value: SignalProtocol, Error == NoError { - /// Flattens the inner signals sent upon `producer` (into a single producer - /// of values), according to the semantics of the given strategy. - /// - /// - note: If an active inner signal emits an error, the returned producer - /// will forward that error immediately. - /// - /// - warning: `interrupted` events on inner signals will be treated like - /// `completed` events on inner signals. - public func flatten(_ strategy: FlattenStrategy) -> SignalProducer { - return self - .promoteErrors(Value.Error.self) - .flatten(strategy) - } -} - -extension SignalProducerProtocol where Value: SignalProtocol, Error == NoError, Value.Error == NoError { - /// Flattens the inner signals sent upon `producer` (into a single producer - /// of values), according to the semantics of the given strategy. - /// - /// - warning: `interrupted` events on inner signals will be treated like - /// `completed` events on inner signals. - public func flatten(_ strategy: FlattenStrategy) -> SignalProducer { - return self - .map(SignalProducer.init) - .flatten(strategy) - } -} - -extension SignalProducerProtocol where Value: SignalProtocol, Value.Error == NoError { - /// Flattens the inner signals sent upon `producer` (into a single producer - /// of values), according to the semantics of the given strategy. - /// - /// - note: If `producer` emits an error, the returned producer will forward - /// that error immediately. - /// - /// - warning: `interrupted` events on inner signals will be treated like - /// `completed` events on inner signals. - public func flatten(_ strategy: FlattenStrategy) -> SignalProducer { - return self.flatMap(strategy) { $0.promoteErrors(Error.self) } - } -} - -extension SignalProducerProtocol where Value: Sequence, Error == NoError { - /// Flattens the `sequence` value sent by `producer` according to - /// the semantics of the given strategy. - public func flatten(_ strategy: FlattenStrategy) -> SignalProducer { - return self.flatMap(strategy) { .init(values: $0) } - } -} - -extension SignalProtocol where Value: SignalProducerProtocol, Error == Value.Error { - /// Returns a signal which sends all the values from producer signal emitted - /// from `signal`, waiting until each inner producer completes before - /// beginning to send the values from the next inner producer. - /// - /// - note: If any of the inner producers fail, the returned signal will - /// forward that failure immediately - /// - /// - note: The returned signal completes only when `signal` and all - /// producers emitted from `signal` complete. - private func concat() -> Signal { - return Signal { relayObserver in - let disposable = CompositeDisposable() - let relayDisposable = CompositeDisposable() - - disposable += relayDisposable - disposable += self.observeConcat(relayObserver, relayDisposable) - - return disposable - } - } - - private func observeConcat(_ observer: Observer, _ disposable: CompositeDisposable? = nil) -> Disposable? { - let state = ConcatState(observer: observer, disposable: disposable) - - return self.observe { event in - switch event { - case let .next(value): - state.enqueueSignalProducer(value.producer) - - case let .failed(error): - observer.sendFailed(error) - - case .completed: - // Add one last producer to the queue, whose sole job is to - // "turn out the lights" by completing `observer`. - state.enqueueSignalProducer(SignalProducer.empty.on(completed: { - observer.sendCompleted() - })) - - case .interrupted: - observer.sendInterrupted() - } - } - } -} - -extension SignalProducerProtocol where Value: SignalProducerProtocol, Error == Value.Error { - /// Returns a producer which sends all the values from each producer emitted - /// from `producer`, waiting until each inner producer completes before - /// beginning to send the values from the next inner producer. - /// - /// - note: If any of the inner producers emit an error, the returned - /// producer will emit that error. - /// - /// - note: The returned producer completes only when `producer` and all - /// producers emitted from `producer` complete. - private func concat() -> SignalProducer { - return SignalProducer { observer, disposable in - self.startWithSignal { signal, signalDisposable in - disposable += signalDisposable - _ = signal.observeConcat(observer, disposable) - } - } - } -} - -extension SignalProducerProtocol { - /// `concat`s `next` onto `self`. - public func concat(_ next: SignalProducer) -> SignalProducer { - return SignalProducer, Error>(values: [ self.producer, next ]).flatten(.concat) - } - - /// `concat`s `value` onto `self`. - public func concat(value: Value) -> SignalProducer { - return self.concat(SignalProducer(value: value)) - } - - /// `concat`s `self` onto initial `previous`. - public func prefix(_ previous: P) -> SignalProducer { - return previous.concat(self.producer) - } - - /// `concat`s `self` onto initial `value`. - public func prefix(value: Value) -> SignalProducer { - return self.prefix(SignalProducer(value: value)) - } -} - -private final class ConcatState { - /// The observer of a started `concat` producer. - let observer: Observer - - /// The top level disposable of a started `concat` producer. - let disposable: CompositeDisposable? - - /// The active producer, if any, and the producers waiting to be started. - let queuedSignalProducers: Atomic<[SignalProducer]> = Atomic([]) - - init(observer: Signal.Observer, disposable: CompositeDisposable?) { - self.observer = observer - self.disposable = disposable - } - - func enqueueSignalProducer(_ producer: SignalProducer) { - if let d = disposable, d.isDisposed { - return - } - - let shouldStart: Bool = queuedSignalProducers.modify { queue in - // An empty queue means the concat is idle, ready & waiting to start - // the next producer. - defer { queue.append(producer) } - return queue.isEmpty - } - - if shouldStart { - startNextSignalProducer(producer) - } - } - - func dequeueSignalProducer() -> SignalProducer? { - if let d = disposable, d.isDisposed { - return nil - } - - return queuedSignalProducers.modify { queue in - // Active producers remain in the queue until completed. Since - // dequeueing happens at completion of the active producer, the - // first producer in the queue can be removed. - if !queue.isEmpty { queue.remove(at: 0) } - return queue.first - } - } - - /// Subscribes to the given signal producer. - func startNextSignalProducer(_ signalProducer: SignalProducer) { - signalProducer.startWithSignal { signal, disposable in - let handle = self.disposable?.add(disposable) ?? nil - - signal.observe { event in - switch event { - case .completed, .interrupted: - handle?.remove() - - if let nextSignalProducer = self.dequeueSignalProducer() { - self.startNextSignalProducer(nextSignalProducer) - } - - case .next, .failed: - self.observer.action(event) - } - } - } - } -} - -extension SignalProtocol where Value: SignalProducerProtocol, Error == Value.Error { - /// Merges a `signal` of SignalProducers down into a single signal, biased - /// toward the producer added earlier. Returns a Signal that will forward - /// events from the inner producers as they arrive. - private func merge() -> Signal { - return Signal { relayObserver in - let disposable = CompositeDisposable() - let relayDisposable = CompositeDisposable() - - disposable += relayDisposable - disposable += self.observeMerge(relayObserver, relayDisposable) - - return disposable - } - } - - private func observeMerge(_ observer: Observer, _ disposable: CompositeDisposable) -> Disposable? { - let inFlight = Atomic(1) - let decrementInFlight = { - let shouldComplete: Bool = inFlight.modify { - $0 -= 1 - return $0 == 0 - } - - if shouldComplete { - observer.sendCompleted() - } - } - - return self.observe { event in - switch event { - case let .next(producer): - producer.startWithSignal { innerSignal, innerDisposable in - inFlight.modify { $0 += 1 } - let handle = disposable.add(innerDisposable) - - innerSignal.observe { event in - switch event { - case .completed, .interrupted: - handle.remove() - decrementInFlight() - - case .next, .failed: - observer.action(event) - } - } - } - - case let .failed(error): - observer.sendFailed(error) - - case .completed: - decrementInFlight() - - case .interrupted: - observer.sendInterrupted() - } - } - } -} - -extension SignalProducerProtocol where Value: SignalProducerProtocol, Error == Value.Error { - /// Merges a `signal` of SignalProducers down into a single signal, biased - /// toward the producer added earlier. Returns a Signal that will forward - /// events from the inner producers as they arrive. - private func merge() -> SignalProducer { - return SignalProducer { relayObserver, disposable in - self.startWithSignal { signal, signalDisposable in - disposable += signalDisposable - - _ = signal.observeMerge(relayObserver, disposable) - } - - } - } -} - -extension SignalProtocol { - /// Merges the given signals into a single `Signal` that will emit all - /// values from each of them, and complete when all of them have completed. - public static func merge(_ signals: Seq) -> Signal { - let producer = SignalProducer(values: signals) - var result: Signal! - - producer.startWithSignal { signal, _ in - result = signal.flatten(.merge) - } - - return result - } - - /// Merges the given signals into a single `Signal` that will emit all - /// values from each of them, and complete when all of them have completed. - public static func merge(_ signals: S...) -> Signal { - return Signal.merge(signals) - } -} - -extension SignalProducerProtocol { - /// Merges the given producers into a single `SignalProducer` that will emit - /// all values from each of them, and complete when all of them have - /// completed. - public static func merge(_ producers: Seq) -> SignalProducer { - return SignalProducer(values: producers).flatten(.merge) - } - - /// Merges the given producers into a single `SignalProducer` that will emit - /// all values from each of them, and complete when all of them have - /// completed. - public static func merge(_ producers: S...) -> SignalProducer { - return SignalProducer.merge(producers) - } -} - -extension SignalProtocol where Value: SignalProducerProtocol, Error == Value.Error { - /// Returns a signal that forwards values from the latest signal sent on - /// `signal`, ignoring values sent on previous inner signal. - /// - /// An error sent on `signal` or the latest inner signal will be sent on the - /// returned signal. - /// - /// The returned signal completes when `signal` and the latest inner - /// signal have both completed. - private func switchToLatest() -> Signal { - return Signal { observer in - let composite = CompositeDisposable() - let serial = SerialDisposable() - - composite += serial - composite += self.observeSwitchToLatest(observer, serial) - - return composite - } - } - - private func observeSwitchToLatest(_ observer: Observer, _ latestInnerDisposable: SerialDisposable) -> Disposable? { - let state = Atomic(LatestState()) - - return self.observe { event in - switch event { - case let .next(innerProducer): - innerProducer.startWithSignal { innerSignal, innerDisposable in - state.modify { - // When we replace the disposable below, this prevents - // the generated Interrupted event from doing any work. - $0.replacingInnerSignal = true - } - - latestInnerDisposable.innerDisposable = innerDisposable - - state.modify { - $0.replacingInnerSignal = false - $0.innerSignalComplete = false - } - - innerSignal.observe { event in - switch event { - case .interrupted: - // If interruption occurred as a result of a new - // producer arriving, we don't want to notify our - // observer. - let shouldComplete: Bool = state.modify { state in - if !state.replacingInnerSignal { - state.innerSignalComplete = true - } - return !state.replacingInnerSignal && state.outerSignalComplete - } - - if shouldComplete { - observer.sendCompleted() - } - - case .completed: - let shouldComplete: Bool = state.modify { - $0.innerSignalComplete = true - return $0.outerSignalComplete - } - - if shouldComplete { - observer.sendCompleted() - } - - case .next, .failed: - observer.action(event) - } - } - } - - case let .failed(error): - observer.sendFailed(error) - - case .completed: - let shouldComplete: Bool = state.modify { - $0.outerSignalComplete = true - return $0.innerSignalComplete - } - - if shouldComplete { - observer.sendCompleted() - } - - case .interrupted: - observer.sendInterrupted() - } - } - } -} - -extension SignalProducerProtocol where Value: SignalProducerProtocol, Error == Value.Error { - /// Returns a signal that forwards values from the latest signal sent on - /// `signal`, ignoring values sent on previous inner signal. - /// - /// An error sent on `signal` or the latest inner signal will be sent on the - /// returned signal. - /// - /// The returned signal completes when `signal` and the latest inner - /// signal have both completed. - private func switchToLatest() -> SignalProducer { - return SignalProducer { observer, disposable in - let latestInnerDisposable = SerialDisposable() - disposable += latestInnerDisposable - - self.startWithSignal { signal, signalDisposable in - disposable += signalDisposable - disposable += signal.observeSwitchToLatest(observer, latestInnerDisposable) - } - } - } -} - -private struct LatestState { - var outerSignalComplete: Bool = false - var innerSignalComplete: Bool = true - - var replacingInnerSignal: Bool = false -} - - -extension SignalProtocol { - /// Maps each event from `signal` to a new signal, then flattens the - /// resulting producers (into a signal of values), according to the - /// semantics of the given strategy. - /// - /// If `signal` or any of the created producers fail, the returned signal - /// will forward that failure immediately. - public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> SignalProducer) -> Signal { - return map(transform).flatten(strategy) - } - - /// Maps each event from `signal` to a new signal, then flattens the - /// resulting producers (into a signal of values), according to the - /// semantics of the given strategy. - /// - /// If `signal` fails, the returned signal will forward that failure - /// immediately. - public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> SignalProducer) -> Signal { - return map(transform).flatten(strategy) - } - - /// Maps each event from `signal` to a new signal, then flattens the - /// resulting signals (into a signal of values), according to the - /// semantics of the given strategy. - /// - /// If `signal` or any of the created signals emit an error, the returned - /// signal will forward that error immediately. - public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> Signal) -> Signal { - return map(transform).flatten(strategy) - } - - /// Maps each event from `signal` to a new signal, then flattens the - /// resulting signals (into a signal of values), according to the - /// semantics of the given strategy. - /// - /// If `signal` emits an error, the returned signal will forward that - /// error immediately. - public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> Signal) -> Signal { - return map(transform).flatten(strategy) - } -} - -extension SignalProtocol where Error == NoError { - /// Maps each event from `signal` to a new signal, then flattens the - /// resulting signals (into a signal of values), according to the - /// semantics of the given strategy. - /// - /// If any of the created signals emit an error, the returned signal - /// will forward that error immediately. - public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> SignalProducer) -> Signal { - return map(transform).flatten(strategy) - } - - /// Maps each event from `signal` to a new signal, then flattens the - /// resulting signals (into a signal of values), according to the - /// semantics of the given strategy. - public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> SignalProducer) -> Signal { - return map(transform).flatten(strategy) - } - - /// Maps each event from `signal` to a new signal, then flattens the - /// resulting signals (into a signal of values), according to the - /// semantics of the given strategy. - /// - /// If any of the created signals emit an error, the returned signal - /// will forward that error immediately. - public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> Signal) -> Signal { - return map(transform).flatten(strategy) - } - - /// Maps each event from `signal` to a new signal, then flattens the - /// resulting signals (into a signal of values), according to the - /// semantics of the given strategy. - public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> Signal) -> Signal { - return map(transform).flatten(strategy) - } -} - -extension SignalProducerProtocol { - /// Maps each event from `self` to a new producer, then flattens the - /// resulting producers (into a producer of values), according to the - /// semantics of the given strategy. - /// - /// If `self` or any of the created producers fail, the returned producer - /// will forward that failure immediately. - public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> SignalProducer) -> SignalProducer { - return map(transform).flatten(strategy) - } - - /// Maps each event from `self` to a new producer, then flattens the - /// resulting producers (into a producer of values), according to the - /// semantics of the given strategy. - /// - /// If `self` fails, the returned producer will forward that failure - /// immediately. - public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> SignalProducer) -> SignalProducer { - return map(transform).flatten(strategy) - } - - /// Maps each event from `self` to a new producer, then flattens the - /// resulting signals (into a producer of values), according to the - /// semantics of the given strategy. - /// - /// If `self` or any of the created signals emit an error, the returned - /// producer will forward that error immediately. - public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> Signal) -> SignalProducer { - return map(transform).flatten(strategy) - } - - /// Maps each event from `self` to a new producer, then flattens the - /// resulting signals (into a producer of values), according to the - /// semantics of the given strategy. - /// - /// If `self` emits an error, the returned producer will forward that - /// error immediately. - public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> Signal) -> SignalProducer { - return map(transform).flatten(strategy) - } -} - -extension SignalProducerProtocol where Error == NoError { - /// Maps each event from `self` to a new producer, then flattens the - /// resulting producers (into a producer of values), according to the - /// semantics of the given strategy. - /// - /// If any of the created producers fail, the returned producer will - /// forward that failure immediately. - public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> SignalProducer) -> SignalProducer { - return map(transform).flatten(strategy) - } - - /// Maps each event from `self` to a new producer, then flattens the - /// resulting producers (into a producer of values), according to the - /// semantics of the given strategy. - public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> SignalProducer) -> SignalProducer { - return map(transform).flatten(strategy) - } - - /// Maps each event from `self` to a new producer, then flattens the - /// resulting signals (into a producer of values), according to the - /// semantics of the given strategy. - /// - /// If any of the created signals emit an error, the returned - /// producer will forward that error immediately. - public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> Signal) -> SignalProducer { - return map(transform).flatten(strategy) - } - - /// Maps each event from `self` to a new producer, then flattens the - /// resulting signals (into a producer of values), according to the - /// semantics of the given strategy. - public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> Signal) -> SignalProducer { - return map(transform).flatten(strategy) - } -} - - -extension SignalProtocol { - /// Catches any failure that may occur on the input signal, mapping to a new - /// producer that starts in its place. - public func flatMapError(_ handler: (Error) -> SignalProducer) -> Signal { - return Signal { observer in - self.observeFlatMapError(handler, observer, SerialDisposable()) - } - } - - private func observeFlatMapError(_ handler: (Error) -> SignalProducer, _ observer: Observer, _ serialDisposable: SerialDisposable) -> Disposable? { - return self.observe { event in - switch event { - case let .next(value): - observer.sendNext(value) - case let .failed(error): - handler(error).startWithSignal { signal, disposable in - serialDisposable.innerDisposable = disposable - signal.observe(observer) - } - case .completed: - observer.sendCompleted() - case .interrupted: - observer.sendInterrupted() - } - } - } -} - -extension SignalProducerProtocol { - /// Catches any failure that may occur on the input producer, mapping to a - /// new producer that starts in its place. - public func flatMapError(_ handler: (Error) -> SignalProducer) -> SignalProducer { - return SignalProducer { observer, disposable in - let serialDisposable = SerialDisposable() - disposable += serialDisposable - - self.startWithSignal { signal, signalDisposable in - serialDisposable.innerDisposable = signalDisposable - - _ = signal.observeFlatMapError(handler, observer, serialDisposable) - } - } - } -} diff --git a/ReactiveCocoa/Swift/FoundationExtensions.swift b/ReactiveCocoa/Swift/FoundationExtensions.swift deleted file mode 100644 index 98b6543e85..0000000000 --- a/ReactiveCocoa/Swift/FoundationExtensions.swift +++ /dev/null @@ -1,80 +0,0 @@ -// -// FoundationExtensions.swift -// ReactiveCocoa -// -// Created by Justin Spahr-Summers on 2014-10-19. -// Copyright (c) 2014 GitHub. All rights reserved. -// - -import Foundation -import enum Result.NoError - -extension NotificationCenter { - /// Returns a SignalProducer to observe posting of the specified - /// notification. - /// - /// - parameters: - /// - name: name of the notification to observe - /// - object: an instance which sends the notifications - /// - /// - returns: A SignalProducer of notifications posted that match the given - /// criteria. - /// - /// - note: If the `object` is deallocated before starting the producer, it - /// will terminate immediately with an `interrupted` event. - /// Otherwise, the producer will not terminate naturally, so it must - /// be explicitly disposed to avoid leaks. - public func rac_notifications(forName name: Notification.Name?, object: AnyObject? = nil) -> SignalProducer { - // We're weakly capturing an optional reference here, which makes destructuring awkward. - let objectWasNil = (object == nil) - return SignalProducer { [weak object] observer, disposable in - guard object != nil || objectWasNil else { - observer.sendInterrupted() - return - } - - let notificationObserver = self.addObserver(forName: name, object: object, queue: nil) { notification in - observer.sendNext(notification) - } - - disposable += { - self.removeObserver(notificationObserver) - } - } - } -} - -private let defaultSessionError = NSError(domain: "org.reactivecocoa.ReactiveCocoa.rac_dataWithRequest", code: 1, userInfo: nil) - -extension URLSession { - /// Returns a SignalProducer which performs the work associated with an - /// `NSURLSession` - /// - /// - parameters: - /// - request: A request that will be performed when the producer is - /// started - /// - /// - returns: A producer that will execute the given request once for each - /// invocation of `start()`. - /// - /// - note: This method will not send an error event in the case of a server - /// side error (i.e. when a response with status code other than - /// 200...299 is received). - public func rac_data(with request: URLRequest) -> SignalProducer<(Data, URLResponse), NSError> { - return SignalProducer { observer, disposable in - let task = self.dataTask(with: request) { data, response, error in - if let data = data, let response = response { - observer.sendNext((data, response)) - observer.sendCompleted() - } else { - observer.sendFailed(error ?? defaultSessionError) - } - } - - disposable += { - task.cancel() - } - task.resume() - } - } -} diff --git a/ReactiveCocoa/Swift/Lifetime.swift b/ReactiveCocoa/Swift/Lifetime.swift index 54f385b9c1..388800548a 100644 --- a/ReactiveCocoa/Swift/Lifetime.swift +++ b/ReactiveCocoa/Swift/Lifetime.swift @@ -1,54 +1,7 @@ import Foundation +import ReactiveSwift import enum Result.NoError -/// Represents the lifetime of an object, and provides a hook to observe when -/// the object deinitializes. -public final class Lifetime { - /// A signal that sends a Completed event when the lifetime ends. - public let ended: Signal<(), NoError> - - /// Initialize a `Lifetime` from a lifetime token, which is expected to be - /// associated with an object. - /// - /// - important: The resulting lifetime object does not retain the lifetime - /// token. - /// - /// - parameters: - /// - token: A lifetime token for detecting the deinitialization of the - /// associated object. - public init(_ token: Token) { - ended = token.ended - } - - /// A token object which completes its signal when it deinitializes. - /// - /// It is generally used in conjuncion with `Lifetime` as a private - /// deinitialization trigger. - /// - /// ``` - /// class MyController { - /// private let token = Lifetime.Token() - /// public var lifetime: Lifetime { - /// return Lifetime(token) - /// } - /// } - /// ``` - public final class Token { - /// A signal that sends a Completed event when the lifetime ends. - private let ended: Signal<(), NoError> - - private let endedObserver: Signal<(), NoError>.Observer - - public init() { - (ended, endedObserver) = Signal.pipe() - } - - deinit { - endedObserver.sendCompleted() - } - } -} - #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) private var lifetimeKey: UInt8 = 0 diff --git a/ReactiveCocoa/Swift/NSObject+KeyValueObserving.swift b/ReactiveCocoa/Swift/NSObject+KeyValueObserving.swift index 32dfe1b541..f00dda85a1 100644 --- a/ReactiveCocoa/Swift/NSObject+KeyValueObserving.swift +++ b/ReactiveCocoa/Swift/NSObject+KeyValueObserving.swift @@ -1,4 +1,5 @@ import Foundation +import ReactiveSwift import enum Result.NoError extension NSObject { diff --git a/ReactiveCocoa/Swift/ObjectiveCBridging.swift b/ReactiveCocoa/Swift/ObjectiveCBridging.swift index f08e04e752..6fd75be418 100644 --- a/ReactiveCocoa/Swift/ObjectiveCBridging.swift +++ b/ReactiveCocoa/Swift/ObjectiveCBridging.swift @@ -6,8 +6,24 @@ // Copyright (c) 2014 GitHub, Inc. All rights reserved. // +import Foundation +import ReactiveSwift import Result +extension SignalProtocol { + /// Turns each value into an Optional. + private func optionalize() -> Signal { + return map(Optional.init) + } +} + +extension SignalProducerProtocol { + /// Turns each value into an Optional. + private func optionalize() -> SignalProducer { + return lift { $0.optionalize() } + } +} + extension RACDisposable: Disposable {} extension RACScheduler: DateSchedulerProtocol { /// The current date, as determined by this scheduler. diff --git a/ReactiveCocoa/Swift/Observer.swift b/ReactiveCocoa/Swift/Observer.swift deleted file mode 100644 index 667a97b64d..0000000000 --- a/ReactiveCocoa/Swift/Observer.swift +++ /dev/null @@ -1,104 +0,0 @@ -// -// Observer.swift -// ReactiveCocoa -// -// Created by Andy Matuschak on 10/2/15. -// Copyright © 2015 GitHub. All rights reserved. -// - -/// A protocol for type-constrained extensions of `Observer`. -public protocol ObserverProtocol { - associatedtype Value - associatedtype Error: Swift.Error - - /// Puts a `next` event into `self`. - func sendNext(_ value: Value) - - /// Puts a failed event into `self`. - func sendFailed(_ error: Error) - - /// Puts a `completed` event into `self`. - func sendCompleted() - - /// Puts an `interrupted` event into `self`. - func sendInterrupted() -} - -/// An Observer is a simple wrapper around a function which can receive Events -/// (typically from a Signal). -public final class Observer { - public typealias Action = (Event) -> Void - - /// An action that will be performed upon arrival of the event. - public let action: Action - - /// An initializer that accepts a closure accepting an event for the - /// observer. - /// - /// - parameters: - /// - action: A closure to lift over received event. - public init(_ action: Action) { - self.action = action - } - - /// An initializer that accepts closures for different event types. - /// - /// - parameters: - /// - next: Optional closure executed when a `next` event is observed. - /// - failed: Optional closure that accepts an `Error` parameter when a - /// failed event is observed. - /// - completed: Optional closure executed when a `completed` event is - /// observed. - /// - interruped: Optional closure executed when an `interrupted` event is - /// observed. - public convenience init( - next: ((Value) -> Void)? = nil, - failed: ((Error) -> Void)? = nil, - completed: (() -> Void)? = nil, - interrupted: (() -> Void)? = nil - ) { - self.init { event in - switch event { - case let .next(value): - next?(value) - - case let .failed(error): - failed?(error) - - case .completed: - completed?() - - case .interrupted: - interrupted?() - } - } - } -} - -extension Observer: ObserverProtocol { - /// Puts a `next` event into `self`. - /// - /// - parameters: - /// - value: A value sent with the `next` event. - public func sendNext(_ value: Value) { - action(.next(value)) - } - - /// Puts a failed event into `self`. - /// - /// - parameters: - /// - error: An error object sent with failed event. - public func sendFailed(_ error: Error) { - action(.failed(error)) - } - - /// Puts a `completed` event into `self`. - public func sendCompleted() { - action(.completed) - } - - /// Puts an `interrupted` event into `self`. - public func sendInterrupted() { - action(.interrupted) - } -} diff --git a/ReactiveCocoa/Swift/Optional.swift b/ReactiveCocoa/Swift/Optional.swift deleted file mode 100644 index a32a029014..0000000000 --- a/ReactiveCocoa/Swift/Optional.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// Optional.swift -// ReactiveCocoa -// -// Created by Neil Pankey on 6/24/15. -// Copyright (c) 2015 GitHub. All rights reserved. -// - -/// An optional protocol for use in type constraints. -public protocol OptionalProtocol { - /// The type contained in the otpional. - associatedtype Wrapped - - init(reconstructing value: Wrapped?) - - /// Extracts an optional from the receiver. - var optional: Wrapped? { get } -} - -extension Optional: OptionalProtocol { - public var optional: Wrapped? { - return self - } - - public init(reconstructing value: Wrapped?) { - self = value - } -} - -extension SignalProtocol { - /// Turns each value into an Optional. - internal func optionalize() -> Signal { - return map(Optional.init) - } -} - -extension SignalProducerProtocol { - /// Turns each value into an Optional. - internal func optionalize() -> SignalProducer { - return lift { $0.optionalize() } - } -} diff --git a/ReactiveCocoa/Swift/Property.swift b/ReactiveCocoa/Swift/Property.swift deleted file mode 100644 index 3fec743901..0000000000 --- a/ReactiveCocoa/Swift/Property.swift +++ /dev/null @@ -1,878 +0,0 @@ -import Foundation -import enum Result.NoError - -/// Represents a property that allows observation of its changes. -/// -/// Only classes can conform to this protocol, because having a signal -/// for changes over time implies the origin must have a unique identity. -public protocol PropertyProtocol: class { - associatedtype Value - - /// The current value of the property. - var value: Value { get } - - /// 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. - var producer: SignalProducer { get } - - /// A signal that will send the property's changes over time. It - /// completes when the property has deinitialized, or has no further - /// change. - var signal: Signal { get } -} - -/// Represents an observable property that can be mutated directly. -public protocol MutablePropertyProtocol: PropertyProtocol { - /// The current value of the property. - var value: Value { get set } -} - -/// Protocol composition operators -/// -/// The producer and the signal of transformed properties would complete -/// only when its source properties have deinitialized. -/// -/// A composed property would retain its ultimate source, but not -/// any intermediate property during the composition. -extension PropertyProtocol { - /// Lifts a unary SignalProducer operator to operate upon PropertyProtocol instead. - private func lift(_ transform: @noescape (SignalProducer) -> SignalProducer) -> Property { - return Property(self, transform: transform) - } - - /// Lifts a binary SignalProducer operator to operate upon PropertyProtocol instead. - private func lift(_ transform: @noescape (SignalProducer) -> (SignalProducer) -> SignalProducer) -> @noescape (P) -> Property { - return { otherProperty in - return Property(self, otherProperty, transform: transform) - } - } - - /// Maps the current value and all subsequent values to a new property. - /// - /// - parameters: - /// - transform: A closure that will map the current `value` of this - /// `Property` to a new value. - /// - /// - returns: A new instance of `AnyProperty` who's holds a mapped value - /// from `self`. - public func map(_ transform: (Value) -> U) -> Property { - return lift { $0.map(transform) } - } - - /// Combines the current value and the subsequent values of two `Property`s in - /// the manner described by `Signal.combineLatestWith:`. - /// - /// - parameters: - /// - other: A property to combine `self`'s value with. - /// - /// - returns: A property that holds a tuple containing values of `self` and - /// the given property. - public func combineLatest(with other: P) -> Property<(Value, P.Value)> { - return lift(SignalProducer.combineLatest(with:))(other) - } - - /// Zips the current value and the subsequent values of two `Property`s in - /// the manner described by `Signal.zipWith`. - /// - /// - parameters: - /// - other: A property to zip `self`'s value with. - /// - /// - returns: A property that holds a tuple containing values of `self` and - /// the given property. - public func zip(with other: P) -> Property<(Value, P.Value)> { - return lift(SignalProducer.zip(with:))(other) - } - - /// Forward events from `self` with history: values of the returned property - /// are a tuple whose first member is the previous value and whose second - /// member is the current value. `initial` is supplied as the first member - /// when `self` sends its first value. - /// - /// - parameters: - /// - initial: A value that will be combined with the first value sent by - /// `self`. - /// - /// - returns: A property that holds tuples that contain previous and - /// current values of `self`. - public func combinePrevious(_ initial: Value) -> Property<(Value, Value)> { - return lift { $0.combinePrevious(initial) } - } - - /// Forward only those values from `self` which do not pass `isRepeat` with - /// respect to the previous value. - /// - /// - parameters: - /// - isRepeat: A predicate to determine if the two given values are equal. - /// - /// - returns: A property that does not emit events for two equal values - /// sequentially. - public func skipRepeats(_ isRepeat: (Value, Value) -> Bool) -> Property { - return lift { $0.skipRepeats(isRepeat) } - } -} - -extension PropertyProtocol where Value: Equatable { - /// Forward only those values from `self` which do not pass `isRepeat` with - /// respect to the previous value. - /// - /// - returns: A property that does not emit events for two equal values - /// sequentially. - public func skipRepeats() -> Property { - return lift { $0.skipRepeats() } - } -} - -extension PropertyProtocol where Value: PropertyProtocol { - /// Flattens the inner property held by `self` (into a single property of - /// values), according to the semantics of the given strategy. - /// - /// - parameters: - /// - strategy: The preferred flatten strategy. - /// - /// - returns: A property that sends the values of its inner properties. - public func flatten(_ strategy: FlattenStrategy) -> Property { - return lift { $0.flatMap(strategy) { $0.producer } } - } -} - -extension PropertyProtocol { - /// Maps each property from `self` to a new property, then flattens the - /// resulting properties (into a single property), according to the - /// semantics of the given strategy. - /// - /// - parameters: - /// - strategy: The preferred flatten strategy. - /// - transform: The transform to be applied on `self` before flattening. - /// - /// - returns: A property that sends the values of its inner properties. - public func flatMap(_ strategy: FlattenStrategy, transform: (Value) -> P) -> Property { - return lift { $0.flatMap(strategy) { transform($0).producer } } - } - - /// Forward only those values from `self` that have unique identities across - /// the set of all values that have been held. - /// - /// - note: This causes the identities to be retained to check for - /// uniqueness. - /// - /// - parameters: - /// - transform: A closure that accepts a value and returns identity - /// value. - /// - /// - returns: A property that sends unique values during its lifetime. - public func uniqueValues(_ transform: (Value) -> Identity) -> Property { - return lift { $0.uniqueValues(transform) } - } -} - -extension PropertyProtocol where Value: Hashable { - /// Forwards only those values from `self` that are unique across the set of - /// all values that have been seen. - /// - /// - note: This causes the identities to be retained to check for uniqueness. - /// Providing a function that returns a unique value for each sent - /// value can help you reduce the memory footprint. - /// - /// - returns: A property that sends unique values during its lifetime. - public func uniqueValues() -> Property { - return lift { $0.uniqueValues() } - } -} - -extension PropertyProtocol { - /// Combines the values of all the given properties, in the manner described - /// by `combineLatest(with:)`. - public static func combineLatest(_ a: A, _ b: B) -> Property<(A.Value, B.Value)> { - return a.combineLatest(with: b) - } - - /// Combines the values of all the given properties, in the manner described - /// by `combineLatest(with:)`. - public static func combineLatest(_ a: A, _ b: B, _ c: C) -> Property<(A.Value, B.Value, C.Value)> { - return combineLatest(a, b) - .combineLatest(with: c) - .map(repack) - } - - /// Combines the values of all the given properties, in the manner described - /// by `combineLatest(with:)`. - public static func combineLatest(_ a: A, _ b: B, _ c: C, _ d: D) -> Property<(A.Value, B.Value, C.Value, D.Value)> { - return combineLatest(a, b, c) - .combineLatest(with: d) - .map(repack) - } - - /// Combines the values of all the given properties, in the manner described - /// by `combineLatest(with:)`. - public static func combineLatest(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value)> { - return combineLatest(a, b, c, d) - .combineLatest(with: e) - .map(repack) - } - - /// Combines the values of all the given properties, in the manner described - /// by `combineLatest(with:)`. - public static func combineLatest(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value)> { - return combineLatest(a, b, c, d, e) - .combineLatest(with: f) - .map(repack) - } - - /// Combines the values of all the given properties, in the manner described - /// by `combineLatest(with:)`. - public static func combineLatest(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value)> { - return combineLatest(a, b, c, d, e, f) - .combineLatest(with: g) - .map(repack) - } - - /// Combines the values of all the given properties, in the manner described - /// by `combineLatest(with:)`. - public static func combineLatest(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G, _ h: H) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value, H.Value)> { - return combineLatest(a, b, c, d, e, f, g) - .combineLatest(with: h) - .map(repack) - } - - /// Combines the values of all the given properties, in the manner described - /// by `combineLatest(with:)`. - public static func combineLatest(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G, _ h: H, _ i: I) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value, H.Value, I.Value)> { - return combineLatest(a, b, c, d, e, f, g, h) - .combineLatest(with: i) - .map(repack) - } - - /// Combines the values of all the given properties, in the manner described - /// by `combineLatest(with:)`. - public static func combineLatest(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G, _ h: H, _ i: I, _ j: J) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value, H.Value, I.Value, J.Value)> { - return combineLatest(a, b, c, d, e, f, g, h, i) - .combineLatest(with: j) - .map(repack) - } - - /// Combines the values of all the given producers, in the manner described by - /// `combineLatest(with:)`. Returns nil if the sequence is empty. - public static func combineLatest(_ properties: S) -> Property<[S.Iterator.Element.Value]>? { - var generator = properties.makeIterator() - if let first = generator.next() { - let initial = first.map { [$0] } - return IteratorSequence(generator).reduce(initial) { property, next in - property.combineLatest(with: next).map { $0.0 + [$0.1] } - } - } - - return nil - } - - /// Zips the values of all the given properties, in the manner described by - /// `zip(with:)`. - public static func zip(_ a: A, _ b: B) -> Property<(A.Value, B.Value)> { - return a.zip(with: b) - } - - /// Zips the values of all the given properties, in the manner described by - /// `zip(with:)`. - public static func zip(_ a: A, _ b: B, _ c: C) -> Property<(A.Value, B.Value, C.Value)> { - return zip(a, b) - .zip(with: c) - .map(repack) - } - - /// Zips the values of all the given properties, in the manner described by - /// `zip(with:)`. - public static func zip(_ a: A, _ b: B, _ c: C, _ d: D) -> Property<(A.Value, B.Value, C.Value, D.Value)> { - return zip(a, b, c) - .zip(with: d) - .map(repack) - } - - /// Zips the values of all the given properties, in the manner described by - /// `zip(with:)`. - public static func zip(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value)> { - return zip(a, b, c, d) - .zip(with: e) - .map(repack) - } - - /// Zips the values of all the given properties, in the manner described by - /// `zip(with:)`. - public static func zip(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value)> { - return zip(a, b, c, d, e) - .zip(with: f) - .map(repack) - } - - /// Zips the values of all the given properties, in the manner described by - /// `zip(with:)`. - public static func zip(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value)> { - return zip(a, b, c, d, e, f) - .zip(with: g) - .map(repack) - } - - /// Zips the values of all the given properties, in the manner described by - /// `zip(with:)`. - public static func zip(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G, _ h: H) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value, H.Value)> { - return zip(a, b, c, d, e, f, g) - .zip(with: h) - .map(repack) - } - - /// Zips the values of all the given properties, in the manner described by - /// `zip(with:)`. - public static func zip(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G, _ h: H, _ i: I) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value, H.Value, I.Value)> { - return zip(a, b, c, d, e, f, g, h) - .zip(with: i) - .map(repack) - } - - /// Zips the values of all the given properties, in the manner described by - /// `zip(with:)`. - public static func zip(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G, _ h: H, _ i: I, _ j: J) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value, H.Value, I.Value, J.Value)> { - return zip(a, b, c, d, e, f, g, h, i) - .zip(with: j) - .map(repack) - } - - /// Zips the values of all the given properties, in the manner described by - /// `zip(with:)`. Returns nil if the sequence is empty. - public static func zip(_ properties: S) -> Property<[S.Iterator.Element.Value]>? { - var generator = properties.makeIterator() - if let first = generator.next() { - let initial = first.map { [$0] } - return IteratorSequence(generator).reduce(initial) { property, next in - property.zip(with: next).map { $0.0 + [$0.1] } - } - } - - return nil - } -} - -/// A read-only property that can be observed for its changes over time. There are -/// three categories of read-only property: -/// -/// # Constant property -/// Created by `Property(value:)`, the producer and signal of a constant -/// property would complete immediately when it is initialized. -/// -/// # Existential property -/// Created by `Property(_:)`, an existential property passes through the -/// behavior of the wrapped property. -/// -/// # Composed property -/// Created by either the compositional operators in `PropertyProtocol`, or -/// `Property(initial:followingBy:)`, a composed property presents a -/// composed view of its source, which can be a set of properties, -/// a producer, or a signal. -/// -/// A composed property respects the lifetime of its source rather than its own. -/// In other words, its producer and signal can outlive the property itself, if -/// its source outlives it too. -public final class Property: PropertyProtocol { - private let sources: [AnyObject] - - private let _value: () -> Value - private let _producer: () -> SignalProducer - private let _signal: () -> Signal - - /// The current value of the property. - public var value: Value { - return _value() - } - - /// A producer for Signals that will send the property's current - /// value, followed by all changes over time, then complete when the - /// property has deinitialized or has no further changes. - public var producer: SignalProducer { - return _producer() - } - - /// A signal that will send the property's changes over time, then - /// complete when the property has deinitialized or has no further changes. - public var signal: Signal { - return _signal() - } - - /// Initializes a constant property. - /// - /// - parameters: - /// - property: A value of the constant property. - public init(value: Value) { - sources = [] - _value = { value } - _producer = { SignalProducer(value: value) } - _signal = { Signal.empty } - } - - /// Initializes an existential property which wraps the given property. - /// - /// - parameters: - /// - property: A property to be wrapped. - public init(_ property: P) { - sources = Property.capture(property) - _value = { property.value } - _producer = { property.producer } - _signal = { property.signal } - } - - /// Initializes a composed property that first takes on `initial`, then each - /// value sent on a signal created by `producer`. - /// - /// - parameters: - /// - initial: Starting value for the property. - /// - producer: A producer that will start immediately and send values to - /// the property. - public convenience init(initial: Value, then producer: SignalProducer) { - self.init(unsafeProducer: producer.prefix(value: initial), - capturing: []) - } - - /// Initialize a composed property that first takes on `initial`, then each - /// value sent on `signal`. - /// - /// - parameters: - /// - initialValue: Starting value for the property. - /// - signal: A signal that will send values to the property. - public convenience init(initial: Value, then signal: Signal) { - self.init(unsafeProducer: SignalProducer(signal: signal).prefix(value: initial), - capturing: []) - } - - /// Initialize a composed property by applying the unary `SignalProducer` - /// transform on `property`. - /// - /// - parameters: - /// - property: The source property. - /// - transform: A unary `SignalProducer` transform to be applied on - /// `property`. - private convenience init(_ property: P, transform: @noescape (SignalProducer) -> SignalProducer) { - self.init(unsafeProducer: transform(property.producer), - capturing: Property.capture(property)) - } - - /// Initialize a composed property by applying the binary `SignalProducer` - /// transform on `firstProperty` and `secondProperty`. - /// - /// - parameters: - /// - firstProperty: The first source property. - /// - secondProperty: The first source property. - /// - transform: A binary `SignalProducer` transform to be applied on - /// `firstProperty` and `secondProperty`. - private convenience init(_ firstProperty: P1, _ secondProperty: P2, transform: @noescape (SignalProducer) -> (SignalProducer) -> SignalProducer) { - self.init(unsafeProducer: transform(firstProperty.producer)(secondProperty.producer), - capturing: Property.capture(firstProperty) + Property.capture(secondProperty)) - } - - /// Initialize a composed property from a producer that promises to send - /// at least one value synchronously in its start handler before sending any - /// subsequent event. - /// - /// - important: The producer and the signal of the created property would - /// complete only when the `unsafeProducer` completes. - /// - /// - warning: If the producer fails its promise, a fatal error would be - /// raised. - /// - /// - parameters: - /// - unsafeProducer: The composed producer for creating the property. - /// - sources: The property sources to be captured. - private init(unsafeProducer: SignalProducer, capturing sources: [AnyObject]) { - // Share a replayed producer with `self.producer` and `self.signal` so - // they see a consistent view of the `self.value`. - // https://github.com/ReactiveCocoa/ReactiveCocoa/pull/3042 - let producer = unsafeProducer.replayLazily(upTo: 1) - - // Verify that an initial is sent. This is friendlier than deadlocking - // in the event that one isn't. - var value: Value? = nil - let disposable = producer.start { event in - switch event { - case let .next(newValue): - value = newValue - - case .completed, .interrupted: - break - - case let .failed(error): - fatalError("Receive unexpected error from a producer of `NoError` type: \(error)") - } - } - guard value != nil else { - fatalError("A producer promised to send at least one value. Received none.") - } - disposable.dispose() - - self.sources = sources - _value = { producer.take(first: 1).single()!.value! } - _producer = { producer } - _signal = { - var extractedSignal: Signal! - producer.startWithSignal { signal, _ in extractedSignal = signal } - return extractedSignal - } - } - - /// Inspect if `property` is an `AnyProperty` and has already captured its - /// sources using a closure. Returns that closure if it does. Otherwise, - /// returns a closure which captures `property`. - /// - /// - parameters: - /// - property: The property to be insepcted. - private static func capture(_ property: P) -> [AnyObject] { - if let property = property as? Property { - return property.sources - } else { - return [property] - } - } -} - -/// A mutable property of type `Value` that allows observation of its changes. -/// -/// Instances of this class are thread-safe. -public final class MutableProperty: MutablePropertyProtocol { - private let observer: Signal.Observer - - private let atomic: RecursiveAtomic - - /// The current value of the property. - /// - /// Setting this to a new value will notify all observers of `signal`, or - /// signals created using `producer`. - public var value: Value { - get { - return atomic.withValue { $0 } - } - - set { - swap(newValue) - } - } - - /// A signal that will send the property's changes over time, - /// then complete when the property has deinitialized. - public let signal: Signal - - /// A producer for Signals that will send the property's current value, - /// followed by all changes over time, then complete when the property has - /// deinitialized. - public var producer: SignalProducer { - return SignalProducer { [atomic, weak self] producerObserver, producerDisposable in - atomic.withValue { value in - if let strongSelf = self { - producerObserver.sendNext(value) - producerDisposable += strongSelf.signal.observe(producerObserver) - } else { - producerObserver.sendNext(value) - producerObserver.sendCompleted() - } - } - } - } - - /// Initializes a mutable property that first takes on `initialValue` - /// - /// - parameters: - /// - initialValue: Starting value for the mutable property. - public init(_ initialValue: Value) { - (signal, observer) = Signal.pipe() - - /// Need a recursive lock around `value` to allow recursive access to - /// `value`. Note that recursive sets will still deadlock because the - /// underlying producer prevents sending recursive events. - atomic = RecursiveAtomic(initialValue, - name: "org.reactivecocoa.ReactiveCocoa.MutableProperty", - didSet: observer.sendNext) - } - - /// Atomically replaces the contents of the variable. - /// - /// - parameters: - /// - newValue: New property value. - /// - /// - returns: The previous property value. - @discardableResult - public func swap(_ newValue: Value) -> Value { - return atomic.swap(newValue) - } - - /// Atomically modifies the variable. - /// - /// - parameters: - /// - action: A closure that accepts old property value and returns a new - /// property value. - /// - /// - returns: The result of the action. - @discardableResult - public func modify(_ action: @noescape (inout Value) throws -> Result) rethrows -> Result { - return try atomic.modify(action) - } - - /// Atomically performs an arbitrary action using the current value of the - /// variable. - /// - /// - parameters: - /// - action: A closure that accepts current property value. - /// - /// - returns: the result of the action. - @discardableResult - public func withValue(action: @noescape (Value) throws -> Result) rethrows -> Result { - return try atomic.withValue(action) - } - - deinit { - observer.sendCompleted() - } -} - -private class Box { - var value: Value - - init(_ value: Value) { - self.value = value - } -} - -infix operator <~ { - associativity right - - // Binds tighter than assignment but looser than everything else - precedence 93 -} - -/// Binds a signal to a property, updating the property's value to the latest -/// value sent by the signal. -/// -/// - note: The binding will automatically terminate when the property is -/// deinitialized, or when the signal sends a `completed` event. -/// -/// ```` -/// let property = MutableProperty(0) -/// let signal = Signal({ /* do some work after some time */ }) -/// property <~ signal -/// ```` -/// -/// ```` -/// let property = MutableProperty(0) -/// let signal = Signal({ /* do some work after some time */ }) -/// let disposable = property <~ signal -/// ... -/// // Terminates binding before property dealloc or signal's -/// // `completed` event. -/// disposable.dispose() -/// ```` -/// -/// - parameters: -/// - property: A property to bind to. -/// - signal: A signal to bind. -/// -/// - returns: A disposable that can be used to terminate binding before the -/// deinitialization of property or signal's `completed` event. -@discardableResult -public func <~ (property: P, signal: Signal) -> Disposable { - let disposable = CompositeDisposable() - disposable += property.producer.startWithCompleted { - disposable.dispose() - } - - disposable += signal.observe { [weak property] event in - switch event { - case let .next(value): - property?.value = value - case .completed: - disposable.dispose() - case .failed, .interrupted: - break - } - } - - return disposable -} - -/// Creates a signal from the given producer, which will be immediately bound to -/// the given property, updating the property's value to the latest value sent -/// by the signal. -/// -/// ```` -/// let property = MutableProperty(0) -/// let producer = SignalProducer(value: 1) -/// property <~ producer -/// print(property.value) // prints `1` -/// ```` -/// -/// ```` -/// let property = MutableProperty(0) -/// let producer = SignalProducer({ /* do some work after some time */ }) -/// let disposable = (property <~ producer) -/// ... -/// // Terminates binding before property dealloc or -/// // signal's `completed` event. -/// disposable.dispose() -/// ```` -/// -/// - note: The binding will automatically terminate when the property is -/// deinitialized, or when the created producer sends a `completed` -/// event. -/// -/// - parameters: -/// - property: A property to bind to. -/// - producer: A producer to bind. -/// -/// - returns: A disposable that can be used to terminate binding before the -/// deinitialization of property or producer's `completed` event. -@discardableResult -public func <~ (property: P, producer: SignalProducer) -> Disposable { - let disposable = CompositeDisposable() - - producer - .on(completed: { disposable.dispose() }) - .startWithSignal { signal, signalDisposable in - disposable += property <~ signal - disposable += signalDisposable - - disposable += property.producer.startWithCompleted { - disposable.dispose() - } - } - - return disposable -} - -/// Binds a signal to a property, updating the property's value to the latest -/// value sent by the signal. -/// -/// - note: The binding will automatically terminate when the property is -/// deinitialized, or when the signal sends a `completed` event. -/// -/// ```` -/// let property = MutableProperty(0) -/// let signal = Signal({ /* do some work after some time */ }) -/// property <~ signal -/// ```` -/// -/// ```` -/// let property = MutableProperty(0) -/// let signal = Signal({ /* do some work after some time */ }) -/// let disposable = property <~ signal -/// ... -/// // Terminates binding before property dealloc or signal's -/// // `completed` event. -/// disposable.dispose() -/// ```` -/// -/// - parameters: -/// - property: A property to bind to. -/// - signal: A signal to bind. -/// -/// - returns: A disposable that can be used to terminate binding before the -/// deinitialization of property or signal's `completed` event. -@discardableResult -public func <~ (property: P, signal: S) -> Disposable { - return property <~ signal.optionalize() -} - -/// Creates a signal from the given producer, which will be immediately bound to -/// the given property, updating the property's value to the latest value sent -/// by the signal. -/// -/// ```` -/// let property = MutableProperty(0) -/// let producer = SignalProducer(value: 1) -/// property <~ producer -/// print(property.value) // prints `1` -/// ```` -/// -/// ```` -/// let property = MutableProperty(0) -/// let producer = SignalProducer({ /* do some work after some time */ }) -/// let disposable = (property <~ producer) -/// ... -/// // Terminates binding before property dealloc or -/// // signal's `completed` event. -/// disposable.dispose() -/// ```` -/// -/// - note: The binding will automatically terminate when the property is -/// deinitialized, or when the created producer sends a `completed` -/// event. -/// -/// - parameters: -/// - property: A property to bind to. -/// - producer: A producer to bind. -/// -/// - returns: A disposable that can be used to terminate binding before the -/// deinitialization of property or producer's `completed` event. -@discardableResult -public func <~ (property: P, producer: S) -> Disposable { - return property <~ producer.optionalize() -} - -/// Binds `destinationProperty` to the latest values of `sourceProperty`. -/// -/// ```` -/// let dstProperty = MutableProperty(0) -/// let srcProperty = ConstantProperty(10) -/// dstProperty <~ srcProperty -/// print(dstProperty.value) // prints 10 -/// ```` -/// -/// ```` -/// let dstProperty = MutableProperty(0) -/// let srcProperty = ConstantProperty(10) -/// let disposable = (dstProperty <~ srcProperty) -/// ... -/// disposable.dispose() // terminate the binding earlier if -/// // needed -/// ```` -/// -/// - note: The binding will automatically terminate when either property is -/// deinitialized. -/// -/// - parameters: -/// - destinationProperty: A property to bind to. -/// - sourceProperty: A property to bind. -/// -/// - returns: A disposable that can be used to terminate binding before the -/// deinitialization of destination property or source property -/// producer's `completed` event. -@discardableResult -public func <~ (destinationProperty: Destination, sourceProperty: Source) -> Disposable { - return destinationProperty <~ sourceProperty.producer -} - -/// Binds `destinationProperty` to the latest values of `sourceProperty`. -/// -/// ```` -/// let dstProperty = MutableProperty(0) -/// let srcProperty = ConstantProperty(10) -/// dstProperty <~ srcProperty -/// print(dstProperty.value) // prints 10 -/// ```` -/// -/// ```` -/// let dstProperty = MutableProperty(0) -/// let srcProperty = ConstantProperty(10) -/// let disposable = (dstProperty <~ srcProperty) -/// ... -/// disposable.dispose() // terminate the binding earlier if -/// // needed -/// ```` -/// -/// - note: The binding will automatically terminate when either property is -/// deinitialized. -/// -/// - parameters: -/// - destinationProperty: A property to bind to. -/// - sourceProperty: A property to bind. -/// -/// - returns: A disposable that can be used to terminate binding before the -/// deinitialization of destination property or source property -/// producer's `completed` event. -@discardableResult -public func <~ (destinationProperty: Destination, sourceProperty: Source) -> Disposable { - return destinationProperty <~ sourceProperty.producer -} diff --git a/ReactiveCocoa/Swift/Scheduler.swift b/ReactiveCocoa/Swift/Scheduler.swift deleted file mode 100644 index c4bd14d0c5..0000000000 --- a/ReactiveCocoa/Swift/Scheduler.swift +++ /dev/null @@ -1,493 +0,0 @@ -// -// Scheduler.swift -// ReactiveCocoa -// -// Created by Justin Spahr-Summers on 2014-06-02. -// Copyright (c) 2014 GitHub. All rights reserved. -// - -import Foundation - -/// Represents a serial queue of work items. -public protocol SchedulerProtocol { - /// Enqueues an action on the scheduler. - /// - /// When the work is executed depends on the scheduler in use. - /// - /// - returns: Optional `Disposable` that can be used to cancel the work - /// before it begins. - @discardableResult - func schedule(_ action: () -> Void) -> Disposable? -} - -/// A particular kind of scheduler that supports enqueuing actions at future -/// dates. -public protocol DateSchedulerProtocol: SchedulerProtocol { - /// The current date, as determined by this scheduler. - /// - /// This can be implemented to deterministically return a known date (e.g., - /// for testing purposes). - var currentDate: Date { get } - - /// Schedules an action for execution at or after the given date. - /// - /// - parameters: - /// - date: Starting time. - /// - action: Closure of the action to perform. - /// - /// - returns: Optional `Disposable` that can be used to cancel the work - /// before it begins. - @discardableResult - func schedule(after date: Date, action: () -> Void) -> Disposable? - - /// Schedules a recurring action at the given interval, beginning at the - /// given date. - /// - /// - parameters: - /// - date: Starting time. - /// - repeatingEvery: Repetition interval. - /// - withLeeway: Some delta for repetition. - /// - action: Closure of the action to perform. - /// - /// - returns: Optional `Disposable` that can be used to cancel the work - /// before it begins. - @discardableResult - func schedule(after date: Date, interval: TimeInterval, leeway: TimeInterval, action: () -> Void) -> Disposable? -} - -/// A scheduler that performs all work synchronously. -public final class ImmediateScheduler: SchedulerProtocol { - public init() {} - - /// Immediately calls passed in `action`. - /// - /// - parameters: - /// - action: Closure of the action to perform. - /// - /// - returns: `nil`. - @discardableResult - public func schedule(_ action: () -> Void) -> Disposable? { - action() - return nil - } -} - -/// A scheduler that performs all work on the main queue, as soon as possible. -/// -/// If the caller is already running on the main queue when an action is -/// scheduled, it may be run synchronously. However, ordering between actions -/// will always be preserved. -public final class UIScheduler: SchedulerProtocol { - private static let dispatchSpecificKey = DispatchSpecificKey() - private static let dispatchSpecificValue = UInt8.max - private static var __once: () = { - DispatchQueue.main.setSpecific(key: UIScheduler.dispatchSpecificKey, - value: dispatchSpecificValue) - }() - - private var queueLength: Int32 = 0 - - /// Initializes `UIScheduler` - public init() { - /// This call is to ensure the main queue has been setup appropriately - /// for `UIScheduler`. It is only called once during the application - /// lifetime, since Swift has a `dispatch_once` like mechanism to - /// lazily initialize global variables and static variables. - _ = UIScheduler.__once - } - - /// Queues an action to be performed on main queue. If the action is called - /// on the main thread and no work is queued, no scheduling takes place and - /// the action is called instantly. - /// - /// - parameters: - /// - action: Closure of the action to perform on the main thread. - /// - /// - returns: `Disposable` that can be used to cancel the work before it - /// begins. - @discardableResult - public func schedule(_ action: () -> Void) -> Disposable? { - let disposable = SimpleDisposable() - let actionAndDecrement = { - if !disposable.isDisposed { - action() - } - - OSAtomicDecrement32(&self.queueLength) - } - - let queued = OSAtomicIncrement32(&queueLength) - - // If we're already running on the main queue, and there isn't work - // already enqueued, we can skip scheduling and just execute directly. - if queued == 1 && DispatchQueue.getSpecific(key: UIScheduler.dispatchSpecificKey) == UIScheduler.dispatchSpecificValue { - actionAndDecrement() - } else { - DispatchQueue.main.async(execute: actionAndDecrement) - } - - return disposable - } -} - -/// A scheduler backed by a serial GCD queue. -public final class QueueScheduler: DateSchedulerProtocol { - /// A singleton `QueueScheduler` that always targets the main thread's GCD - /// queue. - /// - /// - note: Unlike `UIScheduler`, this scheduler supports scheduling for a - /// future date, and will always schedule asynchronously (even if - /// already running on the main thread). - public static let main = QueueScheduler(internalQueue: DispatchQueue.main) - - public var currentDate: Date { - return Date() - } - - internal let queue: DispatchQueue - - internal init(internalQueue: DispatchQueue) { - queue = internalQueue - } - - /// Initializes a scheduler that will target the given queue with its - /// work. - /// - /// - note: Even if the queue is concurrent, all work items enqueued with - /// the `QueueScheduler` will be serial with respect to each other. - /// - /// - warning: Obsoleted in OS X 10.11 - @available(OSX, deprecated:10.10, obsoleted:10.11, message:"Use init(qos:, name:) instead") - @available(iOS, deprecated:8.0, obsoleted:9.0, message:"Use init(qos:, name:) instead.") - public convenience init(queue: DispatchQueue, name: String = "org.reactivecocoa.ReactiveCocoa.QueueScheduler") { - self.init(internalQueue: DispatchQueue(label: name, attributes: [], target: queue)) - } - - /// Initializes a scheduler that will target a new serial queue with the - /// given quality of service class. - /// - /// - parameters: - /// - qos: Dispatch queue's QoS value. - /// - name: Name for the queue in the form of reverse domain. - @available(OSX 10.10, *) - public convenience init( - qos: DispatchQoS = .default, - name: String = "org.reactivecocoa.ReactiveCocoa.QueueScheduler" - ) { - self.init(internalQueue: DispatchQueue( - label: name, - qos: qos - )) - } - - /// Schedules action for dispatch on internal queue - /// - /// - parameters: - /// - action: Closure of the action to schedule. - /// - /// - returns: `Disposable` that can be used to cancel the work before it - /// begins. - @discardableResult - public func schedule(_ action: () -> Void) -> Disposable? { - let d = SimpleDisposable() - - queue.async { - if !d.isDisposed { - action() - } - } - - return d - } - - private func wallTime(with date: Date) -> DispatchWallTime { - let (seconds, frac) = modf(date.timeIntervalSince1970) - - let nsec: Double = frac * Double(NSEC_PER_SEC) - let walltime = timespec(tv_sec: Int(seconds), tv_nsec: Int(nsec)) - - return DispatchWallTime(timespec: walltime) - } - - /// Schedules an action for execution at or after the given date. - /// - /// - parameters: - /// - date: Starting time. - /// - action: Closure of the action to perform. - /// - /// - returns: Optional `Disposable` that can be used to cancel the work - /// before it begins. - @discardableResult - public func schedule(after date: Date, action: () -> Void) -> Disposable? { - let d = SimpleDisposable() - - queue.asyncAfter(wallDeadline: wallTime(with: date)) { - if !d.isDisposed { - action() - } - } - - return d - } - - /// Schedules a recurring action at the given interval and beginning at the - /// given start time. A reasonable default timer interval leeway is - /// provided. - /// - /// - parameters: - /// - date: Date to schedule the first action for. - /// - repeatingEvery: Repetition interval. - /// - action: Closure of the action to repeat. - /// - /// - returns: Optional disposable that can be used to cancel the work - /// before it begins. - @discardableResult - public func schedule(after date: Date, interval: TimeInterval, action: () -> Void) -> Disposable? { - // Apple's "Power Efficiency Guide for Mac Apps" recommends a leeway of - // at least 10% of the timer interval. - return schedule(after: date, interval: interval, leeway: interval * 0.1, action: action) - } - - /// Schedules a recurring action at the given interval with provided leeway, - /// beginning at the given start time. - /// - /// - parameters: - /// - date: Date to schedule the first action for. - /// - repeatingEvery: Repetition interval. - /// - leeway: Some delta for repetition interval. - /// - action: Closure of the action to repeat. - /// - /// - returns: Optional `Disposable` that can be used to cancel the work - /// before it begins. - @discardableResult - public func schedule(after date: Date, interval: TimeInterval, leeway: TimeInterval, action: () -> Void) -> Disposable? { - precondition(interval >= 0) - precondition(leeway >= 0) - - let nsecInterval = interval * Double(NSEC_PER_SEC) - let nsecLeeway = leeway * Double(NSEC_PER_SEC) - - let timer = DispatchSource.makeTimerSource( - flags: DispatchSource.TimerFlags(rawValue: UInt(0)), - queue: queue - ) - timer.scheduleRepeating(wallDeadline: wallTime(with: date), - interval: .nanoseconds(Int(nsecInterval)), - leeway: .nanoseconds(Int(nsecLeeway))) - timer.setEventHandler(handler: action) - timer.resume() - - return ActionDisposable { - timer.cancel() - } - } -} - -/// A scheduler that implements virtualized time, for use in testing. -public final class TestScheduler: DateSchedulerProtocol { - private final class ScheduledAction { - let date: Date - let action: () -> Void - - init(date: Date, action: () -> Void) { - self.date = date - self.action = action - } - - func less(_ rhs: ScheduledAction) -> Bool { - return date.compare(rhs.date) == .orderedAscending - } - } - - private let lock = NSRecursiveLock() - private var _currentDate: Date - - /// The virtual date that the scheduler is currently at. - public var currentDate: Date { - let d: Date - - lock.lock() - d = _currentDate - lock.unlock() - - return d - } - - private var scheduledActions: [ScheduledAction] = [] - - /// Initializes a TestScheduler with the given start date. - /// - /// - parameters: - /// - startDate: The start date of the scheduler. - public init(startDate: Date = Date(timeIntervalSinceReferenceDate: 0)) { - lock.name = "org.reactivecocoa.ReactiveCocoa.TestScheduler" - _currentDate = startDate - } - - private func schedule(_ action: ScheduledAction) -> Disposable { - lock.lock() - scheduledActions.append(action) - scheduledActions.sort { $0.less($1) } - lock.unlock() - - return ActionDisposable { - self.lock.lock() - self.scheduledActions = self.scheduledActions.filter { $0 !== action } - self.lock.unlock() - } - } - - /// Enqueues an action on the scheduler. - /// - /// - note: The work is executed on `currentDate` as it is understood by the - /// scheduler. - /// - /// - parameters: - /// - action: An action that will be performed on scheduler's - /// `currentDate`. - /// - /// - returns: Optional `Disposable` that can be used to cancel the work - /// before it begins. - @discardableResult - public func schedule(_ action: () -> Void) -> Disposable? { - return schedule(ScheduledAction(date: currentDate, action: action)) - } - - /// Schedules an action for execution at or after the given date. - /// - /// - parameters: - /// - date: Starting date. - /// - action: Closure of the action to perform. - /// - /// - returns: Optional disposable that can be used to cancel the work - /// before it begins. - @discardableResult - public func schedule(after delay: TimeInterval, action: () -> Void) -> Disposable? { - return schedule(after: currentDate.addingTimeInterval(delay), action: action) - } - - @discardableResult - public func schedule(after date: Date, action: () -> Void) -> Disposable? { - return schedule(ScheduledAction(date: date, action: action)) - } - - /// Schedules a recurring action at the given interval, beginning at the - /// given start time - /// - /// - parameters: - /// - date: Date to schedule the first action for. - /// - repeatingEvery: Repetition interval. - /// - action: Closure of the action to repeat. - /// - /// - returns: Optional `Disposable` that can be used to cancel the work - /// before it begins. - private func schedule(after date: Date, interval: TimeInterval, disposable: SerialDisposable, action: () -> Void) { - precondition(interval >= 0) - - disposable.innerDisposable = schedule(after: date) { [unowned self] in - action() - self.schedule(after: date.addingTimeInterval(interval), interval: interval, disposable: disposable, action: action) - } - } - - /// Schedules a recurring action at the given interval, beginning at the - /// given interval (counted from `currentDate`). - /// - /// - parameters: - /// - interval: Interval to add to `currentDate`. - /// - repeatingEvery: Repetition interval. - /// - leeway: Some delta for repetition interval. - /// - action: Closure of the action to repeat. - /// - /// - returns: Optional `Disposable` that can be used to cancel the work - /// before it begins. - @discardableResult - public func schedule(after delay: TimeInterval, interval: TimeInterval, leeway: TimeInterval = 0, action: () -> Void) -> Disposable? { - return schedule(after: currentDate.addingTimeInterval(delay), interval: interval, leeway: leeway, action: action) - } - - /// Schedules a recurring action at the given interval with - /// provided leeway, beginning at the given start time. - /// - /// - parameters: - /// - date: Date to schedule the first action for. - /// - repeatingEvery: Repetition interval. - /// - leeway: Some delta for repetition interval. - /// - action: Closure of the action to repeat. - /// - /// - returns: Optional `Disposable` that can be used to cancel the work - /// before it begins. - public func schedule(after date: Date, interval: TimeInterval, leeway: TimeInterval = 0, action: () -> Void) -> Disposable? { - let disposable = SerialDisposable() - schedule(after: date, interval: interval, disposable: disposable, action: action) - return disposable - } - - /// Advances the virtualized clock by an extremely tiny interval, dequeuing - /// and executing any actions along the way. - /// - /// This is intended to be used as a way to execute actions that have been - /// scheduled to run as soon as possible. - public func advance() { - advance(by: DBL_EPSILON) - } - - /// Advances the virtualized clock by the given interval, dequeuing and - /// executing any actions along the way. - /// - /// - parameters: - /// - interval: Interval by which the current date will be advanced. - public func advance(by interval: TimeInterval) { - lock.lock() - advance(to: currentDate.addingTimeInterval(interval)) - lock.unlock() - } - - /// Advances the virtualized clock to the given future date, dequeuing and - /// executing any actions up until that point. - /// - /// - parameters: - /// - newDate: Future date to which the virtual clock will be advanced. - public func advance(to newDate: Date) { - lock.lock() - - assert(currentDate.compare(newDate) != .orderedDescending) - - while scheduledActions.count > 0 { - if newDate.compare(scheduledActions[0].date) == .orderedAscending { - break - } - - _currentDate = scheduledActions[0].date - - let scheduledAction = scheduledActions.remove(at: 0) - scheduledAction.action() - } - - _currentDate = newDate - - lock.unlock() - } - - /// Dequeues and executes all scheduled actions, leaving the scheduler's - /// date at `NSDate.distantFuture()`. - public func run() { - advance(to: Date.distantFuture) - } - - /// Rewinds the virtualized clock by the given interval. - /// This simulates that user changes device date. - /// - /// - parameters: - /// - interval: Interval by which the current date will be retreated. - public func rewind(by interval: TimeInterval) { - lock.lock() - - let newDate = currentDate.addingTimeInterval(-interval) - assert(currentDate.compare(newDate) != .orderedAscending) - _currentDate = newDate - - lock.unlock() - - } -} diff --git a/ReactiveCocoa/Swift/Signal.swift b/ReactiveCocoa/Swift/Signal.swift deleted file mode 100644 index d2368b6885..0000000000 --- a/ReactiveCocoa/Swift/Signal.swift +++ /dev/null @@ -1,1838 +0,0 @@ -import Foundation -import Result - -/// A push-driven stream that sends Events over time, parameterized by the type -/// of values being sent (`Value`) and the type of failure that can occur -/// (`Error`). If no failures should be possible, NoError can be specified for -/// `Error`. -/// -/// An observer of a Signal will see the exact same sequence of events as all -/// other observers. In other words, events will be sent to all observers at the -/// same time. -/// -/// Signals are generally used to represent event streams that are already “in -/// progress,” like notifications, user input, etc. To represent streams that -/// must first be _started_, see the SignalProducer type. -/// -/// A Signal is kept alive until either of the following happens: -/// 1. its input observer receives a terminating event; or -/// 2. it has no active observers, and is not being retained. -public final class Signal { - public typealias Observer = ReactiveCocoa.Observer - - /// The disposable returned by the signal generator. It would be disposed of - /// when the signal terminates. - private var generatorDisposable: Disposable? - - /// The state of the signal. `nil` if the signal has terminated. - private let state: Atomic?> - - /// Initialize a Signal that will immediately invoke the given generator, - /// then forward events sent to the given observer. - /// - /// - note: The disposable returned from the closure will be automatically - /// disposed if a terminating event is sent to the observer. The - /// Signal itself will remain alive until the observer is released. - /// - /// - parameters: - /// - generator: A closure that accepts an implicitly created observer - /// that will act as an event emitter for the signal. - public init(_ generator: @noescape (Observer) -> Disposable?) { - state = Atomic(SignalState()) - - /// Used to ensure that events are serialized during delivery to observers. - let sendLock = NSLock() - sendLock.name = "org.reactivecocoa.ReactiveCocoa.Signal" - - /// When set to `true`, the Signal should interrupt as soon as possible. - let interrupted = Atomic(false) - - let observer = Observer { [weak self] event in - guard let signal = self else { - return - } - - func interrupt() { - if let state = signal.state.swap(nil) { - for observer in state.observers { - observer.sendInterrupted() - } - } - } - - if case .interrupted = event { - // Normally we disallow recursive events, but `interrupted` is - // kind of a special snowflake, since it can inadvertently be - // sent by downstream consumers. - // - // So we'll flag Interrupted events specially, and if it - // happened to occur while we're sending something else, we'll - // wait to deliver it. - interrupted.value = true - - if sendLock.try() { - interrupt() - sendLock.unlock() - - signal.generatorDisposable?.dispose() - } - } else { - if let state = (event.isTerminating ? signal.state.swap(nil) : signal.state.value) { - sendLock.lock() - - for observer in state.observers { - observer.action(event) - } - - let shouldInterrupt = !event.isTerminating && interrupted.value - if shouldInterrupt { - interrupt() - } - - sendLock.unlock() - - if event.isTerminating || shouldInterrupt { - // Dispose only after notifying observers, so disposal - // logic is consistently the last thing to run. - signal.generatorDisposable?.dispose() - } - } - } - } - - generatorDisposable = generator(observer) - } - - deinit { - if state.swap(nil) != nil { - // As the signal can deinitialize only when it has no observers attached, - // only the generator disposable has to be disposed of at this point. - generatorDisposable?.dispose() - } - } - - /// A Signal that never sends any events to its observers. - public static var never: Signal { - return self.init { _ in nil } - } - - /// A Signal that completes immediately without emitting any value. - public static var empty: Signal { - return self.init { observer in - observer.sendCompleted() - return nil - } - } - - /// Create a Signal that will be controlled by sending events to the given - /// observer. - /// - /// - note: The Signal will remain alive until a terminating event is sent - /// to the observer. - /// - /// - returns: A tuple made of signal and observer. - public static func pipe() -> (Signal, Observer) { - var observer: Observer! - let signal = self.init { innerObserver in - observer = innerObserver - return nil - } - - return (signal, observer) - } - - /// Observe the Signal by sending any future events to the given observer. - /// - /// - note: If the Signal has already terminated, the observer will - /// immediately receive an `interrupted` event. - /// - /// - parameters: - /// - observer: An observer to forward the events to. - /// - /// - returns: An optional `Disposable` which can be used to disconnect the - /// observer. - @discardableResult - public func observe(_ observer: Observer) -> Disposable? { - var token: RemovalToken? - state.modify { - $0?.retainedSignal = self - token = $0?.observers.insert(observer) - } - - if let token = token { - return ActionDisposable { [weak self] in - if let strongSelf = self { - strongSelf.state.modify { state in - state?.observers.remove(using: token) - if state?.observers.isEmpty ?? false { - state!.retainedSignal = nil - } - } - } - } - } else { - observer.sendInterrupted() - return nil - } - } -} - -private struct SignalState { - var observers: Bag.Observer> = Bag() - var retainedSignal: Signal? -} - -public protocol SignalProtocol { - /// The type of values being sent on the signal. - associatedtype Value - - /// The type of error that can occur on the signal. If errors aren't - /// possible then `NoError` can be used. - associatedtype Error: Swift.Error - - /// Extracts a signal from the receiver. - var signal: Signal { get } - - /// Observes the Signal by sending any future events to the given observer. - @discardableResult - func observe(_ observer: Signal.Observer) -> Disposable? -} - -extension Signal: SignalProtocol { - public var signal: Signal { - return self - } -} - -extension SignalProtocol { - /// Convenience override for observe(_:) to allow trailing-closure style - /// invocations. - /// - /// - parameters: - /// - action: A closure that will accept an event of the signal - /// - /// - returns: An optional `Disposable` which can be used to stop the - /// invocation of the callback. Disposing of the Disposable will - /// have no effect on the Signal itself. - @discardableResult - public func observe(_ action: Signal.Observer.Action) -> Disposable? { - return observe(Observer(action)) - } - - /// Observe the `Signal` by invoking the given callback when `next` or - /// `failed` event are received. - /// - /// - parameters: - /// - result: A closure that accepts instance of `Result` - /// enum that contains either a `Success(Value)` or - /// `Failure` case. - /// - /// - returns: An optional `Disposable` which can be used to stop the - /// invocation of the callback. Disposing of the Disposable will - /// have no effect on the Signal itself. - @discardableResult - public func observeResult(_ result: (Result) -> Void) -> Disposable? { - return observe( - Observer( - next: { result(.success($0)) }, - failed: { result(.failure($0)) } - ) - ) - } - - /// Observe the `Signal` by invoking the given callback when a `completed` - /// event is received. - /// - /// - parameters: - /// - completed: A closure that is called when `completed` event is - /// received. - /// - /// - returns: An optional `Disposable` which can be used to stop the - /// invocation of the callback. Disposing of the Disposable will - /// have no effect on the Signal itself. - @discardableResult - public func observeCompleted(_ completed: () -> Void) -> Disposable? { - return observe(Observer(completed: completed)) - } - - /// Observe the `Signal` by invoking the given callback when a `failed` - /// event is received. - /// - /// - parameters: - /// - error: A closure that is called when failed event is received. It - /// accepts an error parameter. - /// - /// Returns a Disposable which can be used to stop the invocation of the - /// callback. Disposing of the Disposable will have no effect on the Signal - /// itself. - @discardableResult - public func observeFailed(_ error: (Error) -> Void) -> Disposable? { - return observe(Observer(failed: error)) - } - - /// Observe the `Signal` by invoking the given callback when an - /// `interrupted` event is received. If the Signal has already terminated, - /// the callback will be invoked immediately. - /// - /// - parameters: - /// - interrupted: A closure that is invoked when `interrupted` event is - /// received - /// - /// - returns: An optional `Disposable` which can be used to stop the - /// invocation of the callback. Disposing of the Disposable will - /// have no effect on the Signal itself. - @discardableResult - public func observeInterrupted(_ interrupted: () -> Void) -> Disposable? { - return observe(Observer(interrupted: interrupted)) - } -} - -extension SignalProtocol where Error == NoError { - /// Observe the Signal by invoking the given callback when `next` events are - /// received. - /// - /// - parameters: - /// - next: A closure that accepts a value when `next` event is received. - /// - /// - returns: An optional `Disposable` which can be used to stop the - /// invocation of the callback. Disposing of the Disposable will - /// have no effect on the Signal itself. - @discardableResult - public func observeNext(_ next: (Value) -> Void) -> Disposable? { - return observe(Observer(next: next)) - } -} - -extension SignalProtocol { - /// Map each value in the signal to a new value. - /// - /// - parameters: - /// - transform: A closure that accepts a value from the `next` event and - /// returns a new value. - /// - /// - returns: A signal that will send new values. - public func map(_ transform: (Value) -> U) -> Signal { - return Signal { observer in - return self.observe { event in - observer.action(event.map(transform)) - } - } - } - - /// Map errors in the signal to a new error. - /// - /// - parameters: - /// - transform: A closure that accepts current error object and returns - /// a new type of error object. - /// - /// - returns: A signal that will send new type of errors. - public func mapError(_ transform: (Error) -> F) -> Signal { - return Signal { observer in - return self.observe { event in - observer.action(event.mapError(transform)) - } - } - } - - /// Preserve only the values of the signal that pass the given predicate. - /// - /// - parameters: - /// - predicate: A closure that accepts value and returns `Bool` denoting - /// whether value has passed the test. - /// - /// - returns: A signal that will send only the values passing the given - /// predicate. - public func filter(_ predicate: (Value) -> Bool) -> Signal { - return Signal { observer in - return self.observe { (event: Event) -> Void in - guard let value = event.value else { - observer.action(event) - return - } - - if predicate(value) { - observer.sendNext(value) - } - } - } - } -} - -extension SignalProtocol where Value: OptionalProtocol { - /// Unwrap non-`nil` values and forward them on the returned signal, `nil` - /// values are dropped. - /// - /// - returns: A signal that sends only non-nil values. - public func skipNil() -> Signal { - return filter { $0.optional != nil }.map { $0.optional! } - } -} - -extension SignalProtocol { - /// Take up to `n` values from the signal and then complete. - /// - /// - precondition: `count` must be non-negative number. - /// - /// - parameters: - /// - count: A number of values to take from the signal. - /// - /// - returns: A signal that will yield the first `count` values from `self` - public func take(first count: Int) -> Signal { - precondition(count >= 0) - - return Signal { observer in - if count == 0 { - observer.sendCompleted() - return nil - } - - var taken = 0 - - return self.observe { event in - guard let value = event.value else { - observer.action(event) - return - } - - if taken < count { - taken += 1 - observer.sendNext(value) - } - - if taken == count { - observer.sendCompleted() - } - } - } - } -} - -/// A reference type which wraps an array to auxiliate the collection of values -/// for `collect` operator. -private final class CollectState { - var values: [Value] = [] - - /// Collects a new value. - func append(_ value: Value) { - values.append(value) - } - - /// Check if there are any items remaining. - /// - /// - note: This method also checks if there weren't collected any values - /// and, in that case, it means an empty array should be sent as the - /// result of collect. - var isEmpty: Bool { - /// We use capacity being zero to determine if we haven't collected any - /// value since we're keeping the capacity of the array to avoid - /// unnecessary and expensive allocations). This also guarantees - /// retro-compatibility around the original `collect()` operator. - return values.isEmpty && values.capacity > 0 - } - - /// Removes all values previously collected if any. - func flush() { - // Minor optimization to avoid consecutive allocations. Can - // be useful for sequences of regular or similar size and to - // track if any value was ever collected. - values.removeAll(keepingCapacity: true) - } -} - -extension SignalProtocol { - /// Collect all values sent by the signal then forward them as a single - /// array and complete. - /// - /// - note: When `self` completes without collecting any value, it will send - /// an empty array of values. - /// - /// - returns: A signal that will yield an array of values when `self` - /// completes. - public func collect() -> Signal<[Value], Error> { - return collect { _,_ in false } - } - - /// Collect at most `count` values from `self`, forward them as a single - /// array and complete. - /// - /// - note: When the count is reached the array is sent and the signal - /// starts over yielding a new array of values. - /// - /// - note: When `self` completes any remaining values will be sent, the - /// last array may not have `count` values. Alternatively, if were - /// not collected any values will sent an empty array of values. - /// - /// - precondition: `count` should be greater than zero. - /// - public func collect(count: Int) -> Signal<[Value], Error> { - precondition(count > 0) - return collect { values in values.count == count } - } - - /// Collect values that pass the given predicate then forward them as a - /// single array and complete. - /// - /// - note: When `self` completes any remaining values will be sent, the - /// last array may not match `predicate`. Alternatively, if were not - /// collected any values will sent an empty array of values. - /// - /// ```` - /// let (signal, observer) = Signal.pipe() - /// - /// signal - /// .collect { values in values.reduce(0, combine: +) == 8 } - /// .observeNext { print($0) } - /// - /// observer.sendNext(1) - /// observer.sendNext(3) - /// observer.sendNext(4) - /// observer.sendNext(7) - /// observer.sendNext(1) - /// observer.sendNext(5) - /// observer.sendNext(6) - /// observer.sendCompleted() - /// - /// // Output: - /// // [1, 3, 4] - /// // [7, 1] - /// // [5, 6] - /// ```` - /// - /// - parameters: - /// - predicate: Predicate to match when values should be sent (returning - /// `true`) or alternatively when they should be collected - /// (where it should return `false`). The most recent value - /// (`next`) is included in `values` and will be the end of - /// the current array of values if the predicate returns - /// `true`. - /// - /// - returns: A signal that collects values passing the predicate and, when - /// `self` completes, forwards them as a single array and - /// complets. - public func collect(_ predicate: (values: [Value]) -> Bool) -> Signal<[Value], Error> { - return Signal { observer in - let state = CollectState() - - return self.observe { event in - switch event { - case let .next(value): - state.append(value) - if predicate(values: state.values) { - observer.sendNext(state.values) - state.flush() - } - case .completed: - if !state.isEmpty { - observer.sendNext(state.values) - } - observer.sendCompleted() - case let .failed(error): - observer.sendFailed(error) - case .interrupted: - observer.sendInterrupted() - } - } - } - } - - /// Repeatedly collect an array of values up to a matching `next` value. - /// Then forward them as single array and wait for next events. - /// - /// - note: When `self` completes any remaining values will be sent, the - /// last array may not match `predicate`. Alternatively, if no - /// values were collected an empty array will be sent. - /// - /// ```` - /// let (signal, observer) = Signal.pipe() - /// - /// signal - /// .collect { values, next in next == 7 } - /// .observeNext { print($0) } - /// - /// observer.sendNext(1) - /// observer.sendNext(1) - /// observer.sendNext(7) - /// observer.sendNext(7) - /// observer.sendNext(5) - /// observer.sendNext(6) - /// observer.sendCompleted() - /// - /// // Output: - /// // [1, 1] - /// // [7] - /// // [7, 5, 6] - /// ```` - /// - /// - parameters: - /// - predicate: Predicate to match when values should be sent (returning - /// `true`) or alternatively when they should be collected - /// (where it should return `false`). The most recent value - /// (`next`) is not included in `values` and will be the - /// start of the next array of values if the predicate - /// returns `true`. - /// - /// - returns: A signal that will yield an array of values based on a - /// predicate which matches the values collected and the next - /// value. - public func collect(_ predicate: (values: [Value], next: Value) -> Bool) -> Signal<[Value], Error> { - return Signal { observer in - let state = CollectState() - - return self.observe { event in - switch event { - case let .next(value): - if predicate(values: state.values, next: value) { - observer.sendNext(state.values) - state.flush() - } - state.append(value) - case .completed: - if !state.isEmpty { - observer.sendNext(state.values) - } - observer.sendCompleted() - case let .failed(error): - observer.sendFailed(error) - case .interrupted: - observer.sendInterrupted() - } - } - } - } - - /// Forward all events onto the given scheduler, instead of whichever - /// scheduler they originally arrived upon. - /// - /// - parameters: - /// - scheduler: A scheduler to deliver events on. - /// - /// - returns: A signal that will yield `self` values on provided scheduler. - public func observe(on scheduler: SchedulerProtocol) -> Signal { - return Signal { observer in - return self.observe { event in - scheduler.schedule { - observer.action(event) - } - } - } - } -} - -private final class CombineLatestState { - var latestValue: Value? - var isCompleted = false -} - -extension SignalProtocol { - private func observeWithStates(_ signalState: CombineLatestState, _ otherState: CombineLatestState, _ lock: NSLock, _ observer: Signal<(), Error>.Observer) -> Disposable? { - return self.observe { event in - switch event { - case let .next(value): - lock.lock() - - signalState.latestValue = value - if otherState.latestValue != nil { - observer.sendNext() - } - - lock.unlock() - - case let .failed(error): - observer.sendFailed(error) - - case .completed: - lock.lock() - - signalState.isCompleted = true - if otherState.isCompleted { - observer.sendCompleted() - } - - lock.unlock() - - case .interrupted: - observer.sendInterrupted() - } - } - } - - /// Combine the latest value of the receiver with the latest value from the - /// given signal. - /// - /// - note: The returned signal will not send a value until both inputs have - /// sent at least one value each. - /// - /// - note: If either signal is interrupted, the returned signal will also - /// be interrupted. - /// - /// - parameters: - /// - otherSignal: A signal to combine `self`'s value with. - /// - /// - returns: A signal that will yield a tuple containing values of `self` - /// and given signal. - public func combineLatest(with other: Signal) -> Signal<(Value, U), Error> { - return Signal { observer in - let lock = NSLock() - lock.name = "org.reactivecocoa.ReactiveCocoa.combineLatestWith" - - let signalState = CombineLatestState() - let otherState = CombineLatestState() - - let onBothNext = { - observer.sendNext((signalState.latestValue!, otherState.latestValue!)) - } - - let observer = Signal<(), Error>.Observer(next: onBothNext, failed: observer.sendFailed, completed: observer.sendCompleted, interrupted: observer.sendInterrupted) - - let disposable = CompositeDisposable() - disposable += self.observeWithStates(signalState, otherState, lock, observer) - disposable += other.observeWithStates(otherState, signalState, lock, observer) - - return disposable - } - } - - /// Delay `next` and `completed` events by the given interval, forwarding - /// them on the given scheduler. - /// - /// - note: failed and `interrupted` events are always scheduled - /// immediately. - /// - /// - parameters: - /// - interval: Interval to delay `next` and `completed` events by. - /// - scheduler: A scheduler to deliver delayed events on. - /// - /// - returns: A signal that will delay `next` and `completed` events and - /// will yield them on given scheduler. - public func delay(_ interval: TimeInterval, on scheduler: DateSchedulerProtocol) -> Signal { - precondition(interval >= 0) - - return Signal { observer in - return self.observe { event in - switch event { - case .failed, .interrupted: - scheduler.schedule { - observer.action(event) - } - - case .next, .completed: - let date = scheduler.currentDate.addingTimeInterval(interval) - scheduler.schedule(after: date) { - observer.action(event) - } - } - } - } - } - - /// Skip first `count` number of values then act as usual. - /// - /// - parameters: - /// - count: A number of values to skip. - /// - /// - returns: A signal that will skip the first `count` values, then - /// forward everything afterward. - public func skip(first count: Int) -> Signal { - precondition(count >= 0) - - if count == 0 { - return signal - } - - return Signal { observer in - var skipped = 0 - - return self.observe { event in - if case .next = event, skipped < count { - skipped += 1 - } else { - observer.action(event) - } - } - } - } - - /// Treat all Events from `self` as plain values, allowing them to be - /// manipulated just like any other value. - /// - /// In other words, this brings Events “into the monad”. - /// - /// - note: When a Completed or Failed event is received, the resulting - /// signal will send the Event itself and then complete. When an - /// Interrupted event is received, the resulting signal will send - /// the Event itself and then interrupt. - /// - /// - returns: A signal that sends events as its values. - public func materialize() -> Signal, NoError> { - return Signal { observer in - return self.observe { event in - observer.sendNext(event) - - switch event { - case .interrupted: - observer.sendInterrupted() - - case .completed, .failed: - observer.sendCompleted() - - case .next: - break - } - } - } - } -} - -extension SignalProtocol where Value: EventProtocol, Error == NoError { - /// Translate a signal of `Event` _values_ into a signal of those events - /// themselves. - /// - /// - returns: A signal that sends values carried by `self` events. - public func dematerialize() -> Signal { - return Signal { observer in - return self.observe { event in - switch event { - case let .next(innerEvent): - observer.action(innerEvent.event) - - case .failed: - fatalError("NoError is impossible to construct") - - case .completed: - observer.sendCompleted() - - case .interrupted: - observer.sendInterrupted() - } - } - } - } -} - -extension SignalProtocol { - /// Inject side effects to be performed upon the specified signal events. - /// - /// - parameters: - /// - event: A closure that accepts an event and is invoked on every - /// received event. - /// - next: A closure that accepts a value from `next` event. - /// - failed: A closure that accepts error object and is invoked for - /// failed event. - /// - completed: A closure that is invoked for `completed` event. - /// - interrupted: A closure that is invoked for `interrupted` event. - /// - terminated: A closure that is invoked for any terminating event. - /// - disposed: A closure added as disposable when signal completes. - /// - /// - returns: A signal with attached side-effects for given event cases. - public func on( - event: ((Event) -> Void)? = nil, - failed: ((Error) -> Void)? = nil, - completed: (() -> Void)? = nil, - interrupted: (() -> Void)? = nil, - terminated: (() -> Void)? = nil, - disposed: (() -> Void)? = nil, - next: ((Value) -> Void)? = nil - ) -> Signal { - return Signal { observer in - let disposable = CompositeDisposable() - - _ = disposed.map(disposable.add) - - disposable += signal.observe { receivedEvent in - event?(receivedEvent) - - switch receivedEvent { - case let .next(value): - next?(value) - - case let .failed(error): - failed?(error) - - case .completed: - completed?() - - case .interrupted: - interrupted?() - } - - if receivedEvent.isTerminating { - terminated?() - } - - observer.action(receivedEvent) - } - - return disposable - } - } -} - -private struct SampleState { - var latestValue: Value? = nil - var isSignalCompleted: Bool = false - var isSamplerCompleted: Bool = false -} - -extension SignalProtocol { - /// Forward the latest value from `self` with the value from `sampler` as a - /// tuple, only when`sampler` sends a `next` event. - /// - /// - note: If `sampler` fires before a value has been observed on `self`, - /// nothing happens. - /// - /// - parameters: - /// - sampler: A signal that will trigger the delivery of `next` event - /// from `self`. - /// - /// - returns: A signal that will send values from `self` and `sampler`, - /// sampled (possibly multiple times) by `sampler`, then complete - /// once both input signals have completed, or interrupt if - /// either input signal is interrupted. - public func sample(with sampler: Signal) -> Signal<(Value, T), Error> { - return Signal { observer in - let state = Atomic(SampleState()) - let disposable = CompositeDisposable() - - disposable += self.observe { event in - switch event { - case let .next(value): - state.modify { - $0.latestValue = value - } - - case let .failed(error): - observer.sendFailed(error) - - case .completed: - let shouldComplete: Bool = state.modify { - $0.isSignalCompleted = true - return $0.isSamplerCompleted - } - - if shouldComplete { - observer.sendCompleted() - } - - case .interrupted: - observer.sendInterrupted() - } - } - - disposable += sampler.observe { event in - switch event { - case .next(let samplerValue): - if let value = state.value.latestValue { - observer.sendNext((value, samplerValue)) - } - - case .completed: - let shouldComplete: Bool = state.modify { - $0.isSamplerCompleted = true - return $0.isSignalCompleted - } - - if shouldComplete { - observer.sendCompleted() - } - - case .interrupted: - observer.sendInterrupted() - - case .failed: - break - } - } - - return disposable - } - } - - /// Forward the latest value from `self` whenever `sampler` sends a `next` - /// event. - /// - /// - note: If `sampler` fires before a value has been observed on `self`, - /// nothing happens. - /// - /// - parameters: - /// - sampler: A signal that will trigger the delivery of `next` event - /// from `self`. - /// - /// - returns: A signal that will send values from `self`, sampled (possibly - /// multiple times) by `sampler`, then complete once both input - /// signals have completed, or interrupt if either input signal - /// is interrupted. - public func sample(on sampler: Signal<(), NoError>) -> Signal { - return sample(with: sampler) - .map { $0.0 } - } - - /// Forwards events from `self` until `lifetime` ends, at which point the - /// returned signal will complete. - /// - /// - parameters: - /// - lifetime: A lifetime whose `ended` signal will cause the returned - /// signal to complete. - /// - /// - returns: A signal that will deliver events until `lifetime` ends. - public func take(during lifetime: Lifetime) -> Signal { - return take(until: lifetime.ended) - } - - /// Forward events from `self` until `trigger` sends a `next` or - /// `completed` event, at which point the returned signal will complete. - /// - /// - parameters: - /// - trigger: A signal whose `next` or `completed` events will stop the - /// delivery of `next` events from `self`. - /// - /// - returns: A signal that will deliver events until `trigger` sends - /// `next` or `completed` events. - public func take(until trigger: Signal<(), NoError>) -> Signal { - return Signal { observer in - let disposable = CompositeDisposable() - disposable += self.observe(observer) - - disposable += trigger.observe { event in - switch event { - case .next, .completed: - observer.sendCompleted() - - case .failed, .interrupted: - break - } - } - - return disposable - } - } - - /// Do not forward any values from `self` until `trigger` sends a `next` or - /// `completed` event, at which point the returned signal behaves exactly - /// like `signal`. - /// - /// - parameters: - /// - trigger: A signal whose `next` or `completed` events will start the - /// deliver of events on `self`. - /// - /// - returns: A signal that will deliver events once the `trigger` sends - /// `next` or `completed` events. - public func skip(until trigger: Signal<(), NoError>) -> Signal { - return Signal { observer in - let disposable = SerialDisposable() - - disposable.innerDisposable = trigger.observe { event in - switch event { - case .next, .completed: - disposable.innerDisposable = self.observe(observer) - - case .failed, .interrupted: - break - } - } - - return disposable - } - } - - /// Forward events from `self` with history: values of the returned signal - /// are a tuples whose first member is the previous value and whose second member - /// is the current value. `initial` is supplied as the first member when `self` - /// sends its first value. - /// - /// - parameters: - /// - initial: A value that will be combined with the first value sent by - /// `self`. - /// - /// - returns: A signal that sends tuples that contain previous and current - /// sent values of `self`. - public func combinePrevious(_ initial: Value) -> Signal<(Value, Value), Error> { - return scan((initial, initial)) { previousCombinedValues, newValue in - return (previousCombinedValues.1, newValue) - } - } - - - /// Send only the final value and then immediately completes. - /// - /// - parameters: - /// - initial: Initial value for the accumulator. - /// - combine: A closure that accepts accumulator and sent value of - /// `self`. - /// - /// - returns: A signal that sends accumulated value after `self` completes. - public func reduce(_ initial: U, _ combine: (U, Value) -> U) -> Signal { - // We need to handle the special case in which `signal` sends no values. - // We'll do that by sending `initial` on the output signal (before - // taking the last value). - let (scannedSignalWithInitialValue, outputSignalObserver) = Signal.pipe() - let outputSignal = scannedSignalWithInitialValue.take(last: 1) - - // Now that we've got takeLast() listening to the piped signal, send - // that initial value. - outputSignalObserver.sendNext(initial) - - // Pipe the scanned input signal into the output signal. - scan(initial, combine).observe(outputSignalObserver) - - return outputSignal - } - - /// Aggregate values into a single combined value. When `self` emits its - /// first value, `combine` is invoked with `initial` as the first argument - /// and that emitted value as the second argument. The result is emitted - /// from the signal returned from `scan`. That result is then passed to - /// `combine` as the first argument when the next value is emitted, and so - /// on. - /// - /// - parameters: - /// - initial: Initial value for the accumulator. - /// - combine: A closure that accepts accumulator and sent value of - /// `self`. - /// - /// - returns: A signal that sends accumulated value each time `self` emits - /// own value. - public func scan(_ initial: U, _ combine: (U, Value) -> U) -> Signal { - return Signal { observer in - var accumulator = initial - - return self.observe { event in - observer.action(event.map { value in - accumulator = combine(accumulator, value) - return accumulator - }) - } - } - } -} - -extension SignalProtocol where Value: Equatable { - /// Forward only those values from `self` which are not duplicates of the - /// immedately preceding value. - /// - /// - note: The first value is always forwarded. - /// - /// - returns: A signal that does not send two equal values sequentially. - public func skipRepeats() -> Signal { - return skipRepeats(==) - } -} - -extension SignalProtocol { - /// Forward only those values from `self` which do not pass `isRepeat` with - /// respect to the previous value. - /// - /// - note: The first value is always forwarded. - /// - /// - parameters: - /// - isRepeate: A closure that accepts previous and current values of - /// `self` and returns `Bool` whether these values are - /// repeating. - /// - /// - returns: A signal that forwards only those values that fail given - /// `isRepeat` predicate. - public func skipRepeats(_ isRepeat: (Value, Value) -> Bool) -> Signal { - return self - .scan((nil, false)) { (accumulated: (Value?, Bool), next: Value) -> (value: Value?, repeated: Bool) in - switch accumulated.0 { - case nil: - return (next, false) - case let prev? where isRepeat(prev, next): - return (prev, true) - case _?: - return (Optional(next), false) - } - } - .filter { !$0.repeated } - .map { $0.value } - .skipNil() - } - - /// Do not forward any values from `self` until `predicate` returns false, - /// at which point the returned signal behaves exactly like `signal`. - /// - /// - parameters: - /// - predicate: A closure that accepts a value and returns whether `self` - /// should still not forward that value to a `signal`. - /// - /// - returns: A signal that sends only forwarded values from `self`. - public func skip(while predicate: (Value) -> Bool) -> Signal { - return Signal { observer in - var shouldSkip = true - - return self.observe { event in - switch event { - case let .next(value): - shouldSkip = shouldSkip && predicate(value) - if !shouldSkip { - fallthrough - } - - case .failed, .completed, .interrupted: - observer.action(event) - } - } - } - } - - /// Forward events from `self` until `replacement` begins sending events. - /// - /// - parameters: - /// - replacement: A signal to wait to wait for values from and start - /// sending them as a replacement to `self`'s values. - /// - /// - returns: A signal which passes through `next`, failed, and - /// `interrupted` events from `self` until `replacement` sends - /// an event, at which point the returned signal will send that - /// event and switch to passing through events from `replacement` - /// instead, regardless of whether `self` has sent events - /// already. - public func take(untilReplacement signal: Signal) -> Signal { - return Signal { observer in - let disposable = CompositeDisposable() - - let signalDisposable = self.observe { event in - switch event { - case .completed: - break - - case .next, .failed, .interrupted: - observer.action(event) - } - } - - disposable += signalDisposable - disposable += signal.observe { event in - signalDisposable?.dispose() - observer.action(event) - } - - return disposable - } - } - - /// Wait until `self` completes and then forward the final `count` values - /// on the returned signal. - /// - /// - parameters: - /// - count: Number of last events to send after `self` completes. - /// - /// - returns: A signal that receives up to `count` values from `self` - /// after `self` completes. - public func take(last count: Int) -> Signal { - return Signal { observer in - var buffer: [Value] = [] - buffer.reserveCapacity(count) - - return self.observe { event in - switch event { - case let .next(value): - // To avoid exceeding the reserved capacity of the buffer, - // we remove then add. Remove elements until we have room to - // add one more. - while (buffer.count + 1) > count { - buffer.remove(at: 0) - } - - buffer.append(value) - case let .failed(error): - observer.sendFailed(error) - case .completed: - buffer.forEach(observer.sendNext) - - observer.sendCompleted() - case .interrupted: - observer.sendInterrupted() - } - } - } - } - - /// Forward any values from `self` until `predicate` returns false, at which - /// point the returned signal will complete. - /// - /// - parameters: - /// - predicate: A closure that accepts value and returns `Bool` value - /// whether `self` should forward it to `signal` and continue - /// sending other events. - /// - /// - returns: A signal that sends events until the values sent by `self` - /// pass the given `predicate`. - public func take(while predicate: (Value) -> Bool) -> Signal { - return Signal { observer in - return self.observe { event in - if let value = event.value, !predicate(value) { - observer.sendCompleted() - } else { - observer.action(event) - } - } - } - } -} - -private struct ZipState { - var values: (left: [Left], right: [Right]) = ([], []) - var isCompleted: (left: Bool, right: Bool) = (false, false) - - var isFinished: Bool { - return (isCompleted.left && values.left.isEmpty) || (isCompleted.right && values.right.isEmpty) - } -} - -extension SignalProtocol { - /// Zip elements of two signals into pairs. The elements of any Nth pair - /// are the Nth elements of the two input signals. - /// - /// - parameters: - /// - otherSignal: A signal to zip values with. - /// - /// - returns: A signal that sends tuples of `self` and `otherSignal`. - public func zip(with other: Signal) -> Signal<(Value, U), Error> { - return Signal { observer in - let state = Atomic(ZipState()) - let disposable = CompositeDisposable() - - let flush = { - var tuple: (Value, U)? - var isFinished = false - - state.modify { state in - guard !state.values.left.isEmpty && !state.values.right.isEmpty else { - isFinished = state.isFinished - return - } - - tuple = (state.values.left.removeFirst(), state.values.right.removeFirst()) - isFinished = state.isFinished - } - - if let tuple = tuple { - observer.sendNext(tuple) - } - - if isFinished { - observer.sendCompleted() - } - } - - let onFailed = observer.sendFailed - let onInterrupted = observer.sendInterrupted - - disposable += self.observe { event in - switch event { - case let .next(value): - state.modify { - $0.values.left.append(value) - } - flush() - - case let .failed(error): - onFailed(error) - - case .completed: - state.modify { - $0.isCompleted.left = true - } - flush() - - case .interrupted: - onInterrupted() - } - } - - disposable += other.observe { event in - switch event { - case let .next(value): - state.modify { - $0.values.right.append(value) - } - flush() - - case let .failed(error): - onFailed(error) - - case .completed: - state.modify { - $0.isCompleted.right = true - } - flush() - - case .interrupted: - onInterrupted() - } - } - - return disposable - } - } - - /// Apply `operation` to values from `self` with `Success`ful results - /// forwarded on the returned signal and `Failure`s sent as failed events. - /// - /// - parameters: - /// - operation: A closure that accepts a value and returns a `Result`. - /// - /// - returns: A signal that receives `Success`ful `Result` as `next` event - /// and `Failure` as failed event. - public func attempt(_ operation: (Value) -> Result<(), Error>) -> Signal { - return attemptMap { value in - return operation(value).map { - return value - } - } - } - - /// Apply `operation` to values from `self` with `Success`ful results mapped - /// on the returned signal and `Failure`s sent as failed events. - /// - /// - parameters: - /// - operation: A closure that accepts a value and returns a result of - /// a mapped value as `Success`. - /// - /// - returns: A signal that sends mapped values from `self` if returned - /// `Result` is `Success`ful, failed events otherwise. - public func attemptMap(_ operation: (Value) -> Result) -> Signal { - return Signal { observer in - self.observe { event in - switch event { - case let .next(value): - operation(value).analysis( - ifSuccess: observer.sendNext, - ifFailure: observer.sendFailed - ) - case let .failed(error): - observer.sendFailed(error) - case .completed: - observer.sendCompleted() - case .interrupted: - observer.sendInterrupted() - } - } - } - } - - /// Throttle values sent by the receiver, so that at least `interval` - /// seconds pass between each, then forwards them on the given scheduler. - /// - /// - note: If multiple values are received before the interval has elapsed, - /// the latest value is the one that will be passed on. - /// - /// - note: If the input signal terminates while a value is being throttled, - /// that value will be discarded and the returned signal will - /// terminate immediately. - /// - /// - note: If the device time changed backwords before previous date while - /// a value is being throttled, and if there is a new value sent, - /// the new value will be passed anyway. - /// - /// - parameters: - /// - interval: Number of seconds to wait between sent values. - /// - scheduler: A scheduler to deliver events on. - /// - /// - returns: A signal that sends values at least `interval` seconds - /// appart on a given scheduler. - public func throttle(_ interval: TimeInterval, on scheduler: DateSchedulerProtocol) -> Signal { - precondition(interval >= 0) - - return Signal { observer in - let state: Atomic> = Atomic(ThrottleState()) - let schedulerDisposable = SerialDisposable() - - let disposable = CompositeDisposable() - disposable += schedulerDisposable - - disposable += self.observe { event in - guard let value = event.value else { - schedulerDisposable.innerDisposable = scheduler.schedule { - observer.action(event) - } - return - } - - var scheduleDate: Date! - state.modify { - $0.pendingValue = value - - let proposedScheduleDate: Date - if let previousDate = $0.previousDate, previousDate.compare(scheduler.currentDate) != .orderedDescending { - proposedScheduleDate = previousDate.addingTimeInterval(interval) - } else { - proposedScheduleDate = scheduler.currentDate - } - - switch proposedScheduleDate.compare(scheduler.currentDate) { - case .orderedAscending: - scheduleDate = scheduler.currentDate - - case .orderedSame: fallthrough - case .orderedDescending: - scheduleDate = proposedScheduleDate - } - } - - schedulerDisposable.innerDisposable = scheduler.schedule(after: scheduleDate) { - let pendingValue: Value? = state.modify { state in - defer { - if state.pendingValue != nil { - state.pendingValue = nil - state.previousDate = scheduleDate - } - } - return state.pendingValue - } - - if let pendingValue = pendingValue { - observer.sendNext(pendingValue) - } - } - } - - return disposable - } - } - - /// Debounce values sent by the receiver, such that at least `interval` - /// seconds pass after the receiver has last sent a value, then forward the - /// latest value on the given scheduler. - /// - /// - note: If multiple values are received before the interval has elapsed, - /// the latest value is the one that will be passed on. - /// - /// - note: If the input signal terminates while a value is being debounced, - /// that value will be discarded and the returned signal will - /// terminate immediately. - /// - /// - parameters: - /// - interval: A number of seconds to wait before sending a value. - /// - scheduler: A scheduler to send values on. - /// - /// - returns: A signal that sends values that are sent from `self` at least - /// `interval` seconds apart. - public func debounce(_ interval: TimeInterval, on scheduler: DateSchedulerProtocol) -> Signal { - precondition(interval >= 0) - - return self - .materialize() - .flatMap(.latest) { event -> SignalProducer, NoError> in - if event.isTerminating { - return SignalProducer(value: event).observe(on: scheduler) - } else { - return SignalProducer(value: event).delay(interval, on: scheduler) - } - } - .dematerialize() - } -} - -extension SignalProtocol { - /// Forward only those values from `self` that have unique identities across - /// the set of all values that have been seen. - /// - /// - note: This causes the identities to be retained to check for - /// uniqueness. - /// - /// - parameters: - /// - transform: A closure that accepts a value and returns identity - /// value. - /// - /// - returns: A signal that sends unique values during its lifetime. - public func uniqueValues(_ transform: (Value) -> Identity) -> Signal { - return Signal { observer in - var seenValues: Set = [] - - return self - .observe { event in - switch event { - case let .next(value): - let identity = transform(value) - if !seenValues.contains(identity) { - seenValues.insert(identity) - fallthrough - } - - case .failed, .completed, .interrupted: - observer.action(event) - } - } - } - } -} - -extension SignalProtocol where Value: Hashable { - /// Forward only those values from `self` that are unique across the set of - /// all values that have been seen. - /// - /// - note: This causes the values to be retained to check for uniqueness. - /// Providing a function that returns a unique value for each sent - /// value can help you reduce the memory footprint. - /// - /// - returns: A signal that sends unique values during its lifetime. - public func uniqueValues() -> Signal { - return uniqueValues { $0 } - } -} - -private struct ThrottleState { - var previousDate: Date? = nil - var pendingValue: Value? = nil -} - -extension SignalProtocol { - /// Combines the values of all the given signals, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: Signal, _ b: Signal) -> Signal<(Value, B), Error> { - return a.combineLatest(with: b) - } - - /// Combines the values of all the given signals, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: Signal, _ b: Signal, _ c: Signal) -> Signal<(Value, B, C), Error> { - return combineLatest(a, b) - .combineLatest(with: c) - .map(repack) - } - - /// Combines the values of all the given signals, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal) -> Signal<(Value, B, C, D), Error> { - return combineLatest(a, b, c) - .combineLatest(with: d) - .map(repack) - } - - /// Combines the values of all the given signals, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal) -> Signal<(Value, B, C, D, E), Error> { - return combineLatest(a, b, c, d) - .combineLatest(with: e) - .map(repack) - } - - /// Combines the values of all the given signals, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal) -> Signal<(Value, B, C, D, E, F), Error> { - return combineLatest(a, b, c, d, e) - .combineLatest(with: f) - .map(repack) - } - - /// Combines the values of all the given signals, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal, _ g: Signal) -> Signal<(Value, B, C, D, E, F, G), Error> { - return combineLatest(a, b, c, d, e, f) - .combineLatest(with: g) - .map(repack) - } - - /// Combines the values of all the given signals, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal, _ g: Signal, _ h: Signal) -> Signal<(Value, B, C, D, E, F, G, H), Error> { - return combineLatest(a, b, c, d, e, f, g) - .combineLatest(with: h) - .map(repack) - } - - /// Combines the values of all the given signals, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal, _ g: Signal, _ h: Signal, _ i: Signal) -> Signal<(Value, B, C, D, E, F, G, H, I), Error> { - return combineLatest(a, b, c, d, e, f, g, h) - .combineLatest(with: i) - .map(repack) - } - - /// Combines the values of all the given signals, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal, _ g: Signal, _ h: Signal, _ i: Signal, _ j: Signal) -> Signal<(Value, B, C, D, E, F, G, H, I, J), Error> { - return combineLatest(a, b, c, d, e, f, g, h, i) - .combineLatest(with: j) - .map(repack) - } - - /// Combines the values of all the given signals, in the manner described by - /// `combineLatestWith`. No events will be sent if the sequence is empty. - public static func combineLatest>(_ signals: S) -> Signal<[Value], Error> { - var generator = signals.makeIterator() - if let first = generator.next() { - let initial = first.map { [$0] } - return IteratorSequence(generator).reduce(initial) { signal, next in - signal.combineLatest(with: next).map { $0.0 + [$0.1] } - } - } - - return .never - } - - /// Zips the values of all the given signals, in the manner described by - /// `zipWith`. - public static func zip(_ a: Signal, _ b: Signal) -> Signal<(Value, B), Error> { - return a.zip(with: b) - } - - /// Zips the values of all the given signals, in the manner described by - /// `zipWith`. - public static func zip(_ a: Signal, _ b: Signal, _ c: Signal) -> Signal<(Value, B, C), Error> { - return zip(a, b) - .zip(with: c) - .map(repack) - } - - /// Zips the values of all the given signals, in the manner described by - /// `zipWith`. - public static func zip(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal) -> Signal<(Value, B, C, D), Error> { - return zip(a, b, c) - .zip(with: d) - .map(repack) - } - - /// Zips the values of all the given signals, in the manner described by - /// `zipWith`. - public static func zip(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal) -> Signal<(Value, B, C, D, E), Error> { - return zip(a, b, c, d) - .zip(with: e) - .map(repack) - } - - /// Zips the values of all the given signals, in the manner described by - /// `zipWith`. - public static func zip(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal) -> Signal<(Value, B, C, D, E, F), Error> { - return zip(a, b, c, d, e) - .zip(with: f) - .map(repack) - } - - /// Zips the values of all the given signals, in the manner described by - /// `zipWith`. - public static func zip(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal, _ g: Signal) -> Signal<(Value, B, C, D, E, F, G), Error> { - return zip(a, b, c, d, e, f) - .zip(with: g) - .map(repack) - } - - /// Zips the values of all the given signals, in the manner described by - /// `zipWith`. - public static func zip(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal, _ g: Signal, _ h: Signal) -> Signal<(Value, B, C, D, E, F, G, H), Error> { - return zip(a, b, c, d, e, f, g) - .zip(with: h) - .map(repack) - } - - /// Zips the values of all the given signals, in the manner described by - /// `zipWith`. - public static func zip(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal, _ g: Signal, _ h: Signal, _ i: Signal) -> Signal<(Value, B, C, D, E, F, G, H, I), Error> { - return zip(a, b, c, d, e, f, g, h) - .zip(with: i) - .map(repack) - } - - /// Zips the values of all the given signals, in the manner described by - /// `zipWith`. - public static func zip(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal, _ g: Signal, _ h: Signal, _ i: Signal, _ j: Signal) -> Signal<(Value, B, C, D, E, F, G, H, I, J), Error> { - return zip(a, b, c, d, e, f, g, h, i) - .zip(with: j) - .map(repack) - } - - /// Zips the values of all the given signals, in the manner described by - /// `zipWith`. No events will be sent if the sequence is empty. - public static func zip>(_ signals: S) -> Signal<[Value], Error> { - var generator = signals.makeIterator() - if let first = generator.next() { - let initial = first.map { [$0] } - return IteratorSequence(generator).reduce(initial) { signal, next in - signal.zip(with: next).map { $0.0 + [$0.1] } - } - } - - return .never - } -} - -extension SignalProtocol { - /// Forward events from `self` until `interval`. Then if signal isn't - /// completed yet, fails with `error` on `scheduler`. - /// - /// - note: 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. - /// - /// - parameters: - /// - error: Error to send with failed event if `self` is not completed - /// when `interval` passes. - /// - interval: Number of seconds to wait for `self` to complete. - /// - scheudler: A scheduler to deliver error on. - /// - /// - returns: A signal that sends events for at most `interval` seconds, - /// then, if not `completed` - sends `error` with failed event - /// on `scheduler`. - public func timeout(after interval: TimeInterval, raising error: Error, on scheduler: DateSchedulerProtocol) -> Signal { - precondition(interval >= 0) - - return Signal { observer in - let disposable = CompositeDisposable() - let date = scheduler.currentDate.addingTimeInterval(interval) - - disposable += scheduler.schedule(after: date) { - observer.sendFailed(error) - } - - disposable += self.observe(observer) - return disposable - } - } -} - -extension SignalProtocol where Error == NoError { - /// Promote a signal that does not generate failures into one that can. - /// - /// - note: This does not actually cause failures to be generated for the - /// given signal, but makes it easier to combine with other signals - /// that may fail; for example, with operators like - /// `combineLatestWith`, `zipWith`, `flatten`, etc. - /// - /// - parameters: - /// - _ An `ErrorType`. - /// - /// - returns: A signal that has an instantiatable `ErrorType`. - public func promoteErrors(_: F.Type) -> Signal { - return Signal { observer in - return self.observe { event in - switch event { - case let .next(value): - observer.sendNext(value) - case .failed: - fatalError("NoError is impossible to construct") - case .completed: - observer.sendCompleted() - case .interrupted: - observer.sendInterrupted() - } - } - } - } - - /// Forward events from `self` until `interval`. Then if signal isn't - /// completed yet, fails with `error` on `scheduler`. - /// - /// - note: 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. - /// - /// - parameters: - /// - interval: Number of seconds to wait for `self` to complete. - /// - error: Error to send with `failed` event if `self` is not completed - /// when `interval` passes. - /// - scheudler: A scheduler to deliver error on. - /// - /// - returns: A signal that sends events for at most `interval` seconds, - /// then, if not `completed` - sends `error` with `failed` event - /// on `scheduler`. - public func timeout( - after interval: TimeInterval, - raising error: NewError, - on scheduler: DateSchedulerProtocol - ) -> Signal { - return self - .promoteErrors(NewError.self) - .timeout(after: interval, raising: error, on: scheduler) - } -} diff --git a/ReactiveCocoa/Swift/SignalProducer.swift b/ReactiveCocoa/Swift/SignalProducer.swift deleted file mode 100644 index a3030992f6..0000000000 --- a/ReactiveCocoa/Swift/SignalProducer.swift +++ /dev/null @@ -1,1907 +0,0 @@ -import Foundation -import Result - -/// A SignalProducer creates Signals that can produce values of type `Value` -/// and/or fail with errors of type `Error`. If no failure should be possible, -/// `NoError` can be specified for `Error`. -/// -/// SignalProducers can be used to represent operations or tasks, like network -/// requests, where each invocation of `start()` will create a new underlying -/// operation. This ensures that consumers will receive the results, versus a -/// plain Signal, where the results might be sent before any observers are -/// attached. -/// -/// Because of the behavior of `start()`, different Signals created from the -/// producer may see a different version of Events. The Events may arrive in a -/// different order between Signals, or the stream might be completely -/// different! -public struct SignalProducer { - public typealias ProducedSignal = Signal - - private let startHandler: (Signal.Observer, CompositeDisposable) -> Void - - /// Initializes a `SignalProducer` that will emit the same events as the - /// given signal. - /// - /// If the Disposable returned from `start()` is disposed or a terminating - /// event is sent to the observer, the given signal will be disposed. - /// - /// - parameters: - /// - signal: A signal to observe after starting the producer. - public init(signal: S) { - self.init { observer, disposable in - disposable += signal.observe(observer) - } - } - - /// Initializes a SignalProducer that will invoke the given closure once for - /// each invocation of `start()`. - /// - /// The events that the closure puts into the given observer will become - /// the events sent by the started `Signal` to its observers. - /// - /// - note: If the `Disposable` returned from `start()` is disposed or a - /// terminating event is sent to the observer, the given - /// `CompositeDisposable` will be disposed, at which point work - /// should be interrupted and any temporary resources cleaned up. - /// - /// - parameters: - /// - startHandler: A closure that accepts observer and a disposable. - public init(_ startHandler: (Signal.Observer, CompositeDisposable) -> Void) { - self.startHandler = startHandler - } - - /// Creates a producer for a `Signal` that will immediately send one value - /// then complete. - /// - /// - parameters: - /// - value: A value that should be sent by the `Signal` in a `next` - /// event. - public init(value: Value) { - self.init { observer, disposable in - observer.sendNext(value) - observer.sendCompleted() - } - } - - /// Creates a producer for a `Signal` that will immediately fail with the - /// given error. - /// - /// - parameters: - /// - error: An error that should be sent by the `Signal` in a `failed` - /// event. - public init(error: Error) { - self.init { observer, disposable in - observer.sendFailed(error) - } - } - - /// Creates a producer for a Signal that will immediately send one value - /// then complete, or immediately fail, depending on the given Result. - /// - /// - parameters: - /// - result: A `Result` instance that will send either `next` event if - /// `result` is `Success`ful or `failed` event if `result` is a - /// `Failure`. - public init(result: Result) { - switch result { - case let .success(value): - self.init(value: value) - - case let .failure(error): - self.init(error: error) - } - } - - /// Creates a producer for a Signal that will immediately send the values - /// from the given sequence, then complete. - /// - /// - parameters: - /// - values: A sequence of values that a `Signal` will send as separate - /// `next` events and then complete. - public init(values: S) { - self.init { observer, disposable in - for value in values { - observer.sendNext(value) - - if disposable.isDisposed { - break - } - } - - observer.sendCompleted() - } - } - - /// Creates a producer for a Signal that will immediately send the values - /// from the given sequence, then complete. - /// - /// - parameters: - /// - first: First value for the `Signal` to send. - /// - second: Second value for the `Signal` to send. - /// - tail: Rest of the values to be sent by the `Signal`. - public init(values first: Value, _ second: Value, _ tail: Value...) { - self.init(values: [ first, second ] + tail) - } - - /// A producer for a Signal that will immediately complete without sending - /// any values. - public static var empty: SignalProducer { - return self.init { observer, disposable in - observer.sendCompleted() - } - } - - /// A producer for a Signal that never sends any events to its observers. - public static var never: SignalProducer { - return self.init { _ in return } - } - - /// Create a `SignalProducer` that will attempt the given operation once for - /// each invocation of `start()`. - /// - /// Upon success, the started signal will send the resulting value then - /// complete. Upon failure, the started signal will fail with the error that - /// occurred. - /// - /// - parameters: - /// - operation: A closure that returns instance of `Result`. - /// - /// - returns: A `SignalProducer` that will forward `Success`ful `result` as - /// `next` event and then complete or `failed` event if `result` - /// is a `Failure`. - public static func attempt(_ operation: () -> Result) -> SignalProducer { - return self.init { observer, disposable in - operation().analysis(ifSuccess: { value in - observer.sendNext(value) - observer.sendCompleted() - }, ifFailure: { error in - observer.sendFailed(error) - }) - } - } - - /// Create a Signal from the producer, pass it into the given closure, - /// then start sending events on the Signal when the closure has returned. - /// - /// The closure will also receive a disposable which can be used to - /// interrupt the work associated with the signal and immediately send an - /// `interrupted` event. - /// - /// - parameters: - /// - setUp: A closure that accepts a `signal` and `interrupter`. - public func startWithSignal(_ setup: @noescape (signal: Signal, interrupter: Disposable) -> Void) { - let (signal, observer) = Signal.pipe() - - // Disposes of the work associated with the SignalProducer and any - // upstream producers. - let producerDisposable = CompositeDisposable() - - // Directly disposed of when `start()` or `startWithSignal()` is - // disposed. - let cancelDisposable = ActionDisposable { - observer.sendInterrupted() - producerDisposable.dispose() - } - - setup(signal: signal, interrupter: cancelDisposable) - - if cancelDisposable.isDisposed { - return - } - - let wrapperObserver: Signal.Observer = Observer { event in - observer.action(event) - - if event.isTerminating { - // Dispose only after notifying the Signal, so disposal - // logic is consistently the last thing to run. - producerDisposable.dispose() - } - } - - startHandler(wrapperObserver, producerDisposable) - } -} - -public protocol SignalProducerProtocol { - /// The type of values being sent on the producer - associatedtype Value - /// The type of error that can occur on the producer. If errors aren't possible - /// then `NoError` can be used. - associatedtype Error: Swift.Error - - /// Extracts a signal producer from the receiver. - var producer: SignalProducer { get } - - /// Initialize a signal - init(_ startHandler: (Signal.Observer, CompositeDisposable) -> Void) - - /// Creates a Signal from the producer, passes it into the given closure, - /// then starts sending events on the Signal when the closure has returned. - func startWithSignal(_ setup: @noescape (signal: Signal, interrupter: Disposable) -> Void) -} - -extension SignalProducer: SignalProducerProtocol { - public var producer: SignalProducer { - return self - } -} - -extension SignalProducerProtocol { - /// Create a Signal from the producer, then attach the given observer to - /// the `Signal` as an observer. - /// - /// - parameters: - /// - observer: An observer to attach to produced signal. - /// - /// - returns: A `Disposable` which can be used to interrupt the work - /// associated with the signal and immediately send an - /// `interrupted` event. - @discardableResult - public func start(_ observer: Signal.Observer = Signal.Observer()) -> Disposable { - var disposable: Disposable! - - startWithSignal { signal, innerDisposable in - signal.observe(observer) - disposable = innerDisposable - } - - return disposable - } - - /// Convenience override for start(_:) to allow trailing-closure style - /// invocations. - /// - /// - parameters: - /// - observerAction: A closure that accepts `Event` sent by the produced - /// signal. - /// - /// - returns: A `Disposable` which can be used to interrupt the work - /// associated with the signal and immediately send an - /// `interrupted` event. - @discardableResult - public func start(_ observerAction: Signal.Observer.Action) -> Disposable { - return start(Observer(observerAction)) - } - - /// Create a Signal from the producer, then add an observer to the `Signal`, - /// which will invoke the given callback when `next` or `failed` events are - /// received. - /// - /// - parameters: - /// - result: A closure that accepts a `result` that contains a `Success` - /// case for `next` events or `Failure` case for `failed` event. - /// - /// - returns: A Disposable which can be used to interrupt the work - /// associated with the Signal, and prevent any future callbacks - /// from being invoked. - @discardableResult - public func startWithResult(_ result: (Result) -> Void) -> Disposable { - return start( - Observer( - next: { result(.success($0)) }, - failed: { result(.failure($0)) } - ) - ) - } - - /// Create a Signal from the producer, then add exactly one observer to the - /// Signal, which will invoke the given callback when a `completed` event is - /// received. - /// - /// - parameters: - /// - completed: A closure that will be envoked when produced signal sends - /// `completed` event. - /// - /// - returns: A `Disposable` which can be used to interrupt the work - /// associated with the signal. - @discardableResult - public func startWithCompleted(_ completed: () -> Void) -> Disposable { - return start(Observer(completed: completed)) - } - - /// Creates a Signal from the producer, then adds exactly one observer to - /// the Signal, which will invoke the given callback when a `failed` event - /// is received. - /// - /// - parameters: - /// - failed: A closure that accepts an error object. - /// - /// - returns: A `Disposable` which can be used to interrupt the work - /// associated with the signal. - @discardableResult - public func startWithFailed(_ failed: (Error) -> Void) -> Disposable { - return start(Observer(failed: failed)) - } - - /// Creates a Signal from the producer, then adds exactly one observer to - /// the Signal, which will invoke the given callback when an `interrupted` - /// event is received. - /// - /// - parameters: - /// - interrupted: A closure that is invoked when `interrupted` event is - /// received. - /// - /// - returns: A `Disposable` which can be used to interrupt the work - /// associated with the signal. - @discardableResult - public func startWithInterrupted(_ interrupted: () -> Void) -> Disposable { - return start(Observer(interrupted: interrupted)) - } -} - -extension SignalProducerProtocol where Error == NoError { - /// Create a Signal from the producer, then add exactly one observer to - /// the Signal, which will invoke the given callback when `next` events are - /// received. - /// - /// - parameters: - /// - next: A closure that accepts a value carried by `next` event. - /// - /// - returns: A `Disposable` which can be used to interrupt the work - /// associated with the Signal, and prevent any future callbacks - /// from being invoked. - @discardableResult - public func startWithNext(_ next: (Value) -> Void) -> Disposable { - return start(Observer(next: next)) - } -} - -extension SignalProducerProtocol { - /// Lift an unary Signal operator to operate upon SignalProducers instead. - /// - /// In other words, this will create a new `SignalProducer` which will apply - /// the given `Signal` operator to _every_ created `Signal`, just as if the - /// operator had been applied to each `Signal` yielded from `start()`. - /// - /// - parameters: - /// - transform: An unary operator to lift. - /// - /// - returns: A signal producer that applies signal's operator to every - /// created signal. - public func lift(_ transform: (Signal) -> Signal) -> SignalProducer { - return SignalProducer { observer, outerDisposable in - self.startWithSignal { signal, innerDisposable in - outerDisposable += innerDisposable - - transform(signal).observe(observer) - } - } - } - - - /// Lift a binary Signal operator to operate upon SignalProducers instead. - /// - /// In other words, this will create a new `SignalProducer` which will apply - /// the given `Signal` operator to _every_ `Signal` created from the two - /// producers, just as if the operator had been applied to each `Signal` - /// yielded from `start()`. - /// - /// - note: starting the returned producer will start the receiver of the - /// operator, which may not be adviseable for some operators. - /// - /// - parameters: - /// - transform: A binary operator to lift. - /// - /// - returns: A binary operator that operates on two signal producers. - public func lift(_ transform: (Signal) -> (Signal) -> Signal) -> (SignalProducer) -> SignalProducer { - return liftRight(transform) - } - - /// Right-associative lifting of a binary signal operator over producers. - /// That is, the argument producer will be started before the receiver. When - /// both producers are synchronous this order can be important depending on - /// the operator to generate correct results. - private func liftRight(_ transform: (Signal) -> (Signal) -> Signal) -> (SignalProducer) -> SignalProducer { - return { otherProducer in - return SignalProducer { observer, outerDisposable in - self.startWithSignal { signal, disposable in - outerDisposable.add(disposable) - - otherProducer.startWithSignal { otherSignal, otherDisposable in - outerDisposable += otherDisposable - - transform(signal)(otherSignal).observe(observer) - } - } - } - } - } - - /// Left-associative lifting of a binary signal operator over producers. - /// That is, the receiver will be started before the argument producer. When - /// both producers are synchronous this order can be important depending on - /// the operator to generate correct results. - private func liftLeft(_ transform: (Signal) -> (Signal) -> Signal) -> (SignalProducer) -> SignalProducer { - return { otherProducer in - return SignalProducer { observer, outerDisposable in - otherProducer.startWithSignal { otherSignal, otherDisposable in - outerDisposable += otherDisposable - - self.startWithSignal { signal, disposable in - outerDisposable.add(disposable) - - transform(signal)(otherSignal).observe(observer) - } - } - } - } - } - - - /// Lift a binary Signal operator to operate upon a Signal and a - /// SignalProducer instead. - /// - /// In other words, this will create a new `SignalProducer` which will apply - /// the given `Signal` operator to _every_ `Signal` created from the two - /// producers, just as if the operator had been applied to each `Signal` - /// yielded from `start()`. - /// - /// - parameters: - /// - transform: A binary operator to lift. - /// - /// - returns: A binary operator that works on `Signal` and returns - /// `SignalProducer`. - public func lift(_ transform: (Signal) -> (Signal) -> Signal) -> (Signal) -> SignalProducer { - return { otherSignal in - return SignalProducer { observer, outerDisposable in - let (wrapperSignal, otherSignalObserver) = Signal.pipe() - - // Avoid memory leak caused by the direct use of the given - // signal. - // - // See https://github.com/ReactiveCocoa/ReactiveCocoa/pull/2758 - // for the details. - outerDisposable += ActionDisposable { - otherSignalObserver.sendInterrupted() - } - outerDisposable += otherSignal.observe(otherSignalObserver) - - self.startWithSignal { signal, disposable in - outerDisposable += disposable - outerDisposable += transform(signal)(wrapperSignal).observe(observer) - } - } - } - } - - - /// Map each value in the producer to a new value. - /// - /// - parameters: - /// - transform: A closure that accepts a value and returns a different - /// value. - /// - /// - returns: A signal producer that, when started, will send a mapped - /// value of `self.` - public func map(_ transform: (Value) -> U) -> SignalProducer { - return lift { $0.map(transform) } - } - - /// Map errors in the producer to a new error. - /// - /// - parameters: - /// - transform: A closure that accepts an error object and returns a - /// different error. - /// - /// - returns: A producer that emits errors of new type. - public func mapError(_ transform: (Error) -> F) -> SignalProducer { - return lift { $0.mapError(transform) } - } - - /// Preserve only the values of the producer that pass the given predicate. - /// - /// - parameters: - /// - predicate: A closure that accepts value and returns `Bool` denoting - /// whether value has passed the test. - /// - /// - returns: A producer that, when started, will send only the values - /// passing the given predicate. - public func filter(_ predicate: (Value) -> Bool) -> SignalProducer { - return lift { $0.filter(predicate) } - } - - /// Yield the first `count` values from the input producer. - /// - /// - precondition: `count` must be non-negative number. - /// - /// - parameters: - /// - count: A number of values to take from the signal. - /// - /// - returns: A producer that, when started, will yield the first `count` - /// values from `self`. - public func take(first count: Int) -> SignalProducer { - return lift { $0.take(first: count) } - } - - /// Yield an array of values when `self` completes. - /// - /// - note: When `self` completes without collecting any value, it will send - /// an empty array of values. - /// - /// - returns: A producer that, when started, will yield an array of values - /// when `self` completes. - public func collect() -> SignalProducer<[Value], Error> { - return lift { $0.collect() } - } - - /// Yield an array of values until it reaches a certain count. - /// - /// - precondition: `count` should be greater than zero. - /// - /// - note: When the count is reached the array is sent and the signal - /// starts over yielding a new array of values. - /// - /// - note: When `self` completes any remaining values will be sent, the - /// last array may not have `count` values. Alternatively, if were - /// not collected any values will sent an empty array of values. - /// - /// - returns: A producer that, when started, collects at most `count` - /// values from `self`, forwards them as a single array and - /// completes. - public func collect(count: Int) -> SignalProducer<[Value], Error> { - precondition(count > 0) - return lift { $0.collect(count: count) } - } - - /// Yield an array of values based on a predicate which matches the values - /// collected. - /// - /// - note: When `self` completes any remaining values will be sent, the - /// last array may not match `predicate`. Alternatively, if were not - /// collected any values will sent an empty array of values. - /// - /// ```` - /// let (producer, observer) = SignalProducer.buffer(1) - /// - /// producer - /// .collect { values in values.reduce(0, combine: +) == 8 } - /// .startWithNext { print($0) } - /// - /// observer.sendNext(1) - /// observer.sendNext(3) - /// observer.sendNext(4) - /// observer.sendNext(7) - /// observer.sendNext(1) - /// observer.sendNext(5) - /// observer.sendNext(6) - /// observer.sendCompleted() - /// - /// // Output: - /// // [1, 3, 4] - /// // [7, 1] - /// // [5, 6] - /// ```` - /// - /// - parameters: - /// - predicate: Predicate to match when values should be sent (returning - /// `true`) or alternatively when they should be collected - /// (where it should return `false`). The most recent value - /// (`next`) is included in `values` and will be the end of - /// the current array of values if the predicate returns - /// `true`. - /// - /// - returns: A producer that, when started, collects values passing the - /// predicate and, when `self` completes, forwards them as a - /// single array and complets. - public func collect(_ predicate: (values: [Value]) -> Bool) -> SignalProducer<[Value], Error> { - return lift { $0.collect(predicate) } - } - - /// Yield an array of values based on a predicate which matches the values - /// collected and the next value. - /// - /// - note: When `self` completes any remaining values will be sent, the - /// last array may not match `predicate`. Alternatively, if no - /// values were collected an empty array will be sent. - /// - /// ```` - /// let (producer, observer) = SignalProducer.buffer(1) - /// - /// producer - /// .collect { values, next in next == 7 } - /// .startWithNext { print($0) } - /// - /// observer.sendNext(1) - /// observer.sendNext(1) - /// observer.sendNext(7) - /// observer.sendNext(7) - /// observer.sendNext(5) - /// observer.sendNext(6) - /// observer.sendCompleted() - /// - /// // Output: - /// // [1, 1] - /// // [7] - /// // [7, 5, 6] - /// ```` - /// - /// - parameters: - /// - predicate: Predicate to match when values should be sent (returning - /// `true`) or alternatively when they should be collected - /// (where it should return `false`). The most recent value - /// (`next`) is not included in `values` and will be the - /// start of the next array of values if the predicate - /// returns `true`. - /// - /// - returns: A signal that will yield an array of values based on a - /// predicate which matches the values collected and the next - /// value. - public func collect(_ predicate: (values: [Value], next: Value) -> Bool) -> SignalProducer<[Value], Error> { - return lift { $0.collect(predicate) } - } - - /// Forward all events onto the given scheduler, instead of whichever - /// scheduler they originally arrived upon. - /// - /// - parameters: - /// - scheduler: A scheduler to deliver events on. - /// - /// - returns: A producer that, when started, will yield `self` values on - /// provided scheduler. - public func observe(on scheduler: SchedulerProtocol) -> SignalProducer { - return lift { $0.observe(on: scheduler) } - } - - /// Combine the latest value of the receiver with the latest value from the - /// given producer. - /// - /// - note: The returned producer will not send a value until both inputs - /// have sent at least one value each. - /// - /// - note: If either producer is interrupted, the returned producer will - /// also be interrupted. - /// - /// - parameters: - /// - other: A producer to combine `self`'s value with. - /// - /// - returns: A producer that, when started, will yield a tuple containing - /// values of `self` and given producer. - public func combineLatest(with other: SignalProducer) -> SignalProducer<(Value, U), Error> { - return liftLeft(Signal.combineLatest)(other) - } - - /// Combine the latest value of the receiver with the latest value from - /// the given signal. - /// - /// - note: The returned producer will not send a value until both inputs - /// have sent at least one value each. - /// - /// - note: If either input is interrupted, the returned producer will also - /// be interrupted. - /// - /// - parameters: - /// - other: A signal to combine `self`'s value with. - /// - /// - returns: A producer that, when started, will yield a tuple containing - /// values of `self` and given signal. - public func combineLatest(with other: Signal) -> SignalProducer<(Value, U), Error> { - return lift(Signal.combineLatest(with:))(other) - } - - /// Delay `next` and `completed` events by the given interval, forwarding - /// them on the given scheduler. - /// - /// - note: `failed` and `interrupted` events are always scheduled - /// immediately. - /// - /// - parameters: - /// - interval: Interval to delay `next` and `completed` events by. - /// - scheduler: A scheduler to deliver delayed events on. - /// - /// - returns: A producer that, when started, will delay `next` and - /// `completed` events and will yield them on given scheduler. - public func delay(_ interval: TimeInterval, on scheduler: DateSchedulerProtocol) -> SignalProducer { - return lift { $0.delay(interval, on: scheduler) } - } - - /// Skip the first `count` values, then forward everything afterward. - /// - /// - parameters: - /// - count: A number of values to skip. - /// - /// - returns: A producer that, when started, will skip the first `count` - /// values, then forward everything afterward. - public func skip(first count: Int) -> SignalProducer { - return lift { $0.skip(first: count) } - } - - /// Treats all Events from the input producer as plain values, allowing them - /// to be manipulated just like any other value. - /// - /// In other words, this brings Events “into the monad.” - /// - /// - note: When a Completed or Failed event is received, the resulting - /// producer will send the Event itself and then complete. When an - /// `interrupted` event is received, the resulting producer will - /// send the `Event` itself and then interrupt. - /// - /// - returns: A producer that sends events as its values. - public func materialize() -> SignalProducer, NoError> { - return lift { $0.materialize() } - } - - /// Forward the latest value from `self` with the value from `sampler` as a - /// tuple, only when `sampler` sends a `next` event. - /// - /// - note: If `sampler` fires before a value has been observed on `self`, - /// nothing happens. - /// - /// - parameters: - /// - sampler: A producer that will trigger the delivery of `next` event - /// from `self`. - /// - /// - returns: A producer that will send values from `self` and `sampler`, - /// sampled (possibly multiple times) by `sampler`, then complete - /// once both input producers have completed, or interrupt if - /// either input producer is interrupted. - public func sample(with sampler: SignalProducer) -> SignalProducer<(Value, T), Error> { - return liftLeft(Signal.sample(with:))(sampler) - } - - /// Forward the latest value from `self` with the value from `sampler` as a - /// tuple, only when `sampler` sends a `next` event. - /// - /// - note: If `sampler` fires before a value has been observed on `self`, - /// nothing happens. - /// - /// - parameters: - /// - sampler: A signal that will trigger the delivery of `next` event - /// from `self`. - /// - /// - returns: A producer that, when started, will send values from `self` - /// and `sampler`, sampled (possibly multiple times) by - /// `sampler`, then complete once both input producers have - /// completed, or interrupt if either input producer is - /// interrupted. - public func sample(with sampler: Signal) -> SignalProducer<(Value, T), Error> { - return lift(Signal.sample(with:))(sampler) - } - - /// Forward the latest value from `self` whenever `sampler` sends a `next` - /// event. - /// - /// - note: If `sampler` fires before a value has been observed on `self`, - /// nothing happens. - /// - /// - parameters: - /// - sampler: A producer that will trigger the delivery of `next` event - /// from `self`. - /// - /// - returns: A producer that, when started, will send values from `self`, - /// sampled (possibly multiple times) by `sampler`, then complete - /// once both input producers have completed, or interrupt if - /// either input producer is interrupted. - public func sample(on sampler: SignalProducer<(), NoError>) -> SignalProducer { - return liftLeft(Signal.sample(on:))(sampler) - } - - /// Forward the latest value from `self` whenever `sampler` sends a `next` - /// event. - /// - /// - note: If `sampler` fires before a value has been observed on `self`, - /// nothing happens. - /// - /// - parameters: - /// - trigger: A signal whose `next` or `completed` events will start the - /// deliver of events on `self`. - /// - /// - returns: A producer that will send values from `self`, sampled - /// (possibly multiple times) by `sampler`, then complete once - /// both inputs have completed, or interrupt if either input is - /// interrupted. - public func sample(on sampler: Signal<(), NoError>) -> SignalProducer { - return lift(Signal.sample(on:))(sampler) - } - - /// Forwards events from `self` until `lifetime` ends, at which point the - /// returned producer will complete. - /// - /// - parameters: - /// - lifetime: A lifetime whose `ended` signal will cause the returned - /// producer to complete. - /// - /// - returns: A producer that will deliver events until `lifetime` ends. - public func take(during lifetime: Lifetime) -> SignalProducer { - return take(until: lifetime.ended) - } - - /// Forward events from `self` until `trigger` sends a `next` or `completed` - /// event, at which point the returned producer will complete. - /// - /// - parameters: - /// - trigger: A producer whose `next` or `completed` events will stop the - /// delivery of `next` events from `self`. - /// - /// - returns: A producer that will deliver events until `trigger` sends - /// `next` or `completed` events. - public func take(until trigger: SignalProducer<(), NoError>) -> SignalProducer { - // This should be the implementation of this method: - // return liftRight(Signal.takeUntil)(trigger) - // - // However, due to a Swift miscompilation (with `-O`) we need to inline - // `liftRight` here. - // - // See https://github.com/ReactiveCocoa/ReactiveCocoa/issues/2751 for - // more details. - // - // This can be reverted once tests with -O work correctly. - return SignalProducer { observer, outerDisposable in - self.startWithSignal { signal, disposable in - outerDisposable.add(disposable) - - trigger.startWithSignal { triggerSignal, triggerDisposable in - outerDisposable += triggerDisposable - - signal.take(until: triggerSignal).observe(observer) - } - } - } - } - - /// Forward events from `self` until `trigger` sends a Next or Completed - /// event, at which point the returned producer will complete. - /// - /// - parameters: - /// - trigger: A signal whose `next` or `completed` events will stop the - /// delivery of `next` events from `self`. - /// - /// - returns: A producer that will deliver events until `trigger` sends - /// `next` or `completed` events. - public func take(until trigger: Signal<(), NoError>) -> SignalProducer { - return lift(Signal.take(until:))(trigger) - } - - /// Do not forward any values from `self` until `trigger` sends a `next` - /// or `completed`, at which point the returned producer behaves exactly - /// like `producer`. - /// - /// - parameters: - /// - trigger: A producer whose `next` or `completed` events will start - /// the deliver of events on `self`. - /// - /// - returns: A producer that will deliver events once the `trigger` sends - /// `next` or `completed` events. - public func skip(until trigger: SignalProducer<(), NoError>) -> SignalProducer { - return liftRight(Signal.skip(until:))(trigger) - } - - /// Do not forward any values from `self` until `trigger` sends a `next` - /// or `completed`, at which point the returned signal behaves exactly like - /// `signal`. - /// - /// - parameters: - /// - trigger: A signal whose `next` or `completed` events will start the - /// deliver of events on `self`. - /// - /// - returns: A producer that will deliver events once the `trigger` sends - /// `next` or `completed` events. - public func skip(until trigger: Signal<(), NoError>) -> SignalProducer { - return lift(Signal.skip(until:))(trigger) - } - - /// Forward events from `self` with history: values of the returned producer - /// are a tuple whose first member is the previous value and whose second - /// member is the current value. `initial` is supplied as the first member - /// when `self` sends its first value. - /// - /// - parameters: - /// - initial: A value that will be combined with the first value sent by - /// `self`. - /// - /// - returns: A producer that sends tuples that contain previous and - /// current sent values of `self`. - public func combinePrevious(_ initial: Value) -> SignalProducer<(Value, Value), Error> { - return lift { $0.combinePrevious(initial) } - } - - /// Send only the final value and then immediately completes. - /// - /// - parameters: - /// - initial: Initial value for the accumulator. - /// - combine: A closure that accepts accumulator and sent value of - /// `self`. - /// - /// - returns: A producer that sends accumulated value after `self` - /// completes. - public func reduce(_ initial: U, _ combine: (U, Value) -> U) -> SignalProducer { - return lift { $0.reduce(initial, combine) } - } - - /// Aggregate `self`'s values into a single combined value. When `self` - /// emits its first value, `combine` is invoked with `initial` as the first - /// argument and that emitted value as the second argument. The result is - /// emitted from the producer returned from `scan`. That result is then - /// passed to `combine` as the first argument when the next value is - /// emitted, and so on. - /// - /// - parameters: - /// - initial: Initial value for the accumulator. - /// - combine: A closure that accepts accumulator and sent value of - /// `self`. - /// - /// - returns: A producer that sends accumulated value each time `self` - /// emits own value. - public func scan(_ initial: U, _ combine: (U, Value) -> U) -> SignalProducer { - return lift { $0.scan(initial, combine) } - } - - /// Forward only those values from `self` which do not pass `isRepeat` with - /// respect to the previous value. - /// - /// - note: The first value is always forwarded. - /// - /// - returns: A producer that does not send two equal values sequentially. - public func skipRepeats(_ isRepeat: (Value, Value) -> Bool) -> SignalProducer { - return lift { $0.skipRepeats(isRepeat) } - } - - /// Do not forward any values from `self` until `predicate` returns false, - /// at which point the returned producer behaves exactly like `self`. - /// - /// - parameters: - /// - predicate: A closure that accepts a value and returns whether `self` - /// should still not forward that value to a `producer`. - /// - /// - returns: A producer that sends only forwarded values from `self`. - public func skip(while predicate: (Value) -> Bool) -> SignalProducer { - return lift { $0.skip(while: predicate) } - } - - /// Forward events from `self` until `replacement` begins sending events. - /// - /// - parameters: - /// - replacement: A producer to wait to wait for values from and start - /// sending them as a replacement to `self`'s values. - /// - /// - returns: A producer which passes through `next`, `failed`, and - /// `interrupted` events from `self` until `replacement` sends an - /// event, at which point the returned producer will send that - /// event and switch to passing through events from `replacement` - /// instead, regardless of whether `self` has sent events - /// already. - public func take(untilReplacement signal: SignalProducer) -> SignalProducer { - return liftRight(Signal.take(untilReplacement:))(signal) - } - - /// Forwards events from `self` until `replacement` begins sending events. - /// - /// - parameters: - /// - replacement: A signal to wait to wait for values from and start - /// sending them as a replacement to `self`'s values. - /// - /// - returns: A producer which passes through `next`, `failed`, and - /// `interrupted` events from `self` until `replacement` sends an - /// event, at which point the returned producer will send that - /// event and switch to passing through events from `replacement` - /// instead, regardless of whether `self` has sent events - /// already. - public func take(untilReplacement signal: Signal) -> SignalProducer { - return lift(Signal.take(untilReplacement:))(signal) - } - - /// Wait until `self` completes and then forward the final `count` values - /// on the returned producer. - /// - /// - parameters: - /// - count: Number of last events to send after `self` completes. - /// - /// - returns: A producer that receives up to `count` values from `self` - /// after `self` completes. - public func take(last count: Int) -> SignalProducer { - return lift { $0.take(last: count) } - } - - /// Forward any values from `self` until `predicate` returns false, at which - /// point the returned producer will complete. - /// - /// - parameters: - /// - predicate: A closure that accepts value and returns `Bool` value - /// whether `self` should forward it to `signal` and continue - /// sending other events. - /// - /// - returns: A producer that sends events until the values sent by `self` - /// pass the given `predicate`. - public func take(while predicate: (Value) -> Bool) -> SignalProducer { - return lift { $0.take(while: predicate) } - } - - /// Zip elements of two producers into pairs. The elements of any Nth pair - /// are the Nth elements of the two input producers. - /// - /// - parameters: - /// - other: A producer to zip values with. - /// - /// - returns: A producer that sends tuples of `self` and `otherProducer`. - public func zip(with other: SignalProducer) -> SignalProducer<(Value, U), Error> { - return liftLeft(Signal.zip(with:))(other) - } - - /// Zip elements of this producer and a signal into pairs. The elements of - /// any Nth pair are the Nth elements of the two. - /// - /// - parameters: - /// - other: A signal to zip values with. - /// - /// - returns: A producer that sends tuples of `self` and `otherSignal`. - public func zip(with other: Signal) -> SignalProducer<(Value, U), Error> { - return lift(Signal.zip(with:))(other) - } - - /// Apply `operation` to values from `self` with `Success`ful results - /// forwarded on the returned producer and `Failure`s sent as `failed` - /// events. - /// - /// - parameters: - /// - operation: A closure that accepts a value and returns a `Result`. - /// - /// - returns: A producer that receives `Success`ful `Result` as `next` - /// event and `Failure` as `failed` event. - public func attempt(operation: (Value) -> Result<(), Error>) -> SignalProducer { - return lift { $0.attempt(operation) } - } - - /// Apply `operation` to values from `self` with `Success`ful results - /// mapped on the returned producer and `Failure`s sent as `failed` events. - /// - /// - parameters: - /// - operation: A closure that accepts a value and returns a result of - /// a mapped value as `Success`. - /// - /// - returns: A producer that sends mapped values from `self` if returned - /// `Result` is `Success`ful, `failed` events otherwise. - public func attemptMap(_ operation: (Value) -> Result) -> SignalProducer { - return lift { $0.attemptMap(operation) } - } - - /// Throttle values sent by the receiver, so that at least `interval` - /// seconds pass between each, then forwards them on the given scheduler. - /// - /// - note: If multiple values are received before the interval has elapsed, - /// the latest value is the one that will be passed on. - /// - /// - norw: If `self` terminates while a value is being throttled, that - /// value will be discarded and the returned producer will terminate - /// immediately. - /// - /// - parameters: - /// - interval: Number of seconds to wait between sent values. - /// - scheduler: A scheduler to deliver events on. - /// - /// - returns: A producer that sends values at least `interval` seconds - /// appart on a given scheduler. - public func throttle(_ interval: TimeInterval, on scheduler: DateSchedulerProtocol) -> SignalProducer { - return lift { $0.throttle(interval, on: scheduler) } - } - - /// Debounce values sent by the receiver, such that at least `interval` - /// seconds pass after the receiver has last sent a value, then - /// forward the latest value on the given scheduler. - /// - /// - note: If multiple values are received before the interval has elapsed, - /// the latest value is the one that will be passed on. - /// - /// - note: If `self` terminates while a value is being debounced, - /// that value will be discarded and the returned producer will - /// terminate immediately. - /// - /// - parameters: - /// - interval: A number of seconds to wait before sending a value. - /// - scheduler: A scheduler to send values on. - /// - /// - returns: A producer that sends values that are sent from `self` at - /// least `interval` seconds apart. - public func debounce(_ interval: TimeInterval, on scheduler: DateSchedulerProtocol) -> SignalProducer { - return lift { $0.debounce(interval, on: scheduler) } - } - - /// Forward events from `self` until `interval`. Then if producer isn't - /// completed yet, fails with `error` on `scheduler`. - /// - /// - note: 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. - /// - /// - parameters: - /// - interval: Number of seconds to wait for `self` to complete. - /// - error: Error to send with `failed` event if `self` is not completed - /// when `interval` passes. - /// - scheduler: A scheduler to deliver error on. - /// - /// - returns: A producer that sends events for at most `interval` seconds, - /// then, if not `completed` - sends `error` with `failed` event - /// on `scheduler`. - public func timeout(after interval: TimeInterval, raising error: Error, on scheduler: DateSchedulerProtocol) -> SignalProducer { - return lift { $0.timeout(after: interval, raising: error, on: scheduler) } - } -} - -extension SignalProducerProtocol where Value: OptionalProtocol { - /// Unwraps non-`nil` values and forwards them on the returned signal, `nil` - /// values are dropped. - /// - /// - returns: A producer that sends only non-nil values. - public func skipNil() -> SignalProducer { - return lift { $0.skipNil() } - } -} - -extension SignalProducerProtocol where Value: EventProtocol, Error == NoError { - /// The inverse of materialize(), this will translate a producer of `Event` - /// _values_ into a producer of those events themselves. - /// - /// - returns: A producer that sends values carried by `self` events. - public func dematerialize() -> SignalProducer { - return lift { $0.dematerialize() } - } -} - -extension SignalProducerProtocol where Error == NoError { - /// Promote a producer that does not generate failures into one that can. - /// - /// - note: This does not actually cause failers to be generated for the - /// given producer, but makes it easier to combine with other - /// producers that may fail; for example, with operators like - /// `combineLatestWith`, `zipWith`, `flatten`, etc. - /// - /// - parameters: - /// - _ An `ErrorType`. - /// - /// - returns: A producer that has an instantiatable `ErrorType`. - public func promoteErrors(_: F.Type) -> SignalProducer { - return lift { $0.promoteErrors(F.self) } - } - - /// Forward events from `self` until `interval`. Then if producer isn't - /// completed yet, fails with `error` on `scheduler`. - /// - /// - note: 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. - /// - /// - parameters: - /// - interval: Number of seconds to wait for `self` to complete. - /// - error: Error to send with `failed` event if `self` is not completed - /// when `interval` passes. - /// - scheudler: A scheduler to deliver error on. - /// - /// - returns: A producer that sends events for at most `interval` seconds, - /// then, if not `completed` - sends `error` with `failed` event - /// on `scheduler`. - public func timeout( - after interval: TimeInterval, - raising error: NewError, - on scheduler: DateSchedulerProtocol - ) -> SignalProducer { - return lift { $0.timeout(after: interval, raising: error, on: scheduler) } - } -} - -extension SignalProducerProtocol where Value: Equatable { - /// Forward only those values from `self` which are not duplicates of the - /// immedately preceding value. - /// - /// - note: The first value is always forwarded. - /// - /// - returns: A producer that does not send two equal values sequentially. - public func skipRepeats() -> SignalProducer { - return lift { $0.skipRepeats() } - } -} - -extension SignalProducerProtocol { - /// Forward only those values from `self` that have unique identities across - /// the set of all values that have been seen. - /// - /// - note: This causes the identities to be retained to check for - /// uniqueness. - /// - /// - parameters: - /// - transform: A closure that accepts a value and returns identity - /// value. - /// - /// - returns: A producer that sends unique values during its lifetime. - public func uniqueValues(_ transform: (Value) -> Identity) -> SignalProducer { - return lift { $0.uniqueValues(transform) } - } -} - -extension SignalProducerProtocol where Value: Hashable { - /// Forward only those values from `self` that are unique across the set of - /// all values that have been seen. - /// - /// - note: This causes the values to be retained to check for uniqueness. - /// Providing a function that returns a unique value for each sent - /// value can help you reduce the memory footprint. - /// - /// - returns: A producer that sends unique values during its lifetime. - public func uniqueValues() -> SignalProducer { - return lift { $0.uniqueValues() } - } -} - -extension SignalProducerProtocol { - /// Injects side effects to be performed upon the specified producer events. - /// - /// - note: In a composed producer, `starting` is invoked in the reverse - /// direction of the flow of events. - /// - /// - parameters: - /// - starting: A closure that is invoked before the producer is started. - /// - started: A closure that is invoked after the producer is started. - /// - event: A closure that accepts an event and is invoked on every - /// received event. - /// - next: A closure that accepts a value from `next` event. - /// - failed: A closure that accepts error object and is invoked for - /// `failed` event. - /// - completed: A closure that is invoked for `completed` event. - /// - interrupted: A closure that is invoked for `interrupted` event. - /// - terminated: A closure that is invoked for any terminating event. - /// - disposed: A closure added as disposable when signal completes. - /// - /// - returns: A producer with attached side-effects for given event cases. - public func on( - starting: (() -> Void)? = nil, - started: (() -> Void)? = nil, - event: ((Event) -> Void)? = nil, - next: ((Value) -> Void)? = nil, - failed: ((Error) -> Void)? = nil, - completed: (() -> Void)? = nil, - interrupted: (() -> Void)? = nil, - terminated: (() -> Void)? = nil, - disposed: (() -> Void)? = nil - ) -> SignalProducer { - return SignalProducer { observer, compositeDisposable in - starting?() - defer { started?() } - - self.startWithSignal { signal, disposable in - compositeDisposable += disposable - compositeDisposable += signal - .on( - event: event, - failed: failed, - completed: completed, - interrupted: interrupted, - terminated: terminated, - disposed: disposed, - next: next - ) - .observe(observer) - } - } - } - - /// Start the returned producer on the given `Scheduler`. - /// - /// - note: This implies that any side effects embedded in the producer will - /// be performed on the given scheduler as well. - /// - /// - note: Events may still be sent upon other schedulers — this merely - /// affects where the `start()` method is run. - /// - /// - parameters: - /// - scheduler: A scheduler to deliver events on. - /// - /// - returns: A producer that will deliver events on given `scheduler` when - /// started. - public func start(on scheduler: SchedulerProtocol) -> SignalProducer { - return SignalProducer { observer, compositeDisposable in - compositeDisposable += scheduler.schedule { - self.startWithSignal { signal, signalDisposable in - compositeDisposable += signalDisposable - signal.observe(observer) - } - } - } - } -} - -extension SignalProducerProtocol { - /// Combines the values of all the given producers, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: SignalProducer, _ b: SignalProducer) -> SignalProducer<(Value, B), Error> { - return a.combineLatest(with: b) - } - - /// Combines the values of all the given producers, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer) -> SignalProducer<(Value, B, C), Error> { - return combineLatest(a, b) - .combineLatest(with: c) - .map(repack) - } - - /// Combines the values of all the given producers, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer) -> SignalProducer<(Value, B, C, D), Error> { - return combineLatest(a, b, c) - .combineLatest(with: d) - .map(repack) - } - - /// Combines the values of all the given producers, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer) -> SignalProducer<(Value, B, C, D, E), Error> { - return combineLatest(a, b, c, d) - .combineLatest(with: e) - .map(repack) - } - - /// Combines the values of all the given producers, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F), Error> { - return combineLatest(a, b, c, d, e) - .combineLatest(with: f) - .map(repack) - } - - /// Combines the values of all the given producers, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer, _ g: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F, G), Error> { - return combineLatest(a, b, c, d, e, f) - .combineLatest(with: g) - .map(repack) - } - - /// Combines the values of all the given producers, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer, _ g: SignalProducer, _ h: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F, G, H), Error> { - return combineLatest(a, b, c, d, e, f, g) - .combineLatest(with: h) - .map(repack) - } - - /// Combines the values of all the given producers, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer, _ g: SignalProducer, _ h: SignalProducer, _ i: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F, G, H, I), Error> { - return combineLatest(a, b, c, d, e, f, g, h) - .combineLatest(with: i) - .map(repack) - } - - /// Combines the values of all the given producers, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer, _ g: SignalProducer, _ h: SignalProducer, _ i: SignalProducer, _ j: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F, G, H, I, J), Error> { - return combineLatest(a, b, c, d, e, f, g, h, i) - .combineLatest(with: j) - .map(repack) - } - - /// Combines the values of all the given producers, in the manner described by - /// `combineLatestWith`. Will return an empty `SignalProducer` if the sequence is empty. - public static func combineLatest>(_ producers: S) -> SignalProducer<[Value], Error> { - var generator = producers.makeIterator() - if let first = generator.next() { - let initial = first.map { [$0] } - return IteratorSequence(generator).reduce(initial) { producer, next in - producer.combineLatest(with: next).map { $0.0 + [$0.1] } - } - } - - return .empty - } - - /// Zips the values of all the given producers, in the manner described by - /// `zipWith`. - public static func zip(_ a: SignalProducer, _ b: SignalProducer) -> SignalProducer<(Value, B), Error> { - return a.zip(with: b) - } - - /// Zips the values of all the given producers, in the manner described by - /// `zipWith`. - public static func zip(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer) -> SignalProducer<(Value, B, C), Error> { - return zip(a, b) - .zip(with: c) - .map(repack) - } - - /// Zips the values of all the given producers, in the manner described by - /// `zipWith`. - public static func zip(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer) -> SignalProducer<(Value, B, C, D), Error> { - return zip(a, b, c) - .zip(with: d) - .map(repack) - } - - /// Zips the values of all the given producers, in the manner described by - /// `zipWith`. - public static func zip(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer) -> SignalProducer<(Value, B, C, D, E), Error> { - return zip(a, b, c, d) - .zip(with: e) - .map(repack) - } - - /// Zips the values of all the given producers, in the manner described by - /// `zipWith`. - public static func zip(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F), Error> { - return zip(a, b, c, d, e) - .zip(with: f) - .map(repack) - } - - /// Zips the values of all the given producers, in the manner described by - /// `zipWith`. - public static func zip(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer, _ g: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F, G), Error> { - return zip(a, b, c, d, e, f) - .zip(with: g) - .map(repack) - } - - /// Zips the values of all the given producers, in the manner described by - /// `zipWith`. - public static func zip(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer, _ g: SignalProducer, _ h: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F, G, H), Error> { - return zip(a, b, c, d, e, f, g) - .zip(with: h) - .map(repack) - } - - /// Zips the values of all the given producers, in the manner described by - /// `zipWith`. - public static func zip(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer, _ g: SignalProducer, _ h: SignalProducer, _ i: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F, G, H, I), Error> { - return zip(a, b, c, d, e, f, g, h) - .zip(with: i) - .map(repack) - } - - /// Zips the values of all the given producers, in the manner described by - /// `zipWith`. - public static func zip(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer, _ g: SignalProducer, _ h: SignalProducer, _ i: SignalProducer, _ j: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F, G, H, I, J), Error> { - return zip(a, b, c, d, e, f, g, h, i) - .zip(with: j) - .map(repack) - } - - /// Zips the values of all the given producers, in the manner described by - /// `zipWith`. Will return an empty `SignalProducer` if the sequence is empty. - public static func zip>(_ producers: S) -> SignalProducer<[Value], Error> { - var generator = producers.makeIterator() - if let first = generator.next() { - let initial = first.map { [$0] } - return IteratorSequence(generator).reduce(initial) { producer, next in - producer.zip(with: next).map { $0.0 + [$0.1] } - } - } - - return .empty - } -} - -extension SignalProducerProtocol { - /// Repeat `self` a total of `count` times. In other words, start producer - /// `count` number of times, each one after previously started producer - /// completes. - /// - /// - note: Repeating `1` time results in an equivalent signal producer. - /// - /// - note: Repeating `0` times results in a producer that instantly - /// completes. - /// - /// - parameters: - /// - count: Number of repetitions. - /// - /// - returns: A signal producer start sequentially starts `self` after - /// previously started producer completes. - public func times(_ count: Int) -> SignalProducer { - precondition(count >= 0) - - if count == 0 { - return .empty - } else if count == 1 { - return producer - } - - return SignalProducer { observer, disposable in - let serialDisposable = SerialDisposable() - disposable += serialDisposable - - func iterate(_ current: Int) { - self.startWithSignal { signal, signalDisposable in - serialDisposable.innerDisposable = signalDisposable - - signal.observe { event in - if case .completed = event { - let remainingTimes = current - 1 - if remainingTimes > 0 { - iterate(remainingTimes) - } else { - observer.sendCompleted() - } - } else { - observer.action(event) - } - } - } - } - - iterate(count) - } - } - - /// Ignore failures up to `count` times. - /// - /// - precondition: `count` must be non-negative integer. - /// - /// - parameters: - /// - count: Number of retries. - /// - /// - returns: A signal producer that restarts up to `count` times. - public func retry(upTo count: Int) -> SignalProducer { - precondition(count >= 0) - - if count == 0 { - return producer - } else { - return flatMapError { _ in - self.retry(upTo: count - 1) - } - } - } - - /// Wait for completion of `self`, *then* forward all events from - /// `replacement`. Any failure or interruption sent from `self` is - /// forwarded immediately, in which case `replacement` will not be started, - /// and none of its events will be be forwarded. - /// - /// - note: All values sent from `self` are ignored. - /// - /// - parameters: - /// - replacement: A producer to start when `self` completes. - /// - /// - returns: A producer that sends events from `self` and then from - /// `replacement` when `self` completes. - public func then(_ replacement: SignalProducer) -> SignalProducer { - return SignalProducer { observer, observerDisposable in - self.startWithSignal { signal, signalDisposable in - observerDisposable += signalDisposable - - signal.observe { event in - switch event { - case let .failed(error): - observer.sendFailed(error) - case .completed: - observerDisposable += replacement.start(observer) - case .interrupted: - observer.sendInterrupted() - case .next: - break - } - } - } - } - } - - /// Start the producer, then block, waiting for the first value. - /// - /// When a single value or error is sent, the returned `Result` will - /// represent those cases. However, when no values are sent, `nil` will be - /// returned. - /// - /// - returns: Result when single `next` or `failed` event is received. - /// `nil` when no events are received. - public func first() -> Result? { - return take(first: 1).single() - } - - /// Start the producer, then block, waiting for events: Next and - /// Completed. - /// - /// When a single value or error is sent, the returned `Result` will - /// represent those cases. However, when no values are sent, or when more - /// than one value is sent, `nil` will be returned. - /// - /// - returns: Result when single `next` or `failed` event is received. - /// `nil` when 0 or more than 1 events are received. - public func single() -> Result? { - let semaphore = DispatchSemaphore(value: 0) - var result: Result? - - take(first: 2).start { event in - switch event { - case let .next(value): - if result != nil { - // Move into failure state after recieving another value. - result = nil - return - } - result = .success(value) - case let .failed(error): - result = .failure(error) - semaphore.signal() - case .completed, .interrupted: - semaphore.signal() - } - } - - semaphore.wait() - return result - } - - /// Start the producer, then block, waiting for the last value. - /// - /// When a single value or error is sent, the returned `Result` will - /// represent those cases. However, when no values are sent, `nil` will be - /// returned. - /// - /// - returns: Result when single `next` or `failed` event is received. - /// `nil` when no events are received. - public func last() -> Result? { - return take(last: 1).single() - } - - /// Starts the producer, then blocks, waiting for completion. - /// - /// When a completion or error is sent, the returned `Result` will represent - /// those cases. - /// - /// - returns: Result when single `Completion` or `failed` event is - /// received. - public func wait() -> Result<(), Error> { - return then(SignalProducer<(), Error>(value: ())).last() ?? .success(()) - } - - /// Creates a new `SignalProducer` that will multicast values emitted by - /// the underlying producer, up to `capacity`. - /// This means that all clients of this `SignalProducer` will see the same - /// version of the emitted values/errors. - /// - /// The underlying `SignalProducer` will not be started until `self` is - /// started for the first time. When subscribing to this producer, all - /// previous values (up to `capacity`) will be emitted, followed by any new - /// values. - /// - /// If you find yourself needing *the current value* (the last buffered - /// value) you should consider using `PropertyType` instead, which, unlike - /// this operator, will guarantee at compile time that there's always a - /// buffered value. This operator is not recommended in most cases, as it - /// will introduce an implicit relationship between the original client and - /// the rest, so consider alternatives like `PropertyType`, or representing - /// your stream using a `Signal` instead. - /// - /// This operator is only recommended when you absolutely need to introduce - /// a layer of caching in front of another `SignalProducer`. - /// - /// - precondtion: `capacity` must be non-negative integer. - /// - /// - parameters: - /// - capcity: Number of values to hold. - /// - /// - returns: A caching producer that will hold up to last `capacity` - /// values. - public func replayLazily(upTo capacity: Int) -> SignalProducer { - precondition(capacity >= 0, "Invalid capacity: \(capacity)") - - // This will go "out of scope" when the returned `SignalProducer` goes - // out of scope. This lets us know when we're supposed to dispose the - // underlying producer. This is necessary because `struct`s don't have - // `deinit`. - let lifetimeToken = Lifetime.Token() - let lifetime = Lifetime(lifetimeToken) - - let state = Atomic(ReplayState(upTo: capacity)) - - let start: Atomic<(() -> Void)?> = Atomic { - // Start the underlying producer. - self - .take(during: lifetime) - .start { event in - let observers: Bag.Observer>? = state.modify { state in - defer { state.enqueue(event) } - return state.observers - } - observers?.forEach { $0.action(event) } - } - } - - return SignalProducer { observer, disposable in - // Don't dispose of the original producer until all observers - // have terminated. - disposable += { _ = lifetimeToken } - - while true { - var result: Result>! - state.modify { - result = $0.observe(observer) - } - - switch result! { - case let .success(token): - if let token = token { - disposable += { - state.modify { - $0.removeObserver(using: token) - } - } - } - - // Start the underlying producer if it has never been started. - start.swap(nil)?() - - // Terminate the replay loop. - return - - case let .failure(error): - error.values.forEach(observer.sendNext) - } - } - } - } -} - -/// Represents a recoverable error of an observer not being ready for an -/// attachment to a `ReplayState`, and the observer should replay the supplied -/// values before attempting to observe again. -private struct ReplayError: Error { - /// The values that should be replayed by the observer. - let values: [Value] -} - -private struct ReplayState { - let capacity: Int - - /// All cached values. - var values: [Value] = [] - - /// A termination event emitted by the underlying producer. - /// - /// This will be nil if termination has not occurred. - var terminationEvent: Event? - - /// The observers currently attached to the caching producer, or `nil` if the - /// caching producer was terminated. - var observers: Bag.Observer>? = Bag() - - /// The set of in-flight replay buffers. - var replayBuffers: [ObjectIdentifier: [Value]] = [:] - - /// Initialize the replay state. - /// - /// - parameters: - /// - capacity: The maximum amount of values which can be cached by the - /// replay state. - init(upTo capacity: Int) { - self.capacity = capacity - } - - /// Attempt to observe the replay state. - /// - /// - warning: Repeatedly observing the replay state with the same observer - /// should be avoided. - /// - /// - parameters: - /// - observer: The observer to be registered. - /// - /// - returns: - /// If the observer is successfully attached, a `Result.success` with the - /// corresponding removal token would be returned. Otherwise, a - /// `Result.failure` with a `ReplayError` would be returned. - mutating func observe(_ observer: Signal.Observer) -> Result> { - // Since the only use case is `replayLazily`, which always creates a unique - // `Observer` for every produced signal, we can use the ObjectIdentifier of - // the `Observer` to track them directly. - let id = ObjectIdentifier(observer) - - switch replayBuffers[id] { - case .none where !values.isEmpty: - // No in-flight replay buffers was found, but the `ReplayState` has one or - // more cached values in the `ReplayState`. The observer should replay - // them before attempting to observe again. - replayBuffers[id] = [] - return .failure(ReplayError(values: values)) - - case let .some(buffer) where !buffer.isEmpty: - // An in-flight replay buffer was found with one or more buffered values. - // The observer should replay them before attempting to observe again. - defer { replayBuffers[id] = [] } - return .failure(ReplayError(values: buffer)) - - case let .some(buffer) where buffer.isEmpty: - // Since an in-flight but empty replay buffer was found, the observer is - // ready to be attached to the `ReplayState`. - replayBuffers.removeValue(forKey: id) - - default: - // No values has to be replayed. The observer is ready to be attached to - // the `ReplayState`. - break - } - - if let event = terminationEvent { - observer.action(event) - } - - return .success(observers?.insert(observer)) - } - - /// Enqueue the supplied event to the replay state. - /// - /// - parameter: - /// - event: The event to be cached. - mutating func enqueue(_ event: Event) { - switch event { - case let .next(value): - for key in replayBuffers.keys { - replayBuffers[key]!.append(value) - } - - switch capacity { - case 0: - // With a capacity of zero, `state.values` can never be filled. - break - - case 1: - values = [value] - - default: - values.append(value) - - let overflow = values.count - capacity - if overflow > 0 { - values.removeFirst(overflow) - } - } - - case .completed, .failed, .interrupted: - // Disconnect all observers and prevent future attachments. - terminationEvent = event - observers = nil - } - } - - /// Remove the observer represented by the supplied token. - /// - /// - parameters: - /// - token: The token of the observer to be removed. - mutating func removeObserver(using token: RemovalToken) { - observers?.remove(using: token) - } -} - -/// Create a repeating timer of the given interval, with a reasonable default -/// leeway, sending updates on the given scheduler. -/// -/// - note: This timer will never complete naturally, so all invocations of -/// `start()` must be disposed to avoid leaks. -/// -/// - precondition: Interval must be non-negative number. -/// -/// - parameters: -/// - interval: An interval between invocations. -/// - scheduler: A scheduler to deliver events on. -/// -/// - returns: A producer that sends `NSDate` values every `interval` seconds. -public func timer(interval: TimeInterval, on scheduler: DateSchedulerProtocol) -> SignalProducer { - // Apple's "Power Efficiency Guide for Mac Apps" recommends a leeway of - // at least 10% of the timer interval. - return timer(interval: interval, on: scheduler, leeway: interval * 0.1) -} - -/// Creates a repeating timer of the given interval, sending updates on the -/// given scheduler. -/// -/// - note: This timer will never complete naturally, so all invocations of -/// `start()` must be disposed to avoid leaks. -/// -/// - precondition: Interval must be non-negative number. -/// -/// - precondition: Leeway must be non-negative number. -/// -/// - parameters: -/// - interval: An interval between invocations. -/// - scheduler: A scheduler to deliver events on. -/// - leeway: Interval leeway. Apple's "Power Efficiency Guide for Mac Apps" -/// recommends a leeway of at least 10% of the timer interval. -/// -/// - returns: A producer that sends `NSDate` values every `interval` seconds. -public func timer(interval: TimeInterval, on scheduler: DateSchedulerProtocol, leeway: TimeInterval) -> SignalProducer { - precondition(interval >= 0) - precondition(leeway >= 0) - - return SignalProducer { observer, compositeDisposable in - compositeDisposable += scheduler.schedule(after: scheduler.currentDate.addingTimeInterval(interval), - interval: interval, - leeway: leeway, - action: { observer.sendNext(scheduler.currentDate) }) - } -} diff --git a/ReactiveCocoa/Swift/TupleExtensions.swift b/ReactiveCocoa/Swift/TupleExtensions.swift deleted file mode 100644 index 31e096c30e..0000000000 --- a/ReactiveCocoa/Swift/TupleExtensions.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// TupleExtensions.swift -// ReactiveCocoa -// -// Created by Justin Spahr-Summers on 2014-12-20. -// Copyright (c) 2014 GitHub. All rights reserved. -// - -/// Adds a value into an N-tuple, returning an (N+1)-tuple. -/// -/// Supports creating tuples up to 10 elements long. -internal func repack(_ t: (A, B), value: C) -> (A, B, C) { - return (t.0, t.1, value) -} - -internal func repack(_ t: (A, B, C), value: D) -> (A, B, C, D) { - return (t.0, t.1, t.2, value) -} - -internal func repack(_ t: (A, B, C, D), value: E) -> (A, B, C, D, E) { - return (t.0, t.1, t.2, t.3, value) -} - -internal func repack(_ t: (A, B, C, D, E), value: F) -> (A, B, C, D, E, F) { - return (t.0, t.1, t.2, t.3, t.4, value) -} - -internal func repack(_ t: (A, B, C, D, E, F), value: G) -> (A, B, C, D, E, F, G) { - return (t.0, t.1, t.2, t.3, t.4, t.5, value) -} - -internal func repack(_ t: (A, B, C, D, E, F, G), value: H) -> (A, B, C, D, E, F, G, H) { - return (t.0, t.1, t.2, t.3, t.4, t.5, t.6, value) -} - -internal func repack(_ t: (A, B, C, D, E, F, G, H), value: I) -> (A, B, C, D, E, F, G, H, I) { - return (t.0, t.1, t.2, t.3, t.4, t.5, t.6, t.7, value) -} - -internal func repack(_ t: (A, B, C, D, E, F, G, H, I), value: J) -> (A, B, C, D, E, F, G, H, I, J) { - return (t.0, t.1, t.2, t.3, t.4, t.5, t.6, t.7, t.8, value) -} diff --git a/ReactiveCocoaTests/Swift/ActionSpec.swift b/ReactiveCocoaTests/Swift/ActionSpec.swift deleted file mode 100755 index fc6c7fbfd6..0000000000 --- a/ReactiveCocoaTests/Swift/ActionSpec.swift +++ /dev/null @@ -1,142 +0,0 @@ -// -// ActionSpec.swift -// ReactiveCocoa -// -// Created by Justin Spahr-Summers on 2014-12-11. -// Copyright (c) 2014 GitHub. All rights reserved. -// - -import Result -import Nimble -import Quick -import ReactiveCocoa - -class ActionSpec: QuickSpec { - override func spec() { - describe("Action") { - var action: Action! - var enabled: MutableProperty! - - var executionCount = 0 - var values: [String] = [] - var errors: [NSError] = [] - - var scheduler: TestScheduler! - let testError = NSError(domain: "ActionSpec", code: 1, userInfo: nil) - - beforeEach { - executionCount = 0 - values = [] - errors = [] - enabled = MutableProperty(false) - - scheduler = TestScheduler() - action = Action(enabledIf: enabled) { number in - return SignalProducer { observer, disposable in - executionCount += 1 - - if number % 2 == 0 { - observer.sendNext("\(number)") - observer.sendNext("\(number)\(number)") - - scheduler.schedule { - observer.sendCompleted() - } - } else { - scheduler.schedule { - observer.sendFailed(testError) - } - } - } - } - - action.values.observeNext { values.append($0) } - action.errors.observeNext { errors.append($0) } - } - - it("should be disabled and not executing after initialization") { - expect(action.isEnabled.value) == false - expect(action.isExecuting.value) == false - } - - it("should error if executed while disabled") { - var receivedError: ActionError? - action.apply(0).startWithFailed { - receivedError = $0 - } - - expect(receivedError).notTo(beNil()) - if let error = receivedError { - let expectedError = ActionError.disabled - expect(error == expectedError) == true - } - } - - it("should enable and disable based on the given property") { - enabled.value = true - expect(action.isEnabled.value) == true - expect(action.isExecuting.value) == false - - enabled.value = false - expect(action.isEnabled.value) == false - expect(action.isExecuting.value) == false - } - - describe("execution") { - beforeEach { - enabled.value = true - } - - it("should execute successfully") { - var receivedValue: String? - - action.apply(0) - .assumeNoErrors() - .startWithNext { - receivedValue = $0 - } - - expect(executionCount) == 1 - expect(action.isExecuting.value) == true - expect(action.isEnabled.value) == false - - expect(receivedValue) == "00" - expect(values) == [ "0", "00" ] - expect(errors) == [] - - scheduler.run() - expect(action.isExecuting.value) == false - expect(action.isEnabled.value) == true - - expect(values) == [ "0", "00" ] - expect(errors) == [] - } - - it("should execute with an error") { - var receivedError: ActionError? - - action.apply(1).startWithFailed { - receivedError = $0 - } - - expect(executionCount) == 1 - expect(action.isExecuting.value) == true - expect(action.isEnabled.value) == false - - scheduler.run() - expect(action.isExecuting.value) == false - expect(action.isEnabled.value) == true - - expect(receivedError).notTo(beNil()) - if let error = receivedError { - let expectedError = ActionError.producerFailed(testError) - expect(error == expectedError) == true - } - - expect(values) == [] - expect(errors) == [ testError ] - } - } - } - } -} diff --git a/ReactiveCocoaTests/Swift/AtomicSpec.swift b/ReactiveCocoaTests/Swift/AtomicSpec.swift deleted file mode 100644 index 6fbf8e69d7..0000000000 --- a/ReactiveCocoaTests/Swift/AtomicSpec.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// AtomicSpec.swift -// ReactiveCocoa -// -// Created by Justin Spahr-Summers on 2014-07-13. -// Copyright (c) 2014 GitHub. All rights reserved. -// - -import Nimble -import Quick -import ReactiveCocoa - -class AtomicSpec: QuickSpec { - override func spec() { - var atomic: Atomic! - - beforeEach { - atomic = Atomic(1) - } - - it("should read and write the value directly") { - expect(atomic.value) == 1 - - atomic.value = 2 - expect(atomic.value) == 2 - } - - it("should swap the value atomically") { - expect(atomic.swap(2)) == 1 - expect(atomic.value) == 2 - } - - it("should modify the value atomically") { - atomic.modify { $0 += 1 } - expect(atomic.value) == 2 - } - - it("should perform an action with the value") { - let result: Bool = atomic.withValue { $0 == 1 } - expect(result) == true - expect(atomic.value) == 1 - } - } -} diff --git a/ReactiveCocoaTests/Swift/BagSpec.swift b/ReactiveCocoaTests/Swift/BagSpec.swift deleted file mode 100644 index b7841486c8..0000000000 --- a/ReactiveCocoaTests/Swift/BagSpec.swift +++ /dev/null @@ -1,54 +0,0 @@ -// -// BagSpec.swift -// ReactiveCocoa -// -// Created by Justin Spahr-Summers on 2014-07-13. -// Copyright (c) 2014 GitHub. All rights reserved. -// - -import Nimble -import Quick -import ReactiveCocoa - -class BagSpec: QuickSpec { - override func spec() { - var bag = Bag() - - beforeEach { - bag = Bag() - } - - it("should insert values") { - bag.insert("foo") - bag.insert("bar") - bag.insert("buzz") - - expect(bag).to(contain("foo")) - expect(bag).to(contain("bar")) - expect(bag).to(contain("buzz")) - expect(bag).toNot(contain("fuzz")) - expect(bag).toNot(contain("foobar")) - } - - it("should remove values given the token from insertion") { - let a = bag.insert("foo") - let b = bag.insert("bar") - let c = bag.insert("buzz") - - bag.remove(using: b) - expect(bag).to(contain("foo")) - expect(bag).toNot(contain("bar")) - expect(bag).to(contain("buzz")) - - bag.remove(using: a) - expect(bag).toNot(contain("foo")) - expect(bag).toNot(contain("bar")) - expect(bag).to(contain("buzz")) - - bag.remove(using: c) - expect(bag).toNot(contain("foo")) - expect(bag).toNot(contain("bar")) - expect(bag).toNot(contain("buzz")) - } - } -} diff --git a/ReactiveCocoaTests/Swift/CocoaActionSpec.swift b/ReactiveCocoaTests/Swift/CocoaActionSpec.swift index a11db898e5..d99d777e96 100644 --- a/ReactiveCocoaTests/Swift/CocoaActionSpec.swift +++ b/ReactiveCocoaTests/Swift/CocoaActionSpec.swift @@ -1,3 +1,4 @@ +import ReactiveSwift import Result import Nimble import Quick diff --git a/ReactiveCocoaTests/Swift/DisposableSpec.swift b/ReactiveCocoaTests/Swift/DisposableSpec.swift deleted file mode 100644 index e6928c89ce..0000000000 --- a/ReactiveCocoaTests/Swift/DisposableSpec.swift +++ /dev/null @@ -1,143 +0,0 @@ -// -// DisposableSpec.swift -// ReactiveCocoa -// -// Created by Justin Spahr-Summers on 2014-07-13. -// Copyright (c) 2014 GitHub. All rights reserved. -// - -import Nimble -import Quick -import ReactiveCocoa - -class DisposableSpec: QuickSpec { - override func spec() { - describe("SimpleDisposable") { - it("should set disposed to true") { - let disposable = SimpleDisposable() - expect(disposable.isDisposed) == false - - disposable.dispose() - expect(disposable.isDisposed) == true - } - } - - describe("ActionDisposable") { - it("should run the given action upon disposal") { - var didDispose = false - let disposable = ActionDisposable { - didDispose = true - } - - expect(didDispose) == false - expect(disposable.isDisposed) == false - - disposable.dispose() - expect(didDispose) == true - expect(disposable.isDisposed) == true - } - } - - describe("CompositeDisposable") { - var disposable = CompositeDisposable() - - beforeEach { - disposable = CompositeDisposable() - } - - it("should ignore the addition of nil") { - disposable.add(nil) - return - } - - it("should dispose of added disposables") { - let simpleDisposable = SimpleDisposable() - disposable += simpleDisposable - - var didDispose = false - disposable += { - didDispose = true - } - - expect(simpleDisposable.isDisposed) == false - expect(didDispose) == false - expect(disposable.isDisposed) == false - - disposable.dispose() - expect(simpleDisposable.isDisposed) == true - expect(didDispose) == true - expect(disposable.isDisposed) == true - } - - it("should not dispose of removed disposables") { - let simpleDisposable = SimpleDisposable() - let handle = disposable += simpleDisposable - - // We should be allowed to call this any number of times. - handle.remove() - handle.remove() - expect(simpleDisposable.isDisposed) == false - - disposable.dispose() - expect(simpleDisposable.isDisposed) == false - } - } - - describe("ScopedDisposable") { - it("should dispose of the inner disposable upon deinitialization") { - let simpleDisposable = SimpleDisposable() - - func runScoped() { - let scopedDisposable = ScopedDisposable(simpleDisposable) - expect(simpleDisposable.isDisposed) == false - expect(scopedDisposable.isDisposed) == false - } - - expect(simpleDisposable.isDisposed) == false - - runScoped() - expect(simpleDisposable.isDisposed) == true - } - } - - describe("SerialDisposable") { - var disposable: SerialDisposable! - - beforeEach { - disposable = SerialDisposable() - } - - it("should dispose of the inner disposable") { - let simpleDisposable = SimpleDisposable() - disposable.innerDisposable = simpleDisposable - - expect(disposable.innerDisposable).notTo(beNil()) - expect(simpleDisposable.isDisposed) == false - expect(disposable.isDisposed) == false - - disposable.dispose() - expect(disposable.innerDisposable).to(beNil()) - expect(simpleDisposable.isDisposed) == true - expect(disposable.isDisposed) == true - } - - it("should dispose of the previous disposable when swapping innerDisposable") { - let oldDisposable = SimpleDisposable() - let newDisposable = SimpleDisposable() - - disposable.innerDisposable = oldDisposable - expect(oldDisposable.isDisposed) == false - expect(newDisposable.isDisposed) == false - - disposable.innerDisposable = newDisposable - expect(oldDisposable.isDisposed) == true - expect(newDisposable.isDisposed) == false - expect(disposable.isDisposed) == false - - disposable.innerDisposable = nil - expect(newDisposable.isDisposed) == true - expect(disposable.isDisposed) == false - } - } - } -} diff --git a/ReactiveCocoaTests/Swift/DynamicPropertySpec.swift b/ReactiveCocoaTests/Swift/DynamicPropertySpec.swift new file mode 100644 index 0000000000..6eb5cf78fc --- /dev/null +++ b/ReactiveCocoaTests/Swift/DynamicPropertySpec.swift @@ -0,0 +1,233 @@ +// +// DynamicPropertySpec.swift +// ReactiveCocoa +// +// Created by Justin Spahr-Summers on 2015-01-23. +// Copyright (c) 2015 GitHub. All rights reserved. +// + +import ReactiveSwift +import Result +import Nimble +import Quick +import ReactiveCocoa + +private let initialPropertyValue = "InitialValue" +private let subsequentPropertyValue = "SubsequentValue" +private let finalPropertyValue = "FinalValue" + +private let initialOtherPropertyValue = "InitialOtherValue" +private let subsequentOtherPropertyValue = "SubsequentOtherValue" +private let finalOtherPropertyValue = "FinalOtherValue" + +class DynamicPropertySpec: QuickSpec { + override func spec() { + describe("DynamicProperty") { + var object: ObservableObject! + var property: DynamicProperty! + + let propertyValue: () -> Int? = { + if let value: AnyObject = property?.value { + return value as? Int + } else { + return nil + } + } + + beforeEach { + object = ObservableObject() + expect(object.rac_value) == 0 + + property = DynamicProperty(object: object, keyPath: "rac_value") + } + + afterEach { + object = nil + } + + it("should read the underlying object") { + expect(propertyValue()) == 0 + + object.rac_value = 1 + expect(propertyValue()) == 1 + } + + it("should write the underlying object") { + property.value = 1 + expect(object.rac_value) == 1 + expect(propertyValue()) == 1 + } + + it("should yield a producer that sends the current value and then the changes for the key path of the underlying object") { + var values: [Int] = [] + property.producer.startWithNext { value in + expect(value).notTo(beNil()) + values.append(value!) + } + + expect(values) == [ 0 ] + + property.value = 1 + expect(values) == [ 0, 1 ] + + object.rac_value = 2 + expect(values) == [ 0, 1, 2 ] + } + + it("should yield a producer that sends the current value and then the changes for the key path of the underlying object, even if the value actually remains unchanged") { + var values: [Int] = [] + property.producer.startWithNext { value in + expect(value).notTo(beNil()) + values.append(value!) + } + + expect(values) == [ 0 ] + + property.value = 0 + expect(values) == [ 0, 0 ] + + object.rac_value = 0 + expect(values) == [ 0, 0, 0 ] + } + + it("should yield a signal that emits subsequent values for the key path of the underlying object") { + var values: [Int] = [] + property.signal.observeNext { value in + expect(value).notTo(beNil()) + values.append(value!) + } + + expect(values) == [] + + property.value = 1 + expect(values) == [ 1 ] + + object.rac_value = 2 + expect(values) == [ 1, 2 ] + } + + it("should yield a signal that emits subsequent values for the key path of the underlying object, even if the value actually remains unchanged") { + var values: [Int] = [] + property.signal.observeNext { value in + expect(value).notTo(beNil()) + values.append(value!) + } + + expect(values) == [] + + property.value = 0 + expect(values) == [ 0 ] + + object.rac_value = 0 + expect(values) == [ 0, 0 ] + } + + it("should have a completed producer when the underlying object deallocates") { + var completed = false + + property = { + // Use a closure so this object has a shorter lifetime. + let object = ObservableObject() + let property = DynamicProperty(object: object, keyPath: "rac_value") + + property.producer.startWithCompleted { + completed = true + } + + expect(completed) == false + expect(property.value).notTo(beNil()) + return property + }() + + expect(completed).toEventually(beTruthy()) + expect(property.value).to(beNil()) + } + + it("should have a completed signal when the underlying object deallocates") { + var completed = false + + property = { + // Use a closure so this object has a shorter lifetime. + let object = ObservableObject() + let property = DynamicProperty(object: object, keyPath: "rac_value") + + property.signal.observeCompleted { + completed = true + } + + expect(completed) == false + expect(property.value).notTo(beNil()) + return property + }() + + expect(completed).toEventually(beTruthy()) + expect(property.value).to(beNil()) + } + + it("should retain property while DynamicProperty's underlying object is retained"){ + weak var dynamicProperty: DynamicProperty? = property + + property = nil + expect(dynamicProperty).toNot(beNil()) + + object = nil + expect(dynamicProperty).to(beNil()) + } + + it("should support un-bridged reference types") { + let dynamicProperty = DynamicProperty(object: object, keyPath: "rac_reference") + dynamicProperty.value = UnbridgedObject("foo") + expect(object.rac_reference.value) == "foo" + } + } + + describe("binding") { + describe("to a dynamic property") { + var object: ObservableObject! + var property: DynamicProperty! + + beforeEach { + object = ObservableObject() + expect(object.rac_value) == 0 + + property = DynamicProperty(object: object, keyPath: "rac_value") + } + + afterEach { + object = nil + } + + it("should bridge values sent on a signal to Objective-C") { + let (signal, observer) = Signal.pipe() + property <~ signal + observer.sendNext(1) + expect(object.rac_value) == 1 + } + + it("should bridge values sent on a signal producer to Objective-C") { + let producer = SignalProducer(value: 1) + property <~ producer + expect(object.rac_value) == 1 + } + + it("should bridge values from a source property to Objective-C") { + let source = MutableProperty(1) + property <~ source + expect(object.rac_value) == 1 + } + } + } + } +} + +private class ObservableObject: NSObject { + dynamic var rac_value: Int = 0 + dynamic var rac_reference: UnbridgedObject = UnbridgedObject("") +} + +private class UnbridgedObject: NSObject { + let value: String + init(_ value: String) { + self.value = value + } +} diff --git a/ReactiveCocoaTests/Swift/FlattenSpec.swift b/ReactiveCocoaTests/Swift/FlattenSpec.swift deleted file mode 100644 index e87c7efc34..0000000000 --- a/ReactiveCocoaTests/Swift/FlattenSpec.swift +++ /dev/null @@ -1,963 +0,0 @@ -// -// FlattenSpec.swift -// ReactiveCocoa -// -// Created by Oleg Shnitko on 1/22/16. -// Copyright © 2016 GitHub. All rights reserved. -// - -import Result -import Nimble -import Quick -import ReactiveCocoa - -private extension SignalProtocol { - typealias Pipe = (signal: Signal, observer: Observer) -} - -private typealias Pipe = Signal, TestError>.Pipe - -class FlattenSpec: QuickSpec { - override func spec() { - func describeSignalFlattenDisposal(_ flattenStrategy: FlattenStrategy, name: String) { - describe(name) { - var pipe: Pipe! - var disposable: Disposable? - - beforeEach { - pipe = Signal.pipe() - disposable = pipe.signal - .flatten(flattenStrategy) - .observe { _ in } - } - - afterEach { - disposable?.dispose() - } - - context("disposal") { - var disposed = false - - beforeEach { - disposed = false - pipe.observer.sendNext(SignalProducer { _, disposable in - disposable += ActionDisposable { - disposed = true - } - }) - } - - it("should dispose inner signals when outer signal interrupted") { - pipe.observer.sendInterrupted() - expect(disposed) == true - } - - it("should dispose inner signals when outer signal failed") { - pipe.observer.sendFailed(.default) - expect(disposed) == true - } - - it("should not dispose inner signals when outer signal completed") { - pipe.observer.sendCompleted() - expect(disposed) == false - } - } - } - } - - context("Signal") { - describeSignalFlattenDisposal(.latest, name: "switchToLatest") - describeSignalFlattenDisposal(.merge, name: "merge") - describeSignalFlattenDisposal(.concat, name: "concat") - } - - func describeSignalProducerFlattenDisposal(_ flattenStrategy: FlattenStrategy, name: String) { - describe(name) { - it("disposes original signal when result signal interrupted") { - var disposed = false - - let disposable = SignalProducer, NoError> { _, disposable in - disposable += ActionDisposable { - disposed = true - } - } - .flatten(flattenStrategy) - .start() - - disposable.dispose() - expect(disposed) == true - } - } - } - - context("SignalProducer") { - describeSignalProducerFlattenDisposal(.latest, name: "switchToLatest") - describeSignalProducerFlattenDisposal(.merge, name: "merge") - describeSignalProducerFlattenDisposal(.concat, name: "concat") - } - - describe("Signal.flatten()") { - it("works with TestError and a TestError Signal") { - typealias Inner = Signal - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .assumeNoErrors() - .observeNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a TestError Signal") { - typealias Inner = Signal - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .assumeNoErrors() - .observeNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a NoError Signal") { - typealias Inner = Signal - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .observeNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with TestError and a NoError Signal") { - typealias Inner = Signal - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .assumeNoErrors() - .observeNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with TestError and a TestError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .assumeNoErrors() - .observeNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a TestError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .assumeNoErrors() - .observeNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a NoError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .observeNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with TestError and a NoError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .assumeNoErrors() - .observeNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with SequenceType as a value") { - let (signal, innerObserver) = Signal<[Int], NoError>.pipe() - let sequence = [1, 2, 3] - var observedValues = [Int]() - - signal - .flatten(.concat) - .observeNext { value in - observedValues.append(value) - } - - innerObserver.sendNext(sequence) - expect(observedValues) == sequence - } - } - - describe("SignalProducer.flatten()") { - it("works with TestError and a TestError Signal") { - typealias Inner = Signal - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .assumeNoErrors() - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a TestError Signal") { - typealias Inner = Signal - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .assumeNoErrors() - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a NoError Signal") { - typealias Inner = Signal - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with TestError and a NoError Signal") { - typealias Inner = Signal - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .assumeNoErrors() - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with TestError and a TestError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .assumeNoErrors() - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a TestError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .assumeNoErrors() - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a NoError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with TestError and a NoError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .assumeNoErrors() - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with SequenceType as a value") { - let sequence = [1, 2, 3] - var observedValues = [Int]() - - let producer = SignalProducer<[Int], NoError>(value: sequence) - producer - .flatten(.latest) - .startWithNext { value in - observedValues.append(value) - } - - expect(observedValues) == sequence - } - } - - describe("Signal.flatMap()") { - it("works with TestError and a TestError Signal") { - typealias Inner = Signal - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .assumeNoErrors() - .observeNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a TestError Signal") { - typealias Inner = Signal - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .assumeNoErrors() - .observeNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a NoError Signal") { - typealias Inner = Signal - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .observeNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with TestError and a NoError Signal") { - typealias Inner = Signal - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .assumeNoErrors() - .observeNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with TestError and a TestError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .assumeNoErrors() - .observeNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a TestError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .assumeNoErrors() - .observeNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a NoError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .observeNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with TestError and a NoError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .assumeNoErrors() - .observeNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - } - - describe("SignalProducer.flatMap()") { - it("works with TestError and a TestError Signal") { - typealias Inner = Signal - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .assumeNoErrors() - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a TestError Signal") { - typealias Inner = Signal - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .assumeNoErrors() - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a NoError Signal") { - typealias Inner = Signal - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with TestError and a NoError Signal") { - typealias Inner = Signal - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .assumeNoErrors() - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with TestError and a TestError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .assumeNoErrors() - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a TestError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .assumeNoErrors() - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a NoError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with TestError and a NoError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .assumeNoErrors() - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - } - - describe("Signal.merge()") { - it("should emit values from all signals") { - let (signal1, observer1) = Signal.pipe() - let (signal2, observer2) = Signal.pipe() - - let mergedSignals = Signal.merge([signal1, signal2]) - - var lastValue: Int? - mergedSignals.observeNext { lastValue = $0 } - - expect(lastValue).to(beNil()) - - observer1.sendNext(1) - expect(lastValue) == 1 - - observer2.sendNext(2) - expect(lastValue) == 2 - - observer1.sendNext(3) - expect(lastValue) == 3 - } - - it("should not stop when one signal completes") { - let (signal1, observer1) = Signal.pipe() - let (signal2, observer2) = Signal.pipe() - - let mergedSignals = Signal.merge([signal1, signal2]) - - var lastValue: Int? - mergedSignals.observeNext { lastValue = $0 } - - expect(lastValue).to(beNil()) - - observer1.sendNext(1) - expect(lastValue) == 1 - - observer1.sendCompleted() - expect(lastValue) == 1 - - observer2.sendNext(2) - expect(lastValue) == 2 - } - - it("should complete when all signals complete") { - let (signal1, observer1) = Signal.pipe() - let (signal2, observer2) = Signal.pipe() - - let mergedSignals = Signal.merge([signal1, signal2]) - - var completed = false - mergedSignals.observeCompleted { completed = true } - - expect(completed) == false - - observer1.sendNext(1) - expect(completed) == false - - observer1.sendCompleted() - expect(completed) == false - - observer2.sendCompleted() - expect(completed) == true - } - } - - describe("SignalProducer.merge()") { - it("should emit values from all producers") { - let (signal1, observer1) = SignalProducer.pipe() - let (signal2, observer2) = SignalProducer.pipe() - - let mergedSignals = SignalProducer.merge([signal1, signal2]) - - var lastValue: Int? - mergedSignals.startWithNext { lastValue = $0 } - - expect(lastValue).to(beNil()) - - observer1.sendNext(1) - expect(lastValue) == 1 - - observer2.sendNext(2) - expect(lastValue) == 2 - - observer1.sendNext(3) - expect(lastValue) == 3 - } - - it("should not stop when one producer completes") { - let (signal1, observer1) = SignalProducer.pipe() - let (signal2, observer2) = SignalProducer.pipe() - - let mergedSignals = SignalProducer.merge([signal1, signal2]) - - var lastValue: Int? - mergedSignals.startWithNext { lastValue = $0 } - - expect(lastValue).to(beNil()) - - observer1.sendNext(1) - expect(lastValue) == 1 - - observer1.sendCompleted() - expect(lastValue) == 1 - - observer2.sendNext(2) - expect(lastValue) == 2 - } - - it("should complete when all producers complete") { - let (signal1, observer1) = SignalProducer.pipe() - let (signal2, observer2) = SignalProducer.pipe() - - let mergedSignals = SignalProducer.merge([signal1, signal2]) - - var completed = false - mergedSignals.startWithCompleted { completed = true } - - expect(completed) == false - - observer1.sendNext(1) - expect(completed) == false - - observer1.sendCompleted() - expect(completed) == false - - observer2.sendCompleted() - expect(completed) == true - } - } - - describe("SignalProducer.prefix()") { - it("should emit initial value") { - let (signal, observer) = SignalProducer.pipe() - - let mergedSignals = signal.prefix(value: 0) - - var lastValue: Int? - mergedSignals.startWithNext { lastValue = $0 } - - expect(lastValue) == 0 - - observer.sendNext(1) - expect(lastValue) == 1 - - observer.sendNext(2) - expect(lastValue) == 2 - - observer.sendNext(3) - expect(lastValue) == 3 - } - - it("should emit initial value") { - let (signal, observer) = SignalProducer.pipe() - - let mergedSignals = signal.prefix(SignalProducer(value: 0)) - - var lastValue: Int? - mergedSignals.startWithNext { lastValue = $0 } - - expect(lastValue) == 0 - - observer.sendNext(1) - expect(lastValue) == 1 - - observer.sendNext(2) - expect(lastValue) == 2 - - observer.sendNext(3) - expect(lastValue) == 3 - } - } - - describe("SignalProducer.concat(value:)") { - it("should emit final value") { - let (signal, observer) = SignalProducer.pipe() - - let mergedSignals = signal.concat(value: 4) - - var lastValue: Int? - mergedSignals.startWithNext { lastValue = $0 } - - observer.sendNext(1) - expect(lastValue) == 1 - - observer.sendNext(2) - expect(lastValue) == 2 - - observer.sendNext(3) - expect(lastValue) == 3 - - observer.sendCompleted() - expect(lastValue) == 4 - } - } - } -} diff --git a/ReactiveCocoaTests/Swift/FoundationExtensionsSpec.swift b/ReactiveCocoaTests/Swift/FoundationExtensionsSpec.swift deleted file mode 100644 index 7a3ddf943c..0000000000 --- a/ReactiveCocoaTests/Swift/FoundationExtensionsSpec.swift +++ /dev/null @@ -1,59 +0,0 @@ -// -// FoundationExtensionsSpec.swift -// ReactiveCocoa -// -// Created by Neil Pankey on 5/22/15. -// Copyright (c) 2015 GitHub. All rights reserved. -// - -import Result -import Nimble -import Quick -import ReactiveCocoa - -extension Notification.Name { - static let racFirst = Notification.Name(rawValue: "rac_notifications_test") - static let racAnother = Notification.Name(rawValue: "rac_notifications_another") -} - -class FoundationExtensionsSpec: QuickSpec { - override func spec() { - describe("NSNotificationCenter.rac_notifications") { - let center = NotificationCenter.default - - it("should send notifications on the producer") { - let producer = center.rac_notifications(forName: .racFirst) - - var notif: Notification? = nil - let disposable = producer.startWithNext { notif = $0 } - - center.post(name: .racAnother, object: nil) - expect(notif).to(beNil()) - - center.post(name: .racFirst, object: nil) - expect(notif?.name) == .racFirst - - notif = nil - disposable.dispose() - - center.post(name: .racFirst, object: nil) - expect(notif).to(beNil()) - } - - it("should send Interrupted when the observed object is freed") { - var observedObject: AnyObject? = NSObject() - let producer = center.rac_notifications(forName: nil, object: observedObject) - observedObject = nil - - var interrupted = false - let disposable = producer.startWithInterrupted { - interrupted = true - } - expect(interrupted) == true - - disposable.dispose() - } - - } - } -} diff --git a/ReactiveCocoaTests/Swift/KeyValueObservingSpec.swift b/ReactiveCocoaTests/Swift/KeyValueObservingSpec.swift index 7989829354..cd407d92fc 100644 --- a/ReactiveCocoaTests/Swift/KeyValueObservingSpec.swift +++ b/ReactiveCocoaTests/Swift/KeyValueObservingSpec.swift @@ -1,5 +1,6 @@ import Foundation @testable import ReactiveCocoa +import ReactiveSwift import enum Result.NoError import Quick import Nimble diff --git a/ReactiveCocoaTests/Swift/LifetimeSpec.swift b/ReactiveCocoaTests/Swift/LifetimeSpec.swift deleted file mode 100644 index 3569657892..0000000000 --- a/ReactiveCocoaTests/Swift/LifetimeSpec.swift +++ /dev/null @@ -1,83 +0,0 @@ -import Quick -import Nimble -import ReactiveCocoa -import Result - -final class LifetimeSpec: QuickSpec { - override func spec() { - describe("SignalProducerProtocol.takeDuring") { - it("completes a signal when the lifetime ends") { - let (signal, observer) = Signal.pipe() - let object = MutableReference(TestObject()) - - let output = signal.take(during: object.value!.lifetime) - - var results: [Int] = [] - output.observeNext { results.append($0) } - - observer.sendNext(1) - observer.sendNext(2) - object.value = nil - observer.sendNext(3) - - expect(results) == [1, 2] - } - - it("completes a signal producer when the lifetime ends") { - let (producer, observer) = Signal.pipe() - let object = MutableReference(TestObject()) - - let output = producer.take(during: object.value!.lifetime) - - var results: [Int] = [] - output.observeNext { results.append($0) } - - observer.sendNext(1) - observer.sendNext(2) - object.value = nil - observer.sendNext(3) - - expect(results) == [1, 2] - } - } - - describe("NSObject") { - it("should complete its lifetime ended signal when the it deinitializes") { - let object = MutableReference(TestObject()) - - var isCompleted = false - - object.value!.lifetime.ended.observeCompleted { isCompleted = true } - expect(isCompleted) == false - - object.value = nil - expect(isCompleted) == true - } - - it("should complete its lifetime ended signal even if the lifetime object is being retained") { - let object = MutableReference(TestObject()) - let lifetime = object.value!.lifetime - - var isCompleted = false - - lifetime.ended.observeCompleted { isCompleted = true } - expect(isCompleted) == false - - object.value = nil - expect(isCompleted) == true - } - } - } -} - -private final class MutableReference { - var value: Value? - init(_ value: Value?) { - self.value = value - } -} - -private final class TestObject { - private let token = Lifetime.Token() - var lifetime: Lifetime { return Lifetime(token) } -} diff --git a/ReactiveCocoaTests/Swift/ObjectiveCBridgingSpec.swift b/ReactiveCocoaTests/Swift/ObjectiveCBridgingSpec.swift index dd3369a0e2..ea2480c597 100644 --- a/ReactiveCocoaTests/Swift/ObjectiveCBridgingSpec.swift +++ b/ReactiveCocoaTests/Swift/ObjectiveCBridgingSpec.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 GitHub. All rights reserved. // +import ReactiveSwift import Result import Nimble import Quick diff --git a/ReactiveCocoaTests/Swift/PropertySpec.swift b/ReactiveCocoaTests/Swift/PropertySpec.swift deleted file mode 100644 index afb23c2037..0000000000 --- a/ReactiveCocoaTests/Swift/PropertySpec.swift +++ /dev/null @@ -1,1754 +0,0 @@ -// -// PropertySpec.swift -// ReactiveCocoa -// -// Created by Justin Spahr-Summers on 2015-01-23. -// Copyright (c) 2015 GitHub. All rights reserved. -// - -import Result -import Nimble -import Quick -import ReactiveCocoa - -private let initialPropertyValue = "InitialValue" -private let subsequentPropertyValue = "SubsequentValue" -private let finalPropertyValue = "FinalValue" - -private let initialOtherPropertyValue = "InitialOtherValue" -private let subsequentOtherPropertyValue = "SubsequentOtherValue" -private let finalOtherPropertyValue = "FinalOtherValue" - -class PropertySpec: QuickSpec { - override func spec() { - describe("MutableProperty") { - it("should have the value given at initialization") { - let mutableProperty = MutableProperty(initialPropertyValue) - - expect(mutableProperty.value) == initialPropertyValue - } - - it("should yield a producer that sends the current value then all changes") { - let mutableProperty = MutableProperty(initialPropertyValue) - var sentValue: String? - - mutableProperty.producer.startWithNext { sentValue = $0 } - - expect(sentValue) == initialPropertyValue - - mutableProperty.value = subsequentPropertyValue - expect(sentValue) == subsequentPropertyValue - - mutableProperty.value = finalPropertyValue - expect(sentValue) == finalPropertyValue - } - - it("should yield a producer that sends the current value then all changes, even if the value actually remains unchanged") { - let mutableProperty = MutableProperty(initialPropertyValue) - var count = 0 - - mutableProperty.producer.startWithNext { _ in count = count + 1 } - - expect(count) == 1 - - mutableProperty.value = initialPropertyValue - expect(count) == 2 - - mutableProperty.value = initialPropertyValue - expect(count) == 3 - } - - it("should yield a signal that emits subsequent changes to the value") { - let mutableProperty = MutableProperty(initialPropertyValue) - var sentValue: String? - - mutableProperty.signal.observeNext { sentValue = $0 } - - expect(sentValue).to(beNil()) - - mutableProperty.value = subsequentPropertyValue - expect(sentValue) == subsequentPropertyValue - - mutableProperty.value = finalPropertyValue - expect(sentValue) == finalPropertyValue - } - - it("should yield a signal that emits subsequent changes to the value, even if the value actually remains unchanged") { - let mutableProperty = MutableProperty(initialPropertyValue) - var count = 0 - - mutableProperty.signal.observeNext { _ in count = count + 1 } - - expect(count) == 0 - - mutableProperty.value = initialPropertyValue - expect(count) == 1 - - mutableProperty.value = initialPropertyValue - expect(count) == 2 - } - - it("should complete its producer when deallocated") { - var mutableProperty: MutableProperty? = MutableProperty(initialPropertyValue) - var producerCompleted = false - - mutableProperty!.producer.startWithCompleted { producerCompleted = true } - - mutableProperty = nil - expect(producerCompleted) == true - } - - it("should complete its signal when deallocated") { - var mutableProperty: MutableProperty? = MutableProperty(initialPropertyValue) - var signalCompleted = false - - mutableProperty!.signal.observeCompleted { signalCompleted = true } - - mutableProperty = nil - expect(signalCompleted) == true - } - - it("should yield a producer which emits the latest value and complete even if the property is deallocated") { - var mutableProperty: MutableProperty? = MutableProperty(initialPropertyValue) - let producer = mutableProperty!.producer - - var producerCompleted = false - var hasUnanticipatedEvent = false - var latestValue = mutableProperty?.value - - mutableProperty!.value = subsequentPropertyValue - mutableProperty = nil - - producer.start { event in - switch event { - case let .next(value): - latestValue = value - case .completed: - producerCompleted = true - case .interrupted, .failed: - hasUnanticipatedEvent = true - } - } - - expect(hasUnanticipatedEvent) == false - expect(producerCompleted) == true - expect(latestValue) == subsequentPropertyValue - } - - it("should modify the value atomically") { - let property = MutableProperty(initialPropertyValue) - - property.modify { $0 = subsequentPropertyValue } - expect(property.value) == subsequentPropertyValue - } - - it("should modify the value atomically and subsquently send out a Next event with the new value") { - let property = MutableProperty(initialPropertyValue) - var value: String? - - property.producer.startWithNext { - value = $0 - } - - expect(value) == initialPropertyValue - - property.modify { $0 = subsequentPropertyValue } - expect(property.value) == subsequentPropertyValue - expect(value) == subsequentPropertyValue - } - - it("should swap the value atomically") { - let property = MutableProperty(initialPropertyValue) - - expect(property.swap(subsequentPropertyValue)) == initialPropertyValue - expect(property.value) == subsequentPropertyValue - } - - it("should swap the value atomically and subsquently send out a Next event with the new value") { - let property = MutableProperty(initialPropertyValue) - var value: String? - - property.producer.startWithNext { - value = $0 - } - - expect(value) == initialPropertyValue - expect(property.swap(subsequentPropertyValue)) == initialPropertyValue - - expect(property.value) == subsequentPropertyValue - expect(value) == subsequentPropertyValue - } - - it("should perform an action with the value") { - let property = MutableProperty(initialPropertyValue) - - let result: Bool = property.withValue { $0.isEmpty } - - expect(result) == false - expect(property.value) == initialPropertyValue - } - - it("should not deadlock on recursive value access") { - let (producer, observer) = SignalProducer.pipe() - let property = MutableProperty(0) - var value: Int? - - property <~ producer - property.producer.startWithNext { _ in - value = property.value - } - - observer.sendNext(10) - expect(value) == 10 - } - - it("should not deadlock on recursive value access with a closure") { - let (producer, observer) = SignalProducer.pipe() - let property = MutableProperty(0) - var value: Int? - - property <~ producer - property.producer.startWithNext { _ in - value = property.withValue { $0 + 1 } - } - - observer.sendNext(10) - expect(value) == 11 - } - - it("should not deadlock on recursive observation") { - let property = MutableProperty(0) - - var value: Int? - property.producer.startWithNext { _ in - property.producer.startWithNext { x in value = x } - } - - expect(value) == 0 - - property.value = 1 - expect(value) == 1 - } - - it("should not deadlock on recursive ABA observation") { - let propertyA = MutableProperty(0) - let propertyB = MutableProperty(0) - - var value: Int? - propertyA.producer.startWithNext { _ in - propertyB.producer.startWithNext { _ in - propertyA.producer.startWithNext { x in value = x } - } - } - - expect(value) == 0 - - propertyA.value = 1 - expect(value) == 1 - } - } - - describe("Property") { - describe("from a value") { - it("should have the value given at initialization") { - let constantProperty = Property(value: initialPropertyValue) - - expect(constantProperty.value) == initialPropertyValue - } - - it("should yield a signal that interrupts observers without emitting any value.") { - let constantProperty = Property(value: initialPropertyValue) - - var signalInterrupted = false - var hasUnexpectedEventsEmitted = false - - constantProperty.signal.observe { event in - switch event { - case .interrupted: - signalInterrupted = true - case .next, .failed, .completed: - hasUnexpectedEventsEmitted = true - } - } - - expect(signalInterrupted) == true - expect(hasUnexpectedEventsEmitted) == false - } - - it("should yield a producer that sends the current value then completes") { - let constantProperty = Property(value: initialPropertyValue) - - var sentValue: String? - var signalCompleted = false - - constantProperty.producer.start { event in - switch event { - case let .next(value): - sentValue = value - case .completed: - signalCompleted = true - case .failed, .interrupted: - break - } - } - - expect(sentValue) == initialPropertyValue - expect(signalCompleted) == true - } - } - - describe("from a PropertyProtocol") { - it("should pass through behaviors of the input property") { - let constantProperty = Property(value: initialPropertyValue) - let property = Property(constantProperty) - - var sentValue: String? - var signalSentValue: String? - var producerCompleted = false - var signalInterrupted = false - - property.producer.start { event in - switch event { - case let .next(value): - sentValue = value - case .completed: - producerCompleted = true - case .failed, .interrupted: - break - } - } - - property.signal.observe { event in - switch event { - case let .next(value): - signalSentValue = value - case .interrupted: - signalInterrupted = true - case .failed, .completed: - break - } - } - - expect(sentValue) == initialPropertyValue - expect(signalSentValue).to(beNil()) - expect(producerCompleted) == true - expect(signalInterrupted) == true - } - - describe("composed properties") { - it("should have the latest value available before sending any value") { - var latestValue: Int! - - let property = MutableProperty(1) - let mappedProperty = property.map { $0 + 1 } - mappedProperty.producer.startWithNext { _ in latestValue = mappedProperty.value } - - expect(latestValue) == 2 - - property.value = 2 - expect(latestValue) == 3 - - property.value = 3 - expect(latestValue) == 4 - } - - it("should retain its source property") { - var property = Optional(MutableProperty(1)) - weak var weakProperty = property - - var firstMappedProperty = Optional(property!.map { $0 + 1 }) - var secondMappedProperty = Optional(firstMappedProperty!.map { $0 + 2 }) - - // Suppress the "written to but never read" warning on `secondMappedProperty`. - _ = secondMappedProperty - - property = nil - expect(weakProperty).toNot(beNil()) - - firstMappedProperty = nil - expect(weakProperty).toNot(beNil()) - - secondMappedProperty = nil - expect(weakProperty).to(beNil()) - } - - it("should transform property from a property that has a terminated producer") { - let property = Property(value: 1) - let transformedProperty = property.map { $0 + 1 } - - expect(transformedProperty.value) == 2 - } - - describe("signal lifetime and producer lifetime") { - it("should return a producer and a signal which respect the lifetime of the source property instead of the read-only view itself") { - var signalCompleted = 0 - var producerCompleted = 0 - - var property = Optional(MutableProperty(1)) - var firstMappedProperty = Optional(property!.map { $0 + 1 }) - var secondMappedProperty = Optional(firstMappedProperty!.map { $0 + 2 }) - var thirdMappedProperty = Optional(secondMappedProperty!.map { $0 + 2 }) - - firstMappedProperty!.signal.observeCompleted { signalCompleted += 1 } - secondMappedProperty!.signal.observeCompleted { signalCompleted += 1 } - thirdMappedProperty!.signal.observeCompleted { signalCompleted += 1 } - - firstMappedProperty!.producer.startWithCompleted { producerCompleted += 1 } - secondMappedProperty!.producer.startWithCompleted { producerCompleted += 1 } - thirdMappedProperty!.producer.startWithCompleted { producerCompleted += 1 } - - firstMappedProperty = nil - expect(signalCompleted) == 0 - expect(producerCompleted) == 0 - - secondMappedProperty = nil - expect(signalCompleted) == 0 - expect(producerCompleted) == 0 - - property = nil - expect(signalCompleted) == 0 - expect(producerCompleted) == 0 - - thirdMappedProperty = nil - expect(signalCompleted) == 3 - expect(producerCompleted) == 3 - } - } - } - - describe("Property.capture") { - it("should not capture intermediate properties but only the ultimate sources") { - func increment(input: Int) -> Int { - return input + 1 - } - - weak var weakSourceProperty: MutableProperty? - weak var weakPropertyA: Property? - weak var weakPropertyB: Property? - weak var weakPropertyC: Property? - - var finalProperty: Property! - - func scope() { - let property = MutableProperty(1) - weakSourceProperty = property - - let propertyA = property.map(increment) - weakPropertyA = propertyA - - let propertyB = propertyA.map(increment) - weakPropertyB = propertyB - - let propertyC = propertyB.map(increment) - weakPropertyC = propertyC - - finalProperty = propertyC.map(increment) - } - - scope() - - expect(finalProperty.value) == 5 - expect(weakSourceProperty).toNot(beNil()) - expect(weakPropertyA).to(beNil()) - expect(weakPropertyB).to(beNil()) - expect(weakPropertyC).to(beNil()) - } - } - } - - describe("from a value and SignalProducer") { - it("should initially take on the supplied value") { - let property = Property(initial: initialPropertyValue, - then: SignalProducer.never) - - expect(property.value) == initialPropertyValue - } - - it("should take on each value sent on the producer") { - let property = Property(initial: initialPropertyValue, - then: SignalProducer(value: subsequentPropertyValue)) - - expect(property.value) == subsequentPropertyValue - } - - it("should return a producer and a signal that respect the lifetime of its ultimate source") { - var signalCompleted = false - var producerCompleted = false - var signalInterrupted = false - - let (signal, observer) = Signal.pipe() - var property: Property? = Property(initial: 1, - then: SignalProducer(signal: signal)) - let propertySignal = property!.signal - - propertySignal.observeCompleted { signalCompleted = true } - property!.producer.startWithCompleted { producerCompleted = true } - - expect(property!.value) == 1 - - observer.sendNext(2) - expect(property!.value) == 2 - expect(producerCompleted) == false - expect(signalCompleted) == false - - property = nil - expect(producerCompleted) == false - expect(signalCompleted) == false - - observer.sendCompleted() - expect(producerCompleted) == true - expect(signalCompleted) == true - - propertySignal.observeInterrupted { signalInterrupted = true } - expect(signalInterrupted) == true - } - } - - describe("from a value and Signal") { - it("should initially take on the supplied value, then values sent on the signal") { - let (signal, observer) = Signal.pipe() - - let property = Property(initial: initialPropertyValue, - then: signal) - - expect(property.value) == initialPropertyValue - - observer.sendNext(subsequentPropertyValue) - - expect(property.value) == subsequentPropertyValue - } - - - it("should return a producer and a signal that respect the lifetime of its ultimate source") { - var signalCompleted = false - var producerCompleted = false - var signalInterrupted = false - - let (signal, observer) = Signal.pipe() - var property: Property? = Property(initial: 1, - then: signal) - let propertySignal = property!.signal - - propertySignal.observeCompleted { signalCompleted = true } - property!.producer.startWithCompleted { producerCompleted = true } - - expect(property!.value) == 1 - - observer.sendNext(2) - expect(property!.value) == 2 - expect(producerCompleted) == false - expect(signalCompleted) == false - - property = nil - expect(producerCompleted) == false - expect(signalCompleted) == false - - observer.sendCompleted() - expect(producerCompleted) == true - expect(signalCompleted) == true - - propertySignal.observeInterrupted { signalInterrupted = true } - expect(signalInterrupted) == true - } - } - } - - describe("PropertyProtocol") { - describe("map") { - it("should transform the current value and all subsequent values") { - let property = MutableProperty(1) - let mappedProperty = property - .map { $0 + 1 } - expect(mappedProperty.value) == 2 - - property.value = 2 - expect(mappedProperty.value) == 3 - } - } - - describe("combineLatest") { - var property: MutableProperty! - var otherProperty: MutableProperty! - - beforeEach { - property = MutableProperty(initialPropertyValue) - otherProperty = MutableProperty(initialOtherPropertyValue) - } - - it("should forward the latest values from both inputs") { - let combinedProperty = property.combineLatest(with: otherProperty) - var latest: (String, String)? - combinedProperty.signal.observeNext { latest = $0 } - - property.value = subsequentPropertyValue - expect(latest?.0) == subsequentPropertyValue - expect(latest?.1) == initialOtherPropertyValue - - // is there a better way to test tuples? - otherProperty.value = subsequentOtherPropertyValue - expect(latest?.0) == subsequentPropertyValue - expect(latest?.1) == subsequentOtherPropertyValue - - property.value = finalPropertyValue - expect(latest?.0) == finalPropertyValue - expect(latest?.1) == subsequentOtherPropertyValue - } - - it("should complete when the source properties are deinitialized") { - var completed = false - - var combinedProperty = Optional(property.combineLatest(with: otherProperty)) - combinedProperty!.signal.observeCompleted { completed = true } - - combinedProperty = nil - expect(completed) == false - - property = nil - expect(completed) == false - - otherProperty = nil - expect(completed) == true - } - - it("should be consistent between its cached value and its values producer") { - var firstResult: String! - var secondResult: String! - - let combined = property.combineLatest(with: otherProperty) - combined.producer.startWithNext { (left, right) in firstResult = left + right } - - func getValue() -> String { - return combined.value.0 + combined.value.1 - } - - expect(getValue()) == initialPropertyValue + initialOtherPropertyValue - expect(firstResult) == initialPropertyValue + initialOtherPropertyValue - - property.value = subsequentPropertyValue - expect(getValue()) == subsequentPropertyValue + initialOtherPropertyValue - expect(firstResult) == subsequentPropertyValue + initialOtherPropertyValue - - combined.producer.startWithNext { (left, right) in secondResult = left + right } - expect(secondResult) == subsequentPropertyValue + initialOtherPropertyValue - - otherProperty.value = subsequentOtherPropertyValue - expect(getValue()) == subsequentPropertyValue + subsequentOtherPropertyValue - expect(firstResult) == subsequentPropertyValue + subsequentOtherPropertyValue - expect(secondResult) == subsequentPropertyValue + subsequentOtherPropertyValue - } - - it("should be consistent between nested combined properties") { - let A = MutableProperty(1) - let B = MutableProperty(100) - let C = MutableProperty(10000) - - var firstResult: Int! - - let combined = A.combineLatest(with: B) - combined.producer.startWithNext { (left, right) in firstResult = left + right } - - func getValue() -> Int { - return combined.value.0 + combined.value.1 - } - - /// Initial states - expect(getValue()) == 101 - expect(firstResult) == 101 - - A.value = 2 - expect(getValue()) == 102 - expect(firstResult) == 102 - - B.value = 200 - expect(getValue()) == 202 - expect(firstResult) == 202 - - /// Setup - A.value = 3 - expect(getValue()) == 203 - expect(firstResult) == 203 - - /// Zip another property now. - var secondResult: Int! - let anotherCombined = combined.combineLatest(with: C) - anotherCombined.producer.startWithNext { (left, right) in secondResult = (left.0 + left.1) + right } - - func getAnotherValue() -> Int { - return (anotherCombined.value.0.0 + anotherCombined.value.0.1) + anotherCombined.value.1 - } - - expect(getAnotherValue()) == 10203 - - A.value = 4 - expect(getValue()) == 204 - expect(getAnotherValue()) == 10204 - } - } - - describe("zip") { - var property: MutableProperty! - var otherProperty: MutableProperty! - - beforeEach { - property = MutableProperty(initialPropertyValue) - otherProperty = MutableProperty(initialOtherPropertyValue) - } - - it("should combine pairs") { - var result: [String] = [] - - let zippedProperty = property.zip(with: otherProperty) - zippedProperty.producer.startWithNext { (left, right) in result.append("\(left)\(right)") } - - let firstResult = [ "\(initialPropertyValue)\(initialOtherPropertyValue)" ] - let secondResult = firstResult + [ "\(subsequentPropertyValue)\(subsequentOtherPropertyValue)" ] - let thirdResult = secondResult + [ "\(finalPropertyValue)\(finalOtherPropertyValue)" ] - let finalResult = thirdResult + [ "\(initialPropertyValue)\(initialOtherPropertyValue)" ] - - expect(result) == firstResult - - property.value = subsequentPropertyValue - expect(result) == firstResult - - otherProperty.value = subsequentOtherPropertyValue - expect(result) == secondResult - - property.value = finalPropertyValue - otherProperty.value = finalOtherPropertyValue - expect(result) == thirdResult - - property.value = initialPropertyValue - expect(result) == thirdResult - - property.value = subsequentPropertyValue - expect(result) == thirdResult - - otherProperty.value = initialOtherPropertyValue - expect(result) == finalResult - } - - it("should be consistent between its cached value and its values producer") { - var firstResult: String! - var secondResult: String! - - let zippedProperty = property.zip(with: otherProperty) - zippedProperty.producer.startWithNext { (left, right) in firstResult = left + right } - - func getValue() -> String { - return zippedProperty.value.0 + zippedProperty.value.1 - } - - expect(getValue()) == initialPropertyValue + initialOtherPropertyValue - expect(firstResult) == initialPropertyValue + initialOtherPropertyValue - - property.value = subsequentPropertyValue - expect(getValue()) == initialPropertyValue + initialOtherPropertyValue - expect(firstResult) == initialPropertyValue + initialOtherPropertyValue - - // It should still be the tuple with initial property values, - // since `otherProperty` isn't changed yet. - zippedProperty.producer.startWithNext { (left, right) in secondResult = left + right } - expect(secondResult) == initialPropertyValue + initialOtherPropertyValue - - otherProperty.value = subsequentOtherPropertyValue - expect(getValue()) == subsequentPropertyValue + subsequentOtherPropertyValue - expect(firstResult) == subsequentPropertyValue + subsequentOtherPropertyValue - expect(secondResult) == subsequentPropertyValue + subsequentOtherPropertyValue - } - - it("should be consistent between nested zipped properties") { - let A = MutableProperty(1) - let B = MutableProperty(100) - let C = MutableProperty(10000) - - var firstResult: Int! - - let zipped = A.zip(with: B) - zipped.producer.startWithNext { (left, right) in firstResult = left + right } - - func getValue() -> Int { - return zipped.value.0 + zipped.value.1 - } - - /// Initial states - expect(getValue()) == 101 - expect(firstResult) == 101 - - A.value = 2 - expect(getValue()) == 101 - expect(firstResult) == 101 - - B.value = 200 - expect(getValue()) == 202 - expect(firstResult) == 202 - - /// Setup - A.value = 3 - expect(getValue()) == 202 - expect(firstResult) == 202 - - /// Zip another property now. - var secondResult: Int! - let anotherZipped = zipped.zip(with: C) - anotherZipped.producer.startWithNext { (left, right) in secondResult = (left.0 + left.1) + right } - - func getAnotherValue() -> Int { - return (anotherZipped.value.0.0 + anotherZipped.value.0.1) + anotherZipped.value.1 - } - - /// Since `zipped` is 202 now, and `C` is 10000, - /// shouldn't this be 10202? - - /// Verify `zipped` again. - expect(getValue()) == 202 - expect(firstResult) == 202 - - /// Then... well, no. Surprise! (Only before #3042) - /// We get 10203 here. - /// - /// https://github.com/ReactiveCocoa/ReactiveCocoa/pull/3042 - expect(getAnotherValue()) == 10202 - } - - it("should be consistent between combined and nested zipped properties") { - let A = MutableProperty(1) - let B = MutableProperty(100) - let C = MutableProperty(10000) - let D = MutableProperty(1000000) - - var firstResult: Int! - - let zipped = A.zip(with: B) - zipped.producer.startWithNext { (left, right) in firstResult = left + right } - - func getValue() -> Int { - return zipped.value.0 + zipped.value.1 - } - - /// Initial states - expect(getValue()) == 101 - expect(firstResult) == 101 - - A.value = 2 - expect(getValue()) == 101 - expect(firstResult) == 101 - - B.value = 200 - expect(getValue()) == 202 - expect(firstResult) == 202 - - /// Setup - A.value = 3 - expect(getValue()) == 202 - expect(firstResult) == 202 - - /// Zip another property now. - var secondResult: Int! - let anotherZipped = zipped.zip(with: C) - anotherZipped.producer.startWithNext { (left, right) in secondResult = (left.0 + left.1) + right } - - func getAnotherValue() -> Int { - return (anotherZipped.value.0.0 + anotherZipped.value.0.1) + anotherZipped.value.1 - } - - /// Verify `zipped` again. - expect(getValue()) == 202 - expect(firstResult) == 202 - - expect(getAnotherValue()) == 10202 - - /// Zip `D` with `anotherZipped`. - let yetAnotherZipped = anotherZipped.zip(with: D) - - /// Combine with another property. - /// (((Int, Int), Int), (((Int, Int), Int), Int)) - let combined = anotherZipped.combineLatest(with: yetAnotherZipped) - - var thirdResult: Int! - combined.producer.startWithNext { (left, right) in - let leftResult = left.0.0 + left.0.1 + left.1 - let rightResult = right.0.0.0 + right.0.0.1 + right.0.1 + right.1 - thirdResult = leftResult + rightResult - } - - expect(thirdResult) == 1020404 - } - - it("should complete its producer only when the source properties are deinitialized") { - var result: [String] = [] - var completed = false - - var zippedProperty = Optional(property.zip(with: otherProperty)) - zippedProperty!.producer.start { event in - switch event { - case let .next(left, right): - result.append("\(left)\(right)") - case .completed: - completed = true - default: - break - } - } - - expect(completed) == false - expect(result) == [ "\(initialPropertyValue)\(initialOtherPropertyValue)" ] - - property.value = subsequentPropertyValue - expect(result) == [ "\(initialPropertyValue)\(initialOtherPropertyValue)" ] - - zippedProperty = nil - expect(completed) == false - - property = nil - otherProperty = nil - expect(completed) == true - } - } - - describe("unary operators") { - var property: MutableProperty! - - beforeEach { - property = MutableProperty(initialPropertyValue) - } - - describe("combinePrevious") { - it("should pack the current value and the previous value a tuple") { - let transformedProperty = property.combinePrevious(initialPropertyValue) - - expect(transformedProperty.value.0) == initialPropertyValue - expect(transformedProperty.value.1) == initialPropertyValue - - property.value = subsequentPropertyValue - - expect(transformedProperty.value.0) == initialPropertyValue - expect(transformedProperty.value.1) == subsequentPropertyValue - - property.value = finalPropertyValue - - expect(transformedProperty.value.0) == subsequentPropertyValue - expect(transformedProperty.value.1) == finalPropertyValue - } - - it("should complete its producer only when the source property is deinitialized") { - var result: (String, String)? - var completed = false - - var transformedProperty = Optional(property.combinePrevious(initialPropertyValue)) - transformedProperty!.producer.start { event in - switch event { - case let .next(tuple): - result = tuple - case .completed: - completed = true - default: - break - } - } - - expect(result?.0) == initialPropertyValue - expect(result?.1) == initialPropertyValue - - property.value = subsequentPropertyValue - - expect(result?.0) == initialPropertyValue - expect(result?.1) == subsequentPropertyValue - - transformedProperty = nil - expect(completed) == false - - property = nil - expect(completed) == true - } - } - - describe("skipRepeats") { - it("should not emit events for subsequent equatable values that are the same as the current value") { - let transformedProperty = property.skipRepeats() - - var counter = 0 - transformedProperty.signal.observeNext { _ in - counter += 1 - } - - property.value = initialPropertyValue - property.value = initialPropertyValue - property.value = initialPropertyValue - - expect(counter) == 0 - - property.value = subsequentPropertyValue - property.value = subsequentPropertyValue - property.value = subsequentPropertyValue - - expect(counter) == 1 - - property.value = finalPropertyValue - property.value = initialPropertyValue - property.value = subsequentPropertyValue - - expect(counter) == 4 - } - - it("should not emit events for subsequent values that are regarded as the same as the current value by the supplied closure") { - var counter = 0 - let transformedProperty = property.skipRepeats { _, newValue in newValue == initialPropertyValue } - - transformedProperty.signal.observeNext { _ in - counter += 1 - } - - property.value = initialPropertyValue - expect(counter) == 0 - - property.value = subsequentPropertyValue - expect(counter) == 1 - - property.value = finalPropertyValue - expect(counter) == 2 - - property.value = initialPropertyValue - expect(counter) == 2 - } - - it("should complete its producer only when the source property is deinitialized") { - var counter = 0 - var completed = false - - var transformedProperty = Optional(property.skipRepeats()) - transformedProperty!.producer.start { event in - switch event { - case .next: - counter += 1 - case .completed: - completed = true - default: - break - } - } - - expect(counter) == 1 - - property.value = initialPropertyValue - expect(counter) == 1 - - transformedProperty = nil - expect(completed) == false - - property = nil - expect(completed) == true - } - } - - describe("uniqueValues") { - it("should emit hashable values that have not been emited before") { - let transformedProperty = property.uniqueValues() - - var counter = 0 - transformedProperty.signal.observeNext { _ in - counter += 1 - } - - property.value = initialPropertyValue - expect(counter) == 0 - - property.value = subsequentPropertyValue - property.value = subsequentPropertyValue - - expect(counter) == 1 - - property.value = finalPropertyValue - property.value = initialPropertyValue - property.value = subsequentPropertyValue - - expect(counter) == 2 - } - - it("should emit only the values of which the computed identity have not been captured before") { - let transformedProperty = property.uniqueValues { _ in 0 } - - var counter = 0 - transformedProperty.signal.observeNext { _ in - counter += 1 - } - - property.value = initialPropertyValue - property.value = subsequentPropertyValue - property.value = finalPropertyValue - expect(counter) == 0 - } - - it("should complete its producer only when the source property is deinitialized") { - var counter = 0 - var completed = false - - var transformedProperty = Optional(property.uniqueValues()) - transformedProperty!.producer.start { event in - switch event { - case .next: - counter += 1 - case .completed: - completed = true - default: - break - } - } - - expect(counter) == 1 - - property.value = initialPropertyValue - expect(counter) == 1 - - transformedProperty = nil - expect(completed) == false - - property = nil - expect(completed) == true - } - } - } - - describe("flattening") { - describe("flatten") { - describe("FlattenStrategy.concat") { - it("should concatenate the values as the inner property is replaced and deinitialized") { - var firstProperty = Optional(MutableProperty(0)) - var secondProperty = Optional(MutableProperty(10)) - var thirdProperty = Optional(MutableProperty(20)) - - var outerProperty = Optional(MutableProperty(firstProperty!)) - - var receivedValues: [Int] = [] - var errored = false - var completed = false - - var flattenedProperty = Optional(outerProperty!.flatten(.concat)) - - flattenedProperty!.producer.start { event in - switch event { - case let .next(value): - receivedValues.append(value) - case .completed: - completed = true - case .failed: - errored = true - case .interrupted: - break - } - } - - expect(receivedValues) == [ 0 ] - - outerProperty!.value = secondProperty! - secondProperty!.value = 11 - outerProperty!.value = thirdProperty! - thirdProperty!.value = 21 - - expect(receivedValues) == [ 0 ] - expect(completed) == false - - secondProperty!.value = 12 - thirdProperty!.value = 22 - - expect(receivedValues) == [ 0 ] - expect(completed) == false - - firstProperty = nil - - expect(receivedValues) == [ 0, 12 ] - expect(completed) == false - - secondProperty = nil - - expect(receivedValues) == [ 0, 12, 22 ] - expect(completed) == false - - outerProperty = nil - expect(completed) == false - - thirdProperty = nil - expect(completed) == false - - flattenedProperty = nil - expect(completed) == true - expect(errored) == false - } - } - - describe("FlattenStrategy.merge") { - it("should merge the values of all inner properties") { - var firstProperty = Optional(MutableProperty(0)) - var secondProperty = Optional(MutableProperty(10)) - var thirdProperty = Optional(MutableProperty(20)) - - var outerProperty = Optional(MutableProperty(firstProperty!)) - - var receivedValues: [Int] = [] - var errored = false - var completed = false - - var flattenedProperty = Optional(outerProperty!.flatten(.merge)) - - flattenedProperty!.producer.start { event in - switch event { - case let .next(value): - receivedValues.append(value) - case .completed: - completed = true - case .failed: - errored = true - case .interrupted: - break - } - } - - expect(receivedValues) == [ 0 ] - - outerProperty!.value = secondProperty! - secondProperty!.value = 11 - outerProperty!.value = thirdProperty! - thirdProperty!.value = 21 - - expect(receivedValues) == [ 0, 10, 11, 20, 21 ] - expect(completed) == false - - secondProperty!.value = 12 - thirdProperty!.value = 22 - - expect(receivedValues) == [ 0, 10, 11, 20, 21, 12, 22 ] - expect(completed) == false - - firstProperty = nil - - expect(receivedValues) == [ 0, 10, 11, 20, 21, 12, 22 ] - expect(completed) == false - - secondProperty = nil - - expect(receivedValues) == [ 0, 10, 11, 20, 21, 12, 22 ] - expect(completed) == false - - outerProperty = nil - expect(completed) == false - - thirdProperty = nil - expect(completed) == false - - flattenedProperty = nil - expect(completed) == true - expect(errored) == false - } - } - - describe("FlattenStrategy.latest") { - it("should forward values from the latest inner property") { - let firstProperty = Optional(MutableProperty(0)) - var secondProperty = Optional(MutableProperty(10)) - var thirdProperty = Optional(MutableProperty(20)) - - var outerProperty = Optional(MutableProperty(firstProperty!)) - - var receivedValues: [Int] = [] - var errored = false - var completed = false - - outerProperty!.flatten(.latest).producer.start { event in - switch event { - case let .next(value): - receivedValues.append(value) - case .completed: - completed = true - case .failed: - errored = true - case .interrupted: - break - } - } - - expect(receivedValues) == [ 0 ] - - outerProperty!.value = secondProperty! - secondProperty!.value = 11 - outerProperty!.value = thirdProperty! - thirdProperty!.value = 21 - - expect(receivedValues) == [ 0, 10, 11, 20, 21 ] - expect(errored) == false - expect(completed) == false - - secondProperty!.value = 12 - secondProperty = nil - thirdProperty!.value = 22 - thirdProperty = nil - - expect(receivedValues) == [ 0, 10, 11, 20, 21, 22 ] - expect(errored) == false - expect(completed) == false - - outerProperty = nil - expect(errored) == false - expect(completed) == true - } - - it("should release the old properties when switched or deallocated") { - var firstProperty = Optional(MutableProperty(0)) - var secondProperty = Optional(MutableProperty(10)) - var thirdProperty = Optional(MutableProperty(20)) - - weak var weakFirstProperty = firstProperty - weak var weakSecondProperty = secondProperty - weak var weakThirdProperty = thirdProperty - - var outerProperty = Optional(MutableProperty(firstProperty!)) - var flattened = Optional(outerProperty!.flatten(.latest)) - - var errored = false - var completed = false - - flattened!.producer.start { event in - switch event { - case .completed: - completed = true - case .failed: - errored = true - case .interrupted, .next: - break - } - } - - firstProperty = nil - outerProperty!.value = secondProperty! - expect(weakFirstProperty).to(beNil()) - - secondProperty = nil - outerProperty!.value = thirdProperty! - expect(weakSecondProperty).to(beNil()) - - thirdProperty = nil - outerProperty = nil - flattened = nil - expect(weakThirdProperty).to(beNil()) - expect(errored) == false - expect(completed) == true - } - } - } - - describe("flatMap") { - describe("PropertyFlattenStrategy.latest") { - it("should forward values from the latest inner transformed property") { - let firstProperty = Optional(MutableProperty(0)) - var secondProperty = Optional(MutableProperty(10)) - var thirdProperty = Optional(MutableProperty(20)) - - var outerProperty = Optional(MutableProperty(firstProperty!)) - - var receivedValues: [String] = [] - var errored = false - var completed = false - - outerProperty!.flatMap(.latest) { $0.map { "\($0)" } }.producer.start { event in - switch event { - case let .next(value): - receivedValues.append(value) - case .completed: - completed = true - case .failed: - errored = true - case .interrupted: - break - } - } - - expect(receivedValues) == [ "0" ] - - outerProperty!.value = secondProperty! - secondProperty!.value = 11 - outerProperty!.value = thirdProperty! - thirdProperty!.value = 21 - - expect(receivedValues) == [ "0", "10", "11", "20", "21" ] - expect(errored) == false - expect(completed) == false - - secondProperty!.value = 12 - secondProperty = nil - thirdProperty!.value = 22 - thirdProperty = nil - - expect(receivedValues) == [ "0", "10", "11", "20", "21", "22" ] - expect(errored) == false - expect(completed) == false - - outerProperty = nil - expect(errored) == false - expect(completed) == true - } - } - } - } - } - - describe("DynamicProperty") { - var object: ObservableObject! - var property: DynamicProperty! - - let propertyValue: () -> Int? = { - if let value: AnyObject = property?.value { - return value as? Int - } else { - return nil - } - } - - beforeEach { - object = ObservableObject() - expect(object.rac_value) == 0 - - property = DynamicProperty(object: object, keyPath: "rac_value") - } - - afterEach { - object = nil - } - - it("should read the underlying object") { - expect(propertyValue()) == 0 - - object.rac_value = 1 - expect(propertyValue()) == 1 - } - - it("should write the underlying object") { - property.value = 1 - expect(object.rac_value) == 1 - expect(propertyValue()) == 1 - } - - it("should yield a producer that sends the current value and then the changes for the key path of the underlying object") { - var values: [Int] = [] - property.producer.startWithNext { value in - expect(value).notTo(beNil()) - values.append(value!) - } - - expect(values) == [ 0 ] - - property.value = 1 - expect(values) == [ 0, 1 ] - - object.rac_value = 2 - expect(values) == [ 0, 1, 2 ] - } - - it("should yield a producer that sends the current value and then the changes for the key path of the underlying object, even if the value actually remains unchanged") { - var values: [Int] = [] - property.producer.startWithNext { value in - expect(value).notTo(beNil()) - values.append(value!) - } - - expect(values) == [ 0 ] - - property.value = 0 - expect(values) == [ 0, 0 ] - - object.rac_value = 0 - expect(values) == [ 0, 0, 0 ] - } - - it("should yield a signal that emits subsequent values for the key path of the underlying object") { - var values: [Int] = [] - property.signal.observeNext { value in - expect(value).notTo(beNil()) - values.append(value!) - } - - expect(values) == [] - - property.value = 1 - expect(values) == [ 1 ] - - object.rac_value = 2 - expect(values) == [ 1, 2 ] - } - - it("should yield a signal that emits subsequent values for the key path of the underlying object, even if the value actually remains unchanged") { - var values: [Int] = [] - property.signal.observeNext { value in - expect(value).notTo(beNil()) - values.append(value!) - } - - expect(values) == [] - - property.value = 0 - expect(values) == [ 0 ] - - object.rac_value = 0 - expect(values) == [ 0, 0 ] - } - - it("should have a completed producer when the underlying object deallocates") { - var completed = false - - property = { - // Use a closure so this object has a shorter lifetime. - let object = ObservableObject() - let property = DynamicProperty(object: object, keyPath: "rac_value") - - property.producer.startWithCompleted { - completed = true - } - - expect(completed) == false - expect(property.value).notTo(beNil()) - return property - }() - - expect(completed).toEventually(beTruthy()) - expect(property.value).to(beNil()) - } - - it("should have a completed signal when the underlying object deallocates") { - var completed = false - - property = { - // Use a closure so this object has a shorter lifetime. - let object = ObservableObject() - let property = DynamicProperty(object: object, keyPath: "rac_value") - - property.signal.observeCompleted { - completed = true - } - - expect(completed) == false - expect(property.value).notTo(beNil()) - return property - }() - - expect(completed).toEventually(beTruthy()) - expect(property.value).to(beNil()) - } - - it("should retain property while DynamicProperty's underlying object is retained"){ - weak var dynamicProperty: DynamicProperty? = property - - property = nil - expect(dynamicProperty).toNot(beNil()) - - object = nil - expect(dynamicProperty).to(beNil()) - } - - it("should support un-bridged reference types") { - let dynamicProperty = DynamicProperty(object: object, keyPath: "rac_reference") - dynamicProperty.value = UnbridgedObject("foo") - expect(object.rac_reference.value) == "foo" - } - } - - describe("binding") { - describe("from a Signal") { - it("should update the property with values sent from the signal") { - let (signal, observer) = Signal.pipe() - - let mutableProperty = MutableProperty(initialPropertyValue) - - mutableProperty <~ signal - - // Verify that the binding hasn't changed the property value: - expect(mutableProperty.value) == initialPropertyValue - - observer.sendNext(subsequentPropertyValue) - expect(mutableProperty.value) == subsequentPropertyValue - } - - it("should tear down the binding when disposed") { - let (signal, observer) = Signal.pipe() - - let mutableProperty = MutableProperty(initialPropertyValue) - - let bindingDisposable = mutableProperty <~ signal - bindingDisposable.dispose() - - observer.sendNext(subsequentPropertyValue) - expect(mutableProperty.value) == initialPropertyValue - } - - it("should tear down the binding when bound signal is completed") { - let (signal, observer) = Signal.pipe() - - let mutableProperty = MutableProperty(initialPropertyValue) - - let bindingDisposable = mutableProperty <~ signal - - expect(bindingDisposable.isDisposed) == false - observer.sendCompleted() - expect(bindingDisposable.isDisposed) == true - } - - it("should tear down the binding when the property deallocates") { - let (signal, _) = Signal.pipe() - - var mutableProperty: MutableProperty? = MutableProperty(initialPropertyValue) - - let bindingDisposable = mutableProperty! <~ signal - - mutableProperty = nil - expect(bindingDisposable.isDisposed) == true - } - } - - describe("from a SignalProducer") { - it("should start a signal and update the property with its values") { - let signalValues = [initialPropertyValue, subsequentPropertyValue] - let signalProducer = SignalProducer(values: signalValues) - - let mutableProperty = MutableProperty(initialPropertyValue) - - mutableProperty <~ signalProducer - - expect(mutableProperty.value) == signalValues.last! - } - - it("should tear down the binding when disposed") { - let (signalProducer, observer) = SignalProducer.pipe() - - let mutableProperty = MutableProperty(initialPropertyValue) - let disposable = mutableProperty <~ signalProducer - - disposable.dispose() - - observer.sendNext(subsequentPropertyValue) - expect(mutableProperty.value) == initialPropertyValue - } - - it("should tear down the binding when bound signal is completed") { - let (signalProducer, observer) = SignalProducer.pipe() - - let mutableProperty = MutableProperty(initialPropertyValue) - let disposable = mutableProperty <~ signalProducer - - observer.sendCompleted() - - expect(disposable.isDisposed) == true - } - - it("should tear down the binding when the property deallocates") { - let signalValues = [initialPropertyValue, subsequentPropertyValue] - let signalProducer = SignalProducer(values: signalValues) - - var mutableProperty: MutableProperty? = MutableProperty(initialPropertyValue) - let disposable = mutableProperty! <~ signalProducer - - mutableProperty = nil - expect(disposable.isDisposed) == true - } - } - - describe("from another property") { - it("should take the source property's current value") { - let sourceProperty = Property(value: initialPropertyValue) - - let destinationProperty = MutableProperty("") - - destinationProperty <~ sourceProperty.producer - - expect(destinationProperty.value) == initialPropertyValue - } - - it("should update with changes to the source property's value") { - let sourceProperty = MutableProperty(initialPropertyValue) - - let destinationProperty = MutableProperty("") - - destinationProperty <~ sourceProperty.producer - - sourceProperty.value = subsequentPropertyValue - expect(destinationProperty.value) == subsequentPropertyValue - } - - it("should tear down the binding when disposed") { - let sourceProperty = MutableProperty(initialPropertyValue) - - let destinationProperty = MutableProperty("") - - let bindingDisposable = destinationProperty <~ sourceProperty.producer - bindingDisposable.dispose() - - sourceProperty.value = subsequentPropertyValue - - expect(destinationProperty.value) == initialPropertyValue - } - - it("should tear down the binding when the source property deallocates") { - var sourceProperty: MutableProperty? = MutableProperty(initialPropertyValue) - - let destinationProperty = MutableProperty("") - destinationProperty <~ sourceProperty!.producer - - sourceProperty = nil - // TODO: Assert binding was torn down? - } - - it("should tear down the binding when the destination property deallocates") { - let sourceProperty = MutableProperty(initialPropertyValue) - var destinationProperty: MutableProperty? = MutableProperty("") - - let bindingDisposable = destinationProperty! <~ sourceProperty.producer - destinationProperty = nil - - expect(bindingDisposable.isDisposed) == true - } - } - - describe("to a dynamic property") { - var object: ObservableObject! - var property: DynamicProperty! - - beforeEach { - object = ObservableObject() - expect(object.rac_value) == 0 - - property = DynamicProperty(object: object, keyPath: "rac_value") - } - - afterEach { - object = nil - } - - it("should bridge values sent on a signal to Objective-C") { - let (signal, observer) = Signal.pipe() - property <~ signal - observer.sendNext(1) - expect(object.rac_value) == 1 - } - - it("should bridge values sent on a signal producer to Objective-C") { - let producer = SignalProducer(value: 1) - property <~ producer - expect(object.rac_value) == 1 - } - - it("should bridge values from a source property to Objective-C") { - let source = MutableProperty(1) - property <~ source - expect(object.rac_value) == 1 - } - } - } - } -} - -private class ObservableObject: NSObject { - dynamic var rac_value: Int = 0 - dynamic var rac_reference: UnbridgedObject = UnbridgedObject("") -} - -private class UnbridgedObject: NSObject { - let value: String - init(_ value: String) { - self.value = value - } -} diff --git a/ReactiveCocoaTests/Swift/SchedulerSpec.swift b/ReactiveCocoaTests/Swift/SchedulerSpec.swift deleted file mode 100644 index 8060935706..0000000000 --- a/ReactiveCocoaTests/Swift/SchedulerSpec.swift +++ /dev/null @@ -1,298 +0,0 @@ -// -// SchedulerSpec.swift -// ReactiveCocoa -// -// Created by Justin Spahr-Summers on 2014-07-13. -// Copyright (c) 2014 GitHub. All rights reserved. -// - -import Foundation -import Nimble -import Quick -@testable -import ReactiveCocoa - -class SchedulerSpec: QuickSpec { - override func spec() { - describe("ImmediateScheduler") { - it("should run enqueued actions immediately") { - var didRun = false - ImmediateScheduler().schedule { - didRun = true - } - - expect(didRun) == true - } - } - - describe("UIScheduler") { - func dispatchSyncInBackground(_ action: () -> Void) { - let group = DispatchGroup() - - let globalQueue: DispatchQueue - if #available(*, OSX 10.10) { - globalQueue = DispatchQueue.global() - } else { - globalQueue = DispatchQueue.global(priority: .default) - } - - globalQueue.async(group: group, execute: action) - group.wait() - } - - it("should run actions immediately when on the main thread") { - let scheduler = UIScheduler() - var values: [Int] = [] - expect(Thread.isMainThread) == true - - scheduler.schedule { - values.append(0) - } - - expect(values) == [ 0 ] - - scheduler.schedule { - values.append(1) - } - - scheduler.schedule { - values.append(2) - } - - expect(values) == [ 0, 1, 2 ] - } - - it("should enqueue actions scheduled from the background") { - let scheduler = UIScheduler() - var values: [Int] = [] - - dispatchSyncInBackground { - scheduler.schedule { - expect(Thread.isMainThread) == true - values.append(0) - } - - return - } - - expect(values) == [] - expect(values).toEventually(equal([ 0 ])) - - dispatchSyncInBackground { - scheduler.schedule { - expect(Thread.isMainThread) == true - values.append(1) - } - - scheduler.schedule { - expect(Thread.isMainThread) == true - values.append(2) - } - - return - } - - expect(values) == [ 0 ] - expect(values).toEventually(equal([ 0, 1, 2 ])) - } - - it("should run actions enqueued from the main thread after those from the background") { - let scheduler = UIScheduler() - var values: [Int] = [] - - dispatchSyncInBackground { - scheduler.schedule { - expect(Thread.isMainThread) == true - values.append(0) - } - - return - } - - scheduler.schedule { - expect(Thread.isMainThread) == true - values.append(1) - } - - scheduler.schedule { - expect(Thread.isMainThread) == true - values.append(2) - } - - expect(values) == [] - expect(values).toEventually(equal([ 0, 1, 2 ])) - } - } - - describe("QueueScheduler") { - it("should run enqueued actions on a global queue") { - var didRun = false - - let scheduler: QueueScheduler - if #available(OSX 10.10, *) { - scheduler = QueueScheduler(qos: .default, name: "\(#file):\(#line)") - } else { - scheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) - } - - scheduler.schedule { - didRun = true - expect(Thread.isMainThread) == false - } - - expect{didRun}.toEventually(beTruthy()) - } - - describe("on a given queue") { - var scheduler: QueueScheduler! - - beforeEach { - if #available(OSX 10.10, *) { - scheduler = QueueScheduler(qos: .default, name: "\(#file):\(#line)") - } else { - scheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) - } - scheduler.queue.suspend() - } - - it("should run enqueued actions serially on the given queue") { - var value = 0 - - for _ in 0..<5 { - scheduler.schedule { - expect(Thread.isMainThread) == false - value += 1 - } - } - - expect(value) == 0 - - scheduler.queue.resume() - expect{value}.toEventually(equal(5)) - } - - it("should run enqueued actions after a given date") { - var didRun = false - scheduler.schedule(after: Date()) { - didRun = true - expect(Thread.isMainThread) == false - } - - expect(didRun) == false - - scheduler.queue.resume() - expect{didRun}.toEventually(beTruthy()) - } - - it("should repeatedly run actions after a given date") { - let disposable = SerialDisposable() - - var count = 0 - let timesToRun = 3 - - disposable.innerDisposable = scheduler.schedule(after: Date(), interval: 0.01, leeway: 0) { - expect(Thread.isMainThread) == false - - count += 1 - - if count == timesToRun { - disposable.dispose() - } - } - - expect(count) == 0 - - scheduler.queue.resume() - expect{count}.toEventually(equal(timesToRun)) - } - } - } - - describe("TestScheduler") { - var scheduler: TestScheduler! - var startDate: Date! - - // How much dates are allowed to differ when they should be "equal." - let dateComparisonDelta = 0.00001 - - beforeEach { - startDate = Date() - - scheduler = TestScheduler(startDate: startDate) - expect(scheduler.currentDate) == startDate - } - - it("should run immediately enqueued actions upon advancement") { - var string = "" - - scheduler.schedule { - string += "foo" - expect(Thread.isMainThread) == true - } - - scheduler.schedule { - string += "bar" - expect(Thread.isMainThread) == true - } - - expect(string) == "" - - scheduler.advance() - expect(scheduler.currentDate).to(beCloseTo(startDate)) - - expect(string) == "foobar" - } - - it("should run actions when advanced past the target date") { - var string = "" - - scheduler.schedule(after: 15) { [weak scheduler] in - string += "bar" - expect(Thread.isMainThread) == true - expect(scheduler?.currentDate).to(beCloseTo(startDate.addingTimeInterval(15), within: dateComparisonDelta)) - } - - scheduler.schedule(after: 5) { [weak scheduler] in - string += "foo" - expect(Thread.isMainThread) == true - expect(scheduler?.currentDate).to(beCloseTo(startDate.addingTimeInterval(5), within: dateComparisonDelta)) - } - - expect(string) == "" - - scheduler.advance(by: 10) - expect(scheduler.currentDate).to(beCloseTo(startDate.addingTimeInterval(10), within: TimeInterval(dateComparisonDelta))) - expect(string) == "foo" - - scheduler.advance(by: 10) - expect(scheduler.currentDate).to(beCloseTo(startDate.addingTimeInterval(20), within: dateComparisonDelta)) - expect(string) == "foobar" - } - - it("should run all remaining actions in order") { - var string = "" - - scheduler.schedule(after: 15) { - string += "bar" - expect(Thread.isMainThread) == true - } - - scheduler.schedule(after: 5) { - string += "foo" - expect(Thread.isMainThread) == true - } - - scheduler.schedule { - string += "fuzzbuzz" - expect(Thread.isMainThread) == true - } - - expect(string) == "" - - scheduler.run() - expect(scheduler.currentDate) == NSDate.distantFuture - expect(string) == "fuzzbuzzfoobar" - } - } - } -} diff --git a/ReactiveCocoaTests/Swift/SignalLifetimeSpec.swift b/ReactiveCocoaTests/Swift/SignalLifetimeSpec.swift deleted file mode 100644 index 30ad5f86b5..0000000000 --- a/ReactiveCocoaTests/Swift/SignalLifetimeSpec.swift +++ /dev/null @@ -1,414 +0,0 @@ -// -// SignalLifetimeSpec.swift -// ReactiveCocoa -// -// Created by Vadim Yelagin on 2015-12-13. -// Copyright (c) 2015 GitHub. All rights reserved. -// - -import Result -import Nimble -import Quick -import ReactiveCocoa - -class SignalLifetimeSpec: QuickSpec { - override func spec() { - describe("init") { - var testScheduler: TestScheduler! - - beforeEach { - testScheduler = TestScheduler() - } - - it("should deallocate") { - weak var signal: Signal? = Signal { _ in nil } - - expect(signal).to(beNil()) - } - - it("should deallocate if it does not have any observers") { - weak var signal: Signal? = { - let signal: Signal = Signal { _ in nil } - return signal - }() - expect(signal).to(beNil()) - } - - it("should deallocate if no one retains it") { - var signal: Signal? = Signal { _ in nil } - weak var weakSignal = signal - - expect(weakSignal).toNot(beNil()) - - var reference = signal - signal = nil - expect(weakSignal).toNot(beNil()) - - reference = nil - expect(weakSignal).to(beNil()) - } - - it("should deallocate even if the generator observer is retained") { - var observer: Signal.Observer? - - weak var signal: Signal? = { - let signal: Signal = Signal { innerObserver in - observer = innerObserver - return nil - } - return signal - }() - expect(observer).toNot(beNil()) - expect(signal).to(beNil()) - } - - it("should not deallocate if it has at least one observer") { - var disposable: Disposable? = nil - weak var signal: Signal? = { - let signal: Signal = Signal { _ in nil } - disposable = signal.observe(Observer()) - return signal - }() - expect(signal).toNot(beNil()) - disposable?.dispose() - expect(signal).to(beNil()) - } - - it("should be alive until erroring if it has at least one observer, despite not being explicitly retained") { - var errored = false - - weak var signal: Signal? = { - let signal = Signal { observer in - testScheduler.schedule { - observer.sendFailed(TestError.default) - } - return nil - } - signal.observeFailed { _ in errored = true } - return signal - }() - - expect(errored) == false - expect(signal).toNot(beNil()) - - testScheduler.run() - - expect(errored) == true - expect(signal).to(beNil()) - } - - it("should be alive until completion if it has at least one observer, despite not being explicitly retained") { - var completed = false - - weak var signal: Signal? = { - let signal = Signal { observer in - testScheduler.schedule { - observer.sendCompleted() - } - return nil - } - signal.observeCompleted { completed = true } - return signal - }() - - expect(completed) == false - expect(signal).toNot(beNil()) - - testScheduler.run() - - expect(completed) == true - expect(signal).to(beNil()) - } - - it("should be alive until interruption if it has at least one observer, despite not being explicitly retained") { - var interrupted = false - - weak var signal: Signal? = { - let signal = Signal { observer in - testScheduler.schedule { - observer.sendInterrupted() - } - - return nil - } - signal.observeInterrupted { interrupted = true } - return signal - }() - - expect(interrupted) == false - expect(signal).toNot(beNil()) - - testScheduler.run() - - expect(interrupted) == true - expect(signal).to(beNil()) - } - } - - describe("Signal.pipe") { - it("should deallocate") { - weak var signal = Signal<(), NoError>.pipe().0 - - expect(signal).to(beNil()) - } - - it("should be alive until erroring if it has at least one observer, despite not being explicitly retained") { - let testScheduler = TestScheduler() - var errored = false - weak var weakSignal: Signal<(), TestError>? - - // Use an inner closure to help ARC deallocate things as we - // expect. - let test = { - let (signal, observer) = Signal<(), TestError>.pipe() - weakSignal = signal - testScheduler.schedule { - // Note that the input observer has a weak reference to the signal. - observer.sendFailed(TestError.default) - } - signal.observeFailed { _ in errored = true } - } - test() - - expect(weakSignal).toNot(beNil()) - expect(errored) == false - - testScheduler.run() - expect(weakSignal).to(beNil()) - expect(errored) == true - } - - it("should be alive until completion if it has at least one observer, despite not being explicitly retained") { - let testScheduler = TestScheduler() - var completed = false - weak var weakSignal: Signal<(), TestError>? - - // Use an inner closure to help ARC deallocate things as we - // expect. - let test = { - let (signal, observer) = Signal<(), TestError>.pipe() - weakSignal = signal - testScheduler.schedule { - // Note that the input observer has a weak reference to the signal. - observer.sendCompleted() - } - signal.observeCompleted { completed = true } - } - test() - - expect(weakSignal).toNot(beNil()) - expect(completed) == false - - testScheduler.run() - expect(weakSignal).to(beNil()) - expect(completed) == true - } - - it("should be alive until interruption if it has at least one observer, despite not being explicitly retained") { - let testScheduler = TestScheduler() - var interrupted = false - weak var weakSignal: Signal<(), NoError>? - - let test = { - let (signal, observer) = Signal<(), NoError>.pipe() - weakSignal = signal - - testScheduler.schedule { - // Note that the input observer has a weak reference to the signal. - observer.sendInterrupted() - } - - signal.observeInterrupted { interrupted = true } - } - - test() - expect(weakSignal).toNot(beNil()) - expect(interrupted) == false - - testScheduler.run() - expect(weakSignal).to(beNil()) - expect(interrupted) == true - } - } - - describe("testTransform") { - it("should deallocate") { - weak var signal: Signal? = Signal { _ in nil }.testTransform() - - expect(signal).to(beNil()) - } - - it("should not deallocate if it has at least one observer, despite not being explicitly retained") { - weak var signal: Signal? = { - let signal: Signal = Signal { _ in nil }.testTransform() - signal.observe(Observer()) - return signal - }() - expect(signal).toNot(beNil()) - } - - it("should not deallocate if it has at least one observer, despite not being explicitly retained") { - var disposable: Disposable? = nil - weak var signal: Signal? = { - let signal: Signal = Signal { _ in nil }.testTransform() - disposable = signal.observe(Observer()) - return signal - }() - expect(signal).toNot(beNil()) - disposable?.dispose() - expect(signal).to(beNil()) - } - - it("should deallocate if it is unreachable and has no observer") { - let (sourceSignal, sourceObserver) = Signal.pipe() - - var firstCounter = 0 - var secondCounter = 0 - var thirdCounter = 0 - - func run() { - _ = sourceSignal - .map { value -> Int in - firstCounter += 1 - return value - } - .map { value -> Int in - secondCounter += 1 - return value - } - .map { value -> Int in - thirdCounter += 1 - return value - } - } - - run() - - sourceObserver.sendNext(1) - expect(firstCounter) == 0 - expect(secondCounter) == 0 - expect(thirdCounter) == 0 - - sourceObserver.sendNext(2) - expect(firstCounter) == 0 - expect(secondCounter) == 0 - expect(thirdCounter) == 0 - } - - it("should not deallocate if it is unreachable but still has at least one observer") { - let (sourceSignal, sourceObserver) = Signal.pipe() - - var firstCounter = 0 - var secondCounter = 0 - var thirdCounter = 0 - - var disposable: Disposable? - - func run() { - disposable = sourceSignal - .map { value -> Int in - firstCounter += 1 - return value - } - .map { value -> Int in - secondCounter += 1 - return value - } - .map { value -> Int in - thirdCounter += 1 - return value - } - .observe { _ in } - } - - run() - - sourceObserver.sendNext(1) - expect(firstCounter) == 1 - expect(secondCounter) == 1 - expect(thirdCounter) == 1 - - sourceObserver.sendNext(2) - expect(firstCounter) == 2 - expect(secondCounter) == 2 - expect(thirdCounter) == 2 - - disposable?.dispose() - - sourceObserver.sendNext(3) - expect(firstCounter) == 2 - expect(secondCounter) == 2 - expect(thirdCounter) == 2 - } - } - - describe("observe") { - var signal: Signal! - var observer: Signal.Observer! - - var token: NSObject? = nil - weak var weakToken: NSObject? - - func expectTokenNotDeallocated() { - expect(weakToken).toNot(beNil()) - } - - func expectTokenDeallocated() { - expect(weakToken).to(beNil()) - } - - beforeEach { - let (signalTemp, observerTemp) = Signal.pipe() - signal = signalTemp - observer = observerTemp - - token = NSObject() - weakToken = token - - signal.observe { [token = token] _ in - _ = token!.description - } - } - - it("should deallocate observe handler when signal completes") { - expectTokenNotDeallocated() - - observer.sendNext(1) - expectTokenNotDeallocated() - - token = nil - expectTokenNotDeallocated() - - observer.sendNext(2) - expectTokenNotDeallocated() - - observer.sendCompleted() - expectTokenDeallocated() - } - - it("should deallocate observe handler when signal fails") { - expectTokenNotDeallocated() - - observer.sendNext(1) - expectTokenNotDeallocated() - - token = nil - expectTokenNotDeallocated() - - observer.sendNext(2) - expectTokenNotDeallocated() - - observer.sendFailed(.default) - expectTokenDeallocated() - } - } - } -} - -private extension SignalProtocol { - func testTransform() -> Signal { - return Signal { observer in - return self.observe(observer.action) - } - } -} diff --git a/ReactiveCocoaTests/Swift/SignalProducerLiftingSpec.swift b/ReactiveCocoaTests/Swift/SignalProducerLiftingSpec.swift deleted file mode 100644 index eae2d84a3d..0000000000 --- a/ReactiveCocoaTests/Swift/SignalProducerLiftingSpec.swift +++ /dev/null @@ -1,1536 +0,0 @@ -// -// SignalProducerLiftingSpec.swift -// ReactiveCocoa -// -// Created by Neil Pankey on 6/14/15. -// Copyright © 2015 GitHub. All rights reserved. -// - -import Result -import Nimble -import Quick -import ReactiveCocoa - -class SignalProducerLiftingSpec: QuickSpec { - override func spec() { - describe("map") { - it("should transform the values of the signal") { - let (producer, observer) = SignalProducer.pipe() - let mappedProducer = producer.map { String($0 + 1) } - - var lastValue: String? - - mappedProducer.startWithNext { - lastValue = $0 - return - } - - expect(lastValue).to(beNil()) - - observer.sendNext(0) - expect(lastValue) == "1" - - observer.sendNext(1) - expect(lastValue) == "2" - } - } - - describe("mapError") { - it("should transform the errors of the signal") { - let (producer, observer) = SignalProducer.pipe() - let producerError = NSError(domain: "com.reactivecocoa.errordomain", code: 100, userInfo: nil) - var error: NSError? - - producer - .mapError { _ in producerError } - .startWithFailed { error = $0 } - - expect(error).to(beNil()) - - observer.sendFailed(TestError.default) - expect(error) == producerError - } - } - - describe("filter") { - it("should omit values from the producer") { - let (producer, observer) = SignalProducer.pipe() - let mappedProducer = producer.filter { $0 % 2 == 0 } - - var lastValue: Int? - - mappedProducer.startWithNext { lastValue = $0 } - - expect(lastValue).to(beNil()) - - observer.sendNext(0) - expect(lastValue) == 0 - - observer.sendNext(1) - expect(lastValue) == 0 - - observer.sendNext(2) - expect(lastValue) == 2 - } - } - - describe("skipNil") { - it("should forward only non-nil values") { - let (producer, observer) = SignalProducer.pipe() - let mappedProducer = producer.skipNil() - - var lastValue: Int? - - mappedProducer.startWithNext { lastValue = $0 } - expect(lastValue).to(beNil()) - - observer.sendNext(nil) - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue) == 1 - - observer.sendNext(nil) - expect(lastValue) == 1 - - observer.sendNext(2) - expect(lastValue) == 2 - } - } - - describe("scan") { - it("should incrementally accumulate a value") { - let (baseProducer, observer) = SignalProducer.pipe() - let producer = baseProducer.scan("", +) - - var lastValue: String? - - producer.startWithNext { lastValue = $0 } - - expect(lastValue).to(beNil()) - - observer.sendNext("a") - expect(lastValue) == "a" - - observer.sendNext("bb") - expect(lastValue) == "abb" - } - } - - describe("reduce") { - it("should accumulate one value") { - let (baseProducer, observer) = SignalProducer.pipe() - let producer = baseProducer.reduce(1, +) - - var lastValue: Int? - var completed = false - - producer.start { event in - switch event { - case let .next(value): - lastValue = value - case .completed: - completed = true - case .failed, .interrupted: - break - } - } - - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue).to(beNil()) - - observer.sendNext(2) - expect(lastValue).to(beNil()) - - expect(completed) == false - observer.sendCompleted() - expect(completed) == true - - expect(lastValue) == 4 - } - - it("should send the initial value if none are received") { - let (baseProducer, observer) = SignalProducer.pipe() - let producer = baseProducer.reduce(1, +) - - var lastValue: Int? - var completed = false - - producer.start { event in - switch event { - case let .next(value): - lastValue = value - case .completed: - completed = true - case .failed, .interrupted: - break - } - } - - expect(lastValue).to(beNil()) - expect(completed) == false - - observer.sendCompleted() - - expect(lastValue) == 1 - expect(completed) == true - } - } - - describe("skip") { - it("should skip initial values") { - let (baseProducer, observer) = SignalProducer.pipe() - let producer = baseProducer.skip(first: 1) - - var lastValue: Int? - producer.startWithNext { lastValue = $0 } - - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue).to(beNil()) - - observer.sendNext(2) - expect(lastValue) == 2 - } - - it("should not skip any values when 0") { - let (baseProducer, observer) = SignalProducer.pipe() - let producer = baseProducer.skip(first: 0) - - var lastValue: Int? - producer.startWithNext { lastValue = $0 } - - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue) == 1 - - observer.sendNext(2) - expect(lastValue) == 2 - } - } - - describe("skipRepeats") { - it("should skip duplicate Equatable values") { - let (baseProducer, observer) = SignalProducer.pipe() - let producer = baseProducer.skipRepeats() - - var values: [Bool] = [] - producer.startWithNext { values.append($0) } - - expect(values) == [] - - observer.sendNext(true) - expect(values) == [ true ] - - observer.sendNext(true) - expect(values) == [ true ] - - observer.sendNext(false) - expect(values) == [ true, false ] - - observer.sendNext(true) - expect(values) == [ true, false, true ] - } - - it("should skip values according to a predicate") { - let (baseProducer, observer) = SignalProducer.pipe() - let producer = baseProducer.skipRepeats { $0.characters.count == $1.characters.count } - - var values: [String] = [] - producer.startWithNext { values.append($0) } - - expect(values) == [] - - observer.sendNext("a") - expect(values) == [ "a" ] - - observer.sendNext("b") - expect(values) == [ "a" ] - - observer.sendNext("cc") - expect(values) == [ "a", "cc" ] - - observer.sendNext("d") - expect(values) == [ "a", "cc", "d" ] - } - } - - describe("skipWhile") { - var producer: SignalProducer! - var observer: Signal.Observer! - - var lastValue: Int? - - beforeEach { - let (baseProducer, incomingObserver) = SignalProducer.pipe() - - producer = baseProducer.skip { $0 < 2 } - observer = incomingObserver - lastValue = nil - - producer.startWithNext { lastValue = $0 } - } - - it("should skip while the predicate is true") { - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue).to(beNil()) - - observer.sendNext(2) - expect(lastValue) == 2 - - observer.sendNext(0) - expect(lastValue) == 0 - } - - it("should not skip any values when the predicate starts false") { - expect(lastValue).to(beNil()) - - observer.sendNext(3) - expect(lastValue) == 3 - - observer.sendNext(1) - expect(lastValue) == 1 - } - } - - describe("skipUntil") { - var producer: SignalProducer! - var observer: Signal.Observer! - var triggerObserver: Signal<(), NoError>.Observer! - - var lastValue: Int? = nil - - beforeEach { - let (baseProducer, baseIncomingObserver) = SignalProducer.pipe() - let (triggerProducer, incomingTriggerObserver) = SignalProducer<(), NoError>.pipe() - - producer = baseProducer.skip(until: triggerProducer) - observer = baseIncomingObserver - triggerObserver = incomingTriggerObserver - - lastValue = nil - - producer.start { event in - switch event { - case let .next(value): - lastValue = value - case .failed, .completed, .interrupted: - break - } - } - } - - it("should skip values until the trigger fires") { - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue).to(beNil()) - - observer.sendNext(2) - expect(lastValue).to(beNil()) - - triggerObserver.sendNext(()) - observer.sendNext(0) - expect(lastValue) == 0 - } - - it("should skip values until the trigger completes") { - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue).to(beNil()) - - observer.sendNext(2) - expect(lastValue).to(beNil()) - - triggerObserver.sendCompleted() - observer.sendNext(0) - expect(lastValue) == 0 - } - } - - describe("take") { - it("should take initial values") { - let (baseProducer, observer) = SignalProducer.pipe() - let producer = baseProducer.take(first: 2) - - var lastValue: Int? - var completed = false - producer.start { event in - switch event { - case let .next(value): - lastValue = value - case .completed: - completed = true - case .failed, .interrupted: - break - } - } - - expect(lastValue).to(beNil()) - expect(completed) == false - - observer.sendNext(1) - expect(lastValue) == 1 - expect(completed) == false - - observer.sendNext(2) - expect(lastValue) == 2 - expect(completed) == true - } - - it("should complete immediately after taking given number of values") { - let numbers = [ 1, 2, 4, 4, 5 ] - let testScheduler = TestScheduler() - - let producer: SignalProducer = SignalProducer { observer, _ in - // workaround `Class declaration cannot close over value 'observer' defined in outer scope` - let observer = observer - - testScheduler.schedule { - for number in numbers { - observer.sendNext(number) - } - } - } - - var completed = false - - producer - .take(first: numbers.count) - .startWithCompleted { completed = true } - - expect(completed) == false - testScheduler.run() - expect(completed) == true - } - - it("should interrupt when 0") { - let numbers = [ 1, 2, 4, 4, 5 ] - let testScheduler = TestScheduler() - - let producer: SignalProducer = SignalProducer { observer, _ in - // workaround `Class declaration cannot close over value 'observer' defined in outer scope` - let observer = observer - - testScheduler.schedule { - for number in numbers { - observer.sendNext(number) - } - } - } - - var result: [Int] = [] - var interrupted = false - - producer - .take(first: 0) - .start { event in - switch event { - case let .next(number): - result.append(number) - case .interrupted: - interrupted = true - case .failed, .completed: - break - } - } - - expect(interrupted) == true - - testScheduler.run() - expect(result).to(beEmpty()) - } - } - - describe("collect") { - it("should collect all values") { - let (original, observer) = SignalProducer.pipe() - let producer = original.collect() - let expectedResult = [ 1, 2, 3 ] - - var result: [Int]? - - producer.startWithNext { value in - expect(result).to(beNil()) - result = value - } - - for number in expectedResult { - observer.sendNext(number) - } - - expect(result).to(beNil()) - observer.sendCompleted() - expect(result) == expectedResult - } - - it("should complete with an empty array if there are no values") { - let (original, observer) = SignalProducer.pipe() - let producer = original.collect() - - var result: [Int]? - - producer.startWithNext { result = $0 } - - expect(result).to(beNil()) - observer.sendCompleted() - expect(result) == [] - } - - it("should forward errors") { - let (original, observer) = SignalProducer.pipe() - let producer = original.collect() - - var error: TestError? - - producer.startWithFailed { error = $0 } - - expect(error).to(beNil()) - observer.sendFailed(.default) - expect(error) == TestError.default - } - - it("should collect an exact count of values") { - let (original, observer) = SignalProducer.pipe() - - let producer = original.collect(count: 3) - - var observedValues: [[Int]] = [] - - producer.startWithNext { value in - observedValues.append(value) - } - - var expectation: [[Int]] = [] - - for i in 1...7 { - - observer.sendNext(i) - - if i % 3 == 0 { - expectation.append([Int]((i - 2)...i)) - expect(observedValues) == expectation - } else { - expect(observedValues) == expectation - } - } - - observer.sendCompleted() - - expectation.append([7]) - expect(observedValues) == expectation - } - - it("should collect values until it matches a certain value") { - let (original, observer) = SignalProducer.pipe() - - let producer = original.collect { _, next in next != 5 } - - var expectedValues = [ - [5, 5], - [42, 5] - ] - - producer.startWithNext { value in - expect(value) == expectedValues.removeFirst() - } - - producer.startWithCompleted { - expect(expectedValues) == [] - } - - expectedValues - .flatMap { $0 } - .forEach(observer.sendNext) - - observer.sendCompleted() - } - - it("should collect values until it matches a certain condition on values") { - let (original, observer) = SignalProducer.pipe() - - let producer = original.collect { values in values.reduce(0, +) == 10 } - - var expectedValues = [ - [1, 2, 3, 4], - [5, 6, 7, 8, 9] - ] - - producer.startWithNext { value in - expect(value) == expectedValues.removeFirst() - } - - producer.startWithCompleted { - expect(expectedValues) == [] - } - - expectedValues - .flatMap { $0 } - .forEach(observer.sendNext) - - observer.sendCompleted() - } - - } - - describe("takeUntil") { - var producer: SignalProducer! - var observer: Signal.Observer! - var triggerObserver: Signal<(), NoError>.Observer! - - var lastValue: Int? = nil - var completed: Bool = false - - beforeEach { - let (baseProducer, baseIncomingObserver) = SignalProducer.pipe() - let (triggerProducer, incomingTriggerObserver) = SignalProducer<(), NoError>.pipe() - - producer = baseProducer.take(until: triggerProducer) - observer = baseIncomingObserver - triggerObserver = incomingTriggerObserver - - lastValue = nil - completed = false - - producer.start { event in - switch event { - case let .next(value): - lastValue = value - case .completed: - completed = true - case .failed, .interrupted: - break - } - } - } - - it("should take values until the trigger fires") { - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue) == 1 - - observer.sendNext(2) - expect(lastValue) == 2 - - expect(completed) == false - triggerObserver.sendNext(()) - expect(completed) == true - } - - it("should take values until the trigger completes") { - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue) == 1 - - observer.sendNext(2) - expect(lastValue) == 2 - - expect(completed) == false - triggerObserver.sendCompleted() - expect(completed) == true - } - - it("should complete if the trigger fires immediately") { - expect(lastValue).to(beNil()) - expect(completed) == false - - triggerObserver.sendNext(()) - - expect(completed) == true - expect(lastValue).to(beNil()) - } - } - - describe("takeUntilReplacement") { - var producer: SignalProducer! - var observer: Signal.Observer! - var replacementObserver: Signal.Observer! - - var lastValue: Int? = nil - var completed: Bool = false - - beforeEach { - let (baseProducer, incomingObserver) = SignalProducer.pipe() - let (replacementProducer, incomingReplacementObserver) = SignalProducer.pipe() - - producer = baseProducer.take(untilReplacement: replacementProducer) - observer = incomingObserver - replacementObserver = incomingReplacementObserver - - lastValue = nil - completed = false - - producer.start { event in - switch event { - case let .next(value): - lastValue = value - case .completed: - completed = true - case .failed, .interrupted: - break - } - } - } - - it("should take values from the original then the replacement") { - expect(lastValue).to(beNil()) - expect(completed) == false - - observer.sendNext(1) - expect(lastValue) == 1 - - observer.sendNext(2) - expect(lastValue) == 2 - - replacementObserver.sendNext(3) - - expect(lastValue) == 3 - expect(completed) == false - - observer.sendNext(4) - - expect(lastValue) == 3 - expect(completed) == false - - replacementObserver.sendNext(5) - expect(lastValue) == 5 - - expect(completed) == false - replacementObserver.sendCompleted() - expect(completed) == true - } - } - - describe("takeWhile") { - var producer: SignalProducer! - var observer: Signal.Observer! - - beforeEach { - let (baseProducer, incomingObserver) = SignalProducer.pipe() - producer = baseProducer.take { $0 <= 4 } - observer = incomingObserver - } - - it("should take while the predicate is true") { - var latestValue: Int! - var completed = false - - producer.start { event in - switch event { - case let .next(value): - latestValue = value - case .completed: - completed = true - case .failed, .interrupted: - break - } - } - - for value in -1...4 { - observer.sendNext(value) - expect(latestValue) == value - expect(completed) == false - } - - observer.sendNext(5) - expect(latestValue) == 4 - expect(completed) == true - } - - it("should complete if the predicate starts false") { - var latestValue: Int? - var completed = false - - producer.start { event in - switch event { - case let .next(value): - latestValue = value - case .completed: - completed = true - case .failed, .interrupted: - break - } - } - - observer.sendNext(5) - expect(latestValue).to(beNil()) - expect(completed) == true - } - } - - describe("observeOn") { - it("should send events on the given scheduler") { - let testScheduler = TestScheduler() - let (producer, observer) = SignalProducer.pipe() - - var result: [Int] = [] - - producer - .observe(on: testScheduler) - .startWithNext { result.append($0) } - - observer.sendNext(1) - observer.sendNext(2) - expect(result).to(beEmpty()) - - testScheduler.run() - expect(result) == [ 1, 2 ] - } - } - - describe("delay") { - it("should send events on the given scheduler after the interval") { - let testScheduler = TestScheduler() - let producer: SignalProducer = SignalProducer { observer, _ in - testScheduler.schedule { - observer.sendNext(1) - } - testScheduler.schedule(after: 5) { - observer.sendNext(2) - observer.sendCompleted() - } - } - - var result: [Int] = [] - var completed = false - - producer - .delay(10, on: testScheduler) - .start { event in - switch event { - case let .next(number): - result.append(number) - case .completed: - completed = true - case .failed, .interrupted: - break - } - } - - testScheduler.advance(by: 4) // send initial value - expect(result).to(beEmpty()) - - testScheduler.advance(by: 10) // send second value and receive first - expect(result) == [ 1 ] - expect(completed) == false - - testScheduler.advance(by: 10) // send second value and receive first - expect(result) == [ 1, 2 ] - expect(completed) == true - } - - it("should schedule errors immediately") { - let testScheduler = TestScheduler() - let producer: SignalProducer = SignalProducer { observer, _ in - // workaround `Class declaration cannot close over value 'observer' defined in outer scope` - let observer = observer - - testScheduler.schedule { - observer.sendFailed(TestError.default) - } - } - - var errored = false - - producer - .delay(10, on: testScheduler) - .startWithFailed { _ in errored = true } - - testScheduler.advance() - expect(errored) == true - } - } - - describe("throttle") { - var scheduler: TestScheduler! - var observer: Signal.Observer! - var producer: SignalProducer! - - beforeEach { - scheduler = TestScheduler() - - let (baseProducer, baseObserver) = SignalProducer.pipe() - observer = baseObserver - - producer = baseProducer.throttle(1, on: scheduler) - } - - it("should send values on the given scheduler at no less than the interval") { - var values: [Int] = [] - producer.startWithNext { value in - values.append(value) - } - - expect(values) == [] - - observer.sendNext(0) - expect(values) == [] - - scheduler.advance() - expect(values) == [ 0 ] - - observer.sendNext(1) - observer.sendNext(2) - expect(values) == [ 0 ] - - scheduler.advance(by: 1.5) - expect(values) == [ 0, 2 ] - - scheduler.advance(by: 3) - expect(values) == [ 0, 2 ] - - observer.sendNext(3) - expect(values) == [ 0, 2 ] - - scheduler.advance() - expect(values) == [ 0, 2, 3 ] - - observer.sendNext(4) - observer.sendNext(5) - scheduler.advance() - expect(values) == [ 0, 2, 3 ] - - scheduler.rewind(by: 2) - expect(values) == [ 0, 2, 3 ] - - observer.sendNext(6) - scheduler.advance() - expect(values) == [ 0, 2, 3, 6 ] - - observer.sendNext(7) - observer.sendNext(8) - scheduler.advance() - expect(values) == [ 0, 2, 3, 6 ] - - scheduler.run() - expect(values) == [ 0, 2, 3, 6, 8 ] - } - - it("should schedule completion immediately") { - var values: [Int] = [] - var completed = false - - producer.start { event in - switch event { - case let .next(value): - values.append(value) - case .completed: - completed = true - case .failed, .interrupted: - break - } - } - - observer.sendNext(0) - scheduler.advance() - expect(values) == [ 0 ] - - observer.sendNext(1) - observer.sendCompleted() - expect(completed) == false - - scheduler.run() - expect(values) == [ 0 ] - expect(completed) == true - } - } - - describe("sampleWith") { - var sampledProducer: SignalProducer<(Int, String), NoError>! - var observer: Signal.Observer! - var samplerObserver: Signal.Observer! - - beforeEach { - let (producer, incomingObserver) = SignalProducer.pipe() - let (sampler, incomingSamplerObserver) = SignalProducer.pipe() - sampledProducer = producer.sample(with: sampler) - observer = incomingObserver - samplerObserver = incomingSamplerObserver - } - - it("should forward the latest value when the sampler fires") { - var result: [String] = [] - sampledProducer.startWithNext { (left, right) in result.append("\(left)\(right)") } - - observer.sendNext(1) - observer.sendNext(2) - samplerObserver.sendNext("a") - expect(result) == [ "2a" ] - } - - it("should do nothing if sampler fires before signal receives value") { - var result: [String] = [] - sampledProducer.startWithNext { (left, right) in result.append("\(left)\(right)") } - - samplerObserver.sendNext("a") - expect(result).to(beEmpty()) - } - - it("should send lates value multiple times when sampler fires multiple times") { - var result: [String] = [] - sampledProducer.startWithNext { (left, right) in result.append("\(left)\(right)") } - - observer.sendNext(1) - samplerObserver.sendNext("a") - samplerObserver.sendNext("b") - expect(result) == [ "1a", "1b" ] - } - - it("should complete when both inputs have completed") { - var completed = false - sampledProducer.startWithCompleted { completed = true } - - observer.sendCompleted() - expect(completed) == false - - samplerObserver.sendCompleted() - expect(completed) == true - } - - it("should emit an initial value if the sampler is a synchronous SignalProducer") { - let producer = SignalProducer(values: [1]) - let sampler = SignalProducer(value: "a") - - let result = producer.sample(with: sampler) - - var valueReceived: String? - result.startWithNext { (left, right) in valueReceived = "\(left)\(right)" } - - expect(valueReceived) == "1a" - } - } - - describe("sampleOn") { - var sampledProducer: SignalProducer! - var observer: Signal.Observer! - var samplerObserver: Signal<(), NoError>.Observer! - - beforeEach { - let (producer, incomingObserver) = SignalProducer.pipe() - let (sampler, incomingSamplerObserver) = SignalProducer<(), NoError>.pipe() - sampledProducer = producer.sample(on: sampler) - observer = incomingObserver - samplerObserver = incomingSamplerObserver - } - - it("should forward the latest value when the sampler fires") { - var result: [Int] = [] - sampledProducer.startWithNext { result.append($0) } - - observer.sendNext(1) - observer.sendNext(2) - samplerObserver.sendNext(()) - expect(result) == [ 2 ] - } - - it("should do nothing if sampler fires before signal receives value") { - var result: [Int] = [] - sampledProducer.startWithNext { result.append($0) } - - samplerObserver.sendNext(()) - expect(result).to(beEmpty()) - } - - it("should send lates value multiple times when sampler fires multiple times") { - var result: [Int] = [] - sampledProducer.startWithNext { result.append($0) } - - observer.sendNext(1) - samplerObserver.sendNext(()) - samplerObserver.sendNext(()) - expect(result) == [ 1, 1 ] - } - - it("should complete when both inputs have completed") { - var completed = false - sampledProducer.startWithCompleted { completed = true } - - observer.sendCompleted() - expect(completed) == false - - samplerObserver.sendCompleted() - expect(completed) == true - } - - it("should emit an initial value if the sampler is a synchronous SignalProducer") { - let producer = SignalProducer(values: [1]) - let sampler = SignalProducer<(), NoError>(value: ()) - - let result = producer.sample(on: sampler) - - var valueReceived: Int? - result.startWithNext { valueReceived = $0 } - - expect(valueReceived) == 1 - } - - describe("memory") { - class Payload { - let action: () -> Void - - init(onDeinit action: () -> Void) { - self.action = action - } - - deinit { - action() - } - } - - var sampledProducer: SignalProducer! - var observer: Signal.Observer! - - beforeEach { - let (producer, incomingObserver) = SignalProducer.pipe() - let (sampler, _) = Signal<(), NoError>.pipe() - sampledProducer = producer.sample(on: sampler) - observer = incomingObserver - } - - it("should free payload when interrupted after complete of incoming producer") { - var payloadFreed = false - - let disposable = sampledProducer.start() - - observer.sendNext(Payload { payloadFreed = true }) - observer.sendCompleted() - - expect(payloadFreed) == false - - disposable.dispose() - expect(payloadFreed) == true - } - } - } - - describe("combineLatestWith") { - var combinedProducer: SignalProducer<(Int, Double), NoError>! - var observer: Signal.Observer! - var otherObserver: Signal.Observer! - - beforeEach { - let (producer, incomingObserver) = SignalProducer.pipe() - let (otherSignal, incomingOtherObserver) = SignalProducer.pipe() - combinedProducer = producer.combineLatest(with: otherSignal) - observer = incomingObserver - otherObserver = incomingOtherObserver - } - - it("should forward the latest values from both inputs") { - var latest: (Int, Double)? - combinedProducer.startWithNext { latest = $0 } - - observer.sendNext(1) - expect(latest).to(beNil()) - - // is there a better way to test tuples? - otherObserver.sendNext(1.5) - expect(latest?.0) == 1 - expect(latest?.1) == 1.5 - - observer.sendNext(2) - expect(latest?.0) == 2 - expect(latest?.1) == 1.5 - } - - it("should complete when both inputs have completed") { - var completed = false - combinedProducer.startWithCompleted { completed = true } - - observer.sendCompleted() - expect(completed) == false - - otherObserver.sendCompleted() - expect(completed) == true - } - } - - describe("zipWith") { - var leftObserver: Signal.Observer! - var rightObserver: Signal.Observer! - var zipped: SignalProducer<(Int, String), NoError>! - - beforeEach { - let (leftProducer, incomingLeftObserver) = SignalProducer.pipe() - let (rightProducer, incomingRightObserver) = SignalProducer.pipe() - - leftObserver = incomingLeftObserver - rightObserver = incomingRightObserver - zipped = leftProducer.zip(with: rightProducer) - } - - it("should combine pairs") { - var result: [String] = [] - zipped.startWithNext { (left, right) in result.append("\(left)\(right)") } - - leftObserver.sendNext(1) - leftObserver.sendNext(2) - expect(result) == [] - - rightObserver.sendNext("foo") - expect(result) == [ "1foo" ] - - leftObserver.sendNext(3) - rightObserver.sendNext("bar") - expect(result) == [ "1foo", "2bar" ] - - rightObserver.sendNext("buzz") - expect(result) == [ "1foo", "2bar", "3buzz" ] - - rightObserver.sendNext("fuzz") - expect(result) == [ "1foo", "2bar", "3buzz" ] - - leftObserver.sendNext(4) - expect(result) == [ "1foo", "2bar", "3buzz", "4fuzz" ] - } - - it("should complete when the shorter signal has completed") { - var result: [String] = [] - var completed = false - - zipped.start { event in - switch event { - case let .next(left, right): - result.append("\(left)\(right)") - case .completed: - completed = true - case .failed, .interrupted: - break - } - } - - expect(completed) == false - - leftObserver.sendNext(0) - leftObserver.sendCompleted() - expect(completed) == false - expect(result) == [] - - rightObserver.sendNext("foo") - expect(completed) == true - expect(result) == [ "0foo" ] - } - } - - describe("materialize") { - it("should reify events from the signal") { - let (producer, observer) = SignalProducer.pipe() - var latestEvent: Event? - producer - .materialize() - .startWithNext { latestEvent = $0 } - - observer.sendNext(2) - - expect(latestEvent).toNot(beNil()) - if let latestEvent = latestEvent { - switch latestEvent { - case let .next(value): - expect(value) == 2 - case .failed, .completed, .interrupted: - fail() - } - } - - observer.sendFailed(TestError.default) - if let latestEvent = latestEvent { - switch latestEvent { - case .failed: - break - case .next, .completed, .interrupted: - fail() - } - } - } - } - - describe("dematerialize") { - typealias IntEvent = Event - var observer: Signal.Observer! - var dematerialized: SignalProducer! - - beforeEach { - let (producer, incomingObserver) = SignalProducer.pipe() - observer = incomingObserver - dematerialized = producer.dematerialize() - } - - it("should send values for Next events") { - var result: [Int] = [] - dematerialized - .assumeNoErrors() - .startWithNext { result.append($0) } - - expect(result).to(beEmpty()) - - observer.sendNext(.next(2)) - expect(result) == [ 2 ] - - observer.sendNext(.next(4)) - expect(result) == [ 2, 4 ] - } - - it("should error out for Error events") { - var errored = false - dematerialized.startWithFailed { _ in errored = true } - - expect(errored) == false - - observer.sendNext(.failed(TestError.default)) - expect(errored) == true - } - - it("should complete early for Completed events") { - var completed = false - dematerialized.startWithCompleted { completed = true } - - expect(completed) == false - observer.sendNext(IntEvent.completed) - expect(completed) == true - } - } - - describe("takeLast") { - var observer: Signal.Observer! - var lastThree: SignalProducer! - - beforeEach { - let (producer, incomingObserver) = SignalProducer.pipe() - observer = incomingObserver - lastThree = producer.take(last: 3) - } - - it("should send the last N values upon completion") { - var result: [Int] = [] - lastThree - .assumeNoErrors() - .startWithNext { result.append($0) } - - observer.sendNext(1) - observer.sendNext(2) - observer.sendNext(3) - observer.sendNext(4) - expect(result).to(beEmpty()) - - observer.sendCompleted() - expect(result) == [ 2, 3, 4 ] - } - - it("should send less than N values if not enough were received") { - var result: [Int] = [] - lastThree - .assumeNoErrors() - .startWithNext { result.append($0) } - - observer.sendNext(1) - observer.sendNext(2) - observer.sendCompleted() - expect(result) == [ 1, 2 ] - } - - it("should send nothing when errors") { - var result: [Int] = [] - var errored = false - lastThree.start { event in - switch event { - case let .next(value): - result.append(value) - case .failed: - errored = true - case .completed, .interrupted: - break - } - } - - observer.sendNext(1) - observer.sendNext(2) - observer.sendNext(3) - expect(errored) == false - - observer.sendFailed(TestError.default) - expect(errored) == true - expect(result).to(beEmpty()) - } - } - - describe("timeoutWithError") { - var testScheduler: TestScheduler! - var producer: SignalProducer! - var observer: Signal.Observer! - - beforeEach { - testScheduler = TestScheduler() - let (baseProducer, incomingObserver) = SignalProducer.pipe() - producer = baseProducer.timeout(after: 2, raising: TestError.default, on: testScheduler) - observer = incomingObserver - } - - it("should complete if within the interval") { - var completed = false - var errored = false - producer.start { event in - switch event { - case .completed: - completed = true - case .failed: - errored = true - case .next, .interrupted: - break - } - } - - testScheduler.schedule(after: 1) { - observer.sendCompleted() - } - - expect(completed) == false - expect(errored) == false - - testScheduler.run() - expect(completed) == true - expect(errored) == false - } - - it("should error if not completed before the interval has elapsed") { - var completed = false - var errored = false - producer.start { event in - switch event { - case .completed: - completed = true - case .failed: - errored = true - case .next, .interrupted: - break - } - } - - testScheduler.schedule(after: 3) { - observer.sendCompleted() - } - - expect(completed) == false - expect(errored) == false - - testScheduler.run() - expect(completed) == false - expect(errored) == true - } - - it("should be available for NoError") { - let producer: SignalProducer = SignalProducer.never - .timeout(after: 2, raising: TestError.default, on: testScheduler) - - _ = producer - } - } - - describe("attempt") { - it("should forward original values upon success") { - let (baseProducer, observer) = SignalProducer.pipe() - let producer = baseProducer.attempt { _ in - return .success() - } - - var current: Int? - producer - .assumeNoErrors() - .startWithNext { value in - current = value - } - - for value in 1...5 { - observer.sendNext(value) - expect(current) == value - } - } - - it("should error if an attempt fails") { - let (baseProducer, observer) = SignalProducer.pipe() - let producer = baseProducer.attempt { _ in - return .failure(.default) - } - - var error: TestError? - producer.startWithFailed { err in - error = err - } - - observer.sendNext(42) - expect(error) == TestError.default - } - } - - describe("attemptMap") { - it("should forward mapped values upon success") { - let (baseProducer, observer) = SignalProducer.pipe() - let producer = baseProducer.attemptMap { num -> Result in - return .success(num % 2 == 0) - } - - var even: Bool? - producer - .assumeNoErrors() - .startWithNext { value in - even = value - } - - observer.sendNext(1) - expect(even) == false - - observer.sendNext(2) - expect(even) == true - } - - it("should error if a mapping fails") { - let (baseProducer, observer) = SignalProducer.pipe() - let producer = baseProducer.attemptMap { _ -> Result in - return .failure(.default) - } - - var error: TestError? - producer.startWithFailed { err in - error = err - } - - observer.sendNext(42) - expect(error) == TestError.default - } - } - - describe("combinePrevious") { - var observer: Signal.Observer! - let initialValue: Int = 0 - var latestValues: (Int, Int)? - - beforeEach { - latestValues = nil - - let (signal, baseObserver) = SignalProducer.pipe() - observer = baseObserver - signal.combinePrevious(initialValue).startWithNext { latestValues = $0 } - } - - it("should forward the latest value with previous value") { - expect(latestValues).to(beNil()) - - observer.sendNext(1) - expect(latestValues?.0) == initialValue - expect(latestValues?.1) == 1 - - observer.sendNext(2) - expect(latestValues?.0) == 1 - expect(latestValues?.1) == 2 - } - } - } -} diff --git a/ReactiveCocoaTests/Swift/SignalProducerNimbleMatchers.swift b/ReactiveCocoaTests/Swift/SignalProducerNimbleMatchers.swift index ad2d89e569..5790549622 100644 --- a/ReactiveCocoaTests/Swift/SignalProducerNimbleMatchers.swift +++ b/ReactiveCocoaTests/Swift/SignalProducerNimbleMatchers.swift @@ -9,6 +9,7 @@ import Foundation import ReactiveCocoa +import ReactiveSwift import Nimble public func sendValue(_ value: T?, sendError: E?, complete: Bool) -> NonNilMatcherFunc> { diff --git a/ReactiveCocoaTests/Swift/SignalProducerSpec.swift b/ReactiveCocoaTests/Swift/SignalProducerSpec.swift deleted file mode 100644 index ce7ca47861..0000000000 --- a/ReactiveCocoaTests/Swift/SignalProducerSpec.swift +++ /dev/null @@ -1,2257 +0,0 @@ -// -// SignalProducerSpec.swift -// ReactiveCocoa -// -// Created by Justin Spahr-Summers on 2015-01-23. -// Copyright (c) 2015 GitHub. All rights reserved. -// - -import Foundation - -import Result -import Nimble -import Quick -import ReactiveCocoa - -class SignalProducerSpec: QuickSpec { - override func spec() { - describe("init") { - it("should run the handler once per start()") { - var handlerCalledTimes = 0 - let signalProducer = SignalProducer() { observer, disposable in - handlerCalledTimes += 1 - - return - } - - signalProducer.start() - signalProducer.start() - - expect(handlerCalledTimes) == 2 - } - - it("should not release signal observers when given disposable is disposed") { - var disposable: Disposable! - - let producer = SignalProducer { observer, innerDisposable in - disposable = innerDisposable - - innerDisposable += { - // This is necessary to keep the observer long enough to - // even test the memory management. - observer.sendNext(0) - } - } - - weak var objectRetainedByObserver: NSObject? - producer.startWithSignal { signal, _ in - let object = NSObject() - objectRetainedByObserver = object - signal.observeNext { _ in _ = object } - } - - expect(objectRetainedByObserver).toNot(beNil()) - - disposable.dispose() - - // https://github.com/ReactiveCocoa/ReactiveCocoa/pull/2959 - // - // Before #2959, this would be `nil` as the input observer is not - // retained, and observers would not retain the signal. - // - // After #2959, the object is still retained, since the observation - // keeps the signal alive. - expect(objectRetainedByObserver).toNot(beNil()) - } - - it("should dispose of added disposables upon completion") { - let addedDisposable = SimpleDisposable() - var observer: Signal<(), NoError>.Observer! - - let producer = SignalProducer<(), NoError>() { incomingObserver, disposable in - disposable += addedDisposable - observer = incomingObserver - } - - producer.start() - expect(addedDisposable.isDisposed) == false - - observer.sendCompleted() - expect(addedDisposable.isDisposed) == true - } - - it("should dispose of added disposables upon error") { - let addedDisposable = SimpleDisposable() - var observer: Signal<(), TestError>.Observer! - - let producer = SignalProducer<(), TestError>() { incomingObserver, disposable in - disposable += addedDisposable - observer = incomingObserver - } - - producer.start() - expect(addedDisposable.isDisposed) == false - - observer.sendFailed(.default) - expect(addedDisposable.isDisposed) == true - } - - it("should dispose of added disposables upon interruption") { - let addedDisposable = SimpleDisposable() - var observer: Signal<(), NoError>.Observer! - - let producer = SignalProducer<(), NoError>() { incomingObserver, disposable in - disposable += addedDisposable - observer = incomingObserver - } - - producer.start() - expect(addedDisposable.isDisposed) == false - - observer.sendInterrupted() - expect(addedDisposable.isDisposed) == true - } - - it("should dispose of added disposables upon start() disposal") { - let addedDisposable = SimpleDisposable() - - let producer = SignalProducer<(), TestError>() { _, disposable in - disposable += addedDisposable - return - } - - let startDisposable = producer.start() - expect(addedDisposable.isDisposed) == false - - startDisposable.dispose() - expect(addedDisposable.isDisposed) == true - } - } - - describe("init(signal:)") { - var signal: Signal! - var observer: Signal.Observer! - - beforeEach { - // Cannot directly assign due to compiler crash on Xcode 7.0.1 - let (signalTemp, observerTemp) = Signal.pipe() - signal = signalTemp - observer = observerTemp - } - - it("should emit values then complete") { - let producer = SignalProducer(signal: signal) - - var values: [Int] = [] - var error: TestError? - var completed = false - producer.start { event in - switch event { - case let .next(value): - values.append(value) - case let .failed(err): - error = err - case .completed: - completed = true - default: - break - } - } - - expect(values) == [] - expect(error).to(beNil()) - expect(completed) == false - - observer.sendNext(1) - expect(values) == [ 1 ] - observer.sendNext(2) - observer.sendNext(3) - expect(values) == [ 1, 2, 3 ] - - observer.sendCompleted() - expect(completed) == true - } - - it("should emit error") { - let producer = SignalProducer(signal: signal) - - var error: TestError? - let sentError = TestError.default - - producer.start { event in - switch event { - case let .failed(err): - error = err - default: - break - } - } - - expect(error).to(beNil()) - - observer.sendFailed(sentError) - expect(error) == sentError - } - } - - describe("init(value:)") { - it("should immediately send the value then complete") { - let producerValue = "StringValue" - let signalProducer = SignalProducer(value: producerValue) - - expect(signalProducer).to(sendValue(producerValue, sendError: nil, complete: true)) - } - } - - describe("init(error:)") { - it("should immediately send the error") { - let producerError = NSError(domain: "com.reactivecocoa.errordomain", code: 4815, userInfo: nil) - let signalProducer = SignalProducer(error: producerError) - - expect(signalProducer).to(sendValue(nil, sendError: producerError, complete: false)) - } - } - - describe("init(result:)") { - it("should immediately send the value then complete") { - let producerValue = "StringValue" - let producerResult = .success(producerValue) as Result - let signalProducer = SignalProducer(result: producerResult) - - expect(signalProducer).to(sendValue(producerValue, sendError: nil, complete: true)) - } - - it("should immediately send the error") { - let producerError = NSError(domain: "com.reactivecocoa.errordomain", code: 4815, userInfo: nil) - let producerResult = .failure(producerError) as Result - let signalProducer = SignalProducer(result: producerResult) - - expect(signalProducer).to(sendValue(nil, sendError: producerError, complete: false)) - } - } - - describe("init(values:)") { - it("should immediately send the sequence of values") { - let sequenceValues = [1, 2, 3] - let signalProducer = SignalProducer(values: sequenceValues) - - expect(signalProducer).to(sendValues(sequenceValues, sendError: nil, complete: true)) - } - } - - describe("SignalProducer.empty") { - it("should immediately complete") { - let signalProducer = SignalProducer.empty - - expect(signalProducer).to(sendValue(nil, sendError: nil, complete: true)) - } - } - - describe("SignalProducer.never") { - it("should not send any events") { - let signalProducer = SignalProducer.never - - expect(signalProducer).to(sendValue(nil, sendError: nil, complete: false)) - } - } - - describe("trailing closure") { - it("receives next values") { - let (producer, observer) = SignalProducer.pipe() - - var values = [Int]() - producer.startWithNext { next in - values.append(next) - } - - observer.sendNext(1) - expect(values) == [1] - } - } - - describe("SignalProducer.attempt") { - it("should run the operation once per start()") { - var operationRunTimes = 0 - let operation: () -> Result = { - operationRunTimes += 1 - - return .success("OperationValue") - } - - SignalProducer.attempt(operation).start() - SignalProducer.attempt(operation).start() - - expect(operationRunTimes) == 2 - } - - it("should send the value then complete") { - let operationReturnValue = "OperationValue" - let operation: () -> Result = { - return .success(operationReturnValue) - } - - let signalProducer = SignalProducer.attempt(operation) - - expect(signalProducer).to(sendValue(operationReturnValue, sendError: nil, complete: true)) - } - - it("should send the error") { - let operationError = NSError(domain: "com.reactivecocoa.errordomain", code: 4815, userInfo: nil) - let operation: () -> Result = { - return .failure(operationError) - } - - let signalProducer = SignalProducer.attempt(operation) - - expect(signalProducer).to(sendValue(nil, sendError: operationError, complete: false)) - } - } - - describe("startWithSignal") { - it("should invoke the closure before any effects or events") { - var started = false - var value: Int? - - SignalProducer(value: 42) - .on(started: { - started = true - }, next: { - value = $0 - }) - .startWithSignal { _ in - expect(started) == false - expect(value).to(beNil()) - } - - expect(started) == true - expect(value) == 42 - } - - it("should dispose of added disposables if disposed") { - let addedDisposable = SimpleDisposable() - var disposable: Disposable! - - let producer = SignalProducer() { _, disposable in - disposable += addedDisposable - return - } - - producer.startWithSignal { _, innerDisposable in - disposable = innerDisposable - } - - expect(addedDisposable.isDisposed) == false - - disposable.dispose() - expect(addedDisposable.isDisposed) == true - } - - it("should send interrupted if disposed") { - var interrupted = false - var disposable: Disposable! - - SignalProducer(value: 42) - .start(on: TestScheduler()) - .startWithSignal { signal, innerDisposable in - signal.observeInterrupted { - interrupted = true - } - - disposable = innerDisposable - } - - expect(interrupted) == false - - disposable.dispose() - expect(interrupted) == true - } - - it("should release signal observers if disposed") { - weak var objectRetainedByObserver: NSObject? - var disposable: Disposable! - - let producer = SignalProducer.never - producer.startWithSignal { signal, innerDisposable in - let object = NSObject() - objectRetainedByObserver = object - signal.observeNext { _ in _ = object.description } - disposable = innerDisposable - } - - expect(objectRetainedByObserver).toNot(beNil()) - - disposable.dispose() - expect(objectRetainedByObserver).to(beNil()) - } - - it("should not trigger effects if disposed before closure return") { - var started = false - var value: Int? - - SignalProducer(value: 42) - .on(started: { - started = true - }, next: { - value = $0 - }) - .startWithSignal { _, disposable in - expect(started) == false - expect(value).to(beNil()) - - disposable.dispose() - } - - expect(started) == false - expect(value).to(beNil()) - } - - it("should send interrupted if disposed before closure return") { - var interrupted = false - - SignalProducer(value: 42) - .startWithSignal { signal, disposable in - expect(interrupted) == false - - signal.observeInterrupted { - interrupted = true - } - - disposable.dispose() - } - - expect(interrupted) == true - } - - it("should dispose of added disposables upon completion") { - let addedDisposable = SimpleDisposable() - var observer: Signal.Observer! - - let producer = SignalProducer() { incomingObserver, disposable in - disposable += addedDisposable - observer = incomingObserver - } - - producer.startWithSignal { _ in } - expect(addedDisposable.isDisposed) == false - - observer.sendCompleted() - expect(addedDisposable.isDisposed) == true - } - - it("should dispose of added disposables upon error") { - let addedDisposable = SimpleDisposable() - var observer: Signal.Observer! - - let producer = SignalProducer() { incomingObserver, disposable in - disposable += addedDisposable - observer = incomingObserver - } - - producer.startWithSignal { _ in } - expect(addedDisposable.isDisposed) == false - - observer.sendFailed(.default) - expect(addedDisposable.isDisposed) == true - } - } - - describe("start") { - it("should immediately begin sending events") { - let producer = SignalProducer(values: [1, 2]) - - var values: [Int] = [] - var completed = false - producer.start { event in - switch event { - case let .next(value): - values.append(value) - case .completed: - completed = true - default: - break - } - } - - expect(values) == [1, 2] - expect(completed) == true - } - - it("should send interrupted if disposed") { - let producer = SignalProducer<(), NoError>.never - - var interrupted = false - let disposable = producer.startWithInterrupted { - interrupted = true - } - - expect(interrupted) == false - - disposable.dispose() - expect(interrupted) == true - } - - it("should release observer when disposed") { - weak var objectRetainedByObserver: NSObject? - var disposable: Disposable! - let test = { - let producer = SignalProducer.never - let object = NSObject() - objectRetainedByObserver = object - disposable = producer.startWithNext { _ in _ = object } - } - - test() - expect(objectRetainedByObserver).toNot(beNil()) - - disposable.dispose() - expect(objectRetainedByObserver).to(beNil()) - } - - describe("trailing closure") { - it("receives next values") { - let (producer, observer) = SignalProducer.pipe() - - var values = [Int]() - producer.startWithNext { next in - values.append(next) - } - - observer.sendNext(1) - observer.sendNext(2) - observer.sendNext(3) - - observer.sendCompleted() - - expect(values) == [1, 2, 3] - } - - it("receives results") { - let (producer, observer) = SignalProducer.pipe() - - var results: [Result] = [] - producer.startWithResult { results.append($0) } - - observer.sendNext(1) - observer.sendNext(2) - observer.sendNext(3) - observer.sendFailed(.default) - - observer.sendCompleted() - - expect(results).to(haveCount(4)) - expect(results[0].value) == 1 - expect(results[1].value) == 2 - expect(results[2].value) == 3 - expect(results[3].error) == .default - } - } - } - - describe("lift") { - describe("over unary operators") { - it("should invoke transformation once per started signal") { - let baseProducer = SignalProducer(values: [1, 2]) - - var counter = 0 - let transform = { (signal: Signal) -> Signal in - counter += 1 - return signal - } - - let producer = baseProducer.lift(transform) - expect(counter) == 0 - - producer.start() - expect(counter) == 1 - - producer.start() - expect(counter) == 2 - } - - it("should not miss any events") { - let baseProducer = SignalProducer(values: [1, 2, 3, 4]) - - let producer = baseProducer.lift { signal in - return signal.map { $0 * $0 } - } - let result = producer.collect().single() - - expect(result?.value) == [1, 4, 9, 16] - } - } - - describe("over binary operators") { - it("should invoke transformation once per started signal") { - let baseProducer = SignalProducer(values: [1, 2]) - let otherProducer = SignalProducer(values: [3, 4]) - - var counter = 0 - let transform = { (signal: Signal) -> (Signal) -> Signal<(Int, Int), NoError> in - return { otherSignal in - counter += 1 - return Signal.zip(signal, otherSignal) - } - } - - let producer = baseProducer.lift(transform)(otherProducer) - expect(counter) == 0 - - producer.start() - expect(counter) == 1 - - producer.start() - expect(counter) == 2 - } - - it("should not miss any events") { - let baseProducer = SignalProducer(values: [1, 2, 3]) - let otherProducer = SignalProducer(values: [4, 5, 6]) - - let transform = { (signal: Signal) -> (Signal) -> Signal in - return { otherSignal in - return Signal.zip(signal, otherSignal).map { first, second in first + second } - } - } - - let producer = baseProducer.lift(transform)(otherProducer) - let result = producer.collect().single() - - expect(result?.value) == [5, 7, 9] - } - } - - describe("over binary operators with signal") { - it("should invoke transformation once per started signal") { - let baseProducer = SignalProducer(values: [1, 2]) - let (otherSignal, otherSignalObserver) = Signal.pipe() - - var counter = 0 - let transform = { (signal: Signal) -> (Signal) -> Signal<(Int, Int), NoError> in - return { otherSignal in - counter += 1 - return Signal.zip(signal, otherSignal) - } - } - - let producer = baseProducer.lift(transform)(otherSignal) - expect(counter) == 0 - - producer.start() - otherSignalObserver.sendNext(1) - expect(counter) == 1 - - producer.start() - otherSignalObserver.sendNext(2) - expect(counter) == 2 - } - - it("should not miss any events") { - let baseProducer = SignalProducer(values: [ 1, 2, 3 ]) - let (otherSignal, otherSignalObserver) = Signal.pipe() - - let transform = { (signal: Signal) -> (Signal) -> Signal in - return { otherSignal in - return Signal.zip(signal, otherSignal).map(+) - } - } - - let producer = baseProducer.lift(transform)(otherSignal) - var result: [Int] = [] - var completed: Bool = false - - producer.start { event in - switch event { - case .next(let value): result.append(value) - case .completed: completed = true - default: break - } - } - - otherSignalObserver.sendNext(4) - expect(result) == [ 5 ] - - otherSignalObserver.sendNext(5) - expect(result) == [ 5, 7 ] - - otherSignalObserver.sendNext(6) - expect(result) == [ 5, 7, 9 ] - expect(completed) == true - } - } - } - - describe("combineLatest") { - it("should combine the events to one array") { - let (producerA, observerA) = SignalProducer.pipe() - let (producerB, observerB) = SignalProducer.pipe() - - let producer = SignalProducer.combineLatest([producerA, producerB]) - - var values = [[Int]]() - producer.startWithNext { next in - values.append(next) - } - - observerA.sendNext(1) - observerB.sendNext(2) - observerA.sendNext(3) - observerA.sendCompleted() - observerB.sendCompleted() - - expect(values) == [[1, 2], [3, 2]] - } - - it("should start signal producers in order as defined") { - var ids = [Int]() - let createProducer = { (id: Int) -> SignalProducer in - return SignalProducer { observer, disposable in - ids.append(id) - - observer.sendNext(id) - observer.sendCompleted() - } - } - - let producerA = createProducer(1) - let producerB = createProducer(2) - - let producer = SignalProducer.combineLatest([producerA, producerB]) - - var values = [[Int]]() - producer.startWithNext { next in - values.append(next) - } - - expect(ids) == [1, 2] - expect(values) == [[1, 2]] - } - } - - describe("zip") { - it("should zip the events to one array") { - let producerA = SignalProducer(values: [ 1, 2 ]) - let producerB = SignalProducer(values: [ 3, 4 ]) - - let producer = SignalProducer.zip([producerA, producerB]) - let result = producer.collect().single() - - expect(result?.value) == [[1, 3], [2, 4]] - } - - it("should start signal producers in order as defined") { - var ids = [Int]() - let createProducer = { (id: Int) -> SignalProducer in - return SignalProducer { observer, disposable in - ids.append(id) - - observer.sendNext(id) - observer.sendCompleted() - } - } - - let producerA = createProducer(1) - let producerB = createProducer(2) - - let producer = SignalProducer.zip([producerA, producerB]) - - var values = [[Int]]() - producer.startWithNext { next in - values.append(next) - } - - expect(ids) == [1, 2] - expect(values) == [[1, 2]] - } - } - - describe("timer") { - it("should send the current date at the given interval") { - let scheduler = TestScheduler() - let producer = timer(interval: 1, on: scheduler, leeway: 0) - - let startDate = scheduler.currentDate - let tick1 = startDate.addingTimeInterval(1) - let tick2 = startDate.addingTimeInterval(2) - let tick3 = startDate.addingTimeInterval(3) - - var dates: [NSDate] = [] - producer.startWithNext { dates.append($0) } - - scheduler.advance(by: 0.9) - expect(dates) == [] - - scheduler.advance(by: 1) - expect(dates) == [tick1] - - scheduler.advance() - expect(dates) == [tick1] - - scheduler.advance(by: 0.2) - expect(dates) == [tick1, tick2] - - scheduler.advance(by: 1) - expect(dates) == [tick1, tick2, tick3] - } - - it("should release the signal when disposed") { - let scheduler = TestScheduler() - let producer = timer(interval: 1, on: scheduler, leeway: 0) - var interrupted = false - - weak var weakSignal: Signal? - producer.startWithSignal { signal, disposable in - weakSignal = signal - scheduler.schedule { - disposable.dispose() - } - signal.observeInterrupted { interrupted = true } - } - - expect(weakSignal).toNot(beNil()) - expect(interrupted) == false - - scheduler.run() - expect(weakSignal).to(beNil()) - expect(interrupted) == true - } - } - - describe("on") { - it("should attach event handlers to each started signal") { - let (baseProducer, observer) = SignalProducer.pipe() - - var starting = 0 - var started = 0 - var event = 0 - var next = 0 - var completed = 0 - var terminated = 0 - - let producer = baseProducer - .on(starting: { - starting += 1 - }, started: { - started += 1 - }, event: { e in - event += 1 - }, next: { n in - next += 1 - }, completed: { - completed += 1 - }, terminated: { - terminated += 1 - }) - - producer.start() - expect(starting) == 1 - expect(started) == 1 - - producer.start() - expect(starting) == 2 - expect(started) == 2 - - observer.sendNext(1) - expect(event) == 2 - expect(next) == 2 - - observer.sendCompleted() - expect(event) == 4 - expect(completed) == 2 - expect(terminated) == 2 - } - - it("should attach event handlers for disposal") { - let (baseProducer, _) = SignalProducer.pipe() - - var disposed: Bool = false - - let producer = baseProducer - .on(disposed: { disposed = true }) - - let disposable = producer.start() - - expect(disposed) == false - disposable.dispose() - expect(disposed) == true - } - - it("should invoke the `started` action of the inner producer first") { - let (baseProducer, _) = SignalProducer.pipe() - - var numbers = [Int]() - - _ = baseProducer - .on(started: { numbers.append(1) }) - .on(started: { numbers.append(2) }) - .on(started: { numbers.append(3) }) - .start() - - expect(numbers) == [1, 2, 3] - } - - it("should invoke the `starting` action of the outer producer first") { - let (baseProducer, _) = SignalProducer.pipe() - - var numbers = [Int]() - - _ = baseProducer - .on(starting: { numbers.append(1) }) - .on(starting: { numbers.append(2) }) - .on(starting: { numbers.append(3) }) - .start() - - expect(numbers) == [3, 2, 1] - } - } - - describe("startOn") { - it("should invoke effects on the given scheduler") { - let scheduler = TestScheduler() - var invoked = false - - let producer = SignalProducer() { _ in - invoked = true - } - - producer.start(on: scheduler).start() - expect(invoked) == false - - scheduler.advance() - expect(invoked) == true - } - - it("should forward events on their original scheduler") { - let startScheduler = TestScheduler() - let testScheduler = TestScheduler() - - let producer = timer(interval: 2, on: testScheduler, leeway: 0) - - var next: NSDate? - producer.start(on: startScheduler).startWithNext { next = $0 } - - startScheduler.advance(by: 2) - expect(next).to(beNil()) - - testScheduler.advance(by: 1) - expect(next).to(beNil()) - - testScheduler.advance(by: 1) - expect(next) == testScheduler.currentDate - } - } - - describe("flatMapError") { - it("should invoke the handler and start new producer for an error") { - let (baseProducer, baseObserver) = SignalProducer.pipe() - - var values: [Int] = [] - var completed = false - - baseProducer - .flatMapError { (error: TestError) -> SignalProducer in - expect(error) == TestError.default - expect(values) == [1] - - return .init(value: 2) - } - .start { event in - switch event { - case let .next(value): - values.append(value) - case .completed: - completed = true - default: - break - } - } - - baseObserver.sendNext(1) - baseObserver.sendFailed(.default) - - expect(values) == [1, 2] - expect(completed) == true - } - - it("should interrupt the replaced producer on disposal") { - let (baseProducer, baseObserver) = SignalProducer.pipe() - - var (disposed, interrupted) = (false, false) - let disposable = baseProducer - .flatMapError { (error: TestError) -> SignalProducer in - return SignalProducer { _, disposable in - disposable += ActionDisposable { disposed = true } - } - } - .startWithInterrupted { interrupted = true } - - baseObserver.sendFailed(.default) - disposable.dispose() - - expect(interrupted) == true - expect(disposed) == true - } - } - - describe("flatten") { - describe("FlattenStrategy.concat") { - describe("sequencing") { - var completePrevious: (() -> Void)! - var sendSubsequent: (() -> Void)! - var completeOuter: (() -> Void)! - - var subsequentStarted = false - - beforeEach { - let (outerProducer, outerObserver) = SignalProducer, NoError>.pipe() - let (previousProducer, previousObserver) = SignalProducer.pipe() - - subsequentStarted = false - let subsequentProducer = SignalProducer { _ in - subsequentStarted = true - } - - completePrevious = { previousObserver.sendCompleted() } - sendSubsequent = { outerObserver.sendNext(subsequentProducer) } - completeOuter = { outerObserver.sendCompleted() } - - outerProducer.flatten(.concat).start() - outerObserver.sendNext(previousProducer) - } - - it("should immediately start subsequent inner producer if previous inner producer has already completed") { - completePrevious() - sendSubsequent() - expect(subsequentStarted) == true - } - - context("with queued producers") { - beforeEach { - // Place the subsequent producer into `concat`'s queue. - sendSubsequent() - expect(subsequentStarted) == false - } - - it("should start subsequent inner producer upon completion of previous inner producer") { - completePrevious() - expect(subsequentStarted) == true - } - - it("should start subsequent inner producer upon completion of previous inner producer and completion of outer producer") { - completeOuter() - completePrevious() - expect(subsequentStarted) == true - } - } - } - - it("should forward an error from an inner producer") { - let errorProducer = SignalProducer(error: TestError.default) - let outerProducer = SignalProducer, TestError>(value: errorProducer) - - var error: TestError? - (outerProducer.flatten(.concat)).startWithFailed { e in - error = e - } - - expect(error) == TestError.default - } - - it("should forward an error from the outer producer") { - let (outerProducer, outerObserver) = SignalProducer, TestError>.pipe() - - var error: TestError? - outerProducer.flatten(.concat).startWithFailed { e in - error = e - } - - outerObserver.sendFailed(TestError.default) - expect(error) == TestError.default - } - - describe("completion") { - var completeOuter: (() -> Void)! - var completeInner: (() -> Void)! - - var completed = false - - beforeEach { - let (outerProducer, outerObserver) = SignalProducer, NoError>.pipe() - let (innerProducer, innerObserver) = SignalProducer.pipe() - - completeOuter = { outerObserver.sendCompleted() } - completeInner = { innerObserver.sendCompleted() } - - completed = false - outerProducer.flatten(.concat).startWithCompleted { - completed = true - } - - outerObserver.sendNext(innerProducer) - } - - it("should complete when inner producers complete, then outer producer completes") { - completeInner() - expect(completed) == false - - completeOuter() - expect(completed) == true - } - - it("should complete when outer producers completes, then inner producers complete") { - completeOuter() - expect(completed) == false - - completeInner() - expect(completed) == true - } - } - } - - describe("FlattenStrategy.merge") { - describe("behavior") { - var completeA: (() -> Void)! - var sendA: (() -> Void)! - var completeB: (() -> Void)! - var sendB: (() -> Void)! - - var outerCompleted = false - - var recv = [Int]() - - beforeEach { - let (outerProducer, outerObserver) = SignalProducer, NoError>.pipe() - let (producerA, observerA) = SignalProducer.pipe() - let (producerB, observerB) = SignalProducer.pipe() - - completeA = { observerA.sendCompleted() } - completeB = { observerB.sendCompleted() } - - var a = 0 - sendA = { observerA.sendNext(a); a += 1 } - - var b = 100 - sendB = { observerB.sendNext(b); b += 1 } - - outerProducer.flatten(.merge).start { event in - switch event { - case let .next(i): - recv.append(i) - case .completed: - outerCompleted = true - default: - break - } - } - - outerObserver.sendNext(producerA) - outerObserver.sendNext(producerB) - - outerObserver.sendCompleted() - } - - it("should forward values from any inner signals") { - sendA() - sendA() - sendB() - sendA() - sendB() - expect(recv) == [0, 1, 100, 2, 101] - } - - it("should complete when all signals have completed") { - completeA() - expect(outerCompleted) == false - completeB() - expect(outerCompleted) == true - } - } - - describe("error handling") { - it("should forward an error from an inner signal") { - let errorProducer = SignalProducer(error: TestError.default) - let outerProducer = SignalProducer, TestError>(value: errorProducer) - - var error: TestError? - outerProducer.flatten(.merge).startWithFailed { e in - error = e - } - expect(error) == TestError.default - } - - it("should forward an error from the outer signal") { - let (outerProducer, outerObserver) = SignalProducer, TestError>.pipe() - - var error: TestError? - outerProducer.flatten(.merge).startWithFailed { e in - error = e - } - - outerObserver.sendFailed(TestError.default) - expect(error) == TestError.default - } - } - } - - describe("FlattenStrategy.latest") { - it("should forward values from the latest inner signal") { - let (outer, outerObserver) = SignalProducer, TestError>.pipe() - let (firstInner, firstInnerObserver) = SignalProducer.pipe() - let (secondInner, secondInnerObserver) = SignalProducer.pipe() - - var receivedValues: [Int] = [] - var errored = false - var completed = false - - outer.flatten(.latest).start { event in - switch event { - case let .next(value): - receivedValues.append(value) - case .completed: - completed = true - case .failed: - errored = true - case .interrupted: - break - } - } - - outerObserver.sendNext(SignalProducer(value: 0)) - outerObserver.sendNext(firstInner) - firstInnerObserver.sendNext(1) - outerObserver.sendNext(secondInner) - secondInnerObserver.sendNext(2) - outerObserver.sendCompleted() - - expect(receivedValues) == [ 0, 1, 2 ] - expect(errored) == false - expect(completed) == false - - firstInnerObserver.sendNext(3) - firstInnerObserver.sendCompleted() - secondInnerObserver.sendNext(4) - secondInnerObserver.sendCompleted() - - expect(receivedValues) == [ 0, 1, 2, 4 ] - expect(errored) == false - expect(completed) == true - } - - it("should forward an error from an inner signal") { - let inner = SignalProducer(error: .default) - let outer = SignalProducer, TestError>(value: inner) - - let result = outer.flatten(.latest).first() - expect(result?.error) == TestError.default - } - - it("should forward an error from the outer signal") { - let outer = SignalProducer, TestError>(error: .default) - - let result = outer.flatten(.latest).first() - expect(result?.error) == TestError.default - } - - it("should complete when the original and latest signals have completed") { - let inner = SignalProducer.empty - let outer = SignalProducer, TestError>(value: inner) - - var completed = false - outer.flatten(.latest).startWithCompleted { - completed = true - } - - expect(completed) == true - } - - it("should complete when the outer signal completes before sending any signals") { - let outer = SignalProducer, TestError>.empty - - var completed = false - outer.flatten(.latest).startWithCompleted { - completed = true - } - - expect(completed) == true - } - - it("should not deadlock") { - let producer = SignalProducer(value: 1) - .flatMap(.latest) { _ in SignalProducer(value: 10) } - - let result = producer.take(first: 1).last() - expect(result?.value) == 10 - } - } - - describe("interruption") { - var innerObserver: Signal<(), NoError>.Observer! - var outerObserver: Signal, NoError>.Observer! - var execute: ((FlattenStrategy) -> Void)! - - var interrupted = false - var completed = false - - beforeEach { - let (innerProducer, incomingInnerObserver) = SignalProducer<(), NoError>.pipe() - let (outerProducer, incomingOuterObserver) = SignalProducer, NoError>.pipe() - - innerObserver = incomingInnerObserver - outerObserver = incomingOuterObserver - - execute = { strategy in - interrupted = false - completed = false - - outerProducer - .flatten(strategy) - .start { event in - switch event { - case .interrupted: - interrupted = true - case .completed: - completed = true - default: - break - } - } - } - - incomingOuterObserver.sendNext(innerProducer) - } - - describe("Concat") { - it("should drop interrupted from an inner producer") { - execute(.concat) - - innerObserver.sendInterrupted() - expect(interrupted) == false - expect(completed) == false - - outerObserver.sendCompleted() - expect(completed) == true - } - - it("should forward interrupted from the outer producer") { - execute(.concat) - outerObserver.sendInterrupted() - expect(interrupted) == true - } - } - - describe("Latest") { - it("should drop interrupted from an inner producer") { - execute(.latest) - - innerObserver.sendInterrupted() - expect(interrupted) == false - expect(completed) == false - - outerObserver.sendCompleted() - expect(completed) == true - } - - it("should forward interrupted from the outer producer") { - execute(.latest) - outerObserver.sendInterrupted() - expect(interrupted) == true - } - } - - describe("Merge") { - it("should drop interrupted from an inner producer") { - execute(.merge) - - innerObserver.sendInterrupted() - expect(interrupted) == false - expect(completed) == false - - outerObserver.sendCompleted() - expect(completed) == true - } - - it("should forward interrupted from the outer producer") { - execute(.merge) - outerObserver.sendInterrupted() - expect(interrupted) == true - } - } - } - - describe("disposal") { - var completeOuter: (() -> Void)! - var disposeOuter: (() -> Void)! - var execute: ((FlattenStrategy) -> Void)! - - var innerDisposable = SimpleDisposable() - var interrupted = false - - beforeEach { - execute = { strategy in - let (outerProducer, outerObserver) = SignalProducer, NoError>.pipe() - - innerDisposable = SimpleDisposable() - let innerProducer = SignalProducer { $1.add(innerDisposable) } - - interrupted = false - let outerDisposable = outerProducer.flatten(strategy).startWithInterrupted { - interrupted = true - } - - completeOuter = outerObserver.sendCompleted - disposeOuter = outerDisposable.dispose - - outerObserver.sendNext(innerProducer) - } - } - - describe("Concat") { - it("should cancel inner work when disposed before the outer producer completes") { - execute(.concat) - - expect(innerDisposable.isDisposed) == false - expect(interrupted) == false - disposeOuter() - - expect(innerDisposable.isDisposed) == true - expect(interrupted) == true - } - - it("should cancel inner work when disposed after the outer producer completes") { - execute(.concat) - - completeOuter() - - expect(innerDisposable.isDisposed) == false - expect(interrupted) == false - disposeOuter() - - expect(innerDisposable.isDisposed) == true - expect(interrupted) == true - } - } - - describe("Latest") { - it("should cancel inner work when disposed before the outer producer completes") { - execute(.latest) - - expect(innerDisposable.isDisposed) == false - expect(interrupted) == false - disposeOuter() - - expect(innerDisposable.isDisposed) == true - expect(interrupted) == true - } - - it("should cancel inner work when disposed after the outer producer completes") { - execute(.latest) - - completeOuter() - - expect(innerDisposable.isDisposed) == false - expect(interrupted) == false - disposeOuter() - - expect(innerDisposable.isDisposed) == true - expect(interrupted) == true - } - } - - describe("Merge") { - it("should cancel inner work when disposed before the outer producer completes") { - execute(.merge) - - expect(innerDisposable.isDisposed) == false - expect(interrupted) == false - disposeOuter() - - expect(innerDisposable.isDisposed) == true - expect(interrupted) == true - } - - it("should cancel inner work when disposed after the outer producer completes") { - execute(.merge) - - completeOuter() - - expect(innerDisposable.isDisposed) == false - expect(interrupted) == false - disposeOuter() - - expect(innerDisposable.isDisposed) == true - expect(interrupted) == true - } - } - } - } - - describe("times") { - it("should start a signal N times upon completion") { - let original = SignalProducer(values: [ 1, 2, 3 ]) - let producer = original.times(3) - - let result = producer.collect().single() - expect(result?.value) == [ 1, 2, 3, 1, 2, 3, 1, 2, 3 ] - } - - it("should produce an equivalent signal producer if count is 1") { - let original = SignalProducer(value: 1) - let producer = original.times(1) - - let result = producer.collect().single() - expect(result?.value) == [ 1 ] - } - - it("should produce an empty signal if count is 0") { - let original = SignalProducer(value: 1) - let producer = original.times(0) - - let result = producer.first() - expect(result).to(beNil()) - } - - it("should not repeat upon error") { - let results: [Result] = [ - .success(1), - .success(2), - .failure(.default) - ] - - let original = SignalProducer.attemptWithResults(results) - let producer = original.times(3) - - let events = producer - .materialize() - .collect() - .single() - let result = events?.value - - let expectedEvents: [Event] = [ - .next(1), - .next(2), - .failed(.default) - ] - - // TODO: if let result = result where result.count == expectedEvents.count - if result?.count != expectedEvents.count { - fail("Invalid result: \(result)") - } else { - // Can't test for equality because Array is not Equatable, - // and neither is Event. - expect(result![0] == expectedEvents[0]) == true - expect(result![1] == expectedEvents[1]) == true - expect(result![2] == expectedEvents[2]) == true - } - } - - it("should evaluate lazily") { - let original = SignalProducer(value: 1) - let producer = original.times(Int.max) - - let result = producer.take(first: 1).single() - expect(result?.value) == 1 - } - } - - describe("retry") { - it("should start a signal N times upon error") { - let results: [Result] = [ - .failure(.error1), - .failure(.error2), - .success(1) - ] - - let original = SignalProducer.attemptWithResults(results) - let producer = original.retry(upTo: 2) - - let result = producer.single() - - expect(result?.value) == 1 - } - - it("should forward errors that occur after all retries") { - let results: [Result] = [ - .failure(.default), - .failure(.error1), - .failure(.error2), - ] - - let original = SignalProducer.attemptWithResults(results) - let producer = original.retry(upTo: 2) - - let result = producer.single() - - expect(result?.error) == TestError.error2 - } - - it("should not retry upon completion") { - let results: [Result] = [ - .success(1), - .success(2), - .success(3) - ] - - let original = SignalProducer.attemptWithResults(results) - let producer = original.retry(upTo: 2) - - let result = producer.single() - expect(result?.value) == 1 - } - } - - describe("then") { - it("should start the subsequent producer after the completion of the original") { - let (original, observer) = SignalProducer.pipe() - - var subsequentStarted = false - let subsequent = SignalProducer { observer, _ in - subsequentStarted = true - } - - let producer = original.then(subsequent) - producer.start() - expect(subsequentStarted) == false - - observer.sendCompleted() - expect(subsequentStarted) == true - } - - it("should forward errors from the original producer") { - let original = SignalProducer(error: .default) - let subsequent = SignalProducer.empty - - let result = original.then(subsequent).first() - expect(result?.error) == TestError.default - } - - it("should forward errors from the subsequent producer") { - let original = SignalProducer.empty - let subsequent = SignalProducer(error: .default) - - let result = original.then(subsequent).first() - expect(result?.error) == TestError.default - } - - it("should forward interruptions from the original producer") { - let (original, observer) = SignalProducer.pipe() - - var subsequentStarted = false - let subsequent = SignalProducer { observer, _ in - subsequentStarted = true - } - - var interrupted = false - let producer = original.then(subsequent) - producer.startWithInterrupted { - interrupted = true - } - expect(subsequentStarted) == false - - observer.sendInterrupted() - expect(interrupted) == true - } - - it("should complete when both inputs have completed") { - let (original, originalObserver) = SignalProducer.pipe() - let (subsequent, subsequentObserver) = SignalProducer.pipe() - - let producer = original.then(subsequent) - - var completed = false - producer.startWithCompleted { - completed = true - } - - originalObserver.sendCompleted() - expect(completed) == false - - subsequentObserver.sendCompleted() - expect(completed) == true - } - } - - describe("first") { - it("should start a signal then block on the first value") { - let (_signal, observer) = Signal.pipe() - - let forwardingScheduler: QueueScheduler - - if #available(OSX 10.10, *) { - forwardingScheduler = QueueScheduler(qos: .default, name: "\(#file):\(#line)") - } else { - forwardingScheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) - } - - let producer = SignalProducer(signal: _signal.delay(0.1, on: forwardingScheduler)) - - let observingScheduler: QueueScheduler - - if #available(OSX 10.10, *) { - observingScheduler = QueueScheduler(qos: .default, name: "\(#file):\(#line)") - } else { - observingScheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) - } - - var result: Int? - - observingScheduler.schedule { - result = producer.first()?.value - } - - expect(result).to(beNil()) - - observer.sendNext(1) - expect(result).toEventually(be(1), timeout: 5.0) - } - - it("should return a nil result if no values are sent before completion") { - let result = SignalProducer.empty.first() - expect(result).to(beNil()) - } - - it("should return the first value if more than one value is sent") { - let result = SignalProducer(values: [ 1, 2 ]).first() - expect(result?.value) == 1 - } - - it("should return an error if one occurs before the first value") { - let result = SignalProducer(error: .default).first() - expect(result?.error) == TestError.default - } - } - - describe("single") { - it("should start a signal then block until completion") { - let (_signal, observer) = Signal.pipe() - let forwardingScheduler: QueueScheduler - - if #available(OSX 10.10, *) { - forwardingScheduler = QueueScheduler(qos: .default, name: "\(#file):\(#line)") - } else { - forwardingScheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) - } - - let producer = SignalProducer(signal: _signal.delay(0.1, on: forwardingScheduler)) - - let observingScheduler: QueueScheduler - - if #available(OSX 10.10, *) { - observingScheduler = QueueScheduler(qos: .default, name: "\(#file):\(#line)") - } else { - observingScheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) - } - - var result: Int? - - observingScheduler.schedule { - result = producer.single()?.value - } - expect(result).to(beNil()) - - observer.sendNext(1) - - Thread.sleep(forTimeInterval: 3.0) - expect(result).to(beNil()) - - observer.sendCompleted() - expect(result).toEventually(be(1)) - } - - it("should return a nil result if no values are sent before completion") { - let result = SignalProducer.empty.single() - expect(result).to(beNil()) - } - - it("should return a nil result if more than one value is sent before completion") { - let result = SignalProducer(values: [ 1, 2 ]).single() - expect(result).to(beNil()) - } - - it("should return an error if one occurs") { - let result = SignalProducer(error: .default).single() - expect(result?.error) == TestError.default - } - } - - describe("last") { - it("should start a signal then block until completion") { - let (_signal, observer) = Signal.pipe() - let scheduler: QueueScheduler - - if #available(*, OSX 10.10) { - scheduler = QueueScheduler(name: "\(#file):\(#line)") - } else { - scheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) - } - let producer = SignalProducer(signal: _signal.delay(0.1, on: scheduler)) - - var result: Result? - - let group = DispatchGroup() - - let globalQueue: DispatchQueue - if #available(*, OSX 10.10) { - globalQueue = DispatchQueue.global() - } else { - globalQueue = DispatchQueue.global(priority: .default) - } - - globalQueue.async(group: group, flags: []) { - result = producer.last() - } - expect(result).to(beNil()) - - observer.sendNext(1) - observer.sendNext(2) - expect(result).to(beNil()) - - observer.sendCompleted() - group.wait() - - expect(result?.value) == 2 - } - - it("should return a nil result if no values are sent before completion") { - let result = SignalProducer.empty.last() - expect(result).to(beNil()) - } - - it("should return the last value if more than one value is sent") { - let result = SignalProducer(values: [ 1, 2 ]).last() - expect(result?.value) == 2 - } - - it("should return an error if one occurs") { - let result = SignalProducer(error: .default).last() - expect(result?.error) == TestError.default - } - } - - describe("wait") { - it("should start a signal then block until completion") { - let (_signal, observer) = Signal.pipe() - let scheduler: QueueScheduler - if #available(*, OSX 10.10) { - scheduler = QueueScheduler(name: "\(#file):\(#line)") - } else { - scheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) - } - let producer = SignalProducer(signal: _signal.delay(0.1, on: scheduler)) - - var result: Result<(), NoError>? - - let group = DispatchGroup() - - let globalQueue: DispatchQueue - if #available(*, OSX 10.10) { - globalQueue = DispatchQueue.global() - } else { - globalQueue = DispatchQueue.global(priority: .default) - } - - globalQueue.async(group: group, flags: []) { - result = producer.wait() - } - - expect(result).to(beNil()) - - observer.sendCompleted() - group.wait() - - expect(result?.value).toNot(beNil()) - } - - it("should return an error if one occurs") { - let result = SignalProducer(error: .default).wait() - expect(result.error) == TestError.default - } - } - - describe("observeOn") { - it("should immediately cancel upstream producer's work when disposed") { - var upstreamDisposable: Disposable! - let producer = SignalProducer<(), NoError>{ _, innerDisposable in - upstreamDisposable = innerDisposable - } - - var downstreamDisposable: Disposable! - producer - .observe(on: TestScheduler()) - .startWithSignal { signal, innerDisposable in - downstreamDisposable = innerDisposable - } - - expect(upstreamDisposable.isDisposed) == false - - downstreamDisposable.dispose() - expect(upstreamDisposable.isDisposed) == true - } - } - - describe("take") { - it("Should not start concat'ed producer if the first one sends a value when using take(1)") { - let scheduler: QueueScheduler - if #available(OSX 10.10, *) { - scheduler = QueueScheduler(name: "\(#file):\(#line)") - } else { - scheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) - } - - // Delaying producer1 from sending a value to test whether producer2 is started in the mean-time. - let producer1 = SignalProducer() { handler, _ in - handler.sendNext(1) - handler.sendCompleted() - }.start(on: scheduler) - - var started = false - let producer2 = SignalProducer() { handler, _ in - started = true - handler.sendNext(2) - handler.sendCompleted() - } - - let result = producer1.concat(producer2).take(first: 1).collect().first() - - expect(result?.value) == [1] - expect(started) == false - } - } - - describe("replayLazily") { - var producer: SignalProducer! - var observer: SignalProducer.ProducedSignal.Observer! - - var replayedProducer: SignalProducer! - - beforeEach { - let (producerTemp, observerTemp) = SignalProducer.pipe() - producer = producerTemp - observer = observerTemp - - replayedProducer = producer.replayLazily(upTo: 2) - } - - context("subscribing to underlying producer") { - it("emits new values") { - var last: Int? - - replayedProducer - .assumeNoErrors() - .startWithNext { last = $0 } - - expect(last).to(beNil()) - - observer.sendNext(1) - expect(last) == 1 - - observer.sendNext(2) - expect(last) == 2 - } - - it("emits errors") { - var error: TestError? - - replayedProducer.startWithFailed { error = $0 } - expect(error).to(beNil()) - - observer.sendFailed(.default) - expect(error) == TestError.default - } - } - - context("buffers past values") { - it("emits last value upon subscription") { - let disposable = replayedProducer - .start() - - observer.sendNext(1) - disposable.dispose() - - var last: Int? - - replayedProducer - .assumeNoErrors() - .startWithNext { last = $0 } - expect(last) == 1 - } - - it("emits previous failure upon subscription") { - let disposable = replayedProducer - .start() - - observer.sendFailed(.default) - disposable.dispose() - - var error: TestError? - - replayedProducer - .startWithFailed { error = $0 } - expect(error) == TestError.default - } - - it("emits last n values upon subscription") { - var disposable = replayedProducer - .start() - - observer.sendNext(1) - observer.sendNext(2) - observer.sendNext(3) - observer.sendNext(4) - disposable.dispose() - - var values: [Int] = [] - - disposable = replayedProducer - .assumeNoErrors() - .startWithNext { values.append($0) } - expect(values) == [ 3, 4 ] - - observer.sendNext(5) - expect(values) == [ 3, 4, 5 ] - - disposable.dispose() - values = [] - - replayedProducer - .assumeNoErrors() - .startWithNext { values.append($0) } - expect(values) == [ 4, 5 ] - } - } - - context("starting underying producer") { - it("starts lazily") { - var started = false - - let producer = SignalProducer(value: 0) - .on(started: { started = true }) - expect(started) == false - - let replayedProducer = producer - .replayLazily(upTo: 1) - expect(started) == false - - replayedProducer.start() - expect(started) == true - } - - it("shares a single subscription") { - var startedTimes = 0 - - let producer = SignalProducer.never - .on(started: { startedTimes += 1 }) - expect(startedTimes) == 0 - - let replayedProducer = producer - .replayLazily(upTo: 1) - expect(startedTimes) == 0 - - replayedProducer.start() - expect(startedTimes) == 1 - - replayedProducer.start() - expect(startedTimes) == 1 - } - - it("does not start multiple times when subscribing multiple times") { - var startedTimes = 0 - - let producer = SignalProducer(value: 0) - .on(started: { startedTimes += 1 }) - - let replayedProducer = producer - .replayLazily(upTo: 1) - - expect(startedTimes) == 0 - replayedProducer.start().dispose() - expect(startedTimes) == 1 - replayedProducer.start().dispose() - expect(startedTimes) == 1 - } - - it("does not start again if it finished") { - var startedTimes = 0 - - let producer = SignalProducer.empty - .on(started: { startedTimes += 1 }) - expect(startedTimes) == 0 - - let replayedProducer = producer - .replayLazily(upTo: 1) - expect(startedTimes) == 0 - - replayedProducer.start() - expect(startedTimes) == 1 - - replayedProducer.start() - expect(startedTimes) == 1 - } - } - - context("lifetime") { - it("does not dispose underlying subscription if the replayed producer is still in memory") { - var disposed = false - - let producer = SignalProducer.never - .on(disposed: { disposed = true }) - - let replayedProducer = producer - .replayLazily(upTo: 1) - - expect(disposed) == false - let disposable = replayedProducer.start() - expect(disposed) == false - - disposable.dispose() - expect(disposed) == false - } - - it("does not dispose if it has active subscriptions") { - var disposed = false - - let producer = SignalProducer.never - .on(disposed: { disposed = true }) - - var replayedProducer = ImplicitlyUnwrappedOptional(producer.replayLazily(upTo: 1)) - - expect(disposed) == false - let disposable1 = replayedProducer?.start() - let disposable2 = replayedProducer?.start() - expect(disposed) == false - - replayedProducer = nil - expect(disposed) == false - - disposable1?.dispose() - expect(disposed) == false - - disposable2?.dispose() - expect(disposed) == true - } - - it("disposes underlying producer when the producer is deallocated") { - var disposed = false - - let producer = SignalProducer.never - .on(disposed: { disposed = true }) - - var replayedProducer = ImplicitlyUnwrappedOptional(producer.replayLazily(upTo: 1)) - - expect(disposed) == false - let disposable = replayedProducer?.start() - expect(disposed) == false - - disposable?.dispose() - expect(disposed) == false - - replayedProducer = nil - expect(disposed) == true - } - - it("does not leak buffered values") { - final class Value { - private let deinitBlock: () -> Void - - init(deinitBlock: () -> Void) { - self.deinitBlock = deinitBlock - } - - deinit { - self.deinitBlock() - } - } - - var deinitValues = 0 - - var producer: SignalProducer! = SignalProducer(value: Value { - deinitValues += 1 - }) - expect(deinitValues) == 0 - - var replayedProducer: SignalProducer! = producer - .replayLazily(upTo: 1) - - let disposable = replayedProducer - .start() - - disposable.dispose() - expect(deinitValues) == 0 - - producer = nil - expect(deinitValues) == 0 - - replayedProducer = nil - expect(deinitValues) == 1 - } - } - - describe("log events") { - it("should output the correct event") { - let expectations: [(String) -> Void] = [ - { event in expect(event) == "[] started" }, - { event in expect(event) == "[] next 1" }, - { event in expect(event) == "[] completed" }, - { event in expect(event) == "[] terminated" }, - { event in expect(event) == "[] disposed" } - ] - - let logger = TestLogger(expectations: expectations) - - let (producer, observer) = SignalProducer.pipe() - producer - .logEvents(logger: logger.logEvent) - .start() - - observer.sendNext(1) - observer.sendCompleted() - } - } - - describe("init(values) ambiguity") { - it("should not be a SignalProducer, NoError>") { - - let producer1: SignalProducer = SignalProducer.empty - let producer2: SignalProducer = SignalProducer.empty - - // This expression verifies at compile time that the type is as expected. - let _: SignalProducer = SignalProducer(values: [producer1, producer2]) - .flatten(.merge) - } - } - } - } -} - -// MARK: - Helpers - -extension SignalProducer { - internal static func pipe() -> (SignalProducer, ProducedSignal.Observer) { - let (signal, observer) = ProducedSignal.pipe() - let producer = SignalProducer(signal: signal) - return (producer, observer) - } - - /// Creates a producer that can be started as many times as elements in `results`. - /// Each signal will immediately send either a value or an error. - private static func attemptWithResults, C.IndexDistance == C.Index, C.Index == Int>(_ results: C) -> SignalProducer { - let resultCount = results.count - var operationIndex = 0 - - precondition(resultCount > 0) - - let operation: () -> Result = { - if operationIndex < resultCount { - defer { - operationIndex += 1 - } - - return results[results.index(results.startIndex, offsetBy: operationIndex)] - } else { - fail("Operation started too many times") - - return results[results.startIndex] - } - } - - return SignalProducer.attempt(operation) - } -} diff --git a/ReactiveCocoaTests/Swift/SignalSpec.swift b/ReactiveCocoaTests/Swift/SignalSpec.swift deleted file mode 100755 index 38f710eda9..0000000000 --- a/ReactiveCocoaTests/Swift/SignalSpec.swift +++ /dev/null @@ -1,2269 +0,0 @@ -// -// SignalSpec.swift -// ReactiveCocoa -// -// Created by Justin Spahr-Summers on 2015-01-23. -// Copyright (c) 2015 GitHub. All rights reserved. -// - -import Result -import Nimble -import Quick -import ReactiveCocoa - -class SignalSpec: QuickSpec { - override func spec() { - describe("init") { - var testScheduler: TestScheduler! - - beforeEach { - testScheduler = TestScheduler() - } - - it("should run the generator immediately") { - var didRunGenerator = false - _ = Signal { observer in - didRunGenerator = true - return nil - } - - expect(didRunGenerator) == true - } - - it("should forward events to observers") { - let numbers = [ 1, 2, 5 ] - - let signal: Signal = Signal { observer in - testScheduler.schedule { - for number in numbers { - observer.sendNext(number) - } - observer.sendCompleted() - } - return nil - } - - var fromSignal: [Int] = [] - var completed = false - - signal.observe { event in - switch event { - case let .next(number): - fromSignal.append(number) - case .completed: - completed = true - default: - break - } - } - - expect(completed) == false - expect(fromSignal).to(beEmpty()) - - testScheduler.run() - - expect(completed) == true - expect(fromSignal) == numbers - } - - it("should dispose of returned disposable upon error") { - let disposable = SimpleDisposable() - - let signal: Signal = Signal { observer in - testScheduler.schedule { - observer.sendFailed(TestError.default) - } - return disposable - } - - var errored = false - - signal.observeFailed { _ in errored = true } - - expect(errored) == false - expect(disposable.isDisposed) == false - - testScheduler.run() - - expect(errored) == true - expect(disposable.isDisposed) == true - } - - it("should dispose of returned disposable upon completion") { - let disposable = SimpleDisposable() - - let signal: Signal = Signal { observer in - testScheduler.schedule { - observer.sendCompleted() - } - return disposable - } - - var completed = false - - signal.observeCompleted { completed = true } - - expect(completed) == false - expect(disposable.isDisposed) == false - - testScheduler.run() - - expect(completed) == true - expect(disposable.isDisposed) == true - } - - it("should dispose of returned disposable upon interrupted") { - let disposable = SimpleDisposable() - - let signal: Signal = Signal { observer in - testScheduler.schedule { - observer.sendInterrupted() - } - return disposable - } - - var interrupted = false - signal.observeInterrupted { - interrupted = true - } - - expect(interrupted) == false - expect(disposable.isDisposed) == false - - testScheduler.run() - - expect(interrupted) == true - expect(disposable.isDisposed) == true - } - } - - describe("Signal.empty") { - it("should interrupt its observers without emitting any value") { - let signal = Signal<(), NoError>.empty - - var hasUnexpectedEventsEmitted = false - var signalInterrupted = false - - signal.observe { event in - switch event { - case .next, .failed, .completed: - hasUnexpectedEventsEmitted = true - case .interrupted: - signalInterrupted = true - } - } - - expect(hasUnexpectedEventsEmitted) == false - expect(signalInterrupted) == true - } - } - - describe("Signal.pipe") { - it("should forward events to observers") { - let (signal, observer) = Signal.pipe() - - var fromSignal: [Int] = [] - var completed = false - - signal.observe { event in - switch event { - case let .next(number): - fromSignal.append(number) - case .completed: - completed = true - default: - break - } - } - - expect(fromSignal).to(beEmpty()) - expect(completed) == false - - observer.sendNext(1) - expect(fromSignal) == [ 1 ] - - observer.sendNext(2) - expect(fromSignal) == [ 1, 2 ] - - expect(completed) == false - observer.sendCompleted() - expect(completed) == true - } - - context("memory") { - it("should not crash allocating memory with a few observers") { - let (signal, _) = Signal.pipe() - - for _ in 0..<50 { - autoreleasepool { - let disposable = signal.observe { _ in } - - disposable!.dispose() - } - } - } - } - } - - describe("observe") { - var testScheduler: TestScheduler! - - beforeEach { - testScheduler = TestScheduler() - } - - it("should stop forwarding events when disposed") { - let disposable = SimpleDisposable() - - let signal: Signal = Signal { observer in - testScheduler.schedule { - for number in [ 1, 2 ] { - observer.sendNext(number) - } - observer.sendCompleted() - observer.sendNext(4) - } - return disposable - } - - var fromSignal: [Int] = [] - signal.observeNext { number in - fromSignal.append(number) - } - - expect(disposable.isDisposed) == false - expect(fromSignal).to(beEmpty()) - - testScheduler.run() - - expect(disposable.isDisposed) == true - expect(fromSignal) == [ 1, 2 ] - } - - it("should not trigger side effects") { - var runCount = 0 - let signal: Signal<(), NoError> = Signal { observer in - runCount += 1 - return nil - } - - expect(runCount) == 1 - - signal.observe(Observer<(), NoError>()) - expect(runCount) == 1 - } - - it("should release observer after termination") { - weak var testStr: NSMutableString? - let (signal, observer) = Signal.pipe() - - let test = { - let innerStr: NSMutableString = NSMutableString() - signal.observeNext { value in - innerStr.append("\(value)") - } - testStr = innerStr - } - test() - - observer.sendNext(1) - expect(testStr) == "1" - observer.sendNext(2) - expect(testStr) == "12" - - observer.sendCompleted() - expect(testStr).to(beNil()) - } - - it("should release observer after interruption") { - weak var testStr: NSMutableString? - let (signal, observer) = Signal.pipe() - - let test = { - let innerStr: NSMutableString = NSMutableString() - signal.observeNext { value in - innerStr.append("\(value)") - } - - testStr = innerStr - } - - test() - - observer.sendNext(1) - expect(testStr) == "1" - - observer.sendNext(2) - expect(testStr) == "12" - - observer.sendInterrupted() - expect(testStr).to(beNil()) - } - } - - describe("trailing closure") { - it("receives next values") { - var values = [Int]() - let (signal, observer) = Signal.pipe() - - signal.observeNext { next in - values.append(next) - } - - observer.sendNext(1) - expect(values) == [1] - } - - it("receives results") { - let (signal, observer) = Signal.pipe() - - var results: [Result] = [] - signal.observeResult { results.append($0) } - - observer.sendNext(1) - observer.sendNext(2) - observer.sendNext(3) - observer.sendFailed(.default) - - observer.sendCompleted() - - expect(results).to(haveCount(4)) - expect(results[0].value) == 1 - expect(results[1].value) == 2 - expect(results[2].value) == 3 - expect(results[3].error) == .default - } - } - - describe("map") { - it("should transform the values of the signal") { - let (signal, observer) = Signal.pipe() - let mappedSignal = signal.map { String($0 + 1) } - - var lastValue: String? - - mappedSignal.observeNext { - lastValue = $0 - return - } - - expect(lastValue).to(beNil()) - - observer.sendNext(0) - expect(lastValue) == "1" - - observer.sendNext(1) - expect(lastValue) == "2" - } - } - - - describe("mapError") { - it("should transform the errors of the signal") { - let (signal, observer) = Signal.pipe() - let producerError = NSError(domain: "com.reactivecocoa.errordomain", code: 100, userInfo: nil) - var error: NSError? - - signal - .mapError { _ in producerError } - .observeFailed { err in error = err } - - expect(error).to(beNil()) - - observer.sendFailed(TestError.default) - expect(error) == producerError - } - } - - describe("filter") { - it("should omit values from the signal") { - let (signal, observer) = Signal.pipe() - let mappedSignal = signal.filter { $0 % 2 == 0 } - - var lastValue: Int? - - mappedSignal.observeNext { lastValue = $0 } - - expect(lastValue).to(beNil()) - - observer.sendNext(0) - expect(lastValue) == 0 - - observer.sendNext(1) - expect(lastValue) == 0 - - observer.sendNext(2) - expect(lastValue) == 2 - } - } - - describe("skipNil") { - it("should forward only non-nil values") { - let (signal, observer) = Signal.pipe() - let mappedSignal = signal.skipNil() - - var lastValue: Int? - - mappedSignal.observeNext { lastValue = $0 } - expect(lastValue).to(beNil()) - - observer.sendNext(nil) - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue) == 1 - - observer.sendNext(nil) - expect(lastValue) == 1 - - observer.sendNext(2) - expect(lastValue) == 2 - } - } - - describe("scan") { - it("should incrementally accumulate a value") { - let (baseSignal, observer) = Signal.pipe() - let signal = baseSignal.scan("", +) - - var lastValue: String? - - signal.observeNext { lastValue = $0 } - - expect(lastValue).to(beNil()) - - observer.sendNext("a") - expect(lastValue) == "a" - - observer.sendNext("bb") - expect(lastValue) == "abb" - } - } - - describe("reduce") { - it("should accumulate one value") { - let (baseSignal, observer) = Signal.pipe() - let signal = baseSignal.reduce(1, +) - - var lastValue: Int? - var completed = false - - signal.observe { event in - switch event { - case let .next(value): - lastValue = value - case .completed: - completed = true - default: - break - } - } - - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue).to(beNil()) - - observer.sendNext(2) - expect(lastValue).to(beNil()) - - expect(completed) == false - observer.sendCompleted() - expect(completed) == true - - expect(lastValue) == 4 - } - - it("should send the initial value if none are received") { - let (baseSignal, observer) = Signal.pipe() - let signal = baseSignal.reduce(1, +) - - var lastValue: Int? - var completed = false - - signal.observe { event in - switch event { - case let .next(value): - lastValue = value - case .completed: - completed = true - default: - break - } - } - - expect(lastValue).to(beNil()) - expect(completed) == false - - observer.sendCompleted() - - expect(lastValue) == 1 - expect(completed) == true - } - } - - describe("skip") { - it("should skip initial values") { - let (baseSignal, observer) = Signal.pipe() - let signal = baseSignal.skip(first: 1) - - var lastValue: Int? - signal.observeNext { lastValue = $0 } - - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue).to(beNil()) - - observer.sendNext(2) - expect(lastValue) == 2 - } - - it("should not skip any values when 0") { - let (baseSignal, observer) = Signal.pipe() - let signal = baseSignal.skip(first: 0) - - var lastValue: Int? - signal.observeNext { lastValue = $0 } - - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue) == 1 - - observer.sendNext(2) - expect(lastValue) == 2 - } - } - - describe("skipRepeats") { - it("should skip duplicate Equatable values") { - let (baseSignal, observer) = Signal.pipe() - let signal = baseSignal.skipRepeats() - - var values: [Bool] = [] - signal.observeNext { values.append($0) } - - expect(values) == [] - - observer.sendNext(true) - expect(values) == [ true ] - - observer.sendNext(true) - expect(values) == [ true ] - - observer.sendNext(false) - expect(values) == [ true, false ] - - observer.sendNext(true) - expect(values) == [ true, false, true ] - } - - it("should skip values according to a predicate") { - let (baseSignal, observer) = Signal.pipe() - let signal = baseSignal.skipRepeats { $0.characters.count == $1.characters.count } - - var values: [String] = [] - signal.observeNext { values.append($0) } - - expect(values) == [] - - observer.sendNext("a") - expect(values) == [ "a" ] - - observer.sendNext("b") - expect(values) == [ "a" ] - - observer.sendNext("cc") - expect(values) == [ "a", "cc" ] - - observer.sendNext("d") - expect(values) == [ "a", "cc", "d" ] - } - - it("should not store strong reference to previously passed items") { - var disposedItems: [Bool] = [] - - struct Item { - let payload: Bool - let disposable: ScopedDisposable - } - - func item(_ payload: Bool) -> Item { - return Item( - payload: payload, - disposable: ScopedDisposable(ActionDisposable { disposedItems.append(payload) }) - ) - } - - let (baseSignal, observer) = Signal.pipe() - baseSignal.skipRepeats { $0.payload == $1.payload }.observeNext { _ in } - - observer.sendNext(item(true)) - expect(disposedItems) == [] - - observer.sendNext(item(false)) - expect(disposedItems) == [ true ] - - observer.sendNext(item(false)) - expect(disposedItems) == [ true, false ] - - observer.sendNext(item(true)) - expect(disposedItems) == [ true, false, false ] - - observer.sendCompleted() - expect(disposedItems) == [ true, false, false, true ] - } - } - - describe("uniqueValues") { - it("should skip values that have been already seen") { - let (baseSignal, observer) = Signal.pipe() - let signal = baseSignal.uniqueValues() - - var values: [String] = [] - signal.observeNext { values.append($0) } - - expect(values) == [] - - observer.sendNext("a") - expect(values) == [ "a" ] - - observer.sendNext("b") - expect(values) == [ "a", "b" ] - - observer.sendNext("a") - expect(values) == [ "a", "b" ] - - observer.sendNext("b") - expect(values) == [ "a", "b" ] - - observer.sendNext("c") - expect(values) == [ "a", "b", "c" ] - - observer.sendCompleted() - expect(values) == [ "a", "b", "c" ] - } - } - - describe("skipWhile") { - var signal: Signal! - var observer: Signal.Observer! - - var lastValue: Int? - - beforeEach { - let (baseSignal, incomingObserver) = Signal.pipe() - - signal = baseSignal.skip { $0 < 2 } - observer = incomingObserver - lastValue = nil - - signal.observeNext { lastValue = $0 } - } - - it("should skip while the predicate is true") { - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue).to(beNil()) - - observer.sendNext(2) - expect(lastValue) == 2 - - observer.sendNext(0) - expect(lastValue) == 0 - } - - it("should not skip any values when the predicate starts false") { - expect(lastValue).to(beNil()) - - observer.sendNext(3) - expect(lastValue) == 3 - - observer.sendNext(1) - expect(lastValue) == 1 - } - } - - describe("skipUntil") { - var signal: Signal! - var observer: Signal.Observer! - var triggerObserver: Signal<(), NoError>.Observer! - - var lastValue: Int? = nil - - beforeEach { - let (baseSignal, incomingObserver) = Signal.pipe() - let (triggerSignal, incomingTriggerObserver) = Signal<(), NoError>.pipe() - - signal = baseSignal.skip(until: triggerSignal) - observer = incomingObserver - triggerObserver = incomingTriggerObserver - - lastValue = nil - - signal.observe { event in - switch event { - case let .next(value): - lastValue = value - default: - break - } - } - } - - it("should skip values until the trigger fires") { - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue).to(beNil()) - - observer.sendNext(2) - expect(lastValue).to(beNil()) - - triggerObserver.sendNext(()) - observer.sendNext(0) - expect(lastValue) == 0 - } - - it("should skip values until the trigger completes") { - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue).to(beNil()) - - observer.sendNext(2) - expect(lastValue).to(beNil()) - - triggerObserver.sendCompleted() - observer.sendNext(0) - expect(lastValue) == 0 - } - } - - describe("take") { - it("should take initial values") { - let (baseSignal, observer) = Signal.pipe() - let signal = baseSignal.take(first: 2) - - var lastValue: Int? - var completed = false - signal.observe { event in - switch event { - case let .next(value): - lastValue = value - case .completed: - completed = true - default: - break - } - } - - expect(lastValue).to(beNil()) - expect(completed) == false - - observer.sendNext(1) - expect(lastValue) == 1 - expect(completed) == false - - observer.sendNext(2) - expect(lastValue) == 2 - expect(completed) == true - } - - it("should complete immediately after taking given number of values") { - let numbers = [ 1, 2, 4, 4, 5 ] - let testScheduler = TestScheduler() - - var signal: Signal = Signal { observer in - testScheduler.schedule { - for number in numbers { - observer.sendNext(number) - } - } - return nil - } - - var completed = false - - signal = signal.take(first: numbers.count) - signal.observeCompleted { completed = true } - - expect(completed) == false - testScheduler.run() - expect(completed) == true - } - - it("should interrupt when 0") { - let numbers = [ 1, 2, 4, 4, 5 ] - let testScheduler = TestScheduler() - - let signal: Signal = Signal { observer in - testScheduler.schedule { - for number in numbers { - observer.sendNext(number) - } - } - return nil - } - - var result: [Int] = [] - var interrupted = false - - signal - .take(first: 0) - .observe { event in - switch event { - case let .next(number): - result.append(number) - case .interrupted: - interrupted = true - default: - break - } - } - - expect(interrupted) == true - - testScheduler.run() - expect(result).to(beEmpty()) - } - } - - describe("collect") { - it("should collect all values") { - let (original, observer) = Signal.pipe() - let signal = original.collect() - let expectedResult = [ 1, 2, 3 ] - - var result: [Int]? - - signal.observeNext { value in - expect(result).to(beNil()) - result = value - } - - for number in expectedResult { - observer.sendNext(number) - } - - expect(result).to(beNil()) - observer.sendCompleted() - expect(result) == expectedResult - } - - it("should complete with an empty array if there are no values") { - let (original, observer) = Signal.pipe() - let signal = original.collect() - - var result: [Int]? - - signal.observeNext { result = $0 } - - expect(result).to(beNil()) - observer.sendCompleted() - expect(result) == [] - } - - it("should forward errors") { - let (original, observer) = Signal.pipe() - let signal = original.collect() - - var error: TestError? - - signal.observeFailed { error = $0 } - - expect(error).to(beNil()) - observer.sendFailed(.default) - expect(error) == TestError.default - } - - it("should collect an exact count of values") { - let (original, observer) = Signal.pipe() - - let signal = original.collect(count: 3) - - var observedValues: [[Int]] = [] - - signal.observeNext { value in - observedValues.append(value) - } - - var expectation: [[Int]] = [] - - for i in 1...7 { - - observer.sendNext(i) - - if i % 3 == 0 { - expectation.append([Int]((i - 2)...i)) - expect(observedValues) == expectation - } else { - expect(observedValues) == expectation - } - } - - observer.sendCompleted() - - expectation.append([7]) - expect(observedValues) == expectation - } - - it("should collect values until it matches a certain value") { - let (original, observer) = Signal.pipe() - - let signal = original.collect { _, next in next != 5 } - - var expectedValues = [ - [5, 5], - [42, 5] - ] - - signal.observeNext { value in - expect(value) == expectedValues.removeFirst() - } - - signal.observeCompleted { - expect(expectedValues) == [] - } - - expectedValues - .flatMap { $0 } - .forEach(observer.sendNext) - - observer.sendCompleted() - } - - it("should collect values until it matches a certain condition on values") { - let (original, observer) = Signal.pipe() - - let signal = original.collect { values in values.reduce(0, +) == 10 } - - var expectedValues = [ - [1, 2, 3, 4], - [5, 6, 7, 8, 9] - ] - - signal.observeNext { value in - expect(value) == expectedValues.removeFirst() - } - - signal.observeCompleted { - expect(expectedValues) == [] - } - - expectedValues - .flatMap { $0 } - .forEach(observer.sendNext) - - observer.sendCompleted() - } - } - - describe("takeUntil") { - var signal: Signal! - var observer: Signal.Observer! - var triggerObserver: Signal<(), NoError>.Observer! - - var lastValue: Int? = nil - var completed: Bool = false - - beforeEach { - let (baseSignal, incomingObserver) = Signal.pipe() - let (triggerSignal, incomingTriggerObserver) = Signal<(), NoError>.pipe() - - signal = baseSignal.take(until: triggerSignal) - observer = incomingObserver - triggerObserver = incomingTriggerObserver - - lastValue = nil - completed = false - - signal.observe { event in - switch event { - case let .next(value): - lastValue = value - case .completed: - completed = true - default: - break - } - } - } - - it("should take values until the trigger fires") { - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue) == 1 - - observer.sendNext(2) - expect(lastValue) == 2 - - expect(completed) == false - triggerObserver.sendNext(()) - expect(completed) == true - } - - it("should take values until the trigger completes") { - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue) == 1 - - observer.sendNext(2) - expect(lastValue) == 2 - - expect(completed) == false - triggerObserver.sendCompleted() - expect(completed) == true - } - - it("should complete if the trigger fires immediately") { - expect(lastValue).to(beNil()) - expect(completed) == false - - triggerObserver.sendNext(()) - - expect(completed) == true - expect(lastValue).to(beNil()) - } - } - - describe("takeUntilReplacement") { - var signal: Signal! - var observer: Signal.Observer! - var replacementObserver: Signal.Observer! - - var lastValue: Int? = nil - var completed: Bool = false - - beforeEach { - let (baseSignal, incomingObserver) = Signal.pipe() - let (replacementSignal, incomingReplacementObserver) = Signal.pipe() - - signal = baseSignal.take(untilReplacement: replacementSignal) - observer = incomingObserver - replacementObserver = incomingReplacementObserver - - lastValue = nil - completed = false - - signal.observe { event in - switch event { - case let .next(value): - lastValue = value - case .completed: - completed = true - default: - break - } - } - } - - it("should take values from the original then the replacement") { - expect(lastValue).to(beNil()) - expect(completed) == false - - observer.sendNext(1) - expect(lastValue) == 1 - - observer.sendNext(2) - expect(lastValue) == 2 - - replacementObserver.sendNext(3) - - expect(lastValue) == 3 - expect(completed) == false - - observer.sendNext(4) - - expect(lastValue) == 3 - expect(completed) == false - - replacementObserver.sendNext(5) - expect(lastValue) == 5 - - expect(completed) == false - replacementObserver.sendCompleted() - expect(completed) == true - } - } - - describe("takeWhile") { - var signal: Signal! - var observer: Signal.Observer! - - beforeEach { - let (baseSignal, incomingObserver) = Signal.pipe() - signal = baseSignal.take { $0 <= 4 } - observer = incomingObserver - } - - it("should take while the predicate is true") { - var latestValue: Int! - var completed = false - - signal.observe { event in - switch event { - case let .next(value): - latestValue = value - case .completed: - completed = true - default: - break - } - } - - for value in -1...4 { - observer.sendNext(value) - expect(latestValue) == value - expect(completed) == false - } - - observer.sendNext(5) - expect(latestValue) == 4 - expect(completed) == true - } - - it("should complete if the predicate starts false") { - var latestValue: Int? - var completed = false - - signal.observe { event in - switch event { - case let .next(value): - latestValue = value - case .completed: - completed = true - default: - break - } - } - - observer.sendNext(5) - expect(latestValue).to(beNil()) - expect(completed) == true - } - } - - describe("observeOn") { - it("should send events on the given scheduler") { - let testScheduler = TestScheduler() - let (signal, observer) = Signal.pipe() - - var result: [Int] = [] - - signal - .observe(on: testScheduler) - .observeNext { result.append($0) } - - observer.sendNext(1) - observer.sendNext(2) - expect(result).to(beEmpty()) - - testScheduler.run() - expect(result) == [ 1, 2 ] - } - } - - describe("delay") { - it("should send events on the given scheduler after the interval") { - let testScheduler = TestScheduler() - let signal: Signal = Signal { observer in - testScheduler.schedule { - observer.sendNext(1) - } - testScheduler.schedule(after: 5) { - observer.sendNext(2) - observer.sendCompleted() - } - return nil - } - - var result: [Int] = [] - var completed = false - - signal - .delay(10, on: testScheduler) - .observe { event in - switch event { - case let .next(number): - result.append(number) - case .completed: - completed = true - default: - break - } - } - - testScheduler.advance(by: 4) // send initial value - expect(result).to(beEmpty()) - - testScheduler.advance(by: 10) // send second value and receive first - expect(result) == [ 1 ] - expect(completed) == false - - testScheduler.advance(by: 10) // send second value and receive first - expect(result) == [ 1, 2 ] - expect(completed) == true - } - - it("should schedule errors immediately") { - let testScheduler = TestScheduler() - let signal: Signal = Signal { observer in - testScheduler.schedule { - observer.sendFailed(TestError.default) - } - return nil - } - - var errored = false - - signal - .delay(10, on: testScheduler) - .observeFailed { _ in errored = true } - - testScheduler.advance() - expect(errored) == true - } - } - - describe("throttle") { - var scheduler: TestScheduler! - var observer: Signal.Observer! - var signal: Signal! - - beforeEach { - scheduler = TestScheduler() - - let (baseSignal, baseObserver) = Signal.pipe() - observer = baseObserver - - signal = baseSignal.throttle(1, on: scheduler) - expect(signal).notTo(beNil()) - } - - it("should send values on the given scheduler at no less than the interval") { - var values: [Int] = [] - signal.observeNext { value in - values.append(value) - } - - expect(values) == [] - - observer.sendNext(0) - expect(values) == [] - - scheduler.advance() - expect(values) == [ 0 ] - - observer.sendNext(1) - observer.sendNext(2) - expect(values) == [ 0 ] - - scheduler.advance(by: 1.5) - expect(values) == [ 0, 2 ] - - scheduler.advance(by: 3) - expect(values) == [ 0, 2 ] - - observer.sendNext(3) - expect(values) == [ 0, 2 ] - - scheduler.advance() - expect(values) == [ 0, 2, 3 ] - - observer.sendNext(4) - observer.sendNext(5) - scheduler.advance() - expect(values) == [ 0, 2, 3 ] - - scheduler.rewind(by: 2) - expect(values) == [ 0, 2, 3 ] - - observer.sendNext(6) - scheduler.advance() - expect(values) == [ 0, 2, 3, 6 ] - - observer.sendNext(7) - observer.sendNext(8) - scheduler.advance() - expect(values) == [ 0, 2, 3, 6 ] - - scheduler.run() - expect(values) == [ 0, 2, 3, 6, 8 ] - } - - it("should schedule completion immediately") { - var values: [Int] = [] - var completed = false - - signal.observe { event in - switch event { - case let .next(value): - values.append(value) - case .completed: - completed = true - default: - break - } - } - - observer.sendNext(0) - scheduler.advance() - expect(values) == [ 0 ] - - observer.sendNext(1) - observer.sendCompleted() - expect(completed) == false - - scheduler.advance() - expect(values) == [ 0 ] - expect(completed) == true - - scheduler.run() - expect(values) == [ 0 ] - expect(completed) == true - } - } - - describe("debounce") { - var scheduler: TestScheduler! - var observer: Signal.Observer! - var signal: Signal! - - beforeEach { - scheduler = TestScheduler() - - let (baseSignal, baseObserver) = Signal.pipe() - observer = baseObserver - - signal = baseSignal.debounce(1, on: scheduler) - expect(signal).notTo(beNil()) - } - - it("should send values on the given scheduler once the interval has passed since the last value was sent") { - var values: [Int] = [] - signal.observeNext { value in - values.append(value) - } - - expect(values) == [] - - observer.sendNext(0) - expect(values) == [] - - scheduler.advance() - expect(values) == [] - - observer.sendNext(1) - observer.sendNext(2) - expect(values) == [] - - scheduler.advance(by: 1.5) - expect(values) == [ 2 ] - - scheduler.advance(by: 3) - expect(values) == [ 2 ] - - observer.sendNext(3) - expect(values) == [ 2 ] - - scheduler.advance() - expect(values) == [ 2 ] - - observer.sendNext(4) - observer.sendNext(5) - scheduler.advance() - expect(values) == [ 2 ] - - scheduler.run() - expect(values) == [ 2, 5 ] - } - - it("should schedule completion immediately") { - var values: [Int] = [] - var completed = false - - signal.observe { event in - switch event { - case let .next(value): - values.append(value) - case .completed: - completed = true - default: - break - } - } - - observer.sendNext(0) - scheduler.advance() - expect(values) == [] - - observer.sendNext(1) - observer.sendCompleted() - expect(completed) == false - - scheduler.advance() - expect(values) == [] - expect(completed) == true - - scheduler.run() - expect(values) == [] - expect(completed) == true - } - } - - describe("sampleWith") { - var sampledSignal: Signal<(Int, String), NoError>! - var observer: Signal.Observer! - var samplerObserver: Signal.Observer! - - beforeEach { - let (signal, incomingObserver) = Signal.pipe() - let (sampler, incomingSamplerObserver) = Signal.pipe() - sampledSignal = signal.sample(with: sampler) - observer = incomingObserver - samplerObserver = incomingSamplerObserver - } - - it("should forward the latest value when the sampler fires") { - var result: [String] = [] - sampledSignal.observeNext { (left, right) in result.append("\(left)\(right)") } - - observer.sendNext(1) - observer.sendNext(2) - samplerObserver.sendNext("a") - expect(result) == [ "2a" ] - } - - it("should do nothing if sampler fires before signal receives value") { - var result: [String] = [] - sampledSignal.observeNext { (left, right) in result.append("\(left)\(right)") } - - samplerObserver.sendNext("a") - expect(result).to(beEmpty()) - } - - it("should send lates value with sampler value multiple times when sampler fires multiple times") { - var result: [String] = [] - sampledSignal.observeNext { (left, right) in result.append("\(left)\(right)") } - - observer.sendNext(1) - samplerObserver.sendNext("a") - samplerObserver.sendNext("b") - expect(result) == [ "1a", "1b" ] - } - - it("should complete when both inputs have completed") { - var completed = false - sampledSignal.observeCompleted { completed = true } - - observer.sendCompleted() - expect(completed) == false - - samplerObserver.sendCompleted() - expect(completed) == true - } - } - - describe("sampleOn") { - var sampledSignal: Signal! - var observer: Signal.Observer! - var samplerObserver: Signal<(), NoError>.Observer! - - beforeEach { - let (signal, incomingObserver) = Signal.pipe() - let (sampler, incomingSamplerObserver) = Signal<(), NoError>.pipe() - sampledSignal = signal.sample(on: sampler) - observer = incomingObserver - samplerObserver = incomingSamplerObserver - } - - it("should forward the latest value when the sampler fires") { - var result: [Int] = [] - sampledSignal.observeNext { result.append($0) } - - observer.sendNext(1) - observer.sendNext(2) - samplerObserver.sendNext(()) - expect(result) == [ 2 ] - } - - it("should do nothing if sampler fires before signal receives value") { - var result: [Int] = [] - sampledSignal.observeNext { result.append($0) } - - samplerObserver.sendNext(()) - expect(result).to(beEmpty()) - } - - it("should send lates value multiple times when sampler fires multiple times") { - var result: [Int] = [] - sampledSignal.observeNext { result.append($0) } - - observer.sendNext(1) - samplerObserver.sendNext(()) - samplerObserver.sendNext(()) - expect(result) == [ 1, 1 ] - } - - it("should complete when both inputs have completed") { - var completed = false - sampledSignal.observeCompleted { completed = true } - - observer.sendCompleted() - expect(completed) == false - - samplerObserver.sendCompleted() - expect(completed) == true - } - } - - describe("combineLatestWith") { - var combinedSignal: Signal<(Int, Double), NoError>! - var observer: Signal.Observer! - var otherObserver: Signal.Observer! - - beforeEach { - let (signal, incomingObserver) = Signal.pipe() - let (otherSignal, incomingOtherObserver) = Signal.pipe() - combinedSignal = signal.combineLatest(with: otherSignal) - observer = incomingObserver - otherObserver = incomingOtherObserver - } - - it("should forward the latest values from both inputs") { - var latest: (Int, Double)? - combinedSignal.observeNext { latest = $0 } - - observer.sendNext(1) - expect(latest).to(beNil()) - - // is there a better way to test tuples? - otherObserver.sendNext(1.5) - expect(latest?.0) == 1 - expect(latest?.1) == 1.5 - - observer.sendNext(2) - expect(latest?.0) == 2 - expect(latest?.1) == 1.5 - } - - it("should complete when both inputs have completed") { - var completed = false - combinedSignal.observeCompleted { completed = true } - - observer.sendCompleted() - expect(completed) == false - - otherObserver.sendCompleted() - expect(completed) == true - } - } - - describe("zipWith") { - var leftObserver: Signal.Observer! - var rightObserver: Signal.Observer! - var zipped: Signal<(Int, String), NoError>! - - beforeEach { - let (leftSignal, incomingLeftObserver) = Signal.pipe() - let (rightSignal, incomingRightObserver) = Signal.pipe() - - leftObserver = incomingLeftObserver - rightObserver = incomingRightObserver - zipped = leftSignal.zip(with: rightSignal) - } - - it("should combine pairs") { - var result: [String] = [] - zipped.observeNext { (left, right) in result.append("\(left)\(right)") } - - leftObserver.sendNext(1) - leftObserver.sendNext(2) - expect(result) == [] - - rightObserver.sendNext("foo") - expect(result) == [ "1foo" ] - - leftObserver.sendNext(3) - rightObserver.sendNext("bar") - expect(result) == [ "1foo", "2bar" ] - - rightObserver.sendNext("buzz") - expect(result) == [ "1foo", "2bar", "3buzz" ] - - rightObserver.sendNext("fuzz") - expect(result) == [ "1foo", "2bar", "3buzz" ] - - leftObserver.sendNext(4) - expect(result) == [ "1foo", "2bar", "3buzz", "4fuzz" ] - } - - it("should complete when the shorter signal has completed") { - var result: [String] = [] - var completed = false - - zipped.observe { event in - switch event { - case let .next(left, right): - result.append("\(left)\(right)") - case .completed: - completed = true - default: - break - } - } - - expect(completed) == false - - leftObserver.sendNext(0) - leftObserver.sendCompleted() - expect(completed) == false - expect(result) == [] - - rightObserver.sendNext("foo") - expect(completed) == true - expect(result) == [ "0foo" ] - } - - it("should complete when both signal have completed") { - var result: [String] = [] - var completed = false - - zipped.observe { event in - switch event { - case let .next(left, right): - result.append("\(left)\(right)") - case .completed: - completed = true - default: - break - } - } - - expect(completed) == false - - leftObserver.sendNext(0) - leftObserver.sendCompleted() - expect(completed) == false - expect(result) == [] - - rightObserver.sendCompleted() - expect(result) == [ ] - } - - it("should complete and drop unpaired pending values when both signal have completed") { - var result: [String] = [] - var completed = false - - zipped.observe { event in - switch event { - case let .next(left, right): - result.append("\(left)\(right)") - case .completed: - completed = true - default: - break - } - } - - expect(completed) == false - - leftObserver.sendNext(0) - leftObserver.sendNext(1) - leftObserver.sendNext(2) - leftObserver.sendNext(3) - leftObserver.sendCompleted() - expect(completed) == false - expect(result) == [] - - rightObserver.sendNext("foo") - rightObserver.sendNext("bar") - rightObserver.sendCompleted() - expect(result) == ["0foo", "1bar"] - } - } - - describe("materialize") { - it("should reify events from the signal") { - let (signal, observer) = Signal.pipe() - var latestEvent: Event? - signal - .materialize() - .observeNext { latestEvent = $0 } - - observer.sendNext(2) - - expect(latestEvent).toNot(beNil()) - if let latestEvent = latestEvent { - switch latestEvent { - case let .next(value): - expect(value) == 2 - default: - fail() - } - } - - observer.sendFailed(TestError.default) - if let latestEvent = latestEvent { - switch latestEvent { - case .failed: - () - default: - fail() - } - } - } - } - - describe("dematerialize") { - typealias IntEvent = Event - var observer: Signal.Observer! - var dematerialized: Signal! - - beforeEach { - let (signal, incomingObserver) = Signal.pipe() - observer = incomingObserver - dematerialized = signal.dematerialize() - } - - it("should send values for Next events") { - var result: [Int] = [] - dematerialized - .assumeNoErrors() - .observeNext { result.append($0) } - - expect(result).to(beEmpty()) - - observer.sendNext(.next(2)) - expect(result) == [ 2 ] - - observer.sendNext(.next(4)) - expect(result) == [ 2, 4 ] - } - - it("should error out for Error events") { - var errored = false - dematerialized.observeFailed { _ in errored = true } - - expect(errored) == false - - observer.sendNext(.failed(TestError.default)) - expect(errored) == true - } - - it("should complete early for Completed events") { - var completed = false - dematerialized.observeCompleted { completed = true } - - expect(completed) == false - observer.sendNext(IntEvent.completed) - expect(completed) == true - } - } - - describe("takeLast") { - var observer: Signal.Observer! - var lastThree: Signal! - - beforeEach { - let (signal, incomingObserver) = Signal.pipe() - observer = incomingObserver - lastThree = signal.take(last: 3) - } - - it("should send the last N values upon completion") { - var result: [Int] = [] - lastThree - .assumeNoErrors() - .observeNext { result.append($0) } - - observer.sendNext(1) - observer.sendNext(2) - observer.sendNext(3) - observer.sendNext(4) - expect(result).to(beEmpty()) - - observer.sendCompleted() - expect(result) == [ 2, 3, 4 ] - } - - it("should send less than N values if not enough were received") { - var result: [Int] = [] - lastThree - .assumeNoErrors() - .observeNext { result.append($0) } - - observer.sendNext(1) - observer.sendNext(2) - observer.sendCompleted() - expect(result) == [ 1, 2 ] - } - - it("should send nothing when errors") { - var result: [Int] = [] - var errored = false - lastThree.observe { event in - switch event { - case let .next(value): - result.append(value) - case .failed: - errored = true - default: - break - } - } - - observer.sendNext(1) - observer.sendNext(2) - observer.sendNext(3) - expect(errored) == false - - observer.sendFailed(TestError.default) - expect(errored) == true - expect(result).to(beEmpty()) - } - } - - describe("timeoutWithError") { - var testScheduler: TestScheduler! - var signal: Signal! - var observer: Signal.Observer! - - beforeEach { - testScheduler = TestScheduler() - let (baseSignal, incomingObserver) = Signal.pipe() - signal = baseSignal.timeout(after: 2, raising: TestError.default, on: testScheduler) - observer = incomingObserver - } - - it("should complete if within the interval") { - var completed = false - var errored = false - signal.observe { event in - switch event { - case .completed: - completed = true - case .failed: - errored = true - default: - break - } - } - - testScheduler.schedule(after: 1) { - observer.sendCompleted() - } - - expect(completed) == false - expect(errored) == false - - testScheduler.run() - expect(completed) == true - expect(errored) == false - } - - it("should error if not completed before the interval has elapsed") { - var completed = false - var errored = false - signal.observe { event in - switch event { - case .completed: - completed = true - case .failed: - errored = true - default: - break - } - } - - testScheduler.schedule(after: 3) { - observer.sendCompleted() - } - - expect(completed) == false - expect(errored) == false - - testScheduler.run() - expect(completed) == false - expect(errored) == true - } - - it("should be available for NoError") { - let signal: Signal = Signal.never - .timeout(after: 2, raising: TestError.default, on: testScheduler) - - _ = signal - } - } - - describe("attempt") { - it("should forward original values upon success") { - let (baseSignal, observer) = Signal.pipe() - let signal = baseSignal.attempt { _ in - return .success() - } - - var current: Int? - signal - .assumeNoErrors() - .observeNext { value in - current = value - } - - for value in 1...5 { - observer.sendNext(value) - expect(current) == value - } - } - - it("should error if an attempt fails") { - let (baseSignal, observer) = Signal.pipe() - let signal = baseSignal.attempt { _ in - return .failure(.default) - } - - var error: TestError? - signal.observeFailed { err in - error = err - } - - observer.sendNext(42) - expect(error) == TestError.default - } - } - - describe("attemptMap") { - it("should forward mapped values upon success") { - let (baseSignal, observer) = Signal.pipe() - let signal = baseSignal.attemptMap { num -> Result in - return .success(num % 2 == 0) - } - - var even: Bool? - signal - .assumeNoErrors() - .observeNext { value in - even = value - } - - observer.sendNext(1) - expect(even) == false - - observer.sendNext(2) - expect(even) == true - } - - it("should error if a mapping fails") { - let (baseSignal, observer) = Signal.pipe() - let signal = baseSignal.attemptMap { _ -> Result in - return .failure(.default) - } - - var error: TestError? - signal.observeFailed { err in - error = err - } - - observer.sendNext(42) - expect(error) == TestError.default - } - } - - describe("combinePrevious") { - var observer: Signal.Observer! - let initialValue: Int = 0 - var latestValues: (Int, Int)? - - beforeEach { - latestValues = nil - - let (signal, baseObserver) = Signal.pipe() - observer = baseObserver - signal.combinePrevious(initialValue).observeNext { latestValues = $0 } - } - - it("should forward the latest value with previous value") { - expect(latestValues).to(beNil()) - - observer.sendNext(1) - expect(latestValues?.0) == initialValue - expect(latestValues?.1) == 1 - - observer.sendNext(2) - expect(latestValues?.0) == 1 - expect(latestValues?.1) == 2 - } - } - - describe("combineLatest") { - var signalA: Signal! - var signalB: Signal! - var signalC: Signal! - var observerA: Signal.Observer! - var observerB: Signal.Observer! - var observerC: Signal.Observer! - - var combinedValues: [Int]? - var completed: Bool! - - beforeEach { - combinedValues = nil - completed = false - - let (baseSignalA, baseObserverA) = Signal.pipe() - let (baseSignalB, baseObserverB) = Signal.pipe() - let (baseSignalC, baseObserverC) = Signal.pipe() - - signalA = baseSignalA - signalB = baseSignalB - signalC = baseSignalC - - observerA = baseObserverA - observerB = baseObserverB - observerC = baseObserverC - } - - let combineLatestExampleName = "combineLatest examples" - sharedExamples(combineLatestExampleName) { - it("should forward the latest values from all inputs"){ - expect(combinedValues).to(beNil()) - - observerA.sendNext(0) - observerB.sendNext(1) - observerC.sendNext(2) - expect(combinedValues) == [0, 1, 2] - - observerA.sendNext(10) - expect(combinedValues) == [10, 1, 2] - } - - it("should not forward the latest values before all inputs"){ - expect(combinedValues).to(beNil()) - - observerA.sendNext(0) - expect(combinedValues).to(beNil()) - - observerB.sendNext(1) - expect(combinedValues).to(beNil()) - - observerC.sendNext(2) - expect(combinedValues) == [0, 1, 2] - } - - it("should complete when all inputs have completed"){ - expect(completed) == false - - observerA.sendCompleted() - observerB.sendCompleted() - expect(completed) == false - - observerC.sendCompleted() - expect(completed) == true - } - } - - describe("tuple") { - beforeEach { - Signal.combineLatest(signalA, signalB, signalC) - .observe { event in - switch event { - case let .next(value): - combinedValues = [value.0, value.1, value.2] - case .completed: - completed = true - default: - break - } - } - } - - itBehavesLike(combineLatestExampleName) - } - - describe("sequence") { - beforeEach { - Signal.combineLatest([signalA, signalB, signalC]) - .observe { event in - switch event { - case let .next(values): - combinedValues = values - case .completed: - completed = true - default: - break - } - } - } - - itBehavesLike(combineLatestExampleName) - } - } - - describe("zip") { - var signalA: Signal! - var signalB: Signal! - var signalC: Signal! - var observerA: Signal.Observer! - var observerB: Signal.Observer! - var observerC: Signal.Observer! - - var zippedValues: [Int]? - var completed: Bool! - - beforeEach { - zippedValues = nil - completed = false - - let (baseSignalA, baseObserverA) = Signal.pipe() - let (baseSignalB, baseObserverB) = Signal.pipe() - let (baseSignalC, baseObserverC) = Signal.pipe() - - signalA = baseSignalA - signalB = baseSignalB - signalC = baseSignalC - - observerA = baseObserverA - observerB = baseObserverB - observerC = baseObserverC - } - - let zipExampleName = "zip examples" - sharedExamples(zipExampleName) { - it("should combine all set"){ - expect(zippedValues).to(beNil()) - - observerA.sendNext(0) - expect(zippedValues).to(beNil()) - - observerB.sendNext(1) - expect(zippedValues).to(beNil()) - - observerC.sendNext(2) - expect(zippedValues) == [0, 1, 2] - - observerA.sendNext(10) - expect(zippedValues) == [0, 1, 2] - - observerA.sendNext(20) - expect(zippedValues) == [0, 1, 2] - - observerB.sendNext(11) - expect(zippedValues) == [0, 1, 2] - - observerC.sendNext(12) - expect(zippedValues) == [10, 11, 12] - } - - it("should complete when the shorter signal has completed"){ - expect(completed) == false - - observerB.sendNext(1) - observerC.sendNext(2) - observerB.sendCompleted() - observerC.sendCompleted() - expect(completed) == false - - observerA.sendNext(0) - expect(completed) == true - } - } - - describe("tuple") { - beforeEach { - Signal.zip(signalA, signalB, signalC) - .observe { event in - switch event { - case let .next(value): - zippedValues = [value.0, value.1, value.2] - case .completed: - completed = true - default: - break - } - } - } - - itBehavesLike(zipExampleName) - } - - describe("sequence") { - beforeEach { - Signal.zip([signalA, signalB, signalC]) - .observe { event in - switch event { - case let .next(values): - zippedValues = values - case .completed: - completed = true - default: - break - } - } - } - - itBehavesLike(zipExampleName) - } - - describe("log events") { - it("should output the correct event without identifier") { - let expectations: [(String) -> Void] = [ - { event in expect(event) == "[] next 1" }, - { event in expect(event) == "[] completed" }, - { event in expect(event) == "[] terminated" }, - { event in expect(event) == "[] disposed" }, - ] - - let logger = TestLogger(expectations: expectations) - - let (signal, observer) = Signal.pipe() - signal - .logEvents(logger: logger.logEvent) - .observe { _ in } - - observer.sendNext(1) - observer.sendCompleted() - } - - it("should output the correct event with identifier") { - let expectations: [(String) -> Void] = [ - { event in expect(event) == "[test.rac] next 1" }, - { event in expect(event) == "[test.rac] failed error1" }, - { event in expect(event) == "[test.rac] terminated" }, - { event in expect(event) == "[test.rac] disposed" }, - ] - - let logger = TestLogger(expectations: expectations) - - let (signal, observer) = Signal.pipe() - signal - .logEvents(identifier: "test.rac", logger: logger.logEvent) - .observe { _ in } - - observer.sendNext(1) - observer.sendFailed(.error1) - } - - it("should only output the events specified in the `events` parameter") { - let expectations: [(String) -> Void] = [ - { event in expect(event) == "[test.rac] failed error1" }, - ] - - let logger = TestLogger(expectations: expectations) - - let (signal, observer) = Signal.pipe() - signal - .logEvents(identifier: "test.rac", events: [.failed], logger: logger.logEvent) - .observe { _ in } - - observer.sendNext(1) - observer.sendFailed(.error1) - } - } - } - } -} diff --git a/ReactiveCocoaTests/Swift/TestError.swift b/ReactiveCocoaTests/Swift/TestError.swift index 8ed0054d8d..275aef893a 100644 --- a/ReactiveCocoaTests/Swift/TestError.swift +++ b/ReactiveCocoaTests/Swift/TestError.swift @@ -7,6 +7,7 @@ // import ReactiveCocoa +import ReactiveSwift import Result enum TestError: Int { diff --git a/ReactiveCocoaTests/Swift/TestLogger.swift b/ReactiveCocoaTests/Swift/TestLogger.swift deleted file mode 100644 index 4d6cea60a5..0000000000 --- a/ReactiveCocoaTests/Swift/TestLogger.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// TestLogger.swift -// ReactiveCocoa -// -// Created by Rui Peres on 29/04/2016. -// Copyright © 2016 GitHub. All rights reserved. -// - -import Foundation -@testable import ReactiveCocoa - -final class TestLogger { - private var expectations: [(String) -> Void] - - init(expectations: [(String) -> Void]) { - self.expectations = expectations - } -} - -extension TestLogger { - - func logEvent(_ identifier: String, event: String, fileName: String, functionName: String, lineNumber: Int) { - expectations.removeFirst()("[\(identifier)] \(event)") - } -} diff --git a/ReactiveSwift/Scheduler.swift b/ReactiveSwift/Scheduler.swift index daa3cb429c..0e3ea04ba0 100644 --- a/ReactiveSwift/Scheduler.swift +++ b/ReactiveSwift/Scheduler.swift @@ -144,7 +144,7 @@ public final class QueueScheduler: DateSchedulerProtocol { return Date() } - internal let queue: DispatchQueue + public let queue: DispatchQueue internal init(internalQueue: DispatchQueue) { queue = internalQueue From 954c82b8412ca06c057b7b0c4c3c49e93ebc09ee Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Tue, 16 Aug 2016 17:30:01 -0400 Subject: [PATCH 0338/1028] Rename ReactiveCocoa-Mac to ReactiveCocoa-macOS --- .travis.yml | 2 +- README.md | 6 ++--- ReactiveCocoa.xcodeproj/project.pbxproj | 22 +++++++++---------- ....xcscheme => ReactiveCocoa-macOS.xcscheme} | 12 +++++----- 4 files changed, 21 insertions(+), 21 deletions(-) rename ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/{ReactiveCocoa-Mac.xcscheme => ReactiveCocoa-macOS.xcscheme} (95%) diff --git a/.travis.yml b/.travis.yml index 51ed833049..f4a4ebeeeb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ script: xcode_workspace: ReactiveCocoa.xcworkspace matrix: include: - - xcode_scheme: ReactiveCocoa-Mac + - xcode_scheme: ReactiveCocoa-macOS env: - XCODE_SDK=macosx - XCODE_ACTION="build test" diff --git a/README.md b/README.md index 578cc7b88f..d83ac67bdc 100644 --- a/README.md +++ b/README.md @@ -189,7 +189,7 @@ our event stream. #### Debugging event streams -Due to its nature, a stream's stack trace might have dozens of frames, which, more often than not, can make debugging a very frustrating activity. +Due to its nature, a stream's stack trace might have dozens of frames, which, more often than not, can make debugging a very frustrating activity. A naive way of debugging, is by injecting side effects into the stream, like so: ```swift @@ -354,10 +354,10 @@ We also provide a great Playground, so you can get used to ReactiveCocoa's opera - `carthage checkout` 1. Open `ReactiveCocoa.xcworkspace` 1. Build `Result-Mac` scheme - 1. Build `ReactiveCocoa-Mac` scheme + 1. Build `ReactiveCocoa-macOS` scheme 1. Finally open the `ReactiveCocoa.playground` 1. Choose `View > Show Debug Area` - + [Actions]: Documentation/FrameworkOverview.md#actions [Basic Operators]: Documentation/BasicOperators.md [CHANGELOG]: CHANGELOG.md diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 132c1005d8..8ed66bdb47 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -2010,9 +2010,9 @@ productReference = A9B315541B3940610001CB9C /* ReactiveCocoa.framework */; productType = "com.apple.product-type.framework"; }; - D04725E919E49ED7006002AA /* ReactiveCocoa-Mac */ = { + D04725E919E49ED7006002AA /* ReactiveCocoa-macOS */ = { isa = PBXNativeTarget; - buildConfigurationList = D047260019E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveCocoa-Mac" */; + buildConfigurationList = D047260019E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveCocoa-macOS" */; buildPhases = ( D04725E519E49ED7006002AA /* Sources */, D04725E619E49ED7006002AA /* Frameworks */, @@ -2023,14 +2023,14 @@ ); dependencies = ( ); - name = "ReactiveCocoa-Mac"; + name = "ReactiveCocoa-macOS"; productName = ReactiveCocoa; productReference = D04725EA19E49ED7006002AA /* ReactiveCocoa.framework */; productType = "com.apple.product-type.framework"; }; - D04725F419E49ED7006002AA /* ReactiveCocoa-MacTests */ = { + D04725F419E49ED7006002AA /* ReactiveCocoa-macOSTests */ = { isa = PBXNativeTarget; - buildConfigurationList = D047260319E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveCocoa-MacTests" */; + buildConfigurationList = D047260319E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveCocoa-macOSTests" */; buildPhases = ( D04725F119E49ED7006002AA /* Sources */, D04725F219E49ED7006002AA /* Frameworks */, @@ -2041,7 +2041,7 @@ dependencies = ( D04725F819E49ED7006002AA /* PBXTargetDependency */, ); - name = "ReactiveCocoa-MacTests"; + name = "ReactiveCocoa-macOSTests"; productName = ReactiveCocoaTests; productReference = D04725F519E49ED7006002AA /* ReactiveCocoaTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; @@ -2134,8 +2134,8 @@ projectDirPath = ""; projectRoot = ""; targets = ( - D04725E919E49ED7006002AA /* ReactiveCocoa-Mac */, - D04725F419E49ED7006002AA /* ReactiveCocoa-MacTests */, + D04725E919E49ED7006002AA /* ReactiveCocoa-macOS */, + D04725F419E49ED7006002AA /* ReactiveCocoa-macOSTests */, D047260B19E49F82006002AA /* ReactiveCocoa-iOS */, D047261519E49F82006002AA /* ReactiveCocoa-iOSTests */, A9B315531B3940610001CB9C /* ReactiveCocoa-watchOS */, @@ -2744,7 +2744,7 @@ }; D04725F819E49ED7006002AA /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = D04725E919E49ED7006002AA /* ReactiveCocoa-Mac */; + target = D04725E919E49ED7006002AA /* ReactiveCocoa-macOS */; targetProxy = D04725F719E49ED7006002AA /* PBXContainerItemProxy */; }; D047261919E49F82006002AA /* PBXTargetDependency */ = { @@ -3281,7 +3281,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - D047260019E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveCocoa-Mac" */ = { + D047260019E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveCocoa-macOS" */ = { isa = XCConfigurationList; buildConfigurations = ( D047260119E49ED7006002AA /* Debug */, @@ -3292,7 +3292,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - D047260319E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveCocoa-MacTests" */ = { + D047260319E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveCocoa-macOSTests" */ = { isa = XCConfigurationList; buildConfigurations = ( D047260419E49ED7006002AA /* Debug */, diff --git a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-Mac.xcscheme b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-macOS.xcscheme similarity index 95% rename from ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-Mac.xcscheme rename to ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-macOS.xcscheme index e26a5aa441..f00a9e8579 100644 --- a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-Mac.xcscheme +++ b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-macOS.xcscheme @@ -44,7 +44,7 @@ BuildableIdentifier = "primary" BlueprintIdentifier = "D04725E919E49ED7006002AA" BuildableName = "ReactiveCocoa.framework" - BlueprintName = "ReactiveCocoa-Mac" + BlueprintName = "ReactiveCocoa-macOS" ReferencedContainer = "container:ReactiveCocoa.xcodeproj"> @@ -86,7 +86,7 @@ BuildableIdentifier = "primary" BlueprintIdentifier = "D04725F419E49ED7006002AA" BuildableName = "ReactiveCocoaTests.xctest" - BlueprintName = "ReactiveCocoa-MacTests" + BlueprintName = "ReactiveCocoa-macOSTests" ReferencedContainer = "container:ReactiveCocoa.xcodeproj"> @@ -105,7 +105,7 @@ BuildableIdentifier = "primary" BlueprintIdentifier = "D04725F419E49ED7006002AA" BuildableName = "ReactiveCocoaTests.xctest" - BlueprintName = "ReactiveCocoa-MacTests" + BlueprintName = "ReactiveCocoa-macOSTests" ReferencedContainer = "container:ReactiveCocoa.xcodeproj"> @@ -115,7 +115,7 @@ BuildableIdentifier = "primary" BlueprintIdentifier = "D04725E919E49ED7006002AA" BuildableName = "ReactiveCocoa.framework" - BlueprintName = "ReactiveCocoa-Mac" + BlueprintName = "ReactiveCocoa-macOS" ReferencedContainer = "container:ReactiveCocoa.xcodeproj"> @@ -137,7 +137,7 @@ BuildableIdentifier = "primary" BlueprintIdentifier = "D04725E919E49ED7006002AA" BuildableName = "ReactiveCocoa.framework" - BlueprintName = "ReactiveCocoa-Mac" + BlueprintName = "ReactiveCocoa-macOS" ReferencedContainer = "container:ReactiveCocoa.xcodeproj"> @@ -155,7 +155,7 @@ BuildableIdentifier = "primary" BlueprintIdentifier = "D04725E919E49ED7006002AA" BuildableName = "ReactiveCocoa.framework" - BlueprintName = "ReactiveCocoa-Mac" + BlueprintName = "ReactiveCocoa-macOS" ReferencedContainer = "container:ReactiveCocoa.xcodeproj"> From 20ad15e4fe219193a7190def1e9f51052dc58b7d Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Tue, 16 Aug 2016 17:44:24 -0400 Subject: [PATCH 0339/1028] Split up README for ReactiveSwift --- README-ReactiveSwift.md | 355 ++++++++++++++++++++++++++++++++++++++++ README.md | 280 ++----------------------------- 2 files changed, 366 insertions(+), 269 deletions(-) create mode 100644 README-ReactiveSwift.md diff --git a/README-ReactiveSwift.md b/README-ReactiveSwift.md new file mode 100644 index 0000000000..c4111f8dd6 --- /dev/null +++ b/README-ReactiveSwift.md @@ -0,0 +1,355 @@ +# ReactiveSwift + +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![GitHub release](https://img.shields.io/github/release/ReactiveCocoa/ReactiveCocoa.svg)](https://github.com/ReactiveCocoa/ReactiveCocoa/releases) ![Swift 3.0.x](https://img.shields.io/badge/Swift-3.0.x-orange.svg) ![platforms](https://img.shields.io/badge/platforms-iOS%20%7C%20OS%20X%20%7C%20watchOS%20%7C%20tvOS%20-lightgrey.svg) + +ReactiveSwift is a Swift framework inspired by [Functional Reactive Programming](https://en.wikipedia.org/wiki/Functional_reactive_programming). It provides APIs for composing and transforming **streams of values over time**. + + 1. [Introduction](#introduction) + 1. [Example: online search](#example-online-search) + 1. [How does ReactiveSwift relate to Rx?](#how-does-reactivecocoa-relate-to-rx) + 1. [Getting started](#getting-started) + 1. [Playground](#playground) + +If you’re already familiar with functional reactive programming or what +ReactiveSwift is about, check out the [Documentation][] folder for more in-depth +information about how it all works. Then, dive straight into our [documentation +comments][Code] for learning more about individual APIs. + +If you'd like to use ReactiveSwift with Apple's Cocoa frameworks, +[ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa) provides +extensions that work with ReactiveSwift. + +If you have a question, please see if any discussions in our [GitHub +issues](https://github.com/ReactiveCocoa/ReactiveSwift/issues?q=is%3Aissue+label%3Aquestion+) or [Stack +Overflow](http://stackoverflow.com/questions/tagged/reactive-cocoa) have already +answered it. If not, please feel free to [file your +own](https://github.com/ReactiveCocoa/ReactiveSwift/issues/new)! + +#### Compatibility + +This documents ReactiveSwift 3.x which targets `Swift 3.0.x`. For `Swift 2.x` support see [ReactiveCocoa +4](https://github.com/ReactiveCocoa/ReactiveCocoa/tree/v4.0.0). + +## Introduction + +ReactiveSwift is inspired by [functional reactive +programming](https://joshaber.github.io/2013/02/11/input-and-output/). +Rather than using mutable variables which are replaced and modified in-place, +RAC offers “event streams,” represented by the [`Signal`][Signals] and +[`SignalProducer`][Signal producers] types, that send values over time. + +Event streams unify common patterns for asynchrony and event +handling, including: + + * Delegate methods + * Callback blocks + * Notifications + * Control actions and responder chain events + * [Futures and promises](https://en.wikipedia.org/wiki/Futures_and_promises) + * [Key-value observing](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html) (KVO) + +Because all of these different mechanisms can be represented in the _same_ way, +it’s easy to declaratively chain and combine them together, with less spaghetti +code and state to bridge the gap. + +For more information about the concepts in ReactiveSwift, see the [Framework +Overview][]. + +## Example: online search + +Let’s say you have a text field, and whenever the user types something into it, +you want to make a network request which searches for that query. + +#### Observing text edits + +The first step is to observe edits to the text field, using a RAC extension to +`UITextField` specifically for this purpose: + +```swift +let searchStrings = textField.rac_textSignal() + .toSignalProducer() + .map { text in text as! String } +``` + +This gives us a [signal producer][Signal producers] which sends +values of type `String`. _(The cast is [currently +necessary](https://github.com/ReactiveCocoa/ReactiveCocoa/issues/2182) to bridge +this extension method from Objective-C.)_ + +#### Making network requests + +With each string, we want to execute a network request. Luckily, RAC offers an +`NSURLSession` extension for doing exactly that: + +```swift +let searchResults = searchStrings + .flatMap(.Latest) { (query: String) -> SignalProducer<(NSData, NSURLResponse), NSError> in + let URLRequest = self.searchRequestWithEscapedQuery(query) + return NSURLSession.sharedSession().rac_dataWithRequest(URLRequest) + } + .map { (data, URLResponse) -> String in + let string = String(data: data, encoding: NSUTF8StringEncoding)! + return self.parseJSONResultsFromString(string) + } + .observeOn(UIScheduler()) +``` + +This has transformed our producer of `String`s into a producer of `Array`s +containing the search results, which will be forwarded on the main thread +(thanks to the [`UIScheduler`][Schedulers]). + +Additionally, [`flatMap(.Latest)`][flatMapLatest] here ensures that _only one search_—the +latest—is allowed to be running. If the user types another character while the +network request is still in flight, it will be cancelled before starting a new +one. Just think of how much code that would take to do by hand! + +#### Receiving the results + +This won’t actually execute yet, because producers must be _started_ in order to +receive the results (which prevents doing work when the results are never used). +That’s easy enough: + +```swift +searchResults.startWithNext { results in + print("Search results: \(results)") +} +``` + +Here, we watch for the `Next` [event][Events], which contains our results, and +just log them to the console. This could easily do something else instead, like +update a table view or a label on screen. + +#### Handling failures + +In this example so far, any network error will generate a `Failed` +[event][Events], which will terminate the event stream. Unfortunately, this +means that future queries won’t even be attempted. + +To remedy this, we need to decide what to do with failures that occur. The +quickest solution would be to log them, then ignore them: + +```swift + .flatMap(.Latest) { (query: String) -> SignalProducer<(NSData, NSURLResponse), NSError> in + let URLRequest = self.searchRequestWithEscapedQuery(query) + + return NSURLSession.sharedSession() + .rac_dataWithRequest(URLRequest) + .flatMapError { error in + print("Network error occurred: \(error)") + return SignalProducer.empty + } + } +``` + +By replacing failures with the `empty` event stream, we’re able to effectively +ignore them. + +However, it’s probably more appropriate to retry at least a couple of times +before giving up. Conveniently, there’s a [`retry`][retry] operator to do exactly that! + +Our improved `searchResults` producer might look like this: + +```swift +let searchResults = searchStrings + .flatMap(.Latest) { (query: String) -> SignalProducer<(NSData, NSURLResponse), NSError> in + let URLRequest = self.searchRequestWithEscapedQuery(query) + + return NSURLSession.sharedSession() + .rac_dataWithRequest(URLRequest) + .retry(2) + .flatMapError { error in + print("Network error occurred: \(error)") + return SignalProducer.empty + } + } + .map { (data, URLResponse) -> String in + let string = String(data: data, encoding: NSUTF8StringEncoding)! + return self.parseJSONResultsFromString(string) + } + .observeOn(UIScheduler()) +``` + +#### Throttling requests + +Now, let’s say you only want to actually perform the search periodically, +to minimize traffic. + +ReactiveCocoa has a declarative `throttle` operator that we can apply to our +search strings: + +```swift +let searchStrings = textField.rac_textSignal() + .toSignalProducer() + .map { text in text as! String } + .throttle(0.5, onScheduler: QueueScheduler.mainQueueScheduler) +``` + +This prevents values from being sent less than 0.5 seconds apart. + +To do this manually would require significant state, and end up much harder to +read! With ReactiveCocoa, we can use just one operator to incorporate _time_ into +our event stream. + +#### Debugging event streams + +Due to its nature, a stream's stack trace might have dozens of frames, which, more often than not, can make debugging a very frustrating activity. +A naive way of debugging, is by injecting side effects into the stream, like so: + +```swift +let searchString = textField.rac_textSignal() + .toSignalProducer() + .map { text in text as! String } + .throttle(0.5, onScheduler: QueueScheduler.mainQueueScheduler) + .on(event: { print ($0) }) // the side effect +``` + +This will print the stream's [events][Events], while preserving the original stream behaviour. Both [`SignalProducer`][Signal producers] +and [`Signal`][Signals] provide the `logEvents` operator, that will do this automatically for you: + +```swift +let searchString = textField.rac_textSignal() + .toSignalProducer() + .map { text in text as! String } + .throttle(0.5, onScheduler: QueueScheduler.mainQueueScheduler) + .logEvents() +``` + +For more information and advance usage, check the [Debugging Techniques](Documentation/DebuggingTechniques.md) document. + +## How does ReactiveSwift relate to Rx? + +ReactiveCocoa was originally inspired, and therefore heavily influenced, by +Microsoft’s [Reactive +Extensions](https://msdn.microsoft.com/en-us/data/gg577609.aspx) (Rx) library. There are many ports of Rx, including [RxSwift](https://github.com/ReactiveX/RxSwift), but ReactiveCocoa is _intentionally_ not a direct port. + +**Where ReactiveSwift differs from Rx**, it is usually to: + + * Create a simpler API + * Address common sources of confusion + * More closely match Cocoa conventions + +The following are some of the concrete differences, along with their rationales. + +### Naming + +In most versions of Rx, Streams over time are known as `Observable`s, which +parallels the `Enumerable` type in .NET. Additionally, most operations in Rx.NET +borrow names from [LINQ](https://msdn.microsoft.com/en-us/library/bb397926.aspx), +which uses terms reminiscent of relational databases, like `Select` and `Where`. + +**RAC is focused on matching Swift naming first and foremost**, with terms like +`map` and `filter` instead. Other naming differences are typically inspired by +significantly better alternatives from [Haskell](https://www.haskell.org) or +[Elm](http://elm-lang.org) (which is the primary source for the “signal” +terminology). + +### Signals and Signal Producers (“hot” and “cold” observables) + +One of the most confusing aspects of Rx is that of [“hot”, “cold”, and “warm” +observables](http://www.introtorx.com/content/v1.0.10621.0/14_HotAndColdObservables.html) (event streams). + +In short, given just a method or function declaration like this, in C#: + +```csharp +IObservable Search(string query) +``` + +… it is **impossible to tell** whether subscribing to (observing) that +`IObservable` will involve side effects. If it _does_ involve side effects, it’s +also impossible to tell whether _each subscription_ has a side effect, or if only +the first one does. + +This example is contrived, but it demonstrates **a real, pervasive problem** +that makes it extremely hard to understand Rx code (and pre-3.0 ReactiveCocoa +code) at a glance. + +[ReactiveCocoa 3.0][https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/CHANGELOG.md] has solved this problem by distinguishing side +effects with the separate [`Signal`][Signals] and [`SignalProducer`][Signal producers] types. Although this +means there’s another type to learn about, it improves code clarity and helps +communicates intent much better. + +In other words, **ReactiveSwift’s changes here are [simple, not +easy](http://www.infoq.com/presentations/Simple-Made-Easy)**. + +### Typed errors + +When [signals][] and [signal producers][] are allowed to [fail][Events] in ReactiveSwift, +the kind of error must be specified in the type system. For example, +`Signal` is a signal of integer values that may fail with an error +of type `NSError`. + +More importantly, RAC allows the special type `NoError` to be used instead, +which _statically guarantees_ that an event stream is not allowed to send a +failure. **This eliminates many bugs caused by unexpected failure events.** + +In Rx systems with types, event streams only specify the type of their +values—not the type of their errors—so this sort of guarantee is impossible. + +### UI programming + +Rx is basically agnostic as to how it’s used. Although UI programming with Rx is +very common, it has few features tailored to that particular case. + +ReactiveSwift takes a lot of inspiration from [ReactiveUI](http://reactiveui.net/), +including the basis for [Actions][]. + +Unlike ReactiveUI, which unfortunately cannot directly change Rx to make it more +friendly for UI programming, **ReactiveSwift has been improved many times +specifically for this purpose**—even when it means diverging further from Rx. + +## Getting started + +ReactiveSwift supports `OS X 10.9+`, `iOS 8.0+`, `watchOS 2.0`, and `tvOS 9.0`. + +To add RAC to your application: + + 1. Add the ReactiveSwift repository as a + [submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules) of your + application’s repository. + 1. Run `git submodule update --init --recursive` from within the ReactiveSwift folder. + 1. Drag and drop `ReactiveSwift.xcodeproj` and `Carthage/Checkouts/Result/Result.xcodeproj` + into your application’s Xcode project or workspace. + 1. On the “General” tab of your application target’s settings, add + `ReactiveSwift.framework` and `Result.framework` to the “Embedded Binaries” section. + 1. If your application target does not contain Swift code at all, you should also + set the `EMBEDDED_CONTENT_CONTAINS_SWIFT` build setting to “Yes”. + +Or, if you’re using [Carthage](https://github.com/Carthage/Carthage), simply add +ReactiveSwift to your `Cartfile`: + +``` +github "ReactiveCocoa/ReactiveSwift" +``` +Make sure to add both `ReactiveSwift.framework` and `Result.framework` to "Linked Frameworks and Libraries" and "copy-frameworks" Build Phases. + +Once you’ve set up your project, check out the [Framework Overview][] for +a tour of ReactiveSwift’s concepts, and the [Basic Operators][] for some +introductory examples of using it. + +## Playground + +We also provide a great Playground, so you can get used to ReactiveCocoa's operators. In order to start using it: + + 1. Clone the ReactiveSwift repository. + 1. Retrieve the project dependencies using one of the following terminal commands from the ReactiveSwift project root directory: + - `git submodule update --init --recursive` **OR**, if you have [Carthage](https://github.com/Carthage/Carthage) installed + - `carthage checkout` + 1. Open `ReactiveSwift.xcworkspace` + 1. Build `Result-Mac` scheme + 1. Build `ReactiveSwift-macOS` scheme + 1. Finally open the `ReactiveSwift.playground` + 1. Choose `View > Show Debug Area` + +[Actions]: Documentation/FrameworkOverview.md#actions +[Basic Operators]: Documentation/BasicOperators.md +[CHANGELOG]: CHANGELOG.md +[Code]: ReactiveCocoa +[Documentation]: Documentation +[Events]: Documentation/FrameworkOverview.md#events +[Framework Overview]: Documentation/FrameworkOverview.md +[Schedulers]: Documentation/FrameworkOverview.md#schedulers +[Signal producers]: Documentation/FrameworkOverview.md#signal-producers +[Signals]: Documentation/FrameworkOverview.md#signals +[Swift API]: ReactiveCocoa/Swift +[flatMapLatest]: Documentation/BasicOperators.md#switching-to-the-latest +[retry]: Documentation/BasicOperators.md#retrying diff --git a/README.md b/README.md index d83ac67bdc..b8dc9c0490 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,14 @@ [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![GitHub release](https://img.shields.io/github/release/ReactiveCocoa/ReactiveCocoa.svg)](https://github.com/ReactiveCocoa/ReactiveCocoa/releases) ![Swift 3.0.x](https://img.shields.io/badge/Swift-3.0.x-orange.svg) ![platforms](https://img.shields.io/badge/platforms-iOS%20%7C%20OS%20X%20%7C%20watchOS%20%7C%20tvOS%20-lightgrey.svg) -ReactiveCocoa (RAC) is a Cocoa framework inspired by [Functional Reactive Programming](https://en.wikipedia.org/wiki/Functional_reactive_programming). It provides APIs for composing and transforming **streams of values over time**. +ReactiveCocoa (RAC) is a Cocoa framework built on top of [ReactiveSwift][]. It +provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. 1. [Introduction](#introduction) 1. [Example: online search](#example-online-search) 1. [Objective-C and Swift](#objective-c-and-swift) 1. [How does ReactiveCocoa relate to Rx?](#how-does-reactivecocoa-relate-to-rx) 1. [Getting started](#getting-started) - 1. [Playground](#playground) If you’re already familiar with functional reactive programming or what ReactiveCocoa is about, check out the [Documentation][] folder for more in-depth @@ -49,170 +49,7 @@ Because all of these different mechanisms can be represented in the _same_ way, it’s easy to declaratively chain and combine them together, with less spaghetti code and state to bridge the gap. -For more information about the concepts in ReactiveCocoa, see the [Framework -Overview][]. - -## Example: online search - -Let’s say you have a text field, and whenever the user types something into it, -you want to make a network request which searches for that query. - -#### Observing text edits - -The first step is to observe edits to the text field, using a RAC extension to -`UITextField` specifically for this purpose: - -```swift -let searchStrings = textField.rac_textSignal() - .toSignalProducer() - .map { text in text as! String } -``` - -This gives us a [signal producer][Signal producers] which sends -values of type `String`. _(The cast is [currently -necessary](https://github.com/ReactiveCocoa/ReactiveCocoa/issues/2182) to bridge -this extension method from Objective-C.)_ - -#### Making network requests - -With each string, we want to execute a network request. Luckily, RAC offers an -`NSURLSession` extension for doing exactly that: - -```swift -let searchResults = searchStrings - .flatMap(.Latest) { (query: String) -> SignalProducer<(NSData, NSURLResponse), NSError> in - let URLRequest = self.searchRequestWithEscapedQuery(query) - return NSURLSession.sharedSession().rac_dataWithRequest(URLRequest) - } - .map { (data, URLResponse) -> String in - let string = String(data: data, encoding: NSUTF8StringEncoding)! - return self.parseJSONResultsFromString(string) - } - .observeOn(UIScheduler()) -``` - -This has transformed our producer of `String`s into a producer of `Array`s -containing the search results, which will be forwarded on the main thread -(thanks to the [`UIScheduler`][Schedulers]). - -Additionally, [`flatMap(.Latest)`][flatMapLatest] here ensures that _only one search_—the -latest—is allowed to be running. If the user types another character while the -network request is still in flight, it will be cancelled before starting a new -one. Just think of how much code that would take to do by hand! - -#### Receiving the results - -This won’t actually execute yet, because producers must be _started_ in order to -receive the results (which prevents doing work when the results are never used). -That’s easy enough: - -```swift -searchResults.startWithNext { results in - print("Search results: \(results)") -} -``` - -Here, we watch for the `Next` [event][Events], which contains our results, and -just log them to the console. This could easily do something else instead, like -update a table view or a label on screen. - -#### Handling failures - -In this example so far, any network error will generate a `Failed` -[event][Events], which will terminate the event stream. Unfortunately, this -means that future queries won’t even be attempted. - -To remedy this, we need to decide what to do with failures that occur. The -quickest solution would be to log them, then ignore them: - -```swift - .flatMap(.Latest) { (query: String) -> SignalProducer<(NSData, NSURLResponse), NSError> in - let URLRequest = self.searchRequestWithEscapedQuery(query) - - return NSURLSession.sharedSession() - .rac_dataWithRequest(URLRequest) - .flatMapError { error in - print("Network error occurred: \(error)") - return SignalProducer.empty - } - } -``` - -By replacing failures with the `empty` event stream, we’re able to effectively -ignore them. - -However, it’s probably more appropriate to retry at least a couple of times -before giving up. Conveniently, there’s a [`retry`][retry] operator to do exactly that! - -Our improved `searchResults` producer might look like this: - -```swift -let searchResults = searchStrings - .flatMap(.Latest) { (query: String) -> SignalProducer<(NSData, NSURLResponse), NSError> in - let URLRequest = self.searchRequestWithEscapedQuery(query) - - return NSURLSession.sharedSession() - .rac_dataWithRequest(URLRequest) - .retry(2) - .flatMapError { error in - print("Network error occurred: \(error)") - return SignalProducer.empty - } - } - .map { (data, URLResponse) -> String in - let string = String(data: data, encoding: NSUTF8StringEncoding)! - return self.parseJSONResultsFromString(string) - } - .observeOn(UIScheduler()) -``` - -#### Throttling requests - -Now, let’s say you only want to actually perform the search periodically, -to minimize traffic. - -ReactiveCocoa has a declarative `throttle` operator that we can apply to our -search strings: - -```swift -let searchStrings = textField.rac_textSignal() - .toSignalProducer() - .map { text in text as! String } - .throttle(0.5, onScheduler: QueueScheduler.mainQueueScheduler) -``` - -This prevents values from being sent less than 0.5 seconds apart. - -To do this manually would require significant state, and end up much harder to -read! With ReactiveCocoa, we can use just one operator to incorporate _time_ into -our event stream. - -#### Debugging event streams - -Due to its nature, a stream's stack trace might have dozens of frames, which, more often than not, can make debugging a very frustrating activity. -A naive way of debugging, is by injecting side effects into the stream, like so: - -```swift -let searchString = textField.rac_textSignal() - .toSignalProducer() - .map { text in text as! String } - .throttle(0.5, onScheduler: QueueScheduler.mainQueueScheduler) - .on(event: { print ($0) }) // the side effect -``` - -This will print the stream's [events][Events], while preserving the original stream behaviour. Both [`SignalProducer`][Signal producers] -and [`Signal`][Signals] provide the `logEvents` operator, that will do this automatically for you: - -```swift -let searchString = textField.rac_textSignal() - .toSignalProducer() - .map { text in text as! String } - .throttle(0.5, onScheduler: QueueScheduler.mainQueueScheduler) - .logEvents() -``` - -For more information and advance usage, check the [Debugging Techniques](Documentation/DebuggingTechniques.md) document. - +For more information about the concepts in ReactiveCocoa, see [ReactiveSwift][]. ## Objective-C and Swift @@ -230,87 +67,6 @@ this API, please consult our [legacy documentation][]. **We highly recommend that all new projects use the Swift API.** -## How does ReactiveCocoa relate to Rx? - -ReactiveCocoa was originally inspired, and therefore heavily influenced, by -Microsoft’s [Reactive -Extensions](https://msdn.microsoft.com/en-us/data/gg577609.aspx) (Rx) library. There are many ports of Rx, including [RxSwift](https://github.com/ReactiveX/RxSwift), but ReactiveCocoa is _intentionally_ not a direct port. - -**Where RAC differs from Rx**, it is usually to: - - * Create a simpler API - * Address common sources of confusion - * More closely match Cocoa conventions - -The following are some of the concrete differences, along with their rationales. - -### Naming - -In most versions of Rx, Streams over time are known as `Observable`s, which -parallels the `Enumerable` type in .NET. Additionally, most operations in Rx.NET -borrow names from [LINQ](https://msdn.microsoft.com/en-us/library/bb397926.aspx), -which uses terms reminiscent of relational databases, like `Select` and `Where`. - -**RAC is focused on matching Swift naming first and foremost**, with terms like -`map` and `filter` instead. Other naming differences are typically inspired by -significantly better alternatives from [Haskell](https://www.haskell.org) or -[Elm](http://elm-lang.org) (which is the primary source for the “signal” -terminology). - -### Signals and Signal Producers (“hot” and “cold” observables) - -One of the most confusing aspects of Rx is that of [“hot”, “cold”, and “warm” -observables](http://www.introtorx.com/content/v1.0.10621.0/14_HotAndColdObservables.html) (event streams). - -In short, given just a method or function declaration like this, in C#: - -```csharp -IObservable Search(string query) -``` - -… it is **impossible to tell** whether subscribing to (observing) that -`IObservable` will involve side effects. If it _does_ involve side effects, it’s -also impossible to tell whether _each subscription_ has a side effect, or if only -the first one does. - -This example is contrived, but it demonstrates **a real, pervasive problem** -that makes it extremely hard to understand Rx code (and pre-3.0 ReactiveCocoa -code) at a glance. - -[ReactiveCocoa 3.0][CHANGELOG] has solved this problem by distinguishing side -effects with the separate [`Signal`][Signals] and [`SignalProducer`][Signal producers] types. Although this -means there’s another type to learn about, it improves code clarity and helps -communicates intent much better. - -In other words, **ReactiveCocoa’s changes here are [simple, not -easy](http://www.infoq.com/presentations/Simple-Made-Easy)**. - -### Typed errors - -When [signals][] and [signal producers][] are allowed to [fail][Events] in ReactiveCocoa, -the kind of error must be specified in the type system. For example, -`Signal` is a signal of integer values that may fail with an error -of type `NSError`. - -More importantly, RAC allows the special type `NoError` to be used instead, -which _statically guarantees_ that an event stream is not allowed to send a -failure. **This eliminates many bugs caused by unexpected failure events.** - -In Rx systems with types, event streams only specify the type of their -values—not the type of their errors—so this sort of guarantee is impossible. - -### UI programming - -Rx is basically agnostic as to how it’s used. Although UI programming with Rx is -very common, it has few features tailored to that particular case. - -RAC takes a lot of inspiration from [ReactiveUI](http://reactiveui.net/), -including the basis for [Actions][]. - -Unlike ReactiveUI, which unfortunately cannot directly change Rx to make it more -friendly for UI programming, **ReactiveCocoa has been improved many times -specifically for this purpose**—even when it means diverging further from Rx. - ## Getting started ReactiveCocoa supports `OS X 10.9+`, `iOS 8.0+`, `watchOS 2.0`, and `tvOS 9.0`. @@ -321,10 +77,13 @@ To add RAC to your application: [submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules) of your application’s repository. 1. Run `git submodule update --init --recursive` from within the ReactiveCocoa folder. - 1. Drag and drop `ReactiveCocoa.xcodeproj` and `Carthage/Checkouts/Result/Result.xcodeproj` - into your application’s Xcode project or workspace. + 1. Drag and drop `ReactiveCocoa.xcodeproj`, + `Carthage/Checkouts/ReactiveSwift/ReactiveSwift.xcodeproj`, and + `Carthage/Checkouts/Result/Result.xcodeproj` into your application’s Xcode + project or workspace. 1. On the “General” tab of your application target’s settings, add - `ReactiveCocoa.framework` and `Result.framework` to the “Embedded Binaries” section. + `ReactiveCocoa.framework`, `ReactiveSwift.framework`, and `Result.framework` + to the “Embedded Binaries” section. 1. If your application target does not contain Swift code at all, you should also set the `EMBEDDED_CONTENT_CONTAINS_SWIFT` build setting to “Yes”. @@ -334,7 +93,7 @@ ReactiveCocoa to your `Cartfile`: ``` github "ReactiveCocoa/ReactiveCocoa" ``` -Make sure to add both `ReactiveCocoa.framework` and `Result.framework` to "Linked Frameworks and Libraries" and "copy-frameworks" Build Phases. +Make sure to add `ReactiveCocoa.framework`, `ReactiveSwift`, and `Result.framework` to "Linked Frameworks and Libraries" and "copy-frameworks" Build Phases. If you would prefer to use [CocoaPods](https://cocoapods.org), there are some [unofficial podspecs](https://github.com/CocoaPods/Specs/tree/master/Specs/ReactiveCocoa) @@ -344,33 +103,16 @@ Once you’ve set up your project, check out the [Framework Overview][] for a tour of ReactiveCocoa’s concepts, and the [Basic Operators][] for some introductory examples of using it. -## Playground - -We also provide a great Playground, so you can get used to ReactiveCocoa's operators. In order to start using it: - - 1. Clone the ReactiveCocoa repository. - 1. Retrieve the project dependencies using one of the following terminal commands from the ReactiveCocoa project root directory: - - `git submodule update --init --recursive` **OR**, if you have [Carthage](https://github.com/Carthage/Carthage) installed - - `carthage checkout` - 1. Open `ReactiveCocoa.xcworkspace` - 1. Build `Result-Mac` scheme - 1. Build `ReactiveCocoa-macOS` scheme - 1. Finally open the `ReactiveCocoa.playground` - 1. Choose `View > Show Debug Area` - +[ReactiveSwift]: https://github.com/ReactiveCocoa/ReactiveSwift [Actions]: Documentation/FrameworkOverview.md#actions [Basic Operators]: Documentation/BasicOperators.md [CHANGELOG]: CHANGELOG.md [Code]: ReactiveCocoa [Documentation]: Documentation -[Events]: Documentation/FrameworkOverview.md#events [Framework Overview]: Documentation/FrameworkOverview.md [Legacy Documentation]: Documentation/Legacy [Objective-C API]: ReactiveCocoa/Objective-C [Objective-C Bridging]: Documentation/ObjectiveCBridging.md -[Schedulers]: Documentation/FrameworkOverview.md#schedulers [Signal producers]: Documentation/FrameworkOverview.md#signal-producers [Signals]: Documentation/FrameworkOverview.md#signals [Swift API]: ReactiveCocoa/Swift -[flatMapLatest]: Documentation/BasicOperators.md#switching-to-the-latest -[retry]: Documentation/BasicOperators.md#retrying From 55b8823e0f5746c1a58cdf5f3ab3a35481c767fb Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Wed, 17 Aug 2016 08:23:36 -0400 Subject: [PATCH 0340/1028] Make TestError explicitly internal --- ReactiveCocoaTests/Swift/TestError.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoaTests/Swift/TestError.swift b/ReactiveCocoaTests/Swift/TestError.swift index 275aef893a..43e6905648 100644 --- a/ReactiveCocoaTests/Swift/TestError.swift +++ b/ReactiveCocoaTests/Swift/TestError.swift @@ -10,13 +10,13 @@ import ReactiveCocoa import ReactiveSwift import Result -enum TestError: Int { +internal enum TestError: Int { case `default` = 0 case error1 = 1 case error2 = 2 } -extension TestError: Error { +internal extension TestError: Error { } From 2436352c1a39020c755982208e9ab6db72625665 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Wed, 17 Aug 2016 19:21:56 -0400 Subject: [PATCH 0341/1028] This is ReactiveSwift --- ReactiveSwift.xcodeproj/project.pbxproj | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/ReactiveSwift.xcodeproj/project.pbxproj b/ReactiveSwift.xcodeproj/project.pbxproj index 3baddc24d0..7629a15004 100644 --- a/ReactiveSwift.xcodeproj/project.pbxproj +++ b/ReactiveSwift.xcodeproj/project.pbxproj @@ -208,7 +208,7 @@ /* Begin PBXFileReference section */ 02D260291C1D6DAF003ACC61 /* SignalLifetimeSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalLifetimeSpec.swift; sourceTree = ""; }; - 4A0E10FE1D2A92720065D310 /* Lifetime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Lifetime.swift; path = Lifetime.swift; sourceTree = ""; }; + 4A0E10FE1D2A92720065D310 /* Lifetime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Lifetime.swift; sourceTree = ""; }; 4A0E11031D2A95200065D310 /* LifetimeSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LifetimeSpec.swift; sourceTree = ""; }; 57A4D2411BA13D7A00F7D4B1 /* ReactiveSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Application.xcconfig"; sourceTree = ""; }; @@ -228,10 +228,10 @@ C79B647B1CD52E23003F2376 /* EventLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventLogger.swift; sourceTree = ""; }; CA6F284F1C52626B001879D2 /* FlattenSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlattenSpec.swift; sourceTree = ""; }; CDC42E2E1AE7AB8B00965373 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D00004081A46864E000E7D41 /* TupleExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TupleExtensions.swift; path = TupleExtensions.swift; sourceTree = ""; }; + D00004081A46864E000E7D41 /* TupleExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TupleExtensions.swift; sourceTree = ""; }; D021671C1A6CD50500987861 /* ActionSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ActionSpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; D037672B19EDA75D00A782A9 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Quick.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D03B4A3C19F4C39A009E02AC /* FoundationExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FoundationExtensions.swift; path = FoundationExtensions.swift; sourceTree = ""; }; + D03B4A3C19F4C39A009E02AC /* FoundationExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoundationExtensions.swift; sourceTree = ""; }; D04725EA19E49ED7006002AA /* ReactiveSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D04725EE19E49ED7006002AA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D04725EF19E49ED7006002AA /* ReactiveSwift.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ReactiveSwift.h; sourceTree = ""; }; @@ -258,10 +258,10 @@ D047263B19E49FE8006002AA /* Mac-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-StaticLibrary.xcconfig"; sourceTree = ""; }; D047263C19E49FE8006002AA /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; D05E662419EDD82000904ACA /* Nimble.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Nimble.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D08C54AF1A69A2AC00AD8286 /* Action.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Action.swift; path = Action.swift; sourceTree = ""; }; - D08C54B01A69A2AC00AD8286 /* Property.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Property.swift; path = Property.swift; sourceTree = ""; }; - D08C54B11A69A2AC00AD8286 /* Signal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Signal.swift; path = Signal.swift; sourceTree = ""; }; - D08C54B21A69A2AC00AD8286 /* SignalProducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SignalProducer.swift; path = SignalProducer.swift; sourceTree = ""; }; + D08C54AF1A69A2AC00AD8286 /* Action.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Action.swift; sourceTree = ""; }; + D08C54B01A69A2AC00AD8286 /* Property.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Property.swift; sourceTree = ""; }; + D08C54B11A69A2AC00AD8286 /* Signal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Signal.swift; sourceTree = ""; }; + D08C54B21A69A2AC00AD8286 /* SignalProducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignalProducer.swift; sourceTree = ""; }; D08C54B51A69A3DB00AD8286 /* Event.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Event.swift; sourceTree = ""; }; D0A226071A72E0E900D33B74 /* SignalSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SignalSpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; D0A2260A1A72E6C500D33B74 /* SignalProducerSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SignalProducerSpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; @@ -276,7 +276,7 @@ D0C312F219EF2A7700984962 /* SchedulerSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchedulerSpec.swift; sourceTree = ""; }; D8024DB11B2E1BB0005E6B9A /* SignalProducerLiftingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SignalProducerLiftingSpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; D8170FC01B100EBC004192AD /* FoundationExtensionsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoundationExtensionsSpec.swift; sourceTree = ""; }; - D85C65291C0D84C7005A77AD /* Flatten.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Flatten.swift; path = Flatten.swift; sourceTree = ""; }; + D85C65291C0D84C7005A77AD /* Flatten.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Flatten.swift; sourceTree = ""; }; D871D69E1B3B29A40070F16C /* Optional.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Optional.swift; sourceTree = ""; }; EBCC7DBB1BBF010C00A2AE92 /* Observer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Observer.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; /* End PBXFileReference section */ @@ -381,7 +381,6 @@ D08C54B21A69A2AC00AD8286 /* SignalProducer.swift */, ); name = Signals; - path = ""; sourceTree = ""; }; D03B4A3A19F4C26D009E02AC /* Internal Utilities */ = { @@ -390,7 +389,6 @@ D00004081A46864E000E7D41 /* TupleExtensions.swift */, ); name = "Internal Utilities"; - path = ""; sourceTree = ""; }; D03B4A3B19F4C281009E02AC /* Extensions */ = { @@ -399,14 +397,13 @@ D03B4A3C19F4C39A009E02AC /* FoundationExtensions.swift */, ); name = Extensions; - path = ""; sourceTree = ""; }; D04725E019E49ED7006002AA = { isa = PBXGroup; children = ( D04725EC19E49ED7006002AA /* ReactiveSwift */, - D04725F919E49ED7006002AA /* ReactiveCocoaTests */, + D04725F919E49ED7006002AA /* ReactiveSwiftTests */, D047262519E49FE8006002AA /* Configuration */, D04725EB19E49ED7006002AA /* Products */, ); @@ -457,7 +454,7 @@ name = "Supporting Files"; sourceTree = ""; }; - D04725F919E49ED7006002AA /* ReactiveCocoaTests */ = { + D04725F919E49ED7006002AA /* ReactiveSwiftTests */ = { isa = PBXGroup; children = ( D021671C1A6CD50500987861 /* ActionSpec.swift */, @@ -477,7 +474,6 @@ C79B64731CD38B2B003F2376 /* TestLogger.swift */, D04725FA19E49ED7006002AA /* Supporting Files */, ); - name = ReactiveCocoaTests; path = ReactiveSwiftTests; sourceTree = ""; }; From 33a8addbc5e0b0fe80dcfac969c617b74baff6c8 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Wed, 17 Aug 2016 19:22:46 -0400 Subject: [PATCH 0342/1028] Make this TestError internal as well --- ReactiveSwiftTests/TestError.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ReactiveSwiftTests/TestError.swift b/ReactiveSwiftTests/TestError.swift index 39125cdfed..87824a691e 100644 --- a/ReactiveSwiftTests/TestError.swift +++ b/ReactiveSwiftTests/TestError.swift @@ -9,13 +9,13 @@ import ReactiveSwift import Result -enum TestError: Int { +internal enum TestError: Int { case `default` = 0 case error1 = 1 case error2 = 2 } -extension TestError: Error { +internal extension TestError: Error { } From 30a17347d0fe01d0d10194555b620ea31efa857e Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Tue, 23 Aug 2016 09:14:24 -0400 Subject: [PATCH 0343/1028] Fix .travis.yml scheme names --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index f4a4ebeeeb..c2bee6f281 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,17 +38,17 @@ matrix: - XCODE_ACTION="build test" - XCODE_DESTINATION="arch=x86_64" - XCODE_PLAYGROUND_TARGET="x86_64-apple-macosx10.10" - - xcode_scheme: ReactiveSwift-macOS + - xcode_scheme: ReactiveSwift-iOS env: - XCODE_SDK=iphonesimulator - XCODE_ACTION="build-for-testing test-without-building" - XCODE_DESTINATION="platform=iOS Simulator,name=iPhone 6s" - - xcode_scheme: ReactiveSwift-macOS + - xcode_scheme: ReactiveSwift-tvOS env: - XCODE_SDK=appletvsimulator - XCODE_ACTION="build-for-testing test-without-building" - XCODE_DESTINATION="platform=tvOS Simulator,name=Apple TV 1080p" - - xcode_scheme: ReactiveSwift-macOS + - xcode_scheme: ReactiveSwift-watchOS env: - XCODE_SDK=watchsimulator - XCODE_ACTION=build From aebb6b2473008d925c2216c0872a861d95cde071 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Tue, 23 Aug 2016 09:25:02 -0400 Subject: [PATCH 0344/1028] Update Package.swift --- Package.swift | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/Package.swift b/Package.swift index 814e59be46..a21a71fd70 100644 --- a/Package.swift +++ b/Package.swift @@ -1,24 +1,11 @@ import PackageDescription -let excludes: [String] -#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) - excludes = [ - "Sources/Deprecations+Removals.swift", - "Sources/ObjectiveCBridging.swift" - ] -#else - excludes = [ - "Sources/Deprecations+Removals.swift", - "Sources/DynamicProperty.swift", - "Sources/NSObject+KeyValueObserving.swift", - "Sources/ObjectiveCBridging.swift" - ] -#endif - let package = Package( - name: "ReactiveCocoa", + name: "ReactiveSwift", dependencies: [ .Package(url: "https://github.com/antitypical/Result.git", "3.0.0-alpha.2") ], - exclude: excludes + exclude: [ + "Sources/Deprecations+Removals.swift", + ] ) From d5acfb76f4141b1427973f9e1adb0c488be6c509 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Tue, 23 Aug 2016 20:07:59 -0400 Subject: [PATCH 0345/1028] Use newer version of Result --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index a21a71fd70..4b15638cbe 100644 --- a/Package.swift +++ b/Package.swift @@ -3,7 +3,7 @@ import PackageDescription let package = Package( name: "ReactiveSwift", dependencies: [ - .Package(url: "https://github.com/antitypical/Result.git", "3.0.0-alpha.2") + .Package(url: "https://github.com/antitypical/Result.git", "3.0.0-alpha.3") ], exclude: [ "Sources/Deprecations+Removals.swift", From 6b308875b9ff476863f9726632b776acdab9f50a Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Tue, 23 Aug 2016 20:09:19 -0400 Subject: [PATCH 0346/1028] Update SwiftPM symlink --- Sources | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources b/Sources index c5c6e8cb28..2ebf1f0a4c 120000 --- a/Sources +++ b/Sources @@ -1 +1 @@ -ReactiveCocoa/Swift/ \ No newline at end of file +ReactiveSwift/ \ No newline at end of file From 98cb8bca52e735292d200147ad29cdd3afcea96f Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Thu, 25 Aug 2016 20:20:38 -0400 Subject: [PATCH 0347/1028] Split up Carthage job by platform --- .travis.yml | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c2bee6f281..0acd878ec0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -56,9 +56,27 @@ matrix: - script: - brew update - brew outdated carthage || brew upgrade carthage - - carthage build --no-skip-current + - carthage build --no-skip-current --platform mac env: - - JOB=CARTHAGE + - JOB=CARTHAGE-macOS + - script: + - brew update + - brew outdated carthage || brew upgrade carthage + - carthage build --no-skip-current --platform iOS + env: + - JOB=CARTHAGE-iOS + - script: + - brew update + - brew outdated carthage || brew upgrade carthage + - carthage build --no-skip-current --platform tvOS + env: + - JOB=CARTHAGE-tvOS + - script: + - brew update + - brew outdated carthage || brew upgrade carthage + - carthage build --no-skip-current --platform watchOS + env: + - JOB=CARTHAGE-watchOS - os: osx language: generic script: swift build From 6a9bc2f6c9638d6533956b3700b21f402b089b87 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Thu, 25 Aug 2016 20:24:22 -0400 Subject: [PATCH 0348/1028] Make Carthage builds verbose as well This should help with timeout issues on Travis. --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0acd878ec0..daec383285 100644 --- a/.travis.yml +++ b/.travis.yml @@ -56,25 +56,25 @@ matrix: - script: - brew update - brew outdated carthage || brew upgrade carthage - - carthage build --no-skip-current --platform mac + - carthage build --no-skip-current --platform mac --verbose env: - JOB=CARTHAGE-macOS - script: - brew update - brew outdated carthage || brew upgrade carthage - - carthage build --no-skip-current --platform iOS + - carthage build --no-skip-current --platform iOS --verbose env: - JOB=CARTHAGE-iOS - script: - brew update - brew outdated carthage || brew upgrade carthage - - carthage build --no-skip-current --platform tvOS + - carthage build --no-skip-current --platform tvOS --verbose env: - JOB=CARTHAGE-tvOS - script: - brew update - brew outdated carthage || brew upgrade carthage - - carthage build --no-skip-current --platform watchOS + - carthage build --no-skip-current --platform watchOS --verbose env: - JOB=CARTHAGE-watchOS - os: osx From 0d23a153d35694abeb8c9413da7a02d801e00f73 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Fri, 26 Aug 2016 08:31:24 -0400 Subject: [PATCH 0349/1028] Evidently verbose is a bad idea --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index daec383285..0acd878ec0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -56,25 +56,25 @@ matrix: - script: - brew update - brew outdated carthage || brew upgrade carthage - - carthage build --no-skip-current --platform mac --verbose + - carthage build --no-skip-current --platform mac env: - JOB=CARTHAGE-macOS - script: - brew update - brew outdated carthage || brew upgrade carthage - - carthage build --no-skip-current --platform iOS --verbose + - carthage build --no-skip-current --platform iOS env: - JOB=CARTHAGE-iOS - script: - brew update - brew outdated carthage || brew upgrade carthage - - carthage build --no-skip-current --platform tvOS --verbose + - carthage build --no-skip-current --platform tvOS env: - JOB=CARTHAGE-tvOS - script: - brew update - brew outdated carthage || brew upgrade carthage - - carthage build --no-skip-current --platform watchOS --verbose + - carthage build --no-skip-current --platform watchOS env: - JOB=CARTHAGE-watchOS - os: osx From c40bdebb9612b36ca320b15785475620ad8a92f4 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Sat, 3 Sep 2016 16:37:38 +0900 Subject: [PATCH 0350/1028] Switch back to Quick/Quick --- .gitmodules | 2 +- Cartfile.private | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/Quick | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitmodules b/.gitmodules index 50cdf7fcbb..52bd2aa4e6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,7 +3,7 @@ url = https://github.com/norio-nomura/Nimble.git [submodule "Carthage/Checkouts/Quick"] path = Carthage/Checkouts/Quick - url = https://github.com/norio-nomura/Quick.git + url = https://github.com/Quick/Quick.git [submodule "Carthage/Checkouts/xcconfigs"] path = Carthage/Checkouts/xcconfigs url = https://github.com/jspahrsummers/xcconfigs.git diff --git a/Cartfile.private b/Cartfile.private index e595107b31..4186a2364a 100644 --- a/Cartfile.private +++ b/Cartfile.private @@ -1,3 +1,3 @@ github "jspahrsummers/xcconfigs" "3d9d996" -github "norio-nomura/Quick" "nn-swift-3-compatibility" +github "Quick/Quick" "swift-3.0" github "norio-nomura/Nimble" "nn-swift-3-compatibility" diff --git a/Cartfile.resolved b/Cartfile.resolved index f2e6b9d31a..56d7f9e6c0 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,4 +1,4 @@ github "norio-nomura/Nimble" "3d82a185b49e8fc9f40e7b72feeb279c778f0935" -github "norio-nomura/Quick" "4a7f1d0a7db637c3ca15b8f198a516fbc5581f87" +github "Quick/Quick" "6110c50b518a72a24cd2505c8012fa785c3b1c8e" github "antitypical/Result" "3.0.0-alpha.4" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" diff --git a/Carthage/Checkouts/Quick b/Carthage/Checkouts/Quick index 4a7f1d0a7d..6110c50b51 160000 --- a/Carthage/Checkouts/Quick +++ b/Carthage/Checkouts/Quick @@ -1 +1 @@ -Subproject commit 4a7f1d0a7db637c3ca15b8f198a516fbc5581f87 +Subproject commit 6110c50b518a72a24cd2505c8012fa785c3b1c8e From 7e5a4faf8f712563c7122fcff75d75bb7d55f298 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Sat, 3 Sep 2016 18:25:49 +0900 Subject: [PATCH 0351/1028] Switch back to Quick/Nimble --- .gitmodules | 2 +- Cartfile.private | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/Nimble | 2 +- .../xcshareddata/xcschemes/ReactiveCocoa-Mac.xcscheme | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.gitmodules b/.gitmodules index 52bd2aa4e6..8283dfc1cf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "Carthage/Checkouts/Nimble"] path = Carthage/Checkouts/Nimble - url = https://github.com/norio-nomura/Nimble.git + url = https://github.com/Quick/Nimble.git [submodule "Carthage/Checkouts/Quick"] path = Carthage/Checkouts/Quick url = https://github.com/Quick/Quick.git diff --git a/Cartfile.private b/Cartfile.private index 4186a2364a..6c9286a58b 100644 --- a/Cartfile.private +++ b/Cartfile.private @@ -1,3 +1,3 @@ github "jspahrsummers/xcconfigs" "3d9d996" github "Quick/Quick" "swift-3.0" -github "norio-nomura/Nimble" "nn-swift-3-compatibility" +github "Quick/Nimble" "swift-3.0" diff --git a/Cartfile.resolved b/Cartfile.resolved index 56d7f9e6c0..1a7ad28ef7 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,4 +1,4 @@ -github "norio-nomura/Nimble" "3d82a185b49e8fc9f40e7b72feeb279c778f0935" +github "Quick/Nimble" "4b539a275d2714212ce598177d586680fc088927" github "Quick/Quick" "6110c50b518a72a24cd2505c8012fa785c3b1c8e" github "antitypical/Result" "3.0.0-alpha.4" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" diff --git a/Carthage/Checkouts/Nimble b/Carthage/Checkouts/Nimble index 3d82a185b4..4b539a275d 160000 --- a/Carthage/Checkouts/Nimble +++ b/Carthage/Checkouts/Nimble @@ -1 +1 @@ -Subproject commit 3d82a185b49e8fc9f40e7b72feeb279c778f0935 +Subproject commit 4b539a275d2714212ce598177d586680fc088927 diff --git a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-Mac.xcscheme b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-Mac.xcscheme index 25ac5b0747..861e7668a6 100644 --- a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-Mac.xcscheme +++ b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-Mac.xcscheme @@ -44,7 +44,7 @@ BuildableIdentifier = "primary" BlueprintIdentifier = "1F925EAC195C0D6300ED456B" BuildableName = "Nimble.framework" - BlueprintName = "Nimble-OSX" + BlueprintName = "Nimble-macOS" ReferencedContainer = "container:Carthage/Checkouts/Nimble/Nimble.xcodeproj"> From 3f6f2043ac73a5d26f9008fac5ed34b525a88c16 Mon Sep 17 00:00:00 2001 From: Eimantas Vaiciunas Date: Sun, 4 Sep 2016 11:04:14 +0300 Subject: [PATCH 0352/1028] Fix casing for enum values in markup --- ReactiveCocoa/Swift/Action.swift | 4 ++-- ReactiveCocoa/Swift/Event.swift | 2 +- ReactiveCocoa/Swift/Signal.swift | 20 ++++++++-------- ReactiveCocoa/Swift/SignalProducer.swift | 30 ++++++++++++------------ 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/ReactiveCocoa/Swift/Action.swift b/ReactiveCocoa/Swift/Action.swift index 2f2b52e159..0e7f5f3d75 100644 --- a/ReactiveCocoa/Swift/Action.swift +++ b/ReactiveCocoa/Swift/Action.swift @@ -100,7 +100,7 @@ public final class Action { /// with the given input, then forward the results upon the produced Signal. /// /// - note: If the action is disabled when the returned SignalProducer is - /// started, the produced signal will send `ActionError.NotEnabled`, + /// started, the produced signal will send `ActionError.disabled`, /// and nothing will be sent upon `values` or `errors` for that /// particular signal. /// @@ -158,7 +158,7 @@ public protocol ActionProtocol { /// with the given input, then forward the results upon the produced Signal. /// /// - note: If the action is disabled when the returned SignalProducer is - /// started, the produced signal will send `ActionError.NotEnabled`, + /// started, the produced signal will send `ActionError.disabled`, /// and nothing will be sent upon `values` or `errors` for that /// particular signal. /// diff --git a/ReactiveCocoa/Swift/Event.swift b/ReactiveCocoa/Swift/Event.swift index 3d6d94f6ba..6509f5d226 100644 --- a/ReactiveCocoa/Swift/Event.swift +++ b/ReactiveCocoa/Swift/Event.swift @@ -9,7 +9,7 @@ /// Represents a signal event. /// /// Signals must conform to the grammar: -/// `Next* (Failed | Completed | Interrupted)?` +/// `next* (failed | completed | interrupted)?` public enum Event { /// A value provided by the signal. case next(Value) diff --git a/ReactiveCocoa/Swift/Signal.swift b/ReactiveCocoa/Swift/Signal.swift index 9c13cd1e8c..fdf0226124 100644 --- a/ReactiveCocoa/Swift/Signal.swift +++ b/ReactiveCocoa/Swift/Signal.swift @@ -224,8 +224,8 @@ extension SignalProtocol { /// /// - parameters: /// - result: A closure that accepts instance of `Result` - /// enum that contains either a `Success(Value)` or - /// `Failure` case. + /// enum that contains either a `.success(Value)` or + /// `.failure` case. /// /// - returns: An optional `Disposable` which can be used to stop the /// invocation of the callback. Disposing of the Disposable will @@ -1367,14 +1367,14 @@ extension SignalProtocol { } } - /// Apply `operation` to values from `self` with `Success`ful results - /// forwarded on the returned signal and `Failure`s sent as failed events. + /// Apply `operation` to values from `self` with `success`ful results + /// forwarded on the returned signal and `failure`s sent as failed events. /// /// - parameters: /// - operation: A closure that accepts a value and returns a `Result`. /// - /// - returns: A signal that receives `Success`ful `Result` as `next` event - /// and `Failure` as failed event. + /// - returns: A signal that receives `success`ful `Result` as `next` event + /// and `failure` as failed event. public func attempt(_ operation: @escaping (Value) -> Result<(), Error>) -> Signal { return attemptMap { value in return operation(value).map { @@ -1383,15 +1383,15 @@ extension SignalProtocol { } } - /// Apply `operation` to values from `self` with `Success`ful results mapped - /// on the returned signal and `Failure`s sent as failed events. + /// Apply `operation` to values from `self` with `success`ful results mapped + /// on the returned signal and `failure`s sent as failed events. /// /// - parameters: /// - operation: A closure that accepts a value and returns a result of - /// a mapped value as `Success`. + /// a mapped value as `success`. /// /// - returns: A signal that sends mapped values from `self` if returned - /// `Result` is `Success`ful, failed events otherwise. + /// `Result` is `success`ful, `failed` events otherwise. public func attemptMap(_ operation: @escaping (Value) -> Result) -> Signal { return Signal { observer in self.observe { event in diff --git a/ReactiveCocoa/Swift/SignalProducer.swift b/ReactiveCocoa/Swift/SignalProducer.swift index 6a70eeecb1..0fc75e74fe 100644 --- a/ReactiveCocoa/Swift/SignalProducer.swift +++ b/ReactiveCocoa/Swift/SignalProducer.swift @@ -81,8 +81,8 @@ public struct SignalProducer { /// /// - parameters: /// - result: A `Result` instance that will send either `next` event if - /// `result` is `Success`ful or `failed` event if `result` is a - /// `Failure`. + /// `result` is `success`ful or `failed` event if `result` is a + /// `failure`. public init(result: Result) { switch result { case let .success(value): @@ -147,9 +147,9 @@ public struct SignalProducer { /// - parameters: /// - operation: A closure that returns instance of `Result`. /// - /// - returns: A `SignalProducer` that will forward `Success`ful `result` as + /// - returns: A `SignalProducer` that will forward `success`ful `result` as /// `next` event and then complete or `failed` event if `result` - /// is a `Failure`. + /// is a `failure`. public static func attempt(_ operation: @escaping () -> Result) -> SignalProducer { return self.init { observer, disposable in operation().analysis(ifSuccess: { value in @@ -270,8 +270,8 @@ extension SignalProducerProtocol { /// received. /// /// - parameters: - /// - result: A closure that accepts a `result` that contains a `Success` - /// case for `next` events or `Failure` case for `failed` event. + /// - result: A closure that accepts a `result` that contains a `.success` + /// case for `next` events or `.failure` case for `failed` event. /// /// - returns: A Disposable which can be used to interrupt the work /// associated with the Signal, and prevent any future callbacks @@ -1029,28 +1029,28 @@ extension SignalProducerProtocol { return lift(Signal.zip(with:))(other) } - /// Apply `operation` to values from `self` with `Success`ful results - /// forwarded on the returned producer and `Failure`s sent as `failed` + /// Apply `operation` to values from `self` with `success`ful results + /// forwarded on the returned producer and `failure`s sent as `failed` /// events. /// /// - parameters: /// - operation: A closure that accepts a value and returns a `Result`. /// - /// - returns: A producer that receives `Success`ful `Result` as `next` - /// event and `Failure` as `failed` event. + /// - returns: A producer that receives `success`ful `Result` as `next` + /// event and `failure` as `failed` event. public func attempt(operation: @escaping (Value) -> Result<(), Error>) -> SignalProducer { return lift { $0.attempt(operation) } } - /// Apply `operation` to values from `self` with `Success`ful results - /// mapped on the returned producer and `Failure`s sent as `failed` events. + /// Apply `operation` to values from `self` with `success`ful results + /// mapped on the returned producer and `failure`s sent as `failed` events. /// /// - parameters: /// - operation: A closure that accepts a value and returns a result of - /// a mapped value as `Success`. + /// a mapped value as `success`. /// /// - returns: A producer that sends mapped values from `self` if returned - /// `Result` is `Success`ful, `failed` events otherwise. + /// `Result` is `success`ful, `failed` events otherwise. public func attemptMap(_ operation: @escaping (Value) -> Result) -> SignalProducer { return lift { $0.attemptMap(operation) } } @@ -1671,7 +1671,7 @@ extension SignalProducerProtocol { /// When a completion or error is sent, the returned `Result` will represent /// those cases. /// - /// - returns: Result when single `Completion` or `failed` event is + /// - returns: Result when single `completion` or `failed` event is /// received. public func wait() -> Result<(), Error> { return then(SignalProducer<(), Error>(value: ())).last() ?? .success(()) From 4f6dc44788fb6c732a234295ffdb3a8c28db077d Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Tue, 6 Sep 2016 20:11:36 -0400 Subject: [PATCH 0353/1028] Rename file to reflect that it's an extension # Conflicts: # ReactiveCocoa.xcodeproj/project.pbxproj --- ReactiveCocoa.xcodeproj/project.pbxproj | 24 ++++++++----------- ...Lifetime.swift => NSObject+Lifetime.swift} | 0 2 files changed, 10 insertions(+), 14 deletions(-) rename ReactiveCocoa/Swift/{Lifetime.swift => NSObject+Lifetime.swift} (100%) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 1a1f901486..6bbbe45df1 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -9,10 +9,10 @@ /* Begin PBXBuildFile section */ 314304171ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 314304151ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; 314304181ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 314304161ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.m */; }; - 4A0E10FF1D2A92720065D310 /* Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* Lifetime.swift */; }; - 4A0E11001D2A92720065D310 /* Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* Lifetime.swift */; }; - 4A0E11011D2A92720065D310 /* Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* Lifetime.swift */; }; - 4A0E11021D2A92720065D310 /* Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* Lifetime.swift */; }; + 4A0E10FF1D2A92720065D310 /* NSObject+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */; }; + 4A0E11001D2A92720065D310 /* NSObject+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */; }; + 4A0E11011D2A92720065D310 /* NSObject+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */; }; + 4A0E11021D2A92720065D310 /* NSObject+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */; }; 57A4D1B21BA13D7A00F7D4B1 /* RACCompoundDisposableProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D037646A19EDA41200A782A9 /* RACCompoundDisposableProvider.d */; }; 57A4D1B31BA13D7A00F7D4B1 /* RACSignalProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D03764A319EDA41200A782A9 /* RACSignalProvider.d */; }; 57A4D1B71BA13D7A00F7D4B1 /* ObjectiveCBridging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312C419EF2A5800984962 /* ObjectiveCBridging.swift */; }; @@ -814,7 +814,7 @@ /* Begin PBXFileReference section */ 314304151ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MKAnnotationView+RACSignalSupport.h"; sourceTree = ""; }; 314304161ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MKAnnotationView+RACSignalSupport.m"; sourceTree = ""; }; - 4A0E10FE1D2A92720065D310 /* Lifetime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Lifetime.swift; sourceTree = ""; }; + 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Lifetime.swift"; sourceTree = ""; }; 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Application.xcconfig"; sourceTree = ""; }; 57A4D2451BA13F9700F7D4B1 /* tvOS-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Base.xcconfig"; sourceTree = ""; }; @@ -825,8 +825,6 @@ 7A7065831A3F8967001E8354 /* RACKVOProxySpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACKVOProxySpec.m; sourceTree = ""; }; 7DFBED031CDB8C9500EE435B /* ReactiveCocoaTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveCocoaTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyValueObservingSpec.swift; sourceTree = ""; }; - 9A694EF21D5CE02E009B05BD /* UnidirectionalBinding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnidirectionalBinding.swift; sourceTree = ""; }; - 9ABCB1841D2A5B5A00BCA243 /* Deprecations+Removals.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deprecations+Removals.swift"; sourceTree = ""; }; 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+KeyValueObserving.swift"; sourceTree = ""; }; A97451331B3A935E00F48E55 /* watchOS-Application.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Application.xcconfig"; sourceTree = ""; }; A97451341B3A935E00F48E55 /* watchOS-Base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Base.xcconfig"; sourceTree = ""; }; @@ -1521,7 +1519,7 @@ children = ( C7142DBB1CDEA167009F402D /* CocoaAction.swift */, CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */, - 4A0E10FE1D2A92720065D310 /* Lifetime.swift */, + 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */, ); name = Signals; sourceTree = ""; @@ -2253,7 +2251,7 @@ 57A4D1E01BA13D7A00F7D4B1 /* RACDynamicSignal.m in Sources */, 57A4D1E11BA13D7A00F7D4B1 /* RACEagerSequence.m in Sources */, 57D4768D1C42063C00EFE697 /* UIControl+RACSignalSupport.m in Sources */, - 4A0E11021D2A92720065D310 /* Lifetime.swift in Sources */, + 4A0E11021D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, 57A4D1E21BA13D7A00F7D4B1 /* RACEmptySequence.m in Sources */, 57A4D1E31BA13D7A00F7D4B1 /* RACEmptySignal.m in Sources */, 57A4D1E41BA13D7A00F7D4B1 /* RACErrorSignal.m in Sources */, @@ -2280,7 +2278,6 @@ 57A4D1F81BA13D7A00F7D4B1 /* RACSignalSequence.m in Sources */, 57A4D1F91BA13D7A00F7D4B1 /* RACStream.m in Sources */, 57A4D1FA1BA13D7A00F7D4B1 /* RACStringSequence.m in Sources */, - 9A694EF61D5CE02E009B05BD /* UnidirectionalBinding.swift in Sources */, CD0C45E11CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 57A4D1FB1BA13D7A00F7D4B1 /* RACSubject.m in Sources */, 57A4D1FC1BA13D7A00F7D4B1 /* RACSubscriber.m in Sources */, @@ -2403,7 +2400,6 @@ A9B3158D1B3940750001CB9C /* RACKVOTrampoline.m in Sources */, A9B3158E1B3940750001CB9C /* RACMulticastConnection.m in Sources */, C7142DBE1CDEA194009F402D /* CocoaAction.swift in Sources */, - 9A694EF51D5CE02E009B05BD /* UnidirectionalBinding.swift in Sources */, A9B315901B3940750001CB9C /* RACPassthroughSubscriber.m in Sources */, A9B315911B3940750001CB9C /* RACQueueScheduler.m in Sources */, A9B315921B3940750001CB9C /* RACReplaySubject.m in Sources */, @@ -2419,7 +2415,7 @@ A9B3159C1B3940750001CB9C /* RACStringSequence.m in Sources */, CD0C45E01CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, A9B3159D1B3940750001CB9C /* RACSubject.m in Sources */, - 4A0E11011D2A92720065D310 /* Lifetime.swift in Sources */, + 4A0E11011D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, A9B3159E1B3940750001CB9C /* RACSubscriber.m in Sources */, A9B3159F1B3940750001CB9C /* RACSubscriptingAssignmentTrampoline.m in Sources */, A9B315A01B3940750001CB9C /* RACSubscriptionScheduler.m in Sources */, @@ -2495,7 +2491,7 @@ D037659419EDA41200A782A9 /* RACImmediateScheduler.m in Sources */, 7A7065811A3F88B8001E8354 /* RACKVOProxy.m in Sources */, D037651619EDA41200A782A9 /* NSObject+RACDeallocating.m in Sources */, - 4A0E10FF1D2A92720065D310 /* Lifetime.swift in Sources */, + 4A0E10FF1D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, D037658419EDA41200A782A9 /* RACEmptySignal.m in Sources */, D037654619EDA41200A782A9 /* NSURLConnection+RACSupport.m in Sources */, D03765F019EDA41200A782A9 /* RACSubscriber.m in Sources */, @@ -2623,7 +2619,7 @@ D037665F19EDA41200A782A9 /* UITextField+RACSignalSupport.m in Sources */, D037650F19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m in Sources */, D03765FB19EDA41200A782A9 /* RACSubscriptionScheduler.m in Sources */, - 4A0E11001D2A92720065D310 /* Lifetime.swift in Sources */, + 4A0E11001D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, D037658119EDA41200A782A9 /* RACEmptySequence.m in Sources */, D0C312E019EF2A5800984962 /* ObjectiveCBridging.swift in Sources */, D037654B19EDA41200A782A9 /* NSUserDefaults+RACSupport.m in Sources */, diff --git a/ReactiveCocoa/Swift/Lifetime.swift b/ReactiveCocoa/Swift/NSObject+Lifetime.swift similarity index 100% rename from ReactiveCocoa/Swift/Lifetime.swift rename to ReactiveCocoa/Swift/NSObject+Lifetime.swift From 5c5e746dd57f9c346e3164256e3ff8d14a910e12 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Tue, 6 Sep 2016 20:11:44 -0400 Subject: [PATCH 0354/1028] Remove unnecessary import --- ReactiveCocoa/Swift/NSObject+Lifetime.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/ReactiveCocoa/Swift/NSObject+Lifetime.swift b/ReactiveCocoa/Swift/NSObject+Lifetime.swift index 388800548a..1c74e43063 100644 --- a/ReactiveCocoa/Swift/NSObject+Lifetime.swift +++ b/ReactiveCocoa/Swift/NSObject+Lifetime.swift @@ -1,6 +1,5 @@ import Foundation import ReactiveSwift -import enum Result.NoError #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) From 08dbc8e56913d61e677854ed6fafc474f01788e1 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Tue, 6 Sep 2016 20:11:54 -0400 Subject: [PATCH 0355/1028] Remove unnecessary platform conditional --- ReactiveCocoa/Swift/NSObject+Lifetime.swift | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ReactiveCocoa/Swift/NSObject+Lifetime.swift b/ReactiveCocoa/Swift/NSObject+Lifetime.swift index 1c74e43063..1f220d12d4 100644 --- a/ReactiveCocoa/Swift/NSObject+Lifetime.swift +++ b/ReactiveCocoa/Swift/NSObject+Lifetime.swift @@ -1,8 +1,6 @@ import Foundation import ReactiveSwift -#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) - private var lifetimeKey: UInt8 = 0 private var lifetimeTokenKey: UInt8 = 0 @@ -25,5 +23,3 @@ extension NSObject { return lifetime } } - -#endif From 7e73dbc5b04836eba7b88c4d754d0a16d2beb975 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Tue, 6 Sep 2016 20:12:14 -0400 Subject: [PATCH 0356/1028] Flatten Xcode hierarchy for Swift code --- ReactiveCocoa.xcodeproj/project.pbxproj | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 6bbbe45df1..401145b1fb 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -1514,24 +1514,6 @@ path = "Objective-C"; sourceTree = ""; }; - D03B4A3919F4C25F009E02AC /* Signals */ = { - isa = PBXGroup; - children = ( - C7142DBB1CDEA167009F402D /* CocoaAction.swift */, - CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */, - 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */, - ); - name = Signals; - sourceTree = ""; - }; - D03B4A3B19F4C281009E02AC /* Extensions */ = { - isa = PBXGroup; - children = ( - 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */, - ); - name = Extensions; - sourceTree = ""; - }; D04725E019E49ED7006002AA = { isa = PBXGroup; children = ( @@ -1671,9 +1653,11 @@ D0C312B919EF2A3000984962 /* Swift */ = { isa = PBXGroup; children = ( + C7142DBB1CDEA167009F402D /* CocoaAction.swift */, + CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */, + 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */, + 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */, D0C312C419EF2A5800984962 /* ObjectiveCBridging.swift */, - D03B4A3919F4C25F009E02AC /* Signals */, - D03B4A3B19F4C281009E02AC /* Extensions */, ); path = Swift; sourceTree = ""; From 2838f36fb49c0ef923ae1905844d58eb391726d0 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Tue, 6 Sep 2016 20:13:10 -0400 Subject: [PATCH 0357/1028] Fix description --- ReactiveSwiftTests/LifetimeSpec.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveSwiftTests/LifetimeSpec.swift b/ReactiveSwiftTests/LifetimeSpec.swift index 4c06dca363..215a9d1a48 100644 --- a/ReactiveSwiftTests/LifetimeSpec.swift +++ b/ReactiveSwiftTests/LifetimeSpec.swift @@ -5,7 +5,7 @@ import Result final class LifetimeSpec: QuickSpec { override func spec() { - describe("NSObject.rac_lifetime") { + describe("Lifetime") { it("should complete its lifetime ended signal when the it deinitializes") { let object = MutableReference(TestObject()) From a4ed228fef050604521040babce08b269bc89fec Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Thu, 8 Sep 2016 05:29:05 +0900 Subject: [PATCH 0358/1028] Fix Xcode 8 GM compatibility --- Cartfile.resolved | 4 ++-- Carthage/Checkouts/Nimble | 2 +- Carthage/Checkouts/Quick | 2 +- ReactiveCocoa/Swift/EventLogger.swift | 4 ++-- ReactiveCocoa/Swift/NSObject+KeyValueObserving.swift | 2 +- ReactiveCocoa/Swift/Observer.swift | 4 ++-- ReactiveCocoa/Swift/Signal.swift | 2 +- ReactiveCocoa/Swift/SignalProducer.swift | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Cartfile.resolved b/Cartfile.resolved index 1a7ad28ef7..bacea09bbc 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,4 +1,4 @@ -github "Quick/Nimble" "4b539a275d2714212ce598177d586680fc088927" -github "Quick/Quick" "6110c50b518a72a24cd2505c8012fa785c3b1c8e" +github "Quick/Nimble" "220152be528dcc0537764c179c95b8174028c80c" +github "Quick/Quick" "81d2a7bd95ef91e2604ee0431bba6fe59ad662dc" github "antitypical/Result" "3.0.0-alpha.4" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" diff --git a/Carthage/Checkouts/Nimble b/Carthage/Checkouts/Nimble index 4b539a275d..220152be52 160000 --- a/Carthage/Checkouts/Nimble +++ b/Carthage/Checkouts/Nimble @@ -1 +1 @@ -Subproject commit 4b539a275d2714212ce598177d586680fc088927 +Subproject commit 220152be528dcc0537764c179c95b8174028c80c diff --git a/Carthage/Checkouts/Quick b/Carthage/Checkouts/Quick index 6110c50b51..81d2a7bd95 160000 --- a/Carthage/Checkouts/Quick +++ b/Carthage/Checkouts/Quick @@ -1 +1 @@ -Subproject commit 6110c50b518a72a24cd2505c8012fa785c3b1c8e +Subproject commit 81d2a7bd95ef91e2604ee0431bba6fe59ad662dc diff --git a/ReactiveCocoa/Swift/EventLogger.swift b/ReactiveCocoa/Swift/EventLogger.swift index 13cef16458..fadce70588 100644 --- a/ReactiveCocoa/Swift/EventLogger.swift +++ b/ReactiveCocoa/Swift/EventLogger.swift @@ -54,7 +54,7 @@ extension SignalProtocol { /// - logger: Logger that logs the events. /// /// - returns: Signal that, when observed, logs the fired events. - public func logEvents(identifier: String = "", events: Set = LoggingEvent.Signal.allEvents, fileName: String = #file, functionName: String = #function, lineNumber: Int = #line, logger: EventLogger = defaultEventLog) -> Signal { + public func logEvents(identifier: String = "", events: Set = LoggingEvent.Signal.allEvents, fileName: String = #file, functionName: String = #function, lineNumber: Int = #line, logger: @escaping EventLogger = defaultEventLog) -> Signal { func log(_ event: LoggingEvent.Signal) -> ((T) -> Void)? { return event.logIfNeeded(events: events) { event in logger(identifier, event, fileName, functionName, lineNumber) @@ -91,7 +91,7 @@ extension SignalProducerProtocol { fileName: String = #file, functionName: String = #function, lineNumber: Int = #line, - logger: EventLogger = defaultEventLog + logger: @escaping EventLogger = defaultEventLog ) -> SignalProducer { func log(_ event: LoggingEvent.SignalProducer) -> ((T) -> Void)? { return event.logIfNeeded(events: events) { event in diff --git a/ReactiveCocoa/Swift/NSObject+KeyValueObserving.swift b/ReactiveCocoa/Swift/NSObject+KeyValueObserving.swift index 824b315696..4545524b91 100644 --- a/ReactiveCocoa/Swift/NSObject+KeyValueObserving.swift +++ b/ReactiveCocoa/Swift/NSObject+KeyValueObserving.swift @@ -33,7 +33,7 @@ internal final class KeyValueObserver: NSObject { let key: String let action: Action - fileprivate init(observing object: NSObject, key: String, options: NSKeyValueObservingOptions, action: Action) { + fileprivate init(observing object: NSObject, key: String, options: NSKeyValueObservingOptions, action: @escaping Action) { self.unsafeObject = object self.key = key self.action = action diff --git a/ReactiveCocoa/Swift/Observer.swift b/ReactiveCocoa/Swift/Observer.swift index a73d346f67..5b5b5067d8 100644 --- a/ReactiveCocoa/Swift/Observer.swift +++ b/ReactiveCocoa/Swift/Observer.swift @@ -27,7 +27,7 @@ public protocol ObserverProtocol { /// An Observer is a simple wrapper around a function which can receive Events /// (typically from a Signal). public final class Observer { - public typealias Action = @escaping (Event) -> Void + public typealias Action = (Event) -> Void /// An action that will be performed upon arrival of the event. public let action: Action @@ -37,7 +37,7 @@ public final class Observer { /// /// - parameters: /// - action: A closure to lift over received event. - public init(_ action: Action) { + public init(_ action: @escaping Action) { self.action = action } diff --git a/ReactiveCocoa/Swift/Signal.swift b/ReactiveCocoa/Swift/Signal.swift index fdf0226124..3fe78283f8 100644 --- a/ReactiveCocoa/Swift/Signal.swift +++ b/ReactiveCocoa/Swift/Signal.swift @@ -215,7 +215,7 @@ extension SignalProtocol { /// invocation of the callback. Disposing of the Disposable will /// have no effect on the Signal itself. @discardableResult - public func observe(_ action: Signal.Observer.Action) -> Disposable? { + public func observe(_ action: @escaping Signal.Observer.Action) -> Disposable? { return observe(Observer(action)) } diff --git a/ReactiveCocoa/Swift/SignalProducer.swift b/ReactiveCocoa/Swift/SignalProducer.swift index 0fc75e74fe..2fd3b8d23c 100644 --- a/ReactiveCocoa/Swift/SignalProducer.swift +++ b/ReactiveCocoa/Swift/SignalProducer.swift @@ -261,7 +261,7 @@ extension SignalProducerProtocol { /// associated with the signal and immediately send an /// `interrupted` event. @discardableResult - public func start(_ observerAction: Signal.Observer.Action) -> Disposable { + public func start(_ observerAction: @escaping Signal.Observer.Action) -> Disposable { return start(Observer(observerAction)) } From 61df15676a5618faf3e2795acb0ea1bc9e75dd6c Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 9 Sep 2016 11:42:08 +0100 Subject: [PATCH 0359/1028] EventLogger now logs the `starting` SignalProducer event. --- ReactiveCocoa/Swift/EventLogger.swift | 5 +++-- ReactiveCocoaTests/Swift/SignalProducerSpec.swift | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoa/Swift/EventLogger.swift b/ReactiveCocoa/Swift/EventLogger.swift index fadce70588..7f9c6266b4 100644 --- a/ReactiveCocoa/Swift/EventLogger.swift +++ b/ReactiveCocoa/Swift/EventLogger.swift @@ -19,10 +19,10 @@ public enum LoggingEvent { } public enum SignalProducer: String { - case started, next, completed, failed, terminated, disposed, interrupted + case starting, started, next, completed, failed, terminated, disposed, interrupted public static let allEvents: Set = [ - .started, .next, .completed, .failed, .terminated, .disposed, .interrupted, + .starting, .started, .next, .completed, .failed, .terminated, .disposed, .interrupted, ] } } @@ -100,6 +100,7 @@ extension SignalProducerProtocol { } return self.on( + starting: log(.starting), started: log(.started), next: log(.next), failed: log(.failed), diff --git a/ReactiveCocoaTests/Swift/SignalProducerSpec.swift b/ReactiveCocoaTests/Swift/SignalProducerSpec.swift index 7a0b768267..1cc03012f3 100644 --- a/ReactiveCocoaTests/Swift/SignalProducerSpec.swift +++ b/ReactiveCocoaTests/Swift/SignalProducerSpec.swift @@ -2208,6 +2208,7 @@ class SignalProducerSpec: QuickSpec { describe("log events") { it("should output the correct event") { let expectations: [(String) -> Void] = [ + { event in expect(event) == "[] starting" }, { event in expect(event) == "[] started" }, { event in expect(event) == "[] next 1" }, { event in expect(event) == "[] completed" }, From 8d5f963f0705c849b9574fcdd5033426be16b3d8 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Fri, 9 Sep 2016 09:50:06 -0400 Subject: [PATCH 0360/1028] Remove unneeded build settings --- ReactiveCocoa.xcodeproj/project.pbxproj | 5 ----- ReactiveSwift.xcodeproj/project.pbxproj | 5 ----- 2 files changed, 10 deletions(-) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 401145b1fb..2521e58730 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -2926,11 +2926,9 @@ BITCODE_GENERATION_MODE = bitcode; CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; - ENABLE_BITCODE = NO; ENABLE_TESTABILITY = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; MACOSX_DEPLOYMENT_TARGET = 10.9; - ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; SWIFT_VERSION = 3.0; @@ -2948,7 +2946,6 @@ BITCODE_GENERATION_MODE = bitcode; CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; - ENABLE_BITCODE = NO; GCC_OPTIMIZATION_LEVEL = 0; IPHONEOS_DEPLOYMENT_TARGET = 8.0; MACOSX_DEPLOYMENT_TARGET = 10.9; @@ -3077,7 +3074,6 @@ BITCODE_GENERATION_MODE = bitcode; CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; - ENABLE_BITCODE = NO; IPHONEOS_DEPLOYMENT_TARGET = 8.0; MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; @@ -3151,7 +3147,6 @@ BITCODE_GENERATION_MODE = bitcode; CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; - ENABLE_BITCODE = NO; IPHONEOS_DEPLOYMENT_TARGET = 8.0; MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)-Tests"; diff --git a/ReactiveSwift.xcodeproj/project.pbxproj b/ReactiveSwift.xcodeproj/project.pbxproj index b68999f786..aed684e9b1 100644 --- a/ReactiveSwift.xcodeproj/project.pbxproj +++ b/ReactiveSwift.xcodeproj/project.pbxproj @@ -1220,11 +1220,9 @@ BITCODE_GENERATION_MODE = bitcode; CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; - ENABLE_BITCODE = NO; ENABLE_TESTABILITY = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; MACOSX_DEPLOYMENT_TARGET = 10.9; - ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; SWIFT_VERSION = 3.0; @@ -1242,7 +1240,6 @@ BITCODE_GENERATION_MODE = bitcode; CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; - ENABLE_BITCODE = NO; GCC_OPTIMIZATION_LEVEL = 0; IPHONEOS_DEPLOYMENT_TARGET = 8.0; MACOSX_DEPLOYMENT_TARGET = 10.9; @@ -1367,7 +1364,6 @@ BITCODE_GENERATION_MODE = bitcode; CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; - ENABLE_BITCODE = NO; IPHONEOS_DEPLOYMENT_TARGET = 8.0; MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; @@ -1439,7 +1435,6 @@ BITCODE_GENERATION_MODE = bitcode; CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; - ENABLE_BITCODE = NO; IPHONEOS_DEPLOYMENT_TARGET = 8.0; MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)-Tests"; From 9cd5f934925559d4e061b1b1fc4ed3d3b34e11e6 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Sun, 11 Sep 2016 14:06:08 -0400 Subject: [PATCH 0361/1028] Depend on ReactiveSwift through Carthage --- .gitmodules | 3 + .travis.yml | 26 - Cartfile | 2 +- Cartfile.resolved | 5 +- Carthage/Checkouts/Nimble | 2 +- Carthage/Checkouts/Quick | 2 +- Carthage/Checkouts/ReactiveSwift | 1 + Carthage/Checkouts/Result | 2 +- Package.swift | 11 - README-ReactiveSwift.md | 355 --- .../xcschemes/ReactiveCocoa-iOS.xcscheme | 2 +- .../xcschemes/ReactiveCocoa-macOS.xcscheme | 2 +- .../xcschemes/ReactiveCocoa-tvOS.xcscheme | 2 +- .../contents.xcworkspacedata | 2 +- .../Sandbox.xcplaygroundpage/Contents.swift | 25 - .../Signal.xcplaygroundpage/Contents.swift | 240 -- .../Contents.swift | 767 ------ .../Sources/PlaygroundUtility.swift | 10 - .../contents.xcplayground | 8 - ReactiveSwift.xcodeproj/project.pbxproj | 1596 ------------ .../xcschemes/ReactiveSwift-iOS.xcscheme | 156 -- .../xcschemes/ReactiveSwift-macOS.xcscheme | 156 -- .../xcschemes/ReactiveSwift-tvOS.xcscheme | 156 -- .../xcschemes/ReactiveSwift-watchOS.xcscheme | 71 - ReactiveSwift/Action.swift | 199 -- ReactiveSwift/Atomic.swift | 169 -- ReactiveSwift/Bag.swift | 110 - ReactiveSwift/Deprecations+Removals.swift | 255 -- ReactiveSwift/Disposable.swift | 357 --- ReactiveSwift/Event.swift | 165 -- ReactiveSwift/EventLogger.swift | 133 - ReactiveSwift/Flatten.swift | 928 ------- ReactiveSwift/FoundationExtensions.swift | 80 - ReactiveSwift/Info.plist | 28 - ReactiveSwift/Lifetime.swift | 69 - ReactiveSwift/Observer.swift | 104 - ReactiveSwift/Optional.swift | 42 - ReactiveSwift/Property.swift | 643 ----- ReactiveSwift/ReactiveSwift.h | 15 - ReactiveSwift/Scheduler.swift | 493 ---- ReactiveSwift/Signal.swift | 1843 ------------- ReactiveSwift/SignalProducer.swift | 1943 -------------- ReactiveSwift/TupleExtensions.swift | 42 - ReactiveSwift/UnidirectionalBinding.swift | 278 -- ReactiveSwiftTests/ActionSpec.swift | 142 - ReactiveSwiftTests/AtomicSpec.swift | 44 - ReactiveSwiftTests/BagSpec.swift | 54 - ReactiveSwiftTests/DisposableSpec.swift | 143 - ReactiveSwiftTests/FlattenSpec.swift | 963 ------- .../FoundationExtensionsSpec.swift | 59 - ReactiveSwiftTests/Info.plist | 24 - ReactiveSwiftTests/LifetimeSpec.swift | 47 - ReactiveSwiftTests/PropertySpec.swift | 1569 ----------- ReactiveSwiftTests/SchedulerSpec.swift | 298 --- ReactiveSwiftTests/SignalLifetimeSpec.swift | 414 --- .../SignalProducerLiftingSpec.swift | 1536 ----------- .../SignalProducerNimbleMatchers.swift | 57 - ReactiveSwiftTests/SignalProducerSpec.swift | 2315 ----------------- ReactiveSwiftTests/SignalSpec.swift | 2268 ---------------- ReactiveSwiftTests/TestError.swift | 43 - ReactiveSwiftTests/TestLogger.swift | 24 - Sources | 1 - 62 files changed, 15 insertions(+), 21484 deletions(-) create mode 160000 Carthage/Checkouts/ReactiveSwift delete mode 100644 Package.swift delete mode 100644 README-ReactiveSwift.md delete mode 100644 ReactiveSwift.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift delete mode 100644 ReactiveSwift.playground/Pages/Signal.xcplaygroundpage/Contents.swift delete mode 100644 ReactiveSwift.playground/Pages/SignalProducer.xcplaygroundpage/Contents.swift delete mode 100644 ReactiveSwift.playground/Sources/PlaygroundUtility.swift delete mode 100644 ReactiveSwift.playground/contents.xcplayground delete mode 100644 ReactiveSwift.xcodeproj/project.pbxproj delete mode 100644 ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-iOS.xcscheme delete mode 100644 ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-macOS.xcscheme delete mode 100644 ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-tvOS.xcscheme delete mode 100644 ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-watchOS.xcscheme delete mode 100644 ReactiveSwift/Action.swift delete mode 100644 ReactiveSwift/Atomic.swift delete mode 100644 ReactiveSwift/Bag.swift delete mode 100644 ReactiveSwift/Deprecations+Removals.swift delete mode 100644 ReactiveSwift/Disposable.swift delete mode 100644 ReactiveSwift/Event.swift delete mode 100644 ReactiveSwift/EventLogger.swift delete mode 100644 ReactiveSwift/Flatten.swift delete mode 100644 ReactiveSwift/FoundationExtensions.swift delete mode 100644 ReactiveSwift/Info.plist delete mode 100644 ReactiveSwift/Lifetime.swift delete mode 100644 ReactiveSwift/Observer.swift delete mode 100644 ReactiveSwift/Optional.swift delete mode 100644 ReactiveSwift/Property.swift delete mode 100644 ReactiveSwift/ReactiveSwift.h delete mode 100644 ReactiveSwift/Scheduler.swift delete mode 100644 ReactiveSwift/Signal.swift delete mode 100644 ReactiveSwift/SignalProducer.swift delete mode 100644 ReactiveSwift/TupleExtensions.swift delete mode 100644 ReactiveSwift/UnidirectionalBinding.swift delete mode 100755 ReactiveSwiftTests/ActionSpec.swift delete mode 100644 ReactiveSwiftTests/AtomicSpec.swift delete mode 100644 ReactiveSwiftTests/BagSpec.swift delete mode 100644 ReactiveSwiftTests/DisposableSpec.swift delete mode 100644 ReactiveSwiftTests/FlattenSpec.swift delete mode 100644 ReactiveSwiftTests/FoundationExtensionsSpec.swift delete mode 100644 ReactiveSwiftTests/Info.plist delete mode 100644 ReactiveSwiftTests/LifetimeSpec.swift delete mode 100644 ReactiveSwiftTests/PropertySpec.swift delete mode 100644 ReactiveSwiftTests/SchedulerSpec.swift delete mode 100644 ReactiveSwiftTests/SignalLifetimeSpec.swift delete mode 100644 ReactiveSwiftTests/SignalProducerLiftingSpec.swift delete mode 100644 ReactiveSwiftTests/SignalProducerNimbleMatchers.swift delete mode 100644 ReactiveSwiftTests/SignalProducerSpec.swift delete mode 100755 ReactiveSwiftTests/SignalSpec.swift delete mode 100644 ReactiveSwiftTests/TestError.swift delete mode 100644 ReactiveSwiftTests/TestLogger.swift delete mode 120000 Sources diff --git a/.gitmodules b/.gitmodules index 8283dfc1cf..d3aed82746 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "Carthage/Checkouts/Result"] path = Carthage/Checkouts/Result url = https://github.com/antitypical/Result.git +[submodule "Carthage/Checkouts/ReactiveSwift"] + path = Carthage/Checkouts/ReactiveSwift + url = https://github.com/ReactiveCocoa/ReactiveSwift.git diff --git a/.travis.yml b/.travis.yml index 0acd878ec0..5056f2bf63 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,27 +32,6 @@ matrix: - XCODE_SDK=watchsimulator - XCODE_ACTION=build - XCODE_DESTINATION="platform=watchOS Simulator,name=Apple Watch - 38mm" - - xcode_scheme: ReactiveSwift-macOS - env: - - XCODE_SDK=macosx - - XCODE_ACTION="build test" - - XCODE_DESTINATION="arch=x86_64" - - XCODE_PLAYGROUND_TARGET="x86_64-apple-macosx10.10" - - xcode_scheme: ReactiveSwift-iOS - env: - - XCODE_SDK=iphonesimulator - - XCODE_ACTION="build-for-testing test-without-building" - - XCODE_DESTINATION="platform=iOS Simulator,name=iPhone 6s" - - xcode_scheme: ReactiveSwift-tvOS - env: - - XCODE_SDK=appletvsimulator - - XCODE_ACTION="build-for-testing test-without-building" - - XCODE_DESTINATION="platform=tvOS Simulator,name=Apple TV 1080p" - - xcode_scheme: ReactiveSwift-watchOS - env: - - XCODE_SDK=watchsimulator - - XCODE_ACTION=build - - XCODE_DESTINATION="platform=watchOS Simulator,name=Apple Watch - 38mm" - script: - brew update - brew outdated carthage || brew upgrade carthage @@ -77,11 +56,6 @@ matrix: - carthage build --no-skip-current --platform watchOS env: - JOB=CARTHAGE-watchOS - - os: osx - language: generic - script: swift build - env: - - JOB=SWIFTPM_DARWIN notifications: email: false slack: diff --git a/Cartfile b/Cartfile index e438273700..d55c71844c 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "antitypical/Result" "3.0.0-alpha.4" +github "ReactiveCocoa/ReactiveSwift" "master" diff --git a/Cartfile.resolved b/Cartfile.resolved index bacea09bbc..c58b82e551 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,4 +1,5 @@ -github "Quick/Nimble" "220152be528dcc0537764c179c95b8174028c80c" -github "Quick/Quick" "81d2a7bd95ef91e2604ee0431bba6fe59ad662dc" +github "Quick/Nimble" "9dc342cdffef660414d17f27aec897d0cacfffea" +github "Quick/Quick" "8f2bc636ecfa2cc20696f62548b38d4ab943e299" github "antitypical/Result" "3.0.0-alpha.4" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" +github "ReactiveCocoa/ReactiveSwift" "d6e4b21d01e863ae4406ea8834b12c1a6caf7d6b" diff --git a/Carthage/Checkouts/Nimble b/Carthage/Checkouts/Nimble index 220152be52..9dc342cdff 160000 --- a/Carthage/Checkouts/Nimble +++ b/Carthage/Checkouts/Nimble @@ -1 +1 @@ -Subproject commit 220152be528dcc0537764c179c95b8174028c80c +Subproject commit 9dc342cdffef660414d17f27aec897d0cacfffea diff --git a/Carthage/Checkouts/Quick b/Carthage/Checkouts/Quick index 81d2a7bd95..8f2bc636ec 160000 --- a/Carthage/Checkouts/Quick +++ b/Carthage/Checkouts/Quick @@ -1 +1 @@ -Subproject commit 81d2a7bd95ef91e2604ee0431bba6fe59ad662dc +Subproject commit 8f2bc636ecfa2cc20696f62548b38d4ab943e299 diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift new file mode 160000 index 0000000000..d6e4b21d01 --- /dev/null +++ b/Carthage/Checkouts/ReactiveSwift @@ -0,0 +1 @@ +Subproject commit d6e4b21d01e863ae4406ea8834b12c1a6caf7d6b diff --git a/Carthage/Checkouts/Result b/Carthage/Checkouts/Result index d78cac3865..cc1699dbd8 160000 --- a/Carthage/Checkouts/Result +++ b/Carthage/Checkouts/Result @@ -1 +1 @@ -Subproject commit d78cac38657de99d85f8f1770b290d039721ef27 +Subproject commit cc1699dbd812c71bee7c44c8cbd75497713bc279 diff --git a/Package.swift b/Package.swift deleted file mode 100644 index 4b15638cbe..0000000000 --- a/Package.swift +++ /dev/null @@ -1,11 +0,0 @@ -import PackageDescription - -let package = Package( - name: "ReactiveSwift", - dependencies: [ - .Package(url: "https://github.com/antitypical/Result.git", "3.0.0-alpha.3") - ], - exclude: [ - "Sources/Deprecations+Removals.swift", - ] -) diff --git a/README-ReactiveSwift.md b/README-ReactiveSwift.md deleted file mode 100644 index c4111f8dd6..0000000000 --- a/README-ReactiveSwift.md +++ /dev/null @@ -1,355 +0,0 @@ -# ReactiveSwift - -[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![GitHub release](https://img.shields.io/github/release/ReactiveCocoa/ReactiveCocoa.svg)](https://github.com/ReactiveCocoa/ReactiveCocoa/releases) ![Swift 3.0.x](https://img.shields.io/badge/Swift-3.0.x-orange.svg) ![platforms](https://img.shields.io/badge/platforms-iOS%20%7C%20OS%20X%20%7C%20watchOS%20%7C%20tvOS%20-lightgrey.svg) - -ReactiveSwift is a Swift framework inspired by [Functional Reactive Programming](https://en.wikipedia.org/wiki/Functional_reactive_programming). It provides APIs for composing and transforming **streams of values over time**. - - 1. [Introduction](#introduction) - 1. [Example: online search](#example-online-search) - 1. [How does ReactiveSwift relate to Rx?](#how-does-reactivecocoa-relate-to-rx) - 1. [Getting started](#getting-started) - 1. [Playground](#playground) - -If you’re already familiar with functional reactive programming or what -ReactiveSwift is about, check out the [Documentation][] folder for more in-depth -information about how it all works. Then, dive straight into our [documentation -comments][Code] for learning more about individual APIs. - -If you'd like to use ReactiveSwift with Apple's Cocoa frameworks, -[ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa) provides -extensions that work with ReactiveSwift. - -If you have a question, please see if any discussions in our [GitHub -issues](https://github.com/ReactiveCocoa/ReactiveSwift/issues?q=is%3Aissue+label%3Aquestion+) or [Stack -Overflow](http://stackoverflow.com/questions/tagged/reactive-cocoa) have already -answered it. If not, please feel free to [file your -own](https://github.com/ReactiveCocoa/ReactiveSwift/issues/new)! - -#### Compatibility - -This documents ReactiveSwift 3.x which targets `Swift 3.0.x`. For `Swift 2.x` support see [ReactiveCocoa -4](https://github.com/ReactiveCocoa/ReactiveCocoa/tree/v4.0.0). - -## Introduction - -ReactiveSwift is inspired by [functional reactive -programming](https://joshaber.github.io/2013/02/11/input-and-output/). -Rather than using mutable variables which are replaced and modified in-place, -RAC offers “event streams,” represented by the [`Signal`][Signals] and -[`SignalProducer`][Signal producers] types, that send values over time. - -Event streams unify common patterns for asynchrony and event -handling, including: - - * Delegate methods - * Callback blocks - * Notifications - * Control actions and responder chain events - * [Futures and promises](https://en.wikipedia.org/wiki/Futures_and_promises) - * [Key-value observing](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html) (KVO) - -Because all of these different mechanisms can be represented in the _same_ way, -it’s easy to declaratively chain and combine them together, with less spaghetti -code and state to bridge the gap. - -For more information about the concepts in ReactiveSwift, see the [Framework -Overview][]. - -## Example: online search - -Let’s say you have a text field, and whenever the user types something into it, -you want to make a network request which searches for that query. - -#### Observing text edits - -The first step is to observe edits to the text field, using a RAC extension to -`UITextField` specifically for this purpose: - -```swift -let searchStrings = textField.rac_textSignal() - .toSignalProducer() - .map { text in text as! String } -``` - -This gives us a [signal producer][Signal producers] which sends -values of type `String`. _(The cast is [currently -necessary](https://github.com/ReactiveCocoa/ReactiveCocoa/issues/2182) to bridge -this extension method from Objective-C.)_ - -#### Making network requests - -With each string, we want to execute a network request. Luckily, RAC offers an -`NSURLSession` extension for doing exactly that: - -```swift -let searchResults = searchStrings - .flatMap(.Latest) { (query: String) -> SignalProducer<(NSData, NSURLResponse), NSError> in - let URLRequest = self.searchRequestWithEscapedQuery(query) - return NSURLSession.sharedSession().rac_dataWithRequest(URLRequest) - } - .map { (data, URLResponse) -> String in - let string = String(data: data, encoding: NSUTF8StringEncoding)! - return self.parseJSONResultsFromString(string) - } - .observeOn(UIScheduler()) -``` - -This has transformed our producer of `String`s into a producer of `Array`s -containing the search results, which will be forwarded on the main thread -(thanks to the [`UIScheduler`][Schedulers]). - -Additionally, [`flatMap(.Latest)`][flatMapLatest] here ensures that _only one search_—the -latest—is allowed to be running. If the user types another character while the -network request is still in flight, it will be cancelled before starting a new -one. Just think of how much code that would take to do by hand! - -#### Receiving the results - -This won’t actually execute yet, because producers must be _started_ in order to -receive the results (which prevents doing work when the results are never used). -That’s easy enough: - -```swift -searchResults.startWithNext { results in - print("Search results: \(results)") -} -``` - -Here, we watch for the `Next` [event][Events], which contains our results, and -just log them to the console. This could easily do something else instead, like -update a table view or a label on screen. - -#### Handling failures - -In this example so far, any network error will generate a `Failed` -[event][Events], which will terminate the event stream. Unfortunately, this -means that future queries won’t even be attempted. - -To remedy this, we need to decide what to do with failures that occur. The -quickest solution would be to log them, then ignore them: - -```swift - .flatMap(.Latest) { (query: String) -> SignalProducer<(NSData, NSURLResponse), NSError> in - let URLRequest = self.searchRequestWithEscapedQuery(query) - - return NSURLSession.sharedSession() - .rac_dataWithRequest(URLRequest) - .flatMapError { error in - print("Network error occurred: \(error)") - return SignalProducer.empty - } - } -``` - -By replacing failures with the `empty` event stream, we’re able to effectively -ignore them. - -However, it’s probably more appropriate to retry at least a couple of times -before giving up. Conveniently, there’s a [`retry`][retry] operator to do exactly that! - -Our improved `searchResults` producer might look like this: - -```swift -let searchResults = searchStrings - .flatMap(.Latest) { (query: String) -> SignalProducer<(NSData, NSURLResponse), NSError> in - let URLRequest = self.searchRequestWithEscapedQuery(query) - - return NSURLSession.sharedSession() - .rac_dataWithRequest(URLRequest) - .retry(2) - .flatMapError { error in - print("Network error occurred: \(error)") - return SignalProducer.empty - } - } - .map { (data, URLResponse) -> String in - let string = String(data: data, encoding: NSUTF8StringEncoding)! - return self.parseJSONResultsFromString(string) - } - .observeOn(UIScheduler()) -``` - -#### Throttling requests - -Now, let’s say you only want to actually perform the search periodically, -to minimize traffic. - -ReactiveCocoa has a declarative `throttle` operator that we can apply to our -search strings: - -```swift -let searchStrings = textField.rac_textSignal() - .toSignalProducer() - .map { text in text as! String } - .throttle(0.5, onScheduler: QueueScheduler.mainQueueScheduler) -``` - -This prevents values from being sent less than 0.5 seconds apart. - -To do this manually would require significant state, and end up much harder to -read! With ReactiveCocoa, we can use just one operator to incorporate _time_ into -our event stream. - -#### Debugging event streams - -Due to its nature, a stream's stack trace might have dozens of frames, which, more often than not, can make debugging a very frustrating activity. -A naive way of debugging, is by injecting side effects into the stream, like so: - -```swift -let searchString = textField.rac_textSignal() - .toSignalProducer() - .map { text in text as! String } - .throttle(0.5, onScheduler: QueueScheduler.mainQueueScheduler) - .on(event: { print ($0) }) // the side effect -``` - -This will print the stream's [events][Events], while preserving the original stream behaviour. Both [`SignalProducer`][Signal producers] -and [`Signal`][Signals] provide the `logEvents` operator, that will do this automatically for you: - -```swift -let searchString = textField.rac_textSignal() - .toSignalProducer() - .map { text in text as! String } - .throttle(0.5, onScheduler: QueueScheduler.mainQueueScheduler) - .logEvents() -``` - -For more information and advance usage, check the [Debugging Techniques](Documentation/DebuggingTechniques.md) document. - -## How does ReactiveSwift relate to Rx? - -ReactiveCocoa was originally inspired, and therefore heavily influenced, by -Microsoft’s [Reactive -Extensions](https://msdn.microsoft.com/en-us/data/gg577609.aspx) (Rx) library. There are many ports of Rx, including [RxSwift](https://github.com/ReactiveX/RxSwift), but ReactiveCocoa is _intentionally_ not a direct port. - -**Where ReactiveSwift differs from Rx**, it is usually to: - - * Create a simpler API - * Address common sources of confusion - * More closely match Cocoa conventions - -The following are some of the concrete differences, along with their rationales. - -### Naming - -In most versions of Rx, Streams over time are known as `Observable`s, which -parallels the `Enumerable` type in .NET. Additionally, most operations in Rx.NET -borrow names from [LINQ](https://msdn.microsoft.com/en-us/library/bb397926.aspx), -which uses terms reminiscent of relational databases, like `Select` and `Where`. - -**RAC is focused on matching Swift naming first and foremost**, with terms like -`map` and `filter` instead. Other naming differences are typically inspired by -significantly better alternatives from [Haskell](https://www.haskell.org) or -[Elm](http://elm-lang.org) (which is the primary source for the “signal” -terminology). - -### Signals and Signal Producers (“hot” and “cold” observables) - -One of the most confusing aspects of Rx is that of [“hot”, “cold”, and “warm” -observables](http://www.introtorx.com/content/v1.0.10621.0/14_HotAndColdObservables.html) (event streams). - -In short, given just a method or function declaration like this, in C#: - -```csharp -IObservable Search(string query) -``` - -… it is **impossible to tell** whether subscribing to (observing) that -`IObservable` will involve side effects. If it _does_ involve side effects, it’s -also impossible to tell whether _each subscription_ has a side effect, or if only -the first one does. - -This example is contrived, but it demonstrates **a real, pervasive problem** -that makes it extremely hard to understand Rx code (and pre-3.0 ReactiveCocoa -code) at a glance. - -[ReactiveCocoa 3.0][https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/CHANGELOG.md] has solved this problem by distinguishing side -effects with the separate [`Signal`][Signals] and [`SignalProducer`][Signal producers] types. Although this -means there’s another type to learn about, it improves code clarity and helps -communicates intent much better. - -In other words, **ReactiveSwift’s changes here are [simple, not -easy](http://www.infoq.com/presentations/Simple-Made-Easy)**. - -### Typed errors - -When [signals][] and [signal producers][] are allowed to [fail][Events] in ReactiveSwift, -the kind of error must be specified in the type system. For example, -`Signal` is a signal of integer values that may fail with an error -of type `NSError`. - -More importantly, RAC allows the special type `NoError` to be used instead, -which _statically guarantees_ that an event stream is not allowed to send a -failure. **This eliminates many bugs caused by unexpected failure events.** - -In Rx systems with types, event streams only specify the type of their -values—not the type of their errors—so this sort of guarantee is impossible. - -### UI programming - -Rx is basically agnostic as to how it’s used. Although UI programming with Rx is -very common, it has few features tailored to that particular case. - -ReactiveSwift takes a lot of inspiration from [ReactiveUI](http://reactiveui.net/), -including the basis for [Actions][]. - -Unlike ReactiveUI, which unfortunately cannot directly change Rx to make it more -friendly for UI programming, **ReactiveSwift has been improved many times -specifically for this purpose**—even when it means diverging further from Rx. - -## Getting started - -ReactiveSwift supports `OS X 10.9+`, `iOS 8.0+`, `watchOS 2.0`, and `tvOS 9.0`. - -To add RAC to your application: - - 1. Add the ReactiveSwift repository as a - [submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules) of your - application’s repository. - 1. Run `git submodule update --init --recursive` from within the ReactiveSwift folder. - 1. Drag and drop `ReactiveSwift.xcodeproj` and `Carthage/Checkouts/Result/Result.xcodeproj` - into your application’s Xcode project or workspace. - 1. On the “General” tab of your application target’s settings, add - `ReactiveSwift.framework` and `Result.framework` to the “Embedded Binaries” section. - 1. If your application target does not contain Swift code at all, you should also - set the `EMBEDDED_CONTENT_CONTAINS_SWIFT` build setting to “Yes”. - -Or, if you’re using [Carthage](https://github.com/Carthage/Carthage), simply add -ReactiveSwift to your `Cartfile`: - -``` -github "ReactiveCocoa/ReactiveSwift" -``` -Make sure to add both `ReactiveSwift.framework` and `Result.framework` to "Linked Frameworks and Libraries" and "copy-frameworks" Build Phases. - -Once you’ve set up your project, check out the [Framework Overview][] for -a tour of ReactiveSwift’s concepts, and the [Basic Operators][] for some -introductory examples of using it. - -## Playground - -We also provide a great Playground, so you can get used to ReactiveCocoa's operators. In order to start using it: - - 1. Clone the ReactiveSwift repository. - 1. Retrieve the project dependencies using one of the following terminal commands from the ReactiveSwift project root directory: - - `git submodule update --init --recursive` **OR**, if you have [Carthage](https://github.com/Carthage/Carthage) installed - - `carthage checkout` - 1. Open `ReactiveSwift.xcworkspace` - 1. Build `Result-Mac` scheme - 1. Build `ReactiveSwift-macOS` scheme - 1. Finally open the `ReactiveSwift.playground` - 1. Choose `View > Show Debug Area` - -[Actions]: Documentation/FrameworkOverview.md#actions -[Basic Operators]: Documentation/BasicOperators.md -[CHANGELOG]: CHANGELOG.md -[Code]: ReactiveCocoa -[Documentation]: Documentation -[Events]: Documentation/FrameworkOverview.md#events -[Framework Overview]: Documentation/FrameworkOverview.md -[Schedulers]: Documentation/FrameworkOverview.md#schedulers -[Signal producers]: Documentation/FrameworkOverview.md#signal-producers -[Signals]: Documentation/FrameworkOverview.md#signals -[Swift API]: ReactiveCocoa/Swift -[flatMapLatest]: Documentation/BasicOperators.md#switching-to-the-latest -[retry]: Documentation/BasicOperators.md#retrying diff --git a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme index 6381e6e4d3..2e8560ffbe 100644 --- a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme +++ b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme @@ -31,7 +31,7 @@ BlueprintIdentifier = "D047260B19E49F82006002AA" BuildableName = "ReactiveSwift.framework" BlueprintName = "ReactiveSwift-iOS" - ReferencedContainer = "container:ReactiveSwift.xcodeproj"> + ReferencedContainer = "container:Carthage/Checkouts/ReactiveSwift/ReactiveSwift.xcodeproj"> + ReferencedContainer = "container:Carthage/Checkouts/ReactiveSwift/ReactiveSwift.xcodeproj"> + ReferencedContainer = "container:Carthage/Checkouts/ReactiveSwift/ReactiveSwift.xcodeproj"> + location = "group:Carthage/Checkouts/ReactiveSwift/ReactiveSwift.xcodeproj"> diff --git a/ReactiveSwift.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift b/ReactiveSwift.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift deleted file mode 100644 index c2de48c890..0000000000 --- a/ReactiveSwift.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift +++ /dev/null @@ -1,25 +0,0 @@ -/*: - > # IMPORTANT: To use `ReactiveSwift.playground`, please: - - 1. Retrieve the project dependencies using one of the following terminal commands from the ReactiveSwift project root directory: - - `script/bootstrap` - **OR**, if you have [Carthage](https://github.com/Carthage/Carthage) installed - - `carthage checkout` - 1. Open `ReactiveSwift.xcworkspace` - 1. Build `Result-Mac` scheme - 1. Build `ReactiveSwift-macOS` scheme - 1. Finally open the `ReactiveSwift.playground` - 1. Choose `View > Show Debug Area` - */ - -import Result -import ReactiveSwift -import Foundation - -/*: - ## Sandbox - - A place where you can build your sand castles 🏖. -*/ - - diff --git a/ReactiveSwift.playground/Pages/Signal.xcplaygroundpage/Contents.swift b/ReactiveSwift.playground/Pages/Signal.xcplaygroundpage/Contents.swift deleted file mode 100644 index 385659b078..0000000000 --- a/ReactiveSwift.playground/Pages/Signal.xcplaygroundpage/Contents.swift +++ /dev/null @@ -1,240 +0,0 @@ -/*: -> # IMPORTANT: To use `ReactiveSwift.playground`, please: - -1. Retrieve the project dependencies using one of the following terminal commands from the ReactiveSwift project root directory: - - `script/bootstrap` -**OR**, if you have [Carthage](https://github.com/Carthage/Carthage) installed - - `carthage checkout` -1. Open `ReactiveSwift.xcworkspace` -1. Build `Result-Mac` scheme -1. Build `ReactiveSwift-macOS` scheme -1. Finally open the `ReactiveSwift.playground` -1. Choose `View > Show Debug Area` -*/ - -import Result -import ReactiveSwift -import Foundation - -/*: -## Signal - -A **signal**, represented by the [`Signal`](https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/ReactiveSwift/Signal.swift) type, is any series of [`Event`](https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/ReactiveSwift/Event.swift) values -over time that can be observed. - -Signals are generally used to represent event streams that are already “in progress”, -like notifications, user input, etc. As work is performed or data is received, -events are _sent_ on the signal, which pushes them out to any observers. -All observers see the events at the same time. - -Users must observe a signal in order to access its events. -Observing a signal does not trigger any side effects. In other words, -signals are entirely producer-driven and push-based, and consumers (observers) -cannot have any effect on their lifetime. While observing a signal, the user -can only evaluate the events in the same order as they are sent on the signal. There -is no random access to values of a signal. - -Signals can be manipulated by applying [primitives](https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/BasicOperators.md) to them. -Typical primitives to manipulate a single signal like `filter`, `map` and -`reduce` are available, as well as primitives to manipulate multiple signals -at once (`zip`). Primitives operate only on the `Next` events of a signal. - -The lifetime of a signal consists of any number of `Next` events, followed by -one terminating event, which may be any one of `Failed`, `Completed`, or -`Interrupted` (but not a combination). -Terminating events are not included in the signal’s values—they must be -handled specially. -*/ - -/*: -### `Subscription` -A Signal represents and event stream that is already "in progress", sometimes also called "hot". This means, that a subscriber may miss events that have been sent before the subscription. -Furthermore, the subscription to a signal does not trigger any side effects -*/ -scopedExample("Subscription") { - // Signal.pipe is a way to manually control a signal. the returned observer can be used to send values to the signal - let (signal, observer) = Signal.pipe() - - let subscriber1 = Observer(next: { print("Subscriber 1 received \($0)") } ) - let subscriber2 = Observer(next: { print("Subscriber 2 received \($0)") } ) - - print("Subscriber 1 subscribes to the signal") - signal.observe(subscriber1) - - print("Send value `10` on the signal") - // subscriber1 will receive the value - observer.sendNext(10) - - print("Subscriber 2 subscribes to the signal") - // Notice how nothing happens at this moment, i.e. subscriber2 does not receive the previously sent value - signal.observe(subscriber2) - - print("Send value `20` on the signal") - // Notice that now, subscriber1 and subscriber2 will receive the value - observer.sendNext(20) -} - -/*: -### `empty` -A Signal that completes immediately without emitting any value. -*/ -scopedExample("`empty`") { - let emptySignal = Signal.empty - - let observer = Observer( - next: { _ in print("next not called") }, - failed: { _ in print("error not called") }, - completed: { print("completed not called") }, - interrupted: { print("interrupted called") } - ) - - emptySignal.observe(observer) -} - -/*: -### `never` -A Signal that never sends any events to its observers. -*/ -scopedExample("`never`") { - let neverSignal = Signal.never - - let observer = Observer( - next: { _ in print("next not called") }, - failed: { _ in print("error not called") }, - completed: { print("completed not called") }, - interrupted: { print("interrupted not called") } - ) - - neverSignal.observe(observer) -} - -/*: -## `Operators` -### `uniqueValues` -Forwards only those values from `self` that are unique across the set of -all values that have been seen. - -Note: This causes the values to be retained to check for uniqueness. Providing -a function that returns a unique value for each sent value can help you reduce -the memory footprint. -*/ -scopedExample("`uniqueValues`") { - let (signal, observer) = Signal.pipe() - let subscriber = Observer(next: { print("Subscriber received \($0)") } ) - let uniqueSignal = signal.uniqueValues() - - uniqueSignal.observe(subscriber) - observer.sendNext(1) - observer.sendNext(2) - observer.sendNext(3) - observer.sendNext(4) - observer.sendNext(3) - observer.sendNext(3) - observer.sendNext(5) -} - -/*: -### `map` -Maps each value in the signal to a new value. -*/ -scopedExample("`map`") { - let (signal, observer) = Signal.pipe() - let subscriber = Observer(next: { print("Subscriber received \($0)") } ) - let mappedSignal = signal.map { $0 * 2 } - - mappedSignal.observe(subscriber) - print("Send value `10` on the signal") - observer.sendNext(10) -} - -/*: -### `mapError` -Maps errors in the signal to a new error. -*/ -scopedExample("`mapError`") { - let (signal, observer) = Signal.pipe() - let subscriber = Observer(failed: { print("Subscriber received error: \($0)") } ) - let mappedErrorSignal = signal.mapError { (error:NSError) -> NSError in - let userInfo = [NSLocalizedDescriptionKey: "🔥"] - let code = error.code + 10000 - let mappedError = NSError(domain: "com.reactivecocoa.errordomain", code: code, userInfo: userInfo) - return mappedError - } - - mappedErrorSignal.observe(subscriber) - print("Send error `NSError(domain: \"com.reactivecocoa.errordomain\", code: 4815, userInfo: nil)` on the signal") - observer.sendFailed(NSError(domain: "com.reactivecocoa.errordomain", code: 4815, userInfo: nil)) -} - -/*: -### `filter` -Preserves only the values of the signal that pass the given predicate. -*/ -scopedExample("`filter`") { - let (signal, observer) = Signal.pipe() - let subscriber = Observer(next: { print("Subscriber received \($0)") } ) - // subscriber will only receive events with values greater than 12 - let filteredSignal = signal.filter { $0 > 12 ? true : false } - - filteredSignal.observe(subscriber) - observer.sendNext(10) - observer.sendNext(11) - observer.sendNext(12) - observer.sendNext(13) - observer.sendNext(14) -} - -/*: -### `skipNil` -Unwraps non-`nil` values and forwards them on the returned signal, `nil` -values are dropped. -*/ -scopedExample("`skipNil`") { - let (signal, observer) = Signal.pipe() - // note that the signal is of type `Int?` and observer is of type `Int`, given we're unwrapping - // non-`nil` values - let subscriber = Observer(next: { print("Subscriber received \($0)") } ) - let skipNilSignal = signal.skipNil() - - skipNilSignal.observe(subscriber) - observer.sendNext(1) - observer.sendNext(nil) - observer.sendNext(3) -} - -/*: -### `take(first:)` -Returns a signal that will yield the first `count` values from `self` -*/ -scopedExample("`take(first:)`") { - let (signal, observer) = Signal.pipe() - let subscriber = Observer(next: { print("Subscriber received \($0)") } ) - let takeSignal = signal.take(first: 2) - - takeSignal.observe(subscriber) - observer.sendNext(1) - observer.sendNext(2) - observer.sendNext(3) - observer.sendNext(4) -} - -/*: -### `collect` -Returns a signal that will yield an array of values when `self` completes. -- Note: When `self` completes without collecting any value, it will send -an empty array of values. -*/ -scopedExample("`collect`") { - let (signal, observer) = Signal.pipe() - // note that the signal is of type `Int` and observer is of type `[Int]` given we're "collecting" - // `Int` values for the lifetime of the signal - let subscriber = Observer<[Int], NoError>(next: { print("Subscriber received \($0)") } ) - let collectSignal = signal.collect() - - collectSignal.observe(subscriber) - observer.sendNext(1) - observer.sendNext(2) - observer.sendNext(3) - observer.sendNext(4) - observer.sendCompleted() -} diff --git a/ReactiveSwift.playground/Pages/SignalProducer.xcplaygroundpage/Contents.swift b/ReactiveSwift.playground/Pages/SignalProducer.xcplaygroundpage/Contents.swift deleted file mode 100644 index 0bc7a9dfbf..0000000000 --- a/ReactiveSwift.playground/Pages/SignalProducer.xcplaygroundpage/Contents.swift +++ /dev/null @@ -1,767 +0,0 @@ -/*: -> # IMPORTANT: To use `ReactiveSwift.playground`, please: - -1. Retrieve the project dependencies using one of the following terminal commands from the ReactiveSwift project root directory: - - `script/bootstrap` - **OR**, if you have [Carthage](https://github.com/Carthage/Carthage) installed - - `carthage checkout` -1. Open `ReactiveSwift.xcworkspace` -1. Build `Result-Mac` scheme -1. Build `ReactiveSwift-macOS` scheme -1. Finally open the `ReactiveSwift.playground` -1. Choose `View > Show Debug Area` -*/ - -import Result -import ReactiveSwift -import Foundation - -/*: -## SignalProducer - -A **signal producer**, represented by the [`SignalProducer`](https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/ReactiveSwift/SignalProducer.swift) type, creates -[signals](https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/ReactiveSwift/Signal.swift) and performs side effects. - -They can be used to represent operations or tasks, like network -requests, where each invocation of `start()` will create a new underlying -operation, and allow the caller to observe the result(s). The -`startWithSignal()` variant gives access to the produced signal, allowing it to -be observed multiple times if desired. - -Because of the behavior of `start()`, each signal created from the same -producer may see a different ordering or version of events, or the stream might -even be completely different! Unlike a plain signal, no work is started (and -thus no events are generated) until an observer is attached, and the work is -restarted anew for each additional observer. - -Starting a signal producer returns a [disposable](#disposables) that can be used to -interrupt/cancel the work associated with the produced signal. - -Just like signals, signal producers can also be manipulated via primitives -like `map`, `filter`, etc. -Every signal primitive can be “lifted” to operate upon signal producers instead, -using the `lift` method. -Furthermore, there are additional primitives that control _when_ and _how_ work -is started—for example, `times`. -*/ - -/*: -### `Subscription` -A SignalProducer represents an operation that can be started on demand. Starting the operation returns a Signal on which the result(s) of the operation can be observed. This behavior is sometimes also called "cold". -This means that a subscriber will never miss any values sent by the SignalProducer. -*/ -scopedExample("Subscription") { - let producer = SignalProducer { observer, _ in - print("New subscription, starting operation") - observer.sendNext(1) - observer.sendNext(2) - } - - let subscriber1 = Observer(next: { print("Subscriber 1 received \($0)") }) - let subscriber2 = Observer(next: { print("Subscriber 2 received \($0)") }) - - print("Subscriber 1 subscribes to producer") - producer.start(subscriber1) - - print("Subscriber 2 subscribes to producer") - // Notice, how the producer will start the work again - producer.start(subscriber2) -} - -/*: -### `empty` -A producer for a Signal that will immediately complete without sending -any values. -*/ -scopedExample("`empty`") { - let emptyProducer = SignalProducer.empty - - let observer = Observer( - next: { _ in print("next not called") }, - failed: { _ in print("error not called") }, - completed: { print("completed called") } - ) - - emptyProducer.start(observer) -} - -/*: -### `never` -A producer for a Signal that never sends any events to its observers. -*/ -scopedExample("`never`") { - let neverProducer = SignalProducer.never - - let observer = Observer( - next: { _ in print("next not called") }, - failed: { _ in print("error not called") }, - completed: { print("completed not called") } - ) - - neverProducer.start(observer) -} - -/*: -### `startWithSignal` -Creates a Signal from the producer, passes it into the given closure, -then starts sending events on the Signal when the closure has returned. - -The closure will also receive a disposable which can be used to -interrupt the work associated with the signal and immediately send an -`Interrupted` event. -*/ -scopedExample("`startWithSignal`") { - var started = false - var value: Int? - - SignalProducer(value: 42) - .on(next: { - value = $0 - }) - .startWithSignal { signal, disposable in - print(value) - } - - print(value) -} - -/*: -### `startWithResult` -Creates a Signal from the producer, then adds exactly one observer to -the Signal, which will invoke the given callback when `next` or `failed` -events are received. - -Returns a Disposable which can be used to interrupt the work associated -with the Signal, and prevent any future callbacks from being invoked. -*/ -scopedExample("`startWithResult`") { - SignalProducer(value: 42) - .startWithResult { result in - print(result.value) - } -} - -/*: -### `startWithNext` -Creates a Signal from the producer, then adds exactly one observer to -the Signal, which will invoke the given callback when `next` events are -received. - -This method is available only if the producer never emits error, or in -other words, has an error type of `NoError`. - -Returns a Disposable which can be used to interrupt the work associated -with the Signal, and prevent any future callbacks from being invoked. -*/ -scopedExample("`startWithNext`") { - SignalProducer(value: 42) - .startWithNext { value in - print(value) - } -} - -/*: -### `startWithCompleted` -Creates a Signal from the producer, then adds exactly one observer to -the Signal, which will invoke the given callback when a `completed` event is -received. - -Returns a Disposable which can be used to interrupt the work associated -with the Signal. -*/ -scopedExample("`startWithCompleted`") { - SignalProducer(value: 42) - .startWithCompleted { - print("completed called") - } -} - -/*: -### `startWithFailed` -Creates a Signal from the producer, then adds exactly one observer to -the Signal, which will invoke the given callback when a `failed` event is -received. - -Returns a Disposable which can be used to interrupt the work associated -with the Signal. -*/ -scopedExample("`startWithFailed`") { - SignalProducer(error: NSError(domain: "example", code: 42, userInfo: nil)) - .startWithFailed { error in - print(error) - } -} - -/*: -### `startWithInterrupted` -Creates a Signal from the producer, then adds exactly one observer to -the Signal, which will invoke the given callback when an `interrupted` event -is received. - -Returns a Disposable which can be used to interrupt the work associated -with the Signal. -*/ -scopedExample("`startWithInterrupted`") { - let disposable = SignalProducer.never - .startWithInterrupted { - print("interrupted called") - } - - disposable.dispose() -} - - -/*: -### `lift` -Lifts an unary Signal operator to operate upon SignalProducers instead. - -In other words, this will create a new SignalProducer which will apply -the given Signal operator to _every_ created Signal, just as if the -operator had been applied to each Signal yielded from start(). -*/ -scopedExample("`lift`") { - var counter = 0 - let transform: (Signal) -> Signal = { signal in - counter = 42 - return signal - } - - SignalProducer(value: 0) - .lift(transform) - .startWithNext { _ in - print(counter) - } -} - -/*: -### `map` -Maps each value in the producer to a new value. -*/ -scopedExample("`map`") { - SignalProducer(value: 1) - .map { $0 + 41 } - .startWithNext { value in - print(value) - } -} - -/*: -### `mapError` -Maps errors in the producer to a new error. -*/ -scopedExample("`mapError`") { - SignalProducer(error: NSError(domain: "mapError", code: 42, userInfo: nil)) - .mapError { PlaygroundError.example($0.description) } - .startWithFailed { error in - print(error) - } -} - -/*: -### `filter` -Preserves only the values of the producer that pass the given predicate. -*/ -scopedExample("`filter`") { - SignalProducer(values: [ 1, 2, 3, 4 ]) - .filter { $0 > 3 } - .startWithNext { value in - print(value) - } -} - -/*: -### `take(first:)` -Returns a producer that will yield the first `count` values from the -input producer. -*/ -scopedExample("`take(first:)`") { - SignalProducer(values: [ 1, 2, 3, 4 ]) - .take(first: 2) - .startWithNext { value in - print(value) - } -} - -/*: -### `observe(on:)` -Forwards all events onto the given scheduler, instead of whichever -scheduler they originally arrived upon. -*/ -scopedExample("`observe(on:)`") { - let baseProducer = SignalProducer(values: [ 1, 2, 3, 4 ]) - let completion = { print("is main thread? \(Thread.current.isMainThread)") } - - baseProducer - .observe(on: QueueScheduler(qos: .default, name: "test")) - .startWithCompleted(completion) - - baseProducer - .startWithCompleted(completion) -} - -/*: -### `collect` -Returns a producer that will yield an array of values until it completes. -*/ -scopedExample("`collect`") { - SignalProducer { observer, disposable in - observer.sendNext(1) - observer.sendNext(2) - observer.sendNext(3) - observer.sendNext(4) - observer.sendCompleted() - } - .collect() - .startWithNext { value in - print(value) - } -} - -/*: -### `collect(count:)` -Returns a producer that will yield an array of values until it reaches a certain count. -*/ -scopedExample("`collect(count:)`") { - SignalProducer { observer, disposable in - observer.sendNext(1) - observer.sendNext(2) - observer.sendNext(3) - observer.sendNext(4) - observer.sendCompleted() - } - .collect(count: 2) - .startWithNext { value in - print(value) - } -} - -/*: -### `collect(_:)` matching values inclusively -Returns a producer that will yield an array of values based on a predicate -which matches the values collected. - -When producer completes any remaining values will be sent, the last values -array may not match `predicate`. Alternatively, if were not collected any -values will sent an empty array of values. -*/ -scopedExample("`collect(_:)` matching values inclusively") { - SignalProducer { observer, disposable in - observer.sendNext(1) - observer.sendNext(2) - observer.sendNext(3) - observer.sendNext(4) - observer.sendCompleted() - } - .collect { values in values.reduce(0, +) == 3 } - .startWithNext { value in - print(value) - } -} - -/*: -### `collect(_:)` matching values exclusively -Returns a producer that will yield an array of values based on a predicate -which matches the values collected and the next value. - -When producer completes any remaining values will be sent, the last values -array may not match `predicate`. Alternatively, if were not collected any -values will sent an empty array of values. -*/ -scopedExample("`collect(_:)` matching values exclusively") { - SignalProducer { observer, disposable in - observer.sendNext(1) - observer.sendNext(2) - observer.sendNext(3) - observer.sendNext(4) - observer.sendCompleted() - } - .collect { values, next in next == 3 } - .startWithNext { value in - print(value) - } -} - -/*: -### `combineLatest(with:)` -Combines the latest value of the receiver with the latest value from -the given producer. - -The returned producer will not send a value until both inputs have sent at -least one value each. If either producer is interrupted, the returned producer -will also be interrupted. -*/ -scopedExample("`combineLatest(with:)`") { - let producer1 = SignalProducer(values: [ 1, 2, 3, 4 ]) - let producer2 = SignalProducer(values: [ 1, 2 ]) - - producer1 - .combineLatest(with: producer2) - .startWithNext { value in - print("\(value)") - } -} - -/*: -### `skip(first:)` -Returns a producer that will skip the first `count` values, then forward -everything afterward. -*/ -scopedExample("`skip(first:)`") { - SignalProducer(values: [ 1, 2, 3, 4 ]) - .skip(first: 2) - .startWithNext { value in - print(value) - } -} - -/*: -### `materialize` - -Treats all Events from the input producer as plain values, allowing them to be -manipulated just like any other value. - -In other words, this brings Events “into the monad.” - -When a Completed or Failed event is received, the resulting producer will send -the Event itself and then complete. When an Interrupted event is received, -the resulting producer will send the Event itself and then interrupt. -*/ -scopedExample("`materialize`") { - SignalProducer(values: [ 1, 2, 3, 4 ]) - .materialize() - .startWithNext { value in - print(value) - } -} - -/*: -### `sample(on:)` -Forwards the latest value from `self` whenever `sampler` sends a Next -event. - -If `sampler` fires before a value has been observed on `self`, nothing -happens. - -Returns a producer that will send values from `self`, sampled (possibly -multiple times) by `sampler`, then complete once both input producers have -completed, or interrupt if either input producer is interrupted. -*/ -scopedExample("`sample(on:)`") { - let baseProducer = SignalProducer(values: [ 1, 2, 3, 4 ]) - let sampledOnProducer = SignalProducer(values: [ 1, 2 ]) - .map { _ in () } - - baseProducer - .sample(on: sampledOnProducer) - .startWithNext { value in - print(value) - } -} - -/*: -### `combinePrevious` -Forwards events from `self` with history: values of the returned producer -are a tuple whose first member is the previous value and whose second member -is the current value. `initial` is supplied as the first member when `self` -sends its first value. -*/ -scopedExample("`combinePrevious`") { - SignalProducer(values: [ 1, 2, 3, 4 ]) - .combinePrevious(42) - .startWithNext { value in - print("\(value)") - } -} - -/*: -### `scan` -Aggregates `self`'s values into a single combined value. When `self` emits -its first value, `combine` is invoked with `initial` as the first argument and -that emitted value as the second argument. The result is emitted from the -producer returned from `scan`. That result is then passed to `combine` as the -first argument when the next value is emitted, and so on. -*/ -scopedExample("`scan`") { - SignalProducer(values: [ 1, 2, 3, 4 ]) - .scan(0, +) - .startWithNext { value in - print(value) - } -} - -/*: -### `reduce` -Like `scan`, but sends only the final value and then immediately completes. -*/ -scopedExample("`reduce`") { - SignalProducer(values: [ 1, 2, 3, 4 ]) - .reduce(0, +) - .startWithNext { value in - print(value) - } -} - -/*: -### `skipRepeats` -Forwards only those values from `self` which do not pass `isRepeat` with -respect to the previous value. The first value is always forwarded. -*/ -scopedExample("`skipRepeats`") { - SignalProducer(values: [ 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 1, 1, 1, 2, 2, 2, 4 ]) - .skipRepeats(==) - .startWithNext { value in - print(value) - } -} - -/*: -### `skip(while:)` -Does not forward any values from `self` until `predicate` returns false, -at which point the returned signal behaves exactly like `self`. -*/ -scopedExample("`skip(while:)`") { - // Note that trailing closure is used for `skip(while:)`. - SignalProducer(values: [ 3, 3, 3, 3, 1, 2, 3, 4 ]) - .skip { $0 > 2 } - .startWithNext { value in - print(value) - } -} - -/*: -### `take(untilReplacement:)` -Forwards events from `self` until `replacement` begins sending events. - -Returns a producer which passes through `Next`, `Failed`, and `Interrupted` -events from `self` until `replacement` sends an event, at which point the -returned producer will send that event and switch to passing through events -from `replacement` instead, regardless of whether `self` has sent events -already. -*/ -scopedExample("`take(untilReplacement:)`") { - let (replacementSignal, incomingReplacementObserver) = Signal.pipe() - - let baseProducer = SignalProducer { incomingObserver, _ in - incomingObserver.sendNext(1) - incomingObserver.sendNext(2) - incomingObserver.sendNext(3) - - incomingReplacementObserver.sendNext(42) - - incomingObserver.sendNext(4) - - incomingReplacementObserver.sendNext(42) - } - - baseProducer - .take(untilReplacement: replacementSignal) - .startWithNext { value in - print(value) - } -} - -/*: -### `take(last:)` -Waits until `self` completes and then forwards the final `count` values -on the returned producer. -*/ -scopedExample("`take(last:)`") { - SignalProducer(values: [ 1, 2, 3, 4 ]) - .take(last: 2) - .startWithNext { value in - print(value) - } -} - -/*: -### `skipNil` -Unwraps non-`nil` values and forwards them on the returned signal, `nil` -values are dropped. -*/ -scopedExample("`skipNil`") { - SignalProducer(values: [ nil, 1, 2, nil, 3, 4, nil ]) - .skipNil() - .startWithNext { value in - print(value) - } -} - - -/*: -### `zip(with:)` -Zips elements of two producers into pairs. The elements of any Nth pair -are the Nth elements of the two input producers. -*/ -scopedExample("`zip(with:)`") { - let baseProducer = SignalProducer(values: [ 1, 2, 3, 4 ]) - let zippedProducer = SignalProducer(values: [ 42, 43 ]) - - baseProducer - .zip(with: zippedProducer) - .startWithNext { value in - print("\(value)") - } -} - -/*: -### `times` -Repeats `self` a total of `count` times. Repeating `1` times results in -an equivalent signal producer. -*/ -scopedExample("`times`") { - var counter = 0 - - SignalProducer<(), NoError> { observer, disposable in - counter += 1 - observer.sendCompleted() - } - .times(42) - .start() - - print(counter) -} - -/*: -### `retry(upTo:)` -Ignores failures up to `count` times. -*/ -scopedExample("`retry(upTo:)`") { - var tries = 0 - - SignalProducer { observer, disposable in - if tries == 0 { - tries += 1 - observer.sendFailed(NSError(domain: "retry", code: 0, userInfo: nil)) - } else { - observer.sendNext(42) - observer.sendCompleted() - } - } - .retry(upTo: 1) - .startWithResult { result in - print(result) - } -} - -/*: -### `then` -Waits for completion of `producer`, *then* forwards all events from -`replacement`. Any failure sent from `producer` is forwarded immediately, in -which case `replacement` will not be started, and none of its events will be -be forwarded. All values sent from `producer` are ignored. -*/ -scopedExample("`then`") { - let baseProducer = SignalProducer(values: [ 1, 2, 3, 4 ]) - let thenProducer = SignalProducer(value: 42) - - baseProducer - .then(thenProducer) - .startWithNext { value in - print(value) - } -} - -/*: -### `replayLazily(upTo:)` -Creates a new `SignalProducer` that will multicast values emitted by -the underlying producer, up to `capacity`. -This means that all clients of this `SignalProducer` will see the same version -of the emitted values/errors. - -The underlying `SignalProducer` will not be started until `self` is started -for the first time. When subscribing to this producer, all previous values -(up to `capacity`) will be emitted, followed by any new values. - -If you find yourself needing *the current value* (the last buffered value) -you should consider using `PropertyType` instead, which, unlike this operator, -will guarantee at compile time that there's always a buffered value. -This operator is not recommended in most cases, as it will introduce an implicit -relationship between the original client and the rest, so consider alternatives -like `PropertyType`, `SignalProducer.buffer`, or representing your stream using -a `Signal` instead. - -This operator is only recommended when you absolutely need to introduce -a layer of caching in front of another `SignalProducer`. - -This operator has the same semantics as `SignalProducer.buffer`. -*/ -scopedExample("`replayLazily(upTo:)`") { - let baseProducer = SignalProducer(values: [ 1, 2, 3, 4, 42 ]) - .replayLazily(upTo: 2) - - baseProducer.startWithNext { value in - print(value) - } - - baseProducer.startWithNext { value in - print(value) - } - - baseProducer.startWithNext { value in - print(value) - } -} - -/*: -### `flatMap(.latest)` -Maps each event from `self` to a new producer, then flattens the -resulting producers (into a producer of values), according to the -semantics of the given strategy. - -If `self` or any of the created producers fail, the returned producer -will forward that failure immediately. -*/ -scopedExample("`flatMap(.latest)`") { - SignalProducer(values: [ 1, 2, 3, 4 ]) - .flatMap(.latest) { SignalProducer(value: $0 + 3) } - .startWithNext { value in - print(value) - } -} - -/*: -### `flatMapError` -Catches any failure that may occur on the input producer, mapping to a new producer -that starts in its place. -*/ -scopedExample("`flatMapError`") { - SignalProducer(error: NSError(domain: "flatMapError", code: 42, userInfo: nil)) - .flatMapError { SignalProducer(value: $0.code) } - .startWithNext { value in - print(value) - } -} - -/*: -### `sample(with:)` -Forwards the latest value from `self` with the value from `sampler` as a tuple, -only when `sampler` sends a Next event. - -If `sampler` fires before a value has been observed on `self`, nothing happens. -Returns a producer that will send values from `self` and `sampler`, -sampled (possibly multiple times) by `sampler`, then complete once both -input producers have completed, or interrupt if either input producer is interrupted. -*/ -scopedExample("`sample(with:)`") { - let producer = SignalProducer(values: [ 1, 2, 3, 4 ]) - let sampler = SignalProducer(values: [ "a", "b" ]) - - let result = producer.sample(with: sampler) - - result.startWithNext { left, right in - print("\(left) \(right)") - } -} - -/*: -### `logEvents` -Logs all events that the receiver sends. -By default, it will print to the standard output. -*/ -scopedExample("`log events`") { - let baseProducer = SignalProducer(values: [ 1, 2, 3, 4, 42 ]) - - baseProducer - .logEvents(identifier: "Playground is fun!") - .start() -} diff --git a/ReactiveSwift.playground/Sources/PlaygroundUtility.swift b/ReactiveSwift.playground/Sources/PlaygroundUtility.swift deleted file mode 100644 index 282b07f49e..0000000000 --- a/ReactiveSwift.playground/Sources/PlaygroundUtility.swift +++ /dev/null @@ -1,10 +0,0 @@ -import Foundation - -public func scopedExample(_ exampleDescription: String, _ action: () -> Void) { - print("\n--- \(exampleDescription) ---\n") - action() -} - -public enum PlaygroundError: Error { - case example(String) -} diff --git a/ReactiveSwift.playground/contents.xcplayground b/ReactiveSwift.playground/contents.xcplayground deleted file mode 100644 index 43399d296e..0000000000 --- a/ReactiveSwift.playground/contents.xcplayground +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/ReactiveSwift.xcodeproj/project.pbxproj b/ReactiveSwift.xcodeproj/project.pbxproj deleted file mode 100644 index aed684e9b1..0000000000 --- a/ReactiveSwift.xcodeproj/project.pbxproj +++ /dev/null @@ -1,1596 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 02D2602A1C1D6DAF003ACC61 /* SignalLifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02D260291C1D6DAF003ACC61 /* SignalLifetimeSpec.swift */; }; - 02D2602B1C1D6DB8003ACC61 /* SignalLifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02D260291C1D6DAF003ACC61 /* SignalLifetimeSpec.swift */; }; - 4A0E10FF1D2A92720065D310 /* Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* Lifetime.swift */; }; - 4A0E11001D2A92720065D310 /* Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* Lifetime.swift */; }; - 4A0E11011D2A92720065D310 /* Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* Lifetime.swift */; }; - 4A0E11021D2A92720065D310 /* Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* Lifetime.swift */; }; - 4A0E11041D2A95200065D310 /* LifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E11031D2A95200065D310 /* LifetimeSpec.swift */; }; - 4A0E11051D2A95200065D310 /* LifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E11031D2A95200065D310 /* LifetimeSpec.swift */; }; - 4A0E11061D2A95200065D310 /* LifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E11031D2A95200065D310 /* LifetimeSpec.swift */; }; - 579504331BB8A34200A5E482 /* BagSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312EF19EF2A7700984962 /* BagSpec.swift */; }; - 579504341BB8A34300A5E482 /* BagSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312EF19EF2A7700984962 /* BagSpec.swift */; }; - 57A4D1B11BA13D7A00F7D4B1 /* Optional.swift in Sources */ = {isa = PBXBuildFile; fileRef = D871D69E1B3B29A40070F16C /* Optional.swift */; }; - 57A4D1B41BA13D7A00F7D4B1 /* Disposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BE19EF2A5800984962 /* Disposable.swift */; }; - 57A4D1B61BA13D7A00F7D4B1 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B51A69A3DB00AD8286 /* Event.swift */; }; - 57A4D1B81BA13D7A00F7D4B1 /* Scheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312C819EF2A5800984962 /* Scheduler.swift */; }; - 57A4D1B91BA13D7A00F7D4B1 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54AF1A69A2AC00AD8286 /* Action.swift */; }; - 57A4D1BA1BA13D7A00F7D4B1 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B01A69A2AC00AD8286 /* Property.swift */; }; - 57A4D1BB1BA13D7A00F7D4B1 /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B11A69A2AC00AD8286 /* Signal.swift */; }; - 57A4D1BC1BA13D7A00F7D4B1 /* SignalProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B21A69A2AC00AD8286 /* SignalProducer.swift */; }; - 57A4D1BD1BA13D7A00F7D4B1 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BB19EF2A5800984962 /* Atomic.swift */; }; - 57A4D1BE1BA13D7A00F7D4B1 /* Bag.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BC19EF2A5800984962 /* Bag.swift */; }; - 57A4D1BF1BA13D7A00F7D4B1 /* TupleExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00004081A46864E000E7D41 /* TupleExtensions.swift */; }; - 57A4D1C01BA13D7A00F7D4B1 /* FoundationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B4A3C19F4C39A009E02AC /* FoundationExtensions.swift */; }; - 57A4D2081BA13D7A00F7D4B1 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; - 57A4D20A1BA13D7A00F7D4B1 /* ReactiveSwift.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveSwift.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 7DFBED081CDB8C9500EE435B /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57A4D2411BA13D7A00F7D4B1 /* ReactiveSwift.framework */; }; - 7DFBED1E1CDB8D7000EE435B /* ReactiveSwift.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 57A4D2411BA13D7A00F7D4B1 /* ReactiveSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 7DFBED1F1CDB8D7800EE435B /* Quick.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 7DFBED201CDB8D7D00EE435B /* Nimble.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 7DFBED211CDB8D8300EE435B /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 7DFBED221CDB8DE300EE435B /* ActionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D021671C1A6CD50500987861 /* ActionSpec.swift */; }; - 7DFBED231CDB8DE300EE435B /* AtomicSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312EE19EF2A7700984962 /* AtomicSpec.swift */; }; - 7DFBED241CDB8DE300EE435B /* BagSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312EF19EF2A7700984962 /* BagSpec.swift */; }; - 7DFBED251CDB8DE300EE435B /* DisposableSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312F019EF2A7700984962 /* DisposableSpec.swift */; }; - 7DFBED261CDB8DE300EE435B /* FoundationExtensionsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8170FC01B100EBC004192AD /* FoundationExtensionsSpec.swift */; }; - 7DFBED281CDB8DE300EE435B /* PropertySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260D1A72F16D00D33B74 /* PropertySpec.swift */; }; - 7DFBED291CDB8DE300EE435B /* SchedulerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312F219EF2A7700984962 /* SchedulerSpec.swift */; }; - 7DFBED2A1CDB8DE300EE435B /* SignalLifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02D260291C1D6DAF003ACC61 /* SignalLifetimeSpec.swift */; }; - 7DFBED2B1CDB8DE300EE435B /* SignalProducerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260A1A72E6C500D33B74 /* SignalProducerSpec.swift */; }; - 7DFBED2C1CDB8DE300EE435B /* SignalProducerLiftingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8024DB11B2E1BB0005E6B9A /* SignalProducerLiftingSpec.swift */; }; - 7DFBED2D1CDB8DE300EE435B /* SignalSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A226071A72E0E900D33B74 /* SignalSpec.swift */; }; - 7DFBED2E1CDB8DE300EE435B /* FlattenSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA6F284F1C52626B001879D2 /* FlattenSpec.swift */; }; - 7DFBED2F1CDB8DE300EE435B /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B696FB801A7640C00075236D /* TestError.swift */; }; - 7DFBED301CDB8DE300EE435B /* TestLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79B64731CD38B2B003F2376 /* TestLogger.swift */; }; - 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */; }; - 9ABCB1851D2A5B5A00BCA243 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ABCB1841D2A5B5A00BCA243 /* Deprecations+Removals.swift */; }; - 9ABCB1861D2A5B5A00BCA243 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ABCB1841D2A5B5A00BCA243 /* Deprecations+Removals.swift */; }; - 9ABCB1871D2A5B5A00BCA243 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ABCB1841D2A5B5A00BCA243 /* Deprecations+Removals.swift */; }; - 9ABCB1881D2A5B5A00BCA243 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ABCB1841D2A5B5A00BCA243 /* Deprecations+Removals.swift */; }; - A9B315BC1B3940810001CB9C /* Disposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BE19EF2A5800984962 /* Disposable.swift */; }; - A9B315BE1B3940810001CB9C /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B51A69A3DB00AD8286 /* Event.swift */; }; - A9B315C01B3940810001CB9C /* Scheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312C819EF2A5800984962 /* Scheduler.swift */; }; - A9B315C11B3940810001CB9C /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54AF1A69A2AC00AD8286 /* Action.swift */; }; - A9B315C21B3940810001CB9C /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B01A69A2AC00AD8286 /* Property.swift */; }; - A9B315C31B3940810001CB9C /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B11A69A2AC00AD8286 /* Signal.swift */; }; - A9B315C41B3940810001CB9C /* SignalProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B21A69A2AC00AD8286 /* SignalProducer.swift */; }; - A9B315C51B3940810001CB9C /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BB19EF2A5800984962 /* Atomic.swift */; }; - A9B315C61B3940810001CB9C /* Bag.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BC19EF2A5800984962 /* Bag.swift */; }; - A9B315C71B3940810001CB9C /* TupleExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00004081A46864E000E7D41 /* TupleExtensions.swift */; }; - A9B315C81B3940810001CB9C /* FoundationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B4A3C19F4C39A009E02AC /* FoundationExtensions.swift */; }; - A9B315C91B3940980001CB9C /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; - A9B315CA1B3940AB0001CB9C /* ReactiveSwift.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveSwift.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9F793341B60D0140026BCBA /* Optional.swift in Sources */ = {isa = PBXBuildFile; fileRef = D871D69E1B3B29A40070F16C /* Optional.swift */; }; - B696FB811A7640C00075236D /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B696FB801A7640C00075236D /* TestError.swift */; }; - B696FB821A7640C00075236D /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B696FB801A7640C00075236D /* TestError.swift */; }; - BE9CF3951D751B6B003AE479 /* UnidirectionalBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE9CF3941D751B6B003AE479 /* UnidirectionalBinding.swift */; }; - BE9CF3961D751B70003AE479 /* UnidirectionalBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE9CF3941D751B6B003AE479 /* UnidirectionalBinding.swift */; }; - BE9CF3971D751B71003AE479 /* UnidirectionalBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE9CF3941D751B6B003AE479 /* UnidirectionalBinding.swift */; }; - BE9CF3981D751B71003AE479 /* UnidirectionalBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE9CF3941D751B6B003AE479 /* UnidirectionalBinding.swift */; }; - BFA6B94D1A7604D400C846D1 /* SignalProducerNimbleMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */; }; - BFA6B94E1A7604D500C846D1 /* SignalProducerNimbleMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */; }; - C79B64741CD38B2B003F2376 /* TestLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79B64731CD38B2B003F2376 /* TestLogger.swift */; }; - C79B64751CD38B2B003F2376 /* TestLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79B64731CD38B2B003F2376 /* TestLogger.swift */; }; - C79B647C1CD52E23003F2376 /* EventLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79B647B1CD52E23003F2376 /* EventLogger.swift */; }; - C79B647D1CD52E4A003F2376 /* EventLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79B647B1CD52E23003F2376 /* EventLogger.swift */; }; - C79B647F1CD52E4D003F2376 /* EventLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79B647B1CD52E23003F2376 /* EventLogger.swift */; }; - C79B64801CD52E4E003F2376 /* EventLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79B647B1CD52E23003F2376 /* EventLogger.swift */; }; - CA6F28501C52626B001879D2 /* FlattenSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA6F284F1C52626B001879D2 /* FlattenSpec.swift */; }; - CA6F28511C52626B001879D2 /* FlattenSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA6F284F1C52626B001879D2 /* FlattenSpec.swift */; }; - CDC42E2F1AE7AB8B00965373 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; - CDC42E301AE7AB8B00965373 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; - CDC42E311AE7AB8B00965373 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; - CDC42E331AE7AC6D00965373 /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - CDCD247A1C277EEC00710AEE /* AtomicSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312EE19EF2A7700984962 /* AtomicSpec.swift */; }; - CDCD247B1C277EED00710AEE /* AtomicSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312EE19EF2A7700984962 /* AtomicSpec.swift */; }; - CDF066CA1CDC1CA200199626 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; }; - CDF066CB1CDC1CA200199626 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; }; - D00004091A46864E000E7D41 /* TupleExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00004081A46864E000E7D41 /* TupleExtensions.swift */; }; - D000040A1A46864E000E7D41 /* TupleExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00004081A46864E000E7D41 /* TupleExtensions.swift */; }; - D01B7B6219EDD8FE00D26E01 /* Nimble.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - D01B7B6319EDD8FE00D26E01 /* Quick.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - D01B7B6419EDD94B00D26E01 /* ReactiveSwift.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D047260C19E49F82006002AA /* ReactiveSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - D021671D1A6CD50500987861 /* ActionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D021671C1A6CD50500987861 /* ActionSpec.swift */; }; - D021671E1A6CD50500987861 /* ActionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D021671C1A6CD50500987861 /* ActionSpec.swift */; }; - D037666419EDA43C00A782A9 /* ReactiveSwift.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveSwift.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037672D19EDA75D00A782A9 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; }; - D037672F19EDA78B00A782A9 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; }; - D03B4A3D19F4C39A009E02AC /* FoundationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B4A3C19F4C39A009E02AC /* FoundationExtensions.swift */; }; - D03B4A3E19F4C39A009E02AC /* FoundationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B4A3C19F4C39A009E02AC /* FoundationExtensions.swift */; }; - D04725F019E49ED7006002AA /* ReactiveSwift.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveSwift.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D04725F619E49ED7006002AA /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D04725EA19E49ED7006002AA /* ReactiveSwift.framework */; }; - D047261719E49F82006002AA /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D047260C19E49F82006002AA /* ReactiveSwift.framework */; }; - D05E662519EDD82000904ACA /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; }; - D05E662619EDD83000904ACA /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; }; - D08C54B31A69A2AE00AD8286 /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B11A69A2AC00AD8286 /* Signal.swift */; }; - D08C54B41A69A2AF00AD8286 /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B11A69A2AC00AD8286 /* Signal.swift */; }; - D08C54B61A69A3DB00AD8286 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B51A69A3DB00AD8286 /* Event.swift */; }; - D08C54B71A69A3DB00AD8286 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B51A69A3DB00AD8286 /* Event.swift */; }; - D08C54B81A69A9D000AD8286 /* SignalProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B21A69A2AC00AD8286 /* SignalProducer.swift */; }; - D08C54B91A69A9D100AD8286 /* SignalProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B21A69A2AC00AD8286 /* SignalProducer.swift */; }; - D08C54BA1A69C54300AD8286 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B01A69A2AC00AD8286 /* Property.swift */; }; - D08C54BB1A69C54400AD8286 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54B01A69A2AC00AD8286 /* Property.swift */; }; - D0A226081A72E0E900D33B74 /* SignalSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A226071A72E0E900D33B74 /* SignalSpec.swift */; }; - D0A226091A72E0E900D33B74 /* SignalSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A226071A72E0E900D33B74 /* SignalSpec.swift */; }; - D0A2260B1A72E6C500D33B74 /* SignalProducerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260A1A72E6C500D33B74 /* SignalProducerSpec.swift */; }; - D0A2260C1A72E6C500D33B74 /* SignalProducerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260A1A72E6C500D33B74 /* SignalProducerSpec.swift */; }; - D0A2260E1A72F16D00D33B74 /* PropertySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260D1A72F16D00D33B74 /* PropertySpec.swift */; }; - D0A2260F1A72F16D00D33B74 /* PropertySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260D1A72F16D00D33B74 /* PropertySpec.swift */; }; - D0C312CD19EF2A5800984962 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BB19EF2A5800984962 /* Atomic.swift */; }; - D0C312CE19EF2A5800984962 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BB19EF2A5800984962 /* Atomic.swift */; }; - D0C312CF19EF2A5800984962 /* Bag.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BC19EF2A5800984962 /* Bag.swift */; }; - D0C312D019EF2A5800984962 /* Bag.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BC19EF2A5800984962 /* Bag.swift */; }; - D0C312D319EF2A5800984962 /* Disposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BE19EF2A5800984962 /* Disposable.swift */; }; - D0C312D419EF2A5800984962 /* Disposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312BE19EF2A5800984962 /* Disposable.swift */; }; - D0C312E719EF2A5800984962 /* Scheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312C819EF2A5800984962 /* Scheduler.swift */; }; - D0C312E819EF2A5800984962 /* Scheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312C819EF2A5800984962 /* Scheduler.swift */; }; - D0C3130C19EF2B1F00984962 /* DisposableSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312F019EF2A7700984962 /* DisposableSpec.swift */; }; - D0C3130E19EF2B1F00984962 /* SchedulerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312F219EF2A7700984962 /* SchedulerSpec.swift */; }; - D0C3131219EF2B2000984962 /* DisposableSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312F019EF2A7700984962 /* DisposableSpec.swift */; }; - D0C3131419EF2B2000984962 /* SchedulerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312F219EF2A7700984962 /* SchedulerSpec.swift */; }; - D0D11AB91A6AE87700C1F8B1 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54AF1A69A2AC00AD8286 /* Action.swift */; }; - D0D11ABA1A6AE87700C1F8B1 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C54AF1A69A2AC00AD8286 /* Action.swift */; }; - D8024DB21B2E1BB0005E6B9A /* SignalProducerLiftingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8024DB11B2E1BB0005E6B9A /* SignalProducerLiftingSpec.swift */; }; - D8024DB31B2E1BB0005E6B9A /* SignalProducerLiftingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8024DB11B2E1BB0005E6B9A /* SignalProducerLiftingSpec.swift */; }; - D8170FC11B100EBC004192AD /* FoundationExtensionsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8170FC01B100EBC004192AD /* FoundationExtensionsSpec.swift */; }; - D8170FC21B100EBC004192AD /* FoundationExtensionsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8170FC01B100EBC004192AD /* FoundationExtensionsSpec.swift */; }; - D85C652A1C0D84C7005A77AD /* Flatten.swift in Sources */ = {isa = PBXBuildFile; fileRef = D85C65291C0D84C7005A77AD /* Flatten.swift */; }; - D85C652B1C0E70E3005A77AD /* Flatten.swift in Sources */ = {isa = PBXBuildFile; fileRef = D85C65291C0D84C7005A77AD /* Flatten.swift */; }; - D85C652C1C0E70E4005A77AD /* Flatten.swift in Sources */ = {isa = PBXBuildFile; fileRef = D85C65291C0D84C7005A77AD /* Flatten.swift */; }; - D85C652D1C0E70E5005A77AD /* Flatten.swift in Sources */ = {isa = PBXBuildFile; fileRef = D85C65291C0D84C7005A77AD /* Flatten.swift */; }; - D871D69F1B3B29A40070F16C /* Optional.swift in Sources */ = {isa = PBXBuildFile; fileRef = D871D69E1B3B29A40070F16C /* Optional.swift */; }; - D8E84A671B3B32FB00C3E831 /* Optional.swift in Sources */ = {isa = PBXBuildFile; fileRef = D871D69E1B3B29A40070F16C /* Optional.swift */; }; - EBCC7DBC1BBF010C00A2AE92 /* Observer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBCC7DBB1BBF010C00A2AE92 /* Observer.swift */; }; - EBCC7DBD1BBF01E100A2AE92 /* Observer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBCC7DBB1BBF010C00A2AE92 /* Observer.swift */; }; - EBCC7DBE1BBF01E200A2AE92 /* Observer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBCC7DBB1BBF010C00A2AE92 /* Observer.swift */; }; - EBCC7DBF1BBF01E200A2AE92 /* Observer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBCC7DBB1BBF010C00A2AE92 /* Observer.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 7DFBED091CDB8C9500EE435B /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D04725E119E49ED7006002AA /* Project object */; - proxyType = 1; - remoteGlobalIDString = 57A4D1AF1BA13D7A00F7D4B1; - remoteInfo = "ReactiveCocoa-tvOS"; - }; - D04725F719E49ED7006002AA /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D04725E119E49ED7006002AA /* Project object */; - proxyType = 1; - remoteGlobalIDString = D04725E919E49ED7006002AA; - remoteInfo = ReactiveCocoa; - }; - D047261819E49F82006002AA /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D04725E119E49ED7006002AA /* Project object */; - proxyType = 1; - remoteGlobalIDString = D047260B19E49F82006002AA; - remoteInfo = ReactiveCocoa; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 7DFBED151CDB8CEC00EE435B /* Copy Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 7DFBED211CDB8D8300EE435B /* Result.framework in Copy Frameworks */, - 7DFBED201CDB8D7D00EE435B /* Nimble.framework in Copy Frameworks */, - 7DFBED1F1CDB8D7800EE435B /* Quick.framework in Copy Frameworks */, - 7DFBED1E1CDB8D7000EE435B /* ReactiveSwift.framework in Copy Frameworks */, - ); - name = "Copy Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; - D01B7B6119EDD8F600D26E01 /* Copy Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - CDC42E331AE7AC6D00965373 /* Result.framework in Copy Frameworks */, - D01B7B6219EDD8FE00D26E01 /* Nimble.framework in Copy Frameworks */, - D01B7B6319EDD8FE00D26E01 /* Quick.framework in Copy Frameworks */, - D01B7B6419EDD94B00D26E01 /* ReactiveSwift.framework in Copy Frameworks */, - ); - name = "Copy Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 02D260291C1D6DAF003ACC61 /* SignalLifetimeSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalLifetimeSpec.swift; sourceTree = ""; }; - 4A0E10FE1D2A92720065D310 /* Lifetime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Lifetime.swift; sourceTree = ""; }; - 4A0E11031D2A95200065D310 /* LifetimeSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LifetimeSpec.swift; sourceTree = ""; }; - 57A4D2411BA13D7A00F7D4B1 /* ReactiveSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Application.xcconfig"; sourceTree = ""; }; - 57A4D2451BA13F9700F7D4B1 /* tvOS-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Base.xcconfig"; sourceTree = ""; }; - 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Framework.xcconfig"; sourceTree = ""; }; - 57A4D2471BA13F9700F7D4B1 /* tvOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-StaticLibrary.xcconfig"; sourceTree = ""; }; - 7DFBED031CDB8C9500EE435B /* ReactiveSwiftTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveSwiftTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 9ABCB1841D2A5B5A00BCA243 /* Deprecations+Removals.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deprecations+Removals.swift"; sourceTree = ""; }; - A97451331B3A935E00F48E55 /* watchOS-Application.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Application.xcconfig"; sourceTree = ""; }; - A97451341B3A935E00F48E55 /* watchOS-Base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Base.xcconfig"; sourceTree = ""; }; - A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Framework.xcconfig"; sourceTree = ""; }; - A97451361B3A935E00F48E55 /* watchOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-StaticLibrary.xcconfig"; sourceTree = ""; }; - A9B315541B3940610001CB9C /* ReactiveSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - B696FB801A7640C00075236D /* TestError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestError.swift; sourceTree = ""; }; - BE9CF3941D751B6B003AE479 /* UnidirectionalBinding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnidirectionalBinding.swift; sourceTree = ""; }; - BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalProducerNimbleMatchers.swift; sourceTree = ""; }; - C79B64731CD38B2B003F2376 /* TestLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestLogger.swift; sourceTree = ""; }; - C79B647B1CD52E23003F2376 /* EventLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventLogger.swift; sourceTree = ""; }; - CA6F284F1C52626B001879D2 /* FlattenSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlattenSpec.swift; sourceTree = ""; }; - CDC42E2E1AE7AB8B00965373 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D00004081A46864E000E7D41 /* TupleExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TupleExtensions.swift; sourceTree = ""; }; - D021671C1A6CD50500987861 /* ActionSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ActionSpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - D037672B19EDA75D00A782A9 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Quick.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D03B4A3C19F4C39A009E02AC /* FoundationExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoundationExtensions.swift; sourceTree = ""; }; - D04725EA19E49ED7006002AA /* ReactiveSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D04725EE19E49ED7006002AA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - D04725EF19E49ED7006002AA /* ReactiveSwift.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ReactiveSwift.h; sourceTree = ""; }; - D04725F519E49ED7006002AA /* ReactiveSwiftTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveSwiftTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - D04725FB19E49ED7006002AA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - D047260C19E49F82006002AA /* ReactiveSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D047261619E49F82006002AA /* ReactiveSwiftTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveSwiftTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - D047262719E49FE8006002AA /* Common.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Common.xcconfig; sourceTree = ""; }; - D047262919E49FE8006002AA /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; - D047262A19E49FE8006002AA /* Profile.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Profile.xcconfig; sourceTree = ""; }; - D047262B19E49FE8006002AA /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; - D047262C19E49FE8006002AA /* Test.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Test.xcconfig; sourceTree = ""; }; - D047262E19E49FE8006002AA /* Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Application.xcconfig; sourceTree = ""; }; - D047262F19E49FE8006002AA /* Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Framework.xcconfig; sourceTree = ""; }; - D047263019E49FE8006002AA /* StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = StaticLibrary.xcconfig; sourceTree = ""; }; - D047263219E49FE8006002AA /* iOS-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-Application.xcconfig"; sourceTree = ""; }; - D047263319E49FE8006002AA /* iOS-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-Base.xcconfig"; sourceTree = ""; }; - D047263419E49FE8006002AA /* iOS-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-Framework.xcconfig"; sourceTree = ""; }; - D047263519E49FE8006002AA /* iOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-StaticLibrary.xcconfig"; sourceTree = ""; }; - D047263719E49FE8006002AA /* Mac-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Application.xcconfig"; sourceTree = ""; }; - D047263819E49FE8006002AA /* Mac-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Base.xcconfig"; sourceTree = ""; }; - D047263919E49FE8006002AA /* Mac-DynamicLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-DynamicLibrary.xcconfig"; sourceTree = ""; }; - D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Framework.xcconfig"; sourceTree = ""; }; - D047263B19E49FE8006002AA /* Mac-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-StaticLibrary.xcconfig"; sourceTree = ""; }; - D047263C19E49FE8006002AA /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - D05E662419EDD82000904ACA /* Nimble.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Nimble.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D08C54AF1A69A2AC00AD8286 /* Action.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Action.swift; sourceTree = ""; }; - D08C54B01A69A2AC00AD8286 /* Property.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Property.swift; sourceTree = ""; }; - D08C54B11A69A2AC00AD8286 /* Signal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Signal.swift; sourceTree = ""; }; - D08C54B21A69A2AC00AD8286 /* SignalProducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignalProducer.swift; sourceTree = ""; }; - D08C54B51A69A3DB00AD8286 /* Event.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Event.swift; sourceTree = ""; }; - D0A226071A72E0E900D33B74 /* SignalSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SignalSpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - D0A2260A1A72E6C500D33B74 /* SignalProducerSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SignalProducerSpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - D0A2260D1A72F16D00D33B74 /* PropertySpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = PropertySpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - D0C312BB19EF2A5800984962 /* Atomic.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Atomic.swift; sourceTree = ""; }; - D0C312BC19EF2A5800984962 /* Bag.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Bag.swift; sourceTree = ""; }; - D0C312BE19EF2A5800984962 /* Disposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Disposable.swift; sourceTree = ""; }; - D0C312C819EF2A5800984962 /* Scheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Scheduler.swift; sourceTree = ""; }; - D0C312EE19EF2A7700984962 /* AtomicSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AtomicSpec.swift; sourceTree = ""; }; - D0C312EF19EF2A7700984962 /* BagSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BagSpec.swift; sourceTree = ""; }; - D0C312F019EF2A7700984962 /* DisposableSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisposableSpec.swift; sourceTree = ""; }; - D0C312F219EF2A7700984962 /* SchedulerSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchedulerSpec.swift; sourceTree = ""; }; - D8024DB11B2E1BB0005E6B9A /* SignalProducerLiftingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SignalProducerLiftingSpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - D8170FC01B100EBC004192AD /* FoundationExtensionsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoundationExtensionsSpec.swift; sourceTree = ""; }; - D85C65291C0D84C7005A77AD /* Flatten.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Flatten.swift; sourceTree = ""; }; - D871D69E1B3B29A40070F16C /* Optional.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Optional.swift; sourceTree = ""; }; - EBCC7DBB1BBF010C00A2AE92 /* Observer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Observer.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 57A4D2071BA13D7A00F7D4B1 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 57A4D2081BA13D7A00F7D4B1 /* Result.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 7DFBED001CDB8C9500EE435B /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - CDF066CA1CDC1CA200199626 /* Nimble.framework in Frameworks */, - CDF066CB1CDC1CA200199626 /* Quick.framework in Frameworks */, - 7DFBED081CDB8C9500EE435B /* ReactiveSwift.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A9B315501B3940610001CB9C /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - A9B315C91B3940980001CB9C /* Result.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D04725E619E49ED7006002AA /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - CDC42E2F1AE7AB8B00965373 /* Result.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D04725F219E49ED7006002AA /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - CDC42E301AE7AB8B00965373 /* Result.framework in Frameworks */, - D05E662519EDD82000904ACA /* Nimble.framework in Frameworks */, - D037672D19EDA75D00A782A9 /* Quick.framework in Frameworks */, - D04725F619E49ED7006002AA /* ReactiveSwift.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D047260819E49F82006002AA /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - CDC42E311AE7AB8B00965373 /* Result.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D047261319E49F82006002AA /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - D05E662619EDD83000904ACA /* Nimble.framework in Frameworks */, - D037672F19EDA78B00A782A9 /* Quick.framework in Frameworks */, - D047261719E49F82006002AA /* ReactiveSwift.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 57A4D2431BA13F9700F7D4B1 /* tvOS */ = { - isa = PBXGroup; - children = ( - 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */, - 57A4D2451BA13F9700F7D4B1 /* tvOS-Base.xcconfig */, - 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */, - 57A4D2471BA13F9700F7D4B1 /* tvOS-StaticLibrary.xcconfig */, - ); - path = tvOS; - sourceTree = ""; - }; - A97451321B3A935E00F48E55 /* watchOS */ = { - isa = PBXGroup; - children = ( - A97451331B3A935E00F48E55 /* watchOS-Application.xcconfig */, - A97451341B3A935E00F48E55 /* watchOS-Base.xcconfig */, - A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */, - A97451361B3A935E00F48E55 /* watchOS-StaticLibrary.xcconfig */, - ); - path = watchOS; - sourceTree = ""; - }; - D03B4A3919F4C25F009E02AC /* Signals */ = { - isa = PBXGroup; - children = ( - D08C54AF1A69A2AC00AD8286 /* Action.swift */, - D85C65291C0D84C7005A77AD /* Flatten.swift */, - 4A0E10FE1D2A92720065D310 /* Lifetime.swift */, - D08C54B01A69A2AC00AD8286 /* Property.swift */, - D08C54B11A69A2AC00AD8286 /* Signal.swift */, - D08C54B21A69A2AC00AD8286 /* SignalProducer.swift */, - BE9CF3941D751B6B003AE479 /* UnidirectionalBinding.swift */, - ); - name = Signals; - sourceTree = ""; - }; - D03B4A3A19F4C26D009E02AC /* Internal Utilities */ = { - isa = PBXGroup; - children = ( - D00004081A46864E000E7D41 /* TupleExtensions.swift */, - ); - name = "Internal Utilities"; - sourceTree = ""; - }; - D03B4A3B19F4C281009E02AC /* Extensions */ = { - isa = PBXGroup; - children = ( - D03B4A3C19F4C39A009E02AC /* FoundationExtensions.swift */, - ); - name = Extensions; - sourceTree = ""; - }; - D04725E019E49ED7006002AA = { - isa = PBXGroup; - children = ( - D04725EC19E49ED7006002AA /* ReactiveSwift */, - D04725F919E49ED7006002AA /* ReactiveSwiftTests */, - D047262519E49FE8006002AA /* Configuration */, - D04725EB19E49ED7006002AA /* Products */, - ); - sourceTree = ""; - usesTabs = 1; - }; - D04725EB19E49ED7006002AA /* Products */ = { - isa = PBXGroup; - children = ( - D04725EA19E49ED7006002AA /* ReactiveSwift.framework */, - D04725F519E49ED7006002AA /* ReactiveSwiftTests.xctest */, - D047260C19E49F82006002AA /* ReactiveSwift.framework */, - D047261619E49F82006002AA /* ReactiveSwiftTests.xctest */, - A9B315541B3940610001CB9C /* ReactiveSwift.framework */, - 57A4D2411BA13D7A00F7D4B1 /* ReactiveSwift.framework */, - 7DFBED031CDB8C9500EE435B /* ReactiveSwiftTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - D04725EC19E49ED7006002AA /* ReactiveSwift */ = { - isa = PBXGroup; - children = ( - D04725EF19E49ED7006002AA /* ReactiveSwift.h */, - D0C312BB19EF2A5800984962 /* Atomic.swift */, - D0C312BC19EF2A5800984962 /* Bag.swift */, - D0C312BE19EF2A5800984962 /* Disposable.swift */, - D08C54B51A69A3DB00AD8286 /* Event.swift */, - EBCC7DBB1BBF010C00A2AE92 /* Observer.swift */, - D871D69E1B3B29A40070F16C /* Optional.swift */, - D0C312C819EF2A5800984962 /* Scheduler.swift */, - C79B647B1CD52E23003F2376 /* EventLogger.swift */, - D03B4A3919F4C25F009E02AC /* Signals */, - D03B4A3A19F4C26D009E02AC /* Internal Utilities */, - D03B4A3B19F4C281009E02AC /* Extensions */, - 9ABCB1841D2A5B5A00BCA243 /* Deprecations+Removals.swift */, - D04725ED19E49ED7006002AA /* Supporting Files */, - ); - path = ReactiveSwift; - sourceTree = ""; - }; - D04725ED19E49ED7006002AA /* Supporting Files */ = { - isa = PBXGroup; - children = ( - CDC42E2E1AE7AB8B00965373 /* Result.framework */, - D04725EE19E49ED7006002AA /* Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - D04725F919E49ED7006002AA /* ReactiveSwiftTests */ = { - isa = PBXGroup; - children = ( - D021671C1A6CD50500987861 /* ActionSpec.swift */, - D0C312EE19EF2A7700984962 /* AtomicSpec.swift */, - D0C312EF19EF2A7700984962 /* BagSpec.swift */, - D0C312F019EF2A7700984962 /* DisposableSpec.swift */, - CA6F284F1C52626B001879D2 /* FlattenSpec.swift */, - D8170FC01B100EBC004192AD /* FoundationExtensionsSpec.swift */, - 4A0E11031D2A95200065D310 /* LifetimeSpec.swift */, - D0A2260D1A72F16D00D33B74 /* PropertySpec.swift */, - D0C312F219EF2A7700984962 /* SchedulerSpec.swift */, - 02D260291C1D6DAF003ACC61 /* SignalLifetimeSpec.swift */, - D8024DB11B2E1BB0005E6B9A /* SignalProducerLiftingSpec.swift */, - D0A2260A1A72E6C500D33B74 /* SignalProducerSpec.swift */, - D0A226071A72E0E900D33B74 /* SignalSpec.swift */, - B696FB801A7640C00075236D /* TestError.swift */, - C79B64731CD38B2B003F2376 /* TestLogger.swift */, - D04725FA19E49ED7006002AA /* Supporting Files */, - ); - path = ReactiveSwiftTests; - sourceTree = ""; - }; - D04725FA19E49ED7006002AA /* Supporting Files */ = { - isa = PBXGroup; - children = ( - D05E662419EDD82000904ACA /* Nimble.framework */, - D037672B19EDA75D00A782A9 /* Quick.framework */, - BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */, - D04725FB19E49ED7006002AA /* Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - D047262519E49FE8006002AA /* Configuration */ = { - isa = PBXGroup; - children = ( - D047262619E49FE8006002AA /* Base */, - D047263119E49FE8006002AA /* iOS */, - D047263619E49FE8006002AA /* Mac OS X */, - A97451321B3A935E00F48E55 /* watchOS */, - 57A4D2431BA13F9700F7D4B1 /* tvOS */, - D047263C19E49FE8006002AA /* README.md */, - ); - name = Configuration; - path = Carthage/Checkouts/xcconfigs; - sourceTree = ""; - }; - D047262619E49FE8006002AA /* Base */ = { - isa = PBXGroup; - children = ( - D047262719E49FE8006002AA /* Common.xcconfig */, - D047262819E49FE8006002AA /* Configurations */, - D047262D19E49FE8006002AA /* Targets */, - ); - path = Base; - sourceTree = ""; - }; - D047262819E49FE8006002AA /* Configurations */ = { - isa = PBXGroup; - children = ( - D047262919E49FE8006002AA /* Debug.xcconfig */, - D047262A19E49FE8006002AA /* Profile.xcconfig */, - D047262B19E49FE8006002AA /* Release.xcconfig */, - D047262C19E49FE8006002AA /* Test.xcconfig */, - ); - path = Configurations; - sourceTree = ""; - }; - D047262D19E49FE8006002AA /* Targets */ = { - isa = PBXGroup; - children = ( - D047262E19E49FE8006002AA /* Application.xcconfig */, - D047262F19E49FE8006002AA /* Framework.xcconfig */, - D047263019E49FE8006002AA /* StaticLibrary.xcconfig */, - ); - path = Targets; - sourceTree = ""; - }; - D047263119E49FE8006002AA /* iOS */ = { - isa = PBXGroup; - children = ( - D047263219E49FE8006002AA /* iOS-Application.xcconfig */, - D047263319E49FE8006002AA /* iOS-Base.xcconfig */, - D047263419E49FE8006002AA /* iOS-Framework.xcconfig */, - D047263519E49FE8006002AA /* iOS-StaticLibrary.xcconfig */, - ); - path = iOS; - sourceTree = ""; - }; - D047263619E49FE8006002AA /* Mac OS X */ = { - isa = PBXGroup; - children = ( - D047263719E49FE8006002AA /* Mac-Application.xcconfig */, - D047263819E49FE8006002AA /* Mac-Base.xcconfig */, - D047263919E49FE8006002AA /* Mac-DynamicLibrary.xcconfig */, - D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */, - D047263B19E49FE8006002AA /* Mac-StaticLibrary.xcconfig */, - ); - path = "Mac OS X"; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - 57A4D2091BA13D7A00F7D4B1 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 57A4D20A1BA13D7A00F7D4B1 /* ReactiveSwift.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A9B315511B3940610001CB9C /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - A9B315CA1B3940AB0001CB9C /* ReactiveSwift.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D04725E719E49ED7006002AA /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - D04725F019E49ED7006002AA /* ReactiveSwift.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D047260919E49F82006002AA /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - D037666419EDA43C00A782A9 /* ReactiveSwift.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 57A4D1AF1BA13D7A00F7D4B1 /* ReactiveSwift-tvOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = 57A4D23C1BA13D7A00F7D4B1 /* Build configuration list for PBXNativeTarget "ReactiveSwift-tvOS" */; - buildPhases = ( - 57A4D1B01BA13D7A00F7D4B1 /* Sources */, - 57A4D2071BA13D7A00F7D4B1 /* Frameworks */, - 57A4D2091BA13D7A00F7D4B1 /* Headers */, - 57A4D23B1BA13D7A00F7D4B1 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "ReactiveSwift-tvOS"; - productName = ReactiveCocoa; - productReference = 57A4D2411BA13D7A00F7D4B1 /* ReactiveSwift.framework */; - productType = "com.apple.product-type.framework"; - }; - 7DFBED021CDB8C9500EE435B /* ReactiveSwift-tvOSTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 7DFBED0F1CDB8C9500EE435B /* Build configuration list for PBXNativeTarget "ReactiveSwift-tvOSTests" */; - buildPhases = ( - 7DFBECFF1CDB8C9500EE435B /* Sources */, - 7DFBED001CDB8C9500EE435B /* Frameworks */, - 7DFBED011CDB8C9500EE435B /* Resources */, - 7DFBED151CDB8CEC00EE435B /* Copy Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - 7DFBED0A1CDB8C9500EE435B /* PBXTargetDependency */, - ); - name = "ReactiveSwift-tvOSTests"; - productName = "ReactiveCocoa-tvOSTests"; - productReference = 7DFBED031CDB8C9500EE435B /* ReactiveSwiftTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - A9B315531B3940610001CB9C /* ReactiveSwift-watchOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = A9B3155D1B3940610001CB9C /* Build configuration list for PBXNativeTarget "ReactiveSwift-watchOS" */; - buildPhases = ( - A9B3154F1B3940610001CB9C /* Sources */, - A9B315501B3940610001CB9C /* Frameworks */, - A9B315511B3940610001CB9C /* Headers */, - A9B315521B3940610001CB9C /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "ReactiveSwift-watchOS"; - productName = ReactiveCocoa; - productReference = A9B315541B3940610001CB9C /* ReactiveSwift.framework */; - productType = "com.apple.product-type.framework"; - }; - D04725E919E49ED7006002AA /* ReactiveSwift-macOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = D047260019E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveSwift-macOS" */; - buildPhases = ( - D04725E519E49ED7006002AA /* Sources */, - D04725E619E49ED7006002AA /* Frameworks */, - D04725E719E49ED7006002AA /* Headers */, - D04725E819E49ED7006002AA /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "ReactiveSwift-macOS"; - productName = ReactiveCocoa; - productReference = D04725EA19E49ED7006002AA /* ReactiveSwift.framework */; - productType = "com.apple.product-type.framework"; - }; - D04725F419E49ED7006002AA /* ReactiveSwift-macOSTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = D047260319E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveSwift-macOSTests" */; - buildPhases = ( - D04725F119E49ED7006002AA /* Sources */, - D04725F219E49ED7006002AA /* Frameworks */, - D04725F319E49ED7006002AA /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - D04725F819E49ED7006002AA /* PBXTargetDependency */, - ); - name = "ReactiveSwift-macOSTests"; - productName = ReactiveCocoaTests; - productReference = D04725F519E49ED7006002AA /* ReactiveSwiftTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - D047260B19E49F82006002AA /* ReactiveSwift-iOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = D047261F19E49F82006002AA /* Build configuration list for PBXNativeTarget "ReactiveSwift-iOS" */; - buildPhases = ( - D047260719E49F82006002AA /* Sources */, - D047260819E49F82006002AA /* Frameworks */, - D047260919E49F82006002AA /* Headers */, - D047260A19E49F82006002AA /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "ReactiveSwift-iOS"; - productName = ReactiveCocoa; - productReference = D047260C19E49F82006002AA /* ReactiveSwift.framework */; - productType = "com.apple.product-type.framework"; - }; - D047261519E49F82006002AA /* ReactiveSwift-iOSTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = D047262219E49F82006002AA /* Build configuration list for PBXNativeTarget "ReactiveSwift-iOSTests" */; - buildPhases = ( - D047261219E49F82006002AA /* Sources */, - D047261319E49F82006002AA /* Frameworks */, - D047261419E49F82006002AA /* Resources */, - D01B7B6119EDD8F600D26E01 /* Copy Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - D047261919E49F82006002AA /* PBXTargetDependency */, - ); - name = "ReactiveSwift-iOSTests"; - productName = ReactiveCocoaTests; - productReference = D047261619E49F82006002AA /* ReactiveSwiftTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - D04725E119E49ED7006002AA /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0710; - ORGANIZATIONNAME = GitHub; - TargetAttributes = { - 57A4D1AF1BA13D7A00F7D4B1 = { - LastSwiftMigration = 0800; - }; - 7DFBED021CDB8C9500EE435B = { - CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 0800; - }; - A9B315531B3940610001CB9C = { - CreatedOnToolsVersion = 7.0; - LastSwiftMigration = 0800; - }; - D04725E919E49ED7006002AA = { - CreatedOnToolsVersion = 6.1; - LastSwiftMigration = 0800; - }; - D04725F419E49ED7006002AA = { - CreatedOnToolsVersion = 6.1; - LastSwiftMigration = 0800; - }; - D047260B19E49F82006002AA = { - CreatedOnToolsVersion = 6.1; - LastSwiftMigration = 0800; - }; - D047261519E49F82006002AA = { - CreatedOnToolsVersion = 6.1; - LastSwiftMigration = 0800; - }; - }; - }; - buildConfigurationList = D04725E419E49ED7006002AA /* Build configuration list for PBXProject "ReactiveSwift" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - ); - mainGroup = D04725E019E49ED7006002AA; - productRefGroup = D04725EB19E49ED7006002AA /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - D04725E919E49ED7006002AA /* ReactiveSwift-macOS */, - D04725F419E49ED7006002AA /* ReactiveSwift-macOSTests */, - D047260B19E49F82006002AA /* ReactiveSwift-iOS */, - D047261519E49F82006002AA /* ReactiveSwift-iOSTests */, - A9B315531B3940610001CB9C /* ReactiveSwift-watchOS */, - 57A4D1AF1BA13D7A00F7D4B1 /* ReactiveSwift-tvOS */, - 7DFBED021CDB8C9500EE435B /* ReactiveSwift-tvOSTests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 57A4D23B1BA13D7A00F7D4B1 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 7DFBED011CDB8C9500EE435B /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A9B315521B3940610001CB9C /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D04725E819E49ED7006002AA /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D04725F319E49ED7006002AA /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D047260A19E49F82006002AA /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D047261419E49F82006002AA /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 57A4D1B01BA13D7A00F7D4B1 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 57A4D1B11BA13D7A00F7D4B1 /* Optional.swift in Sources */, - 57A4D1B41BA13D7A00F7D4B1 /* Disposable.swift in Sources */, - 57A4D1B61BA13D7A00F7D4B1 /* Event.swift in Sources */, - 57A4D1B81BA13D7A00F7D4B1 /* Scheduler.swift in Sources */, - 57A4D1B91BA13D7A00F7D4B1 /* Action.swift in Sources */, - 57A4D1BA1BA13D7A00F7D4B1 /* Property.swift in Sources */, - 57A4D1BB1BA13D7A00F7D4B1 /* Signal.swift in Sources */, - 57A4D1BC1BA13D7A00F7D4B1 /* SignalProducer.swift in Sources */, - 57A4D1BD1BA13D7A00F7D4B1 /* Atomic.swift in Sources */, - 57A4D1BE1BA13D7A00F7D4B1 /* Bag.swift in Sources */, - 57A4D1BF1BA13D7A00F7D4B1 /* TupleExtensions.swift in Sources */, - 57A4D1C01BA13D7A00F7D4B1 /* FoundationExtensions.swift in Sources */, - D85C652D1C0E70E5005A77AD /* Flatten.swift in Sources */, - 9ABCB1881D2A5B5A00BCA243 /* Deprecations+Removals.swift in Sources */, - EBCC7DBF1BBF01E200A2AE92 /* Observer.swift in Sources */, - C79B64801CD52E4E003F2376 /* EventLogger.swift in Sources */, - 4A0E11021D2A92720065D310 /* Lifetime.swift in Sources */, - BE9CF3981D751B71003AE479 /* UnidirectionalBinding.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 7DFBECFF1CDB8C9500EE435B /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 7DFBED221CDB8DE300EE435B /* ActionSpec.swift in Sources */, - 7DFBED231CDB8DE300EE435B /* AtomicSpec.swift in Sources */, - 7DFBED241CDB8DE300EE435B /* BagSpec.swift in Sources */, - 7DFBED251CDB8DE300EE435B /* DisposableSpec.swift in Sources */, - 7DFBED261CDB8DE300EE435B /* FoundationExtensionsSpec.swift in Sources */, - 7DFBED281CDB8DE300EE435B /* PropertySpec.swift in Sources */, - 7DFBED291CDB8DE300EE435B /* SchedulerSpec.swift in Sources */, - 7DFBED2A1CDB8DE300EE435B /* SignalLifetimeSpec.swift in Sources */, - 7DFBED2B1CDB8DE300EE435B /* SignalProducerSpec.swift in Sources */, - 7DFBED2C1CDB8DE300EE435B /* SignalProducerLiftingSpec.swift in Sources */, - 7DFBED2D1CDB8DE300EE435B /* SignalSpec.swift in Sources */, - 7DFBED2E1CDB8DE300EE435B /* FlattenSpec.swift in Sources */, - 7DFBED2F1CDB8DE300EE435B /* TestError.swift in Sources */, - 7DFBED301CDB8DE300EE435B /* TestLogger.swift in Sources */, - 4A0E11061D2A95200065D310 /* LifetimeSpec.swift in Sources */, - 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A9B3154F1B3940610001CB9C /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - A9F793341B60D0140026BCBA /* Optional.swift in Sources */, - A9B315BC1B3940810001CB9C /* Disposable.swift in Sources */, - A9B315BE1B3940810001CB9C /* Event.swift in Sources */, - A9B315C01B3940810001CB9C /* Scheduler.swift in Sources */, - A9B315C11B3940810001CB9C /* Action.swift in Sources */, - A9B315C21B3940810001CB9C /* Property.swift in Sources */, - A9B315C31B3940810001CB9C /* Signal.swift in Sources */, - A9B315C41B3940810001CB9C /* SignalProducer.swift in Sources */, - A9B315C51B3940810001CB9C /* Atomic.swift in Sources */, - A9B315C61B3940810001CB9C /* Bag.swift in Sources */, - A9B315C71B3940810001CB9C /* TupleExtensions.swift in Sources */, - A9B315C81B3940810001CB9C /* FoundationExtensions.swift in Sources */, - D85C652C1C0E70E4005A77AD /* Flatten.swift in Sources */, - 9ABCB1871D2A5B5A00BCA243 /* Deprecations+Removals.swift in Sources */, - EBCC7DBE1BBF01E200A2AE92 /* Observer.swift in Sources */, - C79B647F1CD52E4D003F2376 /* EventLogger.swift in Sources */, - 4A0E11011D2A92720065D310 /* Lifetime.swift in Sources */, - BE9CF3971D751B71003AE479 /* UnidirectionalBinding.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D04725E519E49ED7006002AA /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D00004091A46864E000E7D41 /* TupleExtensions.swift in Sources */, - D871D69F1B3B29A40070F16C /* Optional.swift in Sources */, - D08C54B61A69A3DB00AD8286 /* Event.swift in Sources */, - D0C312D319EF2A5800984962 /* Disposable.swift in Sources */, - EBCC7DBC1BBF010C00A2AE92 /* Observer.swift in Sources */, - D03B4A3D19F4C39A009E02AC /* FoundationExtensions.swift in Sources */, - D08C54B31A69A2AE00AD8286 /* Signal.swift in Sources */, - D85C652A1C0D84C7005A77AD /* Flatten.swift in Sources */, - D0C312CF19EF2A5800984962 /* Bag.swift in Sources */, - 4A0E10FF1D2A92720065D310 /* Lifetime.swift in Sources */, - D0C312E719EF2A5800984962 /* Scheduler.swift in Sources */, - D0C312CD19EF2A5800984962 /* Atomic.swift in Sources */, - D08C54BA1A69C54300AD8286 /* Property.swift in Sources */, - D0D11AB91A6AE87700C1F8B1 /* Action.swift in Sources */, - C79B647C1CD52E23003F2376 /* EventLogger.swift in Sources */, - 9ABCB1851D2A5B5A00BCA243 /* Deprecations+Removals.swift in Sources */, - D08C54B81A69A9D000AD8286 /* SignalProducer.swift in Sources */, - BE9CF3951D751B6B003AE479 /* UnidirectionalBinding.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D04725F119E49ED7006002AA /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D0A2260E1A72F16D00D33B74 /* PropertySpec.swift in Sources */, - B696FB811A7640C00075236D /* TestError.swift in Sources */, - D021671D1A6CD50500987861 /* ActionSpec.swift in Sources */, - D0C3130E19EF2B1F00984962 /* SchedulerSpec.swift in Sources */, - BFA6B94D1A7604D400C846D1 /* SignalProducerNimbleMatchers.swift in Sources */, - D8170FC11B100EBC004192AD /* FoundationExtensionsSpec.swift in Sources */, - C79B64741CD38B2B003F2376 /* TestLogger.swift in Sources */, - CA6F28501C52626B001879D2 /* FlattenSpec.swift in Sources */, - 4A0E11041D2A95200065D310 /* LifetimeSpec.swift in Sources */, - CDCD247A1C277EEC00710AEE /* AtomicSpec.swift in Sources */, - 579504331BB8A34200A5E482 /* BagSpec.swift in Sources */, - D0A226081A72E0E900D33B74 /* SignalSpec.swift in Sources */, - 02D2602B1C1D6DB8003ACC61 /* SignalLifetimeSpec.swift in Sources */, - D0C3130C19EF2B1F00984962 /* DisposableSpec.swift in Sources */, - D0A2260B1A72E6C500D33B74 /* SignalProducerSpec.swift in Sources */, - D8024DB21B2E1BB0005E6B9A /* SignalProducerLiftingSpec.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D047260719E49F82006002AA /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D08C54B41A69A2AF00AD8286 /* Signal.swift in Sources */, - D8E84A671B3B32FB00C3E831 /* Optional.swift in Sources */, - D0C312D419EF2A5800984962 /* Disposable.swift in Sources */, - D08C54B91A69A9D100AD8286 /* SignalProducer.swift in Sources */, - 9ABCB1861D2A5B5A00BCA243 /* Deprecations+Removals.swift in Sources */, - EBCC7DBD1BBF01E100A2AE92 /* Observer.swift in Sources */, - D85C652B1C0E70E3005A77AD /* Flatten.swift in Sources */, - 4A0E11001D2A92720065D310 /* Lifetime.swift in Sources */, - D08C54BB1A69C54400AD8286 /* Property.swift in Sources */, - D03B4A3E19F4C39A009E02AC /* FoundationExtensions.swift in Sources */, - D000040A1A46864E000E7D41 /* TupleExtensions.swift in Sources */, - D08C54B71A69A3DB00AD8286 /* Event.swift in Sources */, - C79B647D1CD52E4A003F2376 /* EventLogger.swift in Sources */, - D0C312CE19EF2A5800984962 /* Atomic.swift in Sources */, - D0C312E819EF2A5800984962 /* Scheduler.swift in Sources */, - D0C312D019EF2A5800984962 /* Bag.swift in Sources */, - D0D11ABA1A6AE87700C1F8B1 /* Action.swift in Sources */, - BE9CF3961D751B70003AE479 /* UnidirectionalBinding.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D047261219E49F82006002AA /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D0A2260C1A72E6C500D33B74 /* SignalProducerSpec.swift in Sources */, - D0A2260F1A72F16D00D33B74 /* PropertySpec.swift in Sources */, - D0A226091A72E0E900D33B74 /* SignalSpec.swift in Sources */, - CDCD247B1C277EED00710AEE /* AtomicSpec.swift in Sources */, - D021671E1A6CD50500987861 /* ActionSpec.swift in Sources */, - D8024DB31B2E1BB0005E6B9A /* SignalProducerLiftingSpec.swift in Sources */, - BFA6B94E1A7604D500C846D1 /* SignalProducerNimbleMatchers.swift in Sources */, - B696FB821A7640C00075236D /* TestError.swift in Sources */, - D8170FC21B100EBC004192AD /* FoundationExtensionsSpec.swift in Sources */, - D0C3131419EF2B2000984962 /* SchedulerSpec.swift in Sources */, - C79B64751CD38B2B003F2376 /* TestLogger.swift in Sources */, - D0C3131219EF2B2000984962 /* DisposableSpec.swift in Sources */, - CA6F28511C52626B001879D2 /* FlattenSpec.swift in Sources */, - 579504341BB8A34300A5E482 /* BagSpec.swift in Sources */, - 4A0E11051D2A95200065D310 /* LifetimeSpec.swift in Sources */, - 02D2602A1C1D6DAF003ACC61 /* SignalLifetimeSpec.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 7DFBED0A1CDB8C9500EE435B /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 57A4D1AF1BA13D7A00F7D4B1 /* ReactiveSwift-tvOS */; - targetProxy = 7DFBED091CDB8C9500EE435B /* PBXContainerItemProxy */; - }; - D04725F819E49ED7006002AA /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D04725E919E49ED7006002AA /* ReactiveSwift-macOS */; - targetProxy = D04725F719E49ED7006002AA /* PBXContainerItemProxy */; - }; - D047261919E49F82006002AA /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D047260B19E49F82006002AA /* ReactiveSwift-iOS */; - targetProxy = D047261819E49F82006002AA /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 57A4D23D1BA13D7A00F7D4B1 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); - INFOPLIST_FILE = ReactiveSwift/Info.plist; - }; - name = Debug; - }; - 57A4D23E1BA13D7A00F7D4B1 /* Test */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); - INFOPLIST_FILE = ReactiveSwift/Info.plist; - }; - name = Test; - }; - 57A4D23F1BA13D7A00F7D4B1 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); - INFOPLIST_FILE = ReactiveSwift/Info.plist; - }; - name = Release; - }; - 57A4D2401BA13D7A00F7D4B1 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); - INFOPLIST_FILE = ReactiveSwift/Info.plist; - }; - name = Profile; - }; - 7DFBED0B1CDB8C9500EE435B /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; - buildSettings = { - CODE_SIGN_IDENTITY = ""; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - INFOPLIST_FILE = ReactiveSwiftTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Debug; - }; - 7DFBED0C1CDB8C9500EE435B /* Test */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; - buildSettings = { - CODE_SIGN_IDENTITY = ""; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - INFOPLIST_FILE = ReactiveSwiftTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Test; - }; - 7DFBED0D1CDB8C9500EE435B /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; - buildSettings = { - CODE_SIGN_IDENTITY = ""; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - INFOPLIST_FILE = ReactiveSwiftTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Release; - }; - 7DFBED0E1CDB8C9500EE435B /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; - buildSettings = { - CODE_SIGN_IDENTITY = ""; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - INFOPLIST_FILE = ReactiveSwiftTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Profile; - }; - A9B315591B3940610001CB9C /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); - INFOPLIST_FILE = ReactiveSwift/Info.plist; - }; - name = Debug; - }; - A9B3155A1B3940610001CB9C /* Test */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); - INFOPLIST_FILE = ReactiveSwift/Info.plist; - }; - name = Test; - }; - A9B3155B1B3940610001CB9C /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); - INFOPLIST_FILE = ReactiveSwift/Info.plist; - }; - name = Release; - }; - A9B3155C1B3940610001CB9C /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); - INFOPLIST_FILE = ReactiveSwift/Info.plist; - }; - name = Profile; - }; - D04725FE19E49ED7006002AA /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047262919E49FE8006002AA /* Debug.xcconfig */; - buildSettings = { - BITCODE_GENERATION_MODE = bitcode; - CODE_SIGNING_REQUIRED = NO; - CURRENT_PROJECT_VERSION = 1; - ENABLE_TESTABILITY = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; - PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(PROJECT_NAME)"; - SWIFT_VERSION = 3.0; - TVOS_DEPLOYMENT_TARGET = 9.0; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 2.0; - }; - name = Debug; - }; - D04725FF19E49ED7006002AA /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047262B19E49FE8006002AA /* Release.xcconfig */; - buildSettings = { - BITCODE_GENERATION_MODE = bitcode; - CODE_SIGNING_REQUIRED = NO; - CURRENT_PROJECT_VERSION = 1; - GCC_OPTIMIZATION_LEVEL = 0; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; - PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(PROJECT_NAME)"; - SWIFT_VERSION = 3.0; - TVOS_DEPLOYMENT_TARGET = 9.0; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 2.0; - }; - name = Release; - }; - D047260119E49ED7006002AA /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - FRAMEWORK_VERSION = A; - INFOPLIST_FILE = ReactiveSwift/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - }; - name = Debug; - }; - D047260219E49ED7006002AA /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - FRAMEWORK_VERSION = A; - INFOPLIST_FILE = ReactiveSwift/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - }; - name = Release; - }; - D047260419E49ED7006002AA /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; - buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(DEVELOPER_FRAMEWORKS_DIR)", - "$(inherited)", - ); - INFOPLIST_FILE = ReactiveSwiftTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Debug; - }; - D047260519E49ED7006002AA /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; - buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(DEVELOPER_FRAMEWORKS_DIR)", - "$(inherited)", - ); - INFOPLIST_FILE = ReactiveSwiftTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Release; - }; - D047262019E49F82006002AA /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - INFOPLIST_FILE = ReactiveSwift/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - }; - name = Debug; - }; - D047262119E49F82006002AA /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - INFOPLIST_FILE = ReactiveSwift/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - }; - name = Release; - }; - D047262319E49F82006002AA /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; - buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - INFOPLIST_FILE = ReactiveSwiftTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Debug; - }; - D047262419E49F82006002AA /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; - buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - INFOPLIST_FILE = ReactiveSwiftTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Release; - }; - D047263D19E4A008006002AA /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047262A19E49FE8006002AA /* Profile.xcconfig */; - buildSettings = { - BITCODE_GENERATION_MODE = bitcode; - CODE_SIGNING_REQUIRED = NO; - CURRENT_PROJECT_VERSION = 1; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; - PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(PROJECT_NAME)"; - SWIFT_VERSION = 3.0; - TVOS_DEPLOYMENT_TARGET = 9.0; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 2.0; - }; - name = Profile; - }; - D047263E19E4A008006002AA /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - FRAMEWORK_VERSION = A; - INFOPLIST_FILE = ReactiveSwift/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - }; - name = Profile; - }; - D047263F19E4A008006002AA /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; - buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(DEVELOPER_FRAMEWORKS_DIR)", - "$(inherited)", - ); - INFOPLIST_FILE = ReactiveSwiftTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Profile; - }; - D047264019E4A008006002AA /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - INFOPLIST_FILE = ReactiveSwift/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - }; - name = Profile; - }; - D047264119E4A008006002AA /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; - buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - INFOPLIST_FILE = ReactiveSwiftTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Profile; - }; - D047264219E4A00B006002AA /* Test */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047262C19E49FE8006002AA /* Test.xcconfig */; - buildSettings = { - BITCODE_GENERATION_MODE = bitcode; - CODE_SIGNING_REQUIRED = NO; - CURRENT_PROJECT_VERSION = 1; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; - PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)-Tests"; - PRODUCT_NAME = "$(PROJECT_NAME)"; - SWIFT_VERSION = 3.0; - TVOS_DEPLOYMENT_TARGET = 9.0; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 2.0; - }; - name = Test; - }; - D047264319E4A00B006002AA /* Test */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - FRAMEWORK_VERSION = A; - INFOPLIST_FILE = ReactiveSwift/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - }; - name = Test; - }; - D047264419E4A00B006002AA /* Test */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; - buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(DEVELOPER_FRAMEWORKS_DIR)", - "$(inherited)", - ); - INFOPLIST_FILE = ReactiveSwiftTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Test; - }; - D047264519E4A00B006002AA /* Test */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - INFOPLIST_FILE = ReactiveSwift/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - }; - name = Test; - }; - D047264619E4A00B006002AA /* Test */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; - buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - INFOPLIST_FILE = ReactiveSwiftTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Test; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 57A4D23C1BA13D7A00F7D4B1 /* Build configuration list for PBXNativeTarget "ReactiveSwift-tvOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 57A4D23D1BA13D7A00F7D4B1 /* Debug */, - 57A4D23E1BA13D7A00F7D4B1 /* Test */, - 57A4D23F1BA13D7A00F7D4B1 /* Release */, - 57A4D2401BA13D7A00F7D4B1 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 7DFBED0F1CDB8C9500EE435B /* Build configuration list for PBXNativeTarget "ReactiveSwift-tvOSTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 7DFBED0B1CDB8C9500EE435B /* Debug */, - 7DFBED0C1CDB8C9500EE435B /* Test */, - 7DFBED0D1CDB8C9500EE435B /* Release */, - 7DFBED0E1CDB8C9500EE435B /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - A9B3155D1B3940610001CB9C /* Build configuration list for PBXNativeTarget "ReactiveSwift-watchOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - A9B315591B3940610001CB9C /* Debug */, - A9B3155A1B3940610001CB9C /* Test */, - A9B3155B1B3940610001CB9C /* Release */, - A9B3155C1B3940610001CB9C /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D04725E419E49ED7006002AA /* Build configuration list for PBXProject "ReactiveSwift" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D04725FE19E49ED7006002AA /* Debug */, - D047264219E4A00B006002AA /* Test */, - D04725FF19E49ED7006002AA /* Release */, - D047263D19E4A008006002AA /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D047260019E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveSwift-macOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D047260119E49ED7006002AA /* Debug */, - D047264319E4A00B006002AA /* Test */, - D047260219E49ED7006002AA /* Release */, - D047263E19E4A008006002AA /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D047260319E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveSwift-macOSTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D047260419E49ED7006002AA /* Debug */, - D047264419E4A00B006002AA /* Test */, - D047260519E49ED7006002AA /* Release */, - D047263F19E4A008006002AA /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D047261F19E49F82006002AA /* Build configuration list for PBXNativeTarget "ReactiveSwift-iOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D047262019E49F82006002AA /* Debug */, - D047264519E4A00B006002AA /* Test */, - D047262119E49F82006002AA /* Release */, - D047264019E4A008006002AA /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D047262219E49F82006002AA /* Build configuration list for PBXNativeTarget "ReactiveSwift-iOSTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D047262319E49F82006002AA /* Debug */, - D047264619E4A00B006002AA /* Test */, - D047262419E49F82006002AA /* Release */, - D047264119E4A008006002AA /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = D04725E119E49ED7006002AA /* Project object */; -} diff --git a/ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-iOS.xcscheme b/ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-iOS.xcscheme deleted file mode 100644 index 749a8751b4..0000000000 --- a/ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-iOS.xcscheme +++ /dev/null @@ -1,156 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-macOS.xcscheme b/ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-macOS.xcscheme deleted file mode 100644 index 4709eecf1e..0000000000 --- a/ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-macOS.xcscheme +++ /dev/null @@ -1,156 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-tvOS.xcscheme b/ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-tvOS.xcscheme deleted file mode 100644 index 863741d9ed..0000000000 --- a/ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-tvOS.xcscheme +++ /dev/null @@ -1,156 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-watchOS.xcscheme b/ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-watchOS.xcscheme deleted file mode 100644 index 2bdc6d5ca2..0000000000 --- a/ReactiveSwift.xcodeproj/xcshareddata/xcschemes/ReactiveSwift-watchOS.xcscheme +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ReactiveSwift/Action.swift b/ReactiveSwift/Action.swift deleted file mode 100644 index 2e3b3bd4b3..0000000000 --- a/ReactiveSwift/Action.swift +++ /dev/null @@ -1,199 +0,0 @@ -import Foundation -import enum Result.NoError - -/// Represents an action that will do some work when executed with a value of -/// type `Input`, then return zero or more values of type `Output` and/or fail -/// with an error of type `Error`. If no failure should be possible, NoError can -/// be specified for the `Error` parameter. -/// -/// Actions enforce serial execution. Any attempt to execute an action multiple -/// times concurrently will return an error. -public final class Action { - private let executeClosure: (Input) -> SignalProducer - private let eventsObserver: Signal, NoError>.Observer - - /// A signal of all events generated from applications of the Action. - /// - /// In other words, this will send every `Event` from every signal generated - /// by each SignalProducer returned from apply(). - public let events: Signal, NoError> - - /// A signal of all values generated from applications of the Action. - /// - /// In other words, this will send every value from every signal generated - /// by each SignalProducer returned from apply(). - public let values: Signal - - /// A signal of all errors generated from applications of the Action. - /// - /// In other words, this will send errors from every signal generated by - /// each SignalProducer returned from apply(). - public let errors: Signal - - /// Whether the action is currently executing. - public var isExecuting: Property { - return Property(_isExecuting) - } - - private let _isExecuting: MutableProperty = MutableProperty(false) - - /// Whether the action is currently enabled. - public var isEnabled: Property { - return Property(_isEnabled) - } - - private let _isEnabled: MutableProperty = MutableProperty(false) - - /// Whether the instantiator of this action wants it to be enabled. - private let isUserEnabled: Property - - /// This queue is used for read-modify-write operations on the `_executing` - /// property. - private let executingQueue = DispatchQueue( - label: "org.reactivecocoa.ReactiveSwift.Action.executingQueue", - attributes: [] - ) - - /// Whether the action should be enabled for the given combination of user - /// enabledness and executing status. - private static func shouldBeEnabled(userEnabled: Bool, executing: Bool) -> Bool { - return userEnabled && !executing - } - - /// Initializes an action that will be conditionally enabled, and creates a - /// SignalProducer for each input. - /// - /// - parameters: - /// - enabledIf: Boolean property that shows whether the action is - /// enabled. - /// - execute: A closure that returns the signal producer returned by - /// calling `apply(Input)` on the action. - public init(enabledIf property: P, _ execute: @escaping (Input) -> SignalProducer) where P.Value == Bool { - executeClosure = execute - isUserEnabled = Property(property) - - (events, eventsObserver) = Signal, NoError>.pipe() - - values = events.map { $0.value }.skipNil() - errors = events.map { $0.error }.skipNil() - - _isEnabled <~ property.producer - .combineLatest(with: isExecuting.producer) - .map(Action.shouldBeEnabled) - } - - /// Initializes an action that will be enabled by default, and creates a - /// SignalProducer for each input. - /// - /// - parameters: - /// - execute: A closure that returns the signal producer returned by - /// calling `apply(Input)` on the action. - public convenience init(_ execute: @escaping (Input) -> SignalProducer) { - self.init(enabledIf: Property(value: true), execute) - } - - deinit { - eventsObserver.sendCompleted() - } - - /// Creates a SignalProducer that, when started, will execute the action - /// with the given input, then forward the results upon the produced Signal. - /// - /// - note: If the action is disabled when the returned SignalProducer is - /// started, the produced signal will send `ActionError.disabled`, - /// and nothing will be sent upon `values` or `errors` for that - /// particular signal. - /// - /// - parameters: - /// - input: A value that will be passed to the closure creating the signal - /// producer. - public func apply(_ input: Input) -> SignalProducer> { - return SignalProducer { observer, disposable in - var startedExecuting = false - - self.executingQueue.sync { - if self._isEnabled.value { - self._isExecuting.value = true - startedExecuting = true - } - } - - if !startedExecuting { - observer.sendFailed(.disabled) - return - } - - self.executeClosure(input).startWithSignal { signal, signalDisposable in - disposable += signalDisposable - - signal.observe { event in - observer.action(event.mapError(ActionError.producerFailed)) - self.eventsObserver.sendNext(event) - } - } - - disposable += { - self._isExecuting.value = false - } - } - } -} - -public protocol ActionProtocol { - /// The type of argument to apply the action to. - associatedtype Input - /// The type of values returned by the action. - associatedtype Output - /// The type of error when the action fails. If errors aren't possible then - /// `NoError` can be used. - associatedtype Error: Swift.Error - - /// Whether the action is currently enabled. - var isEnabled: Property { get } - - /// Extracts an action from the receiver. - var action: Action { get } - - /// Creates a SignalProducer that, when started, will execute the action - /// with the given input, then forward the results upon the produced Signal. - /// - /// - note: If the action is disabled when the returned SignalProducer is - /// started, the produced signal will send `ActionError.disabled`, - /// and nothing will be sent upon `values` or `errors` for that - /// particular signal. - /// - /// - parameters: - /// - input: A value that will be passed to the closure creating the signal - /// producer. - func apply(_ input: Input) -> SignalProducer> -} - -extension Action: ActionProtocol { - public var action: Action { - return self - } -} - -/// The type of error that can occur from Action.apply, where `Error` is the -/// type of error that can be generated by the specific Action instance. -public enum ActionError: Swift.Error { - /// The producer returned from apply() was started while the Action was - /// disabled. - case disabled - - /// The producer returned from apply() sent the given error. - case producerFailed(Error) -} - -public func == (lhs: ActionError, rhs: ActionError) -> Bool { - switch (lhs, rhs) { - case (.disabled, .disabled): - return true - - case let (.producerFailed(left), .producerFailed(right)): - return left == right - - default: - return false - } -} diff --git a/ReactiveSwift/Atomic.swift b/ReactiveSwift/Atomic.swift deleted file mode 100644 index fbea4f8f66..0000000000 --- a/ReactiveSwift/Atomic.swift +++ /dev/null @@ -1,169 +0,0 @@ -// -// Atomic.swift -// ReactiveSwift -// -// Created by Justin Spahr-Summers on 2014-06-10. -// Copyright (c) 2014 GitHub. All rights reserved. -// - -import Foundation - -final class PosixThreadMutex: NSLocking { - private var mutex = pthread_mutex_t() - - init() { - let result = pthread_mutex_init(&mutex, nil) - precondition(result == 0, "Failed to initialize mutex with error \(result).") - } - - deinit { - let result = pthread_mutex_destroy(&mutex) - precondition(result == 0, "Failed to destroy mutex with error \(result).") - } - - func lock() { - let result = pthread_mutex_lock(&mutex) - precondition(result == 0, "Failed to lock \(self) with error \(result).") - } - - func unlock() { - let result = pthread_mutex_unlock(&mutex) - precondition(result == 0, "Failed to unlock \(self) with error \(result).") - } -} - -/// An atomic variable. -public final class Atomic: AtomicProtocol { - private let lock: PosixThreadMutex - private var _value: Value - - /// Initialize the variable with the given initial value. - /// - /// - parameters: - /// - value: Initial value for `self`. - public init(_ value: Value) { - _value = value - lock = PosixThreadMutex() - } - - /// Atomically modifies the variable. - /// - /// - parameters: - /// - action: A closure that takes the current value. - /// - /// - returns: The result of the action. - @discardableResult - public func modify(_ action: (inout Value) throws -> Result) rethrows -> Result { - lock.lock() - defer { lock.unlock() } - - return try action(&_value) - } - - /// Atomically perform an arbitrary action using the current value of the - /// variable. - /// - /// - parameters: - /// - action: A closure that takes the current value. - /// - /// - returns: The result of the action. - @discardableResult - public func withValue(_ action: (Value) throws -> Result) rethrows -> Result { - lock.lock() - defer { lock.unlock() } - - return try action(_value) - } -} - - -/// An atomic variable which uses a recursive lock. -internal final class RecursiveAtomic: AtomicProtocol { - private let lock: NSRecursiveLock - private var _value: Value - private let didSetObserver: ((Value) -> Void)? - - /// Initialize the variable with the given initial value. - /// - /// - parameters: - /// - value: Initial value for `self`. - /// - name: An optional name used to create the recursive lock. - /// - action: An optional closure which would be invoked every time the - /// value of `self` is mutated. - internal init(_ value: Value, name: StaticString? = nil, didSet action: ((Value) -> Void)? = nil) { - _value = value - lock = NSRecursiveLock() - lock.name = name.map(String.init(describing:)) - didSetObserver = action - } - - /// Atomically modifies the variable. - /// - /// - parameters: - /// - action: A closure that takes the current value. - /// - /// - returns: The result of the action. - @discardableResult - func modify(_ action: (inout Value) throws -> Result) rethrows -> Result { - lock.lock() - defer { - didSetObserver?(_value) - lock.unlock() - } - - return try action(&_value) - } - - /// Atomically perform an arbitrary action using the current value of the - /// variable. - /// - /// - parameters: - /// - action: A closure that takes the current value. - /// - /// - returns: The result of the action. - @discardableResult - func withValue(_ action: (Value) throws -> Result) rethrows -> Result { - lock.lock() - defer { lock.unlock() } - - return try action(_value) - } -} - -public protocol AtomicProtocol: class { - associatedtype Value - - @discardableResult - func withValue(_ action: (Value) throws -> Result) rethrows -> Result - - @discardableResult - func modify(_ action: (inout Value) throws -> Result) rethrows -> Result -} - -extension AtomicProtocol { - /// Atomically get or set the value of the variable. - public var value: Value { - get { - return withValue { $0 } - } - - set(newValue) { - swap(newValue) - } - } - - /// Atomically replace the contents of the variable. - /// - /// - parameters: - /// - newValue: A new value for the variable. - /// - /// - returns: The old value. - @discardableResult - public func swap(_ newValue: Value) -> Value { - return modify { (value: inout Value) in - let oldValue = value - value = newValue - return oldValue - } - } -} diff --git a/ReactiveSwift/Bag.swift b/ReactiveSwift/Bag.swift deleted file mode 100644 index 657f746395..0000000000 --- a/ReactiveSwift/Bag.swift +++ /dev/null @@ -1,110 +0,0 @@ -// -// Bag.swift -// ReactiveSwift -// -// Created by Justin Spahr-Summers on 2014-07-10. -// Copyright (c) 2014 GitHub. All rights reserved. -// - -/// A uniquely identifying token for removing a value that was inserted into a -/// Bag. -public final class RemovalToken { - fileprivate var identifier: UInt? - - fileprivate init(identifier: UInt) { - self.identifier = identifier - } -} - -/// An unordered, non-unique collection of values of type `Element`. -public struct Bag { - fileprivate var elements: [BagElement] = [] - private var currentIdentifier: UInt = 0 - - public init() { - } - - /// Insert the given value into `self`, and return a token that can - /// later be passed to `removeValueForToken()`. - /// - /// - parameters: - /// - value: A value that will be inserted. - @discardableResult - public mutating func insert(_ value: Element) -> RemovalToken { - let (nextIdentifier, overflow) = UInt.addWithOverflow(currentIdentifier, 1) - if overflow { - reindex() - } - - let token = RemovalToken(identifier: currentIdentifier) - let element = BagElement(value: value, identifier: currentIdentifier, token: token) - - elements.append(element) - currentIdentifier = nextIdentifier - - return token - } - - /// Remove a value, given the token returned from `insert()`. - /// - /// - note: If the value has already been removed, nothing happens. - /// - /// - parameters: - /// - token: A token returned from a call to `insert()`. - public mutating func remove(using token: RemovalToken) { - if let identifier = token.identifier { - // Removal is more likely for recent objects than old ones. - for i in elements.indices.reversed() { - if elements[i].identifier == identifier { - elements.remove(at: i) - token.identifier = nil - break - } - } - } - } - - /// In the event of an identifier overflow (highly, highly unlikely), reset - /// all current identifiers to reclaim a contiguous set of available - /// identifiers for the future. - private mutating func reindex() { - for i in elements.indices { - currentIdentifier = UInt(i) - - elements[i].identifier = currentIdentifier - elements[i].token.identifier = currentIdentifier - } - } -} - -extension Bag: Collection { - public typealias Index = Array.Index - - public var startIndex: Index { - return elements.startIndex - } - - public var endIndex: Index { - return elements.endIndex - } - - public subscript(index: Index) -> Element { - return elements[index].value - } - - public func index(after i: Index) -> Index { - return i + 1 - } -} - -private struct BagElement { - let value: Value - var identifier: UInt - let token: RemovalToken -} - -extension BagElement: CustomStringConvertible { - var description: String { - return "BagElement(\(value))" - } -} diff --git a/ReactiveSwift/Deprecations+Removals.swift b/ReactiveSwift/Deprecations+Removals.swift deleted file mode 100644 index 53899b6245..0000000000 --- a/ReactiveSwift/Deprecations+Removals.swift +++ /dev/null @@ -1,255 +0,0 @@ -import Foundation -import enum Result.NoError - -// MARK: Removed Types and APIs in ReactiveCocoa 5.0. - -// Renamed Protocols -@available(*, unavailable, renamed:"ActionProtocol") -public enum ActionType {} - -@available(*, unavailable, renamed:"SignalProtocol") -public enum SignalType {} - -@available(*, unavailable, renamed:"SignalProducerProtocol") -public enum SignalProducerType {} - -@available(*, unavailable, renamed:"PropertyProtocol") -public enum PropertyType {} - -@available(*, unavailable, renamed:"MutablePropertyProtocol") -public enum MutablePropertyType {} - -@available(*, unavailable, renamed:"ObserverProtocol") -public enum ObserverType {} - -@available(*, unavailable, renamed:"SchedulerProtocol") -public enum SchedulerType {} - -@available(*, unavailable, renamed:"DateSchedulerProtocol") -public enum DateSchedulerType {} - -@available(*, unavailable, renamed:"OptionalProtocol") -public enum OptionalType {} - -@available(*, unavailable, renamed:"EventLoggerProtocol") -public enum EventLoggerType {} - -@available(*, unavailable, renamed:"EventProtocol") -public enum EventType {} - -// Renamed and Removed Types - -@available(*, unavailable, renamed:"Property") -public struct AnyProperty {} - -@available(*, unavailable, message:"Use 'Property(value:)' to create a constant property instead. 'ConstantProperty' is removed in RAC 5.0.") -public struct ConstantProperty {} - -// Renamed Properties - -extension Disposable { - @available(*, unavailable, renamed:"isDisposed") - public var disposed: Bool { fatalError() } -} - -extension ActionProtocol { - @available(*, unavailable, renamed:"isEnabled") - public var enabled: Bool { fatalError() } - - @available(*, unavailable, renamed:"isExecuting") - public var executing: Bool { fatalError() } -} - -// Renamed Enum cases - -extension Event { - @available(*, unavailable, renamed:"next") - public static var Next: Event { fatalError() } - - @available(*, unavailable, renamed:"failed") - public static var Failed: Event { fatalError() } - - @available(*, unavailable, renamed:"completed") - public static var Completed: Event { fatalError() } - - @available(*, unavailable, renamed:"interrupted") - public static var Interrupted: Event { fatalError() } -} - -extension ActionError { - @available(*, unavailable, renamed:"producerFailed") - public static var ProducerError: ActionError { fatalError() } - - @available(*, unavailable, renamed:"disabled") - public static var NotEnabled: ActionError { fatalError() } -} - -extension FlattenStrategy { - @available(*, unavailable, renamed:"latest") - public static var Latest: FlattenStrategy { fatalError() } - - @available(*, unavailable, renamed:"concat") - public static var Concat: FlattenStrategy { fatalError() } - - @available(*, unavailable, renamed:"merge") - public static var Merge: FlattenStrategy { fatalError() } -} - -// Methods - -extension Bag { - @available(*, unavailable, renamed:"remove(using:)") - public func removeValueForToken(_ token: RemovalToken) { fatalError() } -} - -extension CompositeDisposable { - @available(*, unavailable, renamed:"add(_:)") - public func addDisposable(_ d: Disposable) -> DisposableHandle { fatalError() } -} - -extension SignalProtocol { - @available(*, unavailable, renamed:"take(first:)") - public func take(_ count: Int) -> Signal { fatalError() } - - @available(*, unavailable, renamed:"take(last:)") - public func takeLast(_ count: Int) -> Signal { fatalError() } - - @available(*, unavailable, renamed:"skip(first:)") - public func skip(_ count: Int) -> Signal { fatalError() } - - @available(*, unavailable, renamed:"observe(on:)") - public func observeOn(_ scheduler: UIScheduler) -> Signal { fatalError() } - - @available(*, unavailable, renamed:"combineLatest(with:)") - public func combineLatestWith(_ otherSignal: S) -> Signal<(Value, S.Value), Error> { fatalError() } - - @available(*, unavailable, renamed:"zip(with:)") - public func zipWith(_ otherSignal: S) -> Signal<(Value, S.Value), Error> { fatalError() } - - @available(*, unavailable, renamed:"take(until:)") - public func takeUntil(_ trigger: Signal<(), NoError>) -> Signal { fatalError() } - - @available(*, unavailable, renamed:"take(untilReplacement:)") - public func takeUntilReplacement(_ replacement: Signal) -> Signal { fatalError() } - - @available(*, unavailable, renamed:"skip(until:)") - public func skipUntil(_ trigger: Signal<(), NoError>) -> Signal { fatalError() } - - @available(*, unavailable, renamed:"skip(while:)") - public func skipWhile(_ predicate: (Value) -> Bool) -> Signal { fatalError() } - - @available(*, unavailable, renamed:"take(while:)") - public func takeWhile(_ predicate: (Value) -> Bool) -> Signal { fatalError() } - - @available(*, unavailable, renamed:"timeout(after:raising:on:)") - public func timeoutWithError(_ error: Error, afterInterval: TimeInterval, onScheduler: SchedulerProtocol) -> Signal { fatalError() } - - @available(*, unavailable, message: "This Signal may emit errors which must be handled explicitly, or observed using `observeResult(_:)`") - public func observeNext(_ next: (Value) -> Void) -> Disposable? { fatalError() } -} - -extension SignalProtocol where Value: OptionalProtocol { - @available(*, unavailable, renamed:"skipNil()") - public func ignoreNil() -> SignalProducer { fatalError() } -} - -extension SignalProducerProtocol { - @available(*, unavailable, renamed:"take(first:)") - public func take(_ count: Int) -> SignalProducer { fatalError() } - - @available(*, unavailable, renamed:"take(last:)") - public func takeLast(_ count: Int) -> SignalProducer { fatalError() } - - @available(*, unavailable, renamed:"skip(first:)") - public func skip(_ count: Int) -> SignalProducer { fatalError() } - - @available(*, unavailable, renamed:"observe(on:)") - public func observeOn(_ scheduler: UIScheduler) -> SignalProducer { fatalError() } - - @available(*, unavailable, renamed:"start(on:)") - public func startOn(_ scheduler: UIScheduler) -> SignalProducer { fatalError() } - - @available(*, unavailable, renamed:"combineLatest(with:)") - public func combineLatestWith(_ otherProducer: SignalProducer) -> SignalProducer<(Value, U), Error> { fatalError() } - - @available(*, unavailable, renamed:"combineLatest(with:)") - public func combineLatestWith(_ otherSignal: Signal) -> SignalProducer<(Value, U), Error> { fatalError() } - - @available(*, unavailable, renamed:"zip(with:)") - public func zipWith(_ otherProducer: SignalProducer) -> SignalProducer<(Value, U), Error> { fatalError() } - - @available(*, unavailable, renamed:"zip(with:)") - public func zipWith(_ otherSignal: Signal) -> SignalProducer<(Value, U), Error> { fatalError() } - - @available(*, unavailable, renamed:"take(until:)") - public func takeUntil(_ trigger: Signal<(), NoError>) -> SignalProducer { fatalError() } - - @available(*, unavailable, renamed:"take(until:)") - public func takeUntil(_ trigger: SignalProducer<(), NoError>) -> SignalProducer { fatalError() } - - @available(*, unavailable, renamed:"take(untilReplacement:)") - public func takeUntilReplacement(_ replacement: Signal) -> SignalProducer { fatalError() } - - @available(*, unavailable, renamed:"take(untilReplacement:)") - public func takeUntilReplacement(_ replacement: SignalProducer) -> SignalProducer { fatalError() } - - @available(*, unavailable, renamed:"skip(until:)") - public func skipUntil(_ trigger: Signal<(), NoError>) -> SignalProducer { fatalError() } - - @available(*, unavailable, renamed:"skip(until:)") - public func skipUntil(_ trigger: SignalProducer<(), NoError>) -> SignalProducer { fatalError() } - - @available(*, unavailable, renamed:"skip(while:)") - public func skipWhile(_ predicate: (Value) -> Bool) -> SignalProducer { fatalError() } - - @available(*, unavailable, renamed:"take(while:)") - public func takeWhile(_ predicate: (Value) -> Bool) -> SignalProducer { fatalError() } - - @available(*, unavailable, renamed:"timeout(after:raising:on:)") - public func timeoutWithError(_ error: Error, afterInterval: TimeInterval, onScheduler: SchedulerProtocol) -> SignalProducer { fatalError() } - - @available(*, unavailable, message:"This SignalProducer may emit errors which must be handled explicitly, or observed using `startWithResult(_:)`.") - public func startWithNext(_ next: (Value) -> Void) -> Disposable { fatalError() } -} - -extension SignalProducerProtocol where Value: OptionalProtocol { - @available(*, unavailable, renamed:"skipNil()") - public func ignoreNil() -> SignalProducer { fatalError() } -} - -extension SignalProducer { - @available(*, unavailable, message:"Use properties instead. `buffer(_:)` is removed in RAC 5.0.") - public static func buffer(_ capacity: Int) -> (SignalProducer, Signal.Observer) { fatalError() } -} - -extension PropertyProtocol { - @available(*, unavailable, renamed:"combineLatest(with:)") - public func combineLatestWith(_ otherProperty: P) -> Property<(Value, P.Value)> { fatalError() } - - @available(*, unavailable, renamed:"zip(with:)") - public func zipWith(_ otherProperty: P) -> Property<(Value, P.Value)> { fatalError() } -} - -extension Property { - @available(*, unavailable, renamed:"AnyProperty(initial:then:)") - public convenience init(initialValue: Value, producer: SignalProducer) { fatalError() } - - @available(*, unavailable, renamed:"AnyProperty(initial:then:)") - public convenience init(initialValue: Value, signal: Signal) { fatalError() } -} - -extension DateSchedulerProtocol { - @available(*, unavailable, renamed:"schedule(after:action:)") - func scheduleAfter(date: Date, _ action: () -> Void) -> Disposable? { fatalError() } - - @available(*, unavailable, renamed:"schedule(after:interval:leeway:)") - func scheduleAfter(date: Date, repeatingEvery: TimeInterval, withLeeway: TimeInterval, action: () -> Void) -> Disposable? { fatalError() } -} - -extension TestScheduler { - @available(*, unavailable, renamed:"advanced(by:)") - public func advanceByInterval(_ interval: TimeInterval) { fatalError() } - - @available(*, unavailable, renamed:"advanced(to:)") - public func advanceToDate(_ date: Date) { fatalError() } -} diff --git a/ReactiveSwift/Disposable.swift b/ReactiveSwift/Disposable.swift deleted file mode 100644 index 5c48b71aa2..0000000000 --- a/ReactiveSwift/Disposable.swift +++ /dev/null @@ -1,357 +0,0 @@ -// -// Disposable.swift -// ReactiveSwift -// -// Created by Justin Spahr-Summers on 2014-06-02. -// Copyright (c) 2014 GitHub. All rights reserved. -// - -/// Represents something that can be “disposed”, usually associated with freeing -/// resources or canceling work. -public protocol Disposable: class { - /// Whether this disposable has been disposed already. - var isDisposed: Bool { get } - - /// Method for disposing of resources when appropriate. - func dispose() -} - -/// A type-erased disposable that forwards operations to an underlying disposable. -public final class AnyDisposable: Disposable { - private let disposable: Disposable - - public var isDisposed: Bool { - return disposable.isDisposed - } - - public init(_ disposable: Disposable) { - self.disposable = disposable - } - - public func dispose() { - disposable.dispose() - } -} - -/// A disposable that only flips `isDisposed` upon disposal, and performs no other -/// work. -public final class SimpleDisposable: Disposable { - private let _isDisposed = Atomic(false) - - public var isDisposed: Bool { - return _isDisposed.value - } - - public init() {} - - public func dispose() { - _isDisposed.value = true - } -} - -/// A disposable that will run an action upon disposal. -public final class ActionDisposable: Disposable { - private let action: Atomic<(() -> Void)?> - - public var isDisposed: Bool { - return action.value == nil - } - - /// Initialize the disposable to run the given action upon disposal. - /// - /// - parameters: - /// - action: A closure to run when calling `dispose()`. - public init(action: @escaping () -> Void) { - self.action = Atomic(action) - } - - public func dispose() { - let oldAction = action.swap(nil) - oldAction?() - } -} - -/// A disposable that will dispose of any number of other disposables. -public final class CompositeDisposable: Disposable { - private let disposables: Atomic?> - - /// Represents a handle to a disposable previously added to a - /// CompositeDisposable. - public final class DisposableHandle { - private let bagToken: Atomic - private weak var disposable: CompositeDisposable? - - fileprivate static let empty = DisposableHandle() - - fileprivate init() { - self.bagToken = Atomic(nil) - } - - fileprivate init(bagToken: RemovalToken, disposable: CompositeDisposable) { - self.bagToken = Atomic(bagToken) - self.disposable = disposable - } - - /// Remove the pointed-to disposable from its `CompositeDisposable`. - /// - /// - note: This is useful to minimize memory growth, by removing - /// disposables that are no longer needed. - public func remove() { - if let token = bagToken.swap(nil) { - _ = disposable?.disposables.modify { - $0?.remove(using: token) - } - } - } - } - - public var isDisposed: Bool { - return disposables.value == nil - } - - /// Initialize a `CompositeDisposable` containing the given sequence of - /// disposables. - /// - /// - parameters: - /// - disposables: A collection of objects conforming to the `Disposable` - /// protocol - public init(_ disposables: S) - where S.Iterator.Element == Disposable - { - var bag: Bag = Bag() - - for disposable in disposables { - bag.insert(disposable) - } - - self.disposables = Atomic(bag) - } - - /// Initialize a `CompositeDisposable` containing the given sequence of - /// disposables. - /// - /// - parameters: - /// - disposables: A collection of objects conforming to the `Disposable` - /// protocol - public convenience init(_ disposables: S) - where S.Iterator.Element == Disposable? - { - self.init(disposables.flatMap { $0 }) - } - - /// Initializes an empty `CompositeDisposable`. - public convenience init() { - self.init([Disposable]()) - } - - public func dispose() { - if let ds = disposables.swap(nil) { - for d in ds.reversed() { - d.dispose() - } - } - } - - /// Add the given disposable to the list, then return a handle which can - /// be used to opaquely remove the disposable later (if desired). - /// - /// - parameters: - /// - d: Optional disposable. - /// - /// - returns: An instance of `DisposableHandle` that can be used to - /// opaquely remove the disposable later (if desired). - @discardableResult - public func add(_ d: Disposable?) -> DisposableHandle { - guard let d = d else { - return DisposableHandle.empty - } - - let handle: DisposableHandle? = disposables.modify { - return ($0?.insert(d)).map { DisposableHandle(bagToken: $0, disposable: self) } - } - - if let handle = handle { - return handle - } else { - d.dispose() - return DisposableHandle.empty - } - } - - /// Add an ActionDisposable to the list. - /// - /// - parameters: - /// - action: A closure that will be invoked when `dispose()` is called. - /// - /// - returns: An instance of `DisposableHandle` that can be used to - /// opaquely remove the disposable later (if desired). - public func add(_ action: @escaping () -> Void) -> DisposableHandle { - return add(ActionDisposable(action: action)) - } -} - -/// A disposable that, upon deinitialization, will automatically dispose of -/// another disposable. -public final class ScopedDisposable: Disposable { - /// The disposable which will be disposed when the ScopedDisposable - /// deinitializes. - public let innerDisposable: InnerDisposable - - public var isDisposed: Bool { - return innerDisposable.isDisposed - } - - /// Initialize the receiver to dispose of the argument upon - /// deinitialization. - /// - /// - parameters: - /// - disposable: A disposable to dispose of when deinitializing. - public init(_ disposable: InnerDisposable) { - innerDisposable = disposable - } - - deinit { - dispose() - } - - public func dispose() { - innerDisposable.dispose() - } -} - -extension ScopedDisposable where InnerDisposable: AnyDisposable { - /// Initialize the receiver to dispose of the argument upon - /// deinitialization. - /// - /// - parameters: - /// - disposable: A disposable to dispose of when deinitializing, which - /// will be wrapped in an `AnyDisposable`. - public convenience init(_ disposable: Disposable) { - self.init(AnyDisposable(disposable)) - } -} - -/// A disposable that will optionally dispose of another disposable. -public final class SerialDisposable: Disposable { - private struct State { - var innerDisposable: Disposable? = nil - var isDisposed = false - } - - private let state = Atomic(State()) - - public var isDisposed: Bool { - return state.value.isDisposed - } - - /// The inner disposable to dispose of. - /// - /// Whenever this property is set (even to the same value!), the previous - /// disposable is automatically disposed. - public var innerDisposable: Disposable? { - get { - return state.value.innerDisposable - } - - set(d) { - let oldState: State = state.modify { state in - defer { state.innerDisposable = d } - return state - } - - oldState.innerDisposable?.dispose() - if oldState.isDisposed { - d?.dispose() - } - } - } - - /// Initializes the receiver to dispose of the argument when the - /// SerialDisposable is disposed. - /// - /// - parameters: - /// - disposable: Optional disposable. - public init(_ disposable: Disposable? = nil) { - innerDisposable = disposable - } - - public func dispose() { - let orig = state.swap(State(innerDisposable: nil, isDisposed: true)) - orig.innerDisposable?.dispose() - } -} - -/// Adds the right-hand-side disposable to the left-hand-side -/// `CompositeDisposable`. -/// -/// ```` -/// disposable += producer -/// .filter { ... } -/// .map { ... } -/// .start(observer) -/// ```` -/// -/// - parameters: -/// - lhs: Disposable to add to. -/// - rhs: Disposable to add. -/// -/// - returns: An instance of `DisposableHandle` that can be used to opaquely -/// remove the disposable later (if desired). -@discardableResult -public func +=(lhs: CompositeDisposable, rhs: Disposable?) -> CompositeDisposable.DisposableHandle { - return lhs.add(rhs) -} - -/// Adds the right-hand-side `ActionDisposable` to the left-hand-side -/// `CompositeDisposable`. -/// -/// ```` -/// disposable += { ... } -/// ```` -/// -/// - parameters: -/// - lhs: Disposable to add to. -/// - rhs: Closure to add as a disposable. -/// -/// - returns: An instance of `DisposableHandle` that can be used to opaquely -/// remove the disposable later (if desired). -@discardableResult -public func +=(lhs: CompositeDisposable, rhs: @escaping () -> ()) -> CompositeDisposable.DisposableHandle { - return lhs.add(rhs) -} - -/// Adds the right-hand-side disposable to the left-hand-side -/// `ScopedDisposable`. -/// -/// ```` -/// disposable += { ... } -/// ```` -/// -/// - parameters: -/// - lhs: Disposable to add to. -/// - rhs: Disposable to add. -/// -/// - returns: An instance of `DisposableHandle` that can be used to opaquely -/// remove the disposable later (if desired). -@discardableResult -public func +=(lhs: ScopedDisposable, rhs: Disposable?) -> CompositeDisposable.DisposableHandle { - return lhs.innerDisposable.add(rhs) -} - -/// Adds the right-hand-side disposable to the left-hand-side -/// `ScopedDisposable`. -/// -/// ```` -/// disposable += { ... } -/// ```` -/// -/// - parameters: -/// - lhs: Disposable to add to. -/// - rhs: Closure to add as a disposable. -/// -/// - returns: An instance of `DisposableHandle` that can be used to opaquely -/// remove the disposable later (if desired). -@discardableResult -public func +=(lhs: ScopedDisposable, rhs: @escaping () -> ()) -> CompositeDisposable.DisposableHandle { - return lhs.innerDisposable.add(rhs) -} diff --git a/ReactiveSwift/Event.swift b/ReactiveSwift/Event.swift deleted file mode 100644 index 06bcc2eac3..0000000000 --- a/ReactiveSwift/Event.swift +++ /dev/null @@ -1,165 +0,0 @@ -// -// Event.swift -// ReactiveSwift -// -// Created by Justin Spahr-Summers on 2015-01-16. -// Copyright (c) 2015 GitHub. All rights reserved. -// - -/// Represents a signal event. -/// -/// Signals must conform to the grammar: -/// `next* (failed | completed | interrupted)?` -public enum Event { - /// A value provided by the signal. - case next(Value) - - /// The signal terminated because of an error. No further events will be - /// received. - case failed(Error) - - /// The signal successfully terminated. No further events will be received. - case completed - - /// Event production on the signal has been interrupted. No further events - /// will be received. - /// - /// - important: This event does not signify the successful or failed - /// completion of the signal. - case interrupted - - /// Whether this event indicates signal termination (i.e., that no further - /// events will be received). - public var isTerminating: Bool { - switch self { - case .next: - return false - - case .failed, .completed, .interrupted: - return true - } - } - - /// Lift the given closure over the event's value. - /// - /// - important: The closure is called only on `next` type events. - /// - /// - parameters: - /// - f: A closure that accepts a value and returns a new value - /// - /// - returns: An event with function applied to a value in case `self` is a - /// `next` type of event. - public func map(_ f: (Value) -> U) -> Event { - switch self { - case let .next(value): - return .next(f(value)) - - case let .failed(error): - return .failed(error) - - case .completed: - return .completed - - case .interrupted: - return .interrupted - } - } - - /// Lift the given closure over the event's error. - /// - /// - important: The closure is called only on failed type event. - /// - /// - parameters: - /// - f: A closure that accepts an error object and returns - /// a new error object - /// - /// - returns: An event with function applied to an error object in case - /// `self` is a `.Failed` type of event. - public func mapError(_ f: (Error) -> F) -> Event { - switch self { - case let .next(value): - return .next(value) - - case let .failed(error): - return .failed(f(error)) - - case .completed: - return .completed - - case .interrupted: - return .interrupted - } - } - - /// Unwrap the contained `next` value. - public var value: Value? { - if case let .next(value) = self { - return value - } else { - return nil - } - } - - /// Unwrap the contained `Error` value. - public var error: Error? { - if case let .failed(error) = self { - return error - } else { - return nil - } - } -} - -public func == (lhs: Event, rhs: Event) -> Bool { - switch (lhs, rhs) { - case let (.next(left), .next(right)): - return left == right - - case let (.failed(left), .failed(right)): - return left == right - - case (.completed, .completed): - return true - - case (.interrupted, .interrupted): - return true - - default: - return false - } -} - -extension Event: CustomStringConvertible { - public var description: String { - switch self { - case let .next(value): - return "NEXT \(value)" - - case let .failed(error): - return "FAILED \(error)" - - case .completed: - return "COMPLETED" - - case .interrupted: - return "INTERRUPTED" - } - } -} - -/// Event protocol for constraining signal extensions -public protocol EventProtocol { - /// The value type of an event. - associatedtype Value - /// The error type of an event. If errors aren't possible then `NoError` can - /// be used. - associatedtype Error: Swift.Error - /// Extracts the event from the receiver. - var event: Event { get } -} - -extension Event: EventProtocol { - public var event: Event { - return self - } -} diff --git a/ReactiveSwift/EventLogger.swift b/ReactiveSwift/EventLogger.swift deleted file mode 100644 index 2d43aa01d7..0000000000 --- a/ReactiveSwift/EventLogger.swift +++ /dev/null @@ -1,133 +0,0 @@ -// -// EventLogger.swift -// ReactiveSwift -// -// Created by Rui Peres on 30/04/2016. -// Copyright © 2016 GitHub. All rights reserved. -// - -import Foundation - -/// A namespace for logging event types. -public enum LoggingEvent { - public enum Signal: String { - case next, completed, failed, terminated, disposed, interrupted - - public static let allEvents: Set = [ - .next, .completed, .failed, .terminated, .disposed, .interrupted, - ] - } - - public enum SignalProducer: String { - case starting, started, next, completed, failed, terminated, disposed, interrupted - - public static let allEvents: Set = [ - .starting, .started, .next, .completed, .failed, .terminated, .disposed, .interrupted, - ] - } -} - -private func defaultEventLog(identifier: String, event: String, fileName: String, functionName: String, lineNumber: Int) { - print("[\(identifier)] \(event) fileName: \(fileName), functionName: \(functionName), lineNumber: \(lineNumber)") -} - -/// A type that represents an event logging function. -public typealias EventLogger = ( - _ identifier: String, - _ event: String, - _ fileName: String, - _ functionName: String, - _ lineNumber: Int -) -> Void - -extension SignalProtocol { - /// Logs all events that the receiver sends. By default, it will print to - /// the standard output. - /// - /// - parameters: - /// - identifier: a string to identify the Signal firing events. - /// - events: Types of events to log. - /// - fileName: Name of the file containing the code which fired the - /// event. - /// - functionName: Function where event was fired. - /// - lineNumber: Line number where event was fired. - /// - logger: Logger that logs the events. - /// - /// - returns: Signal that, when observed, logs the fired events. - public func logEvents(identifier: String = "", events: Set = LoggingEvent.Signal.allEvents, fileName: String = #file, functionName: String = #function, lineNumber: Int = #line, logger: @escaping EventLogger = defaultEventLog) -> Signal { - func log(_ event: LoggingEvent.Signal) -> ((T) -> Void)? { - return event.logIfNeeded(events: events) { event in - logger(identifier, event, fileName, functionName, lineNumber) - } - } - - return self.on( - failed: log(.failed), - completed: log(.completed), - interrupted: log(.interrupted), - terminated: log(.terminated), - disposed: log(.disposed), - next: log(.next) - ) - } -} - -extension SignalProducerProtocol { - /// Logs all events that the receiver sends. By default, it will print to - /// the standard output. - /// - /// - parameters: - /// - identifier: a string to identify the SignalProducer firing events. - /// - events: Types of events to log. - /// - fileName: Name of the file containing the code which fired the - /// event. - /// - functionName: Function where event was fired. - /// - lineNumber: Line number where event was fired. - /// - logger: Logger that logs the events. - /// - /// - returns: Signal producer that, when started, logs the fired events. - public func logEvents(identifier: String = "", - events: Set = LoggingEvent.SignalProducer.allEvents, - fileName: String = #file, - functionName: String = #function, - lineNumber: Int = #line, - logger: @escaping EventLogger = defaultEventLog - ) -> SignalProducer { - func log(_ event: LoggingEvent.SignalProducer) -> ((T) -> Void)? { - return event.logIfNeeded(events: events) { event in - logger(identifier, event, fileName, functionName, lineNumber) - } - } - - return self.on( - starting: log(.starting), - started: log(.started), - next: log(.next), - failed: log(.failed), - completed: log(.completed), - interrupted: log(.interrupted), - terminated: log(.terminated), - disposed: log(.disposed) - ) - } -} - -private protocol LoggingEventProtocol: Hashable, RawRepresentable {} -extension LoggingEvent.Signal: LoggingEventProtocol {} -extension LoggingEvent.SignalProducer: LoggingEventProtocol {} - -private extension LoggingEventProtocol { - func logIfNeeded(events: Set, logger: @escaping (String) -> Void) -> ((T) -> Void)? { - guard events.contains(self) else { - return nil - } - - return { value in - if value is Void { - logger("\(self.rawValue)") - } else { - logger("\(self.rawValue) \(value)") - } - } - } -} diff --git a/ReactiveSwift/Flatten.swift b/ReactiveSwift/Flatten.swift deleted file mode 100644 index 716bd5668a..0000000000 --- a/ReactiveSwift/Flatten.swift +++ /dev/null @@ -1,928 +0,0 @@ -// -// Flatten.swift -// ReactiveSwift -// -// Created by Neil Pankey on 11/30/15. -// Copyright © 2015 GitHub. All rights reserved. -// - -import enum Result.NoError - -/// Describes how multiple producers should be joined together. -public enum FlattenStrategy: Equatable { - /// The producers should be merged, so that any value received on any of the - /// input producers will be forwarded immediately to the output producer. - /// - /// The resulting producer will complete only when all inputs have - /// completed. - case merge - - /// The producers should be concatenated, so that their values are sent in - /// the order of the producers themselves. - /// - /// The resulting producer will complete only when all inputs have - /// completed. - case concat - - /// Only the events from the latest input producer should be considered for - /// the output. Any producers received before that point will be disposed - /// of. - /// - /// The resulting producer will complete only when the producer-of-producers - /// and the latest producer has completed. - case latest -} - - -extension SignalProtocol where Value: SignalProducerProtocol, Error == Value.Error { - /// Flattens the inner producers sent upon `signal` (into a single signal of - /// values), according to the semantics of the given strategy. - /// - /// - note: If `signal` or an active inner producer fails, the returned - /// signal will forward that failure immediately. - /// - /// - note: `interrupted` events on inner producers will be treated like - /// `Completed events on inner producers. - public func flatten(_ strategy: FlattenStrategy) -> Signal { - switch strategy { - case .merge: - return self.merge() - - case .concat: - return self.concat() - - case .latest: - return self.switchToLatest() - } - } -} - -extension SignalProtocol where Value: SignalProducerProtocol, Error == NoError { - /// Flattens the inner producers sent upon `signal` (into a single signal of - /// values), according to the semantics of the given strategy. - /// - /// - note: If an active inner producer fails, the returned signal will - /// forward that failure immediately. - /// - /// - warning: `interrupted` events on inner producers will be treated like - /// `completed` events on inner producers. - /// - /// - parameters: - /// - strategy: Strategy used when flattening signals. - public func flatten(_ strategy: FlattenStrategy) -> Signal { - return self - .promoteErrors(Value.Error.self) - .flatten(strategy) - } -} - -extension SignalProtocol where Value: SignalProducerProtocol, Error == NoError, Value.Error == NoError { - /// Flattens the inner producers sent upon `signal` (into a single signal of - /// values), according to the semantics of the given strategy. - /// - /// - warning: `interrupted` events on inner producers will be treated like - /// `completed` events on inner producers. - /// - /// - parameters: - /// - strategy: Strategy used when flattening signals. - public func flatten(_ strategy: FlattenStrategy) -> Signal { - switch strategy { - case .merge: - return self.merge() - - case .concat: - return self.concat() - - case .latest: - return self.switchToLatest() - } - } -} - -extension SignalProtocol where Value: SignalProducerProtocol, Value.Error == NoError { - /// Flattens the inner producers sent upon `signal` (into a single signal of - /// values), according to the semantics of the given strategy. - /// - /// - note: If `signal` fails, the returned signal will forward that failure - /// immediately. - /// - /// - warning: `interrupted` events on inner producers will be treated like - /// `completed` events on inner producers. - public func flatten(_ strategy: FlattenStrategy) -> Signal { - return self.flatMap(strategy) { $0.promoteErrors(Error.self) } - } -} - -extension SignalProducerProtocol where Value: SignalProducerProtocol, Error == Value.Error { - /// Flattens the inner producers sent upon `producer` (into a single - /// producer of values), according to the semantics of the given strategy. - /// - /// - note: If `producer` or an active inner producer fails, the returned - /// producer will forward that failure immediately. - /// - /// - warning: `interrupted` events on inner producers will be treated like - /// `completed` events on inner producers. - public func flatten(_ strategy: FlattenStrategy) -> SignalProducer { - switch strategy { - case .merge: - return self.merge() - - case .concat: - return self.concat() - - case .latest: - return self.switchToLatest() - } - } -} - -extension SignalProducerProtocol where Value: SignalProducerProtocol, Error == NoError { - /// Flattens the inner producers sent upon `producer` (into a single - /// producer of values), according to the semantics of the given strategy. - /// - /// - note: If an active inner producer fails, the returned producer will - /// forward that failure immediately. - /// - /// - warning: `interrupted` events on inner producers will be treated like - /// `completed` events on inner producers. - public func flatten(_ strategy: FlattenStrategy) -> SignalProducer { - return self - .promoteErrors(Value.Error.self) - .flatten(strategy) - } -} - -extension SignalProducerProtocol where Value: SignalProducerProtocol, Error == NoError, Value.Error == NoError { - /// Flattens the inner producers sent upon `producer` (into a single - /// producer of values), according to the semantics of the given strategy. - /// - /// - warning: `interrupted` events on inner producers will be treated like - /// `completed` events on inner producers. - public func flatten(_ strategy: FlattenStrategy) -> SignalProducer { - switch strategy { - case .merge: - return self.merge() - - case .concat: - return self.concat() - - case .latest: - return self.switchToLatest() - } - } -} - -extension SignalProducerProtocol where Value: SignalProducerProtocol, Value.Error == NoError { - /// Flattens the inner producers sent upon `signal` (into a single signal of - /// values), according to the semantics of the given strategy. - /// - /// - note: If `signal` fails, the returned signal will forward that failure - /// immediately. - /// - /// - warning: `interrupted` events on inner producers will be treated like - /// `completed` events on inner producers. - public func flatten(_ strategy: FlattenStrategy) -> SignalProducer { - return self.flatMap(strategy) { $0.promoteErrors(Error.self) } - } -} - -extension SignalProtocol where Value: SignalProtocol, Error == Value.Error { - /// Flattens the inner signals sent upon `signal` (into a single signal of - /// values), according to the semantics of the given strategy. - /// - /// - note: If `signal` or an active inner signal emits an error, the - /// returned signal will forward that error immediately. - /// - /// - warning: `interrupted` events on inner signals will be treated like - /// `completed` events on inner signals. - public func flatten(_ strategy: FlattenStrategy) -> Signal { - return self - .map(SignalProducer.init) - .flatten(strategy) - } -} - -extension SignalProtocol where Value: SignalProtocol, Error == NoError { - /// Flattens the inner signals sent upon `signal` (into a single signal of - /// values), according to the semantics of the given strategy. - /// - /// - note: If an active inner signal emits an error, the returned signal - /// will forward that error immediately. - /// - /// - warning: `interrupted` events on inner signals will be treated like - /// `completed` events on inner signals. - public func flatten(_ strategy: FlattenStrategy) -> Signal { - return self - .promoteErrors(Value.Error.self) - .flatten(strategy) - } -} - -extension SignalProtocol where Value: SignalProtocol, Error == NoError, Value.Error == NoError { - /// Flattens the inner signals sent upon `signal` (into a single signal of - /// values), according to the semantics of the given strategy. - /// - /// - warning: `interrupted` events on inner signals will be treated like - /// `completed` events on inner signals. - public func flatten(_ strategy: FlattenStrategy) -> Signal { - return self - .map(SignalProducer.init) - .flatten(strategy) - } -} - -extension SignalProtocol where Value: SignalProtocol, Value.Error == NoError { - /// Flattens the inner signals sent upon `signal` (into a single signal of - /// values), according to the semantics of the given strategy. - /// - /// - note: If `signal` emits an error, the returned signal will forward - /// that error immediately. - /// - /// - warning: `interrupted` events on inner signals will be treated like - /// `completed` events on inner signals. - public func flatten(_ strategy: FlattenStrategy) -> Signal { - return self.flatMap(strategy) { $0.promoteErrors(Error.self) } - } -} - -extension SignalProtocol where Value: Sequence, Error == NoError { - /// Flattens the `sequence` value sent by `signal` according to - /// the semantics of the given strategy. - public func flatten(_ strategy: FlattenStrategy) -> Signal { - return self.flatMap(strategy) { .init(values: $0) } - } -} - -extension SignalProducerProtocol where Value: SignalProtocol, Error == Value.Error { - /// Flattens the inner signals sent upon `producer` (into a single producer - /// of values), according to the semantics of the given strategy. - /// - /// - note: If `producer` or an active inner signal emits an error, the - /// returned producer will forward that error immediately. - /// - /// - warning: `interrupted` events on inner signals will be treated like - /// `completed` events on inner signals. - public func flatten(_ strategy: FlattenStrategy) -> SignalProducer { - return self - .map(SignalProducer.init) - .flatten(strategy) - } -} - -extension SignalProducerProtocol where Value: SignalProtocol, Error == NoError { - /// Flattens the inner signals sent upon `producer` (into a single producer - /// of values), according to the semantics of the given strategy. - /// - /// - note: If an active inner signal emits an error, the returned producer - /// will forward that error immediately. - /// - /// - warning: `interrupted` events on inner signals will be treated like - /// `completed` events on inner signals. - public func flatten(_ strategy: FlattenStrategy) -> SignalProducer { - return self - .promoteErrors(Value.Error.self) - .flatten(strategy) - } -} - -extension SignalProducerProtocol where Value: SignalProtocol, Error == NoError, Value.Error == NoError { - /// Flattens the inner signals sent upon `producer` (into a single producer - /// of values), according to the semantics of the given strategy. - /// - /// - warning: `interrupted` events on inner signals will be treated like - /// `completed` events on inner signals. - public func flatten(_ strategy: FlattenStrategy) -> SignalProducer { - return self - .map(SignalProducer.init) - .flatten(strategy) - } -} - -extension SignalProducerProtocol where Value: SignalProtocol, Value.Error == NoError { - /// Flattens the inner signals sent upon `producer` (into a single producer - /// of values), according to the semantics of the given strategy. - /// - /// - note: If `producer` emits an error, the returned producer will forward - /// that error immediately. - /// - /// - warning: `interrupted` events on inner signals will be treated like - /// `completed` events on inner signals. - public func flatten(_ strategy: FlattenStrategy) -> SignalProducer { - return self.flatMap(strategy) { $0.promoteErrors(Error.self) } - } -} - -extension SignalProducerProtocol where Value: Sequence, Error == NoError { - /// Flattens the `sequence` value sent by `producer` according to - /// the semantics of the given strategy. - public func flatten(_ strategy: FlattenStrategy) -> SignalProducer { - return self.flatMap(strategy) { .init(values: $0) } - } -} - -extension SignalProtocol where Value: SignalProducerProtocol, Error == Value.Error { - /// Returns a signal which sends all the values from producer signal emitted - /// from `signal`, waiting until each inner producer completes before - /// beginning to send the values from the next inner producer. - /// - /// - note: If any of the inner producers fail, the returned signal will - /// forward that failure immediately - /// - /// - note: The returned signal completes only when `signal` and all - /// producers emitted from `signal` complete. - fileprivate func concat() -> Signal { - return Signal { relayObserver in - let disposable = CompositeDisposable() - let relayDisposable = CompositeDisposable() - - disposable += relayDisposable - disposable += self.observeConcat(relayObserver, relayDisposable) - - return disposable - } - } - - fileprivate func observeConcat(_ observer: Observer, _ disposable: CompositeDisposable? = nil) -> Disposable? { - let state = ConcatState(observer: observer, disposable: disposable) - - return self.observe { event in - switch event { - case let .next(value): - state.enqueueSignalProducer(value.producer) - - case let .failed(error): - observer.sendFailed(error) - - case .completed: - // Add one last producer to the queue, whose sole job is to - // "turn out the lights" by completing `observer`. - state.enqueueSignalProducer(SignalProducer.empty.on(completed: { - observer.sendCompleted() - })) - - case .interrupted: - observer.sendInterrupted() - } - } - } -} - -extension SignalProducerProtocol where Value: SignalProducerProtocol, Error == Value.Error { - /// Returns a producer which sends all the values from each producer emitted - /// from `producer`, waiting until each inner producer completes before - /// beginning to send the values from the next inner producer. - /// - /// - note: If any of the inner producers emit an error, the returned - /// producer will emit that error. - /// - /// - note: The returned producer completes only when `producer` and all - /// producers emitted from `producer` complete. - fileprivate func concat() -> SignalProducer { - return SignalProducer { observer, disposable in - self.startWithSignal { signal, signalDisposable in - disposable += signalDisposable - _ = signal.observeConcat(observer, disposable) - } - } - } -} - -extension SignalProducerProtocol { - /// `concat`s `next` onto `self`. - public func concat(_ next: SignalProducer) -> SignalProducer { - return SignalProducer, Error>(values: [ self.producer, next ]).flatten(.concat) - } - - /// `concat`s `value` onto `self`. - public func concat(value: Value) -> SignalProducer { - return self.concat(SignalProducer(value: value)) - } - - /// `concat`s `self` onto initial `previous`. - public func prefix(_ previous: P) -> SignalProducer - where P.Value == Value, P.Error == Error - { - return previous.concat(self.producer) - } - - /// `concat`s `self` onto initial `value`. - public func prefix(value: Value) -> SignalProducer { - return self.prefix(SignalProducer(value: value)) - } -} - -private final class ConcatState { - /// The observer of a started `concat` producer. - let observer: Observer - - /// The top level disposable of a started `concat` producer. - let disposable: CompositeDisposable? - - /// The active producer, if any, and the producers waiting to be started. - let queuedSignalProducers: Atomic<[SignalProducer]> = Atomic([]) - - init(observer: Signal.Observer, disposable: CompositeDisposable?) { - self.observer = observer - self.disposable = disposable - } - - func enqueueSignalProducer(_ producer: SignalProducer) { - if let d = disposable, d.isDisposed { - return - } - - let shouldStart: Bool = queuedSignalProducers.modify { queue in - // An empty queue means the concat is idle, ready & waiting to start - // the next producer. - defer { queue.append(producer) } - return queue.isEmpty - } - - if shouldStart { - startNextSignalProducer(producer) - } - } - - func dequeueSignalProducer() -> SignalProducer? { - if let d = disposable, d.isDisposed { - return nil - } - - return queuedSignalProducers.modify { queue in - // Active producers remain in the queue until completed. Since - // dequeueing happens at completion of the active producer, the - // first producer in the queue can be removed. - if !queue.isEmpty { queue.remove(at: 0) } - return queue.first - } - } - - /// Subscribes to the given signal producer. - func startNextSignalProducer(_ signalProducer: SignalProducer) { - signalProducer.startWithSignal { signal, disposable in - let handle = self.disposable?.add(disposable) ?? nil - - signal.observe { event in - switch event { - case .completed, .interrupted: - handle?.remove() - - if let nextSignalProducer = self.dequeueSignalProducer() { - self.startNextSignalProducer(nextSignalProducer) - } - - case .next, .failed: - self.observer.action(event) - } - } - } - } -} - -extension SignalProtocol where Value: SignalProducerProtocol, Error == Value.Error { - /// Merges a `signal` of SignalProducers down into a single signal, biased - /// toward the producer added earlier. Returns a Signal that will forward - /// events from the inner producers as they arrive. - fileprivate func merge() -> Signal { - return Signal { relayObserver in - let disposable = CompositeDisposable() - let relayDisposable = CompositeDisposable() - - disposable += relayDisposable - disposable += self.observeMerge(relayObserver, relayDisposable) - - return disposable - } - } - - fileprivate func observeMerge(_ observer: Observer, _ disposable: CompositeDisposable) -> Disposable? { - let inFlight = Atomic(1) - let decrementInFlight = { - let shouldComplete: Bool = inFlight.modify { - $0 -= 1 - return $0 == 0 - } - - if shouldComplete { - observer.sendCompleted() - } - } - - return self.observe { event in - switch event { - case let .next(producer): - producer.startWithSignal { innerSignal, innerDisposable in - inFlight.modify { $0 += 1 } - let handle = disposable.add(innerDisposable) - - innerSignal.observe { event in - switch event { - case .completed, .interrupted: - handle.remove() - decrementInFlight() - - case .next, .failed: - observer.action(event) - } - } - } - - case let .failed(error): - observer.sendFailed(error) - - case .completed: - decrementInFlight() - - case .interrupted: - observer.sendInterrupted() - } - } - } -} - -extension SignalProducerProtocol where Value: SignalProducerProtocol, Error == Value.Error { - /// Merges a `signal` of SignalProducers down into a single signal, biased - /// toward the producer added earlier. Returns a Signal that will forward - /// events from the inner producers as they arrive. - fileprivate func merge() -> SignalProducer { - return SignalProducer { relayObserver, disposable in - self.startWithSignal { signal, signalDisposable in - disposable += signalDisposable - - _ = signal.observeMerge(relayObserver, disposable) - } - - } - } -} - -extension SignalProtocol { - /// Merges the given signals into a single `Signal` that will emit all - /// values from each of them, and complete when all of them have completed. - public static func merge(_ signals: Seq) -> Signal - where S.Value == Value, S.Error == Error, Seq.Iterator.Element == S - { - let producer = SignalProducer(values: signals) - var result: Signal! - - producer.startWithSignal { signal, _ in - result = signal.flatten(.merge) - } - - return result - } - - /// Merges the given signals into a single `Signal` that will emit all - /// values from each of them, and complete when all of them have completed. - public static func merge(_ signals: S...) -> Signal - where S.Value == Value, S.Error == Error - { - return Signal.merge(signals) - } -} - -extension SignalProducerProtocol { - /// Merges the given producers into a single `SignalProducer` that will emit - /// all values from each of them, and complete when all of them have - /// completed. - public static func merge(_ producers: Seq) -> SignalProducer - where S.Value == Value, S.Error == Error, Seq.Iterator.Element == S - { - return SignalProducer(values: producers).flatten(.merge) - } - - /// Merges the given producers into a single `SignalProducer` that will emit - /// all values from each of them, and complete when all of them have - /// completed. - public static func merge(_ producers: S...) -> SignalProducer - where S.Value == Value, S.Error == Error - { - return SignalProducer.merge(producers) - } -} - -extension SignalProtocol where Value: SignalProducerProtocol, Error == Value.Error { - /// Returns a signal that forwards values from the latest signal sent on - /// `signal`, ignoring values sent on previous inner signal. - /// - /// An error sent on `signal` or the latest inner signal will be sent on the - /// returned signal. - /// - /// The returned signal completes when `signal` and the latest inner - /// signal have both completed. - fileprivate func switchToLatest() -> Signal { - return Signal { observer in - let composite = CompositeDisposable() - let serial = SerialDisposable() - - composite += serial - composite += self.observeSwitchToLatest(observer, serial) - - return composite - } - } - - fileprivate func observeSwitchToLatest(_ observer: Observer, _ latestInnerDisposable: SerialDisposable) -> Disposable? { - let state = Atomic(LatestState()) - - return self.observe { event in - switch event { - case let .next(innerProducer): - innerProducer.startWithSignal { innerSignal, innerDisposable in - state.modify { - // When we replace the disposable below, this prevents - // the generated Interrupted event from doing any work. - $0.replacingInnerSignal = true - } - - latestInnerDisposable.innerDisposable = innerDisposable - - state.modify { - $0.replacingInnerSignal = false - $0.innerSignalComplete = false - } - - innerSignal.observe { event in - switch event { - case .interrupted: - // If interruption occurred as a result of a new - // producer arriving, we don't want to notify our - // observer. - let shouldComplete: Bool = state.modify { state in - if !state.replacingInnerSignal { - state.innerSignalComplete = true - } - return !state.replacingInnerSignal && state.outerSignalComplete - } - - if shouldComplete { - observer.sendCompleted() - } - - case .completed: - let shouldComplete: Bool = state.modify { - $0.innerSignalComplete = true - return $0.outerSignalComplete - } - - if shouldComplete { - observer.sendCompleted() - } - - case .next, .failed: - observer.action(event) - } - } - } - - case let .failed(error): - observer.sendFailed(error) - - case .completed: - let shouldComplete: Bool = state.modify { - $0.outerSignalComplete = true - return $0.innerSignalComplete - } - - if shouldComplete { - observer.sendCompleted() - } - - case .interrupted: - observer.sendInterrupted() - } - } - } -} - -extension SignalProducerProtocol where Value: SignalProducerProtocol, Error == Value.Error { - /// Returns a signal that forwards values from the latest signal sent on - /// `signal`, ignoring values sent on previous inner signal. - /// - /// An error sent on `signal` or the latest inner signal will be sent on the - /// returned signal. - /// - /// The returned signal completes when `signal` and the latest inner - /// signal have both completed. - fileprivate func switchToLatest() -> SignalProducer { - return SignalProducer { observer, disposable in - let latestInnerDisposable = SerialDisposable() - disposable += latestInnerDisposable - - self.startWithSignal { signal, signalDisposable in - disposable += signalDisposable - disposable += signal.observeSwitchToLatest(observer, latestInnerDisposable) - } - } - } -} - -private struct LatestState { - var outerSignalComplete: Bool = false - var innerSignalComplete: Bool = true - - var replacingInnerSignal: Bool = false -} - - -extension SignalProtocol { - /// Maps each event from `signal` to a new signal, then flattens the - /// resulting producers (into a signal of values), according to the - /// semantics of the given strategy. - /// - /// If `signal` or any of the created producers fail, the returned signal - /// will forward that failure immediately. - public func flatMap(_ strategy: FlattenStrategy, transform: @escaping (Value) -> SignalProducer) -> Signal { - return map(transform).flatten(strategy) - } - - /// Maps each event from `signal` to a new signal, then flattens the - /// resulting producers (into a signal of values), according to the - /// semantics of the given strategy. - /// - /// If `signal` fails, the returned signal will forward that failure - /// immediately. - public func flatMap(_ strategy: FlattenStrategy, transform: @escaping (Value) -> SignalProducer) -> Signal { - return map(transform).flatten(strategy) - } - - /// Maps each event from `signal` to a new signal, then flattens the - /// resulting signals (into a signal of values), according to the - /// semantics of the given strategy. - /// - /// If `signal` or any of the created signals emit an error, the returned - /// signal will forward that error immediately. - public func flatMap(_ strategy: FlattenStrategy, transform: @escaping (Value) -> Signal) -> Signal { - return map(transform).flatten(strategy) - } - - /// Maps each event from `signal` to a new signal, then flattens the - /// resulting signals (into a signal of values), according to the - /// semantics of the given strategy. - /// - /// If `signal` emits an error, the returned signal will forward that - /// error immediately. - public func flatMap(_ strategy: FlattenStrategy, transform: @escaping (Value) -> Signal) -> Signal { - return map(transform).flatten(strategy) - } -} - -extension SignalProtocol where Error == NoError { - /// Maps each event from `signal` to a new signal, then flattens the - /// resulting signals (into a signal of values), according to the - /// semantics of the given strategy. - /// - /// If any of the created signals emit an error, the returned signal - /// will forward that error immediately. - public func flatMap(_ strategy: FlattenStrategy, transform: @escaping (Value) -> SignalProducer) -> Signal { - return map(transform).flatten(strategy) - } - - /// Maps each event from `signal` to a new signal, then flattens the - /// resulting signals (into a signal of values), according to the - /// semantics of the given strategy. - public func flatMap(_ strategy: FlattenStrategy, transform: @escaping (Value) -> SignalProducer) -> Signal { - return map(transform).flatten(strategy) - } - - /// Maps each event from `signal` to a new signal, then flattens the - /// resulting signals (into a signal of values), according to the - /// semantics of the given strategy. - /// - /// If any of the created signals emit an error, the returned signal - /// will forward that error immediately. - public func flatMap(_ strategy: FlattenStrategy, transform: @escaping (Value) -> Signal) -> Signal { - return map(transform).flatten(strategy) - } - - /// Maps each event from `signal` to a new signal, then flattens the - /// resulting signals (into a signal of values), according to the - /// semantics of the given strategy. - public func flatMap(_ strategy: FlattenStrategy, transform: @escaping (Value) -> Signal) -> Signal { - return map(transform).flatten(strategy) - } -} - -extension SignalProducerProtocol { - /// Maps each event from `self` to a new producer, then flattens the - /// resulting producers (into a producer of values), according to the - /// semantics of the given strategy. - /// - /// If `self` or any of the created producers fail, the returned producer - /// will forward that failure immediately. - public func flatMap(_ strategy: FlattenStrategy, transform: @escaping (Value) -> SignalProducer) -> SignalProducer { - return map(transform).flatten(strategy) - } - - /// Maps each event from `self` to a new producer, then flattens the - /// resulting producers (into a producer of values), according to the - /// semantics of the given strategy. - /// - /// If `self` fails, the returned producer will forward that failure - /// immediately. - public func flatMap(_ strategy: FlattenStrategy, transform: @escaping (Value) -> SignalProducer) -> SignalProducer { - return map(transform).flatten(strategy) - } - - /// Maps each event from `self` to a new producer, then flattens the - /// resulting signals (into a producer of values), according to the - /// semantics of the given strategy. - /// - /// If `self` or any of the created signals emit an error, the returned - /// producer will forward that error immediately. - public func flatMap(_ strategy: FlattenStrategy, transform: @escaping (Value) -> Signal) -> SignalProducer { - return map(transform).flatten(strategy) - } - - /// Maps each event from `self` to a new producer, then flattens the - /// resulting signals (into a producer of values), according to the - /// semantics of the given strategy. - /// - /// If `self` emits an error, the returned producer will forward that - /// error immediately. - public func flatMap(_ strategy: FlattenStrategy, transform: @escaping (Value) -> Signal) -> SignalProducer { - return map(transform).flatten(strategy) - } -} - -extension SignalProducerProtocol where Error == NoError { - /// Maps each event from `self` to a new producer, then flattens the - /// resulting producers (into a producer of values), according to the - /// semantics of the given strategy. - /// - /// If any of the created producers fail, the returned producer will - /// forward that failure immediately. - public func flatMap(_ strategy: FlattenStrategy, transform: @escaping (Value) -> SignalProducer) -> SignalProducer { - return map(transform).flatten(strategy) - } - - /// Maps each event from `self` to a new producer, then flattens the - /// resulting producers (into a producer of values), according to the - /// semantics of the given strategy. - public func flatMap(_ strategy: FlattenStrategy, transform: @escaping (Value) -> SignalProducer) -> SignalProducer { - return map(transform).flatten(strategy) - } - - /// Maps each event from `self` to a new producer, then flattens the - /// resulting signals (into a producer of values), according to the - /// semantics of the given strategy. - /// - /// If any of the created signals emit an error, the returned - /// producer will forward that error immediately. - public func flatMap(_ strategy: FlattenStrategy, transform: @escaping (Value) -> Signal) -> SignalProducer { - return map(transform).flatten(strategy) - } - - /// Maps each event from `self` to a new producer, then flattens the - /// resulting signals (into a producer of values), according to the - /// semantics of the given strategy. - public func flatMap(_ strategy: FlattenStrategy, transform: @escaping (Value) -> Signal) -> SignalProducer { - return map(transform).flatten(strategy) - } -} - - -extension SignalProtocol { - /// Catches any failure that may occur on the input signal, mapping to a new - /// producer that starts in its place. - public func flatMapError(_ handler: @escaping (Error) -> SignalProducer) -> Signal { - return Signal { observer in - self.observeFlatMapError(handler, observer, SerialDisposable()) - } - } - - fileprivate func observeFlatMapError(_ handler: @escaping (Error) -> SignalProducer, _ observer: Observer, _ serialDisposable: SerialDisposable) -> Disposable? { - return self.observe { event in - switch event { - case let .next(value): - observer.sendNext(value) - case let .failed(error): - handler(error).startWithSignal { signal, disposable in - serialDisposable.innerDisposable = disposable - signal.observe(observer) - } - case .completed: - observer.sendCompleted() - case .interrupted: - observer.sendInterrupted() - } - } - } -} - -extension SignalProducerProtocol { - /// Catches any failure that may occur on the input producer, mapping to a - /// new producer that starts in its place. - public func flatMapError(_ handler: @escaping (Error) -> SignalProducer) -> SignalProducer { - return SignalProducer { observer, disposable in - let serialDisposable = SerialDisposable() - disposable += serialDisposable - - self.startWithSignal { signal, signalDisposable in - serialDisposable.innerDisposable = signalDisposable - - _ = signal.observeFlatMapError(handler, observer, serialDisposable) - } - } - } -} diff --git a/ReactiveSwift/FoundationExtensions.swift b/ReactiveSwift/FoundationExtensions.swift deleted file mode 100644 index 9a6d8bbf52..0000000000 --- a/ReactiveSwift/FoundationExtensions.swift +++ /dev/null @@ -1,80 +0,0 @@ -// -// FoundationExtensions.swift -// ReactiveSwift -// -// Created by Justin Spahr-Summers on 2014-10-19. -// Copyright (c) 2014 GitHub. All rights reserved. -// - -import Foundation -import enum Result.NoError - -extension NotificationCenter { - /// Returns a SignalProducer to observe posting of the specified - /// notification. - /// - /// - parameters: - /// - name: name of the notification to observe - /// - object: an instance which sends the notifications - /// - /// - returns: A SignalProducer of notifications posted that match the given - /// criteria. - /// - /// - note: If the `object` is deallocated before starting the producer, it - /// will terminate immediately with an `interrupted` event. - /// Otherwise, the producer will not terminate naturally, so it must - /// be explicitly disposed to avoid leaks. - public func rac_notifications(forName name: Notification.Name?, object: AnyObject? = nil) -> SignalProducer { - // We're weakly capturing an optional reference here, which makes destructuring awkward. - let objectWasNil = (object == nil) - return SignalProducer { [weak object] observer, disposable in - guard object != nil || objectWasNil else { - observer.sendInterrupted() - return - } - - let notificationObserver = self.addObserver(forName: name, object: object, queue: nil) { notification in - observer.sendNext(notification) - } - - disposable += { - self.removeObserver(notificationObserver) - } - } - } -} - -private let defaultSessionError = NSError(domain: "org.reactivecocoa.ReactiveSwift.rac_dataWithRequest", code: 1, userInfo: nil) - -extension URLSession { - /// Returns a SignalProducer which performs the work associated with an - /// `NSURLSession` - /// - /// - parameters: - /// - request: A request that will be performed when the producer is - /// started - /// - /// - returns: A producer that will execute the given request once for each - /// invocation of `start()`. - /// - /// - note: This method will not send an error event in the case of a server - /// side error (i.e. when a response with status code other than - /// 200...299 is received). - public func rac_data(with request: URLRequest) -> SignalProducer<(Data, URLResponse), NSError> { - return SignalProducer { observer, disposable in - let task = self.dataTask(with: request) { data, response, error in - if let data = data, let response = response { - observer.sendNext((data, response)) - observer.sendCompleted() - } else { - observer.sendFailed(error as NSError? ?? defaultSessionError) - } - } - - disposable += { - task.cancel() - } - task.resume() - } - } -} diff --git a/ReactiveSwift/Info.plist b/ReactiveSwift/Info.plist deleted file mode 100644 index 9a11afdc43..0000000000 --- a/ReactiveSwift/Info.plist +++ /dev/null @@ -1,28 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - NSHumanReadableCopyright - Copyright © 2014 GitHub. All rights reserved. - NSPrincipalClass - - - diff --git a/ReactiveSwift/Lifetime.swift b/ReactiveSwift/Lifetime.swift deleted file mode 100644 index d4567f4d10..0000000000 --- a/ReactiveSwift/Lifetime.swift +++ /dev/null @@ -1,69 +0,0 @@ -import Foundation -import enum Result.NoError - -/// Represents the lifetime of an object, and provides a hook to observe when -/// the object deinitializes. -public final class Lifetime { - /// MARK: Type properties and methods - - /// A `Lifetime` that has already ended. - public static var empty: Lifetime { - return Lifetime(ended: .empty) - } - - /// MARK: Instance properties - - /// A signal that sends a `completed` event when the lifetime ends. - public let ended: Signal<(), NoError> - - /// MARK: Initializers - - /// Initialize a `Lifetime` object with the supplied ended signal. - /// - /// - parameters: - /// - signal: The ended signal. - private init(ended signal: Signal<(), NoError>) { - ended = signal - } - - /// Initialize a `Lifetime` from a lifetime token, which is expected to be - /// associated with an object. - /// - /// - important: The resulting lifetime object does not retain the lifetime - /// token. - /// - /// - parameters: - /// - token: A lifetime token for detecting the deinitialization of the - /// associated object. - public convenience init(_ token: Token) { - self.init(ended: token.ended) - } - - /// A token object which completes its signal when it deinitializes. - /// - /// It is generally used in conjuncion with `Lifetime` as a private - /// deinitialization trigger. - /// - /// ``` - /// class MyController { - /// private let token = Lifetime.Token() - /// public var lifetime: Lifetime { - /// return Lifetime(token) - /// } - /// } - /// ``` - public final class Token { - /// A signal that sends a Completed event when the lifetime ends. - fileprivate let ended: Signal<(), NoError> - - private let endedObserver: Signal<(), NoError>.Observer - - public init() { - (ended, endedObserver) = Signal.pipe() - } - - deinit { - endedObserver.sendCompleted() - } - } -} diff --git a/ReactiveSwift/Observer.swift b/ReactiveSwift/Observer.swift deleted file mode 100644 index 9bc1ada275..0000000000 --- a/ReactiveSwift/Observer.swift +++ /dev/null @@ -1,104 +0,0 @@ -// -// Observer.swift -// ReactiveSwift -// -// Created by Andy Matuschak on 10/2/15. -// Copyright © 2015 GitHub. All rights reserved. -// - -/// A protocol for type-constrained extensions of `Observer`. -public protocol ObserverProtocol { - associatedtype Value - associatedtype Error: Swift.Error - - /// Puts a `next` event into `self`. - func sendNext(_ value: Value) - - /// Puts a failed event into `self`. - func sendFailed(_ error: Error) - - /// Puts a `completed` event into `self`. - func sendCompleted() - - /// Puts an `interrupted` event into `self`. - func sendInterrupted() -} - -/// An Observer is a simple wrapper around a function which can receive Events -/// (typically from a Signal). -public final class Observer { - public typealias Action = (Event) -> Void - - /// An action that will be performed upon arrival of the event. - public let action: Action - - /// An initializer that accepts a closure accepting an event for the - /// observer. - /// - /// - parameters: - /// - action: A closure to lift over received event. - public init(_ action: @escaping Action) { - self.action = action - } - - /// An initializer that accepts closures for different event types. - /// - /// - parameters: - /// - next: Optional closure executed when a `next` event is observed. - /// - failed: Optional closure that accepts an `Error` parameter when a - /// failed event is observed. - /// - completed: Optional closure executed when a `completed` event is - /// observed. - /// - interruped: Optional closure executed when an `interrupted` event is - /// observed. - public convenience init( - next: ((Value) -> Void)? = nil, - failed: ((Error) -> Void)? = nil, - completed: (() -> Void)? = nil, - interrupted: (() -> Void)? = nil - ) { - self.init { event in - switch event { - case let .next(value): - next?(value) - - case let .failed(error): - failed?(error) - - case .completed: - completed?() - - case .interrupted: - interrupted?() - } - } - } -} - -extension Observer: ObserverProtocol { - /// Puts a `next` event into `self`. - /// - /// - parameters: - /// - value: A value sent with the `next` event. - public func sendNext(_ value: Value) { - action(.next(value)) - } - - /// Puts a failed event into `self`. - /// - /// - parameters: - /// - error: An error object sent with failed event. - public func sendFailed(_ error: Error) { - action(.failed(error)) - } - - /// Puts a `completed` event into `self`. - public func sendCompleted() { - action(.completed) - } - - /// Puts an `interrupted` event into `self`. - public func sendInterrupted() { - action(.interrupted) - } -} diff --git a/ReactiveSwift/Optional.swift b/ReactiveSwift/Optional.swift deleted file mode 100644 index c5446bd733..0000000000 --- a/ReactiveSwift/Optional.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// Optional.swift -// ReactiveSwift -// -// Created by Neil Pankey on 6/24/15. -// Copyright (c) 2015 GitHub. All rights reserved. -// - -/// An optional protocol for use in type constraints. -public protocol OptionalProtocol { - /// The type contained in the otpional. - associatedtype Wrapped - - init(reconstructing value: Wrapped?) - - /// Extracts an optional from the receiver. - var optional: Wrapped? { get } -} - -extension Optional: OptionalProtocol { - public var optional: Wrapped? { - return self - } - - public init(reconstructing value: Wrapped?) { - self = value - } -} - -extension SignalProtocol { - /// Turns each value into an Optional. - internal func optionalize() -> Signal { - return map(Optional.init) - } -} - -extension SignalProducerProtocol { - /// Turns each value into an Optional. - internal func optionalize() -> SignalProducer { - return lift { $0.optionalize() } - } -} diff --git a/ReactiveSwift/Property.swift b/ReactiveSwift/Property.swift deleted file mode 100644 index ea1badf9b8..0000000000 --- a/ReactiveSwift/Property.swift +++ /dev/null @@ -1,643 +0,0 @@ -import Foundation -import enum Result.NoError - -/// Represents a property that allows observation of its changes. -/// -/// Only classes can conform to this protocol, because having a signal -/// for changes over time implies the origin must have a unique identity. -public protocol PropertyProtocol: class { - associatedtype Value - - /// The current value of the property. - var value: Value { get } - - /// 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. - var producer: SignalProducer { get } - - /// A signal that will send the property's changes over time. It - /// completes when the property has deinitialized, or has no further - /// change. - var signal: Signal { get } -} - -/// Represents an observable property that can be mutated directly. -public protocol MutablePropertyProtocol: PropertyProtocol, BindingTarget { - /// The current value of the property. - var value: Value { get set } -} - -/// Default implementation of `MutablePropertyProtocol` for `BindingTarget`. -extension MutablePropertyProtocol { - public func consume(_ value: Value) { - self.value = value - } -} - -/// Protocol composition operators -/// -/// The producer and the signal of transformed properties would complete -/// only when its source properties have deinitialized. -/// -/// A composed property would retain its ultimate source, but not -/// any intermediate property during the composition. -extension PropertyProtocol { - /// Lifts a unary SignalProducer operator to operate upon PropertyProtocol instead. - fileprivate func lift(_ transform: @escaping (SignalProducer) -> SignalProducer) -> Property { - return Property(self, transform: transform) - } - - /// Lifts a binary SignalProducer operator to operate upon PropertyProtocol instead. - fileprivate func lift(_ transform: @escaping (SignalProducer) -> (SignalProducer) -> SignalProducer) -> (P) -> Property { - return { otherProperty in - return Property(self, otherProperty, transform: transform) - } - } - - /// Maps the current value and all subsequent values to a new property. - /// - /// - parameters: - /// - transform: A closure that will map the current `value` of this - /// `Property` to a new value. - /// - /// - returns: A new instance of `AnyProperty` who's holds a mapped value - /// from `self`. - public func map(_ transform: @escaping (Value) -> U) -> Property { - return lift { $0.map(transform) } - } - - /// Combines the current value and the subsequent values of two `Property`s in - /// the manner described by `Signal.combineLatestWith:`. - /// - /// - parameters: - /// - other: A property to combine `self`'s value with. - /// - /// - returns: A property that holds a tuple containing values of `self` and - /// the given property. - public func combineLatest(with other: P) -> Property<(Value, P.Value)> { - return lift(SignalProducer.combineLatest(with:))(other) - } - - /// Zips the current value and the subsequent values of two `Property`s in - /// the manner described by `Signal.zipWith`. - /// - /// - parameters: - /// - other: A property to zip `self`'s value with. - /// - /// - returns: A property that holds a tuple containing values of `self` and - /// the given property. - public func zip(with other: P) -> Property<(Value, P.Value)> { - return lift(SignalProducer.zip(with:))(other) - } - - /// Forward events from `self` with history: values of the returned property - /// are a tuple whose first member is the previous value and whose second - /// member is the current value. `initial` is supplied as the first member - /// when `self` sends its first value. - /// - /// - parameters: - /// - initial: A value that will be combined with the first value sent by - /// `self`. - /// - /// - returns: A property that holds tuples that contain previous and - /// current values of `self`. - public func combinePrevious(_ initial: Value) -> Property<(Value, Value)> { - return lift { $0.combinePrevious(initial) } - } - - /// Forward only those values from `self` which do not pass `isRepeat` with - /// respect to the previous value. - /// - /// - parameters: - /// - isRepeat: A predicate to determine if the two given values are equal. - /// - /// - returns: A property that does not emit events for two equal values - /// sequentially. - public func skipRepeats(_ isRepeat: @escaping (Value, Value) -> Bool) -> Property { - return lift { $0.skipRepeats(isRepeat) } - } -} - -extension PropertyProtocol where Value: Equatable { - /// Forward only those values from `self` which do not pass `isRepeat` with - /// respect to the previous value. - /// - /// - returns: A property that does not emit events for two equal values - /// sequentially. - public func skipRepeats() -> Property { - return lift { $0.skipRepeats() } - } -} - -extension PropertyProtocol where Value: PropertyProtocol { - /// Flattens the inner property held by `self` (into a single property of - /// values), according to the semantics of the given strategy. - /// - /// - parameters: - /// - strategy: The preferred flatten strategy. - /// - /// - returns: A property that sends the values of its inner properties. - public func flatten(_ strategy: FlattenStrategy) -> Property { - return lift { $0.flatMap(strategy) { $0.producer } } - } -} - -extension PropertyProtocol { - /// Maps each property from `self` to a new property, then flattens the - /// resulting properties (into a single property), according to the - /// semantics of the given strategy. - /// - /// - parameters: - /// - strategy: The preferred flatten strategy. - /// - transform: The transform to be applied on `self` before flattening. - /// - /// - returns: A property that sends the values of its inner properties. - public func flatMap(_ strategy: FlattenStrategy, transform: @escaping (Value) -> P) -> Property { - return lift { $0.flatMap(strategy) { transform($0).producer } } - } - - /// Forward only those values from `self` that have unique identities across - /// the set of all values that have been held. - /// - /// - note: This causes the identities to be retained to check for - /// uniqueness. - /// - /// - parameters: - /// - transform: A closure that accepts a value and returns identity - /// value. - /// - /// - returns: A property that sends unique values during its lifetime. - public func uniqueValues(_ transform: @escaping (Value) -> Identity) -> Property { - return lift { $0.uniqueValues(transform) } - } -} - -extension PropertyProtocol where Value: Hashable { - /// Forwards only those values from `self` that are unique across the set of - /// all values that have been seen. - /// - /// - note: This causes the identities to be retained to check for uniqueness. - /// Providing a function that returns a unique value for each sent - /// value can help you reduce the memory footprint. - /// - /// - returns: A property that sends unique values during its lifetime. - public func uniqueValues() -> Property { - return lift { $0.uniqueValues() } - } -} - -extension PropertyProtocol { - /// Combines the values of all the given properties, in the manner described - /// by `combineLatest(with:)`. - public static func combineLatest(_ a: A, _ b: B) -> Property<(A.Value, B.Value)> where Value == A.Value { - return a.combineLatest(with: b) - } - - /// Combines the values of all the given properties, in the manner described - /// by `combineLatest(with:)`. - public static func combineLatest(_ a: A, _ b: B, _ c: C) -> Property<(A.Value, B.Value, C.Value)> where Value == A.Value { - return combineLatest(a, b) - .combineLatest(with: c) - .map(repack) - } - - /// Combines the values of all the given properties, in the manner described - /// by `combineLatest(with:)`. - public static func combineLatest(_ a: A, _ b: B, _ c: C, _ d: D) -> Property<(A.Value, B.Value, C.Value, D.Value)> where Value == A.Value { - return combineLatest(a, b, c) - .combineLatest(with: d) - .map(repack) - } - - /// Combines the values of all the given properties, in the manner described - /// by `combineLatest(with:)`. - public static func combineLatest(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value)> where Value == A.Value { - return combineLatest(a, b, c, d) - .combineLatest(with: e) - .map(repack) - } - - /// Combines the values of all the given properties, in the manner described - /// by `combineLatest(with:)`. - public static func combineLatest(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value)> where Value == A.Value { - return combineLatest(a, b, c, d, e) - .combineLatest(with: f) - .map(repack) - } - - /// Combines the values of all the given properties, in the manner described - /// by `combineLatest(with:)`. - public static func combineLatest(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value)> where Value == A.Value { - return combineLatest(a, b, c, d, e, f) - .combineLatest(with: g) - .map(repack) - } - - /// Combines the values of all the given properties, in the manner described - /// by `combineLatest(with:)`. - public static func combineLatest(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G, _ h: H) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value, H.Value)> where Value == A.Value { - return combineLatest(a, b, c, d, e, f, g) - .combineLatest(with: h) - .map(repack) - } - - /// Combines the values of all the given properties, in the manner described - /// by `combineLatest(with:)`. - public static func combineLatest(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G, _ h: H, _ i: I) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value, H.Value, I.Value)> where Value == A.Value { - return combineLatest(a, b, c, d, e, f, g, h) - .combineLatest(with: i) - .map(repack) - } - - /// Combines the values of all the given properties, in the manner described - /// by `combineLatest(with:)`. - public static func combineLatest(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G, _ h: H, _ i: I, _ j: J) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value, H.Value, I.Value, J.Value)> where Value == A.Value { - return combineLatest(a, b, c, d, e, f, g, h, i) - .combineLatest(with: j) - .map(repack) - } - - /// Combines the values of all the given producers, in the manner described by - /// `combineLatest(with:)`. Returns nil if the sequence is empty. - public static func combineLatest(_ properties: S) -> Property<[S.Iterator.Element.Value]>? where S.Iterator.Element: PropertyProtocol { - var generator = properties.makeIterator() - if let first = generator.next() { - let initial = first.map { [$0] } - return IteratorSequence(generator).reduce(initial) { property, next in - property.combineLatest(with: next).map { $0.0 + [$0.1] } - } - } - - return nil - } - - /// Zips the values of all the given properties, in the manner described by - /// `zip(with:)`. - public static func zip(_ a: A, _ b: B) -> Property<(A.Value, B.Value)> where Value == A.Value { - return a.zip(with: b) - } - - /// Zips the values of all the given properties, in the manner described by - /// `zip(with:)`. - public static func zip(_ a: A, _ b: B, _ c: C) -> Property<(A.Value, B.Value, C.Value)> where Value == A.Value { - return zip(a, b) - .zip(with: c) - .map(repack) - } - - /// Zips the values of all the given properties, in the manner described by - /// `zip(with:)`. - public static func zip(_ a: A, _ b: B, _ c: C, _ d: D) -> Property<(A.Value, B.Value, C.Value, D.Value)> where Value == A.Value { - return zip(a, b, c) - .zip(with: d) - .map(repack) - } - - /// Zips the values of all the given properties, in the manner described by - /// `zip(with:)`. - public static func zip(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value)> where Value == A.Value { - return zip(a, b, c, d) - .zip(with: e) - .map(repack) - } - - /// Zips the values of all the given properties, in the manner described by - /// `zip(with:)`. - public static func zip(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value)> where Value == A.Value { - return zip(a, b, c, d, e) - .zip(with: f) - .map(repack) - } - - /// Zips the values of all the given properties, in the manner described by - /// `zip(with:)`. - public static func zip(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value)> where Value == A.Value { - return zip(a, b, c, d, e, f) - .zip(with: g) - .map(repack) - } - - /// Zips the values of all the given properties, in the manner described by - /// `zip(with:)`. - public static func zip(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G, _ h: H) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value, H.Value)> where Value == A.Value { - return zip(a, b, c, d, e, f, g) - .zip(with: h) - .map(repack) - } - - /// Zips the values of all the given properties, in the manner described by - /// `zip(with:)`. - public static func zip(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G, _ h: H, _ i: I) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value, H.Value, I.Value)> where Value == A.Value { - return zip(a, b, c, d, e, f, g, h) - .zip(with: i) - .map(repack) - } - - /// Zips the values of all the given properties, in the manner described by - /// `zip(with:)`. - public static func zip(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G, _ h: H, _ i: I, _ j: J) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value, H.Value, I.Value, J.Value)> where Value == A.Value { - return zip(a, b, c, d, e, f, g, h, i) - .zip(with: j) - .map(repack) - } - - /// Zips the values of all the given properties, in the manner described by - /// `zip(with:)`. Returns nil if the sequence is empty. - public static func zip(_ properties: S) -> Property<[S.Iterator.Element.Value]>? where S.Iterator.Element: PropertyProtocol { - var generator = properties.makeIterator() - if let first = generator.next() { - let initial = first.map { [$0] } - return IteratorSequence(generator).reduce(initial) { property, next in - property.zip(with: next).map { $0.0 + [$0.1] } - } - } - - return nil - } -} - -/// A read-only property that can be observed for its changes over time. There are -/// three categories of read-only property: -/// -/// # Constant property -/// Created by `Property(value:)`, the producer and signal of a constant -/// property would complete immediately when it is initialized. -/// -/// # Existential property -/// Created by `Property(_:)`, an existential property passes through the -/// behavior of the wrapped property. -/// -/// # Composed property -/// Created by either the compositional operators in `PropertyProtocol`, or -/// `Property(initial:followingBy:)`, a composed property presents a -/// composed view of its source, which can be a set of properties, -/// a producer, or a signal. -/// -/// A composed property respects the lifetime of its source rather than its own. -/// In other words, its producer and signal can outlive the property itself, if -/// its source outlives it too. -public final class Property: PropertyProtocol { - private let sources: [AnyObject] - private let disposable: Disposable? - - private let _value: () -> Value - private let _producer: () -> SignalProducer - private let _signal: () -> Signal - - /// The current value of the property. - public var value: Value { - return _value() - } - - /// A producer for Signals that will send the property's current - /// value, followed by all changes over time, then complete when the - /// property has deinitialized or has no further changes. - public var producer: SignalProducer { - return _producer() - } - - /// A signal that will send the property's changes over time, then - /// complete when the property has deinitialized or has no further changes. - public var signal: Signal { - return _signal() - } - - /// Initializes a constant property. - /// - /// - parameters: - /// - property: A value of the constant property. - public init(value: Value) { - sources = [] - disposable = nil - _value = { value } - _producer = { SignalProducer(value: value) } - _signal = { Signal.empty } - } - - /// Initializes an existential property which wraps the given property. - /// - /// - parameters: - /// - property: A property to be wrapped. - public init(_ property: P) where P.Value == Value { - sources = Property.capture(property) - disposable = nil - _value = { property.value } - _producer = { property.producer } - _signal = { property.signal } - } - - /// Initializes a composed property that first takes on `initial`, then each - /// value sent on a signal created by `producer`. - /// - /// - parameters: - /// - initial: Starting value for the property. - /// - producer: A producer that will start immediately and send values to - /// the property. - public convenience init(initial: Value, then producer: SignalProducer) { - self.init(unsafeProducer: producer.prefix(value: initial), - capturing: []) - } - - /// Initialize a composed property that first takes on `initial`, then each - /// value sent on `signal`. - /// - /// - parameters: - /// - initialValue: Starting value for the property. - /// - signal: A signal that will send values to the property. - public convenience init(initial: Value, then signal: Signal) { - self.init(unsafeProducer: SignalProducer(signal: signal).prefix(value: initial), - capturing: []) - } - - /// Initialize a composed property by applying the unary `SignalProducer` - /// transform on `property`. - /// - /// - parameters: - /// - property: The source property. - /// - transform: A unary `SignalProducer` transform to be applied on - /// `property`. - fileprivate convenience init( - _ property: P, - transform: @escaping (SignalProducer) -> SignalProducer - ) { - self.init( - unsafeProducer: transform(property.producer), - capturing: Property.capture(property) - ) - } - - /// Initialize a composed property by applying the binary `SignalProducer` - /// transform on `firstProperty` and `secondProperty`. - /// - /// - parameters: - /// - firstProperty: The first source property. - /// - secondProperty: The first source property. - /// - transform: A binary `SignalProducer` transform to be applied on - /// `firstProperty` and `secondProperty`. - fileprivate convenience init(_ firstProperty: P1, _ secondProperty: P2, transform: @escaping (SignalProducer) -> (SignalProducer) -> SignalProducer) { - self.init(unsafeProducer: transform(firstProperty.producer)(secondProperty.producer), - capturing: Property.capture(firstProperty) + Property.capture(secondProperty)) - } - - /// Initialize a composed property from a producer that promises to send - /// at least one value synchronously in its start handler before sending any - /// subsequent event. - /// - /// - important: The producer and the signal of the created property would - /// complete only when the `unsafeProducer` completes. - /// - /// - warning: If the producer fails its promise, a fatal error would be - /// raised. - /// - /// - parameters: - /// - unsafeProducer: The composed producer for creating the property. - /// - sources: The property sources to be captured. - private init(unsafeProducer: SignalProducer, capturing sources: [AnyObject]) { - // Share a replayed producer with `self.producer` and `self.signal` so - // they see a consistent view of the `self.value`. - // https://github.com/ReactiveCocoa/ReactiveCocoa/pull/3042 - let producer = unsafeProducer.replayLazily(upTo: 1) - - let atomic = Atomic(nil) - disposable = producer.startWithNext { atomic.value = $0 } - - // Verify that an initial is sent. This is friendlier than deadlocking - // in the event that one isn't. - guard atomic.value != nil else { - fatalError("A producer promised to send at least one value. Received none.") - } - - self.sources = sources - _value = { atomic.value! } - _producer = { producer } - _signal = { - var extractedSignal: Signal! - producer.startWithSignal { signal, _ in extractedSignal = signal } - return extractedSignal - } - } - - deinit { - disposable?.dispose() - } - - /// Inspect if `property` is an `AnyProperty` and has already captured its - /// sources using a closure. Returns that closure if it does. Otherwise, - /// returns a closure which captures `property`. - /// - /// - parameters: - /// - property: The property to be insepcted. - private static func capture(_ property: P) -> [AnyObject] { - if let property = property as? Property { - return property.sources - } else { - return [property] - } - } -} - -/// A mutable property of type `Value` that allows observation of its changes. -/// -/// Instances of this class are thread-safe. -public final class MutableProperty: MutablePropertyProtocol { - private let token: Lifetime.Token - private let observer: Signal.Observer - private let atomic: RecursiveAtomic - - /// The current value of the property. - /// - /// Setting this to a new value will notify all observers of `signal`, or - /// signals created using `producer`. - public var value: Value { - get { - return atomic.withValue { $0 } - } - - set { - swap(newValue) - } - } - - /// The lifetime of the property. - public let lifetime: Lifetime - - /// A signal that will send the property's changes over time, - /// then complete when the property has deinitialized. - public let signal: Signal - - /// A producer for Signals that will send the property's current value, - /// followed by all changes over time, then complete when the property has - /// deinitialized. - public var producer: SignalProducer { - return SignalProducer { [atomic, weak self] producerObserver, producerDisposable in - atomic.withValue { value in - if let strongSelf = self { - producerObserver.sendNext(value) - producerDisposable += strongSelf.signal.observe(producerObserver) - } else { - producerObserver.sendNext(value) - producerObserver.sendCompleted() - } - } - } - } - - /// Initializes a mutable property that first takes on `initialValue` - /// - /// - parameters: - /// - initialValue: Starting value for the mutable property. - public init(_ initialValue: Value) { - (signal, observer) = Signal.pipe() - token = Lifetime.Token() - lifetime = Lifetime(token) - - /// Need a recursive lock around `value` to allow recursive access to - /// `value`. Note that recursive sets will still deadlock because the - /// underlying producer prevents sending recursive events. - atomic = RecursiveAtomic(initialValue, - name: "org.reactivecocoa.ReactiveSwift.MutableProperty", - didSet: observer.sendNext) - } - - /// Atomically replaces the contents of the variable. - /// - /// - parameters: - /// - newValue: New property value. - /// - /// - returns: The previous property value. - @discardableResult - public func swap(_ newValue: Value) -> Value { - return atomic.swap(newValue) - } - - /// Atomically modifies the variable. - /// - /// - parameters: - /// - action: A closure that accepts old property value and returns a new - /// property value. - /// - /// - returns: The result of the action. - @discardableResult - public func modify(_ action: (inout Value) throws -> Result) rethrows -> Result { - return try atomic.modify(action) - } - - /// Atomically performs an arbitrary action using the current value of the - /// variable. - /// - /// - parameters: - /// - action: A closure that accepts current property value. - /// - /// - returns: the result of the action. - @discardableResult - public func withValue(action: (Value) throws -> Result) rethrows -> Result { - return try atomic.withValue(action) - } - - deinit { - observer.sendCompleted() - } -} diff --git a/ReactiveSwift/ReactiveSwift.h b/ReactiveSwift/ReactiveSwift.h deleted file mode 100644 index dde845ccc0..0000000000 --- a/ReactiveSwift/ReactiveSwift.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// ReactiveSwift.h -// ReactiveSwift -// -// Created by Matt Diephouse on 8/15/16. -// Copyright (c) 2016 the ReactiveSwift contributors. All rights reserved. -// - -#import - -//! Project version number for ReactiveSwift. -FOUNDATION_EXPORT double ReactiveSwiftVersionNumber; - -//! Project version string for ReactiveSwift. -FOUNDATION_EXPORT const unsigned char ReactiveSwiftVersionString[]; diff --git a/ReactiveSwift/Scheduler.swift b/ReactiveSwift/Scheduler.swift deleted file mode 100644 index 1045fa21b9..0000000000 --- a/ReactiveSwift/Scheduler.swift +++ /dev/null @@ -1,493 +0,0 @@ -// -// Scheduler.swift -// ReactiveSwift -// -// Created by Justin Spahr-Summers on 2014-06-02. -// Copyright (c) 2014 GitHub. All rights reserved. -// - -import Foundation - -/// Represents a serial queue of work items. -public protocol SchedulerProtocol { - /// Enqueues an action on the scheduler. - /// - /// When the work is executed depends on the scheduler in use. - /// - /// - returns: Optional `Disposable` that can be used to cancel the work - /// before it begins. - @discardableResult - func schedule(_ action: @escaping () -> Void) -> Disposable? -} - -/// A particular kind of scheduler that supports enqueuing actions at future -/// dates. -public protocol DateSchedulerProtocol: SchedulerProtocol { - /// The current date, as determined by this scheduler. - /// - /// This can be implemented to deterministically return a known date (e.g., - /// for testing purposes). - var currentDate: Date { get } - - /// Schedules an action for execution at or after the given date. - /// - /// - parameters: - /// - date: Starting time. - /// - action: Closure of the action to perform. - /// - /// - returns: Optional `Disposable` that can be used to cancel the work - /// before it begins. - @discardableResult - func schedule(after date: Date, action: @escaping () -> Void) -> Disposable? - - /// Schedules a recurring action at the given interval, beginning at the - /// given date. - /// - /// - parameters: - /// - date: Starting time. - /// - repeatingEvery: Repetition interval. - /// - withLeeway: Some delta for repetition. - /// - action: Closure of the action to perform. - /// - /// - returns: Optional `Disposable` that can be used to cancel the work - /// before it begins. - @discardableResult - func schedule(after date: Date, interval: TimeInterval, leeway: TimeInterval, action: @escaping () -> Void) -> Disposable? -} - -/// A scheduler that performs all work synchronously. -public final class ImmediateScheduler: SchedulerProtocol { - public init() {} - - /// Immediately calls passed in `action`. - /// - /// - parameters: - /// - action: Closure of the action to perform. - /// - /// - returns: `nil`. - @discardableResult - public func schedule(_ action: @escaping () -> Void) -> Disposable? { - action() - return nil - } -} - -/// A scheduler that performs all work on the main queue, as soon as possible. -/// -/// If the caller is already running on the main queue when an action is -/// scheduled, it may be run synchronously. However, ordering between actions -/// will always be preserved. -public final class UIScheduler: SchedulerProtocol { - private static let dispatchSpecificKey = DispatchSpecificKey() - private static let dispatchSpecificValue = UInt8.max - private static var __once: () = { - DispatchQueue.main.setSpecific(key: UIScheduler.dispatchSpecificKey, - value: dispatchSpecificValue) - }() - - private var queueLength: Int32 = 0 - - /// Initializes `UIScheduler` - public init() { - /// This call is to ensure the main queue has been setup appropriately - /// for `UIScheduler`. It is only called once during the application - /// lifetime, since Swift has a `dispatch_once` like mechanism to - /// lazily initialize global variables and static variables. - _ = UIScheduler.__once - } - - /// Queues an action to be performed on main queue. If the action is called - /// on the main thread and no work is queued, no scheduling takes place and - /// the action is called instantly. - /// - /// - parameters: - /// - action: Closure of the action to perform on the main thread. - /// - /// - returns: `Disposable` that can be used to cancel the work before it - /// begins. - @discardableResult - public func schedule(_ action: @escaping () -> Void) -> Disposable? { - let disposable = SimpleDisposable() - let actionAndDecrement = { - if !disposable.isDisposed { - action() - } - - OSAtomicDecrement32(&self.queueLength) - } - - let queued = OSAtomicIncrement32(&queueLength) - - // If we're already running on the main queue, and there isn't work - // already enqueued, we can skip scheduling and just execute directly. - if queued == 1 && DispatchQueue.getSpecific(key: UIScheduler.dispatchSpecificKey) == UIScheduler.dispatchSpecificValue { - actionAndDecrement() - } else { - DispatchQueue.main.async(execute: actionAndDecrement) - } - - return disposable - } -} - -/// A scheduler backed by a serial GCD queue. -public final class QueueScheduler: DateSchedulerProtocol { - /// A singleton `QueueScheduler` that always targets the main thread's GCD - /// queue. - /// - /// - note: Unlike `UIScheduler`, this scheduler supports scheduling for a - /// future date, and will always schedule asynchronously (even if - /// already running on the main thread). - public static let main = QueueScheduler(internalQueue: DispatchQueue.main) - - public var currentDate: Date { - return Date() - } - - public let queue: DispatchQueue - - internal init(internalQueue: DispatchQueue) { - queue = internalQueue - } - - /// Initializes a scheduler that will target the given queue with its - /// work. - /// - /// - note: Even if the queue is concurrent, all work items enqueued with - /// the `QueueScheduler` will be serial with respect to each other. - /// - /// - warning: Obsoleted in OS X 10.11 - @available(OSX, deprecated:10.10, obsoleted:10.11, message:"Use init(qos:, name:) instead") - @available(iOS, deprecated:8.0, obsoleted:9.0, message:"Use init(qos:, name:) instead.") - public convenience init(queue: DispatchQueue, name: String = "org.reactivecocoa.ReactiveSwift.QueueScheduler") { - self.init(internalQueue: DispatchQueue(label: name, attributes: [], target: queue)) - } - - /// Initializes a scheduler that will target a new serial queue with the - /// given quality of service class. - /// - /// - parameters: - /// - qos: Dispatch queue's QoS value. - /// - name: Name for the queue in the form of reverse domain. - @available(OSX 10.10, *) - public convenience init( - qos: DispatchQoS = .default, - name: String = "org.reactivecocoa.ReactiveSwift.QueueScheduler" - ) { - self.init(internalQueue: DispatchQueue( - label: name, - qos: qos - )) - } - - /// Schedules action for dispatch on internal queue - /// - /// - parameters: - /// - action: Closure of the action to schedule. - /// - /// - returns: `Disposable` that can be used to cancel the work before it - /// begins. - @discardableResult - public func schedule(_ action: @escaping () -> Void) -> Disposable? { - let d = SimpleDisposable() - - queue.async { - if !d.isDisposed { - action() - } - } - - return d - } - - private func wallTime(with date: Date) -> DispatchWallTime { - let (seconds, frac) = modf(date.timeIntervalSince1970) - - let nsec: Double = frac * Double(NSEC_PER_SEC) - let walltime = timespec(tv_sec: Int(seconds), tv_nsec: Int(nsec)) - - return DispatchWallTime(timespec: walltime) - } - - /// Schedules an action for execution at or after the given date. - /// - /// - parameters: - /// - date: Starting time. - /// - action: Closure of the action to perform. - /// - /// - returns: Optional `Disposable` that can be used to cancel the work - /// before it begins. - @discardableResult - public func schedule(after date: Date, action: @escaping () -> Void) -> Disposable? { - let d = SimpleDisposable() - - queue.asyncAfter(wallDeadline: wallTime(with: date)) { - if !d.isDisposed { - action() - } - } - - return d - } - - /// Schedules a recurring action at the given interval and beginning at the - /// given start time. A reasonable default timer interval leeway is - /// provided. - /// - /// - parameters: - /// - date: Date to schedule the first action for. - /// - repeatingEvery: Repetition interval. - /// - action: Closure of the action to repeat. - /// - /// - returns: Optional disposable that can be used to cancel the work - /// before it begins. - @discardableResult - public func schedule(after date: Date, interval: TimeInterval, action: @escaping () -> Void) -> Disposable? { - // Apple's "Power Efficiency Guide for Mac Apps" recommends a leeway of - // at least 10% of the timer interval. - return schedule(after: date, interval: interval, leeway: interval * 0.1, action: action) - } - - /// Schedules a recurring action at the given interval with provided leeway, - /// beginning at the given start time. - /// - /// - parameters: - /// - date: Date to schedule the first action for. - /// - repeatingEvery: Repetition interval. - /// - leeway: Some delta for repetition interval. - /// - action: Closure of the action to repeat. - /// - /// - returns: Optional `Disposable` that can be used to cancel the work - /// before it begins. - @discardableResult - public func schedule(after date: Date, interval: TimeInterval, leeway: TimeInterval, action: @escaping () -> Void) -> Disposable? { - precondition(interval >= 0) - precondition(leeway >= 0) - - let nsecInterval = interval * Double(NSEC_PER_SEC) - let nsecLeeway = leeway * Double(NSEC_PER_SEC) - - let timer = DispatchSource.makeTimerSource( - flags: DispatchSource.TimerFlags(rawValue: UInt(0)), - queue: queue - ) - timer.scheduleRepeating(wallDeadline: wallTime(with: date), - interval: .nanoseconds(Int(nsecInterval)), - leeway: .nanoseconds(Int(nsecLeeway))) - timer.setEventHandler(handler: action) - timer.resume() - - return ActionDisposable { - timer.cancel() - } - } -} - -/// A scheduler that implements virtualized time, for use in testing. -public final class TestScheduler: DateSchedulerProtocol { - private final class ScheduledAction { - let date: Date - let action: () -> Void - - init(date: Date, action: @escaping () -> Void) { - self.date = date - self.action = action - } - - func less(_ rhs: ScheduledAction) -> Bool { - return date.compare(rhs.date) == .orderedAscending - } - } - - private let lock = NSRecursiveLock() - private var _currentDate: Date - - /// The virtual date that the scheduler is currently at. - public var currentDate: Date { - let d: Date - - lock.lock() - d = _currentDate - lock.unlock() - - return d - } - - private var scheduledActions: [ScheduledAction] = [] - - /// Initializes a TestScheduler with the given start date. - /// - /// - parameters: - /// - startDate: The start date of the scheduler. - public init(startDate: Date = Date(timeIntervalSinceReferenceDate: 0)) { - lock.name = "org.reactivecocoa.ReactiveSwift.TestScheduler" - _currentDate = startDate - } - - private func schedule(_ action: ScheduledAction) -> Disposable { - lock.lock() - scheduledActions.append(action) - scheduledActions.sort { $0.less($1) } - lock.unlock() - - return ActionDisposable { - self.lock.lock() - self.scheduledActions = self.scheduledActions.filter { $0 !== action } - self.lock.unlock() - } - } - - /// Enqueues an action on the scheduler. - /// - /// - note: The work is executed on `currentDate` as it is understood by the - /// scheduler. - /// - /// - parameters: - /// - action: An action that will be performed on scheduler's - /// `currentDate`. - /// - /// - returns: Optional `Disposable` that can be used to cancel the work - /// before it begins. - @discardableResult - public func schedule(_ action: @escaping () -> Void) -> Disposable? { - return schedule(ScheduledAction(date: currentDate, action: action)) - } - - /// Schedules an action for execution at or after the given date. - /// - /// - parameters: - /// - date: Starting date. - /// - action: Closure of the action to perform. - /// - /// - returns: Optional disposable that can be used to cancel the work - /// before it begins. - @discardableResult - public func schedule(after delay: TimeInterval, action: @escaping () -> Void) -> Disposable? { - return schedule(after: currentDate.addingTimeInterval(delay), action: action) - } - - @discardableResult - public func schedule(after date: Date, action: @escaping () -> Void) -> Disposable? { - return schedule(ScheduledAction(date: date, action: action)) - } - - /// Schedules a recurring action at the given interval, beginning at the - /// given start time - /// - /// - parameters: - /// - date: Date to schedule the first action for. - /// - repeatingEvery: Repetition interval. - /// - action: Closure of the action to repeat. - /// - /// - returns: Optional `Disposable` that can be used to cancel the work - /// before it begins. - private func schedule(after date: Date, interval: TimeInterval, disposable: SerialDisposable, action: @escaping () -> Void) { - precondition(interval >= 0) - - disposable.innerDisposable = schedule(after: date) { [unowned self] in - action() - self.schedule(after: date.addingTimeInterval(interval), interval: interval, disposable: disposable, action: action) - } - } - - /// Schedules a recurring action at the given interval, beginning at the - /// given interval (counted from `currentDate`). - /// - /// - parameters: - /// - interval: Interval to add to `currentDate`. - /// - repeatingEvery: Repetition interval. - /// - leeway: Some delta for repetition interval. - /// - action: Closure of the action to repeat. - /// - /// - returns: Optional `Disposable` that can be used to cancel the work - /// before it begins. - @discardableResult - public func schedule(after delay: TimeInterval, interval: TimeInterval, leeway: TimeInterval = 0, action: @escaping () -> Void) -> Disposable? { - return schedule(after: currentDate.addingTimeInterval(delay), interval: interval, leeway: leeway, action: action) - } - - /// Schedules a recurring action at the given interval with - /// provided leeway, beginning at the given start time. - /// - /// - parameters: - /// - date: Date to schedule the first action for. - /// - repeatingEvery: Repetition interval. - /// - leeway: Some delta for repetition interval. - /// - action: Closure of the action to repeat. - /// - /// - returns: Optional `Disposable` that can be used to cancel the work - /// before it begins. - public func schedule(after date: Date, interval: TimeInterval, leeway: TimeInterval = 0, action: @escaping () -> Void) -> Disposable? { - let disposable = SerialDisposable() - schedule(after: date, interval: interval, disposable: disposable, action: action) - return disposable - } - - /// Advances the virtualized clock by an extremely tiny interval, dequeuing - /// and executing any actions along the way. - /// - /// This is intended to be used as a way to execute actions that have been - /// scheduled to run as soon as possible. - public func advance() { - advance(by: DBL_EPSILON) - } - - /// Advances the virtualized clock by the given interval, dequeuing and - /// executing any actions along the way. - /// - /// - parameters: - /// - interval: Interval by which the current date will be advanced. - public func advance(by interval: TimeInterval) { - lock.lock() - advance(to: currentDate.addingTimeInterval(interval)) - lock.unlock() - } - - /// Advances the virtualized clock to the given future date, dequeuing and - /// executing any actions up until that point. - /// - /// - parameters: - /// - newDate: Future date to which the virtual clock will be advanced. - public func advance(to newDate: Date) { - lock.lock() - - assert(currentDate.compare(newDate) != .orderedDescending) - - while scheduledActions.count > 0 { - if newDate.compare(scheduledActions[0].date) == .orderedAscending { - break - } - - _currentDate = scheduledActions[0].date - - let scheduledAction = scheduledActions.remove(at: 0) - scheduledAction.action() - } - - _currentDate = newDate - - lock.unlock() - } - - /// Dequeues and executes all scheduled actions, leaving the scheduler's - /// date at `NSDate.distantFuture()`. - public func run() { - advance(to: Date.distantFuture) - } - - /// Rewinds the virtualized clock by the given interval. - /// This simulates that user changes device date. - /// - /// - parameters: - /// - interval: Interval by which the current date will be retreated. - public func rewind(by interval: TimeInterval) { - lock.lock() - - let newDate = currentDate.addingTimeInterval(-interval) - assert(currentDate.compare(newDate) != .orderedAscending) - _currentDate = newDate - - lock.unlock() - - } -} diff --git a/ReactiveSwift/Signal.swift b/ReactiveSwift/Signal.swift deleted file mode 100644 index 3c16914e49..0000000000 --- a/ReactiveSwift/Signal.swift +++ /dev/null @@ -1,1843 +0,0 @@ -import Foundation -import Result - -/// A push-driven stream that sends Events over time, parameterized by the type -/// of values being sent (`Value`) and the type of failure that can occur -/// (`Error`). If no failures should be possible, NoError can be specified for -/// `Error`. -/// -/// An observer of a Signal will see the exact same sequence of events as all -/// other observers. In other words, events will be sent to all observers at the -/// same time. -/// -/// Signals are generally used to represent event streams that are already “in -/// progress,” like notifications, user input, etc. To represent streams that -/// must first be _started_, see the SignalProducer type. -/// -/// A Signal is kept alive until either of the following happens: -/// 1. its input observer receives a terminating event; or -/// 2. it has no active observers, and is not being retained. -public final class Signal { - public typealias Observer = ReactiveSwift.Observer - - /// The disposable returned by the signal generator. It would be disposed of - /// when the signal terminates. - private var generatorDisposable: Disposable? - - /// The state of the signal. `nil` if the signal has terminated. - private let state: Atomic?> - - /// Initialize a Signal that will immediately invoke the given generator, - /// then forward events sent to the given observer. - /// - /// - note: The disposable returned from the closure will be automatically - /// disposed if a terminating event is sent to the observer. The - /// Signal itself will remain alive until the observer is released. - /// - /// - parameters: - /// - generator: A closure that accepts an implicitly created observer - /// that will act as an event emitter for the signal. - public init(_ generator: (Observer) -> Disposable?) { - state = Atomic(SignalState()) - - /// Used to ensure that events are serialized during delivery to observers. - let sendLock = NSLock() - sendLock.name = "org.reactivecocoa.ReactiveSwift.Signal" - - /// When set to `true`, the Signal should interrupt as soon as possible. - let interrupted = Atomic(false) - - let observer = Observer { [weak self] event in - guard let signal = self else { - return - } - - func interrupt() { - if let state = signal.state.swap(nil) { - for observer in state.observers { - observer.sendInterrupted() - } - } - } - - if case .interrupted = event { - // Normally we disallow recursive events, but `interrupted` is - // kind of a special snowflake, since it can inadvertently be - // sent by downstream consumers. - // - // So we'll flag Interrupted events specially, and if it - // happened to occur while we're sending something else, we'll - // wait to deliver it. - interrupted.value = true - - if sendLock.try() { - interrupt() - sendLock.unlock() - - signal.generatorDisposable?.dispose() - } - } else { - if let state = (event.isTerminating ? signal.state.swap(nil) : signal.state.value) { - sendLock.lock() - - for observer in state.observers { - observer.action(event) - } - - let shouldInterrupt = !event.isTerminating && interrupted.value - if shouldInterrupt { - interrupt() - } - - sendLock.unlock() - - if event.isTerminating || shouldInterrupt { - // Dispose only after notifying observers, so disposal - // logic is consistently the last thing to run. - signal.generatorDisposable?.dispose() - } - } - } - } - - generatorDisposable = generator(observer) - } - - deinit { - if state.swap(nil) != nil { - // As the signal can deinitialize only when it has no observers attached, - // only the generator disposable has to be disposed of at this point. - generatorDisposable?.dispose() - } - } - - /// A Signal that never sends any events to its observers. - public static var never: Signal { - return self.init { _ in nil } - } - - /// A Signal that completes immediately without emitting any value. - public static var empty: Signal { - return self.init { observer in - observer.sendCompleted() - return nil - } - } - - /// Create a Signal that will be controlled by sending events to the given - /// observer. - /// - /// - note: The Signal will remain alive until a terminating event is sent - /// to the observer. - /// - /// - returns: A tuple made of signal and observer. - public static func pipe() -> (Signal, Observer) { - var observer: Observer! - let signal = self.init { innerObserver in - observer = innerObserver - return nil - } - - return (signal, observer) - } - - /// Observe the Signal by sending any future events to the given observer. - /// - /// - note: If the Signal has already terminated, the observer will - /// immediately receive an `interrupted` event. - /// - /// - parameters: - /// - observer: An observer to forward the events to. - /// - /// - returns: An optional `Disposable` which can be used to disconnect the - /// observer. - @discardableResult - public func observe(_ observer: Observer) -> Disposable? { - var token: RemovalToken? - state.modify { - $0?.retainedSignal = self - token = $0?.observers.insert(observer) - } - - if let token = token { - return ActionDisposable { [weak self] in - if let strongSelf = self { - strongSelf.state.modify { state in - state?.observers.remove(using: token) - if state?.observers.isEmpty ?? false { - state!.retainedSignal = nil - } - } - } - } - } else { - observer.sendInterrupted() - return nil - } - } -} - -private struct SignalState { - var observers: Bag.Observer> = Bag() - var retainedSignal: Signal? -} - -public protocol SignalProtocol { - /// The type of values being sent on the signal. - associatedtype Value - - /// The type of error that can occur on the signal. If errors aren't - /// possible then `NoError` can be used. - associatedtype Error: Swift.Error - - /// Extracts a signal from the receiver. - var signal: Signal { get } - - /// Observes the Signal by sending any future events to the given observer. - @discardableResult - func observe(_ observer: Signal.Observer) -> Disposable? -} - -extension Signal: SignalProtocol { - public var signal: Signal { - return self - } -} - -extension SignalProtocol { - /// Convenience override for observe(_:) to allow trailing-closure style - /// invocations. - /// - /// - parameters: - /// - action: A closure that will accept an event of the signal - /// - /// - returns: An optional `Disposable` which can be used to stop the - /// invocation of the callback. Disposing of the Disposable will - /// have no effect on the Signal itself. - @discardableResult - public func observe(_ action: @escaping Signal.Observer.Action) -> Disposable? { - return observe(Observer(action)) - } - - /// Observe the `Signal` by invoking the given callback when `next` or - /// `failed` event are received. - /// - /// - parameters: - /// - result: A closure that accepts instance of `Result` - /// enum that contains either a `.success(Value)` or - /// `.failure` case. - /// - /// - returns: An optional `Disposable` which can be used to stop the - /// invocation of the callback. Disposing of the Disposable will - /// have no effect on the Signal itself. - @discardableResult - public func observeResult(_ result: @escaping (Result) -> Void) -> Disposable? { - return observe( - Observer( - next: { result(.success($0)) }, - failed: { result(.failure($0)) } - ) - ) - } - - /// Observe the `Signal` by invoking the given callback when a `completed` - /// event is received. - /// - /// - parameters: - /// - completed: A closure that is called when `completed` event is - /// received. - /// - /// - returns: An optional `Disposable` which can be used to stop the - /// invocation of the callback. Disposing of the Disposable will - /// have no effect on the Signal itself. - @discardableResult - public func observeCompleted(_ completed: @escaping () -> Void) -> Disposable? { - return observe(Observer(completed: completed)) - } - - /// Observe the `Signal` by invoking the given callback when a `failed` - /// event is received. - /// - /// - parameters: - /// - error: A closure that is called when failed event is received. It - /// accepts an error parameter. - /// - /// Returns a Disposable which can be used to stop the invocation of the - /// callback. Disposing of the Disposable will have no effect on the Signal - /// itself. - @discardableResult - public func observeFailed(_ error: @escaping (Error) -> Void) -> Disposable? { - return observe(Observer(failed: error)) - } - - /// Observe the `Signal` by invoking the given callback when an - /// `interrupted` event is received. If the Signal has already terminated, - /// the callback will be invoked immediately. - /// - /// - parameters: - /// - interrupted: A closure that is invoked when `interrupted` event is - /// received - /// - /// - returns: An optional `Disposable` which can be used to stop the - /// invocation of the callback. Disposing of the Disposable will - /// have no effect on the Signal itself. - @discardableResult - public func observeInterrupted(_ interrupted: @escaping () -> Void) -> Disposable? { - return observe(Observer(interrupted: interrupted)) - } -} - -extension SignalProtocol where Error == NoError { - /// Observe the Signal by invoking the given callback when `next` events are - /// received. - /// - /// - parameters: - /// - next: A closure that accepts a value when `next` event is received. - /// - /// - returns: An optional `Disposable` which can be used to stop the - /// invocation of the callback. Disposing of the Disposable will - /// have no effect on the Signal itself. - @discardableResult - public func observeNext(_ next: @escaping (Value) -> Void) -> Disposable? { - return observe(Observer(next: next)) - } -} - -extension SignalProtocol { - /// Map each value in the signal to a new value. - /// - /// - parameters: - /// - transform: A closure that accepts a value from the `next` event and - /// returns a new value. - /// - /// - returns: A signal that will send new values. - public func map(_ transform: @escaping (Value) -> U) -> Signal { - return Signal { observer in - return self.observe { event in - observer.action(event.map(transform)) - } - } - } - - /// Map errors in the signal to a new error. - /// - /// - parameters: - /// - transform: A closure that accepts current error object and returns - /// a new type of error object. - /// - /// - returns: A signal that will send new type of errors. - public func mapError(_ transform: @escaping (Error) -> F) -> Signal { - return Signal { observer in - return self.observe { event in - observer.action(event.mapError(transform)) - } - } - } - - /// Preserve only the values of the signal that pass the given predicate. - /// - /// - parameters: - /// - predicate: A closure that accepts value and returns `Bool` denoting - /// whether value has passed the test. - /// - /// - returns: A signal that will send only the values passing the given - /// predicate. - public func filter(_ predicate: @escaping (Value) -> Bool) -> Signal { - return Signal { observer in - return self.observe { (event: Event) -> Void in - guard let value = event.value else { - observer.action(event) - return - } - - if predicate(value) { - observer.sendNext(value) - } - } - } - } -} - -extension SignalProtocol where Value: OptionalProtocol { - /// Unwrap non-`nil` values and forward them on the returned signal, `nil` - /// values are dropped. - /// - /// - returns: A signal that sends only non-nil values. - public func skipNil() -> Signal { - return filter { $0.optional != nil }.map { $0.optional! } - } -} - -extension SignalProtocol { - /// Take up to `n` values from the signal and then complete. - /// - /// - precondition: `count` must be non-negative number. - /// - /// - parameters: - /// - count: A number of values to take from the signal. - /// - /// - returns: A signal that will yield the first `count` values from `self` - public func take(first count: Int) -> Signal { - precondition(count >= 0) - - return Signal { observer in - if count == 0 { - observer.sendCompleted() - return nil - } - - var taken = 0 - - return self.observe { event in - guard let value = event.value else { - observer.action(event) - return - } - - if taken < count { - taken += 1 - observer.sendNext(value) - } - - if taken == count { - observer.sendCompleted() - } - } - } - } -} - -/// A reference type which wraps an array to auxiliate the collection of values -/// for `collect` operator. -private final class CollectState { - var values: [Value] = [] - - /// Collects a new value. - func append(_ value: Value) { - values.append(value) - } - - /// Check if there are any items remaining. - /// - /// - note: This method also checks if there weren't collected any values - /// and, in that case, it means an empty array should be sent as the - /// result of collect. - var isEmpty: Bool { - /// We use capacity being zero to determine if we haven't collected any - /// value since we're keeping the capacity of the array to avoid - /// unnecessary and expensive allocations). This also guarantees - /// retro-compatibility around the original `collect()` operator. - return values.isEmpty && values.capacity > 0 - } - - /// Removes all values previously collected if any. - func flush() { - // Minor optimization to avoid consecutive allocations. Can - // be useful for sequences of regular or similar size and to - // track if any value was ever collected. - values.removeAll(keepingCapacity: true) - } -} - -extension SignalProtocol { - /// Collect all values sent by the signal then forward them as a single - /// array and complete. - /// - /// - note: When `self` completes without collecting any value, it will send - /// an empty array of values. - /// - /// - returns: A signal that will yield an array of values when `self` - /// completes. - public func collect() -> Signal<[Value], Error> { - return collect { _,_ in false } - } - - /// Collect at most `count` values from `self`, forward them as a single - /// array and complete. - /// - /// - note: When the count is reached the array is sent and the signal - /// starts over yielding a new array of values. - /// - /// - note: When `self` completes any remaining values will be sent, the - /// last array may not have `count` values. Alternatively, if were - /// not collected any values will sent an empty array of values. - /// - /// - precondition: `count` should be greater than zero. - /// - public func collect(count: Int) -> Signal<[Value], Error> { - precondition(count > 0) - return collect { values in values.count == count } - } - - /// Collect values that pass the given predicate then forward them as a - /// single array and complete. - /// - /// - note: When `self` completes any remaining values will be sent, the - /// last array may not match `predicate`. Alternatively, if were not - /// collected any values will sent an empty array of values. - /// - /// ```` - /// let (signal, observer) = Signal.pipe() - /// - /// signal - /// .collect { values in values.reduce(0, combine: +) == 8 } - /// .observeNext { print($0) } - /// - /// observer.sendNext(1) - /// observer.sendNext(3) - /// observer.sendNext(4) - /// observer.sendNext(7) - /// observer.sendNext(1) - /// observer.sendNext(5) - /// observer.sendNext(6) - /// observer.sendCompleted() - /// - /// // Output: - /// // [1, 3, 4] - /// // [7, 1] - /// // [5, 6] - /// ```` - /// - /// - parameters: - /// - predicate: Predicate to match when values should be sent (returning - /// `true`) or alternatively when they should be collected - /// (where it should return `false`). The most recent value - /// (`next`) is included in `values` and will be the end of - /// the current array of values if the predicate returns - /// `true`. - /// - /// - returns: A signal that collects values passing the predicate and, when - /// `self` completes, forwards them as a single array and - /// complets. - public func collect(_ predicate: @escaping (_ values: [Value]) -> Bool) -> Signal<[Value], Error> { - return Signal { observer in - let state = CollectState() - - return self.observe { event in - switch event { - case let .next(value): - state.append(value) - if predicate(state.values) { - observer.sendNext(state.values) - state.flush() - } - case .completed: - if !state.isEmpty { - observer.sendNext(state.values) - } - observer.sendCompleted() - case let .failed(error): - observer.sendFailed(error) - case .interrupted: - observer.sendInterrupted() - } - } - } - } - - /// Repeatedly collect an array of values up to a matching `next` value. - /// Then forward them as single array and wait for next events. - /// - /// - note: When `self` completes any remaining values will be sent, the - /// last array may not match `predicate`. Alternatively, if no - /// values were collected an empty array will be sent. - /// - /// ```` - /// let (signal, observer) = Signal.pipe() - /// - /// signal - /// .collect { values, next in next == 7 } - /// .observeNext { print($0) } - /// - /// observer.sendNext(1) - /// observer.sendNext(1) - /// observer.sendNext(7) - /// observer.sendNext(7) - /// observer.sendNext(5) - /// observer.sendNext(6) - /// observer.sendCompleted() - /// - /// // Output: - /// // [1, 1] - /// // [7] - /// // [7, 5, 6] - /// ```` - /// - /// - parameters: - /// - predicate: Predicate to match when values should be sent (returning - /// `true`) or alternatively when they should be collected - /// (where it should return `false`). The most recent value - /// (`next`) is not included in `values` and will be the - /// start of the next array of values if the predicate - /// returns `true`. - /// - /// - returns: A signal that will yield an array of values based on a - /// predicate which matches the values collected and the next - /// value. - public func collect(_ predicate: @escaping (_ values: [Value], _ next: Value) -> Bool) -> Signal<[Value], Error> { - return Signal { observer in - let state = CollectState() - - return self.observe { event in - switch event { - case let .next(value): - if predicate(state.values, value) { - observer.sendNext(state.values) - state.flush() - } - state.append(value) - case .completed: - if !state.isEmpty { - observer.sendNext(state.values) - } - observer.sendCompleted() - case let .failed(error): - observer.sendFailed(error) - case .interrupted: - observer.sendInterrupted() - } - } - } - } - - /// Forward all events onto the given scheduler, instead of whichever - /// scheduler they originally arrived upon. - /// - /// - parameters: - /// - scheduler: A scheduler to deliver events on. - /// - /// - returns: A signal that will yield `self` values on provided scheduler. - public func observe(on scheduler: SchedulerProtocol) -> Signal { - return Signal { observer in - return self.observe { event in - scheduler.schedule { - observer.action(event) - } - } - } - } -} - -private final class CombineLatestState { - var latestValue: Value? - var isCompleted = false -} - -extension SignalProtocol { - private func observeWithStates(_ signalState: CombineLatestState, _ otherState: CombineLatestState, _ lock: NSLock, _ observer: Signal<(), Error>.Observer) -> Disposable? { - return self.observe { event in - switch event { - case let .next(value): - lock.lock() - - signalState.latestValue = value - if otherState.latestValue != nil { - observer.sendNext() - } - - lock.unlock() - - case let .failed(error): - observer.sendFailed(error) - - case .completed: - lock.lock() - - signalState.isCompleted = true - if otherState.isCompleted { - observer.sendCompleted() - } - - lock.unlock() - - case .interrupted: - observer.sendInterrupted() - } - } - } - - /// Combine the latest value of the receiver with the latest value from the - /// given signal. - /// - /// - note: The returned signal will not send a value until both inputs have - /// sent at least one value each. - /// - /// - note: If either signal is interrupted, the returned signal will also - /// be interrupted. - /// - /// - parameters: - /// - otherSignal: A signal to combine `self`'s value with. - /// - /// - returns: A signal that will yield a tuple containing values of `self` - /// and given signal. - public func combineLatest(with other: Signal) -> Signal<(Value, U), Error> { - return Signal { observer in - let lock = NSLock() - lock.name = "org.reactivecocoa.ReactiveSwift.combineLatestWith" - - let signalState = CombineLatestState() - let otherState = CombineLatestState() - - let onBothNext = { - observer.sendNext((signalState.latestValue!, otherState.latestValue!)) - } - - let observer = Signal<(), Error>.Observer(next: onBothNext, failed: observer.sendFailed, completed: observer.sendCompleted, interrupted: observer.sendInterrupted) - - let disposable = CompositeDisposable() - disposable += self.observeWithStates(signalState, otherState, lock, observer) - disposable += other.observeWithStates(otherState, signalState, lock, observer) - - return disposable - } - } - - /// Delay `next` and `completed` events by the given interval, forwarding - /// them on the given scheduler. - /// - /// - note: failed and `interrupted` events are always scheduled - /// immediately. - /// - /// - parameters: - /// - interval: Interval to delay `next` and `completed` events by. - /// - scheduler: A scheduler to deliver delayed events on. - /// - /// - returns: A signal that will delay `next` and `completed` events and - /// will yield them on given scheduler. - public func delay(_ interval: TimeInterval, on scheduler: DateSchedulerProtocol) -> Signal { - precondition(interval >= 0) - - return Signal { observer in - return self.observe { event in - switch event { - case .failed, .interrupted: - scheduler.schedule { - observer.action(event) - } - - case .next, .completed: - let date = scheduler.currentDate.addingTimeInterval(interval) - scheduler.schedule(after: date) { - observer.action(event) - } - } - } - } - } - - /// Skip first `count` number of values then act as usual. - /// - /// - parameters: - /// - count: A number of values to skip. - /// - /// - returns: A signal that will skip the first `count` values, then - /// forward everything afterward. - public func skip(first count: Int) -> Signal { - precondition(count >= 0) - - if count == 0 { - return signal - } - - return Signal { observer in - var skipped = 0 - - return self.observe { event in - if case .next = event, skipped < count { - skipped += 1 - } else { - observer.action(event) - } - } - } - } - - /// Treat all Events from `self` as plain values, allowing them to be - /// manipulated just like any other value. - /// - /// In other words, this brings Events “into the monad”. - /// - /// - note: When a Completed or Failed event is received, the resulting - /// signal will send the Event itself and then complete. When an - /// Interrupted event is received, the resulting signal will send - /// the Event itself and then interrupt. - /// - /// - returns: A signal that sends events as its values. - public func materialize() -> Signal, NoError> { - return Signal { observer in - return self.observe { event in - observer.sendNext(event) - - switch event { - case .interrupted: - observer.sendInterrupted() - - case .completed, .failed: - observer.sendCompleted() - - case .next: - break - } - } - } - } -} - -extension SignalProtocol where Value: EventProtocol, Error == NoError { - /// Translate a signal of `Event` _values_ into a signal of those events - /// themselves. - /// - /// - returns: A signal that sends values carried by `self` events. - public func dematerialize() -> Signal { - return Signal { observer in - return self.observe { event in - switch event { - case let .next(innerEvent): - observer.action(innerEvent.event) - - case .failed: - fatalError("NoError is impossible to construct") - - case .completed: - observer.sendCompleted() - - case .interrupted: - observer.sendInterrupted() - } - } - } - } -} - -extension SignalProtocol { - /// Inject side effects to be performed upon the specified signal events. - /// - /// - parameters: - /// - event: A closure that accepts an event and is invoked on every - /// received event. - /// - next: A closure that accepts a value from `next` event. - /// - failed: A closure that accepts error object and is invoked for - /// failed event. - /// - completed: A closure that is invoked for `completed` event. - /// - interrupted: A closure that is invoked for `interrupted` event. - /// - terminated: A closure that is invoked for any terminating event. - /// - disposed: A closure added as disposable when signal completes. - /// - /// - returns: A signal with attached side-effects for given event cases. - public func on( - event: ((Event) -> Void)? = nil, - failed: ((Error) -> Void)? = nil, - completed: (() -> Void)? = nil, - interrupted: (() -> Void)? = nil, - terminated: (() -> Void)? = nil, - disposed: (() -> Void)? = nil, - next: ((Value) -> Void)? = nil - ) -> Signal { - return Signal { observer in - let disposable = CompositeDisposable() - - _ = disposed.map(disposable.add) - - disposable += signal.observe { receivedEvent in - event?(receivedEvent) - - switch receivedEvent { - case let .next(value): - next?(value) - - case let .failed(error): - failed?(error) - - case .completed: - completed?() - - case .interrupted: - interrupted?() - } - - if receivedEvent.isTerminating { - terminated?() - } - - observer.action(receivedEvent) - } - - return disposable - } - } -} - -private struct SampleState { - var latestValue: Value? = nil - var isSignalCompleted: Bool = false - var isSamplerCompleted: Bool = false -} - -extension SignalProtocol { - /// Forward the latest value from `self` with the value from `sampler` as a - /// tuple, only when`sampler` sends a `next` event. - /// - /// - note: If `sampler` fires before a value has been observed on `self`, - /// nothing happens. - /// - /// - parameters: - /// - sampler: A signal that will trigger the delivery of `next` event - /// from `self`. - /// - /// - returns: A signal that will send values from `self` and `sampler`, - /// sampled (possibly multiple times) by `sampler`, then complete - /// once both input signals have completed, or interrupt if - /// either input signal is interrupted. - public func sample(with sampler: Signal) -> Signal<(Value, T), Error> { - return Signal { observer in - let state = Atomic(SampleState()) - let disposable = CompositeDisposable() - - disposable += self.observe { event in - switch event { - case let .next(value): - state.modify { - $0.latestValue = value - } - - case let .failed(error): - observer.sendFailed(error) - - case .completed: - let shouldComplete: Bool = state.modify { - $0.isSignalCompleted = true - return $0.isSamplerCompleted - } - - if shouldComplete { - observer.sendCompleted() - } - - case .interrupted: - observer.sendInterrupted() - } - } - - disposable += sampler.observe { event in - switch event { - case .next(let samplerValue): - if let value = state.value.latestValue { - observer.sendNext((value, samplerValue)) - } - - case .completed: - let shouldComplete: Bool = state.modify { - $0.isSamplerCompleted = true - return $0.isSignalCompleted - } - - if shouldComplete { - observer.sendCompleted() - } - - case .interrupted: - observer.sendInterrupted() - - case .failed: - break - } - } - - return disposable - } - } - - /// Forward the latest value from `self` whenever `sampler` sends a `next` - /// event. - /// - /// - note: If `sampler` fires before a value has been observed on `self`, - /// nothing happens. - /// - /// - parameters: - /// - sampler: A signal that will trigger the delivery of `next` event - /// from `self`. - /// - /// - returns: A signal that will send values from `self`, sampled (possibly - /// multiple times) by `sampler`, then complete once both input - /// signals have completed, or interrupt if either input signal - /// is interrupted. - public func sample(on sampler: Signal<(), NoError>) -> Signal { - return sample(with: sampler) - .map { $0.0 } - } - - /// Forwards events from `self` until `lifetime` ends, at which point the - /// returned signal will complete. - /// - /// - parameters: - /// - lifetime: A lifetime whose `ended` signal will cause the returned - /// signal to complete. - /// - /// - returns: A signal that will deliver events until `lifetime` ends. - public func take(during lifetime: Lifetime) -> Signal { - return take(until: lifetime.ended) - } - - /// Forward events from `self` until `trigger` sends a `next` or - /// `completed` event, at which point the returned signal will complete. - /// - /// - parameters: - /// - trigger: A signal whose `next` or `completed` events will stop the - /// delivery of `next` events from `self`. - /// - /// - returns: A signal that will deliver events until `trigger` sends - /// `next` or `completed` events. - public func take(until trigger: Signal<(), NoError>) -> Signal { - return Signal { observer in - let disposable = CompositeDisposable() - disposable += self.observe(observer) - - disposable += trigger.observe { event in - switch event { - case .next, .completed: - observer.sendCompleted() - - case .failed, .interrupted: - break - } - } - - return disposable - } - } - - /// Do not forward any values from `self` until `trigger` sends a `next` or - /// `completed` event, at which point the returned signal behaves exactly - /// like `signal`. - /// - /// - parameters: - /// - trigger: A signal whose `next` or `completed` events will start the - /// deliver of events on `self`. - /// - /// - returns: A signal that will deliver events once the `trigger` sends - /// `next` or `completed` events. - public func skip(until trigger: Signal<(), NoError>) -> Signal { - return Signal { observer in - let disposable = SerialDisposable() - - disposable.innerDisposable = trigger.observe { event in - switch event { - case .next, .completed: - disposable.innerDisposable = self.observe(observer) - - case .failed, .interrupted: - break - } - } - - return disposable - } - } - - /// Forward events from `self` with history: values of the returned signal - /// are a tuples whose first member is the previous value and whose second member - /// is the current value. `initial` is supplied as the first member when `self` - /// sends its first value. - /// - /// - parameters: - /// - initial: A value that will be combined with the first value sent by - /// `self`. - /// - /// - returns: A signal that sends tuples that contain previous and current - /// sent values of `self`. - public func combinePrevious(_ initial: Value) -> Signal<(Value, Value), Error> { - return scan((initial, initial)) { previousCombinedValues, newValue in - return (previousCombinedValues.1, newValue) - } - } - - - /// Send only the final value and then immediately completes. - /// - /// - parameters: - /// - initial: Initial value for the accumulator. - /// - combine: A closure that accepts accumulator and sent value of - /// `self`. - /// - /// - returns: A signal that sends accumulated value after `self` completes. - public func reduce(_ initial: U, _ combine: @escaping (U, Value) -> U) -> Signal { - // We need to handle the special case in which `signal` sends no values. - // We'll do that by sending `initial` on the output signal (before - // taking the last value). - let (scannedSignalWithInitialValue, outputSignalObserver) = Signal.pipe() - let outputSignal = scannedSignalWithInitialValue.take(last: 1) - - // Now that we've got takeLast() listening to the piped signal, send - // that initial value. - outputSignalObserver.sendNext(initial) - - // Pipe the scanned input signal into the output signal. - self.scan(initial, combine) - .observe(outputSignalObserver) - - return outputSignal - } - - /// Aggregate values into a single combined value. When `self` emits its - /// first value, `combine` is invoked with `initial` as the first argument - /// and that emitted value as the second argument. The result is emitted - /// from the signal returned from `scan`. That result is then passed to - /// `combine` as the first argument when the next value is emitted, and so - /// on. - /// - /// - parameters: - /// - initial: Initial value for the accumulator. - /// - combine: A closure that accepts accumulator and sent value of - /// `self`. - /// - /// - returns: A signal that sends accumulated value each time `self` emits - /// own value. - public func scan(_ initial: U, _ combine: @escaping (U, Value) -> U) -> Signal { - return Signal { observer in - var accumulator = initial - - return self.observe { event in - observer.action(event.map { value in - accumulator = combine(accumulator, value) - return accumulator - }) - } - } - } -} - -extension SignalProtocol where Value: Equatable { - /// Forward only those values from `self` which are not duplicates of the - /// immedately preceding value. - /// - /// - note: The first value is always forwarded. - /// - /// - returns: A signal that does not send two equal values sequentially. - public func skipRepeats() -> Signal { - return skipRepeats(==) - } -} - -extension SignalProtocol { - /// Forward only those values from `self` which do not pass `isRepeat` with - /// respect to the previous value. - /// - /// - note: The first value is always forwarded. - /// - /// - parameters: - /// - isRepeate: A closure that accepts previous and current values of - /// `self` and returns `Bool` whether these values are - /// repeating. - /// - /// - returns: A signal that forwards only those values that fail given - /// `isRepeat` predicate. - public func skipRepeats(_ isRepeat: @escaping (Value, Value) -> Bool) -> Signal { - return self - .scan((nil, false)) { (accumulated: (Value?, Bool), next: Value) -> (value: Value?, repeated: Bool) in - switch accumulated.0 { - case nil: - return (next, false) - case let prev? where isRepeat(prev, next): - return (prev, true) - case _?: - return (Optional(next), false) - } - } - .filter { !$0.repeated } - .map { $0.value } - .skipNil() - } - - /// Do not forward any values from `self` until `predicate` returns false, - /// at which point the returned signal behaves exactly like `signal`. - /// - /// - parameters: - /// - predicate: A closure that accepts a value and returns whether `self` - /// should still not forward that value to a `signal`. - /// - /// - returns: A signal that sends only forwarded values from `self`. - public func skip(while predicate: @escaping (Value) -> Bool) -> Signal { - return Signal { observer in - var shouldSkip = true - - return self.observe { event in - switch event { - case let .next(value): - shouldSkip = shouldSkip && predicate(value) - if !shouldSkip { - fallthrough - } - - case .failed, .completed, .interrupted: - observer.action(event) - } - } - } - } - - /// Forward events from `self` until `replacement` begins sending events. - /// - /// - parameters: - /// - replacement: A signal to wait to wait for values from and start - /// sending them as a replacement to `self`'s values. - /// - /// - returns: A signal which passes through `next`, failed, and - /// `interrupted` events from `self` until `replacement` sends - /// an event, at which point the returned signal will send that - /// event and switch to passing through events from `replacement` - /// instead, regardless of whether `self` has sent events - /// already. - public func take(untilReplacement signal: Signal) -> Signal { - return Signal { observer in - let disposable = CompositeDisposable() - - let signalDisposable = self.observe { event in - switch event { - case .completed: - break - - case .next, .failed, .interrupted: - observer.action(event) - } - } - - disposable += signalDisposable - disposable += signal.observe { event in - signalDisposable?.dispose() - observer.action(event) - } - - return disposable - } - } - - /// Wait until `self` completes and then forward the final `count` values - /// on the returned signal. - /// - /// - parameters: - /// - count: Number of last events to send after `self` completes. - /// - /// - returns: A signal that receives up to `count` values from `self` - /// after `self` completes. - public func take(last count: Int) -> Signal { - return Signal { observer in - var buffer: [Value] = [] - buffer.reserveCapacity(count) - - return self.observe { event in - switch event { - case let .next(value): - // To avoid exceeding the reserved capacity of the buffer, - // we remove then add. Remove elements until we have room to - // add one more. - while (buffer.count + 1) > count { - buffer.remove(at: 0) - } - - buffer.append(value) - case let .failed(error): - observer.sendFailed(error) - case .completed: - buffer.forEach(observer.sendNext) - - observer.sendCompleted() - case .interrupted: - observer.sendInterrupted() - } - } - } - } - - /// Forward any values from `self` until `predicate` returns false, at which - /// point the returned signal will complete. - /// - /// - parameters: - /// - predicate: A closure that accepts value and returns `Bool` value - /// whether `self` should forward it to `signal` and continue - /// sending other events. - /// - /// - returns: A signal that sends events until the values sent by `self` - /// pass the given `predicate`. - public func take(while predicate: @escaping (Value) -> Bool) -> Signal { - return Signal { observer in - return self.observe { event in - if let value = event.value, !predicate(value) { - observer.sendCompleted() - } else { - observer.action(event) - } - } - } - } -} - -private struct ZipState { - var values: (left: [Left], right: [Right]) = ([], []) - var isCompleted: (left: Bool, right: Bool) = (false, false) - - var isFinished: Bool { - return (isCompleted.left && values.left.isEmpty) || (isCompleted.right && values.right.isEmpty) - } -} - -extension SignalProtocol { - /// Zip elements of two signals into pairs. The elements of any Nth pair - /// are the Nth elements of the two input signals. - /// - /// - parameters: - /// - otherSignal: A signal to zip values with. - /// - /// - returns: A signal that sends tuples of `self` and `otherSignal`. - public func zip(with other: Signal) -> Signal<(Value, U), Error> { - return Signal { observer in - let state = Atomic(ZipState()) - let disposable = CompositeDisposable() - - let flush = { - var tuple: (Value, U)? - var isFinished = false - - state.modify { state in - guard !state.values.left.isEmpty && !state.values.right.isEmpty else { - isFinished = state.isFinished - return - } - - tuple = (state.values.left.removeFirst(), state.values.right.removeFirst()) - isFinished = state.isFinished - } - - if let tuple = tuple { - observer.sendNext(tuple) - } - - if isFinished { - observer.sendCompleted() - } - } - - let onFailed = observer.sendFailed - let onInterrupted = observer.sendInterrupted - - disposable += self.observe { event in - switch event { - case let .next(value): - state.modify { - $0.values.left.append(value) - } - flush() - - case let .failed(error): - onFailed(error) - - case .completed: - state.modify { - $0.isCompleted.left = true - } - flush() - - case .interrupted: - onInterrupted() - } - } - - disposable += other.observe { event in - switch event { - case let .next(value): - state.modify { - $0.values.right.append(value) - } - flush() - - case let .failed(error): - onFailed(error) - - case .completed: - state.modify { - $0.isCompleted.right = true - } - flush() - - case .interrupted: - onInterrupted() - } - } - - return disposable - } - } - - /// Apply `operation` to values from `self` with `success`ful results - /// forwarded on the returned signal and `failure`s sent as failed events. - /// - /// - parameters: - /// - operation: A closure that accepts a value and returns a `Result`. - /// - /// - returns: A signal that receives `success`ful `Result` as `next` event - /// and `failure` as failed event. - public func attempt(_ operation: @escaping (Value) -> Result<(), Error>) -> Signal { - return attemptMap { value in - return operation(value).map { - return value - } - } - } - - /// Apply `operation` to values from `self` with `success`ful results mapped - /// on the returned signal and `failure`s sent as failed events. - /// - /// - parameters: - /// - operation: A closure that accepts a value and returns a result of - /// a mapped value as `success`. - /// - /// - returns: A signal that sends mapped values from `self` if returned - /// `Result` is `success`ful, `failed` events otherwise. - public func attemptMap(_ operation: @escaping (Value) -> Result) -> Signal { - return Signal { observer in - self.observe { event in - switch event { - case let .next(value): - operation(value).analysis( - ifSuccess: observer.sendNext, - ifFailure: observer.sendFailed - ) - case let .failed(error): - observer.sendFailed(error) - case .completed: - observer.sendCompleted() - case .interrupted: - observer.sendInterrupted() - } - } - } - } - - /// Throttle values sent by the receiver, so that at least `interval` - /// seconds pass between each, then forwards them on the given scheduler. - /// - /// - note: If multiple values are received before the interval has elapsed, - /// the latest value is the one that will be passed on. - /// - /// - note: If the input signal terminates while a value is being throttled, - /// that value will be discarded and the returned signal will - /// terminate immediately. - /// - /// - note: If the device time changed backwords before previous date while - /// a value is being throttled, and if there is a new value sent, - /// the new value will be passed anyway. - /// - /// - parameters: - /// - interval: Number of seconds to wait between sent values. - /// - scheduler: A scheduler to deliver events on. - /// - /// - returns: A signal that sends values at least `interval` seconds - /// appart on a given scheduler. - public func throttle(_ interval: TimeInterval, on scheduler: DateSchedulerProtocol) -> Signal { - precondition(interval >= 0) - - return Signal { observer in - let state: Atomic> = Atomic(ThrottleState()) - let schedulerDisposable = SerialDisposable() - - let disposable = CompositeDisposable() - disposable += schedulerDisposable - - disposable += self.observe { event in - guard let value = event.value else { - schedulerDisposable.innerDisposable = scheduler.schedule { - observer.action(event) - } - return - } - - var scheduleDate: Date! - state.modify { - $0.pendingValue = value - - let proposedScheduleDate: Date - if let previousDate = $0.previousDate, previousDate.compare(scheduler.currentDate) != .orderedDescending { - proposedScheduleDate = previousDate.addingTimeInterval(interval) - } else { - proposedScheduleDate = scheduler.currentDate - } - - switch proposedScheduleDate.compare(scheduler.currentDate) { - case .orderedAscending: - scheduleDate = scheduler.currentDate - - case .orderedSame: fallthrough - case .orderedDescending: - scheduleDate = proposedScheduleDate - } - } - - schedulerDisposable.innerDisposable = scheduler.schedule(after: scheduleDate) { - let pendingValue: Value? = state.modify { state in - defer { - if state.pendingValue != nil { - state.pendingValue = nil - state.previousDate = scheduleDate - } - } - return state.pendingValue - } - - if let pendingValue = pendingValue { - observer.sendNext(pendingValue) - } - } - } - - return disposable - } - } - - /// Debounce values sent by the receiver, such that at least `interval` - /// seconds pass after the receiver has last sent a value, then forward the - /// latest value on the given scheduler. - /// - /// - note: If multiple values are received before the interval has elapsed, - /// the latest value is the one that will be passed on. - /// - /// - note: If the input signal terminates while a value is being debounced, - /// that value will be discarded and the returned signal will - /// terminate immediately. - /// - /// - parameters: - /// - interval: A number of seconds to wait before sending a value. - /// - scheduler: A scheduler to send values on. - /// - /// - returns: A signal that sends values that are sent from `self` at least - /// `interval` seconds apart. - public func debounce(_ interval: TimeInterval, on scheduler: DateSchedulerProtocol) -> Signal { - precondition(interval >= 0) - - return self - .materialize() - .flatMap(.latest) { event -> SignalProducer, NoError> in - if event.isTerminating { - return SignalProducer(value: event).observe(on: scheduler) - } else { - return SignalProducer(value: event).delay(interval, on: scheduler) - } - } - .dematerialize() - } -} - -extension SignalProtocol { - /// Forward only those values from `self` that have unique identities across - /// the set of all values that have been seen. - /// - /// - note: This causes the identities to be retained to check for - /// uniqueness. - /// - /// - parameters: - /// - transform: A closure that accepts a value and returns identity - /// value. - /// - /// - returns: A signal that sends unique values during its lifetime. - public func uniqueValues(_ transform: @escaping (Value) -> Identity) -> Signal { - return Signal { observer in - var seenValues: Set = [] - - return self - .observe { event in - switch event { - case let .next(value): - let identity = transform(value) - if !seenValues.contains(identity) { - seenValues.insert(identity) - fallthrough - } - - case .failed, .completed, .interrupted: - observer.action(event) - } - } - } - } -} - -extension SignalProtocol where Value: Hashable { - /// Forward only those values from `self` that are unique across the set of - /// all values that have been seen. - /// - /// - note: This causes the values to be retained to check for uniqueness. - /// Providing a function that returns a unique value for each sent - /// value can help you reduce the memory footprint. - /// - /// - returns: A signal that sends unique values during its lifetime. - public func uniqueValues() -> Signal { - return uniqueValues { $0 } - } -} - -private struct ThrottleState { - var previousDate: Date? = nil - var pendingValue: Value? = nil -} - -extension SignalProtocol { - /// Combines the values of all the given signals, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: Signal, _ b: Signal) -> Signal<(Value, B), Error> { - return a.combineLatest(with: b) - } - - /// Combines the values of all the given signals, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: Signal, _ b: Signal, _ c: Signal) -> Signal<(Value, B, C), Error> { - return combineLatest(a, b) - .combineLatest(with: c) - .map(repack) - } - - /// Combines the values of all the given signals, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal) -> Signal<(Value, B, C, D), Error> { - return combineLatest(a, b, c) - .combineLatest(with: d) - .map(repack) - } - - /// Combines the values of all the given signals, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal) -> Signal<(Value, B, C, D, E), Error> { - return combineLatest(a, b, c, d) - .combineLatest(with: e) - .map(repack) - } - - /// Combines the values of all the given signals, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal) -> Signal<(Value, B, C, D, E, F), Error> { - return combineLatest(a, b, c, d, e) - .combineLatest(with: f) - .map(repack) - } - - /// Combines the values of all the given signals, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal, _ g: Signal) -> Signal<(Value, B, C, D, E, F, G), Error> { - return combineLatest(a, b, c, d, e, f) - .combineLatest(with: g) - .map(repack) - } - - /// Combines the values of all the given signals, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal, _ g: Signal, _ h: Signal) -> Signal<(Value, B, C, D, E, F, G, H), Error> { - return combineLatest(a, b, c, d, e, f, g) - .combineLatest(with: h) - .map(repack) - } - - /// Combines the values of all the given signals, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal, _ g: Signal, _ h: Signal, _ i: Signal) -> Signal<(Value, B, C, D, E, F, G, H, I), Error> { - return combineLatest(a, b, c, d, e, f, g, h) - .combineLatest(with: i) - .map(repack) - } - - /// Combines the values of all the given signals, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal, _ g: Signal, _ h: Signal, _ i: Signal, _ j: Signal) -> Signal<(Value, B, C, D, E, F, G, H, I, J), Error> { - return combineLatest(a, b, c, d, e, f, g, h, i) - .combineLatest(with: j) - .map(repack) - } - - /// Combines the values of all the given signals, in the manner described by - /// `combineLatestWith`. No events will be sent if the sequence is empty. - public static func combineLatest(_ signals: S) -> Signal<[Value], Error> - where S.Iterator.Element == Signal - { - var generator = signals.makeIterator() - if let first = generator.next() { - let initial = first.map { [$0] } - return IteratorSequence(generator).reduce(initial) { signal, next in - signal.combineLatest(with: next).map { $0.0 + [$0.1] } - } - } - - return .never - } - - /// Zips the values of all the given signals, in the manner described by - /// `zipWith`. - public static func zip(_ a: Signal, _ b: Signal) -> Signal<(Value, B), Error> { - return a.zip(with: b) - } - - /// Zips the values of all the given signals, in the manner described by - /// `zipWith`. - public static func zip(_ a: Signal, _ b: Signal, _ c: Signal) -> Signal<(Value, B, C), Error> { - return zip(a, b) - .zip(with: c) - .map(repack) - } - - /// Zips the values of all the given signals, in the manner described by - /// `zipWith`. - public static func zip(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal) -> Signal<(Value, B, C, D), Error> { - return zip(a, b, c) - .zip(with: d) - .map(repack) - } - - /// Zips the values of all the given signals, in the manner described by - /// `zipWith`. - public static func zip(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal) -> Signal<(Value, B, C, D, E), Error> { - return zip(a, b, c, d) - .zip(with: e) - .map(repack) - } - - /// Zips the values of all the given signals, in the manner described by - /// `zipWith`. - public static func zip(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal) -> Signal<(Value, B, C, D, E, F), Error> { - return zip(a, b, c, d, e) - .zip(with: f) - .map(repack) - } - - /// Zips the values of all the given signals, in the manner described by - /// `zipWith`. - public static func zip(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal, _ g: Signal) -> Signal<(Value, B, C, D, E, F, G), Error> { - return zip(a, b, c, d, e, f) - .zip(with: g) - .map(repack) - } - - /// Zips the values of all the given signals, in the manner described by - /// `zipWith`. - public static func zip(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal, _ g: Signal, _ h: Signal) -> Signal<(Value, B, C, D, E, F, G, H), Error> { - return zip(a, b, c, d, e, f, g) - .zip(with: h) - .map(repack) - } - - /// Zips the values of all the given signals, in the manner described by - /// `zipWith`. - public static func zip(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal, _ g: Signal, _ h: Signal, _ i: Signal) -> Signal<(Value, B, C, D, E, F, G, H, I), Error> { - return zip(a, b, c, d, e, f, g, h) - .zip(with: i) - .map(repack) - } - - /// Zips the values of all the given signals, in the manner described by - /// `zipWith`. - public static func zip(_ a: Signal, _ b: Signal, _ c: Signal, _ d: Signal, _ e: Signal, _ f: Signal, _ g: Signal, _ h: Signal, _ i: Signal, _ j: Signal) -> Signal<(Value, B, C, D, E, F, G, H, I, J), Error> { - return zip(a, b, c, d, e, f, g, h, i) - .zip(with: j) - .map(repack) - } - - /// Zips the values of all the given signals, in the manner described by - /// `zipWith`. No events will be sent if the sequence is empty. - public static func zip(_ signals: S) -> Signal<[Value], Error> - where S.Iterator.Element == Signal - { - var generator = signals.makeIterator() - if let first = generator.next() { - let initial = first.map { [$0] } - return IteratorSequence(generator).reduce(initial) { signal, next in - signal.zip(with: next).map { $0.0 + [$0.1] } - } - } - - return .never - } -} - -extension SignalProtocol { - /// Forward events from `self` until `interval`. Then if signal isn't - /// completed yet, fails with `error` on `scheduler`. - /// - /// - note: 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. - /// - /// - parameters: - /// - error: Error to send with failed event if `self` is not completed - /// when `interval` passes. - /// - interval: Number of seconds to wait for `self` to complete. - /// - scheudler: A scheduler to deliver error on. - /// - /// - returns: A signal that sends events for at most `interval` seconds, - /// then, if not `completed` - sends `error` with failed event - /// on `scheduler`. - public func timeout(after interval: TimeInterval, raising error: Error, on scheduler: DateSchedulerProtocol) -> Signal { - precondition(interval >= 0) - - return Signal { observer in - let disposable = CompositeDisposable() - let date = scheduler.currentDate.addingTimeInterval(interval) - - disposable += scheduler.schedule(after: date) { - observer.sendFailed(error) - } - - disposable += self.observe(observer) - return disposable - } - } -} - -extension SignalProtocol where Error == NoError { - /// Promote a signal that does not generate failures into one that can. - /// - /// - note: This does not actually cause failures to be generated for the - /// given signal, but makes it easier to combine with other signals - /// that may fail; for example, with operators like - /// `combineLatestWith`, `zipWith`, `flatten`, etc. - /// - /// - parameters: - /// - _ An `ErrorType`. - /// - /// - returns: A signal that has an instantiatable `ErrorType`. - public func promoteErrors(_: F.Type) -> Signal { - return Signal { observer in - return self.observe { event in - switch event { - case let .next(value): - observer.sendNext(value) - case .failed: - fatalError("NoError is impossible to construct") - case .completed: - observer.sendCompleted() - case .interrupted: - observer.sendInterrupted() - } - } - } - } - - /// Forward events from `self` until `interval`. Then if signal isn't - /// completed yet, fails with `error` on `scheduler`. - /// - /// - note: 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. - /// - /// - parameters: - /// - interval: Number of seconds to wait for `self` to complete. - /// - error: Error to send with `failed` event if `self` is not completed - /// when `interval` passes. - /// - scheudler: A scheduler to deliver error on. - /// - /// - returns: A signal that sends events for at most `interval` seconds, - /// then, if not `completed` - sends `error` with `failed` event - /// on `scheduler`. - public func timeout( - after interval: TimeInterval, - raising error: NewError, - on scheduler: DateSchedulerProtocol - ) -> Signal { - return self - .promoteErrors(NewError.self) - .timeout(after: interval, raising: error, on: scheduler) - } -} diff --git a/ReactiveSwift/SignalProducer.swift b/ReactiveSwift/SignalProducer.swift deleted file mode 100644 index 2fd3b8d23c..0000000000 --- a/ReactiveSwift/SignalProducer.swift +++ /dev/null @@ -1,1943 +0,0 @@ -import Foundation -import Result - -/// A SignalProducer creates Signals that can produce values of type `Value` -/// and/or fail with errors of type `Error`. If no failure should be possible, -/// `NoError` can be specified for `Error`. -/// -/// SignalProducers can be used to represent operations or tasks, like network -/// requests, where each invocation of `start()` will create a new underlying -/// operation. This ensures that consumers will receive the results, versus a -/// plain Signal, where the results might be sent before any observers are -/// attached. -/// -/// Because of the behavior of `start()`, different Signals created from the -/// producer may see a different version of Events. The Events may arrive in a -/// different order between Signals, or the stream might be completely -/// different! -public struct SignalProducer { - public typealias ProducedSignal = Signal - - private let startHandler: (Signal.Observer, CompositeDisposable) -> Void - - /// Initializes a `SignalProducer` that will emit the same events as the - /// given signal. - /// - /// If the Disposable returned from `start()` is disposed or a terminating - /// event is sent to the observer, the given signal will be disposed. - /// - /// - parameters: - /// - signal: A signal to observe after starting the producer. - public init(signal: S) where S.Value == Value, S.Error == Error { - self.init { observer, disposable in - disposable += signal.observe(observer) - } - } - - /// Initializes a SignalProducer that will invoke the given closure once for - /// each invocation of `start()`. - /// - /// The events that the closure puts into the given observer will become - /// the events sent by the started `Signal` to its observers. - /// - /// - note: If the `Disposable` returned from `start()` is disposed or a - /// terminating event is sent to the observer, the given - /// `CompositeDisposable` will be disposed, at which point work - /// should be interrupted and any temporary resources cleaned up. - /// - /// - parameters: - /// - startHandler: A closure that accepts observer and a disposable. - public init(_ startHandler: @escaping (Signal.Observer, CompositeDisposable) -> Void) { - self.startHandler = startHandler - } - - /// Creates a producer for a `Signal` that will immediately send one value - /// then complete. - /// - /// - parameters: - /// - value: A value that should be sent by the `Signal` in a `next` - /// event. - public init(value: Value) { - self.init { observer, disposable in - observer.sendNext(value) - observer.sendCompleted() - } - } - - /// Creates a producer for a `Signal` that will immediately fail with the - /// given error. - /// - /// - parameters: - /// - error: An error that should be sent by the `Signal` in a `failed` - /// event. - public init(error: Error) { - self.init { observer, disposable in - observer.sendFailed(error) - } - } - - /// Creates a producer for a Signal that will immediately send one value - /// then complete, or immediately fail, depending on the given Result. - /// - /// - parameters: - /// - result: A `Result` instance that will send either `next` event if - /// `result` is `success`ful or `failed` event if `result` is a - /// `failure`. - public init(result: Result) { - switch result { - case let .success(value): - self.init(value: value) - - case let .failure(error): - self.init(error: error) - } - } - - /// Creates a producer for a Signal that will immediately send the values - /// from the given sequence, then complete. - /// - /// - parameters: - /// - values: A sequence of values that a `Signal` will send as separate - /// `next` events and then complete. - public init(values: S) where S.Iterator.Element == Value { - self.init { observer, disposable in - for value in values { - observer.sendNext(value) - - if disposable.isDisposed { - break - } - } - - observer.sendCompleted() - } - } - - /// Creates a producer for a Signal that will immediately send the values - /// from the given sequence, then complete. - /// - /// - parameters: - /// - first: First value for the `Signal` to send. - /// - second: Second value for the `Signal` to send. - /// - tail: Rest of the values to be sent by the `Signal`. - public init(values first: Value, _ second: Value, _ tail: Value...) { - self.init(values: [ first, second ] + tail) - } - - /// A producer for a Signal that will immediately complete without sending - /// any values. - public static var empty: SignalProducer { - return self.init { observer, disposable in - observer.sendCompleted() - } - } - - /// A producer for a Signal that never sends any events to its observers. - public static var never: SignalProducer { - return self.init { _ in return } - } - - /// Create a `SignalProducer` that will attempt the given operation once for - /// each invocation of `start()`. - /// - /// Upon success, the started signal will send the resulting value then - /// complete. Upon failure, the started signal will fail with the error that - /// occurred. - /// - /// - parameters: - /// - operation: A closure that returns instance of `Result`. - /// - /// - returns: A `SignalProducer` that will forward `success`ful `result` as - /// `next` event and then complete or `failed` event if `result` - /// is a `failure`. - public static func attempt(_ operation: @escaping () -> Result) -> SignalProducer { - return self.init { observer, disposable in - operation().analysis(ifSuccess: { value in - observer.sendNext(value) - observer.sendCompleted() - }, ifFailure: { error in - observer.sendFailed(error) - }) - } - } - - /// Create a Signal from the producer, pass it into the given closure, - /// then start sending events on the Signal when the closure has returned. - /// - /// The closure will also receive a disposable which can be used to - /// interrupt the work associated with the signal and immediately send an - /// `interrupted` event. - /// - /// - parameters: - /// - setUp: A closure that accepts a `signal` and `interrupter`. - public func startWithSignal(_ setup: (_ signal: Signal, _ interrupter: Disposable) -> Void) { - let (signal, observer) = Signal.pipe() - - // Disposes of the work associated with the SignalProducer and any - // upstream producers. - let producerDisposable = CompositeDisposable() - - // Directly disposed of when `start()` or `startWithSignal()` is - // disposed. - let cancelDisposable = ActionDisposable { - observer.sendInterrupted() - producerDisposable.dispose() - } - - setup(signal, cancelDisposable) - - if cancelDisposable.isDisposed { - return - } - - let wrapperObserver: Signal.Observer = Observer { event in - observer.action(event) - - if event.isTerminating { - // Dispose only after notifying the Signal, so disposal - // logic is consistently the last thing to run. - producerDisposable.dispose() - } - } - - startHandler(wrapperObserver, producerDisposable) - } -} - -public protocol SignalProducerProtocol { - /// The type of values being sent on the producer - associatedtype Value - /// The type of error that can occur on the producer. If errors aren't possible - /// then `NoError` can be used. - associatedtype Error: Swift.Error - - /// Extracts a signal producer from the receiver. - var producer: SignalProducer { get } - - /// Initialize a signal - init(_ startHandler: @escaping (Signal.Observer, CompositeDisposable) -> Void) - - /// Creates a Signal from the producer, passes it into the given closure, - /// then starts sending events on the Signal when the closure has returned. - func startWithSignal(_ setup: (_ signal: Signal, _ interrupter: Disposable) -> Void) -} - -extension SignalProducer: SignalProducerProtocol { - public var producer: SignalProducer { - return self - } -} - -extension SignalProducerProtocol { - /// Create a Signal from the producer, then attach the given observer to - /// the `Signal` as an observer. - /// - /// - parameters: - /// - observer: An observer to attach to produced signal. - /// - /// - returns: A `Disposable` which can be used to interrupt the work - /// associated with the signal and immediately send an - /// `interrupted` event. - @discardableResult - public func start(_ observer: Signal.Observer = .init()) -> Disposable { - var disposable: Disposable! - - startWithSignal { signal, innerDisposable in - signal.observe(observer) - disposable = innerDisposable - } - - return disposable - } - - /// Convenience override for start(_:) to allow trailing-closure style - /// invocations. - /// - /// - parameters: - /// - observerAction: A closure that accepts `Event` sent by the produced - /// signal. - /// - /// - returns: A `Disposable` which can be used to interrupt the work - /// associated with the signal and immediately send an - /// `interrupted` event. - @discardableResult - public func start(_ observerAction: @escaping Signal.Observer.Action) -> Disposable { - return start(Observer(observerAction)) - } - - /// Create a Signal from the producer, then add an observer to the `Signal`, - /// which will invoke the given callback when `next` or `failed` events are - /// received. - /// - /// - parameters: - /// - result: A closure that accepts a `result` that contains a `.success` - /// case for `next` events or `.failure` case for `failed` event. - /// - /// - returns: A Disposable which can be used to interrupt the work - /// associated with the Signal, and prevent any future callbacks - /// from being invoked. - @discardableResult - public func startWithResult(_ result: @escaping (Result) -> Void) -> Disposable { - return start( - Observer( - next: { result(.success($0)) }, - failed: { result(.failure($0)) } - ) - ) - } - - /// Create a Signal from the producer, then add exactly one observer to the - /// Signal, which will invoke the given callback when a `completed` event is - /// received. - /// - /// - parameters: - /// - completed: A closure that will be envoked when produced signal sends - /// `completed` event. - /// - /// - returns: A `Disposable` which can be used to interrupt the work - /// associated with the signal. - @discardableResult - public func startWithCompleted(_ completed: @escaping () -> Void) -> Disposable { - return start(Observer(completed: completed)) - } - - /// Creates a Signal from the producer, then adds exactly one observer to - /// the Signal, which will invoke the given callback when a `failed` event - /// is received. - /// - /// - parameters: - /// - failed: A closure that accepts an error object. - /// - /// - returns: A `Disposable` which can be used to interrupt the work - /// associated with the signal. - @discardableResult - public func startWithFailed(_ failed: @escaping (Error) -> Void) -> Disposable { - return start(Observer(failed: failed)) - } - - /// Creates a Signal from the producer, then adds exactly one observer to - /// the Signal, which will invoke the given callback when an `interrupted` - /// event is received. - /// - /// - parameters: - /// - interrupted: A closure that is invoked when `interrupted` event is - /// received. - /// - /// - returns: A `Disposable` which can be used to interrupt the work - /// associated with the signal. - @discardableResult - public func startWithInterrupted(_ interrupted: @escaping () -> Void) -> Disposable { - return start(Observer(interrupted: interrupted)) - } -} - -extension SignalProducerProtocol where Error == NoError { - /// Create a Signal from the producer, then add exactly one observer to - /// the Signal, which will invoke the given callback when `next` events are - /// received. - /// - /// - parameters: - /// - next: A closure that accepts a value carried by `next` event. - /// - /// - returns: A `Disposable` which can be used to interrupt the work - /// associated with the Signal, and prevent any future callbacks - /// from being invoked. - @discardableResult - public func startWithNext(_ next: @escaping (Value) -> Void) -> Disposable { - return start(Observer(next: next)) - } -} - -extension SignalProducerProtocol { - /// Lift an unary Signal operator to operate upon SignalProducers instead. - /// - /// In other words, this will create a new `SignalProducer` which will apply - /// the given `Signal` operator to _every_ created `Signal`, just as if the - /// operator had been applied to each `Signal` yielded from `start()`. - /// - /// - parameters: - /// - transform: An unary operator to lift. - /// - /// - returns: A signal producer that applies signal's operator to every - /// created signal. - public func lift(_ transform: @escaping (Signal) -> Signal) -> SignalProducer { - return SignalProducer { observer, outerDisposable in - self.startWithSignal { signal, innerDisposable in - outerDisposable += innerDisposable - - transform(signal).observe(observer) - } - } - } - - - /// Lift a binary Signal operator to operate upon SignalProducers instead. - /// - /// In other words, this will create a new `SignalProducer` which will apply - /// the given `Signal` operator to _every_ `Signal` created from the two - /// producers, just as if the operator had been applied to each `Signal` - /// yielded from `start()`. - /// - /// - note: starting the returned producer will start the receiver of the - /// operator, which may not be adviseable for some operators. - /// - /// - parameters: - /// - transform: A binary operator to lift. - /// - /// - returns: A binary operator that operates on two signal producers. - public func lift(_ transform: @escaping (Signal) -> (Signal) -> Signal) -> (SignalProducer) -> SignalProducer { - return liftRight(transform) - } - - /// Right-associative lifting of a binary signal operator over producers. - /// That is, the argument producer will be started before the receiver. When - /// both producers are synchronous this order can be important depending on - /// the operator to generate correct results. - private func liftRight(_ transform: @escaping (Signal) -> (Signal) -> Signal) -> (SignalProducer) -> SignalProducer { - return { otherProducer in - return SignalProducer { observer, outerDisposable in - self.startWithSignal { signal, disposable in - outerDisposable.add(disposable) - - otherProducer.startWithSignal { otherSignal, otherDisposable in - outerDisposable += otherDisposable - - transform(signal)(otherSignal).observe(observer) - } - } - } - } - } - - /// Left-associative lifting of a binary signal operator over producers. - /// That is, the receiver will be started before the argument producer. When - /// both producers are synchronous this order can be important depending on - /// the operator to generate correct results. - private func liftLeft(_ transform: @escaping (Signal) -> (Signal) -> Signal) -> (SignalProducer) -> SignalProducer { - return { otherProducer in - return SignalProducer { observer, outerDisposable in - otherProducer.startWithSignal { otherSignal, otherDisposable in - outerDisposable += otherDisposable - - self.startWithSignal { signal, disposable in - outerDisposable.add(disposable) - - transform(signal)(otherSignal).observe(observer) - } - } - } - } - } - - - /// Lift a binary Signal operator to operate upon a Signal and a - /// SignalProducer instead. - /// - /// In other words, this will create a new `SignalProducer` which will apply - /// the given `Signal` operator to _every_ `Signal` created from the two - /// producers, just as if the operator had been applied to each `Signal` - /// yielded from `start()`. - /// - /// - parameters: - /// - transform: A binary operator to lift. - /// - /// - returns: A binary operator that works on `Signal` and returns - /// `SignalProducer`. - public func lift(_ transform: @escaping (Signal) -> (Signal) -> Signal) -> (Signal) -> SignalProducer { - return { otherSignal in - return SignalProducer { observer, outerDisposable in - let (wrapperSignal, otherSignalObserver) = Signal.pipe() - - // Avoid memory leak caused by the direct use of the given - // signal. - // - // See https://github.com/ReactiveCocoa/ReactiveCocoa/pull/2758 - // for the details. - outerDisposable += ActionDisposable { - otherSignalObserver.sendInterrupted() - } - outerDisposable += otherSignal.observe(otherSignalObserver) - - self.startWithSignal { signal, disposable in - outerDisposable += disposable - outerDisposable += transform(signal)(wrapperSignal).observe(observer) - } - } - } - } - - - /// Map each value in the producer to a new value. - /// - /// - parameters: - /// - transform: A closure that accepts a value and returns a different - /// value. - /// - /// - returns: A signal producer that, when started, will send a mapped - /// value of `self.` - public func map(_ transform: @escaping (Value) -> U) -> SignalProducer { - return lift { $0.map(transform) } - } - - /// Map errors in the producer to a new error. - /// - /// - parameters: - /// - transform: A closure that accepts an error object and returns a - /// different error. - /// - /// - returns: A producer that emits errors of new type. - public func mapError(_ transform: @escaping (Error) -> F) -> SignalProducer { - return lift { $0.mapError(transform) } - } - - /// Preserve only the values of the producer that pass the given predicate. - /// - /// - parameters: - /// - predicate: A closure that accepts value and returns `Bool` denoting - /// whether value has passed the test. - /// - /// - returns: A producer that, when started, will send only the values - /// passing the given predicate. - public func filter(_ predicate: @escaping (Value) -> Bool) -> SignalProducer { - return lift { $0.filter(predicate) } - } - - /// Yield the first `count` values from the input producer. - /// - /// - precondition: `count` must be non-negative number. - /// - /// - parameters: - /// - count: A number of values to take from the signal. - /// - /// - returns: A producer that, when started, will yield the first `count` - /// values from `self`. - public func take(first count: Int) -> SignalProducer { - return lift { $0.take(first: count) } - } - - /// Yield an array of values when `self` completes. - /// - /// - note: When `self` completes without collecting any value, it will send - /// an empty array of values. - /// - /// - returns: A producer that, when started, will yield an array of values - /// when `self` completes. - public func collect() -> SignalProducer<[Value], Error> { - return lift { $0.collect() } - } - - /// Yield an array of values until it reaches a certain count. - /// - /// - precondition: `count` should be greater than zero. - /// - /// - note: When the count is reached the array is sent and the signal - /// starts over yielding a new array of values. - /// - /// - note: When `self` completes any remaining values will be sent, the - /// last array may not have `count` values. Alternatively, if were - /// not collected any values will sent an empty array of values. - /// - /// - returns: A producer that, when started, collects at most `count` - /// values from `self`, forwards them as a single array and - /// completes. - public func collect(count: Int) -> SignalProducer<[Value], Error> { - precondition(count > 0) - return lift { $0.collect(count: count) } - } - - /// Yield an array of values based on a predicate which matches the values - /// collected. - /// - /// - note: When `self` completes any remaining values will be sent, the - /// last array may not match `predicate`. Alternatively, if were not - /// collected any values will sent an empty array of values. - /// - /// ```` - /// let (producer, observer) = SignalProducer.buffer(1) - /// - /// producer - /// .collect { values in values.reduce(0, combine: +) == 8 } - /// .startWithNext { print($0) } - /// - /// observer.sendNext(1) - /// observer.sendNext(3) - /// observer.sendNext(4) - /// observer.sendNext(7) - /// observer.sendNext(1) - /// observer.sendNext(5) - /// observer.sendNext(6) - /// observer.sendCompleted() - /// - /// // Output: - /// // [1, 3, 4] - /// // [7, 1] - /// // [5, 6] - /// ```` - /// - /// - parameters: - /// - predicate: Predicate to match when values should be sent (returning - /// `true`) or alternatively when they should be collected - /// (where it should return `false`). The most recent value - /// (`next`) is included in `values` and will be the end of - /// the current array of values if the predicate returns - /// `true`. - /// - /// - returns: A producer that, when started, collects values passing the - /// predicate and, when `self` completes, forwards them as a - /// single array and complets. - public func collect(_ predicate: @escaping (_ values: [Value]) -> Bool) -> SignalProducer<[Value], Error> { - return lift { $0.collect(predicate) } - } - - /// Yield an array of values based on a predicate which matches the values - /// collected and the next value. - /// - /// - note: When `self` completes any remaining values will be sent, the - /// last array may not match `predicate`. Alternatively, if no - /// values were collected an empty array will be sent. - /// - /// ```` - /// let (producer, observer) = SignalProducer.buffer(1) - /// - /// producer - /// .collect { values, next in next == 7 } - /// .startWithNext { print($0) } - /// - /// observer.sendNext(1) - /// observer.sendNext(1) - /// observer.sendNext(7) - /// observer.sendNext(7) - /// observer.sendNext(5) - /// observer.sendNext(6) - /// observer.sendCompleted() - /// - /// // Output: - /// // [1, 1] - /// // [7] - /// // [7, 5, 6] - /// ```` - /// - /// - parameters: - /// - predicate: Predicate to match when values should be sent (returning - /// `true`) or alternatively when they should be collected - /// (where it should return `false`). The most recent value - /// (`next`) is not included in `values` and will be the - /// start of the next array of values if the predicate - /// returns `true`. - /// - /// - returns: A signal that will yield an array of values based on a - /// predicate which matches the values collected and the next - /// value. - public func collect(_ predicate: @escaping (_ values: [Value], _ next: Value) -> Bool) -> SignalProducer<[Value], Error> { - return lift { $0.collect(predicate) } - } - - /// Forward all events onto the given scheduler, instead of whichever - /// scheduler they originally arrived upon. - /// - /// - parameters: - /// - scheduler: A scheduler to deliver events on. - /// - /// - returns: A producer that, when started, will yield `self` values on - /// provided scheduler. - public func observe(on scheduler: SchedulerProtocol) -> SignalProducer { - return lift { $0.observe(on: scheduler) } - } - - /// Combine the latest value of the receiver with the latest value from the - /// given producer. - /// - /// - note: The returned producer will not send a value until both inputs - /// have sent at least one value each. - /// - /// - note: If either producer is interrupted, the returned producer will - /// also be interrupted. - /// - /// - parameters: - /// - other: A producer to combine `self`'s value with. - /// - /// - returns: A producer that, when started, will yield a tuple containing - /// values of `self` and given producer. - public func combineLatest(with other: SignalProducer) -> SignalProducer<(Value, U), Error> { - return liftLeft(Signal.combineLatest)(other) - } - - /// Combine the latest value of the receiver with the latest value from - /// the given signal. - /// - /// - note: The returned producer will not send a value until both inputs - /// have sent at least one value each. - /// - /// - note: If either input is interrupted, the returned producer will also - /// be interrupted. - /// - /// - parameters: - /// - other: A signal to combine `self`'s value with. - /// - /// - returns: A producer that, when started, will yield a tuple containing - /// values of `self` and given signal. - public func combineLatest(with other: Signal) -> SignalProducer<(Value, U), Error> { - return lift(Signal.combineLatest(with:))(other) - } - - /// Delay `next` and `completed` events by the given interval, forwarding - /// them on the given scheduler. - /// - /// - note: `failed` and `interrupted` events are always scheduled - /// immediately. - /// - /// - parameters: - /// - interval: Interval to delay `next` and `completed` events by. - /// - scheduler: A scheduler to deliver delayed events on. - /// - /// - returns: A producer that, when started, will delay `next` and - /// `completed` events and will yield them on given scheduler. - public func delay(_ interval: TimeInterval, on scheduler: DateSchedulerProtocol) -> SignalProducer { - return lift { $0.delay(interval, on: scheduler) } - } - - /// Skip the first `count` values, then forward everything afterward. - /// - /// - parameters: - /// - count: A number of values to skip. - /// - /// - returns: A producer that, when started, will skip the first `count` - /// values, then forward everything afterward. - public func skip(first count: Int) -> SignalProducer { - return lift { $0.skip(first: count) } - } - - /// Treats all Events from the input producer as plain values, allowing them - /// to be manipulated just like any other value. - /// - /// In other words, this brings Events “into the monad.” - /// - /// - note: When a Completed or Failed event is received, the resulting - /// producer will send the Event itself and then complete. When an - /// `interrupted` event is received, the resulting producer will - /// send the `Event` itself and then interrupt. - /// - /// - returns: A producer that sends events as its values. - public func materialize() -> SignalProducer, NoError> { - return lift { $0.materialize() } - } - - /// Forward the latest value from `self` with the value from `sampler` as a - /// tuple, only when `sampler` sends a `next` event. - /// - /// - note: If `sampler` fires before a value has been observed on `self`, - /// nothing happens. - /// - /// - parameters: - /// - sampler: A producer that will trigger the delivery of `next` event - /// from `self`. - /// - /// - returns: A producer that will send values from `self` and `sampler`, - /// sampled (possibly multiple times) by `sampler`, then complete - /// once both input producers have completed, or interrupt if - /// either input producer is interrupted. - public func sample(with sampler: SignalProducer) -> SignalProducer<(Value, T), Error> { - return liftLeft(Signal.sample(with:))(sampler) - } - - /// Forward the latest value from `self` with the value from `sampler` as a - /// tuple, only when `sampler` sends a `next` event. - /// - /// - note: If `sampler` fires before a value has been observed on `self`, - /// nothing happens. - /// - /// - parameters: - /// - sampler: A signal that will trigger the delivery of `next` event - /// from `self`. - /// - /// - returns: A producer that, when started, will send values from `self` - /// and `sampler`, sampled (possibly multiple times) by - /// `sampler`, then complete once both input producers have - /// completed, or interrupt if either input producer is - /// interrupted. - public func sample(with sampler: Signal) -> SignalProducer<(Value, T), Error> { - return lift(Signal.sample(with:))(sampler) - } - - /// Forward the latest value from `self` whenever `sampler` sends a `next` - /// event. - /// - /// - note: If `sampler` fires before a value has been observed on `self`, - /// nothing happens. - /// - /// - parameters: - /// - sampler: A producer that will trigger the delivery of `next` event - /// from `self`. - /// - /// - returns: A producer that, when started, will send values from `self`, - /// sampled (possibly multiple times) by `sampler`, then complete - /// once both input producers have completed, or interrupt if - /// either input producer is interrupted. - public func sample(on sampler: SignalProducer<(), NoError>) -> SignalProducer { - return liftLeft(Signal.sample(on:))(sampler) - } - - /// Forward the latest value from `self` whenever `sampler` sends a `next` - /// event. - /// - /// - note: If `sampler` fires before a value has been observed on `self`, - /// nothing happens. - /// - /// - parameters: - /// - trigger: A signal whose `next` or `completed` events will start the - /// deliver of events on `self`. - /// - /// - returns: A producer that will send values from `self`, sampled - /// (possibly multiple times) by `sampler`, then complete once - /// both inputs have completed, or interrupt if either input is - /// interrupted. - public func sample(on sampler: Signal<(), NoError>) -> SignalProducer { - return lift(Signal.sample(on:))(sampler) - } - - /// Forwards events from `self` until `lifetime` ends, at which point the - /// returned producer will complete. - /// - /// - parameters: - /// - lifetime: A lifetime whose `ended` signal will cause the returned - /// producer to complete. - /// - /// - returns: A producer that will deliver events until `lifetime` ends. - public func take(during lifetime: Lifetime) -> SignalProducer { - return take(until: lifetime.ended) - } - - /// Forward events from `self` until `trigger` sends a `next` or `completed` - /// event, at which point the returned producer will complete. - /// - /// - parameters: - /// - trigger: A producer whose `next` or `completed` events will stop the - /// delivery of `next` events from `self`. - /// - /// - returns: A producer that will deliver events until `trigger` sends - /// `next` or `completed` events. - public func take(until trigger: SignalProducer<(), NoError>) -> SignalProducer { - // This should be the implementation of this method: - // return liftRight(Signal.takeUntil)(trigger) - // - // However, due to a Swift miscompilation (with `-O`) we need to inline - // `liftRight` here. - // - // See https://github.com/ReactiveCocoa/ReactiveCocoa/issues/2751 for - // more details. - // - // This can be reverted once tests with -O work correctly. - return SignalProducer { observer, outerDisposable in - self.startWithSignal { signal, disposable in - outerDisposable.add(disposable) - - trigger.startWithSignal { triggerSignal, triggerDisposable in - outerDisposable += triggerDisposable - - signal.take(until: triggerSignal).observe(observer) - } - } - } - } - - /// Forward events from `self` until `trigger` sends a Next or Completed - /// event, at which point the returned producer will complete. - /// - /// - parameters: - /// - trigger: A signal whose `next` or `completed` events will stop the - /// delivery of `next` events from `self`. - /// - /// - returns: A producer that will deliver events until `trigger` sends - /// `next` or `completed` events. - public func take(until trigger: Signal<(), NoError>) -> SignalProducer { - return lift(Signal.take(until:))(trigger) - } - - /// Do not forward any values from `self` until `trigger` sends a `next` - /// or `completed`, at which point the returned producer behaves exactly - /// like `producer`. - /// - /// - parameters: - /// - trigger: A producer whose `next` or `completed` events will start - /// the deliver of events on `self`. - /// - /// - returns: A producer that will deliver events once the `trigger` sends - /// `next` or `completed` events. - public func skip(until trigger: SignalProducer<(), NoError>) -> SignalProducer { - return liftRight(Signal.skip(until:))(trigger) - } - - /// Do not forward any values from `self` until `trigger` sends a `next` - /// or `completed`, at which point the returned signal behaves exactly like - /// `signal`. - /// - /// - parameters: - /// - trigger: A signal whose `next` or `completed` events will start the - /// deliver of events on `self`. - /// - /// - returns: A producer that will deliver events once the `trigger` sends - /// `next` or `completed` events. - public func skip(until trigger: Signal<(), NoError>) -> SignalProducer { - return lift(Signal.skip(until:))(trigger) - } - - /// Forward events from `self` with history: values of the returned producer - /// are a tuple whose first member is the previous value and whose second - /// member is the current value. `initial` is supplied as the first member - /// when `self` sends its first value. - /// - /// - parameters: - /// - initial: A value that will be combined with the first value sent by - /// `self`. - /// - /// - returns: A producer that sends tuples that contain previous and - /// current sent values of `self`. - public func combinePrevious(_ initial: Value) -> SignalProducer<(Value, Value), Error> { - return lift { $0.combinePrevious(initial) } - } - - /// Send only the final value and then immediately completes. - /// - /// - parameters: - /// - initial: Initial value for the accumulator. - /// - combine: A closure that accepts accumulator and sent value of - /// `self`. - /// - /// - returns: A producer that sends accumulated value after `self` - /// completes. - public func reduce(_ initial: U, _ combine: @escaping (U, Value) -> U) -> SignalProducer { - return lift { $0.reduce(initial, combine) } - } - - /// Aggregate `self`'s values into a single combined value. When `self` - /// emits its first value, `combine` is invoked with `initial` as the first - /// argument and that emitted value as the second argument. The result is - /// emitted from the producer returned from `scan`. That result is then - /// passed to `combine` as the first argument when the next value is - /// emitted, and so on. - /// - /// - parameters: - /// - initial: Initial value for the accumulator. - /// - combine: A closure that accepts accumulator and sent value of - /// `self`. - /// - /// - returns: A producer that sends accumulated value each time `self` - /// emits own value. - public func scan(_ initial: U, _ combine: @escaping (U, Value) -> U) -> SignalProducer { - return lift { $0.scan(initial, combine) } - } - - /// Forward only those values from `self` which do not pass `isRepeat` with - /// respect to the previous value. - /// - /// - note: The first value is always forwarded. - /// - /// - returns: A producer that does not send two equal values sequentially. - public func skipRepeats(_ isRepeat: @escaping (Value, Value) -> Bool) -> SignalProducer { - return lift { $0.skipRepeats(isRepeat) } - } - - /// Do not forward any values from `self` until `predicate` returns false, - /// at which point the returned producer behaves exactly like `self`. - /// - /// - parameters: - /// - predicate: A closure that accepts a value and returns whether `self` - /// should still not forward that value to a `producer`. - /// - /// - returns: A producer that sends only forwarded values from `self`. - public func skip(while predicate: @escaping (Value) -> Bool) -> SignalProducer { - return lift { $0.skip(while: predicate) } - } - - /// Forward events from `self` until `replacement` begins sending events. - /// - /// - parameters: - /// - replacement: A producer to wait to wait for values from and start - /// sending them as a replacement to `self`'s values. - /// - /// - returns: A producer which passes through `next`, `failed`, and - /// `interrupted` events from `self` until `replacement` sends an - /// event, at which point the returned producer will send that - /// event and switch to passing through events from `replacement` - /// instead, regardless of whether `self` has sent events - /// already. - public func take(untilReplacement signal: SignalProducer) -> SignalProducer { - return liftRight(Signal.take(untilReplacement:))(signal) - } - - /// Forwards events from `self` until `replacement` begins sending events. - /// - /// - parameters: - /// - replacement: A signal to wait to wait for values from and start - /// sending them as a replacement to `self`'s values. - /// - /// - returns: A producer which passes through `next`, `failed`, and - /// `interrupted` events from `self` until `replacement` sends an - /// event, at which point the returned producer will send that - /// event and switch to passing through events from `replacement` - /// instead, regardless of whether `self` has sent events - /// already. - public func take(untilReplacement signal: Signal) -> SignalProducer { - return lift(Signal.take(untilReplacement:))(signal) - } - - /// Wait until `self` completes and then forward the final `count` values - /// on the returned producer. - /// - /// - parameters: - /// - count: Number of last events to send after `self` completes. - /// - /// - returns: A producer that receives up to `count` values from `self` - /// after `self` completes. - public func take(last count: Int) -> SignalProducer { - return lift { $0.take(last: count) } - } - - /// Forward any values from `self` until `predicate` returns false, at which - /// point the returned producer will complete. - /// - /// - parameters: - /// - predicate: A closure that accepts value and returns `Bool` value - /// whether `self` should forward it to `signal` and continue - /// sending other events. - /// - /// - returns: A producer that sends events until the values sent by `self` - /// pass the given `predicate`. - public func take(while predicate: @escaping (Value) -> Bool) -> SignalProducer { - return lift { $0.take(while: predicate) } - } - - /// Zip elements of two producers into pairs. The elements of any Nth pair - /// are the Nth elements of the two input producers. - /// - /// - parameters: - /// - other: A producer to zip values with. - /// - /// - returns: A producer that sends tuples of `self` and `otherProducer`. - public func zip(with other: SignalProducer) -> SignalProducer<(Value, U), Error> { - return liftLeft(Signal.zip(with:))(other) - } - - /// Zip elements of this producer and a signal into pairs. The elements of - /// any Nth pair are the Nth elements of the two. - /// - /// - parameters: - /// - other: A signal to zip values with. - /// - /// - returns: A producer that sends tuples of `self` and `otherSignal`. - public func zip(with other: Signal) -> SignalProducer<(Value, U), Error> { - return lift(Signal.zip(with:))(other) - } - - /// Apply `operation` to values from `self` with `success`ful results - /// forwarded on the returned producer and `failure`s sent as `failed` - /// events. - /// - /// - parameters: - /// - operation: A closure that accepts a value and returns a `Result`. - /// - /// - returns: A producer that receives `success`ful `Result` as `next` - /// event and `failure` as `failed` event. - public func attempt(operation: @escaping (Value) -> Result<(), Error>) -> SignalProducer { - return lift { $0.attempt(operation) } - } - - /// Apply `operation` to values from `self` with `success`ful results - /// mapped on the returned producer and `failure`s sent as `failed` events. - /// - /// - parameters: - /// - operation: A closure that accepts a value and returns a result of - /// a mapped value as `success`. - /// - /// - returns: A producer that sends mapped values from `self` if returned - /// `Result` is `success`ful, `failed` events otherwise. - public func attemptMap(_ operation: @escaping (Value) -> Result) -> SignalProducer { - return lift { $0.attemptMap(operation) } - } - - /// Throttle values sent by the receiver, so that at least `interval` - /// seconds pass between each, then forwards them on the given scheduler. - /// - /// - note: If multiple values are received before the interval has elapsed, - /// the latest value is the one that will be passed on. - /// - /// - norw: If `self` terminates while a value is being throttled, that - /// value will be discarded and the returned producer will terminate - /// immediately. - /// - /// - parameters: - /// - interval: Number of seconds to wait between sent values. - /// - scheduler: A scheduler to deliver events on. - /// - /// - returns: A producer that sends values at least `interval` seconds - /// appart on a given scheduler. - public func throttle(_ interval: TimeInterval, on scheduler: DateSchedulerProtocol) -> SignalProducer { - return lift { $0.throttle(interval, on: scheduler) } - } - - /// Debounce values sent by the receiver, such that at least `interval` - /// seconds pass after the receiver has last sent a value, then - /// forward the latest value on the given scheduler. - /// - /// - note: If multiple values are received before the interval has elapsed, - /// the latest value is the one that will be passed on. - /// - /// - note: If `self` terminates while a value is being debounced, - /// that value will be discarded and the returned producer will - /// terminate immediately. - /// - /// - parameters: - /// - interval: A number of seconds to wait before sending a value. - /// - scheduler: A scheduler to send values on. - /// - /// - returns: A producer that sends values that are sent from `self` at - /// least `interval` seconds apart. - public func debounce(_ interval: TimeInterval, on scheduler: DateSchedulerProtocol) -> SignalProducer { - return lift { $0.debounce(interval, on: scheduler) } - } - - /// Forward events from `self` until `interval`. Then if producer isn't - /// completed yet, fails with `error` on `scheduler`. - /// - /// - note: 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. - /// - /// - parameters: - /// - interval: Number of seconds to wait for `self` to complete. - /// - error: Error to send with `failed` event if `self` is not completed - /// when `interval` passes. - /// - scheduler: A scheduler to deliver error on. - /// - /// - returns: A producer that sends events for at most `interval` seconds, - /// then, if not `completed` - sends `error` with `failed` event - /// on `scheduler`. - public func timeout(after interval: TimeInterval, raising error: Error, on scheduler: DateSchedulerProtocol) -> SignalProducer { - return lift { $0.timeout(after: interval, raising: error, on: scheduler) } - } -} - -extension SignalProducerProtocol where Value: OptionalProtocol { - /// Unwraps non-`nil` values and forwards them on the returned signal, `nil` - /// values are dropped. - /// - /// - returns: A producer that sends only non-nil values. - public func skipNil() -> SignalProducer { - return lift { $0.skipNil() } - } -} - -extension SignalProducerProtocol where Value: EventProtocol, Error == NoError { - /// The inverse of materialize(), this will translate a producer of `Event` - /// _values_ into a producer of those events themselves. - /// - /// - returns: A producer that sends values carried by `self` events. - public func dematerialize() -> SignalProducer { - return lift { $0.dematerialize() } - } -} - -extension SignalProducerProtocol where Error == NoError { - /// Promote a producer that does not generate failures into one that can. - /// - /// - note: This does not actually cause failers to be generated for the - /// given producer, but makes it easier to combine with other - /// producers that may fail; for example, with operators like - /// `combineLatestWith`, `zipWith`, `flatten`, etc. - /// - /// - parameters: - /// - _ An `ErrorType`. - /// - /// - returns: A producer that has an instantiatable `ErrorType`. - public func promoteErrors(_: F.Type) -> SignalProducer { - return lift { $0.promoteErrors(F.self) } - } - - /// Forward events from `self` until `interval`. Then if producer isn't - /// completed yet, fails with `error` on `scheduler`. - /// - /// - note: 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. - /// - /// - parameters: - /// - interval: Number of seconds to wait for `self` to complete. - /// - error: Error to send with `failed` event if `self` is not completed - /// when `interval` passes. - /// - scheudler: A scheduler to deliver error on. - /// - /// - returns: A producer that sends events for at most `interval` seconds, - /// then, if not `completed` - sends `error` with `failed` event - /// on `scheduler`. - public func timeout( - after interval: TimeInterval, - raising error: NewError, - on scheduler: DateSchedulerProtocol - ) -> SignalProducer { - return lift { $0.timeout(after: interval, raising: error, on: scheduler) } - } - - /// Wait for completion of `self`, *then* forward all events from - /// `replacement`. - /// - /// - note: All values sent from `self` are ignored. - /// - /// - parameters: - /// - replacement: A producer to start when `self` completes. - /// - /// - returns: A producer that sends events from `self` and then from - /// `replacement` when `self` completes. - public func then(_ replacement: SignalProducer) -> SignalProducer { - return self - .promoteErrors(NewError.self) - .then(replacement) - } -} - -extension SignalProducerProtocol where Value: Equatable { - /// Forward only those values from `self` which are not duplicates of the - /// immedately preceding value. - /// - /// - note: The first value is always forwarded. - /// - /// - returns: A producer that does not send two equal values sequentially. - public func skipRepeats() -> SignalProducer { - return lift { $0.skipRepeats() } - } -} - -extension SignalProducerProtocol { - /// Forward only those values from `self` that have unique identities across - /// the set of all values that have been seen. - /// - /// - note: This causes the identities to be retained to check for - /// uniqueness. - /// - /// - parameters: - /// - transform: A closure that accepts a value and returns identity - /// value. - /// - /// - returns: A producer that sends unique values during its lifetime. - public func uniqueValues(_ transform: @escaping (Value) -> Identity) -> SignalProducer { - return lift { $0.uniqueValues(transform) } - } -} - -extension SignalProducerProtocol where Value: Hashable { - /// Forward only those values from `self` that are unique across the set of - /// all values that have been seen. - /// - /// - note: This causes the values to be retained to check for uniqueness. - /// Providing a function that returns a unique value for each sent - /// value can help you reduce the memory footprint. - /// - /// - returns: A producer that sends unique values during its lifetime. - public func uniqueValues() -> SignalProducer { - return lift { $0.uniqueValues() } - } -} - -extension SignalProducerProtocol { - /// Injects side effects to be performed upon the specified producer events. - /// - /// - note: In a composed producer, `starting` is invoked in the reverse - /// direction of the flow of events. - /// - /// - parameters: - /// - starting: A closure that is invoked before the producer is started. - /// - started: A closure that is invoked after the producer is started. - /// - event: A closure that accepts an event and is invoked on every - /// received event. - /// - next: A closure that accepts a value from `next` event. - /// - failed: A closure that accepts error object and is invoked for - /// `failed` event. - /// - completed: A closure that is invoked for `completed` event. - /// - interrupted: A closure that is invoked for `interrupted` event. - /// - terminated: A closure that is invoked for any terminating event. - /// - disposed: A closure added as disposable when signal completes. - /// - /// - returns: A producer with attached side-effects for given event cases. - public func on( - starting: (() -> Void)? = nil, - started: (() -> Void)? = nil, - event: ((Event) -> Void)? = nil, - next: ((Value) -> Void)? = nil, - failed: ((Error) -> Void)? = nil, - completed: (() -> Void)? = nil, - interrupted: (() -> Void)? = nil, - terminated: (() -> Void)? = nil, - disposed: (() -> Void)? = nil - ) -> SignalProducer { - return SignalProducer { observer, compositeDisposable in - starting?() - defer { started?() } - - self.startWithSignal { signal, disposable in - compositeDisposable += disposable - compositeDisposable += signal - .on( - event: event, - failed: failed, - completed: completed, - interrupted: interrupted, - terminated: terminated, - disposed: disposed, - next: next - ) - .observe(observer) - } - } - } - - /// Start the returned producer on the given `Scheduler`. - /// - /// - note: This implies that any side effects embedded in the producer will - /// be performed on the given scheduler as well. - /// - /// - note: Events may still be sent upon other schedulers — this merely - /// affects where the `start()` method is run. - /// - /// - parameters: - /// - scheduler: A scheduler to deliver events on. - /// - /// - returns: A producer that will deliver events on given `scheduler` when - /// started. - public func start(on scheduler: SchedulerProtocol) -> SignalProducer { - return SignalProducer { observer, compositeDisposable in - compositeDisposable += scheduler.schedule { - self.startWithSignal { signal, signalDisposable in - compositeDisposable += signalDisposable - signal.observe(observer) - } - } - } - } -} - -extension SignalProducerProtocol { - /// Combines the values of all the given producers, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: SignalProducer, _ b: SignalProducer) -> SignalProducer<(Value, B), Error> { - return a.combineLatest(with: b) - } - - /// Combines the values of all the given producers, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer) -> SignalProducer<(Value, B, C), Error> { - return combineLatest(a, b) - .combineLatest(with: c) - .map(repack) - } - - /// Combines the values of all the given producers, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer) -> SignalProducer<(Value, B, C, D), Error> { - return combineLatest(a, b, c) - .combineLatest(with: d) - .map(repack) - } - - /// Combines the values of all the given producers, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer) -> SignalProducer<(Value, B, C, D, E), Error> { - return combineLatest(a, b, c, d) - .combineLatest(with: e) - .map(repack) - } - - /// Combines the values of all the given producers, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F), Error> { - return combineLatest(a, b, c, d, e) - .combineLatest(with: f) - .map(repack) - } - - /// Combines the values of all the given producers, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer, _ g: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F, G), Error> { - return combineLatest(a, b, c, d, e, f) - .combineLatest(with: g) - .map(repack) - } - - /// Combines the values of all the given producers, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer, _ g: SignalProducer, _ h: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F, G, H), Error> { - return combineLatest(a, b, c, d, e, f, g) - .combineLatest(with: h) - .map(repack) - } - - /// Combines the values of all the given producers, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer, _ g: SignalProducer, _ h: SignalProducer, _ i: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F, G, H, I), Error> { - return combineLatest(a, b, c, d, e, f, g, h) - .combineLatest(with: i) - .map(repack) - } - - /// Combines the values of all the given producers, in the manner described by - /// `combineLatestWith`. - public static func combineLatest(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer, _ g: SignalProducer, _ h: SignalProducer, _ i: SignalProducer, _ j: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F, G, H, I, J), Error> { - return combineLatest(a, b, c, d, e, f, g, h, i) - .combineLatest(with: j) - .map(repack) - } - - /// Combines the values of all the given producers, in the manner described by - /// `combineLatestWith`. Will return an empty `SignalProducer` if the sequence is empty. - public static func combineLatest(_ producers: S) -> SignalProducer<[Value], Error> - where S.Iterator.Element == SignalProducer - { - var generator = producers.makeIterator() - if let first = generator.next() { - let initial = first.map { [$0] } - return IteratorSequence(generator).reduce(initial) { producer, next in - producer.combineLatest(with: next).map { $0.0 + [$0.1] } - } - } - - return .empty - } - - /// Zips the values of all the given producers, in the manner described by - /// `zipWith`. - public static func zip(_ a: SignalProducer, _ b: SignalProducer) -> SignalProducer<(Value, B), Error> { - return a.zip(with: b) - } - - /// Zips the values of all the given producers, in the manner described by - /// `zipWith`. - public static func zip(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer) -> SignalProducer<(Value, B, C), Error> { - return zip(a, b) - .zip(with: c) - .map(repack) - } - - /// Zips the values of all the given producers, in the manner described by - /// `zipWith`. - public static func zip(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer) -> SignalProducer<(Value, B, C, D), Error> { - return zip(a, b, c) - .zip(with: d) - .map(repack) - } - - /// Zips the values of all the given producers, in the manner described by - /// `zipWith`. - public static func zip(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer) -> SignalProducer<(Value, B, C, D, E), Error> { - return zip(a, b, c, d) - .zip(with: e) - .map(repack) - } - - /// Zips the values of all the given producers, in the manner described by - /// `zipWith`. - public static func zip(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F), Error> { - return zip(a, b, c, d, e) - .zip(with: f) - .map(repack) - } - - /// Zips the values of all the given producers, in the manner described by - /// `zipWith`. - public static func zip(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer, _ g: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F, G), Error> { - return zip(a, b, c, d, e, f) - .zip(with: g) - .map(repack) - } - - /// Zips the values of all the given producers, in the manner described by - /// `zipWith`. - public static func zip(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer, _ g: SignalProducer, _ h: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F, G, H), Error> { - return zip(a, b, c, d, e, f, g) - .zip(with: h) - .map(repack) - } - - /// Zips the values of all the given producers, in the manner described by - /// `zipWith`. - public static func zip(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer, _ g: SignalProducer, _ h: SignalProducer, _ i: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F, G, H, I), Error> { - return zip(a, b, c, d, e, f, g, h) - .zip(with: i) - .map(repack) - } - - /// Zips the values of all the given producers, in the manner described by - /// `zipWith`. - public static func zip(_ a: SignalProducer, _ b: SignalProducer, _ c: SignalProducer, _ d: SignalProducer, _ e: SignalProducer, _ f: SignalProducer, _ g: SignalProducer, _ h: SignalProducer, _ i: SignalProducer, _ j: SignalProducer) -> SignalProducer<(Value, B, C, D, E, F, G, H, I, J), Error> { - return zip(a, b, c, d, e, f, g, h, i) - .zip(with: j) - .map(repack) - } - - /// Zips the values of all the given producers, in the manner described by - /// `zipWith`. Will return an empty `SignalProducer` if the sequence is empty. - public static func zip(_ producers: S) -> SignalProducer<[Value], Error> - where S.Iterator.Element == SignalProducer - { - var generator = producers.makeIterator() - if let first = generator.next() { - let initial = first.map { [$0] } - return IteratorSequence(generator).reduce(initial) { producer, next in - producer.zip(with: next).map { $0.0 + [$0.1] } - } - } - - return .empty - } -} - -extension SignalProducerProtocol { - /// Repeat `self` a total of `count` times. In other words, start producer - /// `count` number of times, each one after previously started producer - /// completes. - /// - /// - note: Repeating `1` time results in an equivalent signal producer. - /// - /// - note: Repeating `0` times results in a producer that instantly - /// completes. - /// - /// - parameters: - /// - count: Number of repetitions. - /// - /// - returns: A signal producer start sequentially starts `self` after - /// previously started producer completes. - public func times(_ count: Int) -> SignalProducer { - precondition(count >= 0) - - if count == 0 { - return .empty - } else if count == 1 { - return producer - } - - return SignalProducer { observer, disposable in - let serialDisposable = SerialDisposable() - disposable += serialDisposable - - func iterate(_ current: Int) { - self.startWithSignal { signal, signalDisposable in - serialDisposable.innerDisposable = signalDisposable - - signal.observe { event in - if case .completed = event { - let remainingTimes = current - 1 - if remainingTimes > 0 { - iterate(remainingTimes) - } else { - observer.sendCompleted() - } - } else { - observer.action(event) - } - } - } - } - - iterate(count) - } - } - - /// Ignore failures up to `count` times. - /// - /// - precondition: `count` must be non-negative integer. - /// - /// - parameters: - /// - count: Number of retries. - /// - /// - returns: A signal producer that restarts up to `count` times. - public func retry(upTo count: Int) -> SignalProducer { - precondition(count >= 0) - - if count == 0 { - return producer - } else { - return flatMapError { _ in - self.retry(upTo: count - 1) - } - } - } - - /// Wait for completion of `self`, *then* forward all events from - /// `replacement`. Any failure or interruption sent from `self` is - /// forwarded immediately, in which case `replacement` will not be started, - /// and none of its events will be be forwarded. - /// - /// - note: All values sent from `self` are ignored. - /// - /// - parameters: - /// - replacement: A producer to start when `self` completes. - /// - /// - returns: A producer that sends events from `self` and then from - /// `replacement` when `self` completes. - public func then(_ replacement: SignalProducer) -> SignalProducer { - return SignalProducer { observer, observerDisposable in - self.startWithSignal { signal, signalDisposable in - observerDisposable += signalDisposable - - signal.observe { event in - switch event { - case let .failed(error): - observer.sendFailed(error) - case .completed: - observerDisposable += replacement.start(observer) - case .interrupted: - observer.sendInterrupted() - case .next: - break - } - } - } - } - } - - /// Wait for completion of `self`, *then* forward all events from - /// `replacement`. Any failure or interruption sent from `self` is - /// forwarded immediately, in which case `replacement` will not be started, - /// and none of its events will be be forwarded. - /// - /// - note: All values sent from `self` are ignored. - /// - /// - parameters: - /// - replacement: A producer to start when `self` completes. - /// - /// - returns: A producer that sends events from `self` and then from - /// `replacement` when `self` completes. - public func then(_ replacement: SignalProducer) -> SignalProducer { - return self.then(replacement.promoteErrors(Error.self)) - } - - /// Start the producer, then block, waiting for the first value. - /// - /// When a single value or error is sent, the returned `Result` will - /// represent those cases. However, when no values are sent, `nil` will be - /// returned. - /// - /// - returns: Result when single `next` or `failed` event is received. - /// `nil` when no events are received. - public func first() -> Result? { - return take(first: 1).single() - } - - /// Start the producer, then block, waiting for events: Next and - /// Completed. - /// - /// When a single value or error is sent, the returned `Result` will - /// represent those cases. However, when no values are sent, or when more - /// than one value is sent, `nil` will be returned. - /// - /// - returns: Result when single `next` or `failed` event is received. - /// `nil` when 0 or more than 1 events are received. - public func single() -> Result? { - let semaphore = DispatchSemaphore(value: 0) - var result: Result? - - take(first: 2).start { event in - switch event { - case let .next(value): - if result != nil { - // Move into failure state after recieving another value. - result = nil - return - } - result = .success(value) - case let .failed(error): - result = .failure(error) - semaphore.signal() - case .completed, .interrupted: - semaphore.signal() - } - } - - semaphore.wait() - return result - } - - /// Start the producer, then block, waiting for the last value. - /// - /// When a single value or error is sent, the returned `Result` will - /// represent those cases. However, when no values are sent, `nil` will be - /// returned. - /// - /// - returns: Result when single `next` or `failed` event is received. - /// `nil` when no events are received. - public func last() -> Result? { - return take(last: 1).single() - } - - /// Starts the producer, then blocks, waiting for completion. - /// - /// When a completion or error is sent, the returned `Result` will represent - /// those cases. - /// - /// - returns: Result when single `completion` or `failed` event is - /// received. - public func wait() -> Result<(), Error> { - return then(SignalProducer<(), Error>(value: ())).last() ?? .success(()) - } - - /// Creates a new `SignalProducer` that will multicast values emitted by - /// the underlying producer, up to `capacity`. - /// This means that all clients of this `SignalProducer` will see the same - /// version of the emitted values/errors. - /// - /// The underlying `SignalProducer` will not be started until `self` is - /// started for the first time. When subscribing to this producer, all - /// previous values (up to `capacity`) will be emitted, followed by any new - /// values. - /// - /// If you find yourself needing *the current value* (the last buffered - /// value) you should consider using `PropertyType` instead, which, unlike - /// this operator, will guarantee at compile time that there's always a - /// buffered value. This operator is not recommended in most cases, as it - /// will introduce an implicit relationship between the original client and - /// the rest, so consider alternatives like `PropertyType`, or representing - /// your stream using a `Signal` instead. - /// - /// This operator is only recommended when you absolutely need to introduce - /// a layer of caching in front of another `SignalProducer`. - /// - /// - precondtion: `capacity` must be non-negative integer. - /// - /// - parameters: - /// - capcity: Number of values to hold. - /// - /// - returns: A caching producer that will hold up to last `capacity` - /// values. - public func replayLazily(upTo capacity: Int) -> SignalProducer { - precondition(capacity >= 0, "Invalid capacity: \(capacity)") - - // This will go "out of scope" when the returned `SignalProducer` goes - // out of scope. This lets us know when we're supposed to dispose the - // underlying producer. This is necessary because `struct`s don't have - // `deinit`. - let lifetimeToken = Lifetime.Token() - let lifetime = Lifetime(lifetimeToken) - - let state = Atomic(ReplayState(upTo: capacity)) - - let start: Atomic<(() -> Void)?> = Atomic { - // Start the underlying producer. - self - .take(during: lifetime) - .start { event in - let observers: Bag.Observer>? = state.modify { state in - defer { state.enqueue(event) } - return state.observers - } - observers?.forEach { $0.action(event) } - } - } - - return SignalProducer { observer, disposable in - // Don't dispose of the original producer until all observers - // have terminated. - disposable += { _ = lifetimeToken } - - while true { - var result: Result>! - state.modify { - result = $0.observe(observer) - } - - switch result! { - case let .success(token): - if let token = token { - disposable += { - state.modify { - $0.removeObserver(using: token) - } - } - } - - // Start the underlying producer if it has never been started. - start.swap(nil)?() - - // Terminate the replay loop. - return - - case let .failure(error): - error.values.forEach(observer.sendNext) - } - } - } - } -} - -/// Represents a recoverable error of an observer not being ready for an -/// attachment to a `ReplayState`, and the observer should replay the supplied -/// values before attempting to observe again. -private struct ReplayError: Error { - /// The values that should be replayed by the observer. - let values: [Value] -} - -private struct ReplayState { - let capacity: Int - - /// All cached values. - var values: [Value] = [] - - /// A termination event emitted by the underlying producer. - /// - /// This will be nil if termination has not occurred. - var terminationEvent: Event? - - /// The observers currently attached to the caching producer, or `nil` if the - /// caching producer was terminated. - var observers: Bag.Observer>? = Bag() - - /// The set of in-flight replay buffers. - var replayBuffers: [ObjectIdentifier: [Value]] = [:] - - /// Initialize the replay state. - /// - /// - parameters: - /// - capacity: The maximum amount of values which can be cached by the - /// replay state. - init(upTo capacity: Int) { - self.capacity = capacity - } - - /// Attempt to observe the replay state. - /// - /// - warning: Repeatedly observing the replay state with the same observer - /// should be avoided. - /// - /// - parameters: - /// - observer: The observer to be registered. - /// - /// - returns: - /// If the observer is successfully attached, a `Result.success` with the - /// corresponding removal token would be returned. Otherwise, a - /// `Result.failure` with a `ReplayError` would be returned. - mutating func observe(_ observer: Signal.Observer) -> Result> { - // Since the only use case is `replayLazily`, which always creates a unique - // `Observer` for every produced signal, we can use the ObjectIdentifier of - // the `Observer` to track them directly. - let id = ObjectIdentifier(observer) - - switch replayBuffers[id] { - case .none where !values.isEmpty: - // No in-flight replay buffers was found, but the `ReplayState` has one or - // more cached values in the `ReplayState`. The observer should replay - // them before attempting to observe again. - replayBuffers[id] = [] - return .failure(ReplayError(values: values)) - - case let .some(buffer) where !buffer.isEmpty: - // An in-flight replay buffer was found with one or more buffered values. - // The observer should replay them before attempting to observe again. - defer { replayBuffers[id] = [] } - return .failure(ReplayError(values: buffer)) - - case let .some(buffer) where buffer.isEmpty: - // Since an in-flight but empty replay buffer was found, the observer is - // ready to be attached to the `ReplayState`. - replayBuffers.removeValue(forKey: id) - - default: - // No values has to be replayed. The observer is ready to be attached to - // the `ReplayState`. - break - } - - if let event = terminationEvent { - observer.action(event) - } - - return .success(observers?.insert(observer)) - } - - /// Enqueue the supplied event to the replay state. - /// - /// - parameter: - /// - event: The event to be cached. - mutating func enqueue(_ event: Event) { - switch event { - case let .next(value): - for key in replayBuffers.keys { - replayBuffers[key]!.append(value) - } - - switch capacity { - case 0: - // With a capacity of zero, `state.values` can never be filled. - break - - case 1: - values = [value] - - default: - values.append(value) - - let overflow = values.count - capacity - if overflow > 0 { - values.removeFirst(overflow) - } - } - - case .completed, .failed, .interrupted: - // Disconnect all observers and prevent future attachments. - terminationEvent = event - observers = nil - } - } - - /// Remove the observer represented by the supplied token. - /// - /// - parameters: - /// - token: The token of the observer to be removed. - mutating func removeObserver(using token: RemovalToken) { - observers?.remove(using: token) - } -} - -/// Create a repeating timer of the given interval, with a reasonable default -/// leeway, sending updates on the given scheduler. -/// -/// - note: This timer will never complete naturally, so all invocations of -/// `start()` must be disposed to avoid leaks. -/// -/// - precondition: Interval must be non-negative number. -/// -/// - parameters: -/// - interval: An interval between invocations. -/// - scheduler: A scheduler to deliver events on. -/// -/// - returns: A producer that sends `NSDate` values every `interval` seconds. -public func timer(interval: TimeInterval, on scheduler: DateSchedulerProtocol) -> SignalProducer { - // Apple's "Power Efficiency Guide for Mac Apps" recommends a leeway of - // at least 10% of the timer interval. - return timer(interval: interval, on: scheduler, leeway: interval * 0.1) -} - -/// Creates a repeating timer of the given interval, sending updates on the -/// given scheduler. -/// -/// - note: This timer will never complete naturally, so all invocations of -/// `start()` must be disposed to avoid leaks. -/// -/// - precondition: Interval must be non-negative number. -/// -/// - precondition: Leeway must be non-negative number. -/// -/// - parameters: -/// - interval: An interval between invocations. -/// - scheduler: A scheduler to deliver events on. -/// - leeway: Interval leeway. Apple's "Power Efficiency Guide for Mac Apps" -/// recommends a leeway of at least 10% of the timer interval. -/// -/// - returns: A producer that sends `NSDate` values every `interval` seconds. -public func timer(interval: TimeInterval, on scheduler: DateSchedulerProtocol, leeway: TimeInterval) -> SignalProducer { - precondition(interval >= 0) - precondition(leeway >= 0) - - return SignalProducer { observer, compositeDisposable in - compositeDisposable += scheduler.schedule(after: scheduler.currentDate.addingTimeInterval(interval), - interval: interval, - leeway: leeway, - action: { observer.sendNext(scheduler.currentDate) }) - } -} diff --git a/ReactiveSwift/TupleExtensions.swift b/ReactiveSwift/TupleExtensions.swift deleted file mode 100644 index f976369328..0000000000 --- a/ReactiveSwift/TupleExtensions.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// TupleExtensions.swift -// ReactiveSwift -// -// Created by Justin Spahr-Summers on 2014-12-20. -// Copyright (c) 2014 GitHub. All rights reserved. -// - -/// Adds a value into an N-tuple, returning an (N+1)-tuple. -/// -/// Supports creating tuples up to 10 elements long. -internal func repack(_ t: (A, B), value: C) -> (A, B, C) { - return (t.0, t.1, value) -} - -internal func repack(_ t: (A, B, C), value: D) -> (A, B, C, D) { - return (t.0, t.1, t.2, value) -} - -internal func repack(_ t: (A, B, C, D), value: E) -> (A, B, C, D, E) { - return (t.0, t.1, t.2, t.3, value) -} - -internal func repack(_ t: (A, B, C, D, E), value: F) -> (A, B, C, D, E, F) { - return (t.0, t.1, t.2, t.3, t.4, value) -} - -internal func repack(_ t: (A, B, C, D, E, F), value: G) -> (A, B, C, D, E, F, G) { - return (t.0, t.1, t.2, t.3, t.4, t.5, value) -} - -internal func repack(_ t: (A, B, C, D, E, F, G), value: H) -> (A, B, C, D, E, F, G, H) { - return (t.0, t.1, t.2, t.3, t.4, t.5, t.6, value) -} - -internal func repack(_ t: (A, B, C, D, E, F, G, H), value: I) -> (A, B, C, D, E, F, G, H, I) { - return (t.0, t.1, t.2, t.3, t.4, t.5, t.6, t.7, value) -} - -internal func repack(_ t: (A, B, C, D, E, F, G, H, I), value: J) -> (A, B, C, D, E, F, G, H, I, J) { - return (t.0, t.1, t.2, t.3, t.4, t.5, t.6, t.7, t.8, value) -} diff --git a/ReactiveSwift/UnidirectionalBinding.swift b/ReactiveSwift/UnidirectionalBinding.swift deleted file mode 100644 index a782f31414..0000000000 --- a/ReactiveSwift/UnidirectionalBinding.swift +++ /dev/null @@ -1,278 +0,0 @@ -import enum Result.NoError - -precedencegroup BindingPrecedence { - associativity: right - - // Binds tighter than assignment but looser than everything else - higherThan: AssignmentPrecedence -} - -infix operator <~ : BindingPrecedence - -/// Describes a target to which can be bound. -public protocol BindingTarget: class { - associatedtype Value - - /// The lifetime of `self`. The binding operators use this to determine when - /// the binding should be teared down. - var lifetime: Lifetime { get } - - /// Consume a value from the binding. - func consume(_ value: Value) - - /// Binds a signal to a target, updating the target's value to the latest - /// value sent by the signal. - /// - /// - note: The binding will automatically terminate when the target is - /// deinitialized, or when the signal sends a `completed` event. - /// - /// ```` - /// let property = MutableProperty(0) - /// let signal = Signal({ /* do some work after some time */ }) - /// property <~ signal - /// ```` - /// - /// ```` - /// let property = MutableProperty(0) - /// let signal = Signal({ /* do some work after some time */ }) - /// let disposable = property <~ signal - /// ... - /// // Terminates binding before property dealloc or signal's - /// // `completed` event. - /// disposable.dispose() - /// ```` - /// - /// - parameters: - /// - target: A target to be bond to. - /// - signal: A signal to bind. - /// - /// - returns: A disposable that can be used to terminate binding before the - /// deinitialization of the target or the signal's `completed` - /// event. - @discardableResult - static func <~ (target: Self, signal: Source) -> Disposable? where Source.Value == Value, Source.Error == NoError -} - -extension BindingTarget { - /// Binds a signal to a target, updating the target's value to the latest - /// value sent by the signal. - /// - /// - note: The binding will automatically terminate when the target is - /// deinitialized, or when the signal sends a `completed` event. - /// - /// ```` - /// let property = MutableProperty(0) - /// let signal = Signal({ /* do some work after some time */ }) - /// property <~ signal - /// ```` - /// - /// ```` - /// let property = MutableProperty(0) - /// let signal = Signal({ /* do some work after some time */ }) - /// let disposable = property <~ signal - /// ... - /// // Terminates binding before property dealloc or signal's - /// // `completed` event. - /// disposable.dispose() - /// ```` - /// - /// - parameters: - /// - target: A target to be bond to. - /// - signal: A signal to bind. - /// - /// - returns: A disposable that can be used to terminate binding before the - /// deinitialization of the target or the signal's `completed` - /// event. - @discardableResult - public static func <~ (target: Self, signal: Source) -> Disposable? where Source.Value == Value, Source.Error == NoError { - return signal - .take(during: target.lifetime) - .observeNext { [weak target] value in - target?.consume(value) - } - } - - /// Binds a producer to a target, updating the target's value to the latest - /// value sent by the producer. - /// - /// - note: The binding will automatically terminate when the target is - /// deinitialized, or when the producer sends a `completed` event. - /// - /// ```` - /// let property = MutableProperty(0) - /// let producer = SignalProducer(value: 1) - /// property <~ producer - /// print(property.value) // prints `1` - /// ```` - /// - /// ```` - /// let property = MutableProperty(0) - /// let producer = SignalProducer({ /* do some work after some time */ }) - /// let disposable = (property <~ producer) - /// ... - /// // Terminates binding before property dealloc or - /// // signal's `completed` event. - /// disposable.dispose() - /// ```` - /// - /// - parameters: - /// - target: A target to be bond to. - /// - producer: A producer to bind. - /// - /// - returns: A disposable that can be used to terminate binding before the - /// deinitialization of the target or the producer's `completed - /// event. - @discardableResult - public static func <~ (target: Self, producer: Source) -> Disposable where Source.Value == Value, Source.Error == NoError { - var disposable: Disposable! - - producer - .take(during: target.lifetime) - .startWithSignal { signal, signalDisposable in - disposable = signalDisposable - target <~ signal - } - - return disposable - } - - /// Binds a property to a target, updating the target's value to the latest - /// value sent by the property. - /// - /// - note: The binding will automatically terminate when either the target or - /// the property deinitializes. - /// - /// ```` - /// let dstProperty = MutableProperty(0) - /// let srcProperty = ConstantProperty(10) - /// dstProperty <~ srcProperty - /// print(dstProperty.value) // prints 10 - /// ```` - /// - /// ```` - /// let dstProperty = MutableProperty(0) - /// let srcProperty = ConstantProperty(10) - /// let disposable = (dstProperty <~ srcProperty) - /// ... - /// disposable.dispose() // terminate the binding earlier if - /// // needed - /// ```` - /// - /// - parameters: - /// - target: A target to be bond to. - /// - property: A property to bind. - /// - /// - returns: A disposable that can be used to terminate binding before the - /// deinitialization of the target or the source property. - @discardableResult - public static func <~ (target: Self, property: Source) -> Disposable where Source.Value == Value { - return target <~ property.producer - } -} - -extension BindingTarget where Value: OptionalProtocol { - /// Binds a signal to a target, updating the target's value to the latest - /// value sent by the signal. - /// - /// - note: The binding will automatically terminate when the target is - /// deinitialized, or when the signal sends a `completed` event. - /// - /// ```` - /// let property = MutableProperty(0) - /// let signal = Signal({ /* do some work after some time */ }) - /// property <~ signal - /// ```` - /// - /// ```` - /// let property = MutableProperty(0) - /// let signal = Signal({ /* do some work after some time */ }) - /// let disposable = property <~ signal - /// ... - /// // Terminates binding before property dealloc or signal's - /// // `completed` event. - /// disposable.dispose() - /// ```` - /// - /// - parameters: - /// - target: A target to be bond to. - /// - signal: A signal to bind. - /// - /// - returns: A disposable that can be used to terminate binding before the - /// deinitialization of the target or the signal's `completed` - /// event. - @discardableResult - public static func <~ (target: Self, signal: Source) -> Disposable? where Source.Value == Value.Wrapped, Source.Error == NoError { - return target <~ signal.map(Value.init(reconstructing:)) - } - - /// Binds a producer to a target, updating the target's value to the latest - /// value sent by the producer. - /// - /// - note: The binding will automatically terminate when the target is - /// deinitialized, or when the producer sends a `completed` event. - /// - /// ```` - /// let property = MutableProperty(0) - /// let producer = SignalProducer(value: 1) - /// property <~ producer - /// print(property.value) // prints `1` - /// ```` - /// - /// ```` - /// let property = MutableProperty(0) - /// let producer = SignalProducer({ /* do some work after some time */ }) - /// let disposable = (property <~ producer) - /// ... - /// // Terminates binding before property dealloc or - /// // signal's `completed` event. - /// disposable.dispose() - /// ```` - /// - /// - parameters: - /// - target: A target to be bond to. - /// - producer: A producer to bind. - /// - /// - returns: A disposable that can be used to terminate binding before the - /// deinitialization of the target or the producer's `completed` - /// event. - @discardableResult - public static func <~ (target: Self, producer: Source) -> Disposable where Source.Value == Value.Wrapped, Source.Error == NoError { - return target <~ producer.map(Value.init(reconstructing:)) - } - - /// Binds a property to a target, updating the target's value to the latest - /// value sent by the property. - /// - /// - note: The binding will automatically terminate when either the target or - /// the property deinitializes. - /// - /// ```` - /// let dstProperty = MutableProperty(0) - /// let srcProperty = ConstantProperty(10) - /// dstProperty <~ srcProperty - /// print(dstProperty.value) // prints 10 - /// ```` - /// - /// ```` - /// let dstProperty = MutableProperty(0) - /// let srcProperty = ConstantProperty(10) - /// let disposable = (dstProperty <~ srcProperty) - /// ... - /// disposable.dispose() // terminate the binding earlier if - /// // needed - /// ```` - /// - /// - note: The binding will automatically terminate when either property is - /// deinitialized. - /// - /// - parameters: - /// - target: A target to be bond to. - /// - property: A property to bind. - /// - /// - returns: A disposable that can be used to terminate binding before the - /// deinitialization of the target or the source property. - @discardableResult - public static func <~ (target: Self, property: Source) -> Disposable where Source.Value == Value.Wrapped { - return target <~ property.producer - } -} diff --git a/ReactiveSwiftTests/ActionSpec.swift b/ReactiveSwiftTests/ActionSpec.swift deleted file mode 100755 index 7a56cd5501..0000000000 --- a/ReactiveSwiftTests/ActionSpec.swift +++ /dev/null @@ -1,142 +0,0 @@ -// -// ActionSpec.swift -// ReactiveSwift -// -// Created by Justin Spahr-Summers on 2014-12-11. -// Copyright (c) 2014 GitHub. All rights reserved. -// - -import Result -import Nimble -import Quick -import ReactiveSwift - -class ActionSpec: QuickSpec { - override func spec() { - describe("Action") { - var action: Action! - var enabled: MutableProperty! - - var executionCount = 0 - var values: [String] = [] - var errors: [NSError] = [] - - var scheduler: TestScheduler! - let testError = NSError(domain: "ActionSpec", code: 1, userInfo: nil) - - beforeEach { - executionCount = 0 - values = [] - errors = [] - enabled = MutableProperty(false) - - scheduler = TestScheduler() - action = Action(enabledIf: enabled) { number in - return SignalProducer { observer, disposable in - executionCount += 1 - - if number % 2 == 0 { - observer.sendNext("\(number)") - observer.sendNext("\(number)\(number)") - - scheduler.schedule { - observer.sendCompleted() - } - } else { - scheduler.schedule { - observer.sendFailed(testError) - } - } - } - } - - action.values.observeNext { values.append($0) } - action.errors.observeNext { errors.append($0) } - } - - it("should be disabled and not executing after initialization") { - expect(action.isEnabled.value) == false - expect(action.isExecuting.value) == false - } - - it("should error if executed while disabled") { - var receivedError: ActionError? - action.apply(0).startWithFailed { - receivedError = $0 - } - - expect(receivedError).notTo(beNil()) - if let error = receivedError { - let expectedError = ActionError.disabled - expect(error == expectedError) == true - } - } - - it("should enable and disable based on the given property") { - enabled.value = true - expect(action.isEnabled.value) == true - expect(action.isExecuting.value) == false - - enabled.value = false - expect(action.isEnabled.value) == false - expect(action.isExecuting.value) == false - } - - describe("execution") { - beforeEach { - enabled.value = true - } - - it("should execute successfully") { - var receivedValue: String? - - action.apply(0) - .assumeNoErrors() - .startWithNext { - receivedValue = $0 - } - - expect(executionCount) == 1 - expect(action.isExecuting.value) == true - expect(action.isEnabled.value) == false - - expect(receivedValue) == "00" - expect(values) == [ "0", "00" ] - expect(errors) == [] - - scheduler.run() - expect(action.isExecuting.value) == false - expect(action.isEnabled.value) == true - - expect(values) == [ "0", "00" ] - expect(errors) == [] - } - - it("should execute with an error") { - var receivedError: ActionError? - - action.apply(1).startWithFailed { - receivedError = $0 - } - - expect(executionCount) == 1 - expect(action.isExecuting.value) == true - expect(action.isEnabled.value) == false - - scheduler.run() - expect(action.isExecuting.value) == false - expect(action.isEnabled.value) == true - - expect(receivedError).notTo(beNil()) - if let error = receivedError { - let expectedError = ActionError.producerFailed(testError) - expect(error == expectedError) == true - } - - expect(values) == [] - expect(errors) == [ testError ] - } - } - } - } -} diff --git a/ReactiveSwiftTests/AtomicSpec.swift b/ReactiveSwiftTests/AtomicSpec.swift deleted file mode 100644 index 8234f5b9f4..0000000000 --- a/ReactiveSwiftTests/AtomicSpec.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// AtomicSpec.swift -// ReactiveSwift -// -// Created by Justin Spahr-Summers on 2014-07-13. -// Copyright (c) 2014 GitHub. All rights reserved. -// - -import Nimble -import Quick -import ReactiveSwift - -class AtomicSpec: QuickSpec { - override func spec() { - var atomic: Atomic! - - beforeEach { - atomic = Atomic(1) - } - - it("should read and write the value directly") { - expect(atomic.value) == 1 - - atomic.value = 2 - expect(atomic.value) == 2 - } - - it("should swap the value atomically") { - expect(atomic.swap(2)) == 1 - expect(atomic.value) == 2 - } - - it("should modify the value atomically") { - atomic.modify { $0 += 1 } - expect(atomic.value) == 2 - } - - it("should perform an action with the value") { - let result: Bool = atomic.withValue { $0 == 1 } - expect(result) == true - expect(atomic.value) == 1 - } - } -} diff --git a/ReactiveSwiftTests/BagSpec.swift b/ReactiveSwiftTests/BagSpec.swift deleted file mode 100644 index 706e781a07..0000000000 --- a/ReactiveSwiftTests/BagSpec.swift +++ /dev/null @@ -1,54 +0,0 @@ -// -// BagSpec.swift -// ReactiveSwift -// -// Created by Justin Spahr-Summers on 2014-07-13. -// Copyright (c) 2014 GitHub. All rights reserved. -// - -import Nimble -import Quick -import ReactiveSwift - -class BagSpec: QuickSpec { - override func spec() { - var bag = Bag() - - beforeEach { - bag = Bag() - } - - it("should insert values") { - bag.insert("foo") - bag.insert("bar") - bag.insert("buzz") - - expect(bag).to(contain("foo")) - expect(bag).to(contain("bar")) - expect(bag).to(contain("buzz")) - expect(bag).toNot(contain("fuzz")) - expect(bag).toNot(contain("foobar")) - } - - it("should remove values given the token from insertion") { - let a = bag.insert("foo") - let b = bag.insert("bar") - let c = bag.insert("buzz") - - bag.remove(using: b) - expect(bag).to(contain("foo")) - expect(bag).toNot(contain("bar")) - expect(bag).to(contain("buzz")) - - bag.remove(using: a) - expect(bag).toNot(contain("foo")) - expect(bag).toNot(contain("bar")) - expect(bag).to(contain("buzz")) - - bag.remove(using: c) - expect(bag).toNot(contain("foo")) - expect(bag).toNot(contain("bar")) - expect(bag).toNot(contain("buzz")) - } - } -} diff --git a/ReactiveSwiftTests/DisposableSpec.swift b/ReactiveSwiftTests/DisposableSpec.swift deleted file mode 100644 index b8ac11cf9c..0000000000 --- a/ReactiveSwiftTests/DisposableSpec.swift +++ /dev/null @@ -1,143 +0,0 @@ -// -// DisposableSpec.swift -// ReactiveSwift -// -// Created by Justin Spahr-Summers on 2014-07-13. -// Copyright (c) 2014 GitHub. All rights reserved. -// - -import Nimble -import Quick -import ReactiveSwift - -class DisposableSpec: QuickSpec { - override func spec() { - describe("SimpleDisposable") { - it("should set disposed to true") { - let disposable = SimpleDisposable() - expect(disposable.isDisposed) == false - - disposable.dispose() - expect(disposable.isDisposed) == true - } - } - - describe("ActionDisposable") { - it("should run the given action upon disposal") { - var didDispose = false - let disposable = ActionDisposable { - didDispose = true - } - - expect(didDispose) == false - expect(disposable.isDisposed) == false - - disposable.dispose() - expect(didDispose) == true - expect(disposable.isDisposed) == true - } - } - - describe("CompositeDisposable") { - var disposable = CompositeDisposable() - - beforeEach { - disposable = CompositeDisposable() - } - - it("should ignore the addition of nil") { - disposable.add(nil) - return - } - - it("should dispose of added disposables") { - let simpleDisposable = SimpleDisposable() - disposable += simpleDisposable - - var didDispose = false - disposable += { - didDispose = true - } - - expect(simpleDisposable.isDisposed) == false - expect(didDispose) == false - expect(disposable.isDisposed) == false - - disposable.dispose() - expect(simpleDisposable.isDisposed) == true - expect(didDispose) == true - expect(disposable.isDisposed) == true - } - - it("should not dispose of removed disposables") { - let simpleDisposable = SimpleDisposable() - let handle = disposable += simpleDisposable - - // We should be allowed to call this any number of times. - handle.remove() - handle.remove() - expect(simpleDisposable.isDisposed) == false - - disposable.dispose() - expect(simpleDisposable.isDisposed) == false - } - } - - describe("ScopedDisposable") { - it("should dispose of the inner disposable upon deinitialization") { - let simpleDisposable = SimpleDisposable() - - func runScoped() { - let scopedDisposable = ScopedDisposable(simpleDisposable) - expect(simpleDisposable.isDisposed) == false - expect(scopedDisposable.isDisposed) == false - } - - expect(simpleDisposable.isDisposed) == false - - runScoped() - expect(simpleDisposable.isDisposed) == true - } - } - - describe("SerialDisposable") { - var disposable: SerialDisposable! - - beforeEach { - disposable = SerialDisposable() - } - - it("should dispose of the inner disposable") { - let simpleDisposable = SimpleDisposable() - disposable.innerDisposable = simpleDisposable - - expect(disposable.innerDisposable).notTo(beNil()) - expect(simpleDisposable.isDisposed) == false - expect(disposable.isDisposed) == false - - disposable.dispose() - expect(disposable.innerDisposable).to(beNil()) - expect(simpleDisposable.isDisposed) == true - expect(disposable.isDisposed) == true - } - - it("should dispose of the previous disposable when swapping innerDisposable") { - let oldDisposable = SimpleDisposable() - let newDisposable = SimpleDisposable() - - disposable.innerDisposable = oldDisposable - expect(oldDisposable.isDisposed) == false - expect(newDisposable.isDisposed) == false - - disposable.innerDisposable = newDisposable - expect(oldDisposable.isDisposed) == true - expect(newDisposable.isDisposed) == false - expect(disposable.isDisposed) == false - - disposable.innerDisposable = nil - expect(newDisposable.isDisposed) == true - expect(disposable.isDisposed) == false - } - } - } -} diff --git a/ReactiveSwiftTests/FlattenSpec.swift b/ReactiveSwiftTests/FlattenSpec.swift deleted file mode 100644 index bf19fdfa2f..0000000000 --- a/ReactiveSwiftTests/FlattenSpec.swift +++ /dev/null @@ -1,963 +0,0 @@ -// -// FlattenSpec.swift -// ReactiveSwift -// -// Created by Oleg Shnitko on 1/22/16. -// Copyright © 2016 GitHub. All rights reserved. -// - -import Result -import Nimble -import Quick -import ReactiveSwift - -private extension SignalProtocol { - typealias Pipe = (signal: Signal, observer: Observer) -} - -private typealias Pipe = Signal, TestError>.Pipe - -class FlattenSpec: QuickSpec { - override func spec() { - func describeSignalFlattenDisposal(_ flattenStrategy: FlattenStrategy, name: String) { - describe(name) { - var pipe: Pipe! - var disposable: Disposable? - - beforeEach { - pipe = Signal.pipe() - disposable = pipe.signal - .flatten(flattenStrategy) - .observe { _ in } - } - - afterEach { - disposable?.dispose() - } - - context("disposal") { - var disposed = false - - beforeEach { - disposed = false - pipe.observer.sendNext(SignalProducer { _, disposable in - disposable += ActionDisposable { - disposed = true - } - }) - } - - it("should dispose inner signals when outer signal interrupted") { - pipe.observer.sendInterrupted() - expect(disposed) == true - } - - it("should dispose inner signals when outer signal failed") { - pipe.observer.sendFailed(.default) - expect(disposed) == true - } - - it("should not dispose inner signals when outer signal completed") { - pipe.observer.sendCompleted() - expect(disposed) == false - } - } - } - } - - context("Signal") { - describeSignalFlattenDisposal(.latest, name: "switchToLatest") - describeSignalFlattenDisposal(.merge, name: "merge") - describeSignalFlattenDisposal(.concat, name: "concat") - } - - func describeSignalProducerFlattenDisposal(_ flattenStrategy: FlattenStrategy, name: String) { - describe(name) { - it("disposes original signal when result signal interrupted") { - var disposed = false - - let disposable = SignalProducer, NoError> { _, disposable in - disposable += ActionDisposable { - disposed = true - } - } - .flatten(flattenStrategy) - .start() - - disposable.dispose() - expect(disposed) == true - } - } - } - - context("SignalProducer") { - describeSignalProducerFlattenDisposal(.latest, name: "switchToLatest") - describeSignalProducerFlattenDisposal(.merge, name: "merge") - describeSignalProducerFlattenDisposal(.concat, name: "concat") - } - - describe("Signal.flatten()") { - it("works with TestError and a TestError Signal") { - typealias Inner = Signal - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .assumeNoErrors() - .observeNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a TestError Signal") { - typealias Inner = Signal - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .assumeNoErrors() - .observeNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a NoError Signal") { - typealias Inner = Signal - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .observeNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with TestError and a NoError Signal") { - typealias Inner = Signal - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .assumeNoErrors() - .observeNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with TestError and a TestError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .assumeNoErrors() - .observeNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a TestError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .assumeNoErrors() - .observeNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a NoError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .observeNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with TestError and a NoError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .assumeNoErrors() - .observeNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with SequenceType as a value") { - let (signal, innerObserver) = Signal<[Int], NoError>.pipe() - let sequence = [1, 2, 3] - var observedValues = [Int]() - - signal - .flatten(.concat) - .observeNext { value in - observedValues.append(value) - } - - innerObserver.sendNext(sequence) - expect(observedValues) == sequence - } - } - - describe("SignalProducer.flatten()") { - it("works with TestError and a TestError Signal") { - typealias Inner = Signal - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .assumeNoErrors() - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a TestError Signal") { - typealias Inner = Signal - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .assumeNoErrors() - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a NoError Signal") { - typealias Inner = Signal - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with TestError and a NoError Signal") { - typealias Inner = Signal - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .assumeNoErrors() - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with TestError and a TestError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .assumeNoErrors() - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a TestError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .assumeNoErrors() - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a NoError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with TestError and a NoError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatten(.latest) - .assumeNoErrors() - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(inner) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with SequenceType as a value") { - let sequence = [1, 2, 3] - var observedValues = [Int]() - - let producer = SignalProducer<[Int], NoError>(value: sequence) - producer - .flatten(.latest) - .startWithNext { value in - observedValues.append(value) - } - - expect(observedValues) == sequence - } - } - - describe("Signal.flatMap()") { - it("works with TestError and a TestError Signal") { - typealias Inner = Signal - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .assumeNoErrors() - .observeNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a TestError Signal") { - typealias Inner = Signal - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .assumeNoErrors() - .observeNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a NoError Signal") { - typealias Inner = Signal - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .observeNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with TestError and a NoError Signal") { - typealias Inner = Signal - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .assumeNoErrors() - .observeNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with TestError and a TestError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .assumeNoErrors() - .observeNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a TestError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .assumeNoErrors() - .observeNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a NoError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .observeNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with TestError and a NoError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = Signal - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .assumeNoErrors() - .observeNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - } - - describe("SignalProducer.flatMap()") { - it("works with TestError and a TestError Signal") { - typealias Inner = Signal - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .assumeNoErrors() - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a TestError Signal") { - typealias Inner = Signal - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .assumeNoErrors() - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a NoError Signal") { - typealias Inner = Signal - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with TestError and a NoError Signal") { - typealias Inner = Signal - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .assumeNoErrors() - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with TestError and a TestError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .assumeNoErrors() - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a TestError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .assumeNoErrors() - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with NoError and a NoError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - - it("works with TestError and a NoError SignalProducer") { - typealias Inner = SignalProducer - typealias Outer = SignalProducer - - let (inner, innerObserver) = Inner.pipe() - let (outer, outerObserver) = Outer.pipe() - - var observed: Int? = nil - outer - .flatMap(.latest) { _ in inner } - .assumeNoErrors() - .startWithNext { value in - observed = value - } - - outerObserver.sendNext(4) - innerObserver.sendNext(4) - expect(observed) == 4 - } - } - - describe("Signal.merge()") { - it("should emit values from all signals") { - let (signal1, observer1) = Signal.pipe() - let (signal2, observer2) = Signal.pipe() - - let mergedSignals = Signal.merge([signal1, signal2]) - - var lastValue: Int? - mergedSignals.observeNext { lastValue = $0 } - - expect(lastValue).to(beNil()) - - observer1.sendNext(1) - expect(lastValue) == 1 - - observer2.sendNext(2) - expect(lastValue) == 2 - - observer1.sendNext(3) - expect(lastValue) == 3 - } - - it("should not stop when one signal completes") { - let (signal1, observer1) = Signal.pipe() - let (signal2, observer2) = Signal.pipe() - - let mergedSignals = Signal.merge([signal1, signal2]) - - var lastValue: Int? - mergedSignals.observeNext { lastValue = $0 } - - expect(lastValue).to(beNil()) - - observer1.sendNext(1) - expect(lastValue) == 1 - - observer1.sendCompleted() - expect(lastValue) == 1 - - observer2.sendNext(2) - expect(lastValue) == 2 - } - - it("should complete when all signals complete") { - let (signal1, observer1) = Signal.pipe() - let (signal2, observer2) = Signal.pipe() - - let mergedSignals = Signal.merge([signal1, signal2]) - - var completed = false - mergedSignals.observeCompleted { completed = true } - - expect(completed) == false - - observer1.sendNext(1) - expect(completed) == false - - observer1.sendCompleted() - expect(completed) == false - - observer2.sendCompleted() - expect(completed) == true - } - } - - describe("SignalProducer.merge()") { - it("should emit values from all producers") { - let (signal1, observer1) = SignalProducer.pipe() - let (signal2, observer2) = SignalProducer.pipe() - - let mergedSignals = SignalProducer.merge([signal1, signal2]) - - var lastValue: Int? - mergedSignals.startWithNext { lastValue = $0 } - - expect(lastValue).to(beNil()) - - observer1.sendNext(1) - expect(lastValue) == 1 - - observer2.sendNext(2) - expect(lastValue) == 2 - - observer1.sendNext(3) - expect(lastValue) == 3 - } - - it("should not stop when one producer completes") { - let (signal1, observer1) = SignalProducer.pipe() - let (signal2, observer2) = SignalProducer.pipe() - - let mergedSignals = SignalProducer.merge([signal1, signal2]) - - var lastValue: Int? - mergedSignals.startWithNext { lastValue = $0 } - - expect(lastValue).to(beNil()) - - observer1.sendNext(1) - expect(lastValue) == 1 - - observer1.sendCompleted() - expect(lastValue) == 1 - - observer2.sendNext(2) - expect(lastValue) == 2 - } - - it("should complete when all producers complete") { - let (signal1, observer1) = SignalProducer.pipe() - let (signal2, observer2) = SignalProducer.pipe() - - let mergedSignals = SignalProducer.merge([signal1, signal2]) - - var completed = false - mergedSignals.startWithCompleted { completed = true } - - expect(completed) == false - - observer1.sendNext(1) - expect(completed) == false - - observer1.sendCompleted() - expect(completed) == false - - observer2.sendCompleted() - expect(completed) == true - } - } - - describe("SignalProducer.prefix()") { - it("should emit initial value") { - let (signal, observer) = SignalProducer.pipe() - - let mergedSignals = signal.prefix(value: 0) - - var lastValue: Int? - mergedSignals.startWithNext { lastValue = $0 } - - expect(lastValue) == 0 - - observer.sendNext(1) - expect(lastValue) == 1 - - observer.sendNext(2) - expect(lastValue) == 2 - - observer.sendNext(3) - expect(lastValue) == 3 - } - - it("should emit initial value") { - let (signal, observer) = SignalProducer.pipe() - - let mergedSignals = signal.prefix(SignalProducer(value: 0)) - - var lastValue: Int? - mergedSignals.startWithNext { lastValue = $0 } - - expect(lastValue) == 0 - - observer.sendNext(1) - expect(lastValue) == 1 - - observer.sendNext(2) - expect(lastValue) == 2 - - observer.sendNext(3) - expect(lastValue) == 3 - } - } - - describe("SignalProducer.concat(value:)") { - it("should emit final value") { - let (signal, observer) = SignalProducer.pipe() - - let mergedSignals = signal.concat(value: 4) - - var lastValue: Int? - mergedSignals.startWithNext { lastValue = $0 } - - observer.sendNext(1) - expect(lastValue) == 1 - - observer.sendNext(2) - expect(lastValue) == 2 - - observer.sendNext(3) - expect(lastValue) == 3 - - observer.sendCompleted() - expect(lastValue) == 4 - } - } - } -} diff --git a/ReactiveSwiftTests/FoundationExtensionsSpec.swift b/ReactiveSwiftTests/FoundationExtensionsSpec.swift deleted file mode 100644 index 249eb44a8f..0000000000 --- a/ReactiveSwiftTests/FoundationExtensionsSpec.swift +++ /dev/null @@ -1,59 +0,0 @@ -// -// FoundationExtensionsSpec.swift -// ReactiveSwift -// -// Created by Neil Pankey on 5/22/15. -// Copyright (c) 2015 GitHub. All rights reserved. -// - -import Result -import Nimble -import Quick -import ReactiveSwift - -extension Notification.Name { - static let racFirst = Notification.Name(rawValue: "rac_notifications_test") - static let racAnother = Notification.Name(rawValue: "rac_notifications_another") -} - -class FoundationExtensionsSpec: QuickSpec { - override func spec() { - describe("NSNotificationCenter.rac_notifications") { - let center = NotificationCenter.default - - it("should send notifications on the producer") { - let producer = center.rac_notifications(forName: .racFirst) - - var notif: Notification? = nil - let disposable = producer.startWithNext { notif = $0 } - - center.post(name: .racAnother, object: nil) - expect(notif).to(beNil()) - - center.post(name: .racFirst, object: nil) - expect(notif?.name) == .racFirst - - notif = nil - disposable.dispose() - - center.post(name: .racFirst, object: nil) - expect(notif).to(beNil()) - } - - it("should send Interrupted when the observed object is freed") { - var observedObject: AnyObject? = NSObject() - let producer = center.rac_notifications(forName: nil, object: observedObject) - observedObject = nil - - var interrupted = false - let disposable = producer.startWithInterrupted { - interrupted = true - } - expect(interrupted) == true - - disposable.dispose() - } - - } - } -} diff --git a/ReactiveSwiftTests/Info.plist b/ReactiveSwiftTests/Info.plist deleted file mode 100644 index ba72822e87..0000000000 --- a/ReactiveSwiftTests/Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - - diff --git a/ReactiveSwiftTests/LifetimeSpec.swift b/ReactiveSwiftTests/LifetimeSpec.swift deleted file mode 100644 index 215a9d1a48..0000000000 --- a/ReactiveSwiftTests/LifetimeSpec.swift +++ /dev/null @@ -1,47 +0,0 @@ -import Quick -import Nimble -import ReactiveSwift -import Result - -final class LifetimeSpec: QuickSpec { - override func spec() { - describe("Lifetime") { - it("should complete its lifetime ended signal when the it deinitializes") { - let object = MutableReference(TestObject()) - - var isCompleted = false - - object.value!.lifetime.ended.observeCompleted { isCompleted = true } - expect(isCompleted) == false - - object.value = nil - expect(isCompleted) == true - } - - it("should complete its lifetime ended signal even if the lifetime object is being retained") { - let object = MutableReference(TestObject()) - let lifetime = object.value!.lifetime - - var isCompleted = false - - lifetime.ended.observeCompleted { isCompleted = true } - expect(isCompleted) == false - - object.value = nil - expect(isCompleted) == true - } - } - } -} - -internal final class MutableReference { - var value: Value? - init(_ value: Value?) { - self.value = value - } -} - -internal final class TestObject { - private let token = Lifetime.Token() - var lifetime: Lifetime { return Lifetime(token) } -} diff --git a/ReactiveSwiftTests/PropertySpec.swift b/ReactiveSwiftTests/PropertySpec.swift deleted file mode 100644 index 248767199e..0000000000 --- a/ReactiveSwiftTests/PropertySpec.swift +++ /dev/null @@ -1,1569 +0,0 @@ -// -// PropertySpec.swift -// ReactiveSwift -// -// Created by Justin Spahr-Summers on 2015-01-23. -// Copyright (c) 2015 GitHub. All rights reserved. -// - -import Result -import Nimble -import Quick -@testable import ReactiveSwift - -private let initialPropertyValue = "InitialValue" -private let subsequentPropertyValue = "SubsequentValue" -private let finalPropertyValue = "FinalValue" - -private let initialOtherPropertyValue = "InitialOtherValue" -private let subsequentOtherPropertyValue = "SubsequentOtherValue" -private let finalOtherPropertyValue = "FinalOtherValue" - -class PropertySpec: QuickSpec { - override func spec() { - describe("MutableProperty") { - it("should have the value given at initialization") { - let mutableProperty = MutableProperty(initialPropertyValue) - - expect(mutableProperty.value) == initialPropertyValue - } - - it("should yield a producer that sends the current value then all changes") { - let mutableProperty = MutableProperty(initialPropertyValue) - var sentValue: String? - - mutableProperty.producer.startWithNext { sentValue = $0 } - - expect(sentValue) == initialPropertyValue - - mutableProperty.value = subsequentPropertyValue - expect(sentValue) == subsequentPropertyValue - - mutableProperty.value = finalPropertyValue - expect(sentValue) == finalPropertyValue - } - - it("should yield a producer that sends the current value then all changes, even if the value actually remains unchanged") { - let mutableProperty = MutableProperty(initialPropertyValue) - var count = 0 - - mutableProperty.producer.startWithNext { _ in count = count + 1 } - - expect(count) == 1 - - mutableProperty.value = initialPropertyValue - expect(count) == 2 - - mutableProperty.value = initialPropertyValue - expect(count) == 3 - } - - it("should yield a signal that emits subsequent changes to the value") { - let mutableProperty = MutableProperty(initialPropertyValue) - var sentValue: String? - - mutableProperty.signal.observeNext { sentValue = $0 } - - expect(sentValue).to(beNil()) - - mutableProperty.value = subsequentPropertyValue - expect(sentValue) == subsequentPropertyValue - - mutableProperty.value = finalPropertyValue - expect(sentValue) == finalPropertyValue - } - - it("should yield a signal that emits subsequent changes to the value, even if the value actually remains unchanged") { - let mutableProperty = MutableProperty(initialPropertyValue) - var count = 0 - - mutableProperty.signal.observeNext { _ in count = count + 1 } - - expect(count) == 0 - - mutableProperty.value = initialPropertyValue - expect(count) == 1 - - mutableProperty.value = initialPropertyValue - expect(count) == 2 - } - - it("should complete its producer when deallocated") { - var mutableProperty: MutableProperty? = MutableProperty(initialPropertyValue) - var producerCompleted = false - - mutableProperty!.producer.startWithCompleted { producerCompleted = true } - - mutableProperty = nil - expect(producerCompleted) == true - } - - it("should complete its signal when deallocated") { - var mutableProperty: MutableProperty? = MutableProperty(initialPropertyValue) - var signalCompleted = false - - mutableProperty!.signal.observeCompleted { signalCompleted = true } - - mutableProperty = nil - expect(signalCompleted) == true - } - - it("should yield a producer which emits the latest value and complete even if the property is deallocated") { - var mutableProperty: MutableProperty? = MutableProperty(initialPropertyValue) - let producer = mutableProperty!.producer - - var producerCompleted = false - var hasUnanticipatedEvent = false - var latestValue = mutableProperty?.value - - mutableProperty!.value = subsequentPropertyValue - mutableProperty = nil - - producer.start { event in - switch event { - case let .next(value): - latestValue = value - case .completed: - producerCompleted = true - case .interrupted, .failed: - hasUnanticipatedEvent = true - } - } - - expect(hasUnanticipatedEvent) == false - expect(producerCompleted) == true - expect(latestValue) == subsequentPropertyValue - } - - it("should modify the value atomically") { - let property = MutableProperty(initialPropertyValue) - - property.modify { $0 = subsequentPropertyValue } - expect(property.value) == subsequentPropertyValue - } - - it("should modify the value atomically and subsquently send out a Next event with the new value") { - let property = MutableProperty(initialPropertyValue) - var value: String? - - property.producer.startWithNext { - value = $0 - } - - expect(value) == initialPropertyValue - - property.modify { $0 = subsequentPropertyValue } - expect(property.value) == subsequentPropertyValue - expect(value) == subsequentPropertyValue - } - - it("should swap the value atomically") { - let property = MutableProperty(initialPropertyValue) - - expect(property.swap(subsequentPropertyValue)) == initialPropertyValue - expect(property.value) == subsequentPropertyValue - } - - it("should swap the value atomically and subsquently send out a Next event with the new value") { - let property = MutableProperty(initialPropertyValue) - var value: String? - - property.producer.startWithNext { - value = $0 - } - - expect(value) == initialPropertyValue - expect(property.swap(subsequentPropertyValue)) == initialPropertyValue - - expect(property.value) == subsequentPropertyValue - expect(value) == subsequentPropertyValue - } - - it("should perform an action with the value") { - let property = MutableProperty(initialPropertyValue) - - let result: Bool = property.withValue { $0.isEmpty } - - expect(result) == false - expect(property.value) == initialPropertyValue - } - - it("should not deadlock on recursive value access") { - let (producer, observer) = SignalProducer.pipe() - let property = MutableProperty(0) - var value: Int? - - property <~ producer - property.producer.startWithNext { _ in - value = property.value - } - - observer.sendNext(10) - expect(value) == 10 - } - - it("should not deadlock on recursive value access with a closure") { - let (producer, observer) = SignalProducer.pipe() - let property = MutableProperty(0) - var value: Int? - - property <~ producer - property.producer.startWithNext { _ in - value = property.withValue { $0 + 1 } - } - - observer.sendNext(10) - expect(value) == 11 - } - - it("should not deadlock on recursive observation") { - let property = MutableProperty(0) - - var value: Int? - property.producer.startWithNext { _ in - property.producer.startWithNext { x in value = x } - } - - expect(value) == 0 - - property.value = 1 - expect(value) == 1 - } - - it("should not deadlock on recursive ABA observation") { - let propertyA = MutableProperty(0) - let propertyB = MutableProperty(0) - - var value: Int? - propertyA.producer.startWithNext { _ in - propertyB.producer.startWithNext { _ in - propertyA.producer.startWithNext { x in value = x } - } - } - - expect(value) == 0 - - propertyA.value = 1 - expect(value) == 1 - } - - it("should expose a lifetime that ends upon its deinitialization") { - var property = Optional(MutableProperty(1)) - - var isEnded = false - property!.lifetime.ended.observeCompleted { - isEnded = true - } - - expect(isEnded) == false - - property!.value = 2 - expect(isEnded) == false - - property = nil - expect(isEnded) == true - } - } - - describe("Property") { - describe("constant property") { - it("should have the value given at initialization") { - let constantProperty = Property(value: initialPropertyValue) - - expect(constantProperty.value) == initialPropertyValue - } - - it("should yield a signal that interrupts observers without emitting any value.") { - let constantProperty = Property(value: initialPropertyValue) - - var signalInterrupted = false - var hasUnexpectedEventsEmitted = false - - constantProperty.signal.observe { event in - switch event { - case .interrupted: - signalInterrupted = true - case .next, .failed, .completed: - hasUnexpectedEventsEmitted = true - } - } - - expect(signalInterrupted) == true - expect(hasUnexpectedEventsEmitted) == false - } - - it("should yield a producer that sends the current value then completes") { - let constantProperty = Property(value: initialPropertyValue) - - var sentValue: String? - var signalCompleted = false - - constantProperty.producer.start { event in - switch event { - case let .next(value): - sentValue = value - case .completed: - signalCompleted = true - case .failed, .interrupted: - break - } - } - - expect(sentValue) == initialPropertyValue - expect(signalCompleted) == true - } - } - - describe("existential property") { - it("should pass through behaviors of the wrapped property") { - let constantProperty = Property(value: initialPropertyValue) - let property = Property(constantProperty) - - var sentValue: String? - var signalSentValue: String? - var producerCompleted = false - var signalInterrupted = false - - property.producer.start { event in - switch event { - case let .next(value): - sentValue = value - case .completed: - producerCompleted = true - case .failed, .interrupted: - break - } - } - - property.signal.observe { event in - switch event { - case let .next(value): - signalSentValue = value - case .interrupted: - signalInterrupted = true - case .failed, .completed: - break - } - } - - expect(sentValue) == initialPropertyValue - expect(signalSentValue).to(beNil()) - expect(producerCompleted) == true - expect(signalInterrupted) == true - } - } - - describe("composed properties") { - describe("from properties") { - it("should have the latest value available before sending any value") { - var latestValue: Int! - - let property = MutableProperty(1) - let mappedProperty = property.map { $0 + 1 } - mappedProperty.producer.startWithNext { _ in latestValue = mappedProperty.value } - - expect(latestValue) == 2 - - property.value = 2 - expect(latestValue) == 3 - - property.value = 3 - expect(latestValue) == 4 - } - - it("should retain its source property") { - var property = Optional(MutableProperty(1)) - weak var weakProperty = property - - var firstMappedProperty = Optional(property!.map { $0 + 1 }) - var secondMappedProperty = Optional(firstMappedProperty!.map { $0 + 2 }) - - // Suppress the "written to but never read" warning on `secondMappedProperty`. - _ = secondMappedProperty - - property = nil - expect(weakProperty).toNot(beNil()) - - firstMappedProperty = nil - expect(weakProperty).toNot(beNil()) - - secondMappedProperty = nil - expect(weakProperty).to(beNil()) - } - - it("should transform property from a property that has a terminated producer") { - let property = Property(value: 1) - let transformedProperty = property.map { $0 + 1 } - - expect(transformedProperty.value) == 2 - } - - it("should return a producer and a signal which respect the lifetime of the source property instead of the read-only view itself") { - var signalCompleted = 0 - var producerCompleted = 0 - - var property = Optional(MutableProperty(1)) - var firstMappedProperty = Optional(property!.map { $0 + 1 }) - var secondMappedProperty = Optional(firstMappedProperty!.map { $0 + 2 }) - var thirdMappedProperty = Optional(secondMappedProperty!.map { $0 + 2 }) - - firstMappedProperty!.signal.observeCompleted { signalCompleted += 1 } - secondMappedProperty!.signal.observeCompleted { signalCompleted += 1 } - thirdMappedProperty!.signal.observeCompleted { signalCompleted += 1 } - - firstMappedProperty!.producer.startWithCompleted { producerCompleted += 1 } - secondMappedProperty!.producer.startWithCompleted { producerCompleted += 1 } - thirdMappedProperty!.producer.startWithCompleted { producerCompleted += 1 } - - firstMappedProperty = nil - expect(signalCompleted) == 0 - expect(producerCompleted) == 0 - - secondMappedProperty = nil - expect(signalCompleted) == 0 - expect(producerCompleted) == 0 - - property = nil - expect(signalCompleted) == 0 - expect(producerCompleted) == 0 - - thirdMappedProperty = nil - expect(signalCompleted) == 3 - expect(producerCompleted) == 3 - } - - it("should not capture intermediate properties but only the ultimate sources") { - func increment(input: Int) -> Int { - return input + 1 - } - - weak var weakSourceProperty: MutableProperty? - weak var weakPropertyA: Property? - weak var weakPropertyB: Property? - weak var weakPropertyC: Property? - - var finalProperty: Property! - - func scope() { - let property = MutableProperty(1) - weakSourceProperty = property - - let propertyA = property.map(increment) - weakPropertyA = propertyA - - let propertyB = propertyA.map(increment) - weakPropertyB = propertyB - - let propertyC = propertyB.map(increment) - weakPropertyC = propertyC - - finalProperty = propertyC.map(increment) - } - - scope() - - expect(finalProperty.value) == 5 - expect(weakSourceProperty).toNot(beNil()) - expect(weakPropertyA).to(beNil()) - expect(weakPropertyB).to(beNil()) - expect(weakPropertyC).to(beNil()) - } - } - - describe("from a value and SignalProducer") { - it("should initially take on the supplied value") { - let property = Property(initial: initialPropertyValue, - then: SignalProducer.never) - - expect(property.value) == initialPropertyValue - } - - it("should take on each value sent on the producer") { - let property = Property(initial: initialPropertyValue, - then: SignalProducer(value: subsequentPropertyValue)) - - expect(property.value) == subsequentPropertyValue - } - - it("should return a producer and a signal that respect the lifetime of its ultimate source") { - var signalCompleted = false - var producerCompleted = false - var signalInterrupted = false - - let (signal, observer) = Signal.pipe() - var property: Property? = Property(initial: 1, - then: SignalProducer(signal: signal)) - let propertySignal = property!.signal - - propertySignal.observeCompleted { signalCompleted = true } - property!.producer.startWithCompleted { producerCompleted = true } - - expect(property!.value) == 1 - - observer.sendNext(2) - expect(property!.value) == 2 - expect(producerCompleted) == false - expect(signalCompleted) == false - - property = nil - expect(producerCompleted) == false - expect(signalCompleted) == false - - observer.sendCompleted() - expect(producerCompleted) == true - expect(signalCompleted) == true - - propertySignal.observeInterrupted { signalInterrupted = true } - expect(signalInterrupted) == true - } - } - - describe("from a value and Signal") { - it("should initially take on the supplied value, then values sent on the signal") { - let (signal, observer) = Signal.pipe() - - let property = Property(initial: initialPropertyValue, - then: signal) - - expect(property.value) == initialPropertyValue - - observer.sendNext(subsequentPropertyValue) - - expect(property.value) == subsequentPropertyValue - } - - - it("should return a producer and a signal that respect the lifetime of its ultimate source") { - var signalCompleted = false - var producerCompleted = false - var signalInterrupted = false - - let (signal, observer) = Signal.pipe() - var property: Property? = Property(initial: 1, - then: signal) - let propertySignal = property!.signal - - propertySignal.observeCompleted { signalCompleted = true } - property!.producer.startWithCompleted { producerCompleted = true } - - expect(property!.value) == 1 - - observer.sendNext(2) - expect(property!.value) == 2 - expect(producerCompleted) == false - expect(signalCompleted) == false - - property = nil - expect(producerCompleted) == false - expect(signalCompleted) == false - - observer.sendCompleted() - expect(producerCompleted) == true - expect(signalCompleted) == true - - propertySignal.observeInterrupted { signalInterrupted = true } - expect(signalInterrupted) == true - } - } - } - } - - describe("PropertyProtocol") { - describe("map") { - it("should transform the current value and all subsequent values") { - let property = MutableProperty(1) - let mappedProperty = property - .map { $0 + 1 } - expect(mappedProperty.value) == 2 - - property.value = 2 - expect(mappedProperty.value) == 3 - } - } - - describe("combineLatest") { - var property: MutableProperty! - var otherProperty: MutableProperty! - - beforeEach { - property = MutableProperty(initialPropertyValue) - otherProperty = MutableProperty(initialOtherPropertyValue) - } - - it("should forward the latest values from both inputs") { - let combinedProperty = property.combineLatest(with: otherProperty) - var latest: (String, String)? - combinedProperty.signal.observeNext { latest = $0 } - - property.value = subsequentPropertyValue - expect(latest?.0) == subsequentPropertyValue - expect(latest?.1) == initialOtherPropertyValue - - // is there a better way to test tuples? - otherProperty.value = subsequentOtherPropertyValue - expect(latest?.0) == subsequentPropertyValue - expect(latest?.1) == subsequentOtherPropertyValue - - property.value = finalPropertyValue - expect(latest?.0) == finalPropertyValue - expect(latest?.1) == subsequentOtherPropertyValue - } - - it("should complete when the source properties are deinitialized") { - var completed = false - - var combinedProperty = Optional(property.combineLatest(with: otherProperty)) - combinedProperty!.signal.observeCompleted { completed = true } - - combinedProperty = nil - expect(completed) == false - - property = nil - expect(completed) == false - - otherProperty = nil - expect(completed) == true - } - - it("should be consistent between its cached value and its values producer") { - var firstResult: String! - var secondResult: String! - - let combined = property.combineLatest(with: otherProperty) - combined.producer.startWithNext { (left, right) in firstResult = left + right } - - func getValue() -> String { - return combined.value.0 + combined.value.1 - } - - expect(getValue()) == initialPropertyValue + initialOtherPropertyValue - expect(firstResult) == initialPropertyValue + initialOtherPropertyValue - - property.value = subsequentPropertyValue - expect(getValue()) == subsequentPropertyValue + initialOtherPropertyValue - expect(firstResult) == subsequentPropertyValue + initialOtherPropertyValue - - combined.producer.startWithNext { (left, right) in secondResult = left + right } - expect(secondResult) == subsequentPropertyValue + initialOtherPropertyValue - - otherProperty.value = subsequentOtherPropertyValue - expect(getValue()) == subsequentPropertyValue + subsequentOtherPropertyValue - expect(firstResult) == subsequentPropertyValue + subsequentOtherPropertyValue - expect(secondResult) == subsequentPropertyValue + subsequentOtherPropertyValue - } - - it("should be consistent between nested combined properties") { - let A = MutableProperty(1) - let B = MutableProperty(100) - let C = MutableProperty(10000) - - var firstResult: Int! - - let combined = A.combineLatest(with: B) - combined.producer.startWithNext { (left, right) in firstResult = left + right } - - func getValue() -> Int { - return combined.value.0 + combined.value.1 - } - - /// Initial states - expect(getValue()) == 101 - expect(firstResult) == 101 - - A.value = 2 - expect(getValue()) == 102 - expect(firstResult) == 102 - - B.value = 200 - expect(getValue()) == 202 - expect(firstResult) == 202 - - /// Setup - A.value = 3 - expect(getValue()) == 203 - expect(firstResult) == 203 - - /// Zip another property now. - var secondResult: Int! - let anotherCombined = combined.combineLatest(with: C) - anotherCombined.producer.startWithNext { (left, right) in secondResult = (left.0 + left.1) + right } - - func getAnotherValue() -> Int { - return (anotherCombined.value.0.0 + anotherCombined.value.0.1) + anotherCombined.value.1 - } - - expect(getAnotherValue()) == 10203 - - A.value = 4 - expect(getValue()) == 204 - expect(getAnotherValue()) == 10204 - } - } - - describe("zip") { - var property: MutableProperty! - var otherProperty: MutableProperty! - - beforeEach { - property = MutableProperty(initialPropertyValue) - otherProperty = MutableProperty(initialOtherPropertyValue) - } - - it("should combine pairs") { - var result: [String] = [] - - let zippedProperty = property.zip(with: otherProperty) - zippedProperty.producer.startWithNext { (left, right) in result.append("\(left)\(right)") } - - let firstResult = [ "\(initialPropertyValue)\(initialOtherPropertyValue)" ] - let secondResult = firstResult + [ "\(subsequentPropertyValue)\(subsequentOtherPropertyValue)" ] - let thirdResult = secondResult + [ "\(finalPropertyValue)\(finalOtherPropertyValue)" ] - let finalResult = thirdResult + [ "\(initialPropertyValue)\(initialOtherPropertyValue)" ] - - expect(result) == firstResult - - property.value = subsequentPropertyValue - expect(result) == firstResult - - otherProperty.value = subsequentOtherPropertyValue - expect(result) == secondResult - - property.value = finalPropertyValue - otherProperty.value = finalOtherPropertyValue - expect(result) == thirdResult - - property.value = initialPropertyValue - expect(result) == thirdResult - - property.value = subsequentPropertyValue - expect(result) == thirdResult - - otherProperty.value = initialOtherPropertyValue - expect(result) == finalResult - } - - it("should be consistent between its cached value and its values producer") { - var firstResult: String! - var secondResult: String! - - let zippedProperty = property.zip(with: otherProperty) - zippedProperty.producer.startWithNext { (left, right) in firstResult = left + right } - - func getValue() -> String { - return zippedProperty.value.0 + zippedProperty.value.1 - } - - expect(getValue()) == initialPropertyValue + initialOtherPropertyValue - expect(firstResult) == initialPropertyValue + initialOtherPropertyValue - - property.value = subsequentPropertyValue - expect(getValue()) == initialPropertyValue + initialOtherPropertyValue - expect(firstResult) == initialPropertyValue + initialOtherPropertyValue - - // It should still be the tuple with initial property values, - // since `otherProperty` isn't changed yet. - zippedProperty.producer.startWithNext { (left, right) in secondResult = left + right } - expect(secondResult) == initialPropertyValue + initialOtherPropertyValue - - otherProperty.value = subsequentOtherPropertyValue - expect(getValue()) == subsequentPropertyValue + subsequentOtherPropertyValue - expect(firstResult) == subsequentPropertyValue + subsequentOtherPropertyValue - expect(secondResult) == subsequentPropertyValue + subsequentOtherPropertyValue - } - - it("should be consistent between nested zipped properties") { - let A = MutableProperty(1) - let B = MutableProperty(100) - let C = MutableProperty(10000) - - var firstResult: Int! - - let zipped = A.zip(with: B) - zipped.producer.startWithNext { (left, right) in firstResult = left + right } - - func getValue() -> Int { - return zipped.value.0 + zipped.value.1 - } - - /// Initial states - expect(getValue()) == 101 - expect(firstResult) == 101 - - A.value = 2 - expect(getValue()) == 101 - expect(firstResult) == 101 - - B.value = 200 - expect(getValue()) == 202 - expect(firstResult) == 202 - - /// Setup - A.value = 3 - expect(getValue()) == 202 - expect(firstResult) == 202 - - /// Zip another property now. - var secondResult: Int! - let anotherZipped = zipped.zip(with: C) - anotherZipped.producer.startWithNext { (left, right) in secondResult = (left.0 + left.1) + right } - - func getAnotherValue() -> Int { - return (anotherZipped.value.0.0 + anotherZipped.value.0.1) + anotherZipped.value.1 - } - - /// Since `zipped` is 202 now, and `C` is 10000, - /// shouldn't this be 10202? - - /// Verify `zipped` again. - expect(getValue()) == 202 - expect(firstResult) == 202 - - /// Then... well, no. Surprise! (Only before #3042) - /// We get 10203 here. - /// - /// https://github.com/ReactiveCocoa/ReactiveCocoa/pull/3042 - expect(getAnotherValue()) == 10202 - } - - it("should be consistent between combined and nested zipped properties") { - let A = MutableProperty(1) - let B = MutableProperty(100) - let C = MutableProperty(10000) - let D = MutableProperty(1000000) - - var firstResult: Int! - - let zipped = A.zip(with: B) - zipped.producer.startWithNext { (left, right) in firstResult = left + right } - - func getValue() -> Int { - return zipped.value.0 + zipped.value.1 - } - - /// Initial states - expect(getValue()) == 101 - expect(firstResult) == 101 - - A.value = 2 - expect(getValue()) == 101 - expect(firstResult) == 101 - - B.value = 200 - expect(getValue()) == 202 - expect(firstResult) == 202 - - /// Setup - A.value = 3 - expect(getValue()) == 202 - expect(firstResult) == 202 - - /// Zip another property now. - var secondResult: Int! - let anotherZipped = zipped.zip(with: C) - anotherZipped.producer.startWithNext { (left, right) in secondResult = (left.0 + left.1) + right } - - func getAnotherValue() -> Int { - return (anotherZipped.value.0.0 + anotherZipped.value.0.1) + anotherZipped.value.1 - } - - /// Verify `zipped` again. - expect(getValue()) == 202 - expect(firstResult) == 202 - - expect(getAnotherValue()) == 10202 - - /// Zip `D` with `anotherZipped`. - let yetAnotherZipped = anotherZipped.zip(with: D) - - /// Combine with another property. - /// (((Int, Int), Int), (((Int, Int), Int), Int)) - let combined = anotherZipped.combineLatest(with: yetAnotherZipped) - - var thirdResult: Int! - combined.producer.startWithNext { (left, right) in - let leftResult = left.0.0 + left.0.1 + left.1 - let rightResult = right.0.0.0 + right.0.0.1 + right.0.1 + right.1 - thirdResult = leftResult + rightResult - } - - expect(thirdResult) == 1020404 - } - - it("should complete its producer only when the source properties are deinitialized") { - var result: [String] = [] - var completed = false - - var zippedProperty = Optional(property.zip(with: otherProperty)) - zippedProperty!.producer.start { event in - switch event { - case let .next(left, right): - result.append("\(left)\(right)") - case .completed: - completed = true - default: - break - } - } - - expect(completed) == false - expect(result) == [ "\(initialPropertyValue)\(initialOtherPropertyValue)" ] - - property.value = subsequentPropertyValue - expect(result) == [ "\(initialPropertyValue)\(initialOtherPropertyValue)" ] - - zippedProperty = nil - expect(completed) == false - - property = nil - otherProperty = nil - expect(completed) == true - } - } - - describe("unary operators") { - var property: MutableProperty! - - beforeEach { - property = MutableProperty(initialPropertyValue) - } - - describe("combinePrevious") { - it("should pack the current value and the previous value a tuple") { - let transformedProperty = property.combinePrevious(initialPropertyValue) - - expect(transformedProperty.value.0) == initialPropertyValue - expect(transformedProperty.value.1) == initialPropertyValue - - property.value = subsequentPropertyValue - - expect(transformedProperty.value.0) == initialPropertyValue - expect(transformedProperty.value.1) == subsequentPropertyValue - - property.value = finalPropertyValue - - expect(transformedProperty.value.0) == subsequentPropertyValue - expect(transformedProperty.value.1) == finalPropertyValue - } - - it("should complete its producer only when the source property is deinitialized") { - var result: (String, String)? - var completed = false - - var transformedProperty = Optional(property.combinePrevious(initialPropertyValue)) - transformedProperty!.producer.start { event in - switch event { - case let .next(tuple): - result = tuple - case .completed: - completed = true - default: - break - } - } - - expect(result?.0) == initialPropertyValue - expect(result?.1) == initialPropertyValue - - property.value = subsequentPropertyValue - - expect(result?.0) == initialPropertyValue - expect(result?.1) == subsequentPropertyValue - - transformedProperty = nil - expect(completed) == false - - property = nil - expect(completed) == true - } - } - - describe("skipRepeats") { - it("should not emit events for subsequent equatable values that are the same as the current value") { - let transformedProperty = property.skipRepeats() - - var counter = 0 - transformedProperty.signal.observeNext { _ in - counter += 1 - } - - property.value = initialPropertyValue - property.value = initialPropertyValue - property.value = initialPropertyValue - - expect(counter) == 0 - - property.value = subsequentPropertyValue - property.value = subsequentPropertyValue - property.value = subsequentPropertyValue - - expect(counter) == 1 - - property.value = finalPropertyValue - property.value = initialPropertyValue - property.value = subsequentPropertyValue - - expect(counter) == 4 - } - - it("should not emit events for subsequent values that are regarded as the same as the current value by the supplied closure") { - var counter = 0 - let transformedProperty = property.skipRepeats { _, newValue in newValue == initialPropertyValue } - - transformedProperty.signal.observeNext { _ in - counter += 1 - } - - property.value = initialPropertyValue - expect(counter) == 0 - - property.value = subsequentPropertyValue - expect(counter) == 1 - - property.value = finalPropertyValue - expect(counter) == 2 - - property.value = initialPropertyValue - expect(counter) == 2 - } - - it("should complete its producer only when the source property is deinitialized") { - var counter = 0 - var completed = false - - var transformedProperty = Optional(property.skipRepeats()) - transformedProperty!.producer.start { event in - switch event { - case .next: - counter += 1 - case .completed: - completed = true - default: - break - } - } - - expect(counter) == 1 - - property.value = initialPropertyValue - expect(counter) == 1 - - transformedProperty = nil - expect(completed) == false - - property = nil - expect(completed) == true - } - } - - describe("uniqueValues") { - it("should emit hashable values that have not been emited before") { - let transformedProperty = property.uniqueValues() - - var counter = 0 - transformedProperty.signal.observeNext { _ in - counter += 1 - } - - property.value = initialPropertyValue - expect(counter) == 0 - - property.value = subsequentPropertyValue - property.value = subsequentPropertyValue - - expect(counter) == 1 - - property.value = finalPropertyValue - property.value = initialPropertyValue - property.value = subsequentPropertyValue - - expect(counter) == 2 - } - - it("should emit only the values of which the computed identity have not been captured before") { - let transformedProperty = property.uniqueValues { _ in 0 } - - var counter = 0 - transformedProperty.signal.observeNext { _ in - counter += 1 - } - - property.value = initialPropertyValue - property.value = subsequentPropertyValue - property.value = finalPropertyValue - expect(counter) == 0 - } - - it("should complete its producer only when the source property is deinitialized") { - var counter = 0 - var completed = false - - var transformedProperty = Optional(property.uniqueValues()) - transformedProperty!.producer.start { event in - switch event { - case .next: - counter += 1 - case .completed: - completed = true - default: - break - } - } - - expect(counter) == 1 - - property.value = initialPropertyValue - expect(counter) == 1 - - transformedProperty = nil - expect(completed) == false - - property = nil - expect(completed) == true - } - } - } - - describe("flattening") { - describe("flatten") { - describe("FlattenStrategy.concat") { - it("should concatenate the values as the inner property is replaced and deinitialized") { - var firstProperty = Optional(MutableProperty(0)) - var secondProperty = Optional(MutableProperty(10)) - var thirdProperty = Optional(MutableProperty(20)) - - var outerProperty = Optional(MutableProperty(firstProperty!)) - - var receivedValues: [Int] = [] - var errored = false - var completed = false - - var flattenedProperty = Optional(outerProperty!.flatten(.concat)) - - flattenedProperty!.producer.start { event in - switch event { - case let .next(value): - receivedValues.append(value) - case .completed: - completed = true - case .failed: - errored = true - case .interrupted: - break - } - } - - expect(receivedValues) == [ 0 ] - - outerProperty!.value = secondProperty! - secondProperty!.value = 11 - outerProperty!.value = thirdProperty! - thirdProperty!.value = 21 - - expect(receivedValues) == [ 0 ] - expect(completed) == false - - secondProperty!.value = 12 - thirdProperty!.value = 22 - - expect(receivedValues) == [ 0 ] - expect(completed) == false - - firstProperty = nil - - expect(receivedValues) == [ 0, 12 ] - expect(completed) == false - - secondProperty = nil - - expect(receivedValues) == [ 0, 12, 22 ] - expect(completed) == false - - outerProperty = nil - expect(completed) == false - - thirdProperty = nil - expect(completed) == false - - flattenedProperty = nil - expect(completed) == true - expect(errored) == false - } - } - - describe("FlattenStrategy.merge") { - it("should merge the values of all inner properties") { - var firstProperty = Optional(MutableProperty(0)) - var secondProperty = Optional(MutableProperty(10)) - var thirdProperty = Optional(MutableProperty(20)) - - var outerProperty = Optional(MutableProperty(firstProperty!)) - - var receivedValues: [Int] = [] - var errored = false - var completed = false - - var flattenedProperty = Optional(outerProperty!.flatten(.merge)) - - flattenedProperty!.producer.start { event in - switch event { - case let .next(value): - receivedValues.append(value) - case .completed: - completed = true - case .failed: - errored = true - case .interrupted: - break - } - } - - expect(receivedValues) == [ 0 ] - - outerProperty!.value = secondProperty! - secondProperty!.value = 11 - outerProperty!.value = thirdProperty! - thirdProperty!.value = 21 - - expect(receivedValues) == [ 0, 10, 11, 20, 21 ] - expect(completed) == false - - secondProperty!.value = 12 - thirdProperty!.value = 22 - - expect(receivedValues) == [ 0, 10, 11, 20, 21, 12, 22 ] - expect(completed) == false - - firstProperty = nil - - expect(receivedValues) == [ 0, 10, 11, 20, 21, 12, 22 ] - expect(completed) == false - - secondProperty = nil - - expect(receivedValues) == [ 0, 10, 11, 20, 21, 12, 22 ] - expect(completed) == false - - outerProperty = nil - expect(completed) == false - - thirdProperty = nil - expect(completed) == false - - flattenedProperty = nil - expect(completed) == true - expect(errored) == false - } - } - - describe("FlattenStrategy.latest") { - it("should forward values from the latest inner property") { - let firstProperty = Optional(MutableProperty(0)) - var secondProperty = Optional(MutableProperty(10)) - var thirdProperty = Optional(MutableProperty(20)) - - var outerProperty = Optional(MutableProperty(firstProperty!)) - - var receivedValues: [Int] = [] - var errored = false - var completed = false - - outerProperty!.flatten(.latest).producer.start { event in - switch event { - case let .next(value): - receivedValues.append(value) - case .completed: - completed = true - case .failed: - errored = true - case .interrupted: - break - } - } - - expect(receivedValues) == [ 0 ] - - outerProperty!.value = secondProperty! - secondProperty!.value = 11 - outerProperty!.value = thirdProperty! - thirdProperty!.value = 21 - - expect(receivedValues) == [ 0, 10, 11, 20, 21 ] - expect(errored) == false - expect(completed) == false - - secondProperty!.value = 12 - secondProperty = nil - thirdProperty!.value = 22 - thirdProperty = nil - - expect(receivedValues) == [ 0, 10, 11, 20, 21, 22 ] - expect(errored) == false - expect(completed) == false - - outerProperty = nil - expect(errored) == false - expect(completed) == true - } - - it("should release the old properties when switched or deallocated") { - var firstProperty = Optional(MutableProperty(0)) - var secondProperty = Optional(MutableProperty(10)) - var thirdProperty = Optional(MutableProperty(20)) - - weak var weakFirstProperty = firstProperty - weak var weakSecondProperty = secondProperty - weak var weakThirdProperty = thirdProperty - - var outerProperty = Optional(MutableProperty(firstProperty!)) - var flattened = Optional(outerProperty!.flatten(.latest)) - - var errored = false - var completed = false - - flattened!.producer.start { event in - switch event { - case .completed: - completed = true - case .failed: - errored = true - case .interrupted, .next: - break - } - } - - firstProperty = nil - outerProperty!.value = secondProperty! - expect(weakFirstProperty).to(beNil()) - - secondProperty = nil - outerProperty!.value = thirdProperty! - expect(weakSecondProperty).to(beNil()) - - thirdProperty = nil - outerProperty = nil - flattened = nil - expect(weakThirdProperty).to(beNil()) - expect(errored) == false - expect(completed) == true - } - } - } - - describe("flatMap") { - describe("PropertyFlattenStrategy.latest") { - it("should forward values from the latest inner transformed property") { - let firstProperty = Optional(MutableProperty(0)) - var secondProperty = Optional(MutableProperty(10)) - var thirdProperty = Optional(MutableProperty(20)) - - var outerProperty = Optional(MutableProperty(firstProperty!)) - - var receivedValues: [String] = [] - var errored = false - var completed = false - - outerProperty!.flatMap(.latest) { $0.map { "\($0)" } }.producer.start { event in - switch event { - case let .next(value): - receivedValues.append(value) - case .completed: - completed = true - case .failed: - errored = true - case .interrupted: - break - } - } - - expect(receivedValues) == [ "0" ] - - outerProperty!.value = secondProperty! - secondProperty!.value = 11 - outerProperty!.value = thirdProperty! - thirdProperty!.value = 21 - - expect(receivedValues) == [ "0", "10", "11", "20", "21" ] - expect(errored) == false - expect(completed) == false - - secondProperty!.value = 12 - secondProperty = nil - thirdProperty!.value = 22 - thirdProperty = nil - - expect(receivedValues) == [ "0", "10", "11", "20", "21", "22" ] - expect(errored) == false - expect(completed) == false - - outerProperty = nil - expect(errored) == false - expect(completed) == true - } - } - } - } - } - - describe("binding") { - describe("from a Signal") { - it("should update the property with values sent from the signal") { - let (signal, observer) = Signal.pipe() - - let mutableProperty = MutableProperty(initialPropertyValue) - - mutableProperty <~ signal - - // Verify that the binding hasn't changed the property value: - expect(mutableProperty.value) == initialPropertyValue - - observer.sendNext(subsequentPropertyValue) - expect(mutableProperty.value) == subsequentPropertyValue - } - - it("should tear down the binding when disposed") { - let (signal, observer) = Signal.pipe() - - let mutableProperty = MutableProperty(initialPropertyValue) - - let bindingDisposable = mutableProperty <~ signal - bindingDisposable!.dispose() - - observer.sendNext(subsequentPropertyValue) - expect(mutableProperty.value) == initialPropertyValue - } - - it("should tear down the binding when the property deallocates") { - var signal: Signal? = { - let (signal, _) = Signal.pipe() - return signal - }() - weak var weakSignal = signal - - var mutableProperty: MutableProperty? = MutableProperty(initialPropertyValue) - - mutableProperty! <~ signal! - signal = nil - - // The binding attached an observer to the signal, so it cannot - // deinitialize. - expect(weakSignal).toNot(beNil()) - - // The deinitialization should tear down the binding, which would - // remove the last observer from the signal, causing it to - // dispose of itself. - mutableProperty = nil - expect(weakSignal).to(beNil()) - } - } - - describe("from a SignalProducer") { - it("should start a signal and update the property with its values") { - let signalValues = [initialPropertyValue, subsequentPropertyValue] - let signalProducer = SignalProducer(values: signalValues) - - let mutableProperty = MutableProperty(initialPropertyValue) - - mutableProperty <~ signalProducer - - expect(mutableProperty.value) == signalValues.last! - } - - it("should tear down the binding when disposed") { - let (signalProducer, observer) = SignalProducer.pipe() - - let mutableProperty = MutableProperty(initialPropertyValue) - let disposable = mutableProperty <~ signalProducer - - disposable.dispose() - - observer.sendNext(subsequentPropertyValue) - expect(mutableProperty.value) == initialPropertyValue - } - - it("should tear down the binding when the property deallocates") { - let (signal, _) = Signal.pipe() - let signalProducer = SignalProducer(signal: signal) - - var mutableProperty: MutableProperty? = MutableProperty(initialPropertyValue) - - var isDisposed = false - mutableProperty! <~ signalProducer.on(disposed: { isDisposed = true }) - expect(isDisposed) == false - - mutableProperty = nil - expect(isDisposed) == true - } - } - - describe("from another property") { - it("should take the source property's current value") { - let sourceProperty = Property(value: initialPropertyValue) - - let destinationProperty = MutableProperty("") - - destinationProperty <~ sourceProperty.producer - - expect(destinationProperty.value) == initialPropertyValue - } - - it("should update with changes to the source property's value") { - let sourceProperty = MutableProperty(initialPropertyValue) - - let destinationProperty = MutableProperty("") - - destinationProperty <~ sourceProperty.producer - - sourceProperty.value = subsequentPropertyValue - expect(destinationProperty.value) == subsequentPropertyValue - } - - it("should tear down the binding when disposed") { - let sourceProperty = MutableProperty(initialPropertyValue) - - let destinationProperty = MutableProperty("") - - let bindingDisposable = destinationProperty <~ sourceProperty.producer - bindingDisposable.dispose() - - sourceProperty.value = subsequentPropertyValue - - expect(destinationProperty.value) == initialPropertyValue - } - - it("should tear down the binding when the source property deallocates") { - var sourceProperty: MutableProperty? = MutableProperty(initialPropertyValue) - - let destinationProperty = MutableProperty("") - destinationProperty <~ sourceProperty!.producer - - sourceProperty = nil - // TODO: Assert binding was torn down? - } - - it("should tear down the binding when the destination property deallocates") { - let sourceProperty = MutableProperty(initialPropertyValue) - var destinationProperty: MutableProperty? = MutableProperty("") - - var isDisposed = false - destinationProperty! <~ sourceProperty.producer.on(disposed: { isDisposed = true }) - expect(isDisposed) == false - - destinationProperty = nil - expect(isDisposed) == true - } - } - } - } -} - -private class ObservableObject: NSObject { - dynamic var rac_value: Int = 0 - dynamic var rac_reference: UnbridgedObject = UnbridgedObject("") -} - -private class UnbridgedObject: NSObject { - let value: String - init(_ value: String) { - self.value = value - } -} diff --git a/ReactiveSwiftTests/SchedulerSpec.swift b/ReactiveSwiftTests/SchedulerSpec.swift deleted file mode 100644 index 2ea04a4d75..0000000000 --- a/ReactiveSwiftTests/SchedulerSpec.swift +++ /dev/null @@ -1,298 +0,0 @@ -// -// SchedulerSpec.swift -// ReactiveSwift -// -// Created by Justin Spahr-Summers on 2014-07-13. -// Copyright (c) 2014 GitHub. All rights reserved. -// - -import Foundation -import Nimble -import Quick -@testable -import ReactiveSwift - -class SchedulerSpec: QuickSpec { - override func spec() { - describe("ImmediateScheduler") { - it("should run enqueued actions immediately") { - var didRun = false - ImmediateScheduler().schedule { - didRun = true - } - - expect(didRun) == true - } - } - - describe("UIScheduler") { - func dispatchSyncInBackground(_ action: @escaping () -> Void) { - let group = DispatchGroup() - - let globalQueue: DispatchQueue - if #available(*, OSX 10.10) { - globalQueue = DispatchQueue.global() - } else { - globalQueue = DispatchQueue.global(priority: .default) - } - - globalQueue.async(group: group, execute: action) - group.wait() - } - - it("should run actions immediately when on the main thread") { - let scheduler = UIScheduler() - var values: [Int] = [] - expect(Thread.isMainThread) == true - - scheduler.schedule { - values.append(0) - } - - expect(values) == [ 0 ] - - scheduler.schedule { - values.append(1) - } - - scheduler.schedule { - values.append(2) - } - - expect(values) == [ 0, 1, 2 ] - } - - it("should enqueue actions scheduled from the background") { - let scheduler = UIScheduler() - var values: [Int] = [] - - dispatchSyncInBackground { - scheduler.schedule { - expect(Thread.isMainThread) == true - values.append(0) - } - - return - } - - expect(values) == [] - expect(values).toEventually(equal([ 0 ])) - - dispatchSyncInBackground { - scheduler.schedule { - expect(Thread.isMainThread) == true - values.append(1) - } - - scheduler.schedule { - expect(Thread.isMainThread) == true - values.append(2) - } - - return - } - - expect(values) == [ 0 ] - expect(values).toEventually(equal([ 0, 1, 2 ])) - } - - it("should run actions enqueued from the main thread after those from the background") { - let scheduler = UIScheduler() - var values: [Int] = [] - - dispatchSyncInBackground { - scheduler.schedule { - expect(Thread.isMainThread) == true - values.append(0) - } - - return - } - - scheduler.schedule { - expect(Thread.isMainThread) == true - values.append(1) - } - - scheduler.schedule { - expect(Thread.isMainThread) == true - values.append(2) - } - - expect(values) == [] - expect(values).toEventually(equal([ 0, 1, 2 ])) - } - } - - describe("QueueScheduler") { - it("should run enqueued actions on a global queue") { - var didRun = false - - let scheduler: QueueScheduler - if #available(OSX 10.10, *) { - scheduler = QueueScheduler(qos: .default, name: "\(#file):\(#line)") - } else { - scheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) - } - - scheduler.schedule { - didRun = true - expect(Thread.isMainThread) == false - } - - expect{didRun}.toEventually(beTruthy()) - } - - describe("on a given queue") { - var scheduler: QueueScheduler! - - beforeEach { - if #available(OSX 10.10, *) { - scheduler = QueueScheduler(qos: .default, name: "\(#file):\(#line)") - } else { - scheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) - } - scheduler.queue.suspend() - } - - it("should run enqueued actions serially on the given queue") { - var value = 0 - - for _ in 0..<5 { - scheduler.schedule { - expect(Thread.isMainThread) == false - value += 1 - } - } - - expect(value) == 0 - - scheduler.queue.resume() - expect{value}.toEventually(equal(5)) - } - - it("should run enqueued actions after a given date") { - var didRun = false - scheduler.schedule(after: Date()) { - didRun = true - expect(Thread.isMainThread) == false - } - - expect(didRun) == false - - scheduler.queue.resume() - expect{didRun}.toEventually(beTruthy()) - } - - it("should repeatedly run actions after a given date") { - let disposable = SerialDisposable() - - var count = 0 - let timesToRun = 3 - - disposable.innerDisposable = scheduler.schedule(after: Date(), interval: 0.01, leeway: 0) { - expect(Thread.isMainThread) == false - - count += 1 - - if count == timesToRun { - disposable.dispose() - } - } - - expect(count) == 0 - - scheduler.queue.resume() - expect{count}.toEventually(equal(timesToRun)) - } - } - } - - describe("TestScheduler") { - var scheduler: TestScheduler! - var startDate: Date! - - // How much dates are allowed to differ when they should be "equal." - let dateComparisonDelta = 0.00001 - - beforeEach { - startDate = Date() - - scheduler = TestScheduler(startDate: startDate) - expect(scheduler.currentDate) == startDate - } - - it("should run immediately enqueued actions upon advancement") { - var string = "" - - scheduler.schedule { - string += "foo" - expect(Thread.isMainThread) == true - } - - scheduler.schedule { - string += "bar" - expect(Thread.isMainThread) == true - } - - expect(string) == "" - - scheduler.advance() - expect(scheduler.currentDate).to(beCloseTo(startDate)) - - expect(string) == "foobar" - } - - it("should run actions when advanced past the target date") { - var string = "" - - scheduler.schedule(after: 15) { [weak scheduler] in - string += "bar" - expect(Thread.isMainThread) == true - expect(scheduler?.currentDate).to(beCloseTo(startDate.addingTimeInterval(15), within: dateComparisonDelta)) - } - - scheduler.schedule(after: 5) { [weak scheduler] in - string += "foo" - expect(Thread.isMainThread) == true - expect(scheduler?.currentDate).to(beCloseTo(startDate.addingTimeInterval(5), within: dateComparisonDelta)) - } - - expect(string) == "" - - scheduler.advance(by: 10) - expect(scheduler.currentDate).to(beCloseTo(startDate.addingTimeInterval(10), within: TimeInterval(dateComparisonDelta))) - expect(string) == "foo" - - scheduler.advance(by: 10) - expect(scheduler.currentDate).to(beCloseTo(startDate.addingTimeInterval(20), within: dateComparisonDelta)) - expect(string) == "foobar" - } - - it("should run all remaining actions in order") { - var string = "" - - scheduler.schedule(after: 15) { - string += "bar" - expect(Thread.isMainThread) == true - } - - scheduler.schedule(after: 5) { - string += "foo" - expect(Thread.isMainThread) == true - } - - scheduler.schedule { - string += "fuzzbuzz" - expect(Thread.isMainThread) == true - } - - expect(string) == "" - - scheduler.run() - expect(scheduler.currentDate) == NSDate.distantFuture - expect(string) == "fuzzbuzzfoobar" - } - } - } -} diff --git a/ReactiveSwiftTests/SignalLifetimeSpec.swift b/ReactiveSwiftTests/SignalLifetimeSpec.swift deleted file mode 100644 index a1ea79ea5d..0000000000 --- a/ReactiveSwiftTests/SignalLifetimeSpec.swift +++ /dev/null @@ -1,414 +0,0 @@ -// -// SignalLifetimeSpec.swift -// ReactiveSwift -// -// Created by Vadim Yelagin on 2015-12-13. -// Copyright (c) 2015 GitHub. All rights reserved. -// - -import Result -import Nimble -import Quick -import ReactiveSwift - -class SignalLifetimeSpec: QuickSpec { - override func spec() { - describe("init") { - var testScheduler: TestScheduler! - - beforeEach { - testScheduler = TestScheduler() - } - - it("should deallocate") { - weak var signal: Signal? = Signal { _ in nil } - - expect(signal).to(beNil()) - } - - it("should deallocate if it does not have any observers") { - weak var signal: Signal? = { - let signal: Signal = Signal { _ in nil } - return signal - }() - expect(signal).to(beNil()) - } - - it("should deallocate if no one retains it") { - var signal: Signal? = Signal { _ in nil } - weak var weakSignal = signal - - expect(weakSignal).toNot(beNil()) - - var reference = signal - signal = nil - expect(weakSignal).toNot(beNil()) - - reference = nil - expect(weakSignal).to(beNil()) - } - - it("should deallocate even if the generator observer is retained") { - var observer: Signal.Observer? - - weak var signal: Signal? = { - let signal: Signal = Signal { innerObserver in - observer = innerObserver - return nil - } - return signal - }() - expect(observer).toNot(beNil()) - expect(signal).to(beNil()) - } - - it("should not deallocate if it has at least one observer") { - var disposable: Disposable? = nil - weak var signal: Signal? = { - let signal: Signal = Signal { _ in nil } - disposable = signal.observe(Observer()) - return signal - }() - expect(signal).toNot(beNil()) - disposable?.dispose() - expect(signal).to(beNil()) - } - - it("should be alive until erroring if it has at least one observer, despite not being explicitly retained") { - var errored = false - - weak var signal: Signal? = { - let signal = Signal { observer in - testScheduler.schedule { - observer.sendFailed(TestError.default) - } - return nil - } - signal.observeFailed { _ in errored = true } - return signal - }() - - expect(errored) == false - expect(signal).toNot(beNil()) - - testScheduler.run() - - expect(errored) == true - expect(signal).to(beNil()) - } - - it("should be alive until completion if it has at least one observer, despite not being explicitly retained") { - var completed = false - - weak var signal: Signal? = { - let signal = Signal { observer in - testScheduler.schedule { - observer.sendCompleted() - } - return nil - } - signal.observeCompleted { completed = true } - return signal - }() - - expect(completed) == false - expect(signal).toNot(beNil()) - - testScheduler.run() - - expect(completed) == true - expect(signal).to(beNil()) - } - - it("should be alive until interruption if it has at least one observer, despite not being explicitly retained") { - var interrupted = false - - weak var signal: Signal? = { - let signal = Signal { observer in - testScheduler.schedule { - observer.sendInterrupted() - } - - return nil - } - signal.observeInterrupted { interrupted = true } - return signal - }() - - expect(interrupted) == false - expect(signal).toNot(beNil()) - - testScheduler.run() - - expect(interrupted) == true - expect(signal).to(beNil()) - } - } - - describe("Signal.pipe") { - it("should deallocate") { - weak var signal = Signal<(), NoError>.pipe().0 - - expect(signal).to(beNil()) - } - - it("should be alive until erroring if it has at least one observer, despite not being explicitly retained") { - let testScheduler = TestScheduler() - var errored = false - weak var weakSignal: Signal<(), TestError>? - - // Use an inner closure to help ARC deallocate things as we - // expect. - let test = { - let (signal, observer) = Signal<(), TestError>.pipe() - weakSignal = signal - testScheduler.schedule { - // Note that the input observer has a weak reference to the signal. - observer.sendFailed(TestError.default) - } - signal.observeFailed { _ in errored = true } - } - test() - - expect(weakSignal).toNot(beNil()) - expect(errored) == false - - testScheduler.run() - expect(weakSignal).to(beNil()) - expect(errored) == true - } - - it("should be alive until completion if it has at least one observer, despite not being explicitly retained") { - let testScheduler = TestScheduler() - var completed = false - weak var weakSignal: Signal<(), TestError>? - - // Use an inner closure to help ARC deallocate things as we - // expect. - let test = { - let (signal, observer) = Signal<(), TestError>.pipe() - weakSignal = signal - testScheduler.schedule { - // Note that the input observer has a weak reference to the signal. - observer.sendCompleted() - } - signal.observeCompleted { completed = true } - } - test() - - expect(weakSignal).toNot(beNil()) - expect(completed) == false - - testScheduler.run() - expect(weakSignal).to(beNil()) - expect(completed) == true - } - - it("should be alive until interruption if it has at least one observer, despite not being explicitly retained") { - let testScheduler = TestScheduler() - var interrupted = false - weak var weakSignal: Signal<(), NoError>? - - let test = { - let (signal, observer) = Signal<(), NoError>.pipe() - weakSignal = signal - - testScheduler.schedule { - // Note that the input observer has a weak reference to the signal. - observer.sendInterrupted() - } - - signal.observeInterrupted { interrupted = true } - } - - test() - expect(weakSignal).toNot(beNil()) - expect(interrupted) == false - - testScheduler.run() - expect(weakSignal).to(beNil()) - expect(interrupted) == true - } - } - - describe("testTransform") { - it("should deallocate") { - weak var signal: Signal? = Signal { _ in nil }.testTransform() - - expect(signal).to(beNil()) - } - - it("should not deallocate if it has at least one observer, despite not being explicitly retained") { - weak var signal: Signal? = { - let signal: Signal = Signal { _ in nil }.testTransform() - signal.observe(Observer()) - return signal - }() - expect(signal).toNot(beNil()) - } - - it("should not deallocate if it has at least one observer, despite not being explicitly retained") { - var disposable: Disposable? = nil - weak var signal: Signal? = { - let signal: Signal = Signal { _ in nil }.testTransform() - disposable = signal.observe(Observer()) - return signal - }() - expect(signal).toNot(beNil()) - disposable?.dispose() - expect(signal).to(beNil()) - } - - it("should deallocate if it is unreachable and has no observer") { - let (sourceSignal, sourceObserver) = Signal.pipe() - - var firstCounter = 0 - var secondCounter = 0 - var thirdCounter = 0 - - func run() { - _ = sourceSignal - .map { value -> Int in - firstCounter += 1 - return value - } - .map { value -> Int in - secondCounter += 1 - return value - } - .map { value -> Int in - thirdCounter += 1 - return value - } - } - - run() - - sourceObserver.sendNext(1) - expect(firstCounter) == 0 - expect(secondCounter) == 0 - expect(thirdCounter) == 0 - - sourceObserver.sendNext(2) - expect(firstCounter) == 0 - expect(secondCounter) == 0 - expect(thirdCounter) == 0 - } - - it("should not deallocate if it is unreachable but still has at least one observer") { - let (sourceSignal, sourceObserver) = Signal.pipe() - - var firstCounter = 0 - var secondCounter = 0 - var thirdCounter = 0 - - var disposable: Disposable? - - func run() { - disposable = sourceSignal - .map { value -> Int in - firstCounter += 1 - return value - } - .map { value -> Int in - secondCounter += 1 - return value - } - .map { value -> Int in - thirdCounter += 1 - return value - } - .observe { _ in } - } - - run() - - sourceObserver.sendNext(1) - expect(firstCounter) == 1 - expect(secondCounter) == 1 - expect(thirdCounter) == 1 - - sourceObserver.sendNext(2) - expect(firstCounter) == 2 - expect(secondCounter) == 2 - expect(thirdCounter) == 2 - - disposable?.dispose() - - sourceObserver.sendNext(3) - expect(firstCounter) == 2 - expect(secondCounter) == 2 - expect(thirdCounter) == 2 - } - } - - describe("observe") { - var signal: Signal! - var observer: Signal.Observer! - - var token: NSObject? = nil - weak var weakToken: NSObject? - - func expectTokenNotDeallocated() { - expect(weakToken).toNot(beNil()) - } - - func expectTokenDeallocated() { - expect(weakToken).to(beNil()) - } - - beforeEach { - let (signalTemp, observerTemp) = Signal.pipe() - signal = signalTemp - observer = observerTemp - - token = NSObject() - weakToken = token - - signal.observe { [token = token] _ in - _ = token!.description - } - } - - it("should deallocate observe handler when signal completes") { - expectTokenNotDeallocated() - - observer.sendNext(1) - expectTokenNotDeallocated() - - token = nil - expectTokenNotDeallocated() - - observer.sendNext(2) - expectTokenNotDeallocated() - - observer.sendCompleted() - expectTokenDeallocated() - } - - it("should deallocate observe handler when signal fails") { - expectTokenNotDeallocated() - - observer.sendNext(1) - expectTokenNotDeallocated() - - token = nil - expectTokenNotDeallocated() - - observer.sendNext(2) - expectTokenNotDeallocated() - - observer.sendFailed(.default) - expectTokenDeallocated() - } - } - } -} - -private extension SignalProtocol { - func testTransform() -> Signal { - return Signal { observer in - return self.observe(observer.action) - } - } -} diff --git a/ReactiveSwiftTests/SignalProducerLiftingSpec.swift b/ReactiveSwiftTests/SignalProducerLiftingSpec.swift deleted file mode 100644 index 57ada877f5..0000000000 --- a/ReactiveSwiftTests/SignalProducerLiftingSpec.swift +++ /dev/null @@ -1,1536 +0,0 @@ -// -// SignalProducerLiftingSpec.swift -// ReactiveSwift -// -// Created by Neil Pankey on 6/14/15. -// Copyright © 2015 GitHub. All rights reserved. -// - -import Result -import Nimble -import Quick -import ReactiveSwift - -class SignalProducerLiftingSpec: QuickSpec { - override func spec() { - describe("map") { - it("should transform the values of the signal") { - let (producer, observer) = SignalProducer.pipe() - let mappedProducer = producer.map { String($0 + 1) } - - var lastValue: String? - - mappedProducer.startWithNext { - lastValue = $0 - return - } - - expect(lastValue).to(beNil()) - - observer.sendNext(0) - expect(lastValue) == "1" - - observer.sendNext(1) - expect(lastValue) == "2" - } - } - - describe("mapError") { - it("should transform the errors of the signal") { - let (producer, observer) = SignalProducer.pipe() - let producerError = NSError(domain: "com.reactivecocoa.errordomain", code: 100, userInfo: nil) - var error: NSError? - - producer - .mapError { _ in producerError } - .startWithFailed { error = $0 } - - expect(error).to(beNil()) - - observer.sendFailed(TestError.default) - expect(error) == producerError - } - } - - describe("filter") { - it("should omit values from the producer") { - let (producer, observer) = SignalProducer.pipe() - let mappedProducer = producer.filter { $0 % 2 == 0 } - - var lastValue: Int? - - mappedProducer.startWithNext { lastValue = $0 } - - expect(lastValue).to(beNil()) - - observer.sendNext(0) - expect(lastValue) == 0 - - observer.sendNext(1) - expect(lastValue) == 0 - - observer.sendNext(2) - expect(lastValue) == 2 - } - } - - describe("skipNil") { - it("should forward only non-nil values") { - let (producer, observer) = SignalProducer.pipe() - let mappedProducer = producer.skipNil() - - var lastValue: Int? - - mappedProducer.startWithNext { lastValue = $0 } - expect(lastValue).to(beNil()) - - observer.sendNext(nil) - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue) == 1 - - observer.sendNext(nil) - expect(lastValue) == 1 - - observer.sendNext(2) - expect(lastValue) == 2 - } - } - - describe("scan") { - it("should incrementally accumulate a value") { - let (baseProducer, observer) = SignalProducer.pipe() - let producer = baseProducer.scan("", +) - - var lastValue: String? - - producer.startWithNext { lastValue = $0 } - - expect(lastValue).to(beNil()) - - observer.sendNext("a") - expect(lastValue) == "a" - - observer.sendNext("bb") - expect(lastValue) == "abb" - } - } - - describe("reduce") { - it("should accumulate one value") { - let (baseProducer, observer) = SignalProducer.pipe() - let producer = baseProducer.reduce(1, +) - - var lastValue: Int? - var completed = false - - producer.start { event in - switch event { - case let .next(value): - lastValue = value - case .completed: - completed = true - case .failed, .interrupted: - break - } - } - - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue).to(beNil()) - - observer.sendNext(2) - expect(lastValue).to(beNil()) - - expect(completed) == false - observer.sendCompleted() - expect(completed) == true - - expect(lastValue) == 4 - } - - it("should send the initial value if none are received") { - let (baseProducer, observer) = SignalProducer.pipe() - let producer = baseProducer.reduce(1, +) - - var lastValue: Int? - var completed = false - - producer.start { event in - switch event { - case let .next(value): - lastValue = value - case .completed: - completed = true - case .failed, .interrupted: - break - } - } - - expect(lastValue).to(beNil()) - expect(completed) == false - - observer.sendCompleted() - - expect(lastValue) == 1 - expect(completed) == true - } - } - - describe("skip") { - it("should skip initial values") { - let (baseProducer, observer) = SignalProducer.pipe() - let producer = baseProducer.skip(first: 1) - - var lastValue: Int? - producer.startWithNext { lastValue = $0 } - - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue).to(beNil()) - - observer.sendNext(2) - expect(lastValue) == 2 - } - - it("should not skip any values when 0") { - let (baseProducer, observer) = SignalProducer.pipe() - let producer = baseProducer.skip(first: 0) - - var lastValue: Int? - producer.startWithNext { lastValue = $0 } - - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue) == 1 - - observer.sendNext(2) - expect(lastValue) == 2 - } - } - - describe("skipRepeats") { - it("should skip duplicate Equatable values") { - let (baseProducer, observer) = SignalProducer.pipe() - let producer = baseProducer.skipRepeats() - - var values: [Bool] = [] - producer.startWithNext { values.append($0) } - - expect(values) == [] - - observer.sendNext(true) - expect(values) == [ true ] - - observer.sendNext(true) - expect(values) == [ true ] - - observer.sendNext(false) - expect(values) == [ true, false ] - - observer.sendNext(true) - expect(values) == [ true, false, true ] - } - - it("should skip values according to a predicate") { - let (baseProducer, observer) = SignalProducer.pipe() - let producer = baseProducer.skipRepeats { $0.characters.count == $1.characters.count } - - var values: [String] = [] - producer.startWithNext { values.append($0) } - - expect(values) == [] - - observer.sendNext("a") - expect(values) == [ "a" ] - - observer.sendNext("b") - expect(values) == [ "a" ] - - observer.sendNext("cc") - expect(values) == [ "a", "cc" ] - - observer.sendNext("d") - expect(values) == [ "a", "cc", "d" ] - } - } - - describe("skipWhile") { - var producer: SignalProducer! - var observer: Signal.Observer! - - var lastValue: Int? - - beforeEach { - let (baseProducer, incomingObserver) = SignalProducer.pipe() - - producer = baseProducer.skip { $0 < 2 } - observer = incomingObserver - lastValue = nil - - producer.startWithNext { lastValue = $0 } - } - - it("should skip while the predicate is true") { - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue).to(beNil()) - - observer.sendNext(2) - expect(lastValue) == 2 - - observer.sendNext(0) - expect(lastValue) == 0 - } - - it("should not skip any values when the predicate starts false") { - expect(lastValue).to(beNil()) - - observer.sendNext(3) - expect(lastValue) == 3 - - observer.sendNext(1) - expect(lastValue) == 1 - } - } - - describe("skipUntil") { - var producer: SignalProducer! - var observer: Signal.Observer! - var triggerObserver: Signal<(), NoError>.Observer! - - var lastValue: Int? = nil - - beforeEach { - let (baseProducer, baseIncomingObserver) = SignalProducer.pipe() - let (triggerProducer, incomingTriggerObserver) = SignalProducer<(), NoError>.pipe() - - producer = baseProducer.skip(until: triggerProducer) - observer = baseIncomingObserver - triggerObserver = incomingTriggerObserver - - lastValue = nil - - producer.start { event in - switch event { - case let .next(value): - lastValue = value - case .failed, .completed, .interrupted: - break - } - } - } - - it("should skip values until the trigger fires") { - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue).to(beNil()) - - observer.sendNext(2) - expect(lastValue).to(beNil()) - - triggerObserver.sendNext(()) - observer.sendNext(0) - expect(lastValue) == 0 - } - - it("should skip values until the trigger completes") { - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue).to(beNil()) - - observer.sendNext(2) - expect(lastValue).to(beNil()) - - triggerObserver.sendCompleted() - observer.sendNext(0) - expect(lastValue) == 0 - } - } - - describe("take") { - it("should take initial values") { - let (baseProducer, observer) = SignalProducer.pipe() - let producer = baseProducer.take(first: 2) - - var lastValue: Int? - var completed = false - producer.start { event in - switch event { - case let .next(value): - lastValue = value - case .completed: - completed = true - case .failed, .interrupted: - break - } - } - - expect(lastValue).to(beNil()) - expect(completed) == false - - observer.sendNext(1) - expect(lastValue) == 1 - expect(completed) == false - - observer.sendNext(2) - expect(lastValue) == 2 - expect(completed) == true - } - - it("should complete immediately after taking given number of values") { - let numbers = [ 1, 2, 4, 4, 5 ] - let testScheduler = TestScheduler() - - let producer: SignalProducer = SignalProducer { observer, _ in - // workaround `Class declaration cannot close over value 'observer' defined in outer scope` - let observer = observer - - testScheduler.schedule { - for number in numbers { - observer.sendNext(number) - } - } - } - - var completed = false - - producer - .take(first: numbers.count) - .startWithCompleted { completed = true } - - expect(completed) == false - testScheduler.run() - expect(completed) == true - } - - it("should interrupt when 0") { - let numbers = [ 1, 2, 4, 4, 5 ] - let testScheduler = TestScheduler() - - let producer: SignalProducer = SignalProducer { observer, _ in - // workaround `Class declaration cannot close over value 'observer' defined in outer scope` - let observer = observer - - testScheduler.schedule { - for number in numbers { - observer.sendNext(number) - } - } - } - - var result: [Int] = [] - var interrupted = false - - producer - .take(first: 0) - .start { event in - switch event { - case let .next(number): - result.append(number) - case .interrupted: - interrupted = true - case .failed, .completed: - break - } - } - - expect(interrupted) == true - - testScheduler.run() - expect(result).to(beEmpty()) - } - } - - describe("collect") { - it("should collect all values") { - let (original, observer) = SignalProducer.pipe() - let producer = original.collect() - let expectedResult = [ 1, 2, 3 ] - - var result: [Int]? - - producer.startWithNext { value in - expect(result).to(beNil()) - result = value - } - - for number in expectedResult { - observer.sendNext(number) - } - - expect(result).to(beNil()) - observer.sendCompleted() - expect(result) == expectedResult - } - - it("should complete with an empty array if there are no values") { - let (original, observer) = SignalProducer.pipe() - let producer = original.collect() - - var result: [Int]? - - producer.startWithNext { result = $0 } - - expect(result).to(beNil()) - observer.sendCompleted() - expect(result) == [] - } - - it("should forward errors") { - let (original, observer) = SignalProducer.pipe() - let producer = original.collect() - - var error: TestError? - - producer.startWithFailed { error = $0 } - - expect(error).to(beNil()) - observer.sendFailed(.default) - expect(error) == TestError.default - } - - it("should collect an exact count of values") { - let (original, observer) = SignalProducer.pipe() - - let producer = original.collect(count: 3) - - var observedValues: [[Int]] = [] - - producer.startWithNext { value in - observedValues.append(value) - } - - var expectation: [[Int]] = [] - - for i in 1...7 { - - observer.sendNext(i) - - if i % 3 == 0 { - expectation.append([Int]((i - 2)...i)) - expect(observedValues as NSArray) == expectation as NSArray - } else { - expect(observedValues as NSArray) == expectation as NSArray - } - } - - observer.sendCompleted() - - expectation.append([7]) - expect(observedValues as NSArray) == expectation as NSArray - } - - it("should collect values until it matches a certain value") { - let (original, observer) = SignalProducer.pipe() - - let producer = original.collect { _, next in next != 5 } - - var expectedValues = [ - [5, 5], - [42, 5] - ] - - producer.startWithNext { value in - expect(value) == expectedValues.removeFirst() - } - - producer.startWithCompleted { - expect(expectedValues as NSArray) == [] as NSArray - } - - expectedValues - .flatMap { $0 } - .forEach(observer.sendNext) - - observer.sendCompleted() - } - - it("should collect values until it matches a certain condition on values") { - let (original, observer) = SignalProducer.pipe() - - let producer = original.collect { values in values.reduce(0, +) == 10 } - - var expectedValues = [ - [1, 2, 3, 4], - [5, 6, 7, 8, 9] - ] - - producer.startWithNext { value in - expect(value) == expectedValues.removeFirst() - } - - producer.startWithCompleted { - expect(expectedValues as NSArray) == [] as NSArray - } - - expectedValues - .flatMap { $0 } - .forEach(observer.sendNext) - - observer.sendCompleted() - } - - } - - describe("takeUntil") { - var producer: SignalProducer! - var observer: Signal.Observer! - var triggerObserver: Signal<(), NoError>.Observer! - - var lastValue: Int? = nil - var completed: Bool = false - - beforeEach { - let (baseProducer, baseIncomingObserver) = SignalProducer.pipe() - let (triggerProducer, incomingTriggerObserver) = SignalProducer<(), NoError>.pipe() - - producer = baseProducer.take(until: triggerProducer) - observer = baseIncomingObserver - triggerObserver = incomingTriggerObserver - - lastValue = nil - completed = false - - producer.start { event in - switch event { - case let .next(value): - lastValue = value - case .completed: - completed = true - case .failed, .interrupted: - break - } - } - } - - it("should take values until the trigger fires") { - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue) == 1 - - observer.sendNext(2) - expect(lastValue) == 2 - - expect(completed) == false - triggerObserver.sendNext(()) - expect(completed) == true - } - - it("should take values until the trigger completes") { - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue) == 1 - - observer.sendNext(2) - expect(lastValue) == 2 - - expect(completed) == false - triggerObserver.sendCompleted() - expect(completed) == true - } - - it("should complete if the trigger fires immediately") { - expect(lastValue).to(beNil()) - expect(completed) == false - - triggerObserver.sendNext(()) - - expect(completed) == true - expect(lastValue).to(beNil()) - } - } - - describe("takeUntilReplacement") { - var producer: SignalProducer! - var observer: Signal.Observer! - var replacementObserver: Signal.Observer! - - var lastValue: Int? = nil - var completed: Bool = false - - beforeEach { - let (baseProducer, incomingObserver) = SignalProducer.pipe() - let (replacementProducer, incomingReplacementObserver) = SignalProducer.pipe() - - producer = baseProducer.take(untilReplacement: replacementProducer) - observer = incomingObserver - replacementObserver = incomingReplacementObserver - - lastValue = nil - completed = false - - producer.start { event in - switch event { - case let .next(value): - lastValue = value - case .completed: - completed = true - case .failed, .interrupted: - break - } - } - } - - it("should take values from the original then the replacement") { - expect(lastValue).to(beNil()) - expect(completed) == false - - observer.sendNext(1) - expect(lastValue) == 1 - - observer.sendNext(2) - expect(lastValue) == 2 - - replacementObserver.sendNext(3) - - expect(lastValue) == 3 - expect(completed) == false - - observer.sendNext(4) - - expect(lastValue) == 3 - expect(completed) == false - - replacementObserver.sendNext(5) - expect(lastValue) == 5 - - expect(completed) == false - replacementObserver.sendCompleted() - expect(completed) == true - } - } - - describe("takeWhile") { - var producer: SignalProducer! - var observer: Signal.Observer! - - beforeEach { - let (baseProducer, incomingObserver) = SignalProducer.pipe() - producer = baseProducer.take { $0 <= 4 } - observer = incomingObserver - } - - it("should take while the predicate is true") { - var latestValue: Int! - var completed = false - - producer.start { event in - switch event { - case let .next(value): - latestValue = value - case .completed: - completed = true - case .failed, .interrupted: - break - } - } - - for value in -1...4 { - observer.sendNext(value) - expect(latestValue) == value - expect(completed) == false - } - - observer.sendNext(5) - expect(latestValue) == 4 - expect(completed) == true - } - - it("should complete if the predicate starts false") { - var latestValue: Int? - var completed = false - - producer.start { event in - switch event { - case let .next(value): - latestValue = value - case .completed: - completed = true - case .failed, .interrupted: - break - } - } - - observer.sendNext(5) - expect(latestValue).to(beNil()) - expect(completed) == true - } - } - - describe("observeOn") { - it("should send events on the given scheduler") { - let testScheduler = TestScheduler() - let (producer, observer) = SignalProducer.pipe() - - var result: [Int] = [] - - producer - .observe(on: testScheduler) - .startWithNext { result.append($0) } - - observer.sendNext(1) - observer.sendNext(2) - expect(result).to(beEmpty()) - - testScheduler.run() - expect(result) == [ 1, 2 ] - } - } - - describe("delay") { - it("should send events on the given scheduler after the interval") { - let testScheduler = TestScheduler() - let producer: SignalProducer = SignalProducer { observer, _ in - testScheduler.schedule { - observer.sendNext(1) - } - testScheduler.schedule(after: 5) { - observer.sendNext(2) - observer.sendCompleted() - } - } - - var result: [Int] = [] - var completed = false - - producer - .delay(10, on: testScheduler) - .start { event in - switch event { - case let .next(number): - result.append(number) - case .completed: - completed = true - case .failed, .interrupted: - break - } - } - - testScheduler.advance(by: 4) // send initial value - expect(result).to(beEmpty()) - - testScheduler.advance(by: 10) // send second value and receive first - expect(result) == [ 1 ] - expect(completed) == false - - testScheduler.advance(by: 10) // send second value and receive first - expect(result) == [ 1, 2 ] - expect(completed) == true - } - - it("should schedule errors immediately") { - let testScheduler = TestScheduler() - let producer: SignalProducer = SignalProducer { observer, _ in - // workaround `Class declaration cannot close over value 'observer' defined in outer scope` - let observer = observer - - testScheduler.schedule { - observer.sendFailed(TestError.default) - } - } - - var errored = false - - producer - .delay(10, on: testScheduler) - .startWithFailed { _ in errored = true } - - testScheduler.advance() - expect(errored) == true - } - } - - describe("throttle") { - var scheduler: TestScheduler! - var observer: Signal.Observer! - var producer: SignalProducer! - - beforeEach { - scheduler = TestScheduler() - - let (baseProducer, baseObserver) = SignalProducer.pipe() - observer = baseObserver - - producer = baseProducer.throttle(1, on: scheduler) - } - - it("should send values on the given scheduler at no less than the interval") { - var values: [Int] = [] - producer.startWithNext { value in - values.append(value) - } - - expect(values) == [] - - observer.sendNext(0) - expect(values) == [] - - scheduler.advance() - expect(values) == [ 0 ] - - observer.sendNext(1) - observer.sendNext(2) - expect(values) == [ 0 ] - - scheduler.advance(by: 1.5) - expect(values) == [ 0, 2 ] - - scheduler.advance(by: 3) - expect(values) == [ 0, 2 ] - - observer.sendNext(3) - expect(values) == [ 0, 2 ] - - scheduler.advance() - expect(values) == [ 0, 2, 3 ] - - observer.sendNext(4) - observer.sendNext(5) - scheduler.advance() - expect(values) == [ 0, 2, 3 ] - - scheduler.rewind(by: 2) - expect(values) == [ 0, 2, 3 ] - - observer.sendNext(6) - scheduler.advance() - expect(values) == [ 0, 2, 3, 6 ] - - observer.sendNext(7) - observer.sendNext(8) - scheduler.advance() - expect(values) == [ 0, 2, 3, 6 ] - - scheduler.run() - expect(values) == [ 0, 2, 3, 6, 8 ] - } - - it("should schedule completion immediately") { - var values: [Int] = [] - var completed = false - - producer.start { event in - switch event { - case let .next(value): - values.append(value) - case .completed: - completed = true - case .failed, .interrupted: - break - } - } - - observer.sendNext(0) - scheduler.advance() - expect(values) == [ 0 ] - - observer.sendNext(1) - observer.sendCompleted() - expect(completed) == false - - scheduler.run() - expect(values) == [ 0 ] - expect(completed) == true - } - } - - describe("sampleWith") { - var sampledProducer: SignalProducer<(Int, String), NoError>! - var observer: Signal.Observer! - var samplerObserver: Signal.Observer! - - beforeEach { - let (producer, incomingObserver) = SignalProducer.pipe() - let (sampler, incomingSamplerObserver) = SignalProducer.pipe() - sampledProducer = producer.sample(with: sampler) - observer = incomingObserver - samplerObserver = incomingSamplerObserver - } - - it("should forward the latest value when the sampler fires") { - var result: [String] = [] - sampledProducer.startWithNext { (left, right) in result.append("\(left)\(right)") } - - observer.sendNext(1) - observer.sendNext(2) - samplerObserver.sendNext("a") - expect(result) == [ "2a" ] - } - - it("should do nothing if sampler fires before signal receives value") { - var result: [String] = [] - sampledProducer.startWithNext { (left, right) in result.append("\(left)\(right)") } - - samplerObserver.sendNext("a") - expect(result).to(beEmpty()) - } - - it("should send lates value multiple times when sampler fires multiple times") { - var result: [String] = [] - sampledProducer.startWithNext { (left, right) in result.append("\(left)\(right)") } - - observer.sendNext(1) - samplerObserver.sendNext("a") - samplerObserver.sendNext("b") - expect(result) == [ "1a", "1b" ] - } - - it("should complete when both inputs have completed") { - var completed = false - sampledProducer.startWithCompleted { completed = true } - - observer.sendCompleted() - expect(completed) == false - - samplerObserver.sendCompleted() - expect(completed) == true - } - - it("should emit an initial value if the sampler is a synchronous SignalProducer") { - let producer = SignalProducer(values: [1]) - let sampler = SignalProducer(value: "a") - - let result = producer.sample(with: sampler) - - var valueReceived: String? - result.startWithNext { (left, right) in valueReceived = "\(left)\(right)" } - - expect(valueReceived) == "1a" - } - } - - describe("sampleOn") { - var sampledProducer: SignalProducer! - var observer: Signal.Observer! - var samplerObserver: Signal<(), NoError>.Observer! - - beforeEach { - let (producer, incomingObserver) = SignalProducer.pipe() - let (sampler, incomingSamplerObserver) = SignalProducer<(), NoError>.pipe() - sampledProducer = producer.sample(on: sampler) - observer = incomingObserver - samplerObserver = incomingSamplerObserver - } - - it("should forward the latest value when the sampler fires") { - var result: [Int] = [] - sampledProducer.startWithNext { result.append($0) } - - observer.sendNext(1) - observer.sendNext(2) - samplerObserver.sendNext(()) - expect(result) == [ 2 ] - } - - it("should do nothing if sampler fires before signal receives value") { - var result: [Int] = [] - sampledProducer.startWithNext { result.append($0) } - - samplerObserver.sendNext(()) - expect(result).to(beEmpty()) - } - - it("should send lates value multiple times when sampler fires multiple times") { - var result: [Int] = [] - sampledProducer.startWithNext { result.append($0) } - - observer.sendNext(1) - samplerObserver.sendNext(()) - samplerObserver.sendNext(()) - expect(result) == [ 1, 1 ] - } - - it("should complete when both inputs have completed") { - var completed = false - sampledProducer.startWithCompleted { completed = true } - - observer.sendCompleted() - expect(completed) == false - - samplerObserver.sendCompleted() - expect(completed) == true - } - - it("should emit an initial value if the sampler is a synchronous SignalProducer") { - let producer = SignalProducer(values: [1]) - let sampler = SignalProducer<(), NoError>(value: ()) - - let result = producer.sample(on: sampler) - - var valueReceived: Int? - result.startWithNext { valueReceived = $0 } - - expect(valueReceived) == 1 - } - - describe("memory") { - class Payload { - let action: () -> Void - - init(onDeinit action: @escaping () -> Void) { - self.action = action - } - - deinit { - action() - } - } - - var sampledProducer: SignalProducer! - var observer: Signal.Observer! - - beforeEach { - let (producer, incomingObserver) = SignalProducer.pipe() - let (sampler, _) = Signal<(), NoError>.pipe() - sampledProducer = producer.sample(on: sampler) - observer = incomingObserver - } - - it("should free payload when interrupted after complete of incoming producer") { - var payloadFreed = false - - let disposable = sampledProducer.start() - - observer.sendNext(Payload { payloadFreed = true }) - observer.sendCompleted() - - expect(payloadFreed) == false - - disposable.dispose() - expect(payloadFreed) == true - } - } - } - - describe("combineLatestWith") { - var combinedProducer: SignalProducer<(Int, Double), NoError>! - var observer: Signal.Observer! - var otherObserver: Signal.Observer! - - beforeEach { - let (producer, incomingObserver) = SignalProducer.pipe() - let (otherSignal, incomingOtherObserver) = SignalProducer.pipe() - combinedProducer = producer.combineLatest(with: otherSignal) - observer = incomingObserver - otherObserver = incomingOtherObserver - } - - it("should forward the latest values from both inputs") { - var latest: (Int, Double)? - combinedProducer.startWithNext { latest = $0 } - - observer.sendNext(1) - expect(latest).to(beNil()) - - // is there a better way to test tuples? - otherObserver.sendNext(1.5) - expect(latest?.0) == 1 - expect(latest?.1) == 1.5 - - observer.sendNext(2) - expect(latest?.0) == 2 - expect(latest?.1) == 1.5 - } - - it("should complete when both inputs have completed") { - var completed = false - combinedProducer.startWithCompleted { completed = true } - - observer.sendCompleted() - expect(completed) == false - - otherObserver.sendCompleted() - expect(completed) == true - } - } - - describe("zipWith") { - var leftObserver: Signal.Observer! - var rightObserver: Signal.Observer! - var zipped: SignalProducer<(Int, String), NoError>! - - beforeEach { - let (leftProducer, incomingLeftObserver) = SignalProducer.pipe() - let (rightProducer, incomingRightObserver) = SignalProducer.pipe() - - leftObserver = incomingLeftObserver - rightObserver = incomingRightObserver - zipped = leftProducer.zip(with: rightProducer) - } - - it("should combine pairs") { - var result: [String] = [] - zipped.startWithNext { (left, right) in result.append("\(left)\(right)") } - - leftObserver.sendNext(1) - leftObserver.sendNext(2) - expect(result) == [] - - rightObserver.sendNext("foo") - expect(result) == [ "1foo" ] - - leftObserver.sendNext(3) - rightObserver.sendNext("bar") - expect(result) == [ "1foo", "2bar" ] - - rightObserver.sendNext("buzz") - expect(result) == [ "1foo", "2bar", "3buzz" ] - - rightObserver.sendNext("fuzz") - expect(result) == [ "1foo", "2bar", "3buzz" ] - - leftObserver.sendNext(4) - expect(result) == [ "1foo", "2bar", "3buzz", "4fuzz" ] - } - - it("should complete when the shorter signal has completed") { - var result: [String] = [] - var completed = false - - zipped.start { event in - switch event { - case let .next(left, right): - result.append("\(left)\(right)") - case .completed: - completed = true - case .failed, .interrupted: - break - } - } - - expect(completed) == false - - leftObserver.sendNext(0) - leftObserver.sendCompleted() - expect(completed) == false - expect(result) == [] - - rightObserver.sendNext("foo") - expect(completed) == true - expect(result) == [ "0foo" ] - } - } - - describe("materialize") { - it("should reify events from the signal") { - let (producer, observer) = SignalProducer.pipe() - var latestEvent: Event? - producer - .materialize() - .startWithNext { latestEvent = $0 } - - observer.sendNext(2) - - expect(latestEvent).toNot(beNil()) - if let latestEvent = latestEvent { - switch latestEvent { - case let .next(value): - expect(value) == 2 - case .failed, .completed, .interrupted: - fail() - } - } - - observer.sendFailed(TestError.default) - if let latestEvent = latestEvent { - switch latestEvent { - case .failed: - break - case .next, .completed, .interrupted: - fail() - } - } - } - } - - describe("dematerialize") { - typealias IntEvent = Event - var observer: Signal.Observer! - var dematerialized: SignalProducer! - - beforeEach { - let (producer, incomingObserver) = SignalProducer.pipe() - observer = incomingObserver - dematerialized = producer.dematerialize() - } - - it("should send values for Next events") { - var result: [Int] = [] - dematerialized - .assumeNoErrors() - .startWithNext { result.append($0) } - - expect(result).to(beEmpty()) - - observer.sendNext(.next(2)) - expect(result) == [ 2 ] - - observer.sendNext(.next(4)) - expect(result) == [ 2, 4 ] - } - - it("should error out for Error events") { - var errored = false - dematerialized.startWithFailed { _ in errored = true } - - expect(errored) == false - - observer.sendNext(.failed(TestError.default)) - expect(errored) == true - } - - it("should complete early for Completed events") { - var completed = false - dematerialized.startWithCompleted { completed = true } - - expect(completed) == false - observer.sendNext(IntEvent.completed) - expect(completed) == true - } - } - - describe("takeLast") { - var observer: Signal.Observer! - var lastThree: SignalProducer! - - beforeEach { - let (producer, incomingObserver) = SignalProducer.pipe() - observer = incomingObserver - lastThree = producer.take(last: 3) - } - - it("should send the last N values upon completion") { - var result: [Int] = [] - lastThree - .assumeNoErrors() - .startWithNext { result.append($0) } - - observer.sendNext(1) - observer.sendNext(2) - observer.sendNext(3) - observer.sendNext(4) - expect(result).to(beEmpty()) - - observer.sendCompleted() - expect(result) == [ 2, 3, 4 ] - } - - it("should send less than N values if not enough were received") { - var result: [Int] = [] - lastThree - .assumeNoErrors() - .startWithNext { result.append($0) } - - observer.sendNext(1) - observer.sendNext(2) - observer.sendCompleted() - expect(result) == [ 1, 2 ] - } - - it("should send nothing when errors") { - var result: [Int] = [] - var errored = false - lastThree.start { event in - switch event { - case let .next(value): - result.append(value) - case .failed: - errored = true - case .completed, .interrupted: - break - } - } - - observer.sendNext(1) - observer.sendNext(2) - observer.sendNext(3) - expect(errored) == false - - observer.sendFailed(TestError.default) - expect(errored) == true - expect(result).to(beEmpty()) - } - } - - describe("timeoutWithError") { - var testScheduler: TestScheduler! - var producer: SignalProducer! - var observer: Signal.Observer! - - beforeEach { - testScheduler = TestScheduler() - let (baseProducer, incomingObserver) = SignalProducer.pipe() - producer = baseProducer.timeout(after: 2, raising: TestError.default, on: testScheduler) - observer = incomingObserver - } - - it("should complete if within the interval") { - var completed = false - var errored = false - producer.start { event in - switch event { - case .completed: - completed = true - case .failed: - errored = true - case .next, .interrupted: - break - } - } - - testScheduler.schedule(after: 1) { - observer.sendCompleted() - } - - expect(completed) == false - expect(errored) == false - - testScheduler.run() - expect(completed) == true - expect(errored) == false - } - - it("should error if not completed before the interval has elapsed") { - var completed = false - var errored = false - producer.start { event in - switch event { - case .completed: - completed = true - case .failed: - errored = true - case .next, .interrupted: - break - } - } - - testScheduler.schedule(after: 3) { - observer.sendCompleted() - } - - expect(completed) == false - expect(errored) == false - - testScheduler.run() - expect(completed) == false - expect(errored) == true - } - - it("should be available for NoError") { - let producer: SignalProducer = SignalProducer.never - .timeout(after: 2, raising: TestError.default, on: testScheduler) - - _ = producer - } - } - - describe("attempt") { - it("should forward original values upon success") { - let (baseProducer, observer) = SignalProducer.pipe() - let producer = baseProducer.attempt { _ in - return .success() - } - - var current: Int? - producer - .assumeNoErrors() - .startWithNext { value in - current = value - } - - for value in 1...5 { - observer.sendNext(value) - expect(current) == value - } - } - - it("should error if an attempt fails") { - let (baseProducer, observer) = SignalProducer.pipe() - let producer = baseProducer.attempt { _ in - return .failure(.default) - } - - var error: TestError? - producer.startWithFailed { err in - error = err - } - - observer.sendNext(42) - expect(error) == TestError.default - } - } - - describe("attemptMap") { - it("should forward mapped values upon success") { - let (baseProducer, observer) = SignalProducer.pipe() - let producer = baseProducer.attemptMap { num -> Result in - return .success(num % 2 == 0) - } - - var even: Bool? - producer - .assumeNoErrors() - .startWithNext { value in - even = value - } - - observer.sendNext(1) - expect(even) == false - - observer.sendNext(2) - expect(even) == true - } - - it("should error if a mapping fails") { - let (baseProducer, observer) = SignalProducer.pipe() - let producer = baseProducer.attemptMap { _ -> Result in - return .failure(.default) - } - - var error: TestError? - producer.startWithFailed { err in - error = err - } - - observer.sendNext(42) - expect(error) == TestError.default - } - } - - describe("combinePrevious") { - var observer: Signal.Observer! - let initialValue: Int = 0 - var latestValues: (Int, Int)? - - beforeEach { - latestValues = nil - - let (signal, baseObserver) = SignalProducer.pipe() - observer = baseObserver - signal.combinePrevious(initialValue).startWithNext { latestValues = $0 } - } - - it("should forward the latest value with previous value") { - expect(latestValues).to(beNil()) - - observer.sendNext(1) - expect(latestValues?.0) == initialValue - expect(latestValues?.1) == 1 - - observer.sendNext(2) - expect(latestValues?.0) == 1 - expect(latestValues?.1) == 2 - } - } - } -} diff --git a/ReactiveSwiftTests/SignalProducerNimbleMatchers.swift b/ReactiveSwiftTests/SignalProducerNimbleMatchers.swift deleted file mode 100644 index bc9654a3e4..0000000000 --- a/ReactiveSwiftTests/SignalProducerNimbleMatchers.swift +++ /dev/null @@ -1,57 +0,0 @@ -// -// SignalProducerNimbleMatchers.swift -// ReactiveSwift -// -// Created by Javier Soto on 1/25/15. -// Copyright (c) 2015 GitHub. All rights reserved. -// - -import Foundation - -import ReactiveSwift -import Nimble - -public func sendValue(_ value: T?, sendError: E?, complete: Bool) -> NonNilMatcherFunc> { - return sendValues(value.map { [$0] } ?? [], sendError: sendError, complete: complete) -} - -public func sendValues(_ values: [T], sendError maybeSendError: E?, complete: Bool) -> NonNilMatcherFunc> { - return NonNilMatcherFunc { actualExpression, failureMessage in - precondition(maybeSendError == nil || !complete, "Signals can't both send an error and complete") - - failureMessage.postfixMessage = "Send values \(values). Send error \(maybeSendError). Complete: \(complete)" - let maybeProducer = try actualExpression.evaluate() - - if let signalProducer = maybeProducer { - var sentValues: [T] = [] - var sentError: E? - var signalCompleted = false - - signalProducer.start { event in - switch event { - case let .next(value): - sentValues.append(value) - case .completed: - signalCompleted = true - case let .failed(error): - sentError = error - default: - break - } - } - - if sentValues != values { - return false - } - - if sentError != maybeSendError { - return false - } - - return signalCompleted == complete - } - else { - return false - } - } -} diff --git a/ReactiveSwiftTests/SignalProducerSpec.swift b/ReactiveSwiftTests/SignalProducerSpec.swift deleted file mode 100644 index d518da8e54..0000000000 --- a/ReactiveSwiftTests/SignalProducerSpec.swift +++ /dev/null @@ -1,2315 +0,0 @@ -// -// SignalProducerSpec.swift -// ReactiveSwift -// -// Created by Justin Spahr-Summers on 2015-01-23. -// Copyright (c) 2015 GitHub. All rights reserved. -// - -import Foundation - -import Result -import Nimble -import Quick -import ReactiveSwift - -class SignalProducerSpec: QuickSpec { - override func spec() { - describe("init") { - it("should run the handler once per start()") { - var handlerCalledTimes = 0 - let signalProducer = SignalProducer() { observer, disposable in - handlerCalledTimes += 1 - - return - } - - signalProducer.start() - signalProducer.start() - - expect(handlerCalledTimes) == 2 - } - - it("should not release signal observers when given disposable is disposed") { - var disposable: Disposable! - - let producer = SignalProducer { observer, innerDisposable in - disposable = innerDisposable - - innerDisposable += { - // This is necessary to keep the observer long enough to - // even test the memory management. - observer.sendNext(0) - } - } - - weak var objectRetainedByObserver: NSObject? - producer.startWithSignal { signal, _ in - let object = NSObject() - objectRetainedByObserver = object - signal.observeNext { _ in _ = object } - } - - expect(objectRetainedByObserver).toNot(beNil()) - - disposable.dispose() - - // https://github.com/ReactiveCocoa/ReactiveCocoa/pull/2959 - // - // Before #2959, this would be `nil` as the input observer is not - // retained, and observers would not retain the signal. - // - // After #2959, the object is still retained, since the observation - // keeps the signal alive. - expect(objectRetainedByObserver).toNot(beNil()) - } - - it("should dispose of added disposables upon completion") { - let addedDisposable = SimpleDisposable() - var observer: Signal<(), NoError>.Observer! - - let producer = SignalProducer<(), NoError>() { incomingObserver, disposable in - disposable += addedDisposable - observer = incomingObserver - } - - producer.start() - expect(addedDisposable.isDisposed) == false - - observer.sendCompleted() - expect(addedDisposable.isDisposed) == true - } - - it("should dispose of added disposables upon error") { - let addedDisposable = SimpleDisposable() - var observer: Signal<(), TestError>.Observer! - - let producer = SignalProducer<(), TestError>() { incomingObserver, disposable in - disposable += addedDisposable - observer = incomingObserver - } - - producer.start() - expect(addedDisposable.isDisposed) == false - - observer.sendFailed(.default) - expect(addedDisposable.isDisposed) == true - } - - it("should dispose of added disposables upon interruption") { - let addedDisposable = SimpleDisposable() - var observer: Signal<(), NoError>.Observer! - - let producer = SignalProducer<(), NoError>() { incomingObserver, disposable in - disposable += addedDisposable - observer = incomingObserver - } - - producer.start() - expect(addedDisposable.isDisposed) == false - - observer.sendInterrupted() - expect(addedDisposable.isDisposed) == true - } - - it("should dispose of added disposables upon start() disposal") { - let addedDisposable = SimpleDisposable() - - let producer = SignalProducer<(), TestError>() { _, disposable in - disposable += addedDisposable - return - } - - let startDisposable = producer.start() - expect(addedDisposable.isDisposed) == false - - startDisposable.dispose() - expect(addedDisposable.isDisposed) == true - } - } - - describe("init(signal:)") { - var signal: Signal! - var observer: Signal.Observer! - - beforeEach { - // Cannot directly assign due to compiler crash on Xcode 7.0.1 - let (signalTemp, observerTemp) = Signal.pipe() - signal = signalTemp - observer = observerTemp - } - - it("should emit values then complete") { - let producer = SignalProducer(signal: signal) - - var values: [Int] = [] - var error: TestError? - var completed = false - producer.start { event in - switch event { - case let .next(value): - values.append(value) - case let .failed(err): - error = err - case .completed: - completed = true - default: - break - } - } - - expect(values) == [] - expect(error).to(beNil()) - expect(completed) == false - - observer.sendNext(1) - expect(values) == [ 1 ] - observer.sendNext(2) - observer.sendNext(3) - expect(values) == [ 1, 2, 3 ] - - observer.sendCompleted() - expect(completed) == true - } - - it("should emit error") { - let producer = SignalProducer(signal: signal) - - var error: TestError? - let sentError = TestError.default - - producer.start { event in - switch event { - case let .failed(err): - error = err - default: - break - } - } - - expect(error).to(beNil()) - - observer.sendFailed(sentError) - expect(error) == sentError - } - } - - describe("init(value:)") { - it("should immediately send the value then complete") { - let producerValue = "StringValue" - let signalProducer = SignalProducer(value: producerValue) - - expect(signalProducer).to(sendValue(producerValue, sendError: nil, complete: true)) - } - } - - describe("init(error:)") { - it("should immediately send the error") { - let producerError = NSError(domain: "com.reactivecocoa.errordomain", code: 4815, userInfo: nil) - let signalProducer = SignalProducer(error: producerError) - - expect(signalProducer).to(sendValue(nil, sendError: producerError, complete: false)) - } - } - - describe("init(result:)") { - it("should immediately send the value then complete") { - let producerValue = "StringValue" - let producerResult = .success(producerValue) as Result - let signalProducer = SignalProducer(result: producerResult) - - expect(signalProducer).to(sendValue(producerValue, sendError: nil, complete: true)) - } - - it("should immediately send the error") { - let producerError = NSError(domain: "com.reactivecocoa.errordomain", code: 4815, userInfo: nil) - let producerResult = .failure(producerError) as Result - let signalProducer = SignalProducer(result: producerResult) - - expect(signalProducer).to(sendValue(nil, sendError: producerError, complete: false)) - } - } - - describe("init(values:)") { - it("should immediately send the sequence of values") { - let sequenceValues = [1, 2, 3] - let signalProducer = SignalProducer(values: sequenceValues) - - expect(signalProducer).to(sendValues(sequenceValues, sendError: nil, complete: true)) - } - } - - describe("SignalProducer.empty") { - it("should immediately complete") { - let signalProducer = SignalProducer.empty - - expect(signalProducer).to(sendValue(nil, sendError: nil, complete: true)) - } - } - - describe("SignalProducer.never") { - it("should not send any events") { - let signalProducer = SignalProducer.never - - expect(signalProducer).to(sendValue(nil, sendError: nil, complete: false)) - } - } - - describe("trailing closure") { - it("receives next values") { - let (producer, observer) = SignalProducer.pipe() - - var values = [Int]() - producer.startWithNext { next in - values.append(next) - } - - observer.sendNext(1) - expect(values) == [1] - } - } - - describe("SignalProducer.attempt") { - it("should run the operation once per start()") { - var operationRunTimes = 0 - let operation: () -> Result = { - operationRunTimes += 1 - - return .success("OperationValue") - } - - SignalProducer.attempt(operation).start() - SignalProducer.attempt(operation).start() - - expect(operationRunTimes) == 2 - } - - it("should send the value then complete") { - let operationReturnValue = "OperationValue" - let operation: () -> Result = { - return .success(operationReturnValue) - } - - let signalProducer = SignalProducer.attempt(operation) - - expect(signalProducer).to(sendValue(operationReturnValue, sendError: nil, complete: true)) - } - - it("should send the error") { - let operationError = NSError(domain: "com.reactivecocoa.errordomain", code: 4815, userInfo: nil) - let operation: () -> Result = { - return .failure(operationError) - } - - let signalProducer = SignalProducer.attempt(operation) - - expect(signalProducer).to(sendValue(nil, sendError: operationError, complete: false)) - } - } - - describe("startWithSignal") { - it("should invoke the closure before any effects or events") { - var started = false - var value: Int? - - SignalProducer(value: 42) - .on(started: { - started = true - }, next: { - value = $0 - }) - .startWithSignal { _ in - expect(started) == false - expect(value).to(beNil()) - } - - expect(started) == true - expect(value) == 42 - } - - it("should dispose of added disposables if disposed") { - let addedDisposable = SimpleDisposable() - var disposable: Disposable! - - let producer = SignalProducer() { _, disposable in - disposable += addedDisposable - return - } - - producer.startWithSignal { _, innerDisposable in - disposable = innerDisposable - } - - expect(addedDisposable.isDisposed) == false - - disposable.dispose() - expect(addedDisposable.isDisposed) == true - } - - it("should send interrupted if disposed") { - var interrupted = false - var disposable: Disposable! - - SignalProducer(value: 42) - .start(on: TestScheduler()) - .startWithSignal { signal, innerDisposable in - signal.observeInterrupted { - interrupted = true - } - - disposable = innerDisposable - } - - expect(interrupted) == false - - disposable.dispose() - expect(interrupted) == true - } - - it("should release signal observers if disposed") { - weak var objectRetainedByObserver: NSObject? - var disposable: Disposable! - - let producer = SignalProducer.never - producer.startWithSignal { signal, innerDisposable in - let object = NSObject() - objectRetainedByObserver = object - signal.observeNext { _ in _ = object.description } - disposable = innerDisposable - } - - expect(objectRetainedByObserver).toNot(beNil()) - - disposable.dispose() - expect(objectRetainedByObserver).to(beNil()) - } - - it("should not trigger effects if disposed before closure return") { - var started = false - var value: Int? - - SignalProducer(value: 42) - .on(started: { - started = true - }, next: { - value = $0 - }) - .startWithSignal { _, disposable in - expect(started) == false - expect(value).to(beNil()) - - disposable.dispose() - } - - expect(started) == false - expect(value).to(beNil()) - } - - it("should send interrupted if disposed before closure return") { - var interrupted = false - - SignalProducer(value: 42) - .startWithSignal { signal, disposable in - expect(interrupted) == false - - signal.observeInterrupted { - interrupted = true - } - - disposable.dispose() - } - - expect(interrupted) == true - } - - it("should dispose of added disposables upon completion") { - let addedDisposable = SimpleDisposable() - var observer: Signal.Observer! - - let producer = SignalProducer() { incomingObserver, disposable in - disposable += addedDisposable - observer = incomingObserver - } - - producer.startWithSignal { _ in } - expect(addedDisposable.isDisposed) == false - - observer.sendCompleted() - expect(addedDisposable.isDisposed) == true - } - - it("should dispose of added disposables upon error") { - let addedDisposable = SimpleDisposable() - var observer: Signal.Observer! - - let producer = SignalProducer() { incomingObserver, disposable in - disposable += addedDisposable - observer = incomingObserver - } - - producer.startWithSignal { _ in } - expect(addedDisposable.isDisposed) == false - - observer.sendFailed(.default) - expect(addedDisposable.isDisposed) == true - } - } - - describe("start") { - it("should immediately begin sending events") { - let producer = SignalProducer(values: [1, 2]) - - var values: [Int] = [] - var completed = false - producer.start { event in - switch event { - case let .next(value): - values.append(value) - case .completed: - completed = true - default: - break - } - } - - expect(values) == [1, 2] - expect(completed) == true - } - - it("should send interrupted if disposed") { - let producer = SignalProducer<(), NoError>.never - - var interrupted = false - let disposable = producer.startWithInterrupted { - interrupted = true - } - - expect(interrupted) == false - - disposable.dispose() - expect(interrupted) == true - } - - it("should release observer when disposed") { - weak var objectRetainedByObserver: NSObject? - var disposable: Disposable! - let test = { - let producer = SignalProducer.never - let object = NSObject() - objectRetainedByObserver = object - disposable = producer.startWithNext { _ in _ = object } - } - - test() - expect(objectRetainedByObserver).toNot(beNil()) - - disposable.dispose() - expect(objectRetainedByObserver).to(beNil()) - } - - describe("trailing closure") { - it("receives next values") { - let (producer, observer) = SignalProducer.pipe() - - var values = [Int]() - producer.startWithNext { next in - values.append(next) - } - - observer.sendNext(1) - observer.sendNext(2) - observer.sendNext(3) - - observer.sendCompleted() - - expect(values) == [1, 2, 3] - } - - it("receives results") { - let (producer, observer) = SignalProducer.pipe() - - var results: [Result] = [] - producer.startWithResult { results.append($0) } - - observer.sendNext(1) - observer.sendNext(2) - observer.sendNext(3) - observer.sendFailed(.default) - - observer.sendCompleted() - - expect(results).to(haveCount(4)) - expect(results[0].value) == 1 - expect(results[1].value) == 2 - expect(results[2].value) == 3 - expect(results[3].error) == .default - } - } - } - - describe("lift") { - describe("over unary operators") { - it("should invoke transformation once per started signal") { - let baseProducer = SignalProducer(values: [1, 2]) - - var counter = 0 - let transform = { (signal: Signal) -> Signal in - counter += 1 - return signal - } - - let producer = baseProducer.lift(transform) - expect(counter) == 0 - - producer.start() - expect(counter) == 1 - - producer.start() - expect(counter) == 2 - } - - it("should not miss any events") { - let baseProducer = SignalProducer(values: [1, 2, 3, 4]) - - let producer = baseProducer.lift { signal in - return signal.map { $0 * $0 } - } - let result = producer.collect().single() - - expect(result?.value) == [1, 4, 9, 16] - } - } - - describe("over binary operators") { - it("should invoke transformation once per started signal") { - let baseProducer = SignalProducer(values: [1, 2]) - let otherProducer = SignalProducer(values: [3, 4]) - - var counter = 0 - let transform = { (signal: Signal) -> (Signal) -> Signal<(Int, Int), NoError> in - return { otherSignal in - counter += 1 - return Signal.zip(signal, otherSignal) - } - } - - let producer = baseProducer.lift(transform)(otherProducer) - expect(counter) == 0 - - producer.start() - expect(counter) == 1 - - producer.start() - expect(counter) == 2 - } - - it("should not miss any events") { - let baseProducer = SignalProducer(values: [1, 2, 3]) - let otherProducer = SignalProducer(values: [4, 5, 6]) - - let transform = { (signal: Signal) -> (Signal) -> Signal in - return { otherSignal in - return Signal.zip(signal, otherSignal).map { first, second in first + second } - } - } - - let producer = baseProducer.lift(transform)(otherProducer) - let result = producer.collect().single() - - expect(result?.value) == [5, 7, 9] - } - } - - describe("over binary operators with signal") { - it("should invoke transformation once per started signal") { - let baseProducer = SignalProducer(values: [1, 2]) - let (otherSignal, otherSignalObserver) = Signal.pipe() - - var counter = 0 - let transform = { (signal: Signal) -> (Signal) -> Signal<(Int, Int), NoError> in - return { otherSignal in - counter += 1 - return Signal.zip(signal, otherSignal) - } - } - - let producer = baseProducer.lift(transform)(otherSignal) - expect(counter) == 0 - - producer.start() - otherSignalObserver.sendNext(1) - expect(counter) == 1 - - producer.start() - otherSignalObserver.sendNext(2) - expect(counter) == 2 - } - - it("should not miss any events") { - let baseProducer = SignalProducer(values: [ 1, 2, 3 ]) - let (otherSignal, otherSignalObserver) = Signal.pipe() - - let transform = { (signal: Signal) -> (Signal) -> Signal in - return { otherSignal in - return Signal.zip(signal, otherSignal).map(+) - } - } - - let producer = baseProducer.lift(transform)(otherSignal) - var result: [Int] = [] - var completed: Bool = false - - producer.start { event in - switch event { - case .next(let value): result.append(value) - case .completed: completed = true - default: break - } - } - - otherSignalObserver.sendNext(4) - expect(result) == [ 5 ] - - otherSignalObserver.sendNext(5) - expect(result) == [ 5, 7 ] - - otherSignalObserver.sendNext(6) - expect(result) == [ 5, 7, 9 ] - expect(completed) == true - } - } - } - - describe("combineLatest") { - it("should combine the events to one array") { - let (producerA, observerA) = SignalProducer.pipe() - let (producerB, observerB) = SignalProducer.pipe() - - let producer = SignalProducer.combineLatest([producerA, producerB]) - - var values = [[Int]]() - producer.startWithNext { next in - values.append(next) - } - - observerA.sendNext(1) - observerB.sendNext(2) - observerA.sendNext(3) - observerA.sendCompleted() - observerB.sendCompleted() - - expect(values as NSArray) == [[1, 2], [3, 2]] - } - - it("should start signal producers in order as defined") { - var ids = [Int]() - let createProducer = { (id: Int) -> SignalProducer in - return SignalProducer { observer, disposable in - ids.append(id) - - observer.sendNext(id) - observer.sendCompleted() - } - } - - let producerA = createProducer(1) - let producerB = createProducer(2) - - let producer = SignalProducer.combineLatest([producerA, producerB]) - - var values = [[Int]]() - producer.startWithNext { next in - values.append(next) - } - - expect(ids) == [1, 2] - expect(values as NSArray) == [[1, 2]] as NSArray - } - } - - describe("zip") { - it("should zip the events to one array") { - let producerA = SignalProducer(values: [ 1, 2 ]) - let producerB = SignalProducer(values: [ 3, 4 ]) - - let producer = SignalProducer.zip([producerA, producerB]) - let result = producer.collect().single() - - expect(result?.value.map { $0 as NSArray }) == [[1, 3], [2, 4]] as NSArray - } - - it("should start signal producers in order as defined") { - var ids = [Int]() - let createProducer = { (id: Int) -> SignalProducer in - return SignalProducer { observer, disposable in - ids.append(id) - - observer.sendNext(id) - observer.sendCompleted() - } - } - - let producerA = createProducer(1) - let producerB = createProducer(2) - - let producer = SignalProducer.zip([producerA, producerB]) - - var values = [[Int]]() - producer.startWithNext { next in - values.append(next) - } - - expect(ids) == [1, 2] - expect(values as NSArray) == [[1, 2]] as NSArray - } - } - - describe("timer") { - it("should send the current date at the given interval") { - let scheduler = TestScheduler() - let producer = timer(interval: 1, on: scheduler, leeway: 0) - - let startDate = scheduler.currentDate - let tick1 = startDate.addingTimeInterval(1) - let tick2 = startDate.addingTimeInterval(2) - let tick3 = startDate.addingTimeInterval(3) - - var dates: [Date] = [] - producer.startWithNext { dates.append($0) } - - scheduler.advance(by: 0.9) - expect(dates) == [] - - scheduler.advance(by: 1) - expect(dates) == [tick1] - - scheduler.advance() - expect(dates) == [tick1] - - scheduler.advance(by: 0.2) - expect(dates) == [tick1, tick2] - - scheduler.advance(by: 1) - expect(dates) == [tick1, tick2, tick3] - } - - it("should release the signal when disposed") { - let scheduler = TestScheduler() - let producer = timer(interval: 1, on: scheduler, leeway: 0) - var interrupted = false - - weak var weakSignal: Signal? - producer.startWithSignal { signal, disposable in - weakSignal = signal - scheduler.schedule { - disposable.dispose() - } - signal.observeInterrupted { interrupted = true } - } - - expect(weakSignal).toNot(beNil()) - expect(interrupted) == false - - scheduler.run() - expect(weakSignal).to(beNil()) - expect(interrupted) == true - } - } - - describe("on") { - it("should attach event handlers to each started signal") { - let (baseProducer, observer) = SignalProducer.pipe() - - var starting = 0 - var started = 0 - var event = 0 - var next = 0 - var completed = 0 - var terminated = 0 - - let producer = baseProducer - .on(starting: { - starting += 1 - }, started: { - started += 1 - }, event: { e in - event += 1 - }, next: { n in - next += 1 - }, completed: { - completed += 1 - }, terminated: { - terminated += 1 - }) - - producer.start() - expect(starting) == 1 - expect(started) == 1 - - producer.start() - expect(starting) == 2 - expect(started) == 2 - - observer.sendNext(1) - expect(event) == 2 - expect(next) == 2 - - observer.sendCompleted() - expect(event) == 4 - expect(completed) == 2 - expect(terminated) == 2 - } - - it("should attach event handlers for disposal") { - let (baseProducer, _) = SignalProducer.pipe() - - var disposed: Bool = false - - let producer = baseProducer - .on(disposed: { disposed = true }) - - let disposable = producer.start() - - expect(disposed) == false - disposable.dispose() - expect(disposed) == true - } - - it("should invoke the `started` action of the inner producer first") { - let (baseProducer, _) = SignalProducer.pipe() - - var numbers = [Int]() - - _ = baseProducer - .on(started: { numbers.append(1) }) - .on(started: { numbers.append(2) }) - .on(started: { numbers.append(3) }) - .start() - - expect(numbers) == [1, 2, 3] - } - - it("should invoke the `starting` action of the outer producer first") { - let (baseProducer, _) = SignalProducer.pipe() - - var numbers = [Int]() - - _ = baseProducer - .on(starting: { numbers.append(1) }) - .on(starting: { numbers.append(2) }) - .on(starting: { numbers.append(3) }) - .start() - - expect(numbers) == [3, 2, 1] - } - } - - describe("startOn") { - it("should invoke effects on the given scheduler") { - let scheduler = TestScheduler() - var invoked = false - - let producer = SignalProducer() { _ in - invoked = true - } - - producer.start(on: scheduler).start() - expect(invoked) == false - - scheduler.advance() - expect(invoked) == true - } - - it("should forward events on their original scheduler") { - let startScheduler = TestScheduler() - let testScheduler = TestScheduler() - - let producer = timer(interval: 2, on: testScheduler, leeway: 0) - - var next: Date? - producer.start(on: startScheduler).startWithNext { next = $0 } - - startScheduler.advance(by: 2) - expect(next).to(beNil()) - - testScheduler.advance(by: 1) - expect(next).to(beNil()) - - testScheduler.advance(by: 1) - expect(next) == testScheduler.currentDate - } - } - - describe("flatMapError") { - it("should invoke the handler and start new producer for an error") { - let (baseProducer, baseObserver) = SignalProducer.pipe() - - var values: [Int] = [] - var completed = false - - baseProducer - .flatMapError { (error: TestError) -> SignalProducer in - expect(error) == TestError.default - expect(values) == [1] - - return .init(value: 2) - } - .start { event in - switch event { - case let .next(value): - values.append(value) - case .completed: - completed = true - default: - break - } - } - - baseObserver.sendNext(1) - baseObserver.sendFailed(.default) - - expect(values) == [1, 2] - expect(completed) == true - } - - it("should interrupt the replaced producer on disposal") { - let (baseProducer, baseObserver) = SignalProducer.pipe() - - var (disposed, interrupted) = (false, false) - let disposable = baseProducer - .flatMapError { (error: TestError) -> SignalProducer in - return SignalProducer { _, disposable in - disposable += ActionDisposable { disposed = true } - } - } - .startWithInterrupted { interrupted = true } - - baseObserver.sendFailed(.default) - disposable.dispose() - - expect(interrupted) == true - expect(disposed) == true - } - } - - describe("flatten") { - describe("FlattenStrategy.concat") { - describe("sequencing") { - var completePrevious: (() -> Void)! - var sendSubsequent: (() -> Void)! - var completeOuter: (() -> Void)! - - var subsequentStarted = false - - beforeEach { - let (outerProducer, outerObserver) = SignalProducer, NoError>.pipe() - let (previousProducer, previousObserver) = SignalProducer.pipe() - - subsequentStarted = false - let subsequentProducer = SignalProducer { _ in - subsequentStarted = true - } - - completePrevious = { previousObserver.sendCompleted() } - sendSubsequent = { outerObserver.sendNext(subsequentProducer) } - completeOuter = { outerObserver.sendCompleted() } - - outerProducer.flatten(.concat).start() - outerObserver.sendNext(previousProducer) - } - - it("should immediately start subsequent inner producer if previous inner producer has already completed") { - completePrevious() - sendSubsequent() - expect(subsequentStarted) == true - } - - context("with queued producers") { - beforeEach { - // Place the subsequent producer into `concat`'s queue. - sendSubsequent() - expect(subsequentStarted) == false - } - - it("should start subsequent inner producer upon completion of previous inner producer") { - completePrevious() - expect(subsequentStarted) == true - } - - it("should start subsequent inner producer upon completion of previous inner producer and completion of outer producer") { - completeOuter() - completePrevious() - expect(subsequentStarted) == true - } - } - } - - it("should forward an error from an inner producer") { - let errorProducer = SignalProducer(error: TestError.default) - let outerProducer = SignalProducer, TestError>(value: errorProducer) - - var error: TestError? - (outerProducer.flatten(.concat)).startWithFailed { e in - error = e - } - - expect(error) == TestError.default - } - - it("should forward an error from the outer producer") { - let (outerProducer, outerObserver) = SignalProducer, TestError>.pipe() - - var error: TestError? - outerProducer.flatten(.concat).startWithFailed { e in - error = e - } - - outerObserver.sendFailed(TestError.default) - expect(error) == TestError.default - } - - describe("completion") { - var completeOuter: (() -> Void)! - var completeInner: (() -> Void)! - - var completed = false - - beforeEach { - let (outerProducer, outerObserver) = SignalProducer, NoError>.pipe() - let (innerProducer, innerObserver) = SignalProducer.pipe() - - completeOuter = { outerObserver.sendCompleted() } - completeInner = { innerObserver.sendCompleted() } - - completed = false - outerProducer.flatten(.concat).startWithCompleted { - completed = true - } - - outerObserver.sendNext(innerProducer) - } - - it("should complete when inner producers complete, then outer producer completes") { - completeInner() - expect(completed) == false - - completeOuter() - expect(completed) == true - } - - it("should complete when outer producers completes, then inner producers complete") { - completeOuter() - expect(completed) == false - - completeInner() - expect(completed) == true - } - } - } - - describe("FlattenStrategy.merge") { - describe("behavior") { - var completeA: (() -> Void)! - var sendA: (() -> Void)! - var completeB: (() -> Void)! - var sendB: (() -> Void)! - - var outerCompleted = false - - var recv = [Int]() - - beforeEach { - let (outerProducer, outerObserver) = SignalProducer, NoError>.pipe() - let (producerA, observerA) = SignalProducer.pipe() - let (producerB, observerB) = SignalProducer.pipe() - - completeA = { observerA.sendCompleted() } - completeB = { observerB.sendCompleted() } - - var a = 0 - sendA = { observerA.sendNext(a); a += 1 } - - var b = 100 - sendB = { observerB.sendNext(b); b += 1 } - - outerProducer.flatten(.merge).start { event in - switch event { - case let .next(i): - recv.append(i) - case .completed: - outerCompleted = true - default: - break - } - } - - outerObserver.sendNext(producerA) - outerObserver.sendNext(producerB) - - outerObserver.sendCompleted() - } - - it("should forward values from any inner signals") { - sendA() - sendA() - sendB() - sendA() - sendB() - expect(recv) == [0, 1, 100, 2, 101] - } - - it("should complete when all signals have completed") { - completeA() - expect(outerCompleted) == false - completeB() - expect(outerCompleted) == true - } - } - - describe("error handling") { - it("should forward an error from an inner signal") { - let errorProducer = SignalProducer(error: TestError.default) - let outerProducer = SignalProducer, TestError>(value: errorProducer) - - var error: TestError? - outerProducer.flatten(.merge).startWithFailed { e in - error = e - } - expect(error) == TestError.default - } - - it("should forward an error from the outer signal") { - let (outerProducer, outerObserver) = SignalProducer, TestError>.pipe() - - var error: TestError? - outerProducer.flatten(.merge).startWithFailed { e in - error = e - } - - outerObserver.sendFailed(TestError.default) - expect(error) == TestError.default - } - } - } - - describe("FlattenStrategy.latest") { - it("should forward values from the latest inner signal") { - let (outer, outerObserver) = SignalProducer, TestError>.pipe() - let (firstInner, firstInnerObserver) = SignalProducer.pipe() - let (secondInner, secondInnerObserver) = SignalProducer.pipe() - - var receivedValues: [Int] = [] - var errored = false - var completed = false - - outer.flatten(.latest).start { event in - switch event { - case let .next(value): - receivedValues.append(value) - case .completed: - completed = true - case .failed: - errored = true - case .interrupted: - break - } - } - - outerObserver.sendNext(SignalProducer(value: 0)) - outerObserver.sendNext(firstInner) - firstInnerObserver.sendNext(1) - outerObserver.sendNext(secondInner) - secondInnerObserver.sendNext(2) - outerObserver.sendCompleted() - - expect(receivedValues) == [ 0, 1, 2 ] - expect(errored) == false - expect(completed) == false - - firstInnerObserver.sendNext(3) - firstInnerObserver.sendCompleted() - secondInnerObserver.sendNext(4) - secondInnerObserver.sendCompleted() - - expect(receivedValues) == [ 0, 1, 2, 4 ] - expect(errored) == false - expect(completed) == true - } - - it("should forward an error from an inner signal") { - let inner = SignalProducer(error: .default) - let outer = SignalProducer, TestError>(value: inner) - - let result = outer.flatten(.latest).first() - expect(result?.error) == TestError.default - } - - it("should forward an error from the outer signal") { - let outer = SignalProducer, TestError>(error: .default) - - let result = outer.flatten(.latest).first() - expect(result?.error) == TestError.default - } - - it("should complete when the original and latest signals have completed") { - let inner = SignalProducer.empty - let outer = SignalProducer, TestError>(value: inner) - - var completed = false - outer.flatten(.latest).startWithCompleted { - completed = true - } - - expect(completed) == true - } - - it("should complete when the outer signal completes before sending any signals") { - let outer = SignalProducer, TestError>.empty - - var completed = false - outer.flatten(.latest).startWithCompleted { - completed = true - } - - expect(completed) == true - } - - it("should not deadlock") { - let producer = SignalProducer(value: 1) - .flatMap(.latest) { _ in SignalProducer(value: 10) } - - let result = producer.take(first: 1).last() - expect(result?.value) == 10 - } - } - - describe("interruption") { - var innerObserver: Signal<(), NoError>.Observer! - var outerObserver: Signal, NoError>.Observer! - var execute: ((FlattenStrategy) -> Void)! - - var interrupted = false - var completed = false - - beforeEach { - let (innerProducer, incomingInnerObserver) = SignalProducer<(), NoError>.pipe() - let (outerProducer, incomingOuterObserver) = SignalProducer, NoError>.pipe() - - innerObserver = incomingInnerObserver - outerObserver = incomingOuterObserver - - execute = { strategy in - interrupted = false - completed = false - - outerProducer - .flatten(strategy) - .start { event in - switch event { - case .interrupted: - interrupted = true - case .completed: - completed = true - default: - break - } - } - } - - incomingOuterObserver.sendNext(innerProducer) - } - - describe("Concat") { - it("should drop interrupted from an inner producer") { - execute(.concat) - - innerObserver.sendInterrupted() - expect(interrupted) == false - expect(completed) == false - - outerObserver.sendCompleted() - expect(completed) == true - } - - it("should forward interrupted from the outer producer") { - execute(.concat) - outerObserver.sendInterrupted() - expect(interrupted) == true - } - } - - describe("Latest") { - it("should drop interrupted from an inner producer") { - execute(.latest) - - innerObserver.sendInterrupted() - expect(interrupted) == false - expect(completed) == false - - outerObserver.sendCompleted() - expect(completed) == true - } - - it("should forward interrupted from the outer producer") { - execute(.latest) - outerObserver.sendInterrupted() - expect(interrupted) == true - } - } - - describe("Merge") { - it("should drop interrupted from an inner producer") { - execute(.merge) - - innerObserver.sendInterrupted() - expect(interrupted) == false - expect(completed) == false - - outerObserver.sendCompleted() - expect(completed) == true - } - - it("should forward interrupted from the outer producer") { - execute(.merge) - outerObserver.sendInterrupted() - expect(interrupted) == true - } - } - } - - describe("disposal") { - var completeOuter: (() -> Void)! - var disposeOuter: (() -> Void)! - var execute: ((FlattenStrategy) -> Void)! - - var innerDisposable = SimpleDisposable() - var interrupted = false - - beforeEach { - execute = { strategy in - let (outerProducer, outerObserver) = SignalProducer, NoError>.pipe() - - innerDisposable = SimpleDisposable() - let innerProducer = SignalProducer { $1.add(innerDisposable) } - - interrupted = false - let outerDisposable = outerProducer.flatten(strategy).startWithInterrupted { - interrupted = true - } - - completeOuter = outerObserver.sendCompleted - disposeOuter = outerDisposable.dispose - - outerObserver.sendNext(innerProducer) - } - } - - describe("Concat") { - it("should cancel inner work when disposed before the outer producer completes") { - execute(.concat) - - expect(innerDisposable.isDisposed) == false - expect(interrupted) == false - disposeOuter() - - expect(innerDisposable.isDisposed) == true - expect(interrupted) == true - } - - it("should cancel inner work when disposed after the outer producer completes") { - execute(.concat) - - completeOuter() - - expect(innerDisposable.isDisposed) == false - expect(interrupted) == false - disposeOuter() - - expect(innerDisposable.isDisposed) == true - expect(interrupted) == true - } - } - - describe("Latest") { - it("should cancel inner work when disposed before the outer producer completes") { - execute(.latest) - - expect(innerDisposable.isDisposed) == false - expect(interrupted) == false - disposeOuter() - - expect(innerDisposable.isDisposed) == true - expect(interrupted) == true - } - - it("should cancel inner work when disposed after the outer producer completes") { - execute(.latest) - - completeOuter() - - expect(innerDisposable.isDisposed) == false - expect(interrupted) == false - disposeOuter() - - expect(innerDisposable.isDisposed) == true - expect(interrupted) == true - } - } - - describe("Merge") { - it("should cancel inner work when disposed before the outer producer completes") { - execute(.merge) - - expect(innerDisposable.isDisposed) == false - expect(interrupted) == false - disposeOuter() - - expect(innerDisposable.isDisposed) == true - expect(interrupted) == true - } - - it("should cancel inner work when disposed after the outer producer completes") { - execute(.merge) - - completeOuter() - - expect(innerDisposable.isDisposed) == false - expect(interrupted) == false - disposeOuter() - - expect(innerDisposable.isDisposed) == true - expect(interrupted) == true - } - } - } - } - - describe("times") { - it("should start a signal N times upon completion") { - let original = SignalProducer(values: [ 1, 2, 3 ]) - let producer = original.times(3) - - let result = producer.collect().single() - expect(result?.value) == [ 1, 2, 3, 1, 2, 3, 1, 2, 3 ] - } - - it("should produce an equivalent signal producer if count is 1") { - let original = SignalProducer(value: 1) - let producer = original.times(1) - - let result = producer.collect().single() - expect(result?.value) == [ 1 ] - } - - it("should produce an empty signal if count is 0") { - let original = SignalProducer(value: 1) - let producer = original.times(0) - - let result = producer.first() - expect(result).to(beNil()) - } - - it("should not repeat upon error") { - let results: [Result] = [ - .success(1), - .success(2), - .failure(.default) - ] - - let original = SignalProducer.attemptWithResults(results) - let producer = original.times(3) - - let events = producer - .materialize() - .collect() - .single() - let result = events?.value - - let expectedEvents: [Event] = [ - .next(1), - .next(2), - .failed(.default) - ] - - // TODO: if let result = result where result.count == expectedEvents.count - if result?.count != expectedEvents.count { - fail("Invalid result: \(result)") - } else { - // Can't test for equality because Array is not Equatable, - // and neither is Event. - expect(result![0] == expectedEvents[0]) == true - expect(result![1] == expectedEvents[1]) == true - expect(result![2] == expectedEvents[2]) == true - } - } - - it("should evaluate lazily") { - let original = SignalProducer(value: 1) - let producer = original.times(Int.max) - - let result = producer.take(first: 1).single() - expect(result?.value) == 1 - } - } - - describe("retry") { - it("should start a signal N times upon error") { - let results: [Result] = [ - .failure(.error1), - .failure(.error2), - .success(1) - ] - - let original = SignalProducer.attemptWithResults(results) - let producer = original.retry(upTo: 2) - - let result = producer.single() - - expect(result?.value) == 1 - } - - it("should forward errors that occur after all retries") { - let results: [Result] = [ - .failure(.default), - .failure(.error1), - .failure(.error2), - ] - - let original = SignalProducer.attemptWithResults(results) - let producer = original.retry(upTo: 2) - - let result = producer.single() - - expect(result?.error) == TestError.error2 - } - - it("should not retry upon completion") { - let results: [Result] = [ - .success(1), - .success(2), - .success(3) - ] - - let original = SignalProducer.attemptWithResults(results) - let producer = original.retry(upTo: 2) - - let result = producer.single() - expect(result?.value) == 1 - } - } - - describe("then") { - it("should start the subsequent producer after the completion of the original") { - let (original, observer) = SignalProducer.pipe() - - var subsequentStarted = false - let subsequent = SignalProducer { observer, _ in - subsequentStarted = true - } - - let producer = original.then(subsequent) - producer.start() - expect(subsequentStarted) == false - - observer.sendCompleted() - expect(subsequentStarted) == true - } - - it("should forward errors from the original producer") { - let original = SignalProducer(error: .default) - let subsequent = SignalProducer.empty - - let result = original.then(subsequent).first() - expect(result?.error) == TestError.default - } - - it("should forward errors from the subsequent producer") { - let original = SignalProducer.empty - let subsequent = SignalProducer(error: .default) - - let result = original.then(subsequent).first() - expect(result?.error) == TestError.default - } - - it("should forward interruptions from the original producer") { - let (original, observer) = SignalProducer.pipe() - - var subsequentStarted = false - let subsequent = SignalProducer { observer, _ in - subsequentStarted = true - } - - var interrupted = false - let producer = original.then(subsequent) - producer.startWithInterrupted { - interrupted = true - } - expect(subsequentStarted) == false - - observer.sendInterrupted() - expect(interrupted) == true - } - - it("should complete when both inputs have completed") { - let (original, originalObserver) = SignalProducer.pipe() - let (subsequent, subsequentObserver) = SignalProducer.pipe() - - let producer = original.then(subsequent) - - var completed = false - producer.startWithCompleted { - completed = true - } - - originalObserver.sendCompleted() - expect(completed) == false - - subsequentObserver.sendCompleted() - expect(completed) == true - } - - it("works with NoError and TestError") { - let producer: SignalProducer = SignalProducer.empty - .then(SignalProducer.empty) - - _ = producer - } - - it("works with TestError and NoError") { - let producer: SignalProducer = SignalProducer.empty - .then(SignalProducer.empty) - - _ = producer - } - - it("works with NoError and NoError") { - let producer: SignalProducer = SignalProducer.empty - .then(SignalProducer.empty) - - _ = producer - } - } - - describe("first") { - it("should start a signal then block on the first value") { - let (_signal, observer) = Signal.pipe() - - let forwardingScheduler: QueueScheduler - - if #available(OSX 10.10, *) { - forwardingScheduler = QueueScheduler(qos: .default, name: "\(#file):\(#line)") - } else { - forwardingScheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) - } - - let producer = SignalProducer(signal: _signal.delay(0.1, on: forwardingScheduler)) - - let observingScheduler: QueueScheduler - - if #available(OSX 10.10, *) { - observingScheduler = QueueScheduler(qos: .default, name: "\(#file):\(#line)") - } else { - observingScheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) - } - - var result: Int? - - observingScheduler.schedule { - result = producer.first()?.value - } - - expect(result).to(beNil()) - - observer.sendNext(1) - expect(result).toEventually(equal(1), timeout: 5.0) - } - - it("should return a nil result if no values are sent before completion") { - let result = SignalProducer.empty.first() - expect(result).to(beNil()) - } - - it("should return the first value if more than one value is sent") { - let result = SignalProducer(values: [ 1, 2 ]).first() - expect(result?.value) == 1 - } - - it("should return an error if one occurs before the first value") { - let result = SignalProducer(error: .default).first() - expect(result?.error) == TestError.default - } - } - - describe("single") { - it("should start a signal then block until completion") { - let (_signal, observer) = Signal.pipe() - let forwardingScheduler: QueueScheduler - - if #available(OSX 10.10, *) { - forwardingScheduler = QueueScheduler(qos: .default, name: "\(#file):\(#line)") - } else { - forwardingScheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) - } - - let producer = SignalProducer(signal: _signal.delay(0.1, on: forwardingScheduler)) - - let observingScheduler: QueueScheduler - - if #available(OSX 10.10, *) { - observingScheduler = QueueScheduler(qos: .default, name: "\(#file):\(#line)") - } else { - observingScheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) - } - - var result: Int? - - observingScheduler.schedule { - result = producer.single()?.value - } - expect(result).to(beNil()) - - observer.sendNext(1) - - Thread.sleep(forTimeInterval: 3.0) - expect(result).to(beNil()) - - observer.sendCompleted() - expect(result).toEventually(equal(1)) - } - - it("should return a nil result if no values are sent before completion") { - let result = SignalProducer.empty.single() - expect(result).to(beNil()) - } - - it("should return a nil result if more than one value is sent before completion") { - let result = SignalProducer(values: [ 1, 2 ]).single() - expect(result).to(beNil()) - } - - it("should return an error if one occurs") { - let result = SignalProducer(error: .default).single() - expect(result?.error) == TestError.default - } - } - - describe("last") { - it("should start a signal then block until completion") { - let (_signal, observer) = Signal.pipe() - let scheduler: QueueScheduler - - if #available(*, OSX 10.10) { - scheduler = QueueScheduler(name: "\(#file):\(#line)") - } else { - scheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) - } - let producer = SignalProducer(signal: _signal.delay(0.1, on: scheduler)) - - var result: Result? - - let group = DispatchGroup() - - let globalQueue: DispatchQueue - if #available(*, OSX 10.10) { - globalQueue = DispatchQueue.global() - } else { - globalQueue = DispatchQueue.global(priority: .default) - } - - globalQueue.async(group: group, flags: []) { - result = producer.last() - } - expect(result).to(beNil()) - - observer.sendNext(1) - observer.sendNext(2) - expect(result).to(beNil()) - - observer.sendCompleted() - group.wait() - - expect(result?.value) == 2 - } - - it("should return a nil result if no values are sent before completion") { - let result = SignalProducer.empty.last() - expect(result).to(beNil()) - } - - it("should return the last value if more than one value is sent") { - let result = SignalProducer(values: [ 1, 2 ]).last() - expect(result?.value) == 2 - } - - it("should return an error if one occurs") { - let result = SignalProducer(error: .default).last() - expect(result?.error) == TestError.default - } - } - - describe("wait") { - it("should start a signal then block until completion") { - let (_signal, observer) = Signal.pipe() - let scheduler: QueueScheduler - if #available(*, OSX 10.10) { - scheduler = QueueScheduler(name: "\(#file):\(#line)") - } else { - scheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) - } - let producer = SignalProducer(signal: _signal.delay(0.1, on: scheduler)) - - var result: Result<(), NoError>? - - let group = DispatchGroup() - - let globalQueue: DispatchQueue - if #available(*, OSX 10.10) { - globalQueue = DispatchQueue.global() - } else { - globalQueue = DispatchQueue.global(priority: .default) - } - - globalQueue.async(group: group, flags: []) { - result = producer.wait() - } - - expect(result).to(beNil()) - - observer.sendCompleted() - group.wait() - - expect(result?.value).toNot(beNil()) - } - - it("should return an error if one occurs") { - let result = SignalProducer(error: .default).wait() - expect(result.error) == TestError.default - } - } - - describe("observeOn") { - it("should immediately cancel upstream producer's work when disposed") { - var upstreamDisposable: Disposable! - let producer = SignalProducer<(), NoError>{ _, innerDisposable in - upstreamDisposable = innerDisposable - } - - var downstreamDisposable: Disposable! - producer - .observe(on: TestScheduler()) - .startWithSignal { signal, innerDisposable in - downstreamDisposable = innerDisposable - } - - expect(upstreamDisposable.isDisposed) == false - - downstreamDisposable.dispose() - expect(upstreamDisposable.isDisposed) == true - } - } - - describe("take") { - it("Should not start concat'ed producer if the first one sends a value when using take(1)") { - let scheduler: QueueScheduler - if #available(OSX 10.10, *) { - scheduler = QueueScheduler(name: "\(#file):\(#line)") - } else { - scheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) - } - - // Delaying producer1 from sending a value to test whether producer2 is started in the mean-time. - let producer1 = SignalProducer() { handler, _ in - handler.sendNext(1) - handler.sendCompleted() - }.start(on: scheduler) - - var started = false - let producer2 = SignalProducer() { handler, _ in - started = true - handler.sendNext(2) - handler.sendCompleted() - } - - let result = producer1.concat(producer2).take(first: 1).collect().first() - - expect(result?.value) == [1] - expect(started) == false - } - } - - describe("replayLazily") { - var producer: SignalProducer! - var observer: SignalProducer.ProducedSignal.Observer! - - var replayedProducer: SignalProducer! - - beforeEach { - let (producerTemp, observerTemp) = SignalProducer.pipe() - producer = producerTemp - observer = observerTemp - - replayedProducer = producer.replayLazily(upTo: 2) - } - - context("subscribing to underlying producer") { - it("emits new values") { - var last: Int? - - replayedProducer - .assumeNoErrors() - .startWithNext { last = $0 } - - expect(last).to(beNil()) - - observer.sendNext(1) - expect(last) == 1 - - observer.sendNext(2) - expect(last) == 2 - } - - it("emits errors") { - var error: TestError? - - replayedProducer.startWithFailed { error = $0 } - expect(error).to(beNil()) - - observer.sendFailed(.default) - expect(error) == TestError.default - } - } - - context("buffers past values") { - it("emits last value upon subscription") { - let disposable = replayedProducer - .start() - - observer.sendNext(1) - disposable.dispose() - - var last: Int? - - replayedProducer - .assumeNoErrors() - .startWithNext { last = $0 } - expect(last) == 1 - } - - it("emits previous failure upon subscription") { - let disposable = replayedProducer - .start() - - observer.sendFailed(.default) - disposable.dispose() - - var error: TestError? - - replayedProducer - .startWithFailed { error = $0 } - expect(error) == TestError.default - } - - it("emits last n values upon subscription") { - var disposable = replayedProducer - .start() - - observer.sendNext(1) - observer.sendNext(2) - observer.sendNext(3) - observer.sendNext(4) - disposable.dispose() - - var values: [Int] = [] - - disposable = replayedProducer - .assumeNoErrors() - .startWithNext { values.append($0) } - expect(values) == [ 3, 4 ] - - observer.sendNext(5) - expect(values) == [ 3, 4, 5 ] - - disposable.dispose() - values = [] - - replayedProducer - .assumeNoErrors() - .startWithNext { values.append($0) } - expect(values) == [ 4, 5 ] - } - } - - context("starting underying producer") { - it("starts lazily") { - var started = false - - let producer = SignalProducer(value: 0) - .on(started: { started = true }) - expect(started) == false - - let replayedProducer = producer - .replayLazily(upTo: 1) - expect(started) == false - - replayedProducer.start() - expect(started) == true - } - - it("shares a single subscription") { - var startedTimes = 0 - - let producer = SignalProducer.never - .on(started: { startedTimes += 1 }) - expect(startedTimes) == 0 - - let replayedProducer = producer - .replayLazily(upTo: 1) - expect(startedTimes) == 0 - - replayedProducer.start() - expect(startedTimes) == 1 - - replayedProducer.start() - expect(startedTimes) == 1 - } - - it("does not start multiple times when subscribing multiple times") { - var startedTimes = 0 - - let producer = SignalProducer(value: 0) - .on(started: { startedTimes += 1 }) - - let replayedProducer = producer - .replayLazily(upTo: 1) - - expect(startedTimes) == 0 - replayedProducer.start().dispose() - expect(startedTimes) == 1 - replayedProducer.start().dispose() - expect(startedTimes) == 1 - } - - it("does not start again if it finished") { - var startedTimes = 0 - - let producer = SignalProducer.empty - .on(started: { startedTimes += 1 }) - expect(startedTimes) == 0 - - let replayedProducer = producer - .replayLazily(upTo: 1) - expect(startedTimes) == 0 - - replayedProducer.start() - expect(startedTimes) == 1 - - replayedProducer.start() - expect(startedTimes) == 1 - } - } - - context("lifetime") { - it("does not dispose underlying subscription if the replayed producer is still in memory") { - var disposed = false - - let producer = SignalProducer.never - .on(disposed: { disposed = true }) - - let replayedProducer = producer - .replayLazily(upTo: 1) - - expect(disposed) == false - let disposable = replayedProducer.start() - expect(disposed) == false - - disposable.dispose() - expect(disposed) == false - } - - it("does not dispose if it has active subscriptions") { - var disposed = false - - let producer = SignalProducer.never - .on(disposed: { disposed = true }) - - var replayedProducer = ImplicitlyUnwrappedOptional(producer.replayLazily(upTo: 1)) - - expect(disposed) == false - let disposable1 = replayedProducer?.start() - let disposable2 = replayedProducer?.start() - expect(disposed) == false - - replayedProducer = nil - expect(disposed) == false - - disposable1?.dispose() - expect(disposed) == false - - disposable2?.dispose() - expect(disposed) == true - } - - it("disposes underlying producer when the producer is deallocated") { - var disposed = false - - let producer = SignalProducer.never - .on(disposed: { disposed = true }) - - var replayedProducer = ImplicitlyUnwrappedOptional(producer.replayLazily(upTo: 1)) - - expect(disposed) == false - let disposable = replayedProducer?.start() - expect(disposed) == false - - disposable?.dispose() - expect(disposed) == false - - replayedProducer = nil - expect(disposed) == true - } - - it("does not leak buffered values") { - final class Value { - private let deinitBlock: () -> Void - - init(deinitBlock: @escaping () -> Void) { - self.deinitBlock = deinitBlock - } - - deinit { - self.deinitBlock() - } - } - - var deinitValues = 0 - - var producer: SignalProducer! = SignalProducer(value: Value { - deinitValues += 1 - }) - expect(deinitValues) == 0 - - var replayedProducer: SignalProducer! = producer - .replayLazily(upTo: 1) - - let disposable = replayedProducer - .start() - - disposable.dispose() - expect(deinitValues) == 0 - - producer = nil - expect(deinitValues) == 0 - - replayedProducer = nil - expect(deinitValues) == 1 - } - } - - describe("log events") { - it("should output the correct event") { - let expectations: [(String) -> Void] = [ - { event in expect(event) == "[] starting" }, - { event in expect(event) == "[] started" }, - { event in expect(event) == "[] next 1" }, - { event in expect(event) == "[] completed" }, - { event in expect(event) == "[] terminated" }, - { event in expect(event) == "[] disposed" } - ] - - let logger = TestLogger(expectations: expectations) - - let (producer, observer) = SignalProducer.pipe() - producer - .logEvents(logger: logger.logEvent) - .start() - - observer.sendNext(1) - observer.sendCompleted() - } - } - - describe("init(values) ambiguity") { - it("should not be a SignalProducer, NoError>") { - - let producer1: SignalProducer = SignalProducer.empty - let producer2: SignalProducer = SignalProducer.empty - - // This expression verifies at compile time that the type is as expected. - let _: SignalProducer = SignalProducer(values: [producer1, producer2]) - .flatten(.merge) - } - } - } - - describe("take(during:)") { - it("completes a signal when the lifetime ends") { - let (signal, observer) = Signal.pipe() - let object = MutableReference(TestObject()) - - let output = signal.take(during: object.value!.lifetime) - - var results: [Int] = [] - output.observeNext { results.append($0) } - - observer.sendNext(1) - observer.sendNext(2) - object.value = nil - observer.sendNext(3) - - expect(results) == [1, 2] - } - - it("completes a signal producer when the lifetime ends") { - let (producer, observer) = Signal.pipe() - let object = MutableReference(TestObject()) - - let output = producer.take(during: object.value!.lifetime) - - var results: [Int] = [] - output.observeNext { results.append($0) } - - observer.sendNext(1) - observer.sendNext(2) - object.value = nil - observer.sendNext(3) - - expect(results) == [1, 2] - } - } - } -} - -// MARK: - Helpers - -extension SignalProducer { - internal static func pipe() -> (SignalProducer, ProducedSignal.Observer) { - let (signal, observer) = ProducedSignal.pipe() - let producer = SignalProducer(signal: signal) - return (producer, observer) - } - - /// Creates a producer that can be started as many times as elements in `results`. - /// Each signal will immediately send either a value or an error. - fileprivate static func attemptWithResults(_ results: C) -> SignalProducer where C.Iterator.Element == Result, C.IndexDistance == C.Index, C.Index == Int { - let resultCount = results.count - var operationIndex = 0 - - precondition(resultCount > 0) - - let operation: () -> Result = { - if operationIndex < resultCount { - defer { - operationIndex += 1 - } - - return results[results.index(results.startIndex, offsetBy: operationIndex)] - } else { - fail("Operation started too many times") - - return results[results.startIndex] - } - } - - return SignalProducer.attempt(operation) - } -} diff --git a/ReactiveSwiftTests/SignalSpec.swift b/ReactiveSwiftTests/SignalSpec.swift deleted file mode 100755 index dd93b1e378..0000000000 --- a/ReactiveSwiftTests/SignalSpec.swift +++ /dev/null @@ -1,2268 +0,0 @@ -// -// SignalSpec.swift -// ReactiveSwift -// -// Created by Justin Spahr-Summers on 2015-01-23. -// Copyright (c) 2015 GitHub. All rights reserved. -// - -import Result -import Nimble -import Quick -import ReactiveSwift - -class SignalSpec: QuickSpec { - override func spec() { - describe("init") { - var testScheduler: TestScheduler! - - beforeEach { - testScheduler = TestScheduler() - } - - it("should run the generator immediately") { - var didRunGenerator = false - _ = Signal { observer in - didRunGenerator = true - return nil - } - - expect(didRunGenerator) == true - } - - it("should forward events to observers") { - let numbers = [ 1, 2, 5 ] - - let signal: Signal = Signal { observer in - testScheduler.schedule { - for number in numbers { - observer.sendNext(number) - } - observer.sendCompleted() - } - return nil - } - - var fromSignal: [Int] = [] - var completed = false - - signal.observe { event in - switch event { - case let .next(number): - fromSignal.append(number) - case .completed: - completed = true - default: - break - } - } - - expect(completed) == false - expect(fromSignal).to(beEmpty()) - - testScheduler.run() - - expect(completed) == true - expect(fromSignal) == numbers - } - - it("should dispose of returned disposable upon error") { - let disposable = SimpleDisposable() - - let signal: Signal = Signal { observer in - testScheduler.schedule { - observer.sendFailed(TestError.default) - } - return disposable - } - - var errored = false - - signal.observeFailed { _ in errored = true } - - expect(errored) == false - expect(disposable.isDisposed) == false - - testScheduler.run() - - expect(errored) == true - expect(disposable.isDisposed) == true - } - - it("should dispose of returned disposable upon completion") { - let disposable = SimpleDisposable() - - let signal: Signal = Signal { observer in - testScheduler.schedule { - observer.sendCompleted() - } - return disposable - } - - var completed = false - - signal.observeCompleted { completed = true } - - expect(completed) == false - expect(disposable.isDisposed) == false - - testScheduler.run() - - expect(completed) == true - expect(disposable.isDisposed) == true - } - - it("should dispose of returned disposable upon interrupted") { - let disposable = SimpleDisposable() - - let signal: Signal = Signal { observer in - testScheduler.schedule { - observer.sendInterrupted() - } - return disposable - } - - var interrupted = false - signal.observeInterrupted { - interrupted = true - } - - expect(interrupted) == false - expect(disposable.isDisposed) == false - - testScheduler.run() - - expect(interrupted) == true - expect(disposable.isDisposed) == true - } - } - - describe("Signal.empty") { - it("should interrupt its observers without emitting any value") { - let signal = Signal<(), NoError>.empty - - var hasUnexpectedEventsEmitted = false - var signalInterrupted = false - - signal.observe { event in - switch event { - case .next, .failed, .completed: - hasUnexpectedEventsEmitted = true - case .interrupted: - signalInterrupted = true - } - } - - expect(hasUnexpectedEventsEmitted) == false - expect(signalInterrupted) == true - } - } - - describe("Signal.pipe") { - it("should forward events to observers") { - let (signal, observer) = Signal.pipe() - - var fromSignal: [Int] = [] - var completed = false - - signal.observe { event in - switch event { - case let .next(number): - fromSignal.append(number) - case .completed: - completed = true - default: - break - } - } - - expect(fromSignal).to(beEmpty()) - expect(completed) == false - - observer.sendNext(1) - expect(fromSignal) == [ 1 ] - - observer.sendNext(2) - expect(fromSignal) == [ 1, 2 ] - - expect(completed) == false - observer.sendCompleted() - expect(completed) == true - } - - context("memory") { - it("should not crash allocating memory with a few observers") { - let (signal, _) = Signal.pipe() - - for _ in 0..<50 { - autoreleasepool { - let disposable = signal.observe { _ in } - - disposable!.dispose() - } - } - } - } - } - - describe("observe") { - var testScheduler: TestScheduler! - - beforeEach { - testScheduler = TestScheduler() - } - - it("should stop forwarding events when disposed") { - let disposable = SimpleDisposable() - - let signal: Signal = Signal { observer in - testScheduler.schedule { - for number in [ 1, 2 ] { - observer.sendNext(number) - } - observer.sendCompleted() - observer.sendNext(4) - } - return disposable - } - - var fromSignal: [Int] = [] - signal.observeNext { number in - fromSignal.append(number) - } - - expect(disposable.isDisposed) == false - expect(fromSignal).to(beEmpty()) - - testScheduler.run() - - expect(disposable.isDisposed) == true - expect(fromSignal) == [ 1, 2 ] - } - - it("should not trigger side effects") { - var runCount = 0 - let signal: Signal<(), NoError> = Signal { observer in - runCount += 1 - return nil - } - - expect(runCount) == 1 - - signal.observe(Observer<(), NoError>()) - expect(runCount) == 1 - } - - it("should release observer after termination") { - weak var testStr: NSMutableString? - let (signal, observer) = Signal.pipe() - - let test = { - let innerStr: NSMutableString = NSMutableString() - signal.observeNext { value in - innerStr.append("\(value)") - } - testStr = innerStr - } - test() - - observer.sendNext(1) - expect(testStr) == "1" - observer.sendNext(2) - expect(testStr) == "12" - - observer.sendCompleted() - expect(testStr).to(beNil()) - } - - it("should release observer after interruption") { - weak var testStr: NSMutableString? - let (signal, observer) = Signal.pipe() - - let test = { - let innerStr: NSMutableString = NSMutableString() - signal.observeNext { value in - innerStr.append("\(value)") - } - - testStr = innerStr - } - - test() - - observer.sendNext(1) - expect(testStr) == "1" - - observer.sendNext(2) - expect(testStr) == "12" - - observer.sendInterrupted() - expect(testStr).to(beNil()) - } - } - - describe("trailing closure") { - it("receives next values") { - var values = [Int]() - let (signal, observer) = Signal.pipe() - - signal.observeNext { next in - values.append(next) - } - - observer.sendNext(1) - expect(values) == [1] - } - - it("receives results") { - let (signal, observer) = Signal.pipe() - - var results: [Result] = [] - signal.observeResult { results.append($0) } - - observer.sendNext(1) - observer.sendNext(2) - observer.sendNext(3) - observer.sendFailed(.default) - - observer.sendCompleted() - - expect(results).to(haveCount(4)) - expect(results[0].value) == 1 - expect(results[1].value) == 2 - expect(results[2].value) == 3 - expect(results[3].error) == .default - } - } - - describe("map") { - it("should transform the values of the signal") { - let (signal, observer) = Signal.pipe() - let mappedSignal = signal.map { String($0 + 1) } - - var lastValue: String? - - mappedSignal.observeNext { - lastValue = $0 - return - } - - expect(lastValue).to(beNil()) - - observer.sendNext(0) - expect(lastValue) == "1" - - observer.sendNext(1) - expect(lastValue) == "2" - } - } - - - describe("mapError") { - it("should transform the errors of the signal") { - let (signal, observer) = Signal.pipe() - let producerError = NSError(domain: "com.reactivecocoa.errordomain", code: 100, userInfo: nil) - var error: NSError? - - signal - .mapError { _ in producerError } - .observeFailed { err in error = err } - - expect(error).to(beNil()) - - observer.sendFailed(TestError.default) - expect(error) == producerError - } - } - - describe("filter") { - it("should omit values from the signal") { - let (signal, observer) = Signal.pipe() - let mappedSignal = signal.filter { $0 % 2 == 0 } - - var lastValue: Int? - - mappedSignal.observeNext { lastValue = $0 } - - expect(lastValue).to(beNil()) - - observer.sendNext(0) - expect(lastValue) == 0 - - observer.sendNext(1) - expect(lastValue) == 0 - - observer.sendNext(2) - expect(lastValue) == 2 - } - } - - describe("skipNil") { - it("should forward only non-nil values") { - let (signal, observer) = Signal.pipe() - let mappedSignal = signal.skipNil() - - var lastValue: Int? - - mappedSignal.observeNext { lastValue = $0 } - expect(lastValue).to(beNil()) - - observer.sendNext(nil) - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue) == 1 - - observer.sendNext(nil) - expect(lastValue) == 1 - - observer.sendNext(2) - expect(lastValue) == 2 - } - } - - describe("scan") { - it("should incrementally accumulate a value") { - let (baseSignal, observer) = Signal.pipe() - let signal = baseSignal.scan("", +) - - var lastValue: String? - - signal.observeNext { lastValue = $0 } - - expect(lastValue).to(beNil()) - - observer.sendNext("a") - expect(lastValue) == "a" - - observer.sendNext("bb") - expect(lastValue) == "abb" - } - } - - describe("reduce") { - it("should accumulate one value") { - let (baseSignal, observer) = Signal.pipe() - let signal = baseSignal.reduce(1, +) - - var lastValue: Int? - var completed = false - - signal.observe { event in - switch event { - case let .next(value): - lastValue = value - case .completed: - completed = true - default: - break - } - } - - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue).to(beNil()) - - observer.sendNext(2) - expect(lastValue).to(beNil()) - - expect(completed) == false - observer.sendCompleted() - expect(completed) == true - - expect(lastValue) == 4 - } - - it("should send the initial value if none are received") { - let (baseSignal, observer) = Signal.pipe() - let signal = baseSignal.reduce(1, +) - - var lastValue: Int? - var completed = false - - signal.observe { event in - switch event { - case let .next(value): - lastValue = value - case .completed: - completed = true - default: - break - } - } - - expect(lastValue).to(beNil()) - expect(completed) == false - - observer.sendCompleted() - - expect(lastValue) == 1 - expect(completed) == true - } - } - - describe("skip") { - it("should skip initial values") { - let (baseSignal, observer) = Signal.pipe() - let signal = baseSignal.skip(first: 1) - - var lastValue: Int? - signal.observeNext { lastValue = $0 } - - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue).to(beNil()) - - observer.sendNext(2) - expect(lastValue) == 2 - } - - it("should not skip any values when 0") { - let (baseSignal, observer) = Signal.pipe() - let signal = baseSignal.skip(first: 0) - - var lastValue: Int? - signal.observeNext { lastValue = $0 } - - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue) == 1 - - observer.sendNext(2) - expect(lastValue) == 2 - } - } - - describe("skipRepeats") { - it("should skip duplicate Equatable values") { - let (baseSignal, observer) = Signal.pipe() - let signal = baseSignal.skipRepeats() - - var values: [Bool] = [] - signal.observeNext { values.append($0) } - - expect(values) == [] - - observer.sendNext(true) - expect(values) == [ true ] - - observer.sendNext(true) - expect(values) == [ true ] - - observer.sendNext(false) - expect(values) == [ true, false ] - - observer.sendNext(true) - expect(values) == [ true, false, true ] - } - - it("should skip values according to a predicate") { - let (baseSignal, observer) = Signal.pipe() - let signal = baseSignal.skipRepeats { $0.characters.count == $1.characters.count } - - var values: [String] = [] - signal.observeNext { values.append($0) } - - expect(values) == [] - - observer.sendNext("a") - expect(values) == [ "a" ] - - observer.sendNext("b") - expect(values) == [ "a" ] - - observer.sendNext("cc") - expect(values) == [ "a", "cc" ] - - observer.sendNext("d") - expect(values) == [ "a", "cc", "d" ] - } - - it("should not store strong reference to previously passed items") { - var disposedItems: [Bool] = [] - - struct Item { - let payload: Bool - let disposable: ScopedDisposable - } - - func item(_ payload: Bool) -> Item { - return Item( - payload: payload, - disposable: ScopedDisposable(ActionDisposable { disposedItems.append(payload) }) - ) - } - - let (baseSignal, observer) = Signal.pipe() - baseSignal.skipRepeats { $0.payload == $1.payload }.observeNext { _ in } - - observer.sendNext(item(true)) - expect(disposedItems) == [] - - observer.sendNext(item(false)) - expect(disposedItems) == [ true ] - - observer.sendNext(item(false)) - expect(disposedItems) == [ true, false ] - - observer.sendNext(item(true)) - expect(disposedItems) == [ true, false, false ] - - observer.sendCompleted() - expect(disposedItems) == [ true, false, false, true ] - } - } - - describe("uniqueValues") { - it("should skip values that have been already seen") { - let (baseSignal, observer) = Signal.pipe() - let signal = baseSignal.uniqueValues() - - var values: [String] = [] - signal.observeNext { values.append($0) } - - expect(values) == [] - - observer.sendNext("a") - expect(values) == [ "a" ] - - observer.sendNext("b") - expect(values) == [ "a", "b" ] - - observer.sendNext("a") - expect(values) == [ "a", "b" ] - - observer.sendNext("b") - expect(values) == [ "a", "b" ] - - observer.sendNext("c") - expect(values) == [ "a", "b", "c" ] - - observer.sendCompleted() - expect(values) == [ "a", "b", "c" ] - } - } - - describe("skipWhile") { - var signal: Signal! - var observer: Signal.Observer! - - var lastValue: Int? - - beforeEach { - let (baseSignal, incomingObserver) = Signal.pipe() - - signal = baseSignal.skip { $0 < 2 } - observer = incomingObserver - lastValue = nil - - signal.observeNext { lastValue = $0 } - } - - it("should skip while the predicate is true") { - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue).to(beNil()) - - observer.sendNext(2) - expect(lastValue) == 2 - - observer.sendNext(0) - expect(lastValue) == 0 - } - - it("should not skip any values when the predicate starts false") { - expect(lastValue).to(beNil()) - - observer.sendNext(3) - expect(lastValue) == 3 - - observer.sendNext(1) - expect(lastValue) == 1 - } - } - - describe("skipUntil") { - var signal: Signal! - var observer: Signal.Observer! - var triggerObserver: Signal<(), NoError>.Observer! - - var lastValue: Int? = nil - - beforeEach { - let (baseSignal, incomingObserver) = Signal.pipe() - let (triggerSignal, incomingTriggerObserver) = Signal<(), NoError>.pipe() - - signal = baseSignal.skip(until: triggerSignal) - observer = incomingObserver - triggerObserver = incomingTriggerObserver - - lastValue = nil - - signal.observe { event in - switch event { - case let .next(value): - lastValue = value - default: - break - } - } - } - - it("should skip values until the trigger fires") { - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue).to(beNil()) - - observer.sendNext(2) - expect(lastValue).to(beNil()) - - triggerObserver.sendNext(()) - observer.sendNext(0) - expect(lastValue) == 0 - } - - it("should skip values until the trigger completes") { - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue).to(beNil()) - - observer.sendNext(2) - expect(lastValue).to(beNil()) - - triggerObserver.sendCompleted() - observer.sendNext(0) - expect(lastValue) == 0 - } - } - - describe("take") { - it("should take initial values") { - let (baseSignal, observer) = Signal.pipe() - let signal = baseSignal.take(first: 2) - - var lastValue: Int? - var completed = false - signal.observe { event in - switch event { - case let .next(value): - lastValue = value - case .completed: - completed = true - default: - break - } - } - - expect(lastValue).to(beNil()) - expect(completed) == false - - observer.sendNext(1) - expect(lastValue) == 1 - expect(completed) == false - - observer.sendNext(2) - expect(lastValue) == 2 - expect(completed) == true - } - - it("should complete immediately after taking given number of values") { - let numbers = [ 1, 2, 4, 4, 5 ] - let testScheduler = TestScheduler() - - var signal: Signal = Signal { observer in - testScheduler.schedule { - for number in numbers { - observer.sendNext(number) - } - } - return nil - } - - var completed = false - - signal = signal.take(first: numbers.count) - signal.observeCompleted { completed = true } - - expect(completed) == false - testScheduler.run() - expect(completed) == true - } - - it("should interrupt when 0") { - let numbers = [ 1, 2, 4, 4, 5 ] - let testScheduler = TestScheduler() - - let signal: Signal = Signal { observer in - testScheduler.schedule { - for number in numbers { - observer.sendNext(number) - } - } - return nil - } - - var result: [Int] = [] - var interrupted = false - - signal - .take(first: 0) - .observe { event in - switch event { - case let .next(number): - result.append(number) - case .interrupted: - interrupted = true - default: - break - } - } - - expect(interrupted) == true - - testScheduler.run() - expect(result).to(beEmpty()) - } - } - - describe("collect") { - it("should collect all values") { - let (original, observer) = Signal.pipe() - let signal = original.collect() - let expectedResult = [ 1, 2, 3 ] - - var result: [Int]? - - signal.observeNext { value in - expect(result).to(beNil()) - result = value - } - - for number in expectedResult { - observer.sendNext(number) - } - - expect(result).to(beNil()) - observer.sendCompleted() - expect(result) == expectedResult - } - - it("should complete with an empty array if there are no values") { - let (original, observer) = Signal.pipe() - let signal = original.collect() - - var result: [Int]? - - signal.observeNext { result = $0 } - - expect(result).to(beNil()) - observer.sendCompleted() - expect(result) == [] - } - - it("should forward errors") { - let (original, observer) = Signal.pipe() - let signal = original.collect() - - var error: TestError? - - signal.observeFailed { error = $0 } - - expect(error).to(beNil()) - observer.sendFailed(.default) - expect(error) == TestError.default - } - - it("should collect an exact count of values") { - let (original, observer) = Signal.pipe() - - let signal = original.collect(count: 3) - - var observedValues: [[Int]] = [] - - signal.observeNext { value in - observedValues.append(value) - } - - var expectation: [[Int]] = [] - - for i in 1...7 { - observer.sendNext(i) - - if i % 3 == 0 { - expectation.append([Int]((i - 2)...i)) - expect(observedValues as NSArray) == expectation as NSArray - } else { - expect(observedValues as NSArray) == expectation as NSArray - } - } - - observer.sendCompleted() - - expectation.append([7]) - expect(observedValues as NSArray) == expectation as NSArray - } - - it("should collect values until it matches a certain value") { - let (original, observer) = Signal.pipe() - - let signal = original.collect { _, next in next != 5 } - - var expectedValues = [ - [5, 5], - [42, 5] - ] - - signal.observeNext { value in - expect(value) == expectedValues.removeFirst() - } - - signal.observeCompleted { - expect(expectedValues as NSArray) == [] - } - - expectedValues - .flatMap { $0 } - .forEach(observer.sendNext) - - observer.sendCompleted() - } - - it("should collect values until it matches a certain condition on values") { - let (original, observer) = Signal.pipe() - - let signal = original.collect { values in values.reduce(0, +) == 10 } - - var expectedValues = [ - [1, 2, 3, 4], - [5, 6, 7, 8, 9] - ] - - signal.observeNext { value in - expect(value) == expectedValues.removeFirst() - } - - signal.observeCompleted { - expect(expectedValues as NSArray) == [] - } - - expectedValues - .flatMap { $0 } - .forEach(observer.sendNext) - - observer.sendCompleted() - } - } - - describe("takeUntil") { - var signal: Signal! - var observer: Signal.Observer! - var triggerObserver: Signal<(), NoError>.Observer! - - var lastValue: Int? = nil - var completed: Bool = false - - beforeEach { - let (baseSignal, incomingObserver) = Signal.pipe() - let (triggerSignal, incomingTriggerObserver) = Signal<(), NoError>.pipe() - - signal = baseSignal.take(until: triggerSignal) - observer = incomingObserver - triggerObserver = incomingTriggerObserver - - lastValue = nil - completed = false - - signal.observe { event in - switch event { - case let .next(value): - lastValue = value - case .completed: - completed = true - default: - break - } - } - } - - it("should take values until the trigger fires") { - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue) == 1 - - observer.sendNext(2) - expect(lastValue) == 2 - - expect(completed) == false - triggerObserver.sendNext(()) - expect(completed) == true - } - - it("should take values until the trigger completes") { - expect(lastValue).to(beNil()) - - observer.sendNext(1) - expect(lastValue) == 1 - - observer.sendNext(2) - expect(lastValue) == 2 - - expect(completed) == false - triggerObserver.sendCompleted() - expect(completed) == true - } - - it("should complete if the trigger fires immediately") { - expect(lastValue).to(beNil()) - expect(completed) == false - - triggerObserver.sendNext(()) - - expect(completed) == true - expect(lastValue).to(beNil()) - } - } - - describe("takeUntilReplacement") { - var signal: Signal! - var observer: Signal.Observer! - var replacementObserver: Signal.Observer! - - var lastValue: Int? = nil - var completed: Bool = false - - beforeEach { - let (baseSignal, incomingObserver) = Signal.pipe() - let (replacementSignal, incomingReplacementObserver) = Signal.pipe() - - signal = baseSignal.take(untilReplacement: replacementSignal) - observer = incomingObserver - replacementObserver = incomingReplacementObserver - - lastValue = nil - completed = false - - signal.observe { event in - switch event { - case let .next(value): - lastValue = value - case .completed: - completed = true - default: - break - } - } - } - - it("should take values from the original then the replacement") { - expect(lastValue).to(beNil()) - expect(completed) == false - - observer.sendNext(1) - expect(lastValue) == 1 - - observer.sendNext(2) - expect(lastValue) == 2 - - replacementObserver.sendNext(3) - - expect(lastValue) == 3 - expect(completed) == false - - observer.sendNext(4) - - expect(lastValue) == 3 - expect(completed) == false - - replacementObserver.sendNext(5) - expect(lastValue) == 5 - - expect(completed) == false - replacementObserver.sendCompleted() - expect(completed) == true - } - } - - describe("takeWhile") { - var signal: Signal! - var observer: Signal.Observer! - - beforeEach { - let (baseSignal, incomingObserver) = Signal.pipe() - signal = baseSignal.take { $0 <= 4 } - observer = incomingObserver - } - - it("should take while the predicate is true") { - var latestValue: Int! - var completed = false - - signal.observe { event in - switch event { - case let .next(value): - latestValue = value - case .completed: - completed = true - default: - break - } - } - - for value in -1...4 { - observer.sendNext(value) - expect(latestValue) == value - expect(completed) == false - } - - observer.sendNext(5) - expect(latestValue) == 4 - expect(completed) == true - } - - it("should complete if the predicate starts false") { - var latestValue: Int? - var completed = false - - signal.observe { event in - switch event { - case let .next(value): - latestValue = value - case .completed: - completed = true - default: - break - } - } - - observer.sendNext(5) - expect(latestValue).to(beNil()) - expect(completed) == true - } - } - - describe("observeOn") { - it("should send events on the given scheduler") { - let testScheduler = TestScheduler() - let (signal, observer) = Signal.pipe() - - var result: [Int] = [] - - signal - .observe(on: testScheduler) - .observeNext { result.append($0) } - - observer.sendNext(1) - observer.sendNext(2) - expect(result).to(beEmpty()) - - testScheduler.run() - expect(result) == [ 1, 2 ] - } - } - - describe("delay") { - it("should send events on the given scheduler after the interval") { - let testScheduler = TestScheduler() - let signal: Signal = Signal { observer in - testScheduler.schedule { - observer.sendNext(1) - } - testScheduler.schedule(after: 5) { - observer.sendNext(2) - observer.sendCompleted() - } - return nil - } - - var result: [Int] = [] - var completed = false - - signal - .delay(10, on: testScheduler) - .observe { event in - switch event { - case let .next(number): - result.append(number) - case .completed: - completed = true - default: - break - } - } - - testScheduler.advance(by: 4) // send initial value - expect(result).to(beEmpty()) - - testScheduler.advance(by: 10) // send second value and receive first - expect(result) == [ 1 ] - expect(completed) == false - - testScheduler.advance(by: 10) // send second value and receive first - expect(result) == [ 1, 2 ] - expect(completed) == true - } - - it("should schedule errors immediately") { - let testScheduler = TestScheduler() - let signal: Signal = Signal { observer in - testScheduler.schedule { - observer.sendFailed(TestError.default) - } - return nil - } - - var errored = false - - signal - .delay(10, on: testScheduler) - .observeFailed { _ in errored = true } - - testScheduler.advance() - expect(errored) == true - } - } - - describe("throttle") { - var scheduler: TestScheduler! - var observer: Signal.Observer! - var signal: Signal! - - beforeEach { - scheduler = TestScheduler() - - let (baseSignal, baseObserver) = Signal.pipe() - observer = baseObserver - - signal = baseSignal.throttle(1, on: scheduler) - expect(signal).notTo(beNil()) - } - - it("should send values on the given scheduler at no less than the interval") { - var values: [Int] = [] - signal.observeNext { value in - values.append(value) - } - - expect(values) == [] - - observer.sendNext(0) - expect(values) == [] - - scheduler.advance() - expect(values) == [ 0 ] - - observer.sendNext(1) - observer.sendNext(2) - expect(values) == [ 0 ] - - scheduler.advance(by: 1.5) - expect(values) == [ 0, 2 ] - - scheduler.advance(by: 3) - expect(values) == [ 0, 2 ] - - observer.sendNext(3) - expect(values) == [ 0, 2 ] - - scheduler.advance() - expect(values) == [ 0, 2, 3 ] - - observer.sendNext(4) - observer.sendNext(5) - scheduler.advance() - expect(values) == [ 0, 2, 3 ] - - scheduler.rewind(by: 2) - expect(values) == [ 0, 2, 3 ] - - observer.sendNext(6) - scheduler.advance() - expect(values) == [ 0, 2, 3, 6 ] - - observer.sendNext(7) - observer.sendNext(8) - scheduler.advance() - expect(values) == [ 0, 2, 3, 6 ] - - scheduler.run() - expect(values) == [ 0, 2, 3, 6, 8 ] - } - - it("should schedule completion immediately") { - var values: [Int] = [] - var completed = false - - signal.observe { event in - switch event { - case let .next(value): - values.append(value) - case .completed: - completed = true - default: - break - } - } - - observer.sendNext(0) - scheduler.advance() - expect(values) == [ 0 ] - - observer.sendNext(1) - observer.sendCompleted() - expect(completed) == false - - scheduler.advance() - expect(values) == [ 0 ] - expect(completed) == true - - scheduler.run() - expect(values) == [ 0 ] - expect(completed) == true - } - } - - describe("debounce") { - var scheduler: TestScheduler! - var observer: Signal.Observer! - var signal: Signal! - - beforeEach { - scheduler = TestScheduler() - - let (baseSignal, baseObserver) = Signal.pipe() - observer = baseObserver - - signal = baseSignal.debounce(1, on: scheduler) - expect(signal).notTo(beNil()) - } - - it("should send values on the given scheduler once the interval has passed since the last value was sent") { - var values: [Int] = [] - signal.observeNext { value in - values.append(value) - } - - expect(values) == [] - - observer.sendNext(0) - expect(values) == [] - - scheduler.advance() - expect(values) == [] - - observer.sendNext(1) - observer.sendNext(2) - expect(values) == [] - - scheduler.advance(by: 1.5) - expect(values) == [ 2 ] - - scheduler.advance(by: 3) - expect(values) == [ 2 ] - - observer.sendNext(3) - expect(values) == [ 2 ] - - scheduler.advance() - expect(values) == [ 2 ] - - observer.sendNext(4) - observer.sendNext(5) - scheduler.advance() - expect(values) == [ 2 ] - - scheduler.run() - expect(values) == [ 2, 5 ] - } - - it("should schedule completion immediately") { - var values: [Int] = [] - var completed = false - - signal.observe { event in - switch event { - case let .next(value): - values.append(value) - case .completed: - completed = true - default: - break - } - } - - observer.sendNext(0) - scheduler.advance() - expect(values) == [] - - observer.sendNext(1) - observer.sendCompleted() - expect(completed) == false - - scheduler.advance() - expect(values) == [] - expect(completed) == true - - scheduler.run() - expect(values) == [] - expect(completed) == true - } - } - - describe("sampleWith") { - var sampledSignal: Signal<(Int, String), NoError>! - var observer: Signal.Observer! - var samplerObserver: Signal.Observer! - - beforeEach { - let (signal, incomingObserver) = Signal.pipe() - let (sampler, incomingSamplerObserver) = Signal.pipe() - sampledSignal = signal.sample(with: sampler) - observer = incomingObserver - samplerObserver = incomingSamplerObserver - } - - it("should forward the latest value when the sampler fires") { - var result: [String] = [] - sampledSignal.observeNext { (left, right) in result.append("\(left)\(right)") } - - observer.sendNext(1) - observer.sendNext(2) - samplerObserver.sendNext("a") - expect(result) == [ "2a" ] - } - - it("should do nothing if sampler fires before signal receives value") { - var result: [String] = [] - sampledSignal.observeNext { (left, right) in result.append("\(left)\(right)") } - - samplerObserver.sendNext("a") - expect(result).to(beEmpty()) - } - - it("should send lates value with sampler value multiple times when sampler fires multiple times") { - var result: [String] = [] - sampledSignal.observeNext { (left, right) in result.append("\(left)\(right)") } - - observer.sendNext(1) - samplerObserver.sendNext("a") - samplerObserver.sendNext("b") - expect(result) == [ "1a", "1b" ] - } - - it("should complete when both inputs have completed") { - var completed = false - sampledSignal.observeCompleted { completed = true } - - observer.sendCompleted() - expect(completed) == false - - samplerObserver.sendCompleted() - expect(completed) == true - } - } - - describe("sampleOn") { - var sampledSignal: Signal! - var observer: Signal.Observer! - var samplerObserver: Signal<(), NoError>.Observer! - - beforeEach { - let (signal, incomingObserver) = Signal.pipe() - let (sampler, incomingSamplerObserver) = Signal<(), NoError>.pipe() - sampledSignal = signal.sample(on: sampler) - observer = incomingObserver - samplerObserver = incomingSamplerObserver - } - - it("should forward the latest value when the sampler fires") { - var result: [Int] = [] - sampledSignal.observeNext { result.append($0) } - - observer.sendNext(1) - observer.sendNext(2) - samplerObserver.sendNext(()) - expect(result) == [ 2 ] - } - - it("should do nothing if sampler fires before signal receives value") { - var result: [Int] = [] - sampledSignal.observeNext { result.append($0) } - - samplerObserver.sendNext(()) - expect(result).to(beEmpty()) - } - - it("should send lates value multiple times when sampler fires multiple times") { - var result: [Int] = [] - sampledSignal.observeNext { result.append($0) } - - observer.sendNext(1) - samplerObserver.sendNext(()) - samplerObserver.sendNext(()) - expect(result) == [ 1, 1 ] - } - - it("should complete when both inputs have completed") { - var completed = false - sampledSignal.observeCompleted { completed = true } - - observer.sendCompleted() - expect(completed) == false - - samplerObserver.sendCompleted() - expect(completed) == true - } - } - - describe("combineLatestWith") { - var combinedSignal: Signal<(Int, Double), NoError>! - var observer: Signal.Observer! - var otherObserver: Signal.Observer! - - beforeEach { - let (signal, incomingObserver) = Signal.pipe() - let (otherSignal, incomingOtherObserver) = Signal.pipe() - combinedSignal = signal.combineLatest(with: otherSignal) - observer = incomingObserver - otherObserver = incomingOtherObserver - } - - it("should forward the latest values from both inputs") { - var latest: (Int, Double)? - combinedSignal.observeNext { latest = $0 } - - observer.sendNext(1) - expect(latest).to(beNil()) - - // is there a better way to test tuples? - otherObserver.sendNext(1.5) - expect(latest?.0) == 1 - expect(latest?.1) == 1.5 - - observer.sendNext(2) - expect(latest?.0) == 2 - expect(latest?.1) == 1.5 - } - - it("should complete when both inputs have completed") { - var completed = false - combinedSignal.observeCompleted { completed = true } - - observer.sendCompleted() - expect(completed) == false - - otherObserver.sendCompleted() - expect(completed) == true - } - } - - describe("zipWith") { - var leftObserver: Signal.Observer! - var rightObserver: Signal.Observer! - var zipped: Signal<(Int, String), NoError>! - - beforeEach { - let (leftSignal, incomingLeftObserver) = Signal.pipe() - let (rightSignal, incomingRightObserver) = Signal.pipe() - - leftObserver = incomingLeftObserver - rightObserver = incomingRightObserver - zipped = leftSignal.zip(with: rightSignal) - } - - it("should combine pairs") { - var result: [String] = [] - zipped.observeNext { (left, right) in result.append("\(left)\(right)") } - - leftObserver.sendNext(1) - leftObserver.sendNext(2) - expect(result) == [] - - rightObserver.sendNext("foo") - expect(result) == [ "1foo" ] - - leftObserver.sendNext(3) - rightObserver.sendNext("bar") - expect(result) == [ "1foo", "2bar" ] - - rightObserver.sendNext("buzz") - expect(result) == [ "1foo", "2bar", "3buzz" ] - - rightObserver.sendNext("fuzz") - expect(result) == [ "1foo", "2bar", "3buzz" ] - - leftObserver.sendNext(4) - expect(result) == [ "1foo", "2bar", "3buzz", "4fuzz" ] - } - - it("should complete when the shorter signal has completed") { - var result: [String] = [] - var completed = false - - zipped.observe { event in - switch event { - case let .next(left, right): - result.append("\(left)\(right)") - case .completed: - completed = true - default: - break - } - } - - expect(completed) == false - - leftObserver.sendNext(0) - leftObserver.sendCompleted() - expect(completed) == false - expect(result) == [] - - rightObserver.sendNext("foo") - expect(completed) == true - expect(result) == [ "0foo" ] - } - - it("should complete when both signal have completed") { - var result: [String] = [] - var completed = false - - zipped.observe { event in - switch event { - case let .next(left, right): - result.append("\(left)\(right)") - case .completed: - completed = true - default: - break - } - } - - expect(completed) == false - - leftObserver.sendNext(0) - leftObserver.sendCompleted() - expect(completed) == false - expect(result) == [] - - rightObserver.sendCompleted() - expect(result) == [ ] - } - - it("should complete and drop unpaired pending values when both signal have completed") { - var result: [String] = [] - var completed = false - - zipped.observe { event in - switch event { - case let .next(left, right): - result.append("\(left)\(right)") - case .completed: - completed = true - default: - break - } - } - - expect(completed) == false - - leftObserver.sendNext(0) - leftObserver.sendNext(1) - leftObserver.sendNext(2) - leftObserver.sendNext(3) - leftObserver.sendCompleted() - expect(completed) == false - expect(result) == [] - - rightObserver.sendNext("foo") - rightObserver.sendNext("bar") - rightObserver.sendCompleted() - expect(result) == ["0foo", "1bar"] - } - } - - describe("materialize") { - it("should reify events from the signal") { - let (signal, observer) = Signal.pipe() - var latestEvent: Event? - signal - .materialize() - .observeNext { latestEvent = $0 } - - observer.sendNext(2) - - expect(latestEvent).toNot(beNil()) - if let latestEvent = latestEvent { - switch latestEvent { - case let .next(value): - expect(value) == 2 - default: - fail() - } - } - - observer.sendFailed(TestError.default) - if let latestEvent = latestEvent { - switch latestEvent { - case .failed: - () - default: - fail() - } - } - } - } - - describe("dematerialize") { - typealias IntEvent = Event - var observer: Signal.Observer! - var dematerialized: Signal! - - beforeEach { - let (signal, incomingObserver) = Signal.pipe() - observer = incomingObserver - dematerialized = signal.dematerialize() - } - - it("should send values for Next events") { - var result: [Int] = [] - dematerialized - .assumeNoErrors() - .observeNext { result.append($0) } - - expect(result).to(beEmpty()) - - observer.sendNext(.next(2)) - expect(result) == [ 2 ] - - observer.sendNext(.next(4)) - expect(result) == [ 2, 4 ] - } - - it("should error out for Error events") { - var errored = false - dematerialized.observeFailed { _ in errored = true } - - expect(errored) == false - - observer.sendNext(.failed(TestError.default)) - expect(errored) == true - } - - it("should complete early for Completed events") { - var completed = false - dematerialized.observeCompleted { completed = true } - - expect(completed) == false - observer.sendNext(IntEvent.completed) - expect(completed) == true - } - } - - describe("takeLast") { - var observer: Signal.Observer! - var lastThree: Signal! - - beforeEach { - let (signal, incomingObserver) = Signal.pipe() - observer = incomingObserver - lastThree = signal.take(last: 3) - } - - it("should send the last N values upon completion") { - var result: [Int] = [] - lastThree - .assumeNoErrors() - .observeNext { result.append($0) } - - observer.sendNext(1) - observer.sendNext(2) - observer.sendNext(3) - observer.sendNext(4) - expect(result).to(beEmpty()) - - observer.sendCompleted() - expect(result) == [ 2, 3, 4 ] - } - - it("should send less than N values if not enough were received") { - var result: [Int] = [] - lastThree - .assumeNoErrors() - .observeNext { result.append($0) } - - observer.sendNext(1) - observer.sendNext(2) - observer.sendCompleted() - expect(result) == [ 1, 2 ] - } - - it("should send nothing when errors") { - var result: [Int] = [] - var errored = false - lastThree.observe { event in - switch event { - case let .next(value): - result.append(value) - case .failed: - errored = true - default: - break - } - } - - observer.sendNext(1) - observer.sendNext(2) - observer.sendNext(3) - expect(errored) == false - - observer.sendFailed(TestError.default) - expect(errored) == true - expect(result).to(beEmpty()) - } - } - - describe("timeoutWithError") { - var testScheduler: TestScheduler! - var signal: Signal! - var observer: Signal.Observer! - - beforeEach { - testScheduler = TestScheduler() - let (baseSignal, incomingObserver) = Signal.pipe() - signal = baseSignal.timeout(after: 2, raising: TestError.default, on: testScheduler) - observer = incomingObserver - } - - it("should complete if within the interval") { - var completed = false - var errored = false - signal.observe { event in - switch event { - case .completed: - completed = true - case .failed: - errored = true - default: - break - } - } - - testScheduler.schedule(after: 1) { - observer.sendCompleted() - } - - expect(completed) == false - expect(errored) == false - - testScheduler.run() - expect(completed) == true - expect(errored) == false - } - - it("should error if not completed before the interval has elapsed") { - var completed = false - var errored = false - signal.observe { event in - switch event { - case .completed: - completed = true - case .failed: - errored = true - default: - break - } - } - - testScheduler.schedule(after: 3) { - observer.sendCompleted() - } - - expect(completed) == false - expect(errored) == false - - testScheduler.run() - expect(completed) == false - expect(errored) == true - } - - it("should be available for NoError") { - let signal: Signal = Signal.never - .timeout(after: 2, raising: TestError.default, on: testScheduler) - - _ = signal - } - } - - describe("attempt") { - it("should forward original values upon success") { - let (baseSignal, observer) = Signal.pipe() - let signal = baseSignal.attempt { _ in - return .success() - } - - var current: Int? - signal - .assumeNoErrors() - .observeNext { value in - current = value - } - - for value in 1...5 { - observer.sendNext(value) - expect(current) == value - } - } - - it("should error if an attempt fails") { - let (baseSignal, observer) = Signal.pipe() - let signal = baseSignal.attempt { _ in - return .failure(.default) - } - - var error: TestError? - signal.observeFailed { err in - error = err - } - - observer.sendNext(42) - expect(error) == TestError.default - } - } - - describe("attemptMap") { - it("should forward mapped values upon success") { - let (baseSignal, observer) = Signal.pipe() - let signal = baseSignal.attemptMap { num -> Result in - return .success(num % 2 == 0) - } - - var even: Bool? - signal - .assumeNoErrors() - .observeNext { value in - even = value - } - - observer.sendNext(1) - expect(even) == false - - observer.sendNext(2) - expect(even) == true - } - - it("should error if a mapping fails") { - let (baseSignal, observer) = Signal.pipe() - let signal = baseSignal.attemptMap { _ -> Result in - return .failure(.default) - } - - var error: TestError? - signal.observeFailed { err in - error = err - } - - observer.sendNext(42) - expect(error) == TestError.default - } - } - - describe("combinePrevious") { - var observer: Signal.Observer! - let initialValue: Int = 0 - var latestValues: (Int, Int)? - - beforeEach { - latestValues = nil - - let (signal, baseObserver) = Signal.pipe() - observer = baseObserver - signal.combinePrevious(initialValue).observeNext { latestValues = $0 } - } - - it("should forward the latest value with previous value") { - expect(latestValues).to(beNil()) - - observer.sendNext(1) - expect(latestValues?.0) == initialValue - expect(latestValues?.1) == 1 - - observer.sendNext(2) - expect(latestValues?.0) == 1 - expect(latestValues?.1) == 2 - } - } - - describe("combineLatest") { - var signalA: Signal! - var signalB: Signal! - var signalC: Signal! - var observerA: Signal.Observer! - var observerB: Signal.Observer! - var observerC: Signal.Observer! - - var combinedValues: [Int]? - var completed: Bool! - - beforeEach { - combinedValues = nil - completed = false - - let (baseSignalA, baseObserverA) = Signal.pipe() - let (baseSignalB, baseObserverB) = Signal.pipe() - let (baseSignalC, baseObserverC) = Signal.pipe() - - signalA = baseSignalA - signalB = baseSignalB - signalC = baseSignalC - - observerA = baseObserverA - observerB = baseObserverB - observerC = baseObserverC - } - - let combineLatestExampleName = "combineLatest examples" - sharedExamples(combineLatestExampleName) { - it("should forward the latest values from all inputs"){ - expect(combinedValues).to(beNil()) - - observerA.sendNext(0) - observerB.sendNext(1) - observerC.sendNext(2) - expect(combinedValues) == [0, 1, 2] - - observerA.sendNext(10) - expect(combinedValues) == [10, 1, 2] - } - - it("should not forward the latest values before all inputs"){ - expect(combinedValues).to(beNil()) - - observerA.sendNext(0) - expect(combinedValues).to(beNil()) - - observerB.sendNext(1) - expect(combinedValues).to(beNil()) - - observerC.sendNext(2) - expect(combinedValues) == [0, 1, 2] - } - - it("should complete when all inputs have completed"){ - expect(completed) == false - - observerA.sendCompleted() - observerB.sendCompleted() - expect(completed) == false - - observerC.sendCompleted() - expect(completed) == true - } - } - - describe("tuple") { - beforeEach { - Signal.combineLatest(signalA, signalB, signalC) - .observe { event in - switch event { - case let .next(value): - combinedValues = [value.0, value.1, value.2] - case .completed: - completed = true - default: - break - } - } - } - - itBehavesLike(combineLatestExampleName) - } - - describe("sequence") { - beforeEach { - Signal.combineLatest([signalA, signalB, signalC]) - .observe { event in - switch event { - case let .next(values): - combinedValues = values - case .completed: - completed = true - default: - break - } - } - } - - itBehavesLike(combineLatestExampleName) - } - } - - describe("zip") { - var signalA: Signal! - var signalB: Signal! - var signalC: Signal! - var observerA: Signal.Observer! - var observerB: Signal.Observer! - var observerC: Signal.Observer! - - var zippedValues: [Int]? - var completed: Bool! - - beforeEach { - zippedValues = nil - completed = false - - let (baseSignalA, baseObserverA) = Signal.pipe() - let (baseSignalB, baseObserverB) = Signal.pipe() - let (baseSignalC, baseObserverC) = Signal.pipe() - - signalA = baseSignalA - signalB = baseSignalB - signalC = baseSignalC - - observerA = baseObserverA - observerB = baseObserverB - observerC = baseObserverC - } - - let zipExampleName = "zip examples" - sharedExamples(zipExampleName) { - it("should combine all set"){ - expect(zippedValues).to(beNil()) - - observerA.sendNext(0) - expect(zippedValues).to(beNil()) - - observerB.sendNext(1) - expect(zippedValues).to(beNil()) - - observerC.sendNext(2) - expect(zippedValues) == [0, 1, 2] - - observerA.sendNext(10) - expect(zippedValues) == [0, 1, 2] - - observerA.sendNext(20) - expect(zippedValues) == [0, 1, 2] - - observerB.sendNext(11) - expect(zippedValues) == [0, 1, 2] - - observerC.sendNext(12) - expect(zippedValues) == [10, 11, 12] - } - - it("should complete when the shorter signal has completed"){ - expect(completed) == false - - observerB.sendNext(1) - observerC.sendNext(2) - observerB.sendCompleted() - observerC.sendCompleted() - expect(completed) == false - - observerA.sendNext(0) - expect(completed) == true - } - } - - describe("tuple") { - beforeEach { - Signal.zip(signalA, signalB, signalC) - .observe { event in - switch event { - case let .next(value): - zippedValues = [value.0, value.1, value.2] - case .completed: - completed = true - default: - break - } - } - } - - itBehavesLike(zipExampleName) - } - - describe("sequence") { - beforeEach { - Signal.zip([signalA, signalB, signalC]) - .observe { event in - switch event { - case let .next(values): - zippedValues = values - case .completed: - completed = true - default: - break - } - } - } - - itBehavesLike(zipExampleName) - } - - describe("log events") { - it("should output the correct event without identifier") { - let expectations: [(String) -> Void] = [ - { event in expect(event) == "[] next 1" }, - { event in expect(event) == "[] completed" }, - { event in expect(event) == "[] terminated" }, - { event in expect(event) == "[] disposed" }, - ] - - let logger = TestLogger(expectations: expectations) - - let (signal, observer) = Signal.pipe() - signal - .logEvents(logger: logger.logEvent) - .observe { _ in } - - observer.sendNext(1) - observer.sendCompleted() - } - - it("should output the correct event with identifier") { - let expectations: [(String) -> Void] = [ - { event in expect(event) == "[test.rac] next 1" }, - { event in expect(event) == "[test.rac] failed error1" }, - { event in expect(event) == "[test.rac] terminated" }, - { event in expect(event) == "[test.rac] disposed" }, - ] - - let logger = TestLogger(expectations: expectations) - - let (signal, observer) = Signal.pipe() - signal - .logEvents(identifier: "test.rac", logger: logger.logEvent) - .observe { _ in } - - observer.sendNext(1) - observer.sendFailed(.error1) - } - - it("should only output the events specified in the `events` parameter") { - let expectations: [(String) -> Void] = [ - { event in expect(event) == "[test.rac] failed error1" }, - ] - - let logger = TestLogger(expectations: expectations) - - let (signal, observer) = Signal.pipe() - signal - .logEvents(identifier: "test.rac", events: [.failed], logger: logger.logEvent) - .observe { _ in } - - observer.sendNext(1) - observer.sendFailed(.error1) - } - } - } - } -} diff --git a/ReactiveSwiftTests/TestError.swift b/ReactiveSwiftTests/TestError.swift deleted file mode 100644 index b9d59df8ab..0000000000 --- a/ReactiveSwiftTests/TestError.swift +++ /dev/null @@ -1,43 +0,0 @@ -// -// TestError.swift -// ReactiveSwift -// -// Created by Almas Sapargali on 1/26/15. -// Copyright (c) 2015 GitHub. All rights reserved. -// - -import ReactiveSwift -import Result - -internal enum TestError: Int { - case `default` = 0 - case error1 = 1 - case error2 = 2 -} - -extension TestError: Error { -} - - -internal extension SignalProducerProtocol { - /// Halts if an error is emitted in the receiver signal. - /// This is useful in tests to be able to just use `startWithNext` - /// in cases where we know that an error won't be emitted. - func assumeNoErrors() -> SignalProducer { - return self.lift { $0.assumeNoErrors() } - } -} - -internal extension SignalProtocol { - /// Halts if an error is emitted in the receiver signal. - /// This is useful in tests to be able to just use `startWithNext` - /// in cases where we know that an error won't be emitted. - func assumeNoErrors() -> Signal { - return self.mapError { error in - fatalError("Unexpected error: \(error)") - - () - } - } -} - diff --git a/ReactiveSwiftTests/TestLogger.swift b/ReactiveSwiftTests/TestLogger.swift deleted file mode 100644 index bc669279bc..0000000000 --- a/ReactiveSwiftTests/TestLogger.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// TestLogger.swift -// ReactiveSwift -// -// Created by Rui Peres on 29/04/2016. -// Copyright © 2016 GitHub. All rights reserved. -// - -import Foundation -@testable import ReactiveSwift - -final class TestLogger { - fileprivate var expectations: [(String) -> Void] - - init(expectations: [(String) -> Void]) { - self.expectations = expectations - } -} - -extension TestLogger { - func logEvent(_ identifier: String, event: String, fileName: String, functionName: String, lineNumber: Int) { - expectations.removeFirst()("[\(identifier)] \(event)") - } -} diff --git a/Sources b/Sources deleted file mode 120000 index 2ebf1f0a4c..0000000000 --- a/Sources +++ /dev/null @@ -1 +0,0 @@ -ReactiveSwift/ \ No newline at end of file From fe089931c322f498087e6a318b41b50ac8627a79 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Sun, 11 Sep 2016 22:48:24 -0400 Subject: [PATCH 0362/1028] Create ReactiveObjC.framework and ReactiveObjCBridge.framework --- .travis.yml | 50 + ReactiveCocoa.xcodeproj/project.pbxproj | 1932 +--------- ReactiveCocoa/{Swift => }/CocoaAction.swift | 0 .../{Swift => }/DynamicProperty.swift | 0 .../NSObject+KeyValueObserving.swift | 0 .../{Swift => }/NSObject+Lifetime.swift | 0 ReactiveCocoa/ReactiveCocoa.h | 80 - .../{Swift => }/CocoaActionSpec.swift | 6 +- .../{Swift => }/DynamicPropertySpec.swift | 0 .../{Swift => }/KeyValueObservingSpec.swift | 0 .../SignalProducerNimbleMatchers.swift | 0 .../{Swift => }/TestError.swift | 0 ReactiveObjC.xcodeproj/project.pbxproj | 3164 +++++++++++++++++ .../xcschemes/ReactiveObjC-iOS.xcscheme | 142 + .../xcschemes/ReactiveObjC-macOS.xcscheme | 142 + .../xcschemes/ReactiveObjC-tvOS.xcscheme | 142 + .../xcschemes/ReactiveObjC-watchOS.xcscheme | 71 + .../contents.xcworkspacedata | 13 + ReactiveObjC/Info.plist | 28 + .../MKAnnotationView+RACSignalSupport.h | 2 +- .../MKAnnotationView+RACSignalSupport.m | 2 +- .../NSArray+RACSequenceAdditions.h | 2 +- .../NSArray+RACSequenceAdditions.m | 2 +- .../NSControl+RACCommandSupport.h | 2 +- .../NSControl+RACCommandSupport.m | 2 +- .../NSControl+RACTextSignalSupport.h | 2 +- .../NSControl+RACTextSignalSupport.m | 4 +- .../NSData+RACSupport.h | 2 +- .../NSData+RACSupport.m | 2 +- .../NSDictionary+RACSequenceAdditions.h | 2 +- .../NSDictionary+RACSequenceAdditions.m | 2 +- .../NSEnumerator+RACSequenceAdditions.h | 2 +- .../NSEnumerator+RACSequenceAdditions.m | 2 +- .../NSFileHandle+RACSupport.h | 2 +- .../NSFileHandle+RACSupport.m | 2 +- .../NSIndexSet+RACSequenceAdditions.h | 2 +- .../NSIndexSet+RACSequenceAdditions.m | 2 +- .../NSInvocation+RACTypeParsing.h | 2 +- .../NSInvocation+RACTypeParsing.m | 2 +- .../NSNotificationCenter+RACSupport.h | 2 +- .../NSNotificationCenter+RACSupport.m | 4 +- .../NSObject+RACAppKitBindings.h | 2 +- .../NSObject+RACAppKitBindings.m | 6 +- .../NSObject+RACDeallocating.h | 2 +- .../NSObject+RACDeallocating.m | 2 +- .../NSObject+RACDescription.h | 2 +- .../NSObject+RACDescription.m | 2 +- .../NSObject+RACKVOWrapper.h | 2 +- .../NSObject+RACKVOWrapper.m | 6 +- .../NSObject+RACLifting.h | 2 +- .../NSObject+RACLifting.m | 4 +- .../NSObject+RACPropertySubscribing.h | 4 +- .../NSObject+RACPropertySubscribing.m | 6 +- .../NSObject+RACSelectorSignal.h | 2 +- .../NSObject+RACSelectorSignal.m | 4 +- .../NSOrderedSet+RACSequenceAdditions.h | 2 +- .../NSOrderedSet+RACSequenceAdditions.m | 2 +- .../NSSet+RACSequenceAdditions.h | 2 +- .../NSSet+RACSequenceAdditions.m | 2 +- .../NSString+RACKeyPathUtilities.h | 2 +- .../NSString+RACKeyPathUtilities.m | 2 +- .../NSString+RACSequenceAdditions.h | 2 +- .../NSString+RACSequenceAdditions.m | 2 +- .../NSString+RACSupport.h | 2 +- .../NSString+RACSupport.m | 2 +- .../NSText+RACSignalSupport.h | 2 +- .../NSText+RACSignalSupport.m | 4 +- .../NSURLConnection+RACSupport.h | 2 +- .../NSURLConnection+RACSupport.m | 4 +- .../NSUserDefaults+RACSupport.h | 2 +- .../NSUserDefaults+RACSupport.m | 4 +- .../RACArraySequence.h | 2 +- .../RACArraySequence.m | 2 +- .../RACBehaviorSubject.h | 2 +- .../RACBehaviorSubject.m | 2 +- .../RACBlockTrampoline.h | 2 +- .../RACBlockTrampoline.m | 2 +- .../Objective-C => ReactiveObjC}/RACChannel.h | 2 +- .../Objective-C => ReactiveObjC}/RACChannel.m | 2 +- .../Objective-C => ReactiveObjC}/RACCommand.h | 2 +- .../Objective-C => ReactiveObjC}/RACCommand.m | 4 +- .../RACCompoundDisposable.h | 2 +- .../RACCompoundDisposable.m | 2 +- .../RACCompoundDisposableProvider.d | 0 .../RACDelegateProxy.h | 2 +- .../RACDelegateProxy.m | 2 +- .../RACDisposable.h | 2 +- .../RACDisposable.m | 2 +- .../RACDynamicSequence.h | 2 +- .../RACDynamicSequence.m | 2 +- .../RACDynamicSignal.h | 2 +- .../RACDynamicSignal.m | 4 +- .../RACEagerSequence.h | 2 +- .../RACEagerSequence.m | 2 +- .../RACEmptySequence.h | 2 +- .../RACEmptySequence.m | 2 +- .../RACEmptySignal.h | 2 +- .../RACEmptySignal.m | 2 +- .../RACErrorSignal.h | 2 +- .../RACErrorSignal.m | 2 +- .../Objective-C => ReactiveObjC}/RACEvent.h | 2 +- .../Objective-C => ReactiveObjC}/RACEvent.m | 2 +- .../RACGroupedSignal.h | 2 +- .../RACGroupedSignal.m | 2 +- .../RACImmediateScheduler.h | 2 +- .../RACImmediateScheduler.m | 4 +- .../RACIndexSetSequence.h | 2 +- .../RACIndexSetSequence.m | 2 +- .../RACKVOChannel.h | 4 +- .../RACKVOChannel.m | 4 +- .../RACKVOProxy.h | 2 +- .../RACKVOProxy.m | 4 +- .../RACKVOTrampoline.h | 2 +- .../RACKVOTrampoline.m | 2 +- .../RACMulticastConnection+Private.h | 2 +- .../RACMulticastConnection.h | 2 +- .../RACMulticastConnection.m | 2 +- .../RACPassthroughSubscriber.h | 2 +- .../RACPassthroughSubscriber.m | 2 +- .../RACQueueScheduler+Subclass.h | 2 +- .../RACQueueScheduler.h | 2 +- .../RACQueueScheduler.m | 2 +- .../RACReplaySubject.h | 2 +- .../RACReplaySubject.m | 2 +- .../RACReturnSignal.h | 2 +- .../RACReturnSignal.m | 2 +- .../RACScheduler+Private.h | 2 +- .../RACScheduler+Subclass.h | 2 +- .../RACScheduler.h | 2 +- .../RACScheduler.m | 8 +- .../RACScopedDisposable.h | 2 +- .../RACScopedDisposable.m | 2 +- .../RACSequence.h | 2 +- .../RACSequence.m | 2 +- .../RACSerialDisposable.h | 2 +- .../RACSerialDisposable.m | 2 +- .../RACSignal+Operations.h | 2 +- .../RACSignal+Operations.m | 2 +- .../Objective-C => ReactiveObjC}/RACSignal.h | 2 +- .../Objective-C => ReactiveObjC}/RACSignal.m | 2 +- .../RACSignalProvider.d | 0 .../RACSignalSequence.h | 2 +- .../RACSignalSequence.m | 2 +- .../RACStream+Private.h | 2 +- .../Objective-C => ReactiveObjC}/RACStream.h | 2 +- .../Objective-C => ReactiveObjC}/RACStream.m | 2 +- .../RACStringSequence.h | 2 +- .../RACStringSequence.m | 2 +- .../Objective-C => ReactiveObjC}/RACSubject.h | 2 +- .../Objective-C => ReactiveObjC}/RACSubject.m | 4 +- .../RACSubscriber+Private.h | 2 +- .../RACSubscriber.h | 2 +- .../RACSubscriber.m | 4 +- .../RACSubscriptingAssignmentTrampoline.h | 4 +- .../RACSubscriptingAssignmentTrampoline.m | 2 +- .../RACSubscriptionScheduler.h | 2 +- .../RACSubscriptionScheduler.m | 4 +- .../RACTargetQueueScheduler.h | 2 +- .../RACTargetQueueScheduler.m | 4 +- .../RACTestScheduler.h | 2 +- .../RACTestScheduler.m | 6 +- .../Objective-C => ReactiveObjC}/RACTuple.h | 2 +- .../Objective-C => ReactiveObjC}/RACTuple.m | 4 +- .../RACTupleSequence.h | 2 +- .../RACTupleSequence.m | 2 +- .../RACUnarySequence.h | 2 +- .../RACUnarySequence.m | 4 +- .../Objective-C => ReactiveObjC}/RACUnit.h | 2 +- .../Objective-C => ReactiveObjC}/RACUnit.m | 2 +- .../RACValueTransformer.h | 2 +- .../RACValueTransformer.m | 2 +- .../ReactiveCocoa-Bridging-Header.h | 0 ReactiveObjC/ReactiveObjC.h | 95 + .../UIActionSheet+RACSignalSupport.h | 2 +- .../UIActionSheet+RACSignalSupport.m | 2 +- .../UIAlertView+RACSignalSupport.h | 2 +- .../UIAlertView+RACSignalSupport.m | 2 +- .../UIBarButtonItem+RACCommandSupport.h | 2 +- .../UIBarButtonItem+RACCommandSupport.m | 4 +- .../UIButton+RACCommandSupport.h | 2 +- .../UIButton+RACCommandSupport.m | 4 +- ...ICollectionReusableView+RACSignalSupport.h | 2 +- ...ICollectionReusableView+RACSignalSupport.m | 2 +- .../UIControl+RACSignalSupport.h | 2 +- .../UIControl+RACSignalSupport.m | 4 +- .../UIControl+RACSignalSupportPrivate.h | 2 +- .../UIControl+RACSignalSupportPrivate.m | 2 +- .../UIDatePicker+RACSignalSupport.h | 2 +- .../UIDatePicker+RACSignalSupport.m | 4 +- .../UIGestureRecognizer+RACSignalSupport.h | 2 +- .../UIGestureRecognizer+RACSignalSupport.m | 4 +- ...UIImagePickerController+RACSignalSupport.h | 2 +- ...UIImagePickerController+RACSignalSupport.m | 2 +- .../UIRefreshControl+RACCommandSupport.h | 2 +- .../UIRefreshControl+RACCommandSupport.m | 4 +- .../UISegmentedControl+RACSignalSupport.h | 2 +- .../UISegmentedControl+RACSignalSupport.m | 4 +- .../UISlider+RACSignalSupport.h | 2 +- .../UISlider+RACSignalSupport.m | 4 +- .../UIStepper+RACSignalSupport.h | 2 +- .../UIStepper+RACSignalSupport.m | 4 +- .../UISwitch+RACSignalSupport.h | 2 +- .../UISwitch+RACSignalSupport.m | 4 +- .../UITableViewCell+RACSignalSupport.h | 2 +- .../UITableViewCell+RACSignalSupport.m | 2 +- ...bleViewHeaderFooterView+RACSignalSupport.h | 2 +- ...bleViewHeaderFooterView+RACSignalSupport.m | 2 +- .../UITextField+RACSignalSupport.h | 2 +- .../UITextField+RACSignalSupport.m | 6 +- .../UITextView+RACSignalSupport.h | 2 +- .../UITextView+RACSignalSupport.m | 4 +- .../extobjc/EXTKeyPathCoding.h | 0 .../extobjc/EXTRuntimeExtensions.h | 0 .../extobjc/EXTRuntimeExtensions.m | 2 +- .../extobjc/EXTScope.h | 0 .../extobjc/metamacros.h | 0 ReactiveObjCBridge.xcodeproj/project.pbxproj | 1430 ++++++++ .../xcschemes/ReactiveObjCBridge-iOS.xcscheme | 184 + .../ReactiveObjCBridge-macOS.xcscheme | 184 + .../ReactiveObjCBridge-tvOS.xcscheme | 184 + .../ReactiveObjCBridge-watchOS.xcscheme | 80 + .../contents.xcworkspacedata | 22 + ReactiveObjCBridge/Info.plist | 28 + .../ObjectiveCBridging.swift | 5 +- ReactiveObjCBridge/ReactiveObjCBridge.h | 15 + ReactiveObjCBridgeTests/Info.plist | 24 + .../ObjectiveCBridgingSpec.swift | 5 +- ReactiveObjCBridgeTests/TestError.swift | 43 + ReactiveObjCTests/Info.plist | 24 + .../NSControllerRACSupportSpec.m | 2 +- .../NSEnumeratorRACSequenceAdditionsSpec.m | 2 +- .../NSNotificationCenterRACSupportSpec.m | 2 +- .../NSObjectRACAppKitBindingsSpec.m | 4 +- .../NSObjectRACDeallocatingSpec.m | 2 +- .../NSObjectRACLiftingSpec.m | 2 +- .../NSObjectRACPropertySubscribingExamples.h | 2 +- .../NSObjectRACPropertySubscribingExamples.m | 4 +- .../NSObjectRACPropertySubscribingSpec.m | 2 +- .../NSObjectRACSelectorSignalSpec.m | 2 +- .../NSStringRACKeyPathUtilitiesSpec.m | 2 +- .../NSURLConnectionRACSupportSpec.m | 2 +- .../NSUserDefaultsRACSupportSpec.m | 2 +- .../RACBlockTrampolineSpec.m | 2 +- .../RACChannelExamples.h | 2 +- .../RACChannelExamples.m | 2 +- .../RACChannelSpec.m | 2 +- .../RACCommandSpec.m | 2 +- .../RACCompoundDisposableSpec.m | 2 +- .../RACControlCommandExamples.h | 2 +- .../RACControlCommandExamples.m | 2 +- .../RACDelegateProxySpec.m | 2 +- .../RACDisposableSpec.m | 2 +- .../RACEventSpec.m | 2 +- .../RACKVOChannelSpec.m | 2 +- .../RACKVOProxySpec.m | 6 +- .../RACKVOWrapperSpec.m | 4 +- .../RACMulticastConnectionSpec.m | 2 +- .../RACPropertySignalExamples.h | 2 +- .../RACPropertySignalExamples.m | 4 +- .../RACSchedulerSpec.m | 4 +- .../RACSequenceAdditionsSpec.m | 2 +- .../RACSequenceExamples.h | 2 +- .../RACSequenceExamples.m | 2 +- .../RACSequenceSpec.m | 2 +- .../RACSerialDisposableSpec.m | 2 +- .../RACSignalSpec.m | 4 +- .../RACStreamExamples.h | 2 +- .../RACStreamExamples.m | 2 +- .../RACSubclassObject.h | 2 +- .../RACSubclassObject.m | 2 +- .../RACSubjectSpec.m | 6 +- .../RACSubscriberExamples.h | 2 +- .../RACSubscriberExamples.m | 2 +- .../RACSubscriberSpec.m | 4 +- .../RACSubscriptingAssignmentTrampolineSpec.m | 2 +- .../RACTargetQueueSchedulerSpec.m | 2 +- .../RACTestExampleScheduler.h | 4 +- .../RACTestExampleScheduler.m | 2 +- .../RACTestObject.h | 2 +- .../RACTestObject.m | 2 +- .../RACTestSchedulerSpec.m | 2 +- .../RACTestUIButton.h | 2 +- .../RACTestUIButton.m | 2 +- .../RACTupleSpec.m | 2 +- .../UIActionSheetRACSupportSpec.m | 2 +- .../UIAlertViewRACSupportSpec.m | 2 +- .../UIBarButtonItemRACSupportSpec.m | 2 +- .../UIButtonRACSupportSpec.m | 2 +- .../UIImagePickerControllerRACSupportSpec.m | 2 +- ReactiveObjCTests/test-data.json | 5 + 290 files changed, 6393 insertions(+), 2317 deletions(-) rename ReactiveCocoa/{Swift => }/CocoaAction.swift (100%) rename ReactiveCocoa/{Swift => }/DynamicProperty.swift (100%) rename ReactiveCocoa/{Swift => }/NSObject+KeyValueObserving.swift (100%) rename ReactiveCocoa/{Swift => }/NSObject+Lifetime.swift (100%) rename ReactiveCocoaTests/{Swift => }/CocoaActionSpec.swift (91%) rename ReactiveCocoaTests/{Swift => }/DynamicPropertySpec.swift (100%) rename ReactiveCocoaTests/{Swift => }/KeyValueObservingSpec.swift (100%) rename ReactiveCocoaTests/{Swift => }/SignalProducerNimbleMatchers.swift (100%) rename ReactiveCocoaTests/{Swift => }/TestError.swift (100%) create mode 100644 ReactiveObjC.xcodeproj/project.pbxproj create mode 100644 ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-iOS.xcscheme create mode 100644 ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-macOS.xcscheme create mode 100644 ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-tvOS.xcscheme create mode 100644 ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-watchOS.xcscheme create mode 100644 ReactiveObjC.xcworkspace/contents.xcworkspacedata create mode 100644 ReactiveObjC/Info.plist rename {ReactiveCocoa/Objective-C => ReactiveObjC}/MKAnnotationView+RACSignalSupport.h (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/MKAnnotationView+RACSignalSupport.m (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSArray+RACSequenceAdditions.h (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSArray+RACSequenceAdditions.m (95%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSControl+RACCommandSupport.h (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSControl+RACCommandSupport.m (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSControl+RACTextSignalSupport.h (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSControl+RACTextSignalSupport.m (95%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSData+RACSupport.h (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSData+RACSupport.m (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSDictionary+RACSequenceAdditions.h (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSDictionary+RACSequenceAdditions.m (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSEnumerator+RACSequenceAdditions.h (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSEnumerator+RACSequenceAdditions.m (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSFileHandle+RACSupport.h (95%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSFileHandle+RACSupport.m (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSIndexSet+RACSequenceAdditions.h (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSIndexSet+RACSequenceAdditions.m (95%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSInvocation+RACTypeParsing.h (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSInvocation+RACTypeParsing.m (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSNotificationCenter+RACSupport.h (95%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSNotificationCenter+RACSupport.m (94%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSObject+RACAppKitBindings.h (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSObject+RACAppKitBindings.m (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSObject+RACDeallocating.h (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSObject+RACDeallocating.m (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSObject+RACDescription.h (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSObject+RACDescription.m (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSObject+RACKVOWrapper.h (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSObject+RACKVOWrapper.m (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSObject+RACLifting.h (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSObject+RACLifting.m (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSObject+RACPropertySubscribing.h (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSObject+RACPropertySubscribing.m (95%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSObject+RACSelectorSignal.h (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSObject+RACSelectorSignal.m (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSOrderedSet+RACSequenceAdditions.h (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSOrderedSet+RACSequenceAdditions.m (95%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSSet+RACSequenceAdditions.h (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSSet+RACSequenceAdditions.m (95%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSString+RACKeyPathUtilities.h (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSString+RACKeyPathUtilities.m (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSString+RACSequenceAdditions.h (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSString+RACSequenceAdditions.m (95%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSString+RACSupport.h (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSString+RACSupport.m (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSText+RACSignalSupport.h (95%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSText+RACSignalSupport.m (94%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSURLConnection+RACSupport.h (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSURLConnection+RACSupport.m (94%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSUserDefaults+RACSupport.h (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/NSUserDefaults+RACSupport.m (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACArraySequence.h (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACArraySequence.m (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACBehaviorSubject.h (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACBehaviorSubject.m (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACBlockTrampoline.h (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACBlockTrampoline.m (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACChannel.h (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACChannel.m (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACCommand.h (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACCommand.m (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACCompoundDisposable.h (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACCompoundDisposable.m (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACCompoundDisposableProvider.d (100%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACDelegateProxy.h (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACDelegateProxy.m (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACDisposable.h (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACDisposable.m (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACDynamicSequence.h (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACDynamicSequence.m (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACDynamicSignal.h (95%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACDynamicSignal.m (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACEagerSequence.h (93%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACEagerSequence.m (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACEmptySequence.h (93%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACEmptySequence.m (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACEmptySignal.h (94%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACEmptySignal.m (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACErrorSignal.h (94%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACErrorSignal.m (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACEvent.h (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACEvent.m (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACGroupedSignal.h (95%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACGroupedSignal.m (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACImmediateScheduler.h (94%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACImmediateScheduler.m (90%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACIndexSetSequence.h (95%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACIndexSetSequence.m (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACKVOChannel.h (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACKVOChannel.m (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACKVOProxy.h (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACKVOProxy.m (92%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACKVOTrampoline.h (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACKVOTrampoline.m (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACMulticastConnection+Private.h (94%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACMulticastConnection.h (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACMulticastConnection.m (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACPassthroughSubscriber.h (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACPassthroughSubscriber.m (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACQueueScheduler+Subclass.h (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACQueueScheduler.h (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACQueueScheduler.m (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACReplaySubject.h (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACReplaySubject.m (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACReturnSignal.h (95%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACReturnSignal.m (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACScheduler+Private.h (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACScheduler+Subclass.h (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACScheduler.h (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACScheduler.m (94%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACScopedDisposable.h (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACScopedDisposable.m (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACSequence.h (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACSequence.m (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACSerialDisposable.h (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACSerialDisposable.m (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACSignal+Operations.h (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACSignal+Operations.m (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACSignal.h (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACSignal.m (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACSignalProvider.d (100%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACSignalSequence.h (95%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACSignalSequence.m (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACStream+Private.h (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACStream.h (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACStream.m (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACStringSequence.h (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACStringSequence.m (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACSubject.h (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACSubject.m (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACSubscriber+Private.h (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACSubscriber.h (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACSubscriber.m (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACSubscriptingAssignmentTrampoline.h (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACSubscriptingAssignmentTrampoline.m (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACSubscriptionScheduler.h (95%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACSubscriptionScheduler.m (92%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACTargetQueueScheduler.h (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACTargetQueueScheduler.m (80%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACTestScheduler.h (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACTestScheduler.m (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACTuple.h (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACTuple.m (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACTupleSequence.h (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACTupleSequence.m (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACUnarySequence.h (93%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACUnarySequence.m (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACUnit.h (95%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACUnit.m (95%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACValueTransformer.h (94%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/RACValueTransformer.m (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/ReactiveCocoa-Bridging-Header.h (100%) create mode 100644 ReactiveObjC/ReactiveObjC.h rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UIActionSheet+RACSignalSupport.h (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UIActionSheet+RACSignalSupport.m (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UIAlertView+RACSignalSupport.h (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UIAlertView+RACSignalSupport.m (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UIBarButtonItem+RACCommandSupport.h (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UIBarButtonItem+RACCommandSupport.m (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UIButton+RACCommandSupport.h (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UIButton+RACCommandSupport.m (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UICollectionReusableView+RACSignalSupport.h (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UICollectionReusableView+RACSignalSupport.m (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UIControl+RACSignalSupport.h (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UIControl+RACSignalSupport.m (95%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UIControl+RACSignalSupportPrivate.h (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UIControl+RACSignalSupportPrivate.m (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UIDatePicker+RACSignalSupport.h (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UIDatePicker+RACSignalSupport.m (88%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UIGestureRecognizer+RACSignalSupport.h (95%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UIGestureRecognizer+RACSignalSupport.m (94%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UIImagePickerController+RACSignalSupport.h (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UIImagePickerController+RACSignalSupport.m (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UIRefreshControl+RACCommandSupport.h (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UIRefreshControl+RACCommandSupport.m (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UISegmentedControl+RACSignalSupport.h (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UISegmentedControl+RACSignalSupport.m (89%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UISlider+RACSignalSupport.h (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UISlider+RACSignalSupport.m (88%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UIStepper+RACSignalSupport.h (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UIStepper+RACSignalSupport.m (88%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UISwitch+RACSignalSupport.h (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UISwitch+RACSignalSupport.m (87%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UITableViewCell+RACSignalSupport.h (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UITableViewCell+RACSignalSupport.m (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UITableViewHeaderFooterView+RACSignalSupport.h (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UITableViewHeaderFooterView+RACSignalSupport.m (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UITextField+RACSignalSupport.h (97%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UITextField+RACSignalSupport.m (90%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UITextView+RACSignalSupport.h (98%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/UITextView+RACSignalSupport.m (96%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/extobjc/EXTKeyPathCoding.h (100%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/extobjc/EXTRuntimeExtensions.h (100%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/extobjc/EXTRuntimeExtensions.m (99%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/extobjc/EXTScope.h (100%) rename {ReactiveCocoa/Objective-C => ReactiveObjC}/extobjc/metamacros.h (100%) create mode 100644 ReactiveObjCBridge.xcodeproj/project.pbxproj create mode 100644 ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-iOS.xcscheme create mode 100644 ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-macOS.xcscheme create mode 100644 ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-tvOS.xcscheme create mode 100644 ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-watchOS.xcscheme create mode 100644 ReactiveObjCBridge.xcworkspace/contents.xcworkspacedata create mode 100644 ReactiveObjCBridge/Info.plist rename {ReactiveCocoa/Swift => ReactiveObjCBridge}/ObjectiveCBridging.swift (98%) create mode 100644 ReactiveObjCBridge/ReactiveObjCBridge.h create mode 100644 ReactiveObjCBridgeTests/Info.plist rename {ReactiveCocoaTests/Swift => ReactiveObjCBridgeTests}/ObjectiveCBridgingSpec.swift (99%) create mode 100644 ReactiveObjCBridgeTests/TestError.swift create mode 100644 ReactiveObjCTests/Info.plist rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/NSControllerRACSupportSpec.m (98%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/NSEnumeratorRACSequenceAdditionsSpec.m (97%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/NSNotificationCenterRACSupportSpec.m (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/NSObjectRACAppKitBindingsSpec.m (94%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/NSObjectRACDeallocatingSpec.m (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/NSObjectRACLiftingSpec.m (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/NSObjectRACPropertySubscribingExamples.h (97%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/NSObjectRACPropertySubscribingExamples.m (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/NSObjectRACPropertySubscribingSpec.m (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/NSObjectRACSelectorSignalSpec.m (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/NSStringRACKeyPathUtilitiesSpec.m (98%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/NSURLConnectionRACSupportSpec.m (98%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/NSUserDefaultsRACSupportSpec.m (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACBlockTrampolineSpec.m (98%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACChannelExamples.h (98%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACChannelExamples.m (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACChannelSpec.m (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACCommandSpec.m (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACCompoundDisposableSpec.m (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACControlCommandExamples.h (97%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACControlCommandExamples.m (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACDelegateProxySpec.m (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACDisposableSpec.m (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACEventSpec.m (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACKVOChannelSpec.m (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACKVOProxySpec.m (97%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACKVOWrapperSpec.m (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACMulticastConnectionSpec.m (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACPropertySignalExamples.h (97%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACPropertySignalExamples.m (98%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACSchedulerSpec.m (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACSequenceAdditionsSpec.m (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACSequenceExamples.h (95%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACSequenceExamples.m (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACSequenceSpec.m (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACSerialDisposableSpec.m (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACSignalSpec.m (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACStreamExamples.h (97%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACStreamExamples.m (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACSubclassObject.h (97%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACSubclassObject.m (98%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACSubjectSpec.m (98%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACSubscriberExamples.h (97%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACSubscriberExamples.m (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACSubscriberSpec.m (97%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACSubscriptingAssignmentTrampolineSpec.m (98%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACTargetQueueSchedulerSpec.m (98%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACTestExampleScheduler.h (80%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACTestExampleScheduler.m (97%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACTestObject.h (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACTestObject.m (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACTestSchedulerSpec.m (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACTestUIButton.h (94%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACTestUIButton.m (97%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/RACTupleSpec.m (99%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/UIActionSheetRACSupportSpec.m (98%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/UIAlertViewRACSupportSpec.m (98%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/UIBarButtonItemRACSupportSpec.m (98%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/UIButtonRACSupportSpec.m (98%) rename {ReactiveCocoaTests/Objective-C => ReactiveObjCTests}/UIImagePickerControllerRACSupportSpec.m (98%) create mode 100644 ReactiveObjCTests/test-data.json diff --git a/.travis.yml b/.travis.yml index 5056f2bf63..ffc6d524d6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,6 +32,56 @@ matrix: - XCODE_SDK=watchsimulator - XCODE_ACTION=build - XCODE_DESTINATION="platform=watchOS Simulator,name=Apple Watch - 38mm" + - xcode_scheme: ReactiveObjC-macOS + xcode_workspace: ReactiveObjC.xcworkspace + env: + - XCODE_SDK=macosx + - XCODE_ACTION="build test" + - XCODE_DESTINATION="arch=x86_64" + - XCODE_PLAYGROUND_TARGET="x86_64-apple-macosx10.10" + - xcode_scheme: ReactiveObjC-iOS + xcode_workspace: ReactiveObjC.xcworkspace + env: + - XCODE_SDK=iphonesimulator + - XCODE_ACTION="build-for-testing test-without-building" + - XCODE_DESTINATION="platform=iOS Simulator,name=iPhone 6s" + - xcode_scheme: ReactiveObjC-tvOS + xcode_workspace: ReactiveObjC.xcworkspace + env: + - XCODE_SDK=appletvsimulator + - XCODE_ACTION="build-for-testing test-without-building" + - XCODE_DESTINATION="platform=tvOS Simulator,name=Apple TV 1080p" + - xcode_scheme: ReactiveObjC-watchOS + xcode_workspace: ReactiveObjC.xcworkspace + env: + - XCODE_SDK=watchsimulator + - XCODE_ACTION=build + - XCODE_DESTINATION="platform=watchOS Simulator,name=Apple Watch - 38mm" + - xcode_scheme: ReactiveObjCBridge-macOS + xcode_workspace: ReactiveObjCBridge.xcworkspace + env: + - XCODE_SDK=macosx + - XCODE_ACTION="build test" + - XCODE_DESTINATION="arch=x86_64" + - XCODE_PLAYGROUND_TARGET="x86_64-apple-macosx10.10" + - xcode_scheme: ReactiveObjCBridge-iOS + xcode_workspace: ReactiveObjCBridge.xcworkspace + env: + - XCODE_SDK=iphonesimulator + - XCODE_ACTION="build-for-testing test-without-building" + - XCODE_DESTINATION="platform=iOS Simulator,name=iPhone 6s" + - xcode_scheme: ReactiveObjCBridge-tvOS + xcode_workspace: ReactiveObjCBridge.xcworkspace + env: + - XCODE_SDK=appletvsimulator + - XCODE_ACTION="build-for-testing test-without-building" + - XCODE_DESTINATION="platform=tvOS Simulator,name=Apple TV 1080p" + - xcode_scheme: ReactiveObjCBridge-watchOS + xcode_workspace: ReactiveObjCBridge.xcworkspace + env: + - XCODE_SDK=watchsimulator + - XCODE_ACTION=build + - XCODE_DESTINATION="platform=watchOS Simulator,name=Apple Watch - 38mm" - script: - brew update - brew outdated carthage || brew upgrade carthage diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 2521e58730..265bc0ba20 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -7,207 +7,20 @@ objects = { /* Begin PBXBuildFile section */ - 314304171ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 314304151ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 314304181ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 314304161ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.m */; }; 4A0E10FF1D2A92720065D310 /* NSObject+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */; }; 4A0E11001D2A92720065D310 /* NSObject+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */; }; 4A0E11011D2A92720065D310 /* NSObject+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */; }; 4A0E11021D2A92720065D310 /* NSObject+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */; }; - 57A4D1B21BA13D7A00F7D4B1 /* RACCompoundDisposableProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D037646A19EDA41200A782A9 /* RACCompoundDisposableProvider.d */; }; - 57A4D1B31BA13D7A00F7D4B1 /* RACSignalProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D03764A319EDA41200A782A9 /* RACSignalProvider.d */; }; - 57A4D1B71BA13D7A00F7D4B1 /* ObjectiveCBridging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312C419EF2A5800984962 /* ObjectiveCBridging.swift */; }; - 57A4D1C11BA13D7A00F7D4B1 /* EXTRuntimeExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037666819EDA57100A782A9 /* EXTRuntimeExtensions.m */; }; - 57A4D1C21BA13D7A00F7D4B1 /* NSArray+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037642B19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m */; }; - 57A4D1C31BA13D7A00F7D4B1 /* NSData+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643119EDA41200A782A9 /* NSData+RACSupport.m */; }; - 57A4D1C41BA13D7A00F7D4B1 /* NSDictionary+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643319EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m */; }; - 57A4D1C51BA13D7A00F7D4B1 /* NSEnumerator+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643519EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m */; }; - 57A4D1C61BA13D7A00F7D4B1 /* NSFileHandle+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643719EDA41200A782A9 /* NSFileHandle+RACSupport.m */; }; - 57A4D1C71BA13D7A00F7D4B1 /* NSIndexSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643919EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m */; }; - 57A4D1C81BA13D7A00F7D4B1 /* NSInvocation+RACTypeParsing.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643B19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m */; }; - 57A4D1C91BA13D7A00F7D4B1 /* NSNotificationCenter+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643D19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m */; }; - 57A4D1CA1BA13D7A00F7D4B1 /* NSObject+RACDeallocating.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644119EDA41200A782A9 /* NSObject+RACDeallocating.m */; }; - 57A4D1CB1BA13D7A00F7D4B1 /* NSObject+RACDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644319EDA41200A782A9 /* NSObject+RACDescription.m */; }; - 57A4D1CC1BA13D7A00F7D4B1 /* NSObject+RACKVOWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644519EDA41200A782A9 /* NSObject+RACKVOWrapper.m */; }; - 57A4D1CD1BA13D7A00F7D4B1 /* NSObject+RACLifting.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644719EDA41200A782A9 /* NSObject+RACLifting.m */; }; - 57A4D1CE1BA13D7A00F7D4B1 /* NSObject+RACPropertySubscribing.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644919EDA41200A782A9 /* NSObject+RACPropertySubscribing.m */; }; - 57A4D1CF1BA13D7A00F7D4B1 /* NSObject+RACSelectorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644B19EDA41200A782A9 /* NSObject+RACSelectorSignal.m */; }; - 57A4D1D01BA13D7A00F7D4B1 /* NSOrderedSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644D19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m */; }; - 57A4D1D11BA13D7A00F7D4B1 /* NSSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644F19EDA41200A782A9 /* NSSet+RACSequenceAdditions.m */; }; - 57A4D1D21BA13D7A00F7D4B1 /* NSString+RACKeyPathUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645119EDA41200A782A9 /* NSString+RACKeyPathUtilities.m */; }; - 57A4D1D31BA13D7A00F7D4B1 /* NSString+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645319EDA41200A782A9 /* NSString+RACSequenceAdditions.m */; }; - 57A4D1D41BA13D7A00F7D4B1 /* NSString+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645519EDA41200A782A9 /* NSString+RACSupport.m */; }; - 57A4D1D61BA13D7A00F7D4B1 /* NSUserDefaults+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645B19EDA41200A782A9 /* NSUserDefaults+RACSupport.m */; }; - 57A4D1D71BA13D7A00F7D4B1 /* RACArraySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645D19EDA41200A782A9 /* RACArraySequence.m */; }; - 57A4D1D81BA13D7A00F7D4B1 /* RACBehaviorSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646119EDA41200A782A9 /* RACBehaviorSubject.m */; }; - 57A4D1D91BA13D7A00F7D4B1 /* RACBlockTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646319EDA41200A782A9 /* RACBlockTrampoline.m */; }; - 57A4D1DA1BA13D7A00F7D4B1 /* RACChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646519EDA41200A782A9 /* RACChannel.m */; }; - 57A4D1DB1BA13D7A00F7D4B1 /* RACCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646719EDA41200A782A9 /* RACCommand.m */; }; - 57A4D1DC1BA13D7A00F7D4B1 /* RACCompoundDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646919EDA41200A782A9 /* RACCompoundDisposable.m */; }; - 57A4D1DD1BA13D7A00F7D4B1 /* RACDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646C19EDA41200A782A9 /* RACDelegateProxy.m */; }; - 57A4D1DE1BA13D7A00F7D4B1 /* RACDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646E19EDA41200A782A9 /* RACDisposable.m */; }; - 57A4D1DF1BA13D7A00F7D4B1 /* RACDynamicSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647019EDA41200A782A9 /* RACDynamicSequence.m */; }; - 57A4D1E01BA13D7A00F7D4B1 /* RACDynamicSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647219EDA41200A782A9 /* RACDynamicSignal.m */; }; - 57A4D1E11BA13D7A00F7D4B1 /* RACEagerSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647419EDA41200A782A9 /* RACEagerSequence.m */; }; - 57A4D1E21BA13D7A00F7D4B1 /* RACEmptySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647619EDA41200A782A9 /* RACEmptySequence.m */; }; - 57A4D1E31BA13D7A00F7D4B1 /* RACEmptySignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647819EDA41200A782A9 /* RACEmptySignal.m */; }; - 57A4D1E41BA13D7A00F7D4B1 /* RACErrorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647A19EDA41200A782A9 /* RACErrorSignal.m */; }; - 57A4D1E51BA13D7A00F7D4B1 /* RACEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647C19EDA41200A782A9 /* RACEvent.m */; }; - 57A4D1E61BA13D7A00F7D4B1 /* RACGroupedSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647E19EDA41200A782A9 /* RACGroupedSignal.m */; }; - 57A4D1E71BA13D7A00F7D4B1 /* RACImmediateScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648019EDA41200A782A9 /* RACImmediateScheduler.m */; }; - 57A4D1E81BA13D7A00F7D4B1 /* RACIndexSetSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648219EDA41200A782A9 /* RACIndexSetSequence.m */; }; - 57A4D1E91BA13D7A00F7D4B1 /* RACKVOChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648419EDA41200A782A9 /* RACKVOChannel.m */; }; - 57A4D1EA1BA13D7A00F7D4B1 /* RACKVOProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A70657E1A3F88B8001E8354 /* RACKVOProxy.m */; }; - 57A4D1EB1BA13D7A00F7D4B1 /* RACKVOTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648619EDA41200A782A9 /* RACKVOTrampoline.m */; }; - 57A4D1EC1BA13D7A00F7D4B1 /* RACMulticastConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648819EDA41200A782A9 /* RACMulticastConnection.m */; }; - 57A4D1EE1BA13D7A00F7D4B1 /* RACPassthroughSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648D19EDA41200A782A9 /* RACPassthroughSubscriber.m */; }; - 57A4D1EF1BA13D7A00F7D4B1 /* RACQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648F19EDA41200A782A9 /* RACQueueScheduler.m */; }; - 57A4D1F01BA13D7A00F7D4B1 /* RACReplaySubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649219EDA41200A782A9 /* RACReplaySubject.m */; }; - 57A4D1F11BA13D7A00F7D4B1 /* RACReturnSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649419EDA41200A782A9 /* RACReturnSignal.m */; }; - 57A4D1F21BA13D7A00F7D4B1 /* RACScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649619EDA41200A782A9 /* RACScheduler.m */; }; - 57A4D1F31BA13D7A00F7D4B1 /* RACScopedDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649A19EDA41200A782A9 /* RACScopedDisposable.m */; }; - 57A4D1F41BA13D7A00F7D4B1 /* RACSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649C19EDA41200A782A9 /* RACSequence.m */; }; - 57A4D1F51BA13D7A00F7D4B1 /* RACSerialDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649E19EDA41200A782A9 /* RACSerialDisposable.m */; }; - 57A4D1F61BA13D7A00F7D4B1 /* RACSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A019EDA41200A782A9 /* RACSignal.m */; }; - 57A4D1F71BA13D7A00F7D4B1 /* RACSignal+Operations.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A219EDA41200A782A9 /* RACSignal+Operations.m */; }; - 57A4D1F81BA13D7A00F7D4B1 /* RACSignalSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A519EDA41200A782A9 /* RACSignalSequence.m */; }; - 57A4D1F91BA13D7A00F7D4B1 /* RACStream.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A719EDA41200A782A9 /* RACStream.m */; }; - 57A4D1FA1BA13D7A00F7D4B1 /* RACStringSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AA19EDA41200A782A9 /* RACStringSequence.m */; }; - 57A4D1FB1BA13D7A00F7D4B1 /* RACSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AC19EDA41200A782A9 /* RACSubject.m */; }; - 57A4D1FC1BA13D7A00F7D4B1 /* RACSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AE19EDA41200A782A9 /* RACSubscriber.m */; }; - 57A4D1FD1BA13D7A00F7D4B1 /* RACSubscriptingAssignmentTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B119EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m */; }; - 57A4D1FE1BA13D7A00F7D4B1 /* RACSubscriptionScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B319EDA41200A782A9 /* RACSubscriptionScheduler.m */; }; - 57A4D1FF1BA13D7A00F7D4B1 /* RACTargetQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B519EDA41200A782A9 /* RACTargetQueueScheduler.m */; }; - 57A4D2001BA13D7A00F7D4B1 /* RACTestScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B719EDA41200A782A9 /* RACTestScheduler.m */; }; - 57A4D2011BA13D7A00F7D4B1 /* RACTuple.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B919EDA41200A782A9 /* RACTuple.m */; }; - 57A4D2021BA13D7A00F7D4B1 /* RACTupleSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BB19EDA41200A782A9 /* RACTupleSequence.m */; }; - 57A4D2031BA13D7A00F7D4B1 /* RACUnarySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BD19EDA41200A782A9 /* RACUnarySequence.m */; }; - 57A4D2041BA13D7A00F7D4B1 /* RACUnit.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BF19EDA41200A782A9 /* RACUnit.m */; }; - 57A4D2051BA13D7A00F7D4B1 /* RACValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C119EDA41200A782A9 /* RACValueTransformer.m */; }; 57A4D2081BA13D7A00F7D4B1 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; 57A4D20A1BA13D7A00F7D4B1 /* ReactiveCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D20B1BA13D7A00F7D4B1 /* EXTKeyPathCoding.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666619EDA57100A782A9 /* EXTKeyPathCoding.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D20C1BA13D7A00F7D4B1 /* EXTScope.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666919EDA57100A782A9 /* EXTScope.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D20D1BA13D7A00F7D4B1 /* metamacros.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666A19EDA57100A782A9 /* metamacros.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D20E1BA13D7A00F7D4B1 /* NSArray+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037642A19EDA41200A782A9 /* NSArray+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D20F1BA13D7A00F7D4B1 /* NSData+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643019EDA41200A782A9 /* NSData+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2101BA13D7A00F7D4B1 /* NSDictionary+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643219EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2111BA13D7A00F7D4B1 /* NSEnumerator+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643419EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2121BA13D7A00F7D4B1 /* NSFileHandle+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643619EDA41200A782A9 /* NSFileHandle+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2131BA13D7A00F7D4B1 /* NSIndexSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643819EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2141BA13D7A00F7D4B1 /* NSNotificationCenter+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643C19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2151BA13D7A00F7D4B1 /* NSObject+RACDeallocating.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644019EDA41200A782A9 /* NSObject+RACDeallocating.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2161BA13D7A00F7D4B1 /* NSObject+RACLifting.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644619EDA41200A782A9 /* NSObject+RACLifting.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2171BA13D7A00F7D4B1 /* NSObject+RACPropertySubscribing.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644819EDA41200A782A9 /* NSObject+RACPropertySubscribing.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2181BA13D7A00F7D4B1 /* NSObject+RACSelectorSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644A19EDA41200A782A9 /* NSObject+RACSelectorSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2191BA13D7A00F7D4B1 /* NSOrderedSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644C19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D21A1BA13D7A00F7D4B1 /* NSSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644E19EDA41200A782A9 /* NSSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D21B1BA13D7A00F7D4B1 /* NSString+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645219EDA41200A782A9 /* NSString+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D21C1BA13D7A00F7D4B1 /* NSString+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645419EDA41200A782A9 /* NSString+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D21E1BA13D7A00F7D4B1 /* NSUserDefaults+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645A19EDA41200A782A9 /* NSUserDefaults+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D21F1BA13D7A00F7D4B1 /* RACBehaviorSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646019EDA41200A782A9 /* RACBehaviorSubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2201BA13D7A00F7D4B1 /* RACChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646419EDA41200A782A9 /* RACChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2211BA13D7A00F7D4B1 /* RACCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646619EDA41200A782A9 /* RACCommand.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2221BA13D7A00F7D4B1 /* RACCompoundDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646819EDA41200A782A9 /* RACCompoundDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2231BA13D7A00F7D4B1 /* RACDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646D19EDA41200A782A9 /* RACDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2241BA13D7A00F7D4B1 /* RACEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = D037647B19EDA41200A782A9 /* RACEvent.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2251BA13D7A00F7D4B1 /* RACGroupedSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037647D19EDA41200A782A9 /* RACGroupedSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2261BA13D7A00F7D4B1 /* RACKVOChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648319EDA41200A782A9 /* RACKVOChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2271BA13D7A00F7D4B1 /* RACMulticastConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648719EDA41200A782A9 /* RACMulticastConnection.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2281BA13D7A00F7D4B1 /* RACQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648E19EDA41200A782A9 /* RACQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2291BA13D7A00F7D4B1 /* RACQueueScheduler+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649019EDA41200A782A9 /* RACQueueScheduler+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D22A1BA13D7A00F7D4B1 /* RACReplaySubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649119EDA41200A782A9 /* RACReplaySubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D22B1BA13D7A00F7D4B1 /* RACScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649519EDA41200A782A9 /* RACScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D22C1BA13D7A00F7D4B1 /* RACScheduler+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649819EDA41200A782A9 /* RACScheduler+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D22D1BA13D7A00F7D4B1 /* RACScopedDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649919EDA41200A782A9 /* RACScopedDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D22E1BA13D7A00F7D4B1 /* RACSequence.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649B19EDA41200A782A9 /* RACSequence.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D22F1BA13D7A00F7D4B1 /* RACSerialDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649D19EDA41200A782A9 /* RACSerialDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2301BA13D7A00F7D4B1 /* RACSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649F19EDA41200A782A9 /* RACSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2311BA13D7A00F7D4B1 /* RACSignal+Operations.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764A119EDA41200A782A9 /* RACSignal+Operations.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2321BA13D7A00F7D4B1 /* RACStream.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764A619EDA41200A782A9 /* RACStream.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2331BA13D7A00F7D4B1 /* RACSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764AB19EDA41200A782A9 /* RACSubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2341BA13D7A00F7D4B1 /* RACSubscriber.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764AD19EDA41200A782A9 /* RACSubscriber.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2351BA13D7A00F7D4B1 /* RACSubscriptingAssignmentTrampoline.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B019EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2361BA13D7A00F7D4B1 /* RACTargetQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B419EDA41200A782A9 /* RACTargetQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2371BA13D7A00F7D4B1 /* RACTestScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B619EDA41200A782A9 /* RACTestScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2381BA13D7A00F7D4B1 /* RACTuple.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B819EDA41200A782A9 /* RACTuple.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2391BA13D7A00F7D4B1 /* RACUnit.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764BE19EDA41200A782A9 /* RACUnit.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57D4768D1C42063C00EFE697 /* UIControl+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764CD19EDA41200A782A9 /* UIControl+RACSignalSupport.m */; }; - 57D476901C4206D400EFE697 /* UIControl+RACSignalSupportPrivate.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764CF19EDA41200A782A9 /* UIControl+RACSignalSupportPrivate.m */; }; - 57D476911C4206DA00EFE697 /* UIGestureRecognizer+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764D319EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.m */; }; - 57D476921C4206DF00EFE697 /* UISegmentedControl+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764D919EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.m */; }; - 57D476951C4206EC00EFE697 /* UITableViewCell+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764E119EDA41200A782A9 /* UITableViewCell+RACSignalSupport.m */; }; - 57D476961C4206EC00EFE697 /* UITableViewHeaderFooterView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764E319EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.m */; }; - 57D476971C4206EC00EFE697 /* UITextField+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764E519EDA41200A782A9 /* UITextField+RACSignalSupport.m */; }; - 57D476981C4206EC00EFE697 /* UITextView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764E719EDA41200A782A9 /* UITextView+RACSignalSupport.m */; }; - 57D4769A1C4206F200EFE697 /* UIButton+RACCommandSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C919EDA41200A782A9 /* UIButton+RACCommandSupport.m */; }; - 57D4769B1C4206F200EFE697 /* UICollectionReusableView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764CB19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.m */; }; - 57DC89A01C5066D400E367B7 /* UIGestureRecognizer+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764D219EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57DC89A11C50672B00E367B7 /* UIControl+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764CC19EDA41200A782A9 /* UIControl+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57DC89A21C50673C00E367B7 /* UISegmentedControl+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764D819EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57DC89A31C50674300E367B7 /* UITableViewCell+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764E019EDA41200A782A9 /* UITableViewCell+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57DC89A41C50674D00E367B7 /* UITableViewHeaderFooterView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764E219EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57DC89A51C50675700E367B7 /* UITextField+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764E419EDA41200A782A9 /* UITextField+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57DC89A61C50675F00E367B7 /* UITextView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764E619EDA41200A782A9 /* UITextView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57DC89A71C50679700E367B7 /* UIButton+RACCommandSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764C819EDA41200A782A9 /* UIButton+RACCommandSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57DC89A81C50679E00E367B7 /* UICollectionReusableView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764CA19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 7A7065811A3F88B8001E8354 /* RACKVOProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A70657E1A3F88B8001E8354 /* RACKVOProxy.m */; }; - 7A7065821A3F88B8001E8354 /* RACKVOProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A70657E1A3F88B8001E8354 /* RACKVOProxy.m */; }; - 7A7065841A3F8967001E8354 /* RACKVOProxySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A7065831A3F8967001E8354 /* RACKVOProxySpec.m */; }; - 7A7065851A3F8967001E8354 /* RACKVOProxySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A7065831A3F8967001E8354 /* RACKVOProxySpec.m */; }; 7DFBED081CDB8C9500EE435B /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */; }; 7DFBED141CDB8CE600EE435B /* test-data.json in Resources */ = {isa = PBXBuildFile; fileRef = D03766B119EDA60000A782A9 /* test-data.json */; }; 7DFBED1E1CDB8D7000EE435B /* ReactiveCocoa.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 7DFBED1F1CDB8D7800EE435B /* Quick.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 7DFBED201CDB8D7D00EE435B /* Nimble.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 7DFBED211CDB8D8300EE435B /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 7DFBED271CDB8DE300EE435B /* ObjectiveCBridgingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A226101A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift */; }; 7DFBED281CDB8DE300EE435B /* DynamicPropertySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */; }; - 7DFBED321CDB8DE300EE435B /* NSEnumeratorRACSequenceAdditionsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667819EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m */; }; - 7DFBED331CDB8DE300EE435B /* NSNotificationCenterRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667919EDA60000A782A9 /* NSNotificationCenterRACSupportSpec.m */; }; - 7DFBED351CDB8DE300EE435B /* NSObjectRACDeallocatingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667B19EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m */; }; - 7DFBED361CDB8DE300EE435B /* NSObjectRACLiftingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667C19EDA60000A782A9 /* NSObjectRACLiftingSpec.m */; }; - 7DFBED381CDB8DE300EE435B /* NSObjectRACPropertySubscribingExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667E19EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.m */; }; - 7DFBED391CDB8DE300EE435B /* NSObjectRACPropertySubscribingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667F19EDA60000A782A9 /* NSObjectRACPropertySubscribingSpec.m */; }; - 7DFBED3A1CDB8DE300EE435B /* NSObjectRACSelectorSignalSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668019EDA60000A782A9 /* NSObjectRACSelectorSignalSpec.m */; }; - 7DFBED3B1CDB8DE300EE435B /* NSStringRACKeyPathUtilitiesSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668119EDA60000A782A9 /* NSStringRACKeyPathUtilitiesSpec.m */; }; - 7DFBED3D1CDB8DE300EE435B /* NSUserDefaultsRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668419EDA60000A782A9 /* NSUserDefaultsRACSupportSpec.m */; }; - 7DFBED3E1CDB8DE300EE435B /* RACBlockTrampolineSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668619EDA60000A782A9 /* RACBlockTrampolineSpec.m */; }; - 7DFBED401CDB8DE300EE435B /* RACChannelExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668819EDA60000A782A9 /* RACChannelExamples.m */; }; - 7DFBED411CDB8DE300EE435B /* RACChannelSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668919EDA60000A782A9 /* RACChannelSpec.m */; }; - 7DFBED421CDB8DE300EE435B /* RACCommandSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668A19EDA60000A782A9 /* RACCommandSpec.m */; }; - 7DFBED431CDB8DE300EE435B /* RACCompoundDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668B19EDA60000A782A9 /* RACCompoundDisposableSpec.m */; }; - 7DFBED451CDB8DE300EE435B /* RACControlCommandExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668D19EDA60000A782A9 /* RACControlCommandExamples.m */; }; - 7DFBED461CDB8DE300EE435B /* RACDelegateProxySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668E19EDA60000A782A9 /* RACDelegateProxySpec.m */; }; - 7DFBED471CDB8DE300EE435B /* RACDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668F19EDA60000A782A9 /* RACDisposableSpec.m */; }; - 7DFBED481CDB8DE300EE435B /* RACEventSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669019EDA60000A782A9 /* RACEventSpec.m */; }; - 7DFBED491CDB8DE300EE435B /* RACKVOChannelSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669119EDA60000A782A9 /* RACKVOChannelSpec.m */; }; - 7DFBED4A1CDB8DE300EE435B /* RACKVOProxySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A7065831A3F8967001E8354 /* RACKVOProxySpec.m */; }; - 7DFBED4B1CDB8DE300EE435B /* RACKVOWrapperSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669219EDA60000A782A9 /* RACKVOWrapperSpec.m */; }; - 7DFBED4C1CDB8DE300EE435B /* RACMulticastConnectionSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669319EDA60000A782A9 /* RACMulticastConnectionSpec.m */; }; - 7DFBED4E1CDB8DE300EE435B /* RACPropertySignalExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669519EDA60000A782A9 /* RACPropertySignalExamples.m */; }; - 7DFBED4F1CDB8DE300EE435B /* RACSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669619EDA60000A782A9 /* RACSchedulerSpec.m */; }; - 7DFBED501CDB8DE300EE435B /* RACSequenceAdditionsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669719EDA60000A782A9 /* RACSequenceAdditionsSpec.m */; }; - 7DFBED521CDB8DE300EE435B /* RACSequenceExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669919EDA60000A782A9 /* RACSequenceExamples.m */; }; - 7DFBED531CDB8DE300EE435B /* RACSequenceSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669A19EDA60000A782A9 /* RACSequenceSpec.m */; }; - 7DFBED541CDB8DE300EE435B /* RACSerialDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669B19EDA60000A782A9 /* RACSerialDisposableSpec.m */; }; - 7DFBED551CDB8DE300EE435B /* RACSignalSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669C19EDA60000A782A9 /* RACSignalSpec.m */; }; - 7DFBED571CDB8DE300EE435B /* RACStreamExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A019EDA60000A782A9 /* RACStreamExamples.m */; }; - 7DFBED591CDB8DE300EE435B /* RACSubclassObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A219EDA60000A782A9 /* RACSubclassObject.m */; }; - 7DFBED5A1CDB8DE300EE435B /* RACSubjectSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A319EDA60000A782A9 /* RACSubjectSpec.m */; }; - 7DFBED5C1CDB8DE300EE435B /* RACSubscriberExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A519EDA60000A782A9 /* RACSubscriberExamples.m */; }; - 7DFBED5D1CDB8DE300EE435B /* RACSubscriberSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A619EDA60000A782A9 /* RACSubscriberSpec.m */; }; - 7DFBED5E1CDB8DE300EE435B /* RACSubscriptingAssignmentTrampolineSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A719EDA60000A782A9 /* RACSubscriptingAssignmentTrampolineSpec.m */; }; - 7DFBED5F1CDB8DE300EE435B /* RACTargetQueueSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A819EDA60000A782A9 /* RACTargetQueueSchedulerSpec.m */; }; - 7DFBED601CDB8DE300EE435B /* RACTupleSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B019EDA60000A782A9 /* RACTupleSpec.m */; }; - 7DFBED631CDB8DE300EE435B /* UIBarButtonItemRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B419EDA60000A782A9 /* UIBarButtonItemRACSupportSpec.m */; }; - 7DFBED641CDB8DE300EE435B /* UIButtonRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B519EDA60000A782A9 /* UIButtonRACSupportSpec.m */; }; - 7DFBED671CDB8DE300EE435B /* RACTestExampleScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131819EF2D9700984962 /* RACTestExampleScheduler.m */; }; - 7DFBED691CDB8DE300EE435B /* RACTestObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131A19EF2D9700984962 /* RACTestObject.m */; }; - 7DFBED6A1CDB8DE300EE435B /* RACTestSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131B19EF2D9700984962 /* RACTestSchedulerSpec.m */; }; - 7DFBED6C1CDB8DE300EE435B /* RACTestUIButton.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131D19EF2D9700984962 /* RACTestUIButton.m */; }; 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */; }; - 7DFBED6E1CDB918900EE435B /* UIBarButtonItem+RACCommandSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C719EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.m */; }; - 7DFBED6F1CDB926400EE435B /* UIBarButtonItem+RACCommandSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764C619EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9A1E72BA1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; 9A1E72BB1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; 9A1E72BC1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; @@ -215,128 +28,8 @@ 9AD0F06B1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; 9AD0F06C1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; 9AD0F06D1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; - A1046B7A1BFF5661004D8045 /* EXTRuntimeExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666719EDA57100A782A9 /* EXTRuntimeExtensions.h */; settings = {ATTRIBUTES = (Private, ); }; }; - A1046B7B1BFF5662004D8045 /* EXTRuntimeExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666719EDA57100A782A9 /* EXTRuntimeExtensions.h */; settings = {ATTRIBUTES = (Private, ); }; }; - A1046B7C1BFF5662004D8045 /* EXTRuntimeExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666719EDA57100A782A9 /* EXTRuntimeExtensions.h */; settings = {ATTRIBUTES = (Private, ); }; }; - A1046B7D1BFF5664004D8045 /* EXTRuntimeExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666719EDA57100A782A9 /* EXTRuntimeExtensions.h */; settings = {ATTRIBUTES = (Private, ); }; }; - A9B3155E1B3940750001CB9C /* EXTRuntimeExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037666819EDA57100A782A9 /* EXTRuntimeExtensions.m */; }; - A9B315601B3940750001CB9C /* NSArray+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037642B19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m */; }; - A9B315631B3940750001CB9C /* NSData+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643119EDA41200A782A9 /* NSData+RACSupport.m */; }; - A9B315641B3940750001CB9C /* NSDictionary+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643319EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m */; }; - A9B315651B3940750001CB9C /* NSEnumerator+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643519EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m */; }; - A9B315661B3940750001CB9C /* NSFileHandle+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643719EDA41200A782A9 /* NSFileHandle+RACSupport.m */; }; - A9B315671B3940750001CB9C /* NSIndexSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643919EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m */; }; - A9B315681B3940750001CB9C /* NSInvocation+RACTypeParsing.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643B19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m */; }; - A9B315691B3940750001CB9C /* NSNotificationCenter+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643D19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m */; }; - A9B3156B1B3940750001CB9C /* NSObject+RACDeallocating.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644119EDA41200A782A9 /* NSObject+RACDeallocating.m */; }; - A9B3156C1B3940750001CB9C /* NSObject+RACDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644319EDA41200A782A9 /* NSObject+RACDescription.m */; }; - A9B3156D1B3940750001CB9C /* NSObject+RACKVOWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644519EDA41200A782A9 /* NSObject+RACKVOWrapper.m */; }; - A9B3156E1B3940750001CB9C /* NSObject+RACLifting.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644719EDA41200A782A9 /* NSObject+RACLifting.m */; }; - A9B3156F1B3940750001CB9C /* NSObject+RACPropertySubscribing.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644919EDA41200A782A9 /* NSObject+RACPropertySubscribing.m */; }; - A9B315701B3940750001CB9C /* NSObject+RACSelectorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644B19EDA41200A782A9 /* NSObject+RACSelectorSignal.m */; }; - A9B315711B3940750001CB9C /* NSOrderedSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644D19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m */; }; - A9B315721B3940750001CB9C /* NSSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644F19EDA41200A782A9 /* NSSet+RACSequenceAdditions.m */; }; - A9B315731B3940750001CB9C /* NSString+RACKeyPathUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645119EDA41200A782A9 /* NSString+RACKeyPathUtilities.m */; }; - A9B315741B3940750001CB9C /* NSString+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645319EDA41200A782A9 /* NSString+RACSequenceAdditions.m */; }; - A9B315751B3940750001CB9C /* NSString+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645519EDA41200A782A9 /* NSString+RACSupport.m */; }; - A9B315781B3940750001CB9C /* NSUserDefaults+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645B19EDA41200A782A9 /* NSUserDefaults+RACSupport.m */; }; - A9B315791B3940750001CB9C /* RACArraySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645D19EDA41200A782A9 /* RACArraySequence.m */; }; - A9B3157A1B3940750001CB9C /* RACBehaviorSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646119EDA41200A782A9 /* RACBehaviorSubject.m */; }; - A9B3157B1B3940750001CB9C /* RACBlockTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646319EDA41200A782A9 /* RACBlockTrampoline.m */; }; - A9B3157C1B3940750001CB9C /* RACChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646519EDA41200A782A9 /* RACChannel.m */; }; - A9B3157D1B3940750001CB9C /* RACCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646719EDA41200A782A9 /* RACCommand.m */; }; - A9B3157E1B3940750001CB9C /* RACCompoundDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646919EDA41200A782A9 /* RACCompoundDisposable.m */; }; - A9B3157F1B3940750001CB9C /* RACDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646C19EDA41200A782A9 /* RACDelegateProxy.m */; }; - A9B315801B3940750001CB9C /* RACDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646E19EDA41200A782A9 /* RACDisposable.m */; }; - A9B315811B3940750001CB9C /* RACDynamicSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647019EDA41200A782A9 /* RACDynamicSequence.m */; }; - A9B315821B3940750001CB9C /* RACDynamicSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647219EDA41200A782A9 /* RACDynamicSignal.m */; }; - A9B315831B3940750001CB9C /* RACEagerSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647419EDA41200A782A9 /* RACEagerSequence.m */; }; - A9B315841B3940750001CB9C /* RACEmptySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647619EDA41200A782A9 /* RACEmptySequence.m */; }; - A9B315851B3940750001CB9C /* RACEmptySignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647819EDA41200A782A9 /* RACEmptySignal.m */; }; - A9B315861B3940750001CB9C /* RACErrorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647A19EDA41200A782A9 /* RACErrorSignal.m */; }; - A9B315871B3940750001CB9C /* RACEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647C19EDA41200A782A9 /* RACEvent.m */; }; - A9B315881B3940750001CB9C /* RACGroupedSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647E19EDA41200A782A9 /* RACGroupedSignal.m */; }; - A9B315891B3940750001CB9C /* RACImmediateScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648019EDA41200A782A9 /* RACImmediateScheduler.m */; }; - A9B3158A1B3940750001CB9C /* RACIndexSetSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648219EDA41200A782A9 /* RACIndexSetSequence.m */; }; - A9B3158B1B3940750001CB9C /* RACKVOChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648419EDA41200A782A9 /* RACKVOChannel.m */; }; - A9B3158C1B3940750001CB9C /* RACKVOProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A70657E1A3F88B8001E8354 /* RACKVOProxy.m */; }; - A9B3158D1B3940750001CB9C /* RACKVOTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648619EDA41200A782A9 /* RACKVOTrampoline.m */; }; - A9B3158E1B3940750001CB9C /* RACMulticastConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648819EDA41200A782A9 /* RACMulticastConnection.m */; }; - A9B315901B3940750001CB9C /* RACPassthroughSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648D19EDA41200A782A9 /* RACPassthroughSubscriber.m */; }; - A9B315911B3940750001CB9C /* RACQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648F19EDA41200A782A9 /* RACQueueScheduler.m */; }; - A9B315921B3940750001CB9C /* RACReplaySubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649219EDA41200A782A9 /* RACReplaySubject.m */; }; - A9B315931B3940750001CB9C /* RACReturnSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649419EDA41200A782A9 /* RACReturnSignal.m */; }; - A9B315941B3940750001CB9C /* RACScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649619EDA41200A782A9 /* RACScheduler.m */; }; - A9B315951B3940750001CB9C /* RACScopedDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649A19EDA41200A782A9 /* RACScopedDisposable.m */; }; - A9B315961B3940750001CB9C /* RACSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649C19EDA41200A782A9 /* RACSequence.m */; }; - A9B315971B3940750001CB9C /* RACSerialDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649E19EDA41200A782A9 /* RACSerialDisposable.m */; }; - A9B315981B3940750001CB9C /* RACSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A019EDA41200A782A9 /* RACSignal.m */; }; - A9B315991B3940750001CB9C /* RACSignal+Operations.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A219EDA41200A782A9 /* RACSignal+Operations.m */; }; - A9B3159A1B3940750001CB9C /* RACSignalSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A519EDA41200A782A9 /* RACSignalSequence.m */; }; - A9B3159B1B3940750001CB9C /* RACStream.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A719EDA41200A782A9 /* RACStream.m */; }; - A9B3159C1B3940750001CB9C /* RACStringSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AA19EDA41200A782A9 /* RACStringSequence.m */; }; - A9B3159D1B3940750001CB9C /* RACSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AC19EDA41200A782A9 /* RACSubject.m */; }; - A9B3159E1B3940750001CB9C /* RACSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AE19EDA41200A782A9 /* RACSubscriber.m */; }; - A9B3159F1B3940750001CB9C /* RACSubscriptingAssignmentTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B119EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m */; }; - A9B315A01B3940750001CB9C /* RACSubscriptionScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B319EDA41200A782A9 /* RACSubscriptionScheduler.m */; }; - A9B315A11B3940750001CB9C /* RACTargetQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B519EDA41200A782A9 /* RACTargetQueueScheduler.m */; }; - A9B315A21B3940750001CB9C /* RACTestScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B719EDA41200A782A9 /* RACTestScheduler.m */; }; - A9B315A31B3940750001CB9C /* RACTuple.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B919EDA41200A782A9 /* RACTuple.m */; }; - A9B315A41B3940750001CB9C /* RACTupleSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BB19EDA41200A782A9 /* RACTupleSequence.m */; }; - A9B315A51B3940750001CB9C /* RACUnarySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BD19EDA41200A782A9 /* RACUnarySequence.m */; }; - A9B315A61B3940750001CB9C /* RACUnit.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BF19EDA41200A782A9 /* RACUnit.m */; }; - A9B315A71B3940750001CB9C /* RACValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C119EDA41200A782A9 /* RACValueTransformer.m */; }; - A9B315BF1B3940810001CB9C /* ObjectiveCBridging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312C419EF2A5800984962 /* ObjectiveCBridging.swift */; }; A9B315C91B3940980001CB9C /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; A9B315CA1B3940AB0001CB9C /* ReactiveCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315CB1B3940AB0001CB9C /* EXTKeyPathCoding.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666619EDA57100A782A9 /* EXTKeyPathCoding.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315CD1B3940AB0001CB9C /* EXTScope.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666919EDA57100A782A9 /* EXTScope.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315CE1B3940AB0001CB9C /* metamacros.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666A19EDA57100A782A9 /* metamacros.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315D01B3940AB0001CB9C /* NSArray+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037642A19EDA41200A782A9 /* NSArray+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315D31B3940AB0001CB9C /* NSData+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643019EDA41200A782A9 /* NSData+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315D41B3940AB0001CB9C /* NSDictionary+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643219EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315D51B3940AB0001CB9C /* NSEnumerator+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643419EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315D61B3940AB0001CB9C /* NSFileHandle+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643619EDA41200A782A9 /* NSFileHandle+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315D71B3940AB0001CB9C /* NSIndexSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643819EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315D91B3940AB0001CB9C /* NSNotificationCenter+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643C19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315DB1B3940AB0001CB9C /* NSObject+RACDeallocating.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644019EDA41200A782A9 /* NSObject+RACDeallocating.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315DE1B3940AB0001CB9C /* NSObject+RACLifting.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644619EDA41200A782A9 /* NSObject+RACLifting.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315DF1B3940AB0001CB9C /* NSObject+RACPropertySubscribing.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644819EDA41200A782A9 /* NSObject+RACPropertySubscribing.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315E01B3940AB0001CB9C /* NSObject+RACSelectorSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644A19EDA41200A782A9 /* NSObject+RACSelectorSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315E11B3940AB0001CB9C /* NSOrderedSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644C19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315E21B3940AB0001CB9C /* NSSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644E19EDA41200A782A9 /* NSSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315E41B3940AB0001CB9C /* NSString+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645219EDA41200A782A9 /* NSString+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315E51B3940AB0001CB9C /* NSString+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645419EDA41200A782A9 /* NSString+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315E81B3940AB0001CB9C /* NSUserDefaults+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645A19EDA41200A782A9 /* NSUserDefaults+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315EA1B3940AB0001CB9C /* RACBehaviorSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646019EDA41200A782A9 /* RACBehaviorSubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315EC1B3940AB0001CB9C /* RACChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646419EDA41200A782A9 /* RACChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315ED1B3940AC0001CB9C /* RACCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646619EDA41200A782A9 /* RACCommand.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315EE1B3940AC0001CB9C /* RACCompoundDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646819EDA41200A782A9 /* RACCompoundDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315F01B3940AC0001CB9C /* RACDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646D19EDA41200A782A9 /* RACDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315F71B3940AC0001CB9C /* RACEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = D037647B19EDA41200A782A9 /* RACEvent.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315F81B3940AC0001CB9C /* RACGroupedSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037647D19EDA41200A782A9 /* RACGroupedSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315FB1B3940AC0001CB9C /* RACKVOChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648319EDA41200A782A9 /* RACKVOChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315FE1B3940AC0001CB9C /* RACMulticastConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648719EDA41200A782A9 /* RACMulticastConnection.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B316021B3940AD0001CB9C /* RACQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648E19EDA41200A782A9 /* RACQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B316031B3940AD0001CB9C /* RACQueueScheduler+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649019EDA41200A782A9 /* RACQueueScheduler+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B316041B3940AD0001CB9C /* RACReplaySubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649119EDA41200A782A9 /* RACReplaySubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B316061B3940AD0001CB9C /* RACScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649519EDA41200A782A9 /* RACScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B316081B3940AD0001CB9C /* RACScheduler+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649819EDA41200A782A9 /* RACScheduler+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B316091B3940AD0001CB9C /* RACScopedDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649919EDA41200A782A9 /* RACScopedDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B3160A1B3940AD0001CB9C /* RACSequence.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649B19EDA41200A782A9 /* RACSequence.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B3160B1B3940AD0001CB9C /* RACSerialDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649D19EDA41200A782A9 /* RACSerialDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B3160C1B3940AE0001CB9C /* RACSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649F19EDA41200A782A9 /* RACSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B3160D1B3940AE0001CB9C /* RACSignal+Operations.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764A119EDA41200A782A9 /* RACSignal+Operations.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B3160F1B3940AE0001CB9C /* RACStream.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764A619EDA41200A782A9 /* RACStream.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B316121B3940AE0001CB9C /* RACSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764AB19EDA41200A782A9 /* RACSubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B316131B3940AE0001CB9C /* RACSubscriber.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764AD19EDA41200A782A9 /* RACSubscriber.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B316151B3940AE0001CB9C /* RACSubscriptingAssignmentTrampoline.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B019EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B316171B3940AF0001CB9C /* RACTargetQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B419EDA41200A782A9 /* RACTargetQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B316181B3940AF0001CB9C /* RACTestScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B619EDA41200A782A9 /* RACTestScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B316191B3940AF0001CB9C /* RACTuple.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B819EDA41200A782A9 /* RACTuple.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B3161C1B3940AF0001CB9C /* RACUnit.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764BE19EDA41200A782A9 /* RACUnit.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B316341B394C7F0001CB9C /* RACCompoundDisposableProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D037646A19EDA41200A782A9 /* RACCompoundDisposableProvider.d */; }; - A9B316351B394C7F0001CB9C /* RACSignalProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D03764A319EDA41200A782A9 /* RACSignalProvider.d */; }; B696FB811A7640C00075236D /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B696FB801A7640C00075236D /* TestError.swift */; }; B696FB821A7640C00075236D /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B696FB801A7640C00075236D /* TestError.swift */; }; BE330A0F1D634F1E00806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A0E1D634F1E00806963 /* ReactiveSwift.framework */; }; @@ -346,10 +39,6 @@ BE330A171D634F4E00806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A161D634F4E00806963 /* ReactiveSwift.framework */; }; BE330A191D634F5900806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A181D634F5900806963 /* ReactiveSwift.framework */; }; BE330A1B1D634F5F00806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A1A1D634F5F00806963 /* ReactiveSwift.framework */; }; - BEBDD6E51CDC292D009A75A9 /* RACDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646B19EDA41200A782A9 /* RACDelegateProxy.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BEBDD6E61CDC292D009A75A9 /* RACDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646B19EDA41200A782A9 /* RACDelegateProxy.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BEBDD6E71CDC292E009A75A9 /* RACDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646B19EDA41200A782A9 /* RACDelegateProxy.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BEBDD6E81CDC292F009A75A9 /* RACDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646B19EDA41200A782A9 /* RACDelegateProxy.h */; settings = {ATTRIBUTES = (Public, ); }; }; BEE020661D637B0000DF261F /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B696FB801A7640C00075236D /* TestError.swift */; }; BFA6B94D1A7604D400C846D1 /* SignalProducerNimbleMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */; }; BFA6B94E1A7604D500C846D1 /* SignalProducerNimbleMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */; }; @@ -373,367 +62,9 @@ D01B7B6219EDD8FE00D26E01 /* Nimble.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; D01B7B6319EDD8FE00D26E01 /* Quick.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; D01B7B6419EDD94B00D26E01 /* ReactiveCocoa.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D047260C19E49F82006002AA /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - D03764E819EDA41200A782A9 /* NSArray+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037642A19EDA41200A782A9 /* NSArray+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03764E919EDA41200A782A9 /* NSArray+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037642A19EDA41200A782A9 /* NSArray+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03764EA19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037642B19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m */; }; - D03764EB19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037642B19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m */; }; - D03764EC19EDA41200A782A9 /* NSControl+RACCommandSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037642C19EDA41200A782A9 /* NSControl+RACCommandSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03764EE19EDA41200A782A9 /* NSControl+RACCommandSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037642D19EDA41200A782A9 /* NSControl+RACCommandSupport.m */; }; - D03764F019EDA41200A782A9 /* NSControl+RACTextSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037642E19EDA41200A782A9 /* NSControl+RACTextSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03764F219EDA41200A782A9 /* NSControl+RACTextSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037642F19EDA41200A782A9 /* NSControl+RACTextSignalSupport.m */; }; - D03764F419EDA41200A782A9 /* NSData+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643019EDA41200A782A9 /* NSData+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03764F519EDA41200A782A9 /* NSData+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643019EDA41200A782A9 /* NSData+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03764F619EDA41200A782A9 /* NSData+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643119EDA41200A782A9 /* NSData+RACSupport.m */; }; - D03764F719EDA41200A782A9 /* NSData+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643119EDA41200A782A9 /* NSData+RACSupport.m */; }; - D03764F819EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643219EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03764F919EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643219EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03764FA19EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643319EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m */; }; - D03764FB19EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643319EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m */; }; - D03764FC19EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643419EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03764FD19EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643419EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03764FE19EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643519EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m */; }; - D03764FF19EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643519EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m */; }; - D037650019EDA41200A782A9 /* NSFileHandle+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643619EDA41200A782A9 /* NSFileHandle+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037650119EDA41200A782A9 /* NSFileHandle+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643619EDA41200A782A9 /* NSFileHandle+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037650219EDA41200A782A9 /* NSFileHandle+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643719EDA41200A782A9 /* NSFileHandle+RACSupport.m */; }; - D037650319EDA41200A782A9 /* NSFileHandle+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643719EDA41200A782A9 /* NSFileHandle+RACSupport.m */; }; - D037650419EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643819EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037650519EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643819EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037650619EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643919EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m */; }; - D037650719EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643919EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m */; }; - D037650A19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643B19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m */; }; - D037650B19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643B19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m */; }; - D037650C19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643C19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037650D19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643C19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037650E19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643D19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m */; }; - D037650F19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643D19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m */; }; - D037651019EDA41200A782A9 /* NSObject+RACAppKitBindings.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643E19EDA41200A782A9 /* NSObject+RACAppKitBindings.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037651219EDA41200A782A9 /* NSObject+RACAppKitBindings.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643F19EDA41200A782A9 /* NSObject+RACAppKitBindings.m */; }; - D037651419EDA41200A782A9 /* NSObject+RACDeallocating.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644019EDA41200A782A9 /* NSObject+RACDeallocating.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037651519EDA41200A782A9 /* NSObject+RACDeallocating.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644019EDA41200A782A9 /* NSObject+RACDeallocating.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037651619EDA41200A782A9 /* NSObject+RACDeallocating.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644119EDA41200A782A9 /* NSObject+RACDeallocating.m */; }; - D037651719EDA41200A782A9 /* NSObject+RACDeallocating.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644119EDA41200A782A9 /* NSObject+RACDeallocating.m */; }; - D037651A19EDA41200A782A9 /* NSObject+RACDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644319EDA41200A782A9 /* NSObject+RACDescription.m */; }; - D037651B19EDA41200A782A9 /* NSObject+RACDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644319EDA41200A782A9 /* NSObject+RACDescription.m */; }; - D037651E19EDA41200A782A9 /* NSObject+RACKVOWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644519EDA41200A782A9 /* NSObject+RACKVOWrapper.m */; }; - D037651F19EDA41200A782A9 /* NSObject+RACKVOWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644519EDA41200A782A9 /* NSObject+RACKVOWrapper.m */; }; - D037652019EDA41200A782A9 /* NSObject+RACLifting.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644619EDA41200A782A9 /* NSObject+RACLifting.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037652119EDA41200A782A9 /* NSObject+RACLifting.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644619EDA41200A782A9 /* NSObject+RACLifting.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037652219EDA41200A782A9 /* NSObject+RACLifting.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644719EDA41200A782A9 /* NSObject+RACLifting.m */; }; - D037652319EDA41200A782A9 /* NSObject+RACLifting.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644719EDA41200A782A9 /* NSObject+RACLifting.m */; }; - D037652419EDA41200A782A9 /* NSObject+RACPropertySubscribing.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644819EDA41200A782A9 /* NSObject+RACPropertySubscribing.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037652519EDA41200A782A9 /* NSObject+RACPropertySubscribing.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644819EDA41200A782A9 /* NSObject+RACPropertySubscribing.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037652619EDA41200A782A9 /* NSObject+RACPropertySubscribing.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644919EDA41200A782A9 /* NSObject+RACPropertySubscribing.m */; }; - D037652719EDA41200A782A9 /* NSObject+RACPropertySubscribing.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644919EDA41200A782A9 /* NSObject+RACPropertySubscribing.m */; }; - D037652819EDA41200A782A9 /* NSObject+RACSelectorSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644A19EDA41200A782A9 /* NSObject+RACSelectorSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037652919EDA41200A782A9 /* NSObject+RACSelectorSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644A19EDA41200A782A9 /* NSObject+RACSelectorSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037652A19EDA41200A782A9 /* NSObject+RACSelectorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644B19EDA41200A782A9 /* NSObject+RACSelectorSignal.m */; }; - D037652B19EDA41200A782A9 /* NSObject+RACSelectorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644B19EDA41200A782A9 /* NSObject+RACSelectorSignal.m */; }; - D037652C19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644C19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037652D19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644C19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037652E19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644D19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m */; }; - D037652F19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644D19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m */; }; - D037653019EDA41200A782A9 /* NSSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644E19EDA41200A782A9 /* NSSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037653119EDA41200A782A9 /* NSSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644E19EDA41200A782A9 /* NSSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037653219EDA41200A782A9 /* NSSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644F19EDA41200A782A9 /* NSSet+RACSequenceAdditions.m */; }; - D037653319EDA41200A782A9 /* NSSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644F19EDA41200A782A9 /* NSSet+RACSequenceAdditions.m */; }; - D037653619EDA41200A782A9 /* NSString+RACKeyPathUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645119EDA41200A782A9 /* NSString+RACKeyPathUtilities.m */; }; - D037653719EDA41200A782A9 /* NSString+RACKeyPathUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645119EDA41200A782A9 /* NSString+RACKeyPathUtilities.m */; }; - D037653819EDA41200A782A9 /* NSString+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645219EDA41200A782A9 /* NSString+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037653919EDA41200A782A9 /* NSString+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645219EDA41200A782A9 /* NSString+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037653A19EDA41200A782A9 /* NSString+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645319EDA41200A782A9 /* NSString+RACSequenceAdditions.m */; }; - D037653B19EDA41200A782A9 /* NSString+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645319EDA41200A782A9 /* NSString+RACSequenceAdditions.m */; }; - D037653C19EDA41200A782A9 /* NSString+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645419EDA41200A782A9 /* NSString+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037653D19EDA41200A782A9 /* NSString+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645419EDA41200A782A9 /* NSString+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037653E19EDA41200A782A9 /* NSString+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645519EDA41200A782A9 /* NSString+RACSupport.m */; }; - D037653F19EDA41200A782A9 /* NSString+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645519EDA41200A782A9 /* NSString+RACSupport.m */; }; - D037654019EDA41200A782A9 /* NSText+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645619EDA41200A782A9 /* NSText+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037654219EDA41200A782A9 /* NSText+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645719EDA41200A782A9 /* NSText+RACSignalSupport.m */; }; - D037654419EDA41200A782A9 /* NSURLConnection+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645819EDA41200A782A9 /* NSURLConnection+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037654519EDA41200A782A9 /* NSURLConnection+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645819EDA41200A782A9 /* NSURLConnection+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037654619EDA41200A782A9 /* NSURLConnection+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645919EDA41200A782A9 /* NSURLConnection+RACSupport.m */; }; - D037654719EDA41200A782A9 /* NSURLConnection+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645919EDA41200A782A9 /* NSURLConnection+RACSupport.m */; }; - D037654819EDA41200A782A9 /* NSUserDefaults+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645A19EDA41200A782A9 /* NSUserDefaults+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037654919EDA41200A782A9 /* NSUserDefaults+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645A19EDA41200A782A9 /* NSUserDefaults+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037654A19EDA41200A782A9 /* NSUserDefaults+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645B19EDA41200A782A9 /* NSUserDefaults+RACSupport.m */; }; - D037654B19EDA41200A782A9 /* NSUserDefaults+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645B19EDA41200A782A9 /* NSUserDefaults+RACSupport.m */; }; - D037654E19EDA41200A782A9 /* RACArraySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645D19EDA41200A782A9 /* RACArraySequence.m */; }; - D037654F19EDA41200A782A9 /* RACArraySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645D19EDA41200A782A9 /* RACArraySequence.m */; }; - D037655619EDA41200A782A9 /* RACBehaviorSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646119EDA41200A782A9 /* RACBehaviorSubject.m */; }; - D037655719EDA41200A782A9 /* RACBehaviorSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646119EDA41200A782A9 /* RACBehaviorSubject.m */; }; - D037655A19EDA41200A782A9 /* RACBlockTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646319EDA41200A782A9 /* RACBlockTrampoline.m */; }; - D037655B19EDA41200A782A9 /* RACBlockTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646319EDA41200A782A9 /* RACBlockTrampoline.m */; }; - D037655C19EDA41200A782A9 /* RACChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646419EDA41200A782A9 /* RACChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037655D19EDA41200A782A9 /* RACChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646419EDA41200A782A9 /* RACChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037655E19EDA41200A782A9 /* RACChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646519EDA41200A782A9 /* RACChannel.m */; }; - D037655F19EDA41200A782A9 /* RACChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646519EDA41200A782A9 /* RACChannel.m */; }; - D037656019EDA41200A782A9 /* RACCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646619EDA41200A782A9 /* RACCommand.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037656119EDA41200A782A9 /* RACCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646619EDA41200A782A9 /* RACCommand.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037656219EDA41200A782A9 /* RACCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646719EDA41200A782A9 /* RACCommand.m */; }; - D037656319EDA41200A782A9 /* RACCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646719EDA41200A782A9 /* RACCommand.m */; }; - D037656419EDA41200A782A9 /* RACCompoundDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646819EDA41200A782A9 /* RACCompoundDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037656519EDA41200A782A9 /* RACCompoundDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646819EDA41200A782A9 /* RACCompoundDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037656619EDA41200A782A9 /* RACCompoundDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646919EDA41200A782A9 /* RACCompoundDisposable.m */; }; - D037656719EDA41200A782A9 /* RACCompoundDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646919EDA41200A782A9 /* RACCompoundDisposable.m */; }; - D037656819EDA41200A782A9 /* RACCompoundDisposableProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D037646A19EDA41200A782A9 /* RACCompoundDisposableProvider.d */; }; - D037656919EDA41200A782A9 /* RACCompoundDisposableProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D037646A19EDA41200A782A9 /* RACCompoundDisposableProvider.d */; }; - D037656C19EDA41200A782A9 /* RACDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646C19EDA41200A782A9 /* RACDelegateProxy.m */; }; - D037656D19EDA41200A782A9 /* RACDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646C19EDA41200A782A9 /* RACDelegateProxy.m */; }; - D037656E19EDA41200A782A9 /* RACDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646D19EDA41200A782A9 /* RACDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037656F19EDA41200A782A9 /* RACDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646D19EDA41200A782A9 /* RACDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037657019EDA41200A782A9 /* RACDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646E19EDA41200A782A9 /* RACDisposable.m */; }; - D037657119EDA41200A782A9 /* RACDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646E19EDA41200A782A9 /* RACDisposable.m */; }; - D037657419EDA41200A782A9 /* RACDynamicSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647019EDA41200A782A9 /* RACDynamicSequence.m */; }; - D037657519EDA41200A782A9 /* RACDynamicSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647019EDA41200A782A9 /* RACDynamicSequence.m */; }; - D037657819EDA41200A782A9 /* RACDynamicSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647219EDA41200A782A9 /* RACDynamicSignal.m */; }; - D037657919EDA41200A782A9 /* RACDynamicSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647219EDA41200A782A9 /* RACDynamicSignal.m */; }; - D037657C19EDA41200A782A9 /* RACEagerSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647419EDA41200A782A9 /* RACEagerSequence.m */; }; - D037657D19EDA41200A782A9 /* RACEagerSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647419EDA41200A782A9 /* RACEagerSequence.m */; }; - D037658019EDA41200A782A9 /* RACEmptySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647619EDA41200A782A9 /* RACEmptySequence.m */; }; - D037658119EDA41200A782A9 /* RACEmptySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647619EDA41200A782A9 /* RACEmptySequence.m */; }; - D037658419EDA41200A782A9 /* RACEmptySignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647819EDA41200A782A9 /* RACEmptySignal.m */; }; - D037658519EDA41200A782A9 /* RACEmptySignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647819EDA41200A782A9 /* RACEmptySignal.m */; }; - D037658819EDA41200A782A9 /* RACErrorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647A19EDA41200A782A9 /* RACErrorSignal.m */; }; - D037658919EDA41200A782A9 /* RACErrorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647A19EDA41200A782A9 /* RACErrorSignal.m */; }; - D037658A19EDA41200A782A9 /* RACEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = D037647B19EDA41200A782A9 /* RACEvent.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037658B19EDA41200A782A9 /* RACEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = D037647B19EDA41200A782A9 /* RACEvent.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037658C19EDA41200A782A9 /* RACEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647C19EDA41200A782A9 /* RACEvent.m */; }; - D037658D19EDA41200A782A9 /* RACEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647C19EDA41200A782A9 /* RACEvent.m */; }; - D037658E19EDA41200A782A9 /* RACGroupedSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037647D19EDA41200A782A9 /* RACGroupedSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037658F19EDA41200A782A9 /* RACGroupedSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037647D19EDA41200A782A9 /* RACGroupedSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037659019EDA41200A782A9 /* RACGroupedSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647E19EDA41200A782A9 /* RACGroupedSignal.m */; }; - D037659119EDA41200A782A9 /* RACGroupedSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647E19EDA41200A782A9 /* RACGroupedSignal.m */; }; - D037659419EDA41200A782A9 /* RACImmediateScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648019EDA41200A782A9 /* RACImmediateScheduler.m */; }; - D037659519EDA41200A782A9 /* RACImmediateScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648019EDA41200A782A9 /* RACImmediateScheduler.m */; }; - D037659819EDA41200A782A9 /* RACIndexSetSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648219EDA41200A782A9 /* RACIndexSetSequence.m */; }; - D037659919EDA41200A782A9 /* RACIndexSetSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648219EDA41200A782A9 /* RACIndexSetSequence.m */; }; - D037659A19EDA41200A782A9 /* RACKVOChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648319EDA41200A782A9 /* RACKVOChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037659B19EDA41200A782A9 /* RACKVOChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648319EDA41200A782A9 /* RACKVOChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037659C19EDA41200A782A9 /* RACKVOChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648419EDA41200A782A9 /* RACKVOChannel.m */; }; - D037659D19EDA41200A782A9 /* RACKVOChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648419EDA41200A782A9 /* RACKVOChannel.m */; }; - D03765A019EDA41200A782A9 /* RACKVOTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648619EDA41200A782A9 /* RACKVOTrampoline.m */; }; - D03765A119EDA41200A782A9 /* RACKVOTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648619EDA41200A782A9 /* RACKVOTrampoline.m */; }; - D03765A219EDA41200A782A9 /* RACMulticastConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648719EDA41200A782A9 /* RACMulticastConnection.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765A319EDA41200A782A9 /* RACMulticastConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648719EDA41200A782A9 /* RACMulticastConnection.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765A419EDA41200A782A9 /* RACMulticastConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648819EDA41200A782A9 /* RACMulticastConnection.m */; }; - D03765A519EDA41200A782A9 /* RACMulticastConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648819EDA41200A782A9 /* RACMulticastConnection.m */; }; - D03765AE19EDA41200A782A9 /* RACPassthroughSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648D19EDA41200A782A9 /* RACPassthroughSubscriber.m */; }; - D03765AF19EDA41200A782A9 /* RACPassthroughSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648D19EDA41200A782A9 /* RACPassthroughSubscriber.m */; }; - D03765B019EDA41200A782A9 /* RACQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648E19EDA41200A782A9 /* RACQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765B119EDA41200A782A9 /* RACQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648E19EDA41200A782A9 /* RACQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765B219EDA41200A782A9 /* RACQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648F19EDA41200A782A9 /* RACQueueScheduler.m */; }; - D03765B319EDA41200A782A9 /* RACQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648F19EDA41200A782A9 /* RACQueueScheduler.m */; }; - D03765B419EDA41200A782A9 /* RACQueueScheduler+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649019EDA41200A782A9 /* RACQueueScheduler+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765B519EDA41200A782A9 /* RACQueueScheduler+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649019EDA41200A782A9 /* RACQueueScheduler+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765B619EDA41200A782A9 /* RACReplaySubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649119EDA41200A782A9 /* RACReplaySubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765B719EDA41200A782A9 /* RACReplaySubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649119EDA41200A782A9 /* RACReplaySubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765B819EDA41200A782A9 /* RACReplaySubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649219EDA41200A782A9 /* RACReplaySubject.m */; }; - D03765B919EDA41200A782A9 /* RACReplaySubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649219EDA41200A782A9 /* RACReplaySubject.m */; }; - D03765BC19EDA41200A782A9 /* RACReturnSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649419EDA41200A782A9 /* RACReturnSignal.m */; }; - D03765BD19EDA41200A782A9 /* RACReturnSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649419EDA41200A782A9 /* RACReturnSignal.m */; }; - D03765BE19EDA41200A782A9 /* RACScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649519EDA41200A782A9 /* RACScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765BF19EDA41200A782A9 /* RACScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649519EDA41200A782A9 /* RACScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765C019EDA41200A782A9 /* RACScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649619EDA41200A782A9 /* RACScheduler.m */; }; - D03765C119EDA41200A782A9 /* RACScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649619EDA41200A782A9 /* RACScheduler.m */; }; - D03765C419EDA41200A782A9 /* RACScheduler+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649819EDA41200A782A9 /* RACScheduler+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765C519EDA41200A782A9 /* RACScheduler+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649819EDA41200A782A9 /* RACScheduler+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765C619EDA41200A782A9 /* RACScopedDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649919EDA41200A782A9 /* RACScopedDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765C719EDA41200A782A9 /* RACScopedDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649919EDA41200A782A9 /* RACScopedDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765C819EDA41200A782A9 /* RACScopedDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649A19EDA41200A782A9 /* RACScopedDisposable.m */; }; - D03765C919EDA41200A782A9 /* RACScopedDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649A19EDA41200A782A9 /* RACScopedDisposable.m */; }; - D03765CA19EDA41200A782A9 /* RACSequence.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649B19EDA41200A782A9 /* RACSequence.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765CB19EDA41200A782A9 /* RACSequence.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649B19EDA41200A782A9 /* RACSequence.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765CC19EDA41200A782A9 /* RACSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649C19EDA41200A782A9 /* RACSequence.m */; }; - D03765CD19EDA41200A782A9 /* RACSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649C19EDA41200A782A9 /* RACSequence.m */; }; - D03765CE19EDA41200A782A9 /* RACSerialDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649D19EDA41200A782A9 /* RACSerialDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765CF19EDA41200A782A9 /* RACSerialDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649D19EDA41200A782A9 /* RACSerialDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765D019EDA41200A782A9 /* RACSerialDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649E19EDA41200A782A9 /* RACSerialDisposable.m */; }; - D03765D119EDA41200A782A9 /* RACSerialDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649E19EDA41200A782A9 /* RACSerialDisposable.m */; }; - D03765D219EDA41200A782A9 /* RACSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649F19EDA41200A782A9 /* RACSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765D319EDA41200A782A9 /* RACSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649F19EDA41200A782A9 /* RACSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765D419EDA41200A782A9 /* RACSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A019EDA41200A782A9 /* RACSignal.m */; }; - D03765D519EDA41200A782A9 /* RACSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A019EDA41200A782A9 /* RACSignal.m */; }; - D03765D619EDA41200A782A9 /* RACSignal+Operations.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764A119EDA41200A782A9 /* RACSignal+Operations.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765D719EDA41200A782A9 /* RACSignal+Operations.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764A119EDA41200A782A9 /* RACSignal+Operations.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765D819EDA41200A782A9 /* RACSignal+Operations.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A219EDA41200A782A9 /* RACSignal+Operations.m */; }; - D03765D919EDA41200A782A9 /* RACSignal+Operations.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A219EDA41200A782A9 /* RACSignal+Operations.m */; }; - D03765DA19EDA41200A782A9 /* RACSignalProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D03764A319EDA41200A782A9 /* RACSignalProvider.d */; }; - D03765DB19EDA41200A782A9 /* RACSignalProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D03764A319EDA41200A782A9 /* RACSignalProvider.d */; }; - D03765DE19EDA41200A782A9 /* RACSignalSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A519EDA41200A782A9 /* RACSignalSequence.m */; }; - D03765DF19EDA41200A782A9 /* RACSignalSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A519EDA41200A782A9 /* RACSignalSequence.m */; }; - D03765E019EDA41200A782A9 /* RACStream.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764A619EDA41200A782A9 /* RACStream.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765E119EDA41200A782A9 /* RACStream.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764A619EDA41200A782A9 /* RACStream.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765E219EDA41200A782A9 /* RACStream.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A719EDA41200A782A9 /* RACStream.m */; }; - D03765E319EDA41200A782A9 /* RACStream.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A719EDA41200A782A9 /* RACStream.m */; }; - D03765E819EDA41200A782A9 /* RACStringSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AA19EDA41200A782A9 /* RACStringSequence.m */; }; - D03765E919EDA41200A782A9 /* RACStringSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AA19EDA41200A782A9 /* RACStringSequence.m */; }; - D03765EA19EDA41200A782A9 /* RACSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764AB19EDA41200A782A9 /* RACSubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765EB19EDA41200A782A9 /* RACSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764AB19EDA41200A782A9 /* RACSubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765EC19EDA41200A782A9 /* RACSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AC19EDA41200A782A9 /* RACSubject.m */; }; - D03765ED19EDA41200A782A9 /* RACSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AC19EDA41200A782A9 /* RACSubject.m */; }; - D03765EE19EDA41200A782A9 /* RACSubscriber.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764AD19EDA41200A782A9 /* RACSubscriber.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765EF19EDA41200A782A9 /* RACSubscriber.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764AD19EDA41200A782A9 /* RACSubscriber.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765F019EDA41200A782A9 /* RACSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AE19EDA41200A782A9 /* RACSubscriber.m */; }; - D03765F119EDA41200A782A9 /* RACSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AE19EDA41200A782A9 /* RACSubscriber.m */; }; - D03765F419EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B019EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765F519EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B019EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765F619EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B119EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m */; }; - D03765F719EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B119EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m */; }; - D03765FA19EDA41200A782A9 /* RACSubscriptionScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B319EDA41200A782A9 /* RACSubscriptionScheduler.m */; }; - D03765FB19EDA41200A782A9 /* RACSubscriptionScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B319EDA41200A782A9 /* RACSubscriptionScheduler.m */; }; - D03765FC19EDA41200A782A9 /* RACTargetQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B419EDA41200A782A9 /* RACTargetQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765FD19EDA41200A782A9 /* RACTargetQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B419EDA41200A782A9 /* RACTargetQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765FE19EDA41200A782A9 /* RACTargetQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B519EDA41200A782A9 /* RACTargetQueueScheduler.m */; }; - D03765FF19EDA41200A782A9 /* RACTargetQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B519EDA41200A782A9 /* RACTargetQueueScheduler.m */; }; - D037660019EDA41200A782A9 /* RACTestScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B619EDA41200A782A9 /* RACTestScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037660119EDA41200A782A9 /* RACTestScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B619EDA41200A782A9 /* RACTestScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037660219EDA41200A782A9 /* RACTestScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B719EDA41200A782A9 /* RACTestScheduler.m */; }; - D037660319EDA41200A782A9 /* RACTestScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B719EDA41200A782A9 /* RACTestScheduler.m */; }; - D037660419EDA41200A782A9 /* RACTuple.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B819EDA41200A782A9 /* RACTuple.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037660519EDA41200A782A9 /* RACTuple.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B819EDA41200A782A9 /* RACTuple.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037660619EDA41200A782A9 /* RACTuple.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B919EDA41200A782A9 /* RACTuple.m */; }; - D037660719EDA41200A782A9 /* RACTuple.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B919EDA41200A782A9 /* RACTuple.m */; }; - D037660A19EDA41200A782A9 /* RACTupleSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BB19EDA41200A782A9 /* RACTupleSequence.m */; }; - D037660B19EDA41200A782A9 /* RACTupleSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BB19EDA41200A782A9 /* RACTupleSequence.m */; }; - D037660E19EDA41200A782A9 /* RACUnarySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BD19EDA41200A782A9 /* RACUnarySequence.m */; }; - D037660F19EDA41200A782A9 /* RACUnarySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BD19EDA41200A782A9 /* RACUnarySequence.m */; }; - D037661019EDA41200A782A9 /* RACUnit.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764BE19EDA41200A782A9 /* RACUnit.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037661119EDA41200A782A9 /* RACUnit.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764BE19EDA41200A782A9 /* RACUnit.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037661219EDA41200A782A9 /* RACUnit.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BF19EDA41200A782A9 /* RACUnit.m */; }; - D037661319EDA41200A782A9 /* RACUnit.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BF19EDA41200A782A9 /* RACUnit.m */; }; - D037661619EDA41200A782A9 /* RACValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C119EDA41200A782A9 /* RACValueTransformer.m */; }; - D037661719EDA41200A782A9 /* RACValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C119EDA41200A782A9 /* RACValueTransformer.m */; }; - D037661919EDA41200A782A9 /* UIActionSheet+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764C219EDA41200A782A9 /* UIActionSheet+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037661B19EDA41200A782A9 /* UIActionSheet+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C319EDA41200A782A9 /* UIActionSheet+RACSignalSupport.m */; }; - D037661D19EDA41200A782A9 /* UIAlertView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764C419EDA41200A782A9 /* UIAlertView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037661F19EDA41200A782A9 /* UIAlertView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C519EDA41200A782A9 /* UIAlertView+RACSignalSupport.m */; }; - D037662119EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764C619EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037662319EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C719EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.m */; }; - D037662519EDA41200A782A9 /* UIButton+RACCommandSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764C819EDA41200A782A9 /* UIButton+RACCommandSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037662719EDA41200A782A9 /* UIButton+RACCommandSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C919EDA41200A782A9 /* UIButton+RACCommandSupport.m */; }; - D037662919EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764CA19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037662B19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764CB19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.m */; }; - D037662D19EDA41200A782A9 /* UIControl+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764CC19EDA41200A782A9 /* UIControl+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037662F19EDA41200A782A9 /* UIControl+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764CD19EDA41200A782A9 /* UIControl+RACSignalSupport.m */; }; - D037663319EDA41200A782A9 /* UIControl+RACSignalSupportPrivate.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764CF19EDA41200A782A9 /* UIControl+RACSignalSupportPrivate.m */; }; - D037663519EDA41200A782A9 /* UIDatePicker+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764D019EDA41200A782A9 /* UIDatePicker+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037663719EDA41200A782A9 /* UIDatePicker+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764D119EDA41200A782A9 /* UIDatePicker+RACSignalSupport.m */; }; - D037663919EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764D219EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037663B19EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764D319EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.m */; }; - D037663D19EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764D419EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037663F19EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764D519EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.m */; }; - D037664119EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764D619EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037664319EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764D719EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.m */; }; - D037664519EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764D819EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037664719EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764D919EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.m */; }; - D037664919EDA41200A782A9 /* UISlider+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764DA19EDA41200A782A9 /* UISlider+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037664B19EDA41200A782A9 /* UISlider+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764DB19EDA41200A782A9 /* UISlider+RACSignalSupport.m */; }; - D037664D19EDA41200A782A9 /* UIStepper+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764DC19EDA41200A782A9 /* UIStepper+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037664F19EDA41200A782A9 /* UIStepper+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764DD19EDA41200A782A9 /* UIStepper+RACSignalSupport.m */; }; - D037665119EDA41200A782A9 /* UISwitch+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764DE19EDA41200A782A9 /* UISwitch+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037665319EDA41200A782A9 /* UISwitch+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764DF19EDA41200A782A9 /* UISwitch+RACSignalSupport.m */; }; - D037665519EDA41200A782A9 /* UITableViewCell+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764E019EDA41200A782A9 /* UITableViewCell+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037665719EDA41200A782A9 /* UITableViewCell+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764E119EDA41200A782A9 /* UITableViewCell+RACSignalSupport.m */; }; - D037665919EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764E219EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037665B19EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764E319EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.m */; }; - D037665D19EDA41200A782A9 /* UITextField+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764E419EDA41200A782A9 /* UITextField+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037665F19EDA41200A782A9 /* UITextField+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764E519EDA41200A782A9 /* UITextField+RACSignalSupport.m */; }; - D037666119EDA41200A782A9 /* UITextView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764E619EDA41200A782A9 /* UITextView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037666319EDA41200A782A9 /* UITextView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764E719EDA41200A782A9 /* UITextView+RACSignalSupport.m */; }; D037666419EDA43C00A782A9 /* ReactiveCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037666B19EDA57100A782A9 /* EXTKeyPathCoding.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666619EDA57100A782A9 /* EXTKeyPathCoding.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037666C19EDA57100A782A9 /* EXTKeyPathCoding.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666619EDA57100A782A9 /* EXTKeyPathCoding.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037666F19EDA57100A782A9 /* EXTRuntimeExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037666819EDA57100A782A9 /* EXTRuntimeExtensions.m */; }; - D037667019EDA57100A782A9 /* EXTRuntimeExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037666819EDA57100A782A9 /* EXTRuntimeExtensions.m */; }; - D037667119EDA57100A782A9 /* EXTScope.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666919EDA57100A782A9 /* EXTScope.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037667219EDA57100A782A9 /* EXTScope.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666919EDA57100A782A9 /* EXTScope.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037667319EDA57100A782A9 /* metamacros.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666A19EDA57100A782A9 /* metamacros.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037667419EDA57100A782A9 /* metamacros.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666A19EDA57100A782A9 /* metamacros.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03766B919EDA60000A782A9 /* NSControllerRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667619EDA60000A782A9 /* NSControllerRACSupportSpec.m */; }; - D03766BD19EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667819EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m */; }; - D03766BE19EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667819EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m */; }; - D03766BF19EDA60000A782A9 /* NSNotificationCenterRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667919EDA60000A782A9 /* NSNotificationCenterRACSupportSpec.m */; }; - D03766C019EDA60000A782A9 /* NSNotificationCenterRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667919EDA60000A782A9 /* NSNotificationCenterRACSupportSpec.m */; }; - D03766C119EDA60000A782A9 /* NSObjectRACAppKitBindingsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667A19EDA60000A782A9 /* NSObjectRACAppKitBindingsSpec.m */; }; - D03766C319EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667B19EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m */; }; - D03766C419EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667B19EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m */; }; - D03766C519EDA60000A782A9 /* NSObjectRACLiftingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667C19EDA60000A782A9 /* NSObjectRACLiftingSpec.m */; }; - D03766C619EDA60000A782A9 /* NSObjectRACLiftingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667C19EDA60000A782A9 /* NSObjectRACLiftingSpec.m */; }; - D03766C719EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667E19EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.m */; }; - D03766C819EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667E19EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.m */; }; - D03766C919EDA60000A782A9 /* NSObjectRACPropertySubscribingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667F19EDA60000A782A9 /* NSObjectRACPropertySubscribingSpec.m */; }; - D03766CA19EDA60000A782A9 /* NSObjectRACPropertySubscribingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667F19EDA60000A782A9 /* NSObjectRACPropertySubscribingSpec.m */; }; - D03766CB19EDA60000A782A9 /* NSObjectRACSelectorSignalSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668019EDA60000A782A9 /* NSObjectRACSelectorSignalSpec.m */; }; - D03766CC19EDA60000A782A9 /* NSObjectRACSelectorSignalSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668019EDA60000A782A9 /* NSObjectRACSelectorSignalSpec.m */; }; - D03766CD19EDA60000A782A9 /* NSStringRACKeyPathUtilitiesSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668119EDA60000A782A9 /* NSStringRACKeyPathUtilitiesSpec.m */; }; - D03766CE19EDA60000A782A9 /* NSStringRACKeyPathUtilitiesSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668119EDA60000A782A9 /* NSStringRACKeyPathUtilitiesSpec.m */; }; - D03766D119EDA60000A782A9 /* NSURLConnectionRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668319EDA60000A782A9 /* NSURLConnectionRACSupportSpec.m */; }; - D03766D219EDA60000A782A9 /* NSURLConnectionRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668319EDA60000A782A9 /* NSURLConnectionRACSupportSpec.m */; }; - D03766D319EDA60000A782A9 /* NSUserDefaultsRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668419EDA60000A782A9 /* NSUserDefaultsRACSupportSpec.m */; }; - D03766D419EDA60000A782A9 /* NSUserDefaultsRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668419EDA60000A782A9 /* NSUserDefaultsRACSupportSpec.m */; }; - D03766D719EDA60000A782A9 /* RACBlockTrampolineSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668619EDA60000A782A9 /* RACBlockTrampolineSpec.m */; }; - D03766D819EDA60000A782A9 /* RACBlockTrampolineSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668619EDA60000A782A9 /* RACBlockTrampolineSpec.m */; }; - D03766D919EDA60000A782A9 /* RACChannelExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668819EDA60000A782A9 /* RACChannelExamples.m */; }; - D03766DA19EDA60000A782A9 /* RACChannelExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668819EDA60000A782A9 /* RACChannelExamples.m */; }; - D03766DB19EDA60000A782A9 /* RACChannelSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668919EDA60000A782A9 /* RACChannelSpec.m */; }; - D03766DC19EDA60000A782A9 /* RACChannelSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668919EDA60000A782A9 /* RACChannelSpec.m */; }; - D03766DD19EDA60000A782A9 /* RACCommandSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668A19EDA60000A782A9 /* RACCommandSpec.m */; }; - D03766DE19EDA60000A782A9 /* RACCommandSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668A19EDA60000A782A9 /* RACCommandSpec.m */; }; - D03766DF19EDA60000A782A9 /* RACCompoundDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668B19EDA60000A782A9 /* RACCompoundDisposableSpec.m */; }; - D03766E019EDA60000A782A9 /* RACCompoundDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668B19EDA60000A782A9 /* RACCompoundDisposableSpec.m */; }; - D03766E119EDA60000A782A9 /* RACControlCommandExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668D19EDA60000A782A9 /* RACControlCommandExamples.m */; }; - D03766E219EDA60000A782A9 /* RACControlCommandExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668D19EDA60000A782A9 /* RACControlCommandExamples.m */; }; - D03766E319EDA60000A782A9 /* RACDelegateProxySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668E19EDA60000A782A9 /* RACDelegateProxySpec.m */; }; - D03766E419EDA60000A782A9 /* RACDelegateProxySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668E19EDA60000A782A9 /* RACDelegateProxySpec.m */; }; - D03766E519EDA60000A782A9 /* RACDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668F19EDA60000A782A9 /* RACDisposableSpec.m */; }; - D03766E619EDA60000A782A9 /* RACDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668F19EDA60000A782A9 /* RACDisposableSpec.m */; }; - D03766E719EDA60000A782A9 /* RACEventSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669019EDA60000A782A9 /* RACEventSpec.m */; }; - D03766E819EDA60000A782A9 /* RACEventSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669019EDA60000A782A9 /* RACEventSpec.m */; }; - D03766E919EDA60000A782A9 /* RACKVOChannelSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669119EDA60000A782A9 /* RACKVOChannelSpec.m */; }; - D03766EA19EDA60000A782A9 /* RACKVOChannelSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669119EDA60000A782A9 /* RACKVOChannelSpec.m */; }; - D03766EB19EDA60000A782A9 /* RACKVOWrapperSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669219EDA60000A782A9 /* RACKVOWrapperSpec.m */; }; - D03766EC19EDA60000A782A9 /* RACKVOWrapperSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669219EDA60000A782A9 /* RACKVOWrapperSpec.m */; }; - D03766ED19EDA60000A782A9 /* RACMulticastConnectionSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669319EDA60000A782A9 /* RACMulticastConnectionSpec.m */; }; - D03766EE19EDA60000A782A9 /* RACMulticastConnectionSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669319EDA60000A782A9 /* RACMulticastConnectionSpec.m */; }; - D03766EF19EDA60000A782A9 /* RACPropertySignalExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669519EDA60000A782A9 /* RACPropertySignalExamples.m */; }; - D03766F019EDA60000A782A9 /* RACPropertySignalExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669519EDA60000A782A9 /* RACPropertySignalExamples.m */; }; - D03766F119EDA60000A782A9 /* RACSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669619EDA60000A782A9 /* RACSchedulerSpec.m */; }; - D03766F219EDA60000A782A9 /* RACSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669619EDA60000A782A9 /* RACSchedulerSpec.m */; }; - D03766F319EDA60000A782A9 /* RACSequenceAdditionsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669719EDA60000A782A9 /* RACSequenceAdditionsSpec.m */; }; - D03766F419EDA60000A782A9 /* RACSequenceAdditionsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669719EDA60000A782A9 /* RACSequenceAdditionsSpec.m */; }; - D03766F519EDA60000A782A9 /* RACSequenceExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669919EDA60000A782A9 /* RACSequenceExamples.m */; }; - D03766F619EDA60000A782A9 /* RACSequenceExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669919EDA60000A782A9 /* RACSequenceExamples.m */; }; - D03766F719EDA60000A782A9 /* RACSequenceSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669A19EDA60000A782A9 /* RACSequenceSpec.m */; }; - D03766F819EDA60000A782A9 /* RACSequenceSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669A19EDA60000A782A9 /* RACSequenceSpec.m */; }; - D03766F919EDA60000A782A9 /* RACSerialDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669B19EDA60000A782A9 /* RACSerialDisposableSpec.m */; }; - D03766FA19EDA60000A782A9 /* RACSerialDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669B19EDA60000A782A9 /* RACSerialDisposableSpec.m */; }; - D03766FB19EDA60000A782A9 /* RACSignalSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669C19EDA60000A782A9 /* RACSignalSpec.m */; }; - D03766FC19EDA60000A782A9 /* RACSignalSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669C19EDA60000A782A9 /* RACSignalSpec.m */; }; - D03766FF19EDA60000A782A9 /* RACStreamExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A019EDA60000A782A9 /* RACStreamExamples.m */; }; - D037670019EDA60000A782A9 /* RACStreamExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A019EDA60000A782A9 /* RACStreamExamples.m */; }; - D037670119EDA60000A782A9 /* RACSubclassObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A219EDA60000A782A9 /* RACSubclassObject.m */; }; - D037670219EDA60000A782A9 /* RACSubclassObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A219EDA60000A782A9 /* RACSubclassObject.m */; }; - D037670319EDA60000A782A9 /* RACSubjectSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A319EDA60000A782A9 /* RACSubjectSpec.m */; }; - D037670419EDA60000A782A9 /* RACSubjectSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A319EDA60000A782A9 /* RACSubjectSpec.m */; }; - D037670519EDA60000A782A9 /* RACSubscriberExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A519EDA60000A782A9 /* RACSubscriberExamples.m */; }; - D037670619EDA60000A782A9 /* RACSubscriberExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A519EDA60000A782A9 /* RACSubscriberExamples.m */; }; - D037670719EDA60000A782A9 /* RACSubscriberSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A619EDA60000A782A9 /* RACSubscriberSpec.m */; }; - D037670819EDA60000A782A9 /* RACSubscriberSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A619EDA60000A782A9 /* RACSubscriberSpec.m */; }; - D037670919EDA60000A782A9 /* RACSubscriptingAssignmentTrampolineSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A719EDA60000A782A9 /* RACSubscriptingAssignmentTrampolineSpec.m */; }; - D037670A19EDA60000A782A9 /* RACSubscriptingAssignmentTrampolineSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A719EDA60000A782A9 /* RACSubscriptingAssignmentTrampolineSpec.m */; }; - D037670B19EDA60000A782A9 /* RACTargetQueueSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A819EDA60000A782A9 /* RACTargetQueueSchedulerSpec.m */; }; - D037670C19EDA60000A782A9 /* RACTargetQueueSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A819EDA60000A782A9 /* RACTargetQueueSchedulerSpec.m */; }; - D037671519EDA60000A782A9 /* RACTupleSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B019EDA60000A782A9 /* RACTupleSpec.m */; }; - D037671619EDA60000A782A9 /* RACTupleSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B019EDA60000A782A9 /* RACTupleSpec.m */; }; D037671719EDA60000A782A9 /* test-data.json in Resources */ = {isa = PBXBuildFile; fileRef = D03766B119EDA60000A782A9 /* test-data.json */; }; D037671819EDA60000A782A9 /* test-data.json in Resources */ = {isa = PBXBuildFile; fileRef = D03766B119EDA60000A782A9 /* test-data.json */; }; - D037671A19EDA60000A782A9 /* UIActionSheetRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B219EDA60000A782A9 /* UIActionSheetRACSupportSpec.m */; }; - D037671C19EDA60000A782A9 /* UIAlertViewRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B319EDA60000A782A9 /* UIAlertViewRACSupportSpec.m */; }; - D037671E19EDA60000A782A9 /* UIBarButtonItemRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B419EDA60000A782A9 /* UIBarButtonItemRACSupportSpec.m */; }; - D037672019EDA60000A782A9 /* UIButtonRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B519EDA60000A782A9 /* UIButtonRACSupportSpec.m */; }; - D037672419EDA60000A782A9 /* UIImagePickerControllerRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B719EDA60000A782A9 /* UIImagePickerControllerRACSupportSpec.m */; }; - D037672719EDA63400A782A9 /* RACBehaviorSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646019EDA41200A782A9 /* RACBehaviorSubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037672819EDA63500A782A9 /* RACBehaviorSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646019EDA41200A782A9 /* RACBehaviorSubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; D037672D19EDA75D00A782A9 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; }; D037672F19EDA78B00A782A9 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; }; D04725F019E49ED7006002AA /* ReactiveCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -743,17 +74,6 @@ D05E662619EDD83000904ACA /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; }; D0A2260E1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */; }; D0A2260F1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */; }; - D0A226111A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A226101A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift */; }; - D0A226121A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A226101A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift */; }; - D0C312DF19EF2A5800984962 /* ObjectiveCBridging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312C419EF2A5800984962 /* ObjectiveCBridging.swift */; }; - D0C312E019EF2A5800984962 /* ObjectiveCBridging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312C419EF2A5800984962 /* ObjectiveCBridging.swift */; }; - D0C3131E19EF2D9700984962 /* RACTestExampleScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131819EF2D9700984962 /* RACTestExampleScheduler.m */; }; - D0C3131F19EF2D9700984962 /* RACTestExampleScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131819EF2D9700984962 /* RACTestExampleScheduler.m */; }; - D0C3132019EF2D9700984962 /* RACTestObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131A19EF2D9700984962 /* RACTestObject.m */; }; - D0C3132119EF2D9700984962 /* RACTestObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131A19EF2D9700984962 /* RACTestObject.m */; }; - D0C3132219EF2D9700984962 /* RACTestSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131B19EF2D9700984962 /* RACTestSchedulerSpec.m */; }; - D0C3132319EF2D9700984962 /* RACTestSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131B19EF2D9700984962 /* RACTestSchedulerSpec.m */; }; - D0C3132519EF2D9700984962 /* RACTestUIButton.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131D19EF2D9700984962 /* RACTestUIButton.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -812,17 +132,12 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 314304151ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MKAnnotationView+RACSignalSupport.h"; sourceTree = ""; }; - 314304161ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MKAnnotationView+RACSignalSupport.m"; sourceTree = ""; }; 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Lifetime.swift"; sourceTree = ""; }; 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Application.xcconfig"; sourceTree = ""; }; 57A4D2451BA13F9700F7D4B1 /* tvOS-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Base.xcconfig"; sourceTree = ""; }; 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Framework.xcconfig"; sourceTree = ""; }; 57A4D2471BA13F9700F7D4B1 /* tvOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-StaticLibrary.xcconfig"; sourceTree = ""; }; - 7A70657D1A3F88B8001E8354 /* RACKVOProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACKVOProxy.h; sourceTree = ""; }; - 7A70657E1A3F88B8001E8354 /* RACKVOProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACKVOProxy.m; sourceTree = ""; }; - 7A7065831A3F8967001E8354 /* RACKVOProxySpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACKVOProxySpec.m; sourceTree = ""; }; 7DFBED031CDB8C9500EE435B /* ReactiveCocoaTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveCocoaTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyValueObservingSpec.swift; sourceTree = ""; }; 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+KeyValueObserving.swift"; sourceTree = ""; }; @@ -839,255 +154,12 @@ BE330A161D634F4E00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-watchos/ReactiveSwift.framework"; sourceTree = ""; }; BE330A181D634F5900806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-appletvos/ReactiveSwift.framework"; sourceTree = ""; }; BE330A1A1D634F5F00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-appletvos/ReactiveSwift.framework"; sourceTree = ""; }; - BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SignalProducerNimbleMatchers.swift; path = Swift/SignalProducerNimbleMatchers.swift; sourceTree = ""; }; + BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalProducerNimbleMatchers.swift; sourceTree = ""; }; C7142DBB1CDEA167009F402D /* CocoaAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaAction.swift; sourceTree = ""; }; CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DynamicProperty.swift; sourceTree = ""; }; CD8401821CEE8ED7009F0ABF /* CocoaActionSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaActionSpec.swift; sourceTree = ""; }; CDC42E2E1AE7AB8B00965373 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D037642A19EDA41200A782A9 /* NSArray+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+RACSequenceAdditions.h"; sourceTree = ""; }; - D037642B19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+RACSequenceAdditions.m"; sourceTree = ""; }; - D037642C19EDA41200A782A9 /* NSControl+RACCommandSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSControl+RACCommandSupport.h"; sourceTree = ""; }; - D037642D19EDA41200A782A9 /* NSControl+RACCommandSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSControl+RACCommandSupport.m"; sourceTree = ""; }; - D037642E19EDA41200A782A9 /* NSControl+RACTextSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSControl+RACTextSignalSupport.h"; sourceTree = ""; }; - D037642F19EDA41200A782A9 /* NSControl+RACTextSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSControl+RACTextSignalSupport.m"; sourceTree = ""; }; - D037643019EDA41200A782A9 /* NSData+RACSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+RACSupport.h"; sourceTree = ""; }; - D037643119EDA41200A782A9 /* NSData+RACSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+RACSupport.m"; sourceTree = ""; }; - D037643219EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+RACSequenceAdditions.h"; sourceTree = ""; }; - D037643319EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+RACSequenceAdditions.m"; sourceTree = ""; }; - D037643419EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSEnumerator+RACSequenceAdditions.h"; sourceTree = ""; }; - D037643519EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSEnumerator+RACSequenceAdditions.m"; sourceTree = ""; }; - D037643619EDA41200A782A9 /* NSFileHandle+RACSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSFileHandle+RACSupport.h"; sourceTree = ""; }; - D037643719EDA41200A782A9 /* NSFileHandle+RACSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSFileHandle+RACSupport.m"; sourceTree = ""; }; - D037643819EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSIndexSet+RACSequenceAdditions.h"; sourceTree = ""; }; - D037643919EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSIndexSet+RACSequenceAdditions.m"; sourceTree = ""; }; - D037643A19EDA41200A782A9 /* NSInvocation+RACTypeParsing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSInvocation+RACTypeParsing.h"; sourceTree = ""; }; - D037643B19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSInvocation+RACTypeParsing.m"; sourceTree = ""; }; - D037643C19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNotificationCenter+RACSupport.h"; sourceTree = ""; }; - D037643D19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSNotificationCenter+RACSupport.m"; sourceTree = ""; }; - D037643E19EDA41200A782A9 /* NSObject+RACAppKitBindings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACAppKitBindings.h"; sourceTree = ""; }; - D037643F19EDA41200A782A9 /* NSObject+RACAppKitBindings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACAppKitBindings.m"; sourceTree = ""; }; - D037644019EDA41200A782A9 /* NSObject+RACDeallocating.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACDeallocating.h"; sourceTree = ""; }; - D037644119EDA41200A782A9 /* NSObject+RACDeallocating.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACDeallocating.m"; sourceTree = ""; }; - D037644219EDA41200A782A9 /* NSObject+RACDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACDescription.h"; sourceTree = ""; }; - D037644319EDA41200A782A9 /* NSObject+RACDescription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACDescription.m"; sourceTree = ""; }; - D037644419EDA41200A782A9 /* NSObject+RACKVOWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACKVOWrapper.h"; sourceTree = ""; }; - D037644519EDA41200A782A9 /* NSObject+RACKVOWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACKVOWrapper.m"; sourceTree = ""; }; - D037644619EDA41200A782A9 /* NSObject+RACLifting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACLifting.h"; sourceTree = ""; }; - D037644719EDA41200A782A9 /* NSObject+RACLifting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACLifting.m"; sourceTree = ""; }; - D037644819EDA41200A782A9 /* NSObject+RACPropertySubscribing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACPropertySubscribing.h"; sourceTree = ""; }; - D037644919EDA41200A782A9 /* NSObject+RACPropertySubscribing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACPropertySubscribing.m"; sourceTree = ""; }; - D037644A19EDA41200A782A9 /* NSObject+RACSelectorSignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACSelectorSignal.h"; sourceTree = ""; }; - D037644B19EDA41200A782A9 /* NSObject+RACSelectorSignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACSelectorSignal.m"; sourceTree = ""; }; - D037644C19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSOrderedSet+RACSequenceAdditions.h"; sourceTree = ""; }; - D037644D19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSOrderedSet+RACSequenceAdditions.m"; sourceTree = ""; }; - D037644E19EDA41200A782A9 /* NSSet+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSSet+RACSequenceAdditions.h"; sourceTree = ""; }; - D037644F19EDA41200A782A9 /* NSSet+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSSet+RACSequenceAdditions.m"; sourceTree = ""; }; - D037645019EDA41200A782A9 /* NSString+RACKeyPathUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+RACKeyPathUtilities.h"; sourceTree = ""; }; - D037645119EDA41200A782A9 /* NSString+RACKeyPathUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+RACKeyPathUtilities.m"; sourceTree = ""; }; - D037645219EDA41200A782A9 /* NSString+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+RACSequenceAdditions.h"; sourceTree = ""; }; - D037645319EDA41200A782A9 /* NSString+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+RACSequenceAdditions.m"; sourceTree = ""; }; - D037645419EDA41200A782A9 /* NSString+RACSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+RACSupport.h"; sourceTree = ""; }; - D037645519EDA41200A782A9 /* NSString+RACSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+RACSupport.m"; sourceTree = ""; }; - D037645619EDA41200A782A9 /* NSText+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSText+RACSignalSupport.h"; sourceTree = ""; }; - D037645719EDA41200A782A9 /* NSText+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSText+RACSignalSupport.m"; sourceTree = ""; }; - D037645819EDA41200A782A9 /* NSURLConnection+RACSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSURLConnection+RACSupport.h"; sourceTree = ""; }; - D037645919EDA41200A782A9 /* NSURLConnection+RACSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSURLConnection+RACSupport.m"; sourceTree = ""; }; - D037645A19EDA41200A782A9 /* NSUserDefaults+RACSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSUserDefaults+RACSupport.h"; sourceTree = ""; }; - D037645B19EDA41200A782A9 /* NSUserDefaults+RACSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSUserDefaults+RACSupport.m"; sourceTree = ""; }; - D037645C19EDA41200A782A9 /* RACArraySequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACArraySequence.h; sourceTree = ""; }; - D037645D19EDA41200A782A9 /* RACArraySequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACArraySequence.m; sourceTree = ""; }; - D037646019EDA41200A782A9 /* RACBehaviorSubject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACBehaviorSubject.h; sourceTree = ""; }; - D037646119EDA41200A782A9 /* RACBehaviorSubject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACBehaviorSubject.m; sourceTree = ""; }; - D037646219EDA41200A782A9 /* RACBlockTrampoline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACBlockTrampoline.h; sourceTree = ""; }; - D037646319EDA41200A782A9 /* RACBlockTrampoline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACBlockTrampoline.m; sourceTree = ""; }; - D037646419EDA41200A782A9 /* RACChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACChannel.h; sourceTree = ""; }; - D037646519EDA41200A782A9 /* RACChannel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACChannel.m; sourceTree = ""; }; - D037646619EDA41200A782A9 /* RACCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACCommand.h; sourceTree = ""; }; - D037646719EDA41200A782A9 /* RACCommand.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACCommand.m; sourceTree = ""; }; - D037646819EDA41200A782A9 /* RACCompoundDisposable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACCompoundDisposable.h; sourceTree = ""; }; - D037646919EDA41200A782A9 /* RACCompoundDisposable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACCompoundDisposable.m; sourceTree = ""; }; - D037646A19EDA41200A782A9 /* RACCompoundDisposableProvider.d */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.dtrace; path = RACCompoundDisposableProvider.d; sourceTree = ""; }; - D037646B19EDA41200A782A9 /* RACDelegateProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACDelegateProxy.h; sourceTree = ""; }; - D037646C19EDA41200A782A9 /* RACDelegateProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACDelegateProxy.m; sourceTree = ""; }; - D037646D19EDA41200A782A9 /* RACDisposable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACDisposable.h; sourceTree = ""; }; - D037646E19EDA41200A782A9 /* RACDisposable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACDisposable.m; sourceTree = ""; }; - D037646F19EDA41200A782A9 /* RACDynamicSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACDynamicSequence.h; sourceTree = ""; }; - D037647019EDA41200A782A9 /* RACDynamicSequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACDynamicSequence.m; sourceTree = ""; }; - D037647119EDA41200A782A9 /* RACDynamicSignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACDynamicSignal.h; sourceTree = ""; }; - D037647219EDA41200A782A9 /* RACDynamicSignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACDynamicSignal.m; sourceTree = ""; }; - D037647319EDA41200A782A9 /* RACEagerSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACEagerSequence.h; sourceTree = ""; }; - D037647419EDA41200A782A9 /* RACEagerSequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACEagerSequence.m; sourceTree = ""; }; - D037647519EDA41200A782A9 /* RACEmptySequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACEmptySequence.h; sourceTree = ""; }; - D037647619EDA41200A782A9 /* RACEmptySequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACEmptySequence.m; sourceTree = ""; }; - D037647719EDA41200A782A9 /* RACEmptySignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACEmptySignal.h; sourceTree = ""; }; - D037647819EDA41200A782A9 /* RACEmptySignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACEmptySignal.m; sourceTree = ""; }; - D037647919EDA41200A782A9 /* RACErrorSignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACErrorSignal.h; sourceTree = ""; }; - D037647A19EDA41200A782A9 /* RACErrorSignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACErrorSignal.m; sourceTree = ""; }; - D037647B19EDA41200A782A9 /* RACEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACEvent.h; sourceTree = ""; }; - D037647C19EDA41200A782A9 /* RACEvent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACEvent.m; sourceTree = ""; }; - D037647D19EDA41200A782A9 /* RACGroupedSignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACGroupedSignal.h; sourceTree = ""; }; - D037647E19EDA41200A782A9 /* RACGroupedSignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACGroupedSignal.m; sourceTree = ""; }; - D037647F19EDA41200A782A9 /* RACImmediateScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACImmediateScheduler.h; sourceTree = ""; }; - D037648019EDA41200A782A9 /* RACImmediateScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACImmediateScheduler.m; sourceTree = ""; }; - D037648119EDA41200A782A9 /* RACIndexSetSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACIndexSetSequence.h; sourceTree = ""; }; - D037648219EDA41200A782A9 /* RACIndexSetSequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACIndexSetSequence.m; sourceTree = ""; }; - D037648319EDA41200A782A9 /* RACKVOChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACKVOChannel.h; sourceTree = ""; }; - D037648419EDA41200A782A9 /* RACKVOChannel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACKVOChannel.m; sourceTree = ""; }; - D037648519EDA41200A782A9 /* RACKVOTrampoline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACKVOTrampoline.h; sourceTree = ""; }; - D037648619EDA41200A782A9 /* RACKVOTrampoline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACKVOTrampoline.m; sourceTree = ""; }; - D037648719EDA41200A782A9 /* RACMulticastConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACMulticastConnection.h; sourceTree = ""; }; - D037648819EDA41200A782A9 /* RACMulticastConnection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACMulticastConnection.m; sourceTree = ""; }; - D037648919EDA41200A782A9 /* RACMulticastConnection+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RACMulticastConnection+Private.h"; sourceTree = ""; }; - D037648C19EDA41200A782A9 /* RACPassthroughSubscriber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACPassthroughSubscriber.h; sourceTree = ""; }; - D037648D19EDA41200A782A9 /* RACPassthroughSubscriber.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACPassthroughSubscriber.m; sourceTree = ""; }; - D037648E19EDA41200A782A9 /* RACQueueScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACQueueScheduler.h; sourceTree = ""; }; - D037648F19EDA41200A782A9 /* RACQueueScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACQueueScheduler.m; sourceTree = ""; }; - D037649019EDA41200A782A9 /* RACQueueScheduler+Subclass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RACQueueScheduler+Subclass.h"; sourceTree = ""; }; - D037649119EDA41200A782A9 /* RACReplaySubject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACReplaySubject.h; sourceTree = ""; }; - D037649219EDA41200A782A9 /* RACReplaySubject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACReplaySubject.m; sourceTree = ""; }; - D037649319EDA41200A782A9 /* RACReturnSignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACReturnSignal.h; sourceTree = ""; }; - D037649419EDA41200A782A9 /* RACReturnSignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACReturnSignal.m; sourceTree = ""; }; - D037649519EDA41200A782A9 /* RACScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACScheduler.h; sourceTree = ""; }; - D037649619EDA41200A782A9 /* RACScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACScheduler.m; sourceTree = ""; }; - D037649719EDA41200A782A9 /* RACScheduler+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RACScheduler+Private.h"; sourceTree = ""; }; - D037649819EDA41200A782A9 /* RACScheduler+Subclass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RACScheduler+Subclass.h"; sourceTree = ""; }; - D037649919EDA41200A782A9 /* RACScopedDisposable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACScopedDisposable.h; sourceTree = ""; }; - D037649A19EDA41200A782A9 /* RACScopedDisposable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACScopedDisposable.m; sourceTree = ""; }; - D037649B19EDA41200A782A9 /* RACSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSequence.h; sourceTree = ""; }; - D037649C19EDA41200A782A9 /* RACSequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSequence.m; sourceTree = ""; }; - D037649D19EDA41200A782A9 /* RACSerialDisposable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSerialDisposable.h; sourceTree = ""; }; - D037649E19EDA41200A782A9 /* RACSerialDisposable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSerialDisposable.m; sourceTree = ""; }; - D037649F19EDA41200A782A9 /* RACSignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSignal.h; sourceTree = ""; }; - D03764A019EDA41200A782A9 /* RACSignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSignal.m; sourceTree = ""; }; - D03764A119EDA41200A782A9 /* RACSignal+Operations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RACSignal+Operations.h"; sourceTree = ""; }; - D03764A219EDA41200A782A9 /* RACSignal+Operations.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RACSignal+Operations.m"; sourceTree = ""; }; - D03764A319EDA41200A782A9 /* RACSignalProvider.d */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.dtrace; path = RACSignalProvider.d; sourceTree = ""; }; - D03764A419EDA41200A782A9 /* RACSignalSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSignalSequence.h; sourceTree = ""; }; - D03764A519EDA41200A782A9 /* RACSignalSequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSignalSequence.m; sourceTree = ""; }; - D03764A619EDA41200A782A9 /* RACStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACStream.h; sourceTree = ""; }; - D03764A719EDA41200A782A9 /* RACStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACStream.m; sourceTree = ""; }; - D03764A819EDA41200A782A9 /* RACStream+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RACStream+Private.h"; sourceTree = ""; }; - D03764A919EDA41200A782A9 /* RACStringSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACStringSequence.h; sourceTree = ""; }; - D03764AA19EDA41200A782A9 /* RACStringSequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACStringSequence.m; sourceTree = ""; }; - D03764AB19EDA41200A782A9 /* RACSubject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSubject.h; sourceTree = ""; }; - D03764AC19EDA41200A782A9 /* RACSubject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubject.m; sourceTree = ""; }; - D03764AD19EDA41200A782A9 /* RACSubscriber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSubscriber.h; sourceTree = ""; }; - D03764AE19EDA41200A782A9 /* RACSubscriber.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubscriber.m; sourceTree = ""; }; - D03764AF19EDA41200A782A9 /* RACSubscriber+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RACSubscriber+Private.h"; sourceTree = ""; }; - D03764B019EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSubscriptingAssignmentTrampoline.h; sourceTree = ""; }; - D03764B119EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubscriptingAssignmentTrampoline.m; sourceTree = ""; }; - D03764B219EDA41200A782A9 /* RACSubscriptionScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSubscriptionScheduler.h; sourceTree = ""; }; - D03764B319EDA41200A782A9 /* RACSubscriptionScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubscriptionScheduler.m; sourceTree = ""; }; - D03764B419EDA41200A782A9 /* RACTargetQueueScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTargetQueueScheduler.h; sourceTree = ""; }; - D03764B519EDA41200A782A9 /* RACTargetQueueScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTargetQueueScheduler.m; sourceTree = ""; }; - D03764B619EDA41200A782A9 /* RACTestScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTestScheduler.h; sourceTree = ""; }; - D03764B719EDA41200A782A9 /* RACTestScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTestScheduler.m; sourceTree = ""; }; - D03764B819EDA41200A782A9 /* RACTuple.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTuple.h; sourceTree = ""; }; - D03764B919EDA41200A782A9 /* RACTuple.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTuple.m; sourceTree = ""; }; - D03764BA19EDA41200A782A9 /* RACTupleSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTupleSequence.h; sourceTree = ""; }; - D03764BB19EDA41200A782A9 /* RACTupleSequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTupleSequence.m; sourceTree = ""; }; - D03764BC19EDA41200A782A9 /* RACUnarySequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACUnarySequence.h; sourceTree = ""; }; - D03764BD19EDA41200A782A9 /* RACUnarySequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACUnarySequence.m; sourceTree = ""; }; - D03764BE19EDA41200A782A9 /* RACUnit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACUnit.h; sourceTree = ""; }; - D03764BF19EDA41200A782A9 /* RACUnit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACUnit.m; sourceTree = ""; }; - D03764C019EDA41200A782A9 /* RACValueTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACValueTransformer.h; sourceTree = ""; }; - D03764C119EDA41200A782A9 /* RACValueTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACValueTransformer.m; sourceTree = ""; }; - D03764C219EDA41200A782A9 /* UIActionSheet+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIActionSheet+RACSignalSupport.h"; sourceTree = ""; }; - D03764C319EDA41200A782A9 /* UIActionSheet+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIActionSheet+RACSignalSupport.m"; sourceTree = ""; }; - D03764C419EDA41200A782A9 /* UIAlertView+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIAlertView+RACSignalSupport.h"; sourceTree = ""; }; - D03764C519EDA41200A782A9 /* UIAlertView+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIAlertView+RACSignalSupport.m"; sourceTree = ""; }; - D03764C619EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIBarButtonItem+RACCommandSupport.h"; sourceTree = ""; }; - D03764C719EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIBarButtonItem+RACCommandSupport.m"; sourceTree = ""; }; - D03764C819EDA41200A782A9 /* UIButton+RACCommandSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIButton+RACCommandSupport.h"; sourceTree = ""; }; - D03764C919EDA41200A782A9 /* UIButton+RACCommandSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIButton+RACCommandSupport.m"; sourceTree = ""; }; - D03764CA19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionReusableView+RACSignalSupport.h"; sourceTree = ""; }; - D03764CB19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionReusableView+RACSignalSupport.m"; sourceTree = ""; }; - D03764CC19EDA41200A782A9 /* UIControl+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIControl+RACSignalSupport.h"; sourceTree = ""; }; - D03764CD19EDA41200A782A9 /* UIControl+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIControl+RACSignalSupport.m"; sourceTree = ""; }; - D03764CE19EDA41200A782A9 /* UIControl+RACSignalSupportPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIControl+RACSignalSupportPrivate.h"; sourceTree = ""; }; - D03764CF19EDA41200A782A9 /* UIControl+RACSignalSupportPrivate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIControl+RACSignalSupportPrivate.m"; sourceTree = ""; }; - D03764D019EDA41200A782A9 /* UIDatePicker+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIDatePicker+RACSignalSupport.h"; sourceTree = ""; }; - D03764D119EDA41200A782A9 /* UIDatePicker+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIDatePicker+RACSignalSupport.m"; sourceTree = ""; }; - D03764D219EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIGestureRecognizer+RACSignalSupport.h"; sourceTree = ""; }; - D03764D319EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIGestureRecognizer+RACSignalSupport.m"; sourceTree = ""; }; - D03764D419EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImagePickerController+RACSignalSupport.h"; sourceTree = ""; }; - D03764D519EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImagePickerController+RACSignalSupport.m"; sourceTree = ""; }; - D03764D619EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIRefreshControl+RACCommandSupport.h"; sourceTree = ""; }; - D03764D719EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIRefreshControl+RACCommandSupport.m"; sourceTree = ""; }; - D03764D819EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UISegmentedControl+RACSignalSupport.h"; sourceTree = ""; }; - D03764D919EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UISegmentedControl+RACSignalSupport.m"; sourceTree = ""; }; - D03764DA19EDA41200A782A9 /* UISlider+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UISlider+RACSignalSupport.h"; sourceTree = ""; }; - D03764DB19EDA41200A782A9 /* UISlider+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UISlider+RACSignalSupport.m"; sourceTree = ""; }; - D03764DC19EDA41200A782A9 /* UIStepper+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIStepper+RACSignalSupport.h"; sourceTree = ""; }; - D03764DD19EDA41200A782A9 /* UIStepper+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIStepper+RACSignalSupport.m"; sourceTree = ""; }; - D03764DE19EDA41200A782A9 /* UISwitch+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UISwitch+RACSignalSupport.h"; sourceTree = ""; }; - D03764DF19EDA41200A782A9 /* UISwitch+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UISwitch+RACSignalSupport.m"; sourceTree = ""; }; - D03764E019EDA41200A782A9 /* UITableViewCell+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableViewCell+RACSignalSupport.h"; sourceTree = ""; }; - D03764E119EDA41200A782A9 /* UITableViewCell+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITableViewCell+RACSignalSupport.m"; sourceTree = ""; }; - D03764E219EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableViewHeaderFooterView+RACSignalSupport.h"; sourceTree = ""; }; - D03764E319EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITableViewHeaderFooterView+RACSignalSupport.m"; sourceTree = ""; }; - D03764E419EDA41200A782A9 /* UITextField+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITextField+RACSignalSupport.h"; sourceTree = ""; }; - D03764E519EDA41200A782A9 /* UITextField+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITextField+RACSignalSupport.m"; sourceTree = ""; }; - D03764E619EDA41200A782A9 /* UITextView+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITextView+RACSignalSupport.h"; sourceTree = ""; }; - D03764E719EDA41200A782A9 /* UITextView+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITextView+RACSignalSupport.m"; sourceTree = ""; }; - D037666619EDA57100A782A9 /* EXTKeyPathCoding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EXTKeyPathCoding.h; sourceTree = ""; }; - D037666719EDA57100A782A9 /* EXTRuntimeExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EXTRuntimeExtensions.h; sourceTree = ""; }; - D037666819EDA57100A782A9 /* EXTRuntimeExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EXTRuntimeExtensions.m; sourceTree = ""; }; - D037666919EDA57100A782A9 /* EXTScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EXTScope.h; sourceTree = ""; }; - D037666A19EDA57100A782A9 /* metamacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = metamacros.h; sourceTree = ""; }; - D037667619EDA60000A782A9 /* NSControllerRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSControllerRACSupportSpec.m; sourceTree = ""; }; - D037667819EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSEnumeratorRACSequenceAdditionsSpec.m; sourceTree = ""; }; - D037667919EDA60000A782A9 /* NSNotificationCenterRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSNotificationCenterRACSupportSpec.m; sourceTree = ""; }; - D037667A19EDA60000A782A9 /* NSObjectRACAppKitBindingsSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSObjectRACAppKitBindingsSpec.m; sourceTree = ""; }; - D037667B19EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSObjectRACDeallocatingSpec.m; sourceTree = ""; }; - D037667C19EDA60000A782A9 /* NSObjectRACLiftingSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSObjectRACLiftingSpec.m; sourceTree = ""; }; - D037667D19EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSObjectRACPropertySubscribingExamples.h; sourceTree = ""; }; - D037667E19EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSObjectRACPropertySubscribingExamples.m; sourceTree = ""; }; - D037667F19EDA60000A782A9 /* NSObjectRACPropertySubscribingSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSObjectRACPropertySubscribingSpec.m; sourceTree = ""; }; - D037668019EDA60000A782A9 /* NSObjectRACSelectorSignalSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSObjectRACSelectorSignalSpec.m; sourceTree = ""; }; - D037668119EDA60000A782A9 /* NSStringRACKeyPathUtilitiesSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSStringRACKeyPathUtilitiesSpec.m; sourceTree = ""; }; - D037668319EDA60000A782A9 /* NSURLConnectionRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSURLConnectionRACSupportSpec.m; sourceTree = ""; }; - D037668419EDA60000A782A9 /* NSUserDefaultsRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSUserDefaultsRACSupportSpec.m; sourceTree = ""; }; - D037668619EDA60000A782A9 /* RACBlockTrampolineSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACBlockTrampolineSpec.m; sourceTree = ""; }; - D037668719EDA60000A782A9 /* RACChannelExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACChannelExamples.h; sourceTree = ""; }; - D037668819EDA60000A782A9 /* RACChannelExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACChannelExamples.m; sourceTree = ""; }; - D037668919EDA60000A782A9 /* RACChannelSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACChannelSpec.m; sourceTree = ""; }; - D037668A19EDA60000A782A9 /* RACCommandSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACCommandSpec.m; sourceTree = ""; }; - D037668B19EDA60000A782A9 /* RACCompoundDisposableSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACCompoundDisposableSpec.m; sourceTree = ""; }; - D037668C19EDA60000A782A9 /* RACControlCommandExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACControlCommandExamples.h; sourceTree = ""; }; - D037668D19EDA60000A782A9 /* RACControlCommandExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACControlCommandExamples.m; sourceTree = ""; }; - D037668E19EDA60000A782A9 /* RACDelegateProxySpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACDelegateProxySpec.m; sourceTree = ""; }; - D037668F19EDA60000A782A9 /* RACDisposableSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACDisposableSpec.m; sourceTree = ""; }; - D037669019EDA60000A782A9 /* RACEventSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACEventSpec.m; sourceTree = ""; }; - D037669119EDA60000A782A9 /* RACKVOChannelSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACKVOChannelSpec.m; sourceTree = ""; }; - D037669219EDA60000A782A9 /* RACKVOWrapperSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACKVOWrapperSpec.m; sourceTree = ""; }; - D037669319EDA60000A782A9 /* RACMulticastConnectionSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACMulticastConnectionSpec.m; sourceTree = ""; }; - D037669419EDA60000A782A9 /* RACPropertySignalExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACPropertySignalExamples.h; sourceTree = ""; }; - D037669519EDA60000A782A9 /* RACPropertySignalExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACPropertySignalExamples.m; sourceTree = ""; }; - D037669619EDA60000A782A9 /* RACSchedulerSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSchedulerSpec.m; sourceTree = ""; }; - D037669719EDA60000A782A9 /* RACSequenceAdditionsSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSequenceAdditionsSpec.m; sourceTree = ""; }; - D037669819EDA60000A782A9 /* RACSequenceExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSequenceExamples.h; sourceTree = ""; }; - D037669919EDA60000A782A9 /* RACSequenceExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSequenceExamples.m; sourceTree = ""; }; - D037669A19EDA60000A782A9 /* RACSequenceSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSequenceSpec.m; sourceTree = ""; }; - D037669B19EDA60000A782A9 /* RACSerialDisposableSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSerialDisposableSpec.m; sourceTree = ""; }; - D037669C19EDA60000A782A9 /* RACSignalSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSignalSpec.m; sourceTree = ""; }; - D037669F19EDA60000A782A9 /* RACStreamExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACStreamExamples.h; sourceTree = ""; }; - D03766A019EDA60000A782A9 /* RACStreamExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACStreamExamples.m; sourceTree = ""; }; - D03766A119EDA60000A782A9 /* RACSubclassObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSubclassObject.h; sourceTree = ""; }; - D03766A219EDA60000A782A9 /* RACSubclassObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubclassObject.m; sourceTree = ""; }; - D03766A319EDA60000A782A9 /* RACSubjectSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubjectSpec.m; sourceTree = ""; }; - D03766A419EDA60000A782A9 /* RACSubscriberExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSubscriberExamples.h; sourceTree = ""; }; - D03766A519EDA60000A782A9 /* RACSubscriberExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubscriberExamples.m; sourceTree = ""; }; - D03766A619EDA60000A782A9 /* RACSubscriberSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubscriberSpec.m; sourceTree = ""; }; - D03766A719EDA60000A782A9 /* RACSubscriptingAssignmentTrampolineSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubscriptingAssignmentTrampolineSpec.m; sourceTree = ""; }; - D03766A819EDA60000A782A9 /* RACTargetQueueSchedulerSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTargetQueueSchedulerSpec.m; sourceTree = ""; }; - D03766B019EDA60000A782A9 /* RACTupleSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTupleSpec.m; sourceTree = ""; }; D03766B119EDA60000A782A9 /* test-data.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "test-data.json"; sourceTree = ""; }; - D03766B219EDA60000A782A9 /* UIActionSheetRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIActionSheetRACSupportSpec.m; sourceTree = ""; }; - D03766B319EDA60000A782A9 /* UIAlertViewRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIAlertViewRACSupportSpec.m; sourceTree = ""; }; - D03766B419EDA60000A782A9 /* UIBarButtonItemRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIBarButtonItemRACSupportSpec.m; sourceTree = ""; }; - D03766B519EDA60000A782A9 /* UIButtonRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIButtonRACSupportSpec.m; sourceTree = ""; }; - D03766B719EDA60000A782A9 /* UIImagePickerControllerRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIImagePickerControllerRACSupportSpec.m; sourceTree = ""; }; D037672B19EDA75D00A782A9 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Quick.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D04725EA19E49ED7006002AA /* ReactiveCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D04725EE19E49ED7006002AA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -1116,15 +188,6 @@ D047263C19E49FE8006002AA /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; D05E662419EDD82000904ACA /* Nimble.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Nimble.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = DynamicPropertySpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - D0A226101A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ObjectiveCBridgingSpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - D0C312C419EF2A5800984962 /* ObjectiveCBridging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjectiveCBridging.swift; sourceTree = ""; }; - D0C3131719EF2D9700984962 /* RACTestExampleScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTestExampleScheduler.h; sourceTree = ""; }; - D0C3131819EF2D9700984962 /* RACTestExampleScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTestExampleScheduler.m; sourceTree = ""; }; - D0C3131919EF2D9700984962 /* RACTestObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTestObject.h; sourceTree = ""; }; - D0C3131A19EF2D9700984962 /* RACTestObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTestObject.m; sourceTree = ""; }; - D0C3131B19EF2D9700984962 /* RACTestSchedulerSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTestSchedulerSpec.m; sourceTree = ""; }; - D0C3131C19EF2D9700984962 /* RACTestUIButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTestUIButton.h; sourceTree = ""; }; - D0C3131D19EF2D9700984962 /* RACTestUIButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTestUIButton.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1237,283 +300,6 @@ name = Frameworks; sourceTree = ""; }; - D037642919EDA3B600A782A9 /* Objective-C */ = { - isa = PBXGroup; - children = ( - D037666519EDA57100A782A9 /* extobjc */, - 314304151ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.h */, - 314304161ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.m */, - D037642A19EDA41200A782A9 /* NSArray+RACSequenceAdditions.h */, - D037642B19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m */, - D037642C19EDA41200A782A9 /* NSControl+RACCommandSupport.h */, - D037642D19EDA41200A782A9 /* NSControl+RACCommandSupport.m */, - D037642E19EDA41200A782A9 /* NSControl+RACTextSignalSupport.h */, - D037642F19EDA41200A782A9 /* NSControl+RACTextSignalSupport.m */, - D037643019EDA41200A782A9 /* NSData+RACSupport.h */, - D037643119EDA41200A782A9 /* NSData+RACSupport.m */, - D037643219EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h */, - D037643319EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m */, - D037643419EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h */, - D037643519EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m */, - D037643619EDA41200A782A9 /* NSFileHandle+RACSupport.h */, - D037643719EDA41200A782A9 /* NSFileHandle+RACSupport.m */, - D037643819EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h */, - D037643919EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m */, - D037643A19EDA41200A782A9 /* NSInvocation+RACTypeParsing.h */, - D037643B19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m */, - D037643C19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h */, - D037643D19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m */, - D037643E19EDA41200A782A9 /* NSObject+RACAppKitBindings.h */, - D037643F19EDA41200A782A9 /* NSObject+RACAppKitBindings.m */, - D037644019EDA41200A782A9 /* NSObject+RACDeallocating.h */, - D037644119EDA41200A782A9 /* NSObject+RACDeallocating.m */, - D037644219EDA41200A782A9 /* NSObject+RACDescription.h */, - D037644319EDA41200A782A9 /* NSObject+RACDescription.m */, - D037644419EDA41200A782A9 /* NSObject+RACKVOWrapper.h */, - D037644519EDA41200A782A9 /* NSObject+RACKVOWrapper.m */, - D037644619EDA41200A782A9 /* NSObject+RACLifting.h */, - D037644719EDA41200A782A9 /* NSObject+RACLifting.m */, - D037644819EDA41200A782A9 /* NSObject+RACPropertySubscribing.h */, - D037644919EDA41200A782A9 /* NSObject+RACPropertySubscribing.m */, - D037644A19EDA41200A782A9 /* NSObject+RACSelectorSignal.h */, - D037644B19EDA41200A782A9 /* NSObject+RACSelectorSignal.m */, - D037644C19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h */, - D037644D19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m */, - D037644E19EDA41200A782A9 /* NSSet+RACSequenceAdditions.h */, - D037644F19EDA41200A782A9 /* NSSet+RACSequenceAdditions.m */, - D037645019EDA41200A782A9 /* NSString+RACKeyPathUtilities.h */, - D037645119EDA41200A782A9 /* NSString+RACKeyPathUtilities.m */, - D037645219EDA41200A782A9 /* NSString+RACSequenceAdditions.h */, - D037645319EDA41200A782A9 /* NSString+RACSequenceAdditions.m */, - D037645419EDA41200A782A9 /* NSString+RACSupport.h */, - D037645519EDA41200A782A9 /* NSString+RACSupport.m */, - D037645619EDA41200A782A9 /* NSText+RACSignalSupport.h */, - D037645719EDA41200A782A9 /* NSText+RACSignalSupport.m */, - D037645819EDA41200A782A9 /* NSURLConnection+RACSupport.h */, - D037645919EDA41200A782A9 /* NSURLConnection+RACSupport.m */, - D037645A19EDA41200A782A9 /* NSUserDefaults+RACSupport.h */, - D037645B19EDA41200A782A9 /* NSUserDefaults+RACSupport.m */, - D037645C19EDA41200A782A9 /* RACArraySequence.h */, - D037645D19EDA41200A782A9 /* RACArraySequence.m */, - D037646019EDA41200A782A9 /* RACBehaviorSubject.h */, - D037646119EDA41200A782A9 /* RACBehaviorSubject.m */, - D037646219EDA41200A782A9 /* RACBlockTrampoline.h */, - D037646319EDA41200A782A9 /* RACBlockTrampoline.m */, - D037646419EDA41200A782A9 /* RACChannel.h */, - D037646519EDA41200A782A9 /* RACChannel.m */, - D037646619EDA41200A782A9 /* RACCommand.h */, - D037646719EDA41200A782A9 /* RACCommand.m */, - D037646819EDA41200A782A9 /* RACCompoundDisposable.h */, - D037646919EDA41200A782A9 /* RACCompoundDisposable.m */, - D037646A19EDA41200A782A9 /* RACCompoundDisposableProvider.d */, - D037646B19EDA41200A782A9 /* RACDelegateProxy.h */, - D037646C19EDA41200A782A9 /* RACDelegateProxy.m */, - D037646D19EDA41200A782A9 /* RACDisposable.h */, - D037646E19EDA41200A782A9 /* RACDisposable.m */, - D037646F19EDA41200A782A9 /* RACDynamicSequence.h */, - D037647019EDA41200A782A9 /* RACDynamicSequence.m */, - D037647119EDA41200A782A9 /* RACDynamicSignal.h */, - D037647219EDA41200A782A9 /* RACDynamicSignal.m */, - D037647319EDA41200A782A9 /* RACEagerSequence.h */, - D037647419EDA41200A782A9 /* RACEagerSequence.m */, - D037647519EDA41200A782A9 /* RACEmptySequence.h */, - D037647619EDA41200A782A9 /* RACEmptySequence.m */, - D037647719EDA41200A782A9 /* RACEmptySignal.h */, - D037647819EDA41200A782A9 /* RACEmptySignal.m */, - D037647919EDA41200A782A9 /* RACErrorSignal.h */, - D037647A19EDA41200A782A9 /* RACErrorSignal.m */, - D037647B19EDA41200A782A9 /* RACEvent.h */, - D037647C19EDA41200A782A9 /* RACEvent.m */, - D037647D19EDA41200A782A9 /* RACGroupedSignal.h */, - D037647E19EDA41200A782A9 /* RACGroupedSignal.m */, - D037647F19EDA41200A782A9 /* RACImmediateScheduler.h */, - D037648019EDA41200A782A9 /* RACImmediateScheduler.m */, - D037648119EDA41200A782A9 /* RACIndexSetSequence.h */, - D037648219EDA41200A782A9 /* RACIndexSetSequence.m */, - D037648319EDA41200A782A9 /* RACKVOChannel.h */, - D037648419EDA41200A782A9 /* RACKVOChannel.m */, - 7A70657D1A3F88B8001E8354 /* RACKVOProxy.h */, - 7A70657E1A3F88B8001E8354 /* RACKVOProxy.m */, - D037648519EDA41200A782A9 /* RACKVOTrampoline.h */, - D037648619EDA41200A782A9 /* RACKVOTrampoline.m */, - D037648719EDA41200A782A9 /* RACMulticastConnection.h */, - D037648819EDA41200A782A9 /* RACMulticastConnection.m */, - D037648919EDA41200A782A9 /* RACMulticastConnection+Private.h */, - D037648C19EDA41200A782A9 /* RACPassthroughSubscriber.h */, - D037648D19EDA41200A782A9 /* RACPassthroughSubscriber.m */, - D037648E19EDA41200A782A9 /* RACQueueScheduler.h */, - D037648F19EDA41200A782A9 /* RACQueueScheduler.m */, - D037649019EDA41200A782A9 /* RACQueueScheduler+Subclass.h */, - D037649119EDA41200A782A9 /* RACReplaySubject.h */, - D037649219EDA41200A782A9 /* RACReplaySubject.m */, - D037649319EDA41200A782A9 /* RACReturnSignal.h */, - D037649419EDA41200A782A9 /* RACReturnSignal.m */, - D037649519EDA41200A782A9 /* RACScheduler.h */, - D037649619EDA41200A782A9 /* RACScheduler.m */, - D037649719EDA41200A782A9 /* RACScheduler+Private.h */, - D037649819EDA41200A782A9 /* RACScheduler+Subclass.h */, - D037649919EDA41200A782A9 /* RACScopedDisposable.h */, - D037649A19EDA41200A782A9 /* RACScopedDisposable.m */, - D037649B19EDA41200A782A9 /* RACSequence.h */, - D037649C19EDA41200A782A9 /* RACSequence.m */, - D037649D19EDA41200A782A9 /* RACSerialDisposable.h */, - D037649E19EDA41200A782A9 /* RACSerialDisposable.m */, - D037649F19EDA41200A782A9 /* RACSignal.h */, - D03764A019EDA41200A782A9 /* RACSignal.m */, - D03764A119EDA41200A782A9 /* RACSignal+Operations.h */, - D03764A219EDA41200A782A9 /* RACSignal+Operations.m */, - D03764A319EDA41200A782A9 /* RACSignalProvider.d */, - D03764A419EDA41200A782A9 /* RACSignalSequence.h */, - D03764A519EDA41200A782A9 /* RACSignalSequence.m */, - D03764A619EDA41200A782A9 /* RACStream.h */, - D03764A719EDA41200A782A9 /* RACStream.m */, - D03764A819EDA41200A782A9 /* RACStream+Private.h */, - D03764A919EDA41200A782A9 /* RACStringSequence.h */, - D03764AA19EDA41200A782A9 /* RACStringSequence.m */, - D03764AB19EDA41200A782A9 /* RACSubject.h */, - D03764AC19EDA41200A782A9 /* RACSubject.m */, - D03764AD19EDA41200A782A9 /* RACSubscriber.h */, - D03764AE19EDA41200A782A9 /* RACSubscriber.m */, - D03764AF19EDA41200A782A9 /* RACSubscriber+Private.h */, - D03764B019EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h */, - D03764B119EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m */, - D03764B219EDA41200A782A9 /* RACSubscriptionScheduler.h */, - D03764B319EDA41200A782A9 /* RACSubscriptionScheduler.m */, - D03764B419EDA41200A782A9 /* RACTargetQueueScheduler.h */, - D03764B519EDA41200A782A9 /* RACTargetQueueScheduler.m */, - D03764B619EDA41200A782A9 /* RACTestScheduler.h */, - D03764B719EDA41200A782A9 /* RACTestScheduler.m */, - D03764B819EDA41200A782A9 /* RACTuple.h */, - D03764B919EDA41200A782A9 /* RACTuple.m */, - D03764BA19EDA41200A782A9 /* RACTupleSequence.h */, - D03764BB19EDA41200A782A9 /* RACTupleSequence.m */, - D03764BC19EDA41200A782A9 /* RACUnarySequence.h */, - D03764BD19EDA41200A782A9 /* RACUnarySequence.m */, - D03764BE19EDA41200A782A9 /* RACUnit.h */, - D03764BF19EDA41200A782A9 /* RACUnit.m */, - D03764C019EDA41200A782A9 /* RACValueTransformer.h */, - D03764C119EDA41200A782A9 /* RACValueTransformer.m */, - D03764C219EDA41200A782A9 /* UIActionSheet+RACSignalSupport.h */, - D03764C319EDA41200A782A9 /* UIActionSheet+RACSignalSupport.m */, - D03764C419EDA41200A782A9 /* UIAlertView+RACSignalSupport.h */, - D03764C519EDA41200A782A9 /* UIAlertView+RACSignalSupport.m */, - D03764C619EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.h */, - D03764C719EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.m */, - D03764C819EDA41200A782A9 /* UIButton+RACCommandSupport.h */, - D03764C919EDA41200A782A9 /* UIButton+RACCommandSupport.m */, - D03764CA19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.h */, - D03764CB19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.m */, - D03764CC19EDA41200A782A9 /* UIControl+RACSignalSupport.h */, - D03764CD19EDA41200A782A9 /* UIControl+RACSignalSupport.m */, - D03764CE19EDA41200A782A9 /* UIControl+RACSignalSupportPrivate.h */, - D03764CF19EDA41200A782A9 /* UIControl+RACSignalSupportPrivate.m */, - D03764D019EDA41200A782A9 /* UIDatePicker+RACSignalSupport.h */, - D03764D119EDA41200A782A9 /* UIDatePicker+RACSignalSupport.m */, - D03764D219EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.h */, - D03764D319EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.m */, - D03764D419EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.h */, - D03764D519EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.m */, - D03764D619EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.h */, - D03764D719EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.m */, - D03764D819EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.h */, - D03764D919EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.m */, - D03764DA19EDA41200A782A9 /* UISlider+RACSignalSupport.h */, - D03764DB19EDA41200A782A9 /* UISlider+RACSignalSupport.m */, - D03764DC19EDA41200A782A9 /* UIStepper+RACSignalSupport.h */, - D03764DD19EDA41200A782A9 /* UIStepper+RACSignalSupport.m */, - D03764DE19EDA41200A782A9 /* UISwitch+RACSignalSupport.h */, - D03764DF19EDA41200A782A9 /* UISwitch+RACSignalSupport.m */, - D03764E019EDA41200A782A9 /* UITableViewCell+RACSignalSupport.h */, - D03764E119EDA41200A782A9 /* UITableViewCell+RACSignalSupport.m */, - D03764E219EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.h */, - D03764E319EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.m */, - D03764E419EDA41200A782A9 /* UITextField+RACSignalSupport.h */, - D03764E519EDA41200A782A9 /* UITextField+RACSignalSupport.m */, - D03764E619EDA41200A782A9 /* UITextView+RACSignalSupport.h */, - D03764E719EDA41200A782A9 /* UITextView+RACSignalSupport.m */, - ); - path = "Objective-C"; - sourceTree = ""; - }; - D037666519EDA57100A782A9 /* extobjc */ = { - isa = PBXGroup; - children = ( - D037666619EDA57100A782A9 /* EXTKeyPathCoding.h */, - D037666719EDA57100A782A9 /* EXTRuntimeExtensions.h */, - D037666819EDA57100A782A9 /* EXTRuntimeExtensions.m */, - D037666919EDA57100A782A9 /* EXTScope.h */, - D037666A19EDA57100A782A9 /* metamacros.h */, - ); - path = extobjc; - sourceTree = ""; - }; - D037667519EDA5D900A782A9 /* Objective-C */ = { - isa = PBXGroup; - children = ( - D037667619EDA60000A782A9 /* NSControllerRACSupportSpec.m */, - D037667819EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m */, - D037667919EDA60000A782A9 /* NSNotificationCenterRACSupportSpec.m */, - D037667A19EDA60000A782A9 /* NSObjectRACAppKitBindingsSpec.m */, - D037667B19EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m */, - D037667C19EDA60000A782A9 /* NSObjectRACLiftingSpec.m */, - D037667D19EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.h */, - D037667E19EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.m */, - D037667F19EDA60000A782A9 /* NSObjectRACPropertySubscribingSpec.m */, - D037668019EDA60000A782A9 /* NSObjectRACSelectorSignalSpec.m */, - D037668119EDA60000A782A9 /* NSStringRACKeyPathUtilitiesSpec.m */, - D037668319EDA60000A782A9 /* NSURLConnectionRACSupportSpec.m */, - D037668419EDA60000A782A9 /* NSUserDefaultsRACSupportSpec.m */, - D037668619EDA60000A782A9 /* RACBlockTrampolineSpec.m */, - D037668719EDA60000A782A9 /* RACChannelExamples.h */, - D037668819EDA60000A782A9 /* RACChannelExamples.m */, - D037668919EDA60000A782A9 /* RACChannelSpec.m */, - D037668A19EDA60000A782A9 /* RACCommandSpec.m */, - D037668B19EDA60000A782A9 /* RACCompoundDisposableSpec.m */, - D037668C19EDA60000A782A9 /* RACControlCommandExamples.h */, - D037668D19EDA60000A782A9 /* RACControlCommandExamples.m */, - D037668E19EDA60000A782A9 /* RACDelegateProxySpec.m */, - D037668F19EDA60000A782A9 /* RACDisposableSpec.m */, - D037669019EDA60000A782A9 /* RACEventSpec.m */, - D037669119EDA60000A782A9 /* RACKVOChannelSpec.m */, - 7A7065831A3F8967001E8354 /* RACKVOProxySpec.m */, - D037669219EDA60000A782A9 /* RACKVOWrapperSpec.m */, - D037669319EDA60000A782A9 /* RACMulticastConnectionSpec.m */, - D037669419EDA60000A782A9 /* RACPropertySignalExamples.h */, - D037669519EDA60000A782A9 /* RACPropertySignalExamples.m */, - D037669619EDA60000A782A9 /* RACSchedulerSpec.m */, - D037669719EDA60000A782A9 /* RACSequenceAdditionsSpec.m */, - D037669819EDA60000A782A9 /* RACSequenceExamples.h */, - D037669919EDA60000A782A9 /* RACSequenceExamples.m */, - D037669A19EDA60000A782A9 /* RACSequenceSpec.m */, - D037669B19EDA60000A782A9 /* RACSerialDisposableSpec.m */, - D037669C19EDA60000A782A9 /* RACSignalSpec.m */, - D037669F19EDA60000A782A9 /* RACStreamExamples.h */, - D03766A019EDA60000A782A9 /* RACStreamExamples.m */, - D03766A119EDA60000A782A9 /* RACSubclassObject.h */, - D03766A219EDA60000A782A9 /* RACSubclassObject.m */, - D03766A319EDA60000A782A9 /* RACSubjectSpec.m */, - D03766A419EDA60000A782A9 /* RACSubscriberExamples.h */, - D03766A519EDA60000A782A9 /* RACSubscriberExamples.m */, - D03766A619EDA60000A782A9 /* RACSubscriberSpec.m */, - D03766A719EDA60000A782A9 /* RACSubscriptingAssignmentTrampolineSpec.m */, - D03766A819EDA60000A782A9 /* RACTargetQueueSchedulerSpec.m */, - D03766B019EDA60000A782A9 /* RACTupleSpec.m */, - D03766B219EDA60000A782A9 /* UIActionSheetRACSupportSpec.m */, - D03766B319EDA60000A782A9 /* UIAlertViewRACSupportSpec.m */, - D03766B419EDA60000A782A9 /* UIBarButtonItemRACSupportSpec.m */, - D03766B519EDA60000A782A9 /* UIButtonRACSupportSpec.m */, - D03766B719EDA60000A782A9 /* UIImagePickerControllerRACSupportSpec.m */, - D0C3131719EF2D9700984962 /* RACTestExampleScheduler.h */, - D0C3131819EF2D9700984962 /* RACTestExampleScheduler.m */, - D0C3131919EF2D9700984962 /* RACTestObject.h */, - D0C3131A19EF2D9700984962 /* RACTestObject.m */, - D0C3131B19EF2D9700984962 /* RACTestSchedulerSpec.m */, - D0C3131C19EF2D9700984962 /* RACTestUIButton.h */, - D0C3131D19EF2D9700984962 /* RACTestUIButton.m */, - ); - path = "Objective-C"; - sourceTree = ""; - }; D04725E019E49ED7006002AA = { isa = PBXGroup; children = ( @@ -1544,8 +330,10 @@ isa = PBXGroup; children = ( D04725EF19E49ED7006002AA /* ReactiveCocoa.h */, - D0C312B919EF2A3000984962 /* Swift */, - D037642919EDA3B600A782A9 /* Objective-C */, + C7142DBB1CDEA167009F402D /* CocoaAction.swift */, + CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */, + 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */, + 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */, D04725ED19E49ED7006002AA /* Supporting Files */, ); path = ReactiveCocoa; @@ -1563,8 +351,10 @@ D04725F919E49ED7006002AA /* ReactiveCocoaTests */ = { isa = PBXGroup; children = ( - D0C312ED19EF2A6F00984962 /* Swift */, - D037667519EDA5D900A782A9 /* Objective-C */, + CD8401821CEE8ED7009F0ABF /* CocoaActionSpec.swift */, + D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */, + B696FB801A7640C00075236D /* TestError.swift */, + 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */, D04725FA19E49ED7006002AA /* Supporting Files */, ); path = ReactiveCocoaTests; @@ -1650,30 +440,6 @@ path = "Mac OS X"; sourceTree = ""; }; - D0C312B919EF2A3000984962 /* Swift */ = { - isa = PBXGroup; - children = ( - C7142DBB1CDEA167009F402D /* CocoaAction.swift */, - CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */, - 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */, - 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */, - D0C312C419EF2A5800984962 /* ObjectiveCBridging.swift */, - ); - path = Swift; - sourceTree = ""; - }; - D0C312ED19EF2A6F00984962 /* Swift */ = { - isa = PBXGroup; - children = ( - CD8401821CEE8ED7009F0ABF /* CocoaActionSpec.swift */, - D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */, - D0A226101A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift */, - B696FB801A7640C00075236D /* TestError.swift */, - 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */, - ); - path = Swift; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -1682,64 +448,6 @@ buildActionMask = 2147483647; files = ( 57A4D20A1BA13D7A00F7D4B1 /* ReactiveCocoa.h in Headers */, - 57A4D20B1BA13D7A00F7D4B1 /* EXTKeyPathCoding.h in Headers */, - A1046B7D1BFF5664004D8045 /* EXTRuntimeExtensions.h in Headers */, - 57A4D20C1BA13D7A00F7D4B1 /* EXTScope.h in Headers */, - 57A4D20D1BA13D7A00F7D4B1 /* metamacros.h in Headers */, - 57A4D20E1BA13D7A00F7D4B1 /* NSArray+RACSequenceAdditions.h in Headers */, - 57A4D20F1BA13D7A00F7D4B1 /* NSData+RACSupport.h in Headers */, - BEBDD6E51CDC292D009A75A9 /* RACDelegateProxy.h in Headers */, - 57A4D2101BA13D7A00F7D4B1 /* NSDictionary+RACSequenceAdditions.h in Headers */, - 57A4D2111BA13D7A00F7D4B1 /* NSEnumerator+RACSequenceAdditions.h in Headers */, - 57A4D2121BA13D7A00F7D4B1 /* NSFileHandle+RACSupport.h in Headers */, - 57A4D2131BA13D7A00F7D4B1 /* NSIndexSet+RACSequenceAdditions.h in Headers */, - 57A4D2141BA13D7A00F7D4B1 /* NSNotificationCenter+RACSupport.h in Headers */, - 57A4D2151BA13D7A00F7D4B1 /* NSObject+RACDeallocating.h in Headers */, - 57A4D2161BA13D7A00F7D4B1 /* NSObject+RACLifting.h in Headers */, - 57A4D2171BA13D7A00F7D4B1 /* NSObject+RACPropertySubscribing.h in Headers */, - 57DC89A01C5066D400E367B7 /* UIGestureRecognizer+RACSignalSupport.h in Headers */, - 57A4D2181BA13D7A00F7D4B1 /* NSObject+RACSelectorSignal.h in Headers */, - 57A4D2191BA13D7A00F7D4B1 /* NSOrderedSet+RACSequenceAdditions.h in Headers */, - 57A4D21A1BA13D7A00F7D4B1 /* NSSet+RACSequenceAdditions.h in Headers */, - 57A4D21B1BA13D7A00F7D4B1 /* NSString+RACSequenceAdditions.h in Headers */, - 57A4D21C1BA13D7A00F7D4B1 /* NSString+RACSupport.h in Headers */, - 57A4D21E1BA13D7A00F7D4B1 /* NSUserDefaults+RACSupport.h in Headers */, - 57A4D21F1BA13D7A00F7D4B1 /* RACBehaviorSubject.h in Headers */, - 57A4D2201BA13D7A00F7D4B1 /* RACChannel.h in Headers */, - 57A4D2211BA13D7A00F7D4B1 /* RACCommand.h in Headers */, - 57A4D2221BA13D7A00F7D4B1 /* RACCompoundDisposable.h in Headers */, - 57A4D2231BA13D7A00F7D4B1 /* RACDisposable.h in Headers */, - 57A4D2241BA13D7A00F7D4B1 /* RACEvent.h in Headers */, - 7DFBED6F1CDB926400EE435B /* UIBarButtonItem+RACCommandSupport.h in Headers */, - 57A4D2251BA13D7A00F7D4B1 /* RACGroupedSignal.h in Headers */, - 57A4D2261BA13D7A00F7D4B1 /* RACKVOChannel.h in Headers */, - 57A4D2271BA13D7A00F7D4B1 /* RACMulticastConnection.h in Headers */, - 57A4D2281BA13D7A00F7D4B1 /* RACQueueScheduler.h in Headers */, - 57DC89A51C50675700E367B7 /* UITextField+RACSignalSupport.h in Headers */, - 57A4D2291BA13D7A00F7D4B1 /* RACQueueScheduler+Subclass.h in Headers */, - 57A4D22A1BA13D7A00F7D4B1 /* RACReplaySubject.h in Headers */, - 57DC89A21C50673C00E367B7 /* UISegmentedControl+RACSignalSupport.h in Headers */, - 57A4D22B1BA13D7A00F7D4B1 /* RACScheduler.h in Headers */, - 57DC89A41C50674D00E367B7 /* UITableViewHeaderFooterView+RACSignalSupport.h in Headers */, - 57A4D22C1BA13D7A00F7D4B1 /* RACScheduler+Subclass.h in Headers */, - 57DC89A11C50672B00E367B7 /* UIControl+RACSignalSupport.h in Headers */, - 57A4D22D1BA13D7A00F7D4B1 /* RACScopedDisposable.h in Headers */, - 57DC89A31C50674300E367B7 /* UITableViewCell+RACSignalSupport.h in Headers */, - 57A4D22E1BA13D7A00F7D4B1 /* RACSequence.h in Headers */, - 57A4D22F1BA13D7A00F7D4B1 /* RACSerialDisposable.h in Headers */, - 57A4D2301BA13D7A00F7D4B1 /* RACSignal.h in Headers */, - 57DC89A81C50679E00E367B7 /* UICollectionReusableView+RACSignalSupport.h in Headers */, - 57DC89A71C50679700E367B7 /* UIButton+RACCommandSupport.h in Headers */, - 57A4D2311BA13D7A00F7D4B1 /* RACSignal+Operations.h in Headers */, - 57A4D2321BA13D7A00F7D4B1 /* RACStream.h in Headers */, - 57A4D2331BA13D7A00F7D4B1 /* RACSubject.h in Headers */, - 57A4D2341BA13D7A00F7D4B1 /* RACSubscriber.h in Headers */, - 57A4D2351BA13D7A00F7D4B1 /* RACSubscriptingAssignmentTrampoline.h in Headers */, - 57A4D2361BA13D7A00F7D4B1 /* RACTargetQueueScheduler.h in Headers */, - 57A4D2371BA13D7A00F7D4B1 /* RACTestScheduler.h in Headers */, - 57A4D2381BA13D7A00F7D4B1 /* RACTuple.h in Headers */, - 57DC89A61C50675F00E367B7 /* UITextView+RACSignalSupport.h in Headers */, - 57A4D2391BA13D7A00F7D4B1 /* RACUnit.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1748,54 +456,6 @@ buildActionMask = 2147483647; files = ( A9B315CA1B3940AB0001CB9C /* ReactiveCocoa.h in Headers */, - A9B315CB1B3940AB0001CB9C /* EXTKeyPathCoding.h in Headers */, - A1046B7C1BFF5662004D8045 /* EXTRuntimeExtensions.h in Headers */, - A9B315CD1B3940AB0001CB9C /* EXTScope.h in Headers */, - A9B315CE1B3940AB0001CB9C /* metamacros.h in Headers */, - A9B315D01B3940AB0001CB9C /* NSArray+RACSequenceAdditions.h in Headers */, - A9B315D31B3940AB0001CB9C /* NSData+RACSupport.h in Headers */, - A9B315D41B3940AB0001CB9C /* NSDictionary+RACSequenceAdditions.h in Headers */, - A9B315D51B3940AB0001CB9C /* NSEnumerator+RACSequenceAdditions.h in Headers */, - A9B315D61B3940AB0001CB9C /* NSFileHandle+RACSupport.h in Headers */, - A9B315D71B3940AB0001CB9C /* NSIndexSet+RACSequenceAdditions.h in Headers */, - A9B315D91B3940AB0001CB9C /* NSNotificationCenter+RACSupport.h in Headers */, - A9B315DB1B3940AB0001CB9C /* NSObject+RACDeallocating.h in Headers */, - A9B315DE1B3940AB0001CB9C /* NSObject+RACLifting.h in Headers */, - A9B315DF1B3940AB0001CB9C /* NSObject+RACPropertySubscribing.h in Headers */, - A9B315E01B3940AB0001CB9C /* NSObject+RACSelectorSignal.h in Headers */, - A9B315E11B3940AB0001CB9C /* NSOrderedSet+RACSequenceAdditions.h in Headers */, - A9B315E21B3940AB0001CB9C /* NSSet+RACSequenceAdditions.h in Headers */, - A9B315E41B3940AB0001CB9C /* NSString+RACSequenceAdditions.h in Headers */, - A9B315E51B3940AB0001CB9C /* NSString+RACSupport.h in Headers */, - A9B315E81B3940AB0001CB9C /* NSUserDefaults+RACSupport.h in Headers */, - A9B315EA1B3940AB0001CB9C /* RACBehaviorSubject.h in Headers */, - A9B315EC1B3940AB0001CB9C /* RACChannel.h in Headers */, - A9B315ED1B3940AC0001CB9C /* RACCommand.h in Headers */, - A9B315EE1B3940AC0001CB9C /* RACCompoundDisposable.h in Headers */, - A9B315F01B3940AC0001CB9C /* RACDisposable.h in Headers */, - A9B315F71B3940AC0001CB9C /* RACEvent.h in Headers */, - BEBDD6E61CDC292D009A75A9 /* RACDelegateProxy.h in Headers */, - A9B315F81B3940AC0001CB9C /* RACGroupedSignal.h in Headers */, - A9B315FB1B3940AC0001CB9C /* RACKVOChannel.h in Headers */, - A9B315FE1B3940AC0001CB9C /* RACMulticastConnection.h in Headers */, - A9B316021B3940AD0001CB9C /* RACQueueScheduler.h in Headers */, - A9B316031B3940AD0001CB9C /* RACQueueScheduler+Subclass.h in Headers */, - A9B316041B3940AD0001CB9C /* RACReplaySubject.h in Headers */, - A9B316061B3940AD0001CB9C /* RACScheduler.h in Headers */, - A9B316081B3940AD0001CB9C /* RACScheduler+Subclass.h in Headers */, - A9B316091B3940AD0001CB9C /* RACScopedDisposable.h in Headers */, - A9B3160A1B3940AD0001CB9C /* RACSequence.h in Headers */, - A9B3160B1B3940AD0001CB9C /* RACSerialDisposable.h in Headers */, - A9B3160C1B3940AE0001CB9C /* RACSignal.h in Headers */, - A9B3160D1B3940AE0001CB9C /* RACSignal+Operations.h in Headers */, - A9B3160F1B3940AE0001CB9C /* RACStream.h in Headers */, - A9B316121B3940AE0001CB9C /* RACSubject.h in Headers */, - A9B316131B3940AE0001CB9C /* RACSubscriber.h in Headers */, - A9B316151B3940AE0001CB9C /* RACSubscriptingAssignmentTrampoline.h in Headers */, - A9B316171B3940AF0001CB9C /* RACTargetQueueScheduler.h in Headers */, - A9B316181B3940AF0001CB9C /* RACTestScheduler.h in Headers */, - A9B316191B3940AF0001CB9C /* RACTuple.h in Headers */, - A9B3161C1B3940AF0001CB9C /* RACUnit.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1804,59 +464,6 @@ buildActionMask = 2147483647; files = ( D04725F019E49ED7006002AA /* ReactiveCocoa.h in Headers */, - D037652019EDA41200A782A9 /* NSObject+RACLifting.h in Headers */, - D03764EC19EDA41200A782A9 /* NSControl+RACCommandSupport.h in Headers */, - D037655C19EDA41200A782A9 /* RACChannel.h in Headers */, - D03765EE19EDA41200A782A9 /* RACSubscriber.h in Headers */, - D03765D219EDA41200A782A9 /* RACSignal.h in Headers */, - D037650419EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h in Headers */, - D037659A19EDA41200A782A9 /* RACKVOChannel.h in Headers */, - D037651419EDA41200A782A9 /* NSObject+RACDeallocating.h in Headers */, - D037650C19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h in Headers */, - D037667319EDA57100A782A9 /* metamacros.h in Headers */, - D037666B19EDA57100A782A9 /* EXTKeyPathCoding.h in Headers */, - D03765F419EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h in Headers */, - D03765C419EDA41200A782A9 /* RACScheduler+Subclass.h in Headers */, - D037656E19EDA41200A782A9 /* RACDisposable.h in Headers */, - D03765B019EDA41200A782A9 /* RACQueueScheduler.h in Headers */, - BEBDD6E81CDC292F009A75A9 /* RACDelegateProxy.h in Headers */, - D037652419EDA41200A782A9 /* NSObject+RACPropertySubscribing.h in Headers */, - D037650019EDA41200A782A9 /* NSFileHandle+RACSupport.h in Headers */, - D037653019EDA41200A782A9 /* NSSet+RACSequenceAdditions.h in Headers */, - D037654019EDA41200A782A9 /* NSText+RACSignalSupport.h in Headers */, - D03765E019EDA41200A782A9 /* RACStream.h in Headers */, - D03765FC19EDA41200A782A9 /* RACTargetQueueScheduler.h in Headers */, - D03765B419EDA41200A782A9 /* RACQueueScheduler+Subclass.h in Headers */, - D037661019EDA41200A782A9 /* RACUnit.h in Headers */, - D037656419EDA41200A782A9 /* RACCompoundDisposable.h in Headers */, - D03764F419EDA41200A782A9 /* NSData+RACSupport.h in Headers */, - D03764FC19EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h in Headers */, - D03765CA19EDA41200A782A9 /* RACSequence.h in Headers */, - D037672719EDA63400A782A9 /* RACBehaviorSubject.h in Headers */, - D037653C19EDA41200A782A9 /* NSString+RACSupport.h in Headers */, - D03765CE19EDA41200A782A9 /* RACSerialDisposable.h in Headers */, - D03765D619EDA41200A782A9 /* RACSignal+Operations.h in Headers */, - D03765B619EDA41200A782A9 /* RACReplaySubject.h in Headers */, - D03765A219EDA41200A782A9 /* RACMulticastConnection.h in Headers */, - D037658E19EDA41200A782A9 /* RACGroupedSignal.h in Headers */, - D037654819EDA41200A782A9 /* NSUserDefaults+RACSupport.h in Headers */, - D03765BE19EDA41200A782A9 /* RACScheduler.h in Headers */, - D037656019EDA41200A782A9 /* RACCommand.h in Headers */, - D037660419EDA41200A782A9 /* RACTuple.h in Headers */, - D03765C619EDA41200A782A9 /* RACScopedDisposable.h in Headers */, - D037660019EDA41200A782A9 /* RACTestScheduler.h in Headers */, - D037652C19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h in Headers */, - D03764F019EDA41200A782A9 /* NSControl+RACTextSignalSupport.h in Headers */, - D03765EA19EDA41200A782A9 /* RACSubject.h in Headers */, - A1046B7A1BFF5661004D8045 /* EXTRuntimeExtensions.h in Headers */, - D037652819EDA41200A782A9 /* NSObject+RACSelectorSignal.h in Headers */, - D037654419EDA41200A782A9 /* NSURLConnection+RACSupport.h in Headers */, - D03764E819EDA41200A782A9 /* NSArray+RACSequenceAdditions.h in Headers */, - D037651019EDA41200A782A9 /* NSObject+RACAppKitBindings.h in Headers */, - D037658A19EDA41200A782A9 /* RACEvent.h in Headers */, - D037667119EDA57100A782A9 /* EXTScope.h in Headers */, - D037653819EDA41200A782A9 /* NSString+RACSequenceAdditions.h in Headers */, - D03764F819EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1864,75 +471,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - D037664519EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.h in Headers */, D037666419EDA43C00A782A9 /* ReactiveCocoa.h in Headers */, - D037652519EDA41200A782A9 /* NSObject+RACPropertySubscribing.h in Headers */, - D03765B119EDA41200A782A9 /* RACQueueScheduler.h in Headers */, - D037662519EDA41200A782A9 /* UIButton+RACCommandSupport.h in Headers */, - D037672819EDA63500A782A9 /* RACBehaviorSubject.h in Headers */, - D037660119EDA41200A782A9 /* RACTestScheduler.h in Headers */, - D03765A319EDA41200A782A9 /* RACMulticastConnection.h in Headers */, - D03765B719EDA41200A782A9 /* RACReplaySubject.h in Headers */, - D037663D19EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.h in Headers */, - D037656F19EDA41200A782A9 /* RACDisposable.h in Headers */, - 314304171ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.h in Headers */, - D037654519EDA41200A782A9 /* NSURLConnection+RACSupport.h in Headers */, - D037661D19EDA41200A782A9 /* UIAlertView+RACSignalSupport.h in Headers */, - D037650D19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h in Headers */, - D037650119EDA41200A782A9 /* NSFileHandle+RACSupport.h in Headers */, - D037666119EDA41200A782A9 /* UITextView+RACSignalSupport.h in Headers */, - D037659B19EDA41200A782A9 /* RACKVOChannel.h in Headers */, - D037652D19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h in Headers */, - D03764F919EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h in Headers */, - D037667219EDA57100A782A9 /* EXTScope.h in Headers */, - D037663519EDA41200A782A9 /* UIDatePicker+RACSignalSupport.h in Headers */, - D037667419EDA57100A782A9 /* metamacros.h in Headers */, - D03764FD19EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h in Headers */, - D037652119EDA41200A782A9 /* NSObject+RACLifting.h in Headers */, - D037665919EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.h in Headers */, - D037656519EDA41200A782A9 /* RACCompoundDisposable.h in Headers */, - D037653D19EDA41200A782A9 /* NSString+RACSupport.h in Headers */, - D037662919EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.h in Headers */, - D037660519EDA41200A782A9 /* RACTuple.h in Headers */, - D037665519EDA41200A782A9 /* UITableViewCell+RACSignalSupport.h in Headers */, - D03764F519EDA41200A782A9 /* NSData+RACSupport.h in Headers */, - D037653919EDA41200A782A9 /* NSString+RACSequenceAdditions.h in Headers */, - D037651519EDA41200A782A9 /* NSObject+RACDeallocating.h in Headers */, - D037658F19EDA41200A782A9 /* RACGroupedSignal.h in Headers */, - D03765C519EDA41200A782A9 /* RACScheduler+Subclass.h in Headers */, - D03765E119EDA41200A782A9 /* RACStream.h in Headers */, - D03765D719EDA41200A782A9 /* RACSignal+Operations.h in Headers */, - D037665D19EDA41200A782A9 /* UITextField+RACSignalSupport.h in Headers */, - D037664919EDA41200A782A9 /* UISlider+RACSignalSupport.h in Headers */, - D03765BF19EDA41200A782A9 /* RACScheduler.h in Headers */, - D03764E919EDA41200A782A9 /* NSArray+RACSequenceAdditions.h in Headers */, - D037654919EDA41200A782A9 /* NSUserDefaults+RACSupport.h in Headers */, - D037663919EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.h in Headers */, - D037653119EDA41200A782A9 /* NSSet+RACSequenceAdditions.h in Headers */, - D03765CB19EDA41200A782A9 /* RACSequence.h in Headers */, - D037662D19EDA41200A782A9 /* UIControl+RACSignalSupport.h in Headers */, - D037666C19EDA57100A782A9 /* EXTKeyPathCoding.h in Headers */, - D037658B19EDA41200A782A9 /* RACEvent.h in Headers */, - D03765CF19EDA41200A782A9 /* RACSerialDisposable.h in Headers */, - D037650519EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h in Headers */, - D037655D19EDA41200A782A9 /* RACChannel.h in Headers */, - D03765B519EDA41200A782A9 /* RACQueueScheduler+Subclass.h in Headers */, - D037665119EDA41200A782A9 /* UISwitch+RACSignalSupport.h in Headers */, - D037664119EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.h in Headers */, - D037652919EDA41200A782A9 /* NSObject+RACSelectorSignal.h in Headers */, - D03765D319EDA41200A782A9 /* RACSignal.h in Headers */, - D03765F519EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h in Headers */, - D03765C719EDA41200A782A9 /* RACScopedDisposable.h in Headers */, - A1046B7B1BFF5662004D8045 /* EXTRuntimeExtensions.h in Headers */, - D037661119EDA41200A782A9 /* RACUnit.h in Headers */, - D03765FD19EDA41200A782A9 /* RACTargetQueueScheduler.h in Headers */, - D037661919EDA41200A782A9 /* UIActionSheet+RACSignalSupport.h in Headers */, - D037664D19EDA41200A782A9 /* UIStepper+RACSignalSupport.h in Headers */, - D037662119EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.h in Headers */, - D03765EB19EDA41200A782A9 /* RACSubject.h in Headers */, - BEBDD6E71CDC292E009A75A9 /* RACDelegateProxy.h in Headers */, - D037656119EDA41200A782A9 /* RACCommand.h in Headers */, - D03765EF19EDA41200A782A9 /* RACSubscriber.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2189,91 +728,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 57D476951C4206EC00EFE697 /* UITableViewCell+RACSignalSupport.m in Sources */, - 57A4D1B21BA13D7A00F7D4B1 /* RACCompoundDisposableProvider.d in Sources */, - 57D476901C4206D400EFE697 /* UIControl+RACSignalSupportPrivate.m in Sources */, - 57A4D1B31BA13D7A00F7D4B1 /* RACSignalProvider.d in Sources */, - 57A4D1B71BA13D7A00F7D4B1 /* ObjectiveCBridging.swift in Sources */, - 57A4D1C11BA13D7A00F7D4B1 /* EXTRuntimeExtensions.m in Sources */, - 57A4D1C21BA13D7A00F7D4B1 /* NSArray+RACSequenceAdditions.m in Sources */, - 57A4D1C31BA13D7A00F7D4B1 /* NSData+RACSupport.m in Sources */, - 57A4D1C41BA13D7A00F7D4B1 /* NSDictionary+RACSequenceAdditions.m in Sources */, - 57A4D1C51BA13D7A00F7D4B1 /* NSEnumerator+RACSequenceAdditions.m in Sources */, - 57D476961C4206EC00EFE697 /* UITableViewHeaderFooterView+RACSignalSupport.m in Sources */, - 57A4D1C61BA13D7A00F7D4B1 /* NSFileHandle+RACSupport.m in Sources */, - 57A4D1C71BA13D7A00F7D4B1 /* NSIndexSet+RACSequenceAdditions.m in Sources */, - 57A4D1C81BA13D7A00F7D4B1 /* NSInvocation+RACTypeParsing.m in Sources */, - 57D4769B1C4206F200EFE697 /* UICollectionReusableView+RACSignalSupport.m in Sources */, - 57A4D1C91BA13D7A00F7D4B1 /* NSNotificationCenter+RACSupport.m in Sources */, - 7DFBED6E1CDB918900EE435B /* UIBarButtonItem+RACCommandSupport.m in Sources */, - 57A4D1CA1BA13D7A00F7D4B1 /* NSObject+RACDeallocating.m in Sources */, - 57A4D1CB1BA13D7A00F7D4B1 /* NSObject+RACDescription.m in Sources */, - 57A4D1CC1BA13D7A00F7D4B1 /* NSObject+RACKVOWrapper.m in Sources */, - 57A4D1CD1BA13D7A00F7D4B1 /* NSObject+RACLifting.m in Sources */, - 57A4D1CE1BA13D7A00F7D4B1 /* NSObject+RACPropertySubscribing.m in Sources */, - 57A4D1CF1BA13D7A00F7D4B1 /* NSObject+RACSelectorSignal.m in Sources */, - 57D476981C4206EC00EFE697 /* UITextView+RACSignalSupport.m in Sources */, - 57A4D1D01BA13D7A00F7D4B1 /* NSOrderedSet+RACSequenceAdditions.m in Sources */, - 57A4D1D11BA13D7A00F7D4B1 /* NSSet+RACSequenceAdditions.m in Sources */, - 57D476911C4206DA00EFE697 /* UIGestureRecognizer+RACSignalSupport.m in Sources */, - 57A4D1D21BA13D7A00F7D4B1 /* NSString+RACKeyPathUtilities.m in Sources */, - 57D4769A1C4206F200EFE697 /* UIButton+RACCommandSupport.m in Sources */, - 57A4D1D31BA13D7A00F7D4B1 /* NSString+RACSequenceAdditions.m in Sources */, - 57A4D1D41BA13D7A00F7D4B1 /* NSString+RACSupport.m in Sources */, - 57A4D1D61BA13D7A00F7D4B1 /* NSUserDefaults+RACSupport.m in Sources */, - 57A4D1D71BA13D7A00F7D4B1 /* RACArraySequence.m in Sources */, - 57A4D1D81BA13D7A00F7D4B1 /* RACBehaviorSubject.m in Sources */, - 57A4D1D91BA13D7A00F7D4B1 /* RACBlockTrampoline.m in Sources */, - 57A4D1DA1BA13D7A00F7D4B1 /* RACChannel.m in Sources */, - 57A4D1DB1BA13D7A00F7D4B1 /* RACCommand.m in Sources */, - 57A4D1DC1BA13D7A00F7D4B1 /* RACCompoundDisposable.m in Sources */, - 57A4D1DD1BA13D7A00F7D4B1 /* RACDelegateProxy.m in Sources */, - 57A4D1DE1BA13D7A00F7D4B1 /* RACDisposable.m in Sources */, 9AD0F06D1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, - 57A4D1DF1BA13D7A00F7D4B1 /* RACDynamicSequence.m in Sources */, C7142DBF1CDEA195009F402D /* CocoaAction.swift in Sources */, - 57A4D1E01BA13D7A00F7D4B1 /* RACDynamicSignal.m in Sources */, - 57A4D1E11BA13D7A00F7D4B1 /* RACEagerSequence.m in Sources */, - 57D4768D1C42063C00EFE697 /* UIControl+RACSignalSupport.m in Sources */, 4A0E11021D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, - 57A4D1E21BA13D7A00F7D4B1 /* RACEmptySequence.m in Sources */, - 57A4D1E31BA13D7A00F7D4B1 /* RACEmptySignal.m in Sources */, - 57A4D1E41BA13D7A00F7D4B1 /* RACErrorSignal.m in Sources */, - 57A4D1E51BA13D7A00F7D4B1 /* RACEvent.m in Sources */, - 57A4D1E61BA13D7A00F7D4B1 /* RACGroupedSignal.m in Sources */, - 57A4D1E71BA13D7A00F7D4B1 /* RACImmediateScheduler.m in Sources */, - 57D476971C4206EC00EFE697 /* UITextField+RACSignalSupport.m in Sources */, - 57A4D1E81BA13D7A00F7D4B1 /* RACIndexSetSequence.m in Sources */, - 57A4D1E91BA13D7A00F7D4B1 /* RACKVOChannel.m in Sources */, - 57A4D1EA1BA13D7A00F7D4B1 /* RACKVOProxy.m in Sources */, - 57A4D1EB1BA13D7A00F7D4B1 /* RACKVOTrampoline.m in Sources */, - 57A4D1EC1BA13D7A00F7D4B1 /* RACMulticastConnection.m in Sources */, - 57A4D1EE1BA13D7A00F7D4B1 /* RACPassthroughSubscriber.m in Sources */, - 57A4D1EF1BA13D7A00F7D4B1 /* RACQueueScheduler.m in Sources */, - 57A4D1F01BA13D7A00F7D4B1 /* RACReplaySubject.m in Sources */, - 57A4D1F11BA13D7A00F7D4B1 /* RACReturnSignal.m in Sources */, - 57A4D1F21BA13D7A00F7D4B1 /* RACScheduler.m in Sources */, - 57A4D1F31BA13D7A00F7D4B1 /* RACScopedDisposable.m in Sources */, - 57A4D1F41BA13D7A00F7D4B1 /* RACSequence.m in Sources */, - 57A4D1F51BA13D7A00F7D4B1 /* RACSerialDisposable.m in Sources */, - 57A4D1F61BA13D7A00F7D4B1 /* RACSignal.m in Sources */, - 57D476921C4206DF00EFE697 /* UISegmentedControl+RACSignalSupport.m in Sources */, - 57A4D1F71BA13D7A00F7D4B1 /* RACSignal+Operations.m in Sources */, - 57A4D1F81BA13D7A00F7D4B1 /* RACSignalSequence.m in Sources */, - 57A4D1F91BA13D7A00F7D4B1 /* RACStream.m in Sources */, - 57A4D1FA1BA13D7A00F7D4B1 /* RACStringSequence.m in Sources */, CD0C45E11CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, - 57A4D1FB1BA13D7A00F7D4B1 /* RACSubject.m in Sources */, - 57A4D1FC1BA13D7A00F7D4B1 /* RACSubscriber.m in Sources */, - 57A4D1FD1BA13D7A00F7D4B1 /* RACSubscriptingAssignmentTrampoline.m in Sources */, - 57A4D1FE1BA13D7A00F7D4B1 /* RACSubscriptionScheduler.m in Sources */, - 57A4D1FF1BA13D7A00F7D4B1 /* RACTargetQueueScheduler.m in Sources */, - 57A4D2001BA13D7A00F7D4B1 /* RACTestScheduler.m in Sources */, - 57A4D2011BA13D7A00F7D4B1 /* RACTuple.m in Sources */, - 57A4D2021BA13D7A00F7D4B1 /* RACTupleSequence.m in Sources */, - 57A4D2031BA13D7A00F7D4B1 /* RACUnarySequence.m in Sources */, - 57A4D2041BA13D7A00F7D4B1 /* RACUnit.m in Sources */, - 57A4D2051BA13D7A00F7D4B1 /* RACValueTransformer.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2281,55 +739,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 7DFBED271CDB8DE300EE435B /* ObjectiveCBridgingSpec.swift in Sources */, 7DFBED281CDB8DE300EE435B /* DynamicPropertySpec.swift in Sources */, - 7DFBED321CDB8DE300EE435B /* NSEnumeratorRACSequenceAdditionsSpec.m in Sources */, - 7DFBED331CDB8DE300EE435B /* NSNotificationCenterRACSupportSpec.m in Sources */, - 7DFBED351CDB8DE300EE435B /* NSObjectRACDeallocatingSpec.m in Sources */, - 7DFBED361CDB8DE300EE435B /* NSObjectRACLiftingSpec.m in Sources */, - 7DFBED381CDB8DE300EE435B /* NSObjectRACPropertySubscribingExamples.m in Sources */, - 7DFBED391CDB8DE300EE435B /* NSObjectRACPropertySubscribingSpec.m in Sources */, - 7DFBED3A1CDB8DE300EE435B /* NSObjectRACSelectorSignalSpec.m in Sources */, - 7DFBED3B1CDB8DE300EE435B /* NSStringRACKeyPathUtilitiesSpec.m in Sources */, - 7DFBED3D1CDB8DE300EE435B /* NSUserDefaultsRACSupportSpec.m in Sources */, - 7DFBED3E1CDB8DE300EE435B /* RACBlockTrampolineSpec.m in Sources */, - 7DFBED401CDB8DE300EE435B /* RACChannelExamples.m in Sources */, - 7DFBED411CDB8DE300EE435B /* RACChannelSpec.m in Sources */, - 7DFBED421CDB8DE300EE435B /* RACCommandSpec.m in Sources */, - 7DFBED431CDB8DE300EE435B /* RACCompoundDisposableSpec.m in Sources */, - 7DFBED451CDB8DE300EE435B /* RACControlCommandExamples.m in Sources */, - 7DFBED461CDB8DE300EE435B /* RACDelegateProxySpec.m in Sources */, - 7DFBED471CDB8DE300EE435B /* RACDisposableSpec.m in Sources */, - 7DFBED481CDB8DE300EE435B /* RACEventSpec.m in Sources */, - 7DFBED491CDB8DE300EE435B /* RACKVOChannelSpec.m in Sources */, CD8401851CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, - 7DFBED4A1CDB8DE300EE435B /* RACKVOProxySpec.m in Sources */, - 7DFBED4B1CDB8DE300EE435B /* RACKVOWrapperSpec.m in Sources */, 9A1E72BC1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */, - 7DFBED4C1CDB8DE300EE435B /* RACMulticastConnectionSpec.m in Sources */, - 7DFBED4E1CDB8DE300EE435B /* RACPropertySignalExamples.m in Sources */, - 7DFBED4F1CDB8DE300EE435B /* RACSchedulerSpec.m in Sources */, - 7DFBED501CDB8DE300EE435B /* RACSequenceAdditionsSpec.m in Sources */, - 7DFBED521CDB8DE300EE435B /* RACSequenceExamples.m in Sources */, - 7DFBED531CDB8DE300EE435B /* RACSequenceSpec.m in Sources */, - 7DFBED541CDB8DE300EE435B /* RACSerialDisposableSpec.m in Sources */, - 7DFBED551CDB8DE300EE435B /* RACSignalSpec.m in Sources */, - 7DFBED571CDB8DE300EE435B /* RACStreamExamples.m in Sources */, - 7DFBED591CDB8DE300EE435B /* RACSubclassObject.m in Sources */, - 7DFBED5A1CDB8DE300EE435B /* RACSubjectSpec.m in Sources */, - 7DFBED5C1CDB8DE300EE435B /* RACSubscriberExamples.m in Sources */, - 7DFBED5D1CDB8DE300EE435B /* RACSubscriberSpec.m in Sources */, - 7DFBED5E1CDB8DE300EE435B /* RACSubscriptingAssignmentTrampolineSpec.m in Sources */, - 7DFBED5F1CDB8DE300EE435B /* RACTargetQueueSchedulerSpec.m in Sources */, - 7DFBED601CDB8DE300EE435B /* RACTupleSpec.m in Sources */, - 7DFBED631CDB8DE300EE435B /* UIBarButtonItemRACSupportSpec.m in Sources */, - 7DFBED641CDB8DE300EE435B /* UIButtonRACSupportSpec.m in Sources */, - 7DFBED671CDB8DE300EE435B /* RACTestExampleScheduler.m in Sources */, - 7DFBED691CDB8DE300EE435B /* RACTestObject.m in Sources */, - 7DFBED6A1CDB8DE300EE435B /* RACTestSchedulerSpec.m in Sources */, BEE020661D637B0000DF261F /* TestError.swift in Sources */, - 7DFBED6C1CDB8DE300EE435B /* RACTestUIButton.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2337,79 +751,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - A9B316341B394C7F0001CB9C /* RACCompoundDisposableProvider.d in Sources */, - A9B316351B394C7F0001CB9C /* RACSignalProvider.d in Sources */, - A9B315BF1B3940810001CB9C /* ObjectiveCBridging.swift in Sources */, - A9B3155E1B3940750001CB9C /* EXTRuntimeExtensions.m in Sources */, - A9B315601B3940750001CB9C /* NSArray+RACSequenceAdditions.m in Sources */, - A9B315631B3940750001CB9C /* NSData+RACSupport.m in Sources */, - A9B315641B3940750001CB9C /* NSDictionary+RACSequenceAdditions.m in Sources */, - A9B315651B3940750001CB9C /* NSEnumerator+RACSequenceAdditions.m in Sources */, - A9B315661B3940750001CB9C /* NSFileHandle+RACSupport.m in Sources */, - A9B315671B3940750001CB9C /* NSIndexSet+RACSequenceAdditions.m in Sources */, - A9B315681B3940750001CB9C /* NSInvocation+RACTypeParsing.m in Sources */, - A9B315691B3940750001CB9C /* NSNotificationCenter+RACSupport.m in Sources */, - A9B3156B1B3940750001CB9C /* NSObject+RACDeallocating.m in Sources */, - A9B3156C1B3940750001CB9C /* NSObject+RACDescription.m in Sources */, - A9B3156D1B3940750001CB9C /* NSObject+RACKVOWrapper.m in Sources */, - A9B3156E1B3940750001CB9C /* NSObject+RACLifting.m in Sources */, - A9B3156F1B3940750001CB9C /* NSObject+RACPropertySubscribing.m in Sources */, - A9B315701B3940750001CB9C /* NSObject+RACSelectorSignal.m in Sources */, - A9B315711B3940750001CB9C /* NSOrderedSet+RACSequenceAdditions.m in Sources */, - A9B315721B3940750001CB9C /* NSSet+RACSequenceAdditions.m in Sources */, - A9B315731B3940750001CB9C /* NSString+RACKeyPathUtilities.m in Sources */, - A9B315741B3940750001CB9C /* NSString+RACSequenceAdditions.m in Sources */, - A9B315751B3940750001CB9C /* NSString+RACSupport.m in Sources */, - A9B315781B3940750001CB9C /* NSUserDefaults+RACSupport.m in Sources */, - A9B315791B3940750001CB9C /* RACArraySequence.m in Sources */, - A9B3157A1B3940750001CB9C /* RACBehaviorSubject.m in Sources */, - A9B3157B1B3940750001CB9C /* RACBlockTrampoline.m in Sources */, - A9B3157C1B3940750001CB9C /* RACChannel.m in Sources */, - A9B3157D1B3940750001CB9C /* RACCommand.m in Sources */, - A9B3157E1B3940750001CB9C /* RACCompoundDisposable.m in Sources */, - A9B3157F1B3940750001CB9C /* RACDelegateProxy.m in Sources */, - A9B315801B3940750001CB9C /* RACDisposable.m in Sources */, - A9B315811B3940750001CB9C /* RACDynamicSequence.m in Sources */, - A9B315821B3940750001CB9C /* RACDynamicSignal.m in Sources */, - A9B315831B3940750001CB9C /* RACEagerSequence.m in Sources */, - A9B315841B3940750001CB9C /* RACEmptySequence.m in Sources */, - A9B315851B3940750001CB9C /* RACEmptySignal.m in Sources */, - A9B315861B3940750001CB9C /* RACErrorSignal.m in Sources */, - A9B315871B3940750001CB9C /* RACEvent.m in Sources */, - A9B315881B3940750001CB9C /* RACGroupedSignal.m in Sources */, - A9B315891B3940750001CB9C /* RACImmediateScheduler.m in Sources */, - A9B3158A1B3940750001CB9C /* RACIndexSetSequence.m in Sources */, - A9B3158B1B3940750001CB9C /* RACKVOChannel.m in Sources */, - A9B3158C1B3940750001CB9C /* RACKVOProxy.m in Sources */, - A9B3158D1B3940750001CB9C /* RACKVOTrampoline.m in Sources */, - A9B3158E1B3940750001CB9C /* RACMulticastConnection.m in Sources */, C7142DBE1CDEA194009F402D /* CocoaAction.swift in Sources */, - A9B315901B3940750001CB9C /* RACPassthroughSubscriber.m in Sources */, - A9B315911B3940750001CB9C /* RACQueueScheduler.m in Sources */, - A9B315921B3940750001CB9C /* RACReplaySubject.m in Sources */, - A9B315931B3940750001CB9C /* RACReturnSignal.m in Sources */, - A9B315941B3940750001CB9C /* RACScheduler.m in Sources */, - A9B315951B3940750001CB9C /* RACScopedDisposable.m in Sources */, - A9B315961B3940750001CB9C /* RACSequence.m in Sources */, - A9B315971B3940750001CB9C /* RACSerialDisposable.m in Sources */, - A9B315981B3940750001CB9C /* RACSignal.m in Sources */, - A9B315991B3940750001CB9C /* RACSignal+Operations.m in Sources */, - A9B3159A1B3940750001CB9C /* RACSignalSequence.m in Sources */, - A9B3159B1B3940750001CB9C /* RACStream.m in Sources */, - A9B3159C1B3940750001CB9C /* RACStringSequence.m in Sources */, CD0C45E01CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, - A9B3159D1B3940750001CB9C /* RACSubject.m in Sources */, 4A0E11011D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, - A9B3159E1B3940750001CB9C /* RACSubscriber.m in Sources */, - A9B3159F1B3940750001CB9C /* RACSubscriptingAssignmentTrampoline.m in Sources */, - A9B315A01B3940750001CB9C /* RACSubscriptionScheduler.m in Sources */, - A9B315A11B3940750001CB9C /* RACTargetQueueScheduler.m in Sources */, - A9B315A21B3940750001CB9C /* RACTestScheduler.m in Sources */, - A9B315A31B3940750001CB9C /* RACTuple.m in Sources */, - A9B315A41B3940750001CB9C /* RACTupleSequence.m in Sources */, - A9B315A51B3940750001CB9C /* RACUnarySequence.m in Sources */, - A9B315A61B3940750001CB9C /* RACUnit.m in Sources */, - A9B315A71B3940750001CB9C /* RACValueTransformer.m in Sources */, 9AD0F06C1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2418,85 +762,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D037654219EDA41200A782A9 /* NSText+RACSignalSupport.m in Sources */, - D037659C19EDA41200A782A9 /* RACKVOChannel.m in Sources */, - D03765C819EDA41200A782A9 /* RACScopedDisposable.m in Sources */, - D03764FE19EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m in Sources */, - D03764EA19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m in Sources */, - D03765C019EDA41200A782A9 /* RACScheduler.m in Sources */, - D037659819EDA41200A782A9 /* RACIndexSetSequence.m in Sources */, - D03765D819EDA41200A782A9 /* RACSignal+Operations.m in Sources */, - D03764F219EDA41200A782A9 /* NSControl+RACTextSignalSupport.m in Sources */, - D037650219EDA41200A782A9 /* NSFileHandle+RACSupport.m in Sources */, - D03765E219EDA41200A782A9 /* RACStream.m in Sources */, - D037655619EDA41200A782A9 /* RACBehaviorSubject.m in Sources */, - D037660219EDA41200A782A9 /* RACTestScheduler.m in Sources */, - D03765B819EDA41200A782A9 /* RACReplaySubject.m in Sources */, - D03765EC19EDA41200A782A9 /* RACSubject.m in Sources */, - D03765D019EDA41200A782A9 /* RACSerialDisposable.m in Sources */, - D037666F19EDA57100A782A9 /* EXTRuntimeExtensions.m in Sources */, - D037653E19EDA41200A782A9 /* NSString+RACSupport.m in Sources */, - D037653619EDA41200A782A9 /* NSString+RACKeyPathUtilities.m in Sources */, - D03764FA19EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m in Sources */, CD0C45DE1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, - D037656819EDA41200A782A9 /* RACCompoundDisposableProvider.d in Sources */, - D037653A19EDA41200A782A9 /* NSString+RACSequenceAdditions.m in Sources */, - D03765E819EDA41200A782A9 /* RACStringSequence.m in Sources */, - D03764EE19EDA41200A782A9 /* NSControl+RACCommandSupport.m in Sources */, 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, - D037660A19EDA41200A782A9 /* RACTupleSequence.m in Sources */, - D03765D419EDA41200A782A9 /* RACSignal.m in Sources */, - D037651A19EDA41200A782A9 /* NSObject+RACDescription.m in Sources */, - D03765A419EDA41200A782A9 /* RACMulticastConnection.m in Sources */, - D037654E19EDA41200A782A9 /* RACArraySequence.m in Sources */, - D037652219EDA41200A782A9 /* NSObject+RACLifting.m in Sources */, - D037650619EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m in Sources */, - D037650E19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m in Sources */, - D03765FA19EDA41200A782A9 /* RACSubscriptionScheduler.m in Sources */, - D037658019EDA41200A782A9 /* RACEmptySequence.m in Sources */, - D037654A19EDA41200A782A9 /* NSUserDefaults+RACSupport.m in Sources */, - D037660E19EDA41200A782A9 /* RACUnarySequence.m in Sources */, - D03765FE19EDA41200A782A9 /* RACTargetQueueScheduler.m in Sources */, C7142DBC1CDEA167009F402D /* CocoaAction.swift in Sources */, - D03765DE19EDA41200A782A9 /* RACSignalSequence.m in Sources */, - D037656C19EDA41200A782A9 /* RACDelegateProxy.m in Sources */, - D037657419EDA41200A782A9 /* RACDynamicSequence.m in Sources */, - D037657019EDA41200A782A9 /* RACDisposable.m in Sources */, - D03765DA19EDA41200A782A9 /* RACSignalProvider.d in Sources */, - D037653219EDA41200A782A9 /* NSSet+RACSequenceAdditions.m in Sources */, - D037651219EDA41200A782A9 /* NSObject+RACAppKitBindings.m in Sources */, - D037656619EDA41200A782A9 /* RACCompoundDisposable.m in Sources */, - D037655A19EDA41200A782A9 /* RACBlockTrampoline.m in Sources */, - D0C312DF19EF2A5800984962 /* ObjectiveCBridging.swift in Sources */, - D037659019EDA41200A782A9 /* RACGroupedSignal.m in Sources */, - D037655E19EDA41200A782A9 /* RACChannel.m in Sources */, - D037657C19EDA41200A782A9 /* RACEagerSequence.m in Sources */, - D037657819EDA41200A782A9 /* RACDynamicSignal.m in Sources */, - D037659419EDA41200A782A9 /* RACImmediateScheduler.m in Sources */, - 7A7065811A3F88B8001E8354 /* RACKVOProxy.m in Sources */, - D037651619EDA41200A782A9 /* NSObject+RACDeallocating.m in Sources */, 4A0E10FF1D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, - D037658419EDA41200A782A9 /* RACEmptySignal.m in Sources */, - D037654619EDA41200A782A9 /* NSURLConnection+RACSupport.m in Sources */, - D03765F019EDA41200A782A9 /* RACSubscriber.m in Sources */, - D03764F619EDA41200A782A9 /* NSData+RACSupport.m in Sources */, - D037656219EDA41200A782A9 /* RACCommand.m in Sources */, - D037658819EDA41200A782A9 /* RACErrorSignal.m in Sources */, - D03765F619EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m in Sources */, - D037661219EDA41200A782A9 /* RACUnit.m in Sources */, - D03765A019EDA41200A782A9 /* RACKVOTrampoline.m in Sources */, - D037650A19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m in Sources */, - D037660619EDA41200A782A9 /* RACTuple.m in Sources */, - D037651E19EDA41200A782A9 /* NSObject+RACKVOWrapper.m in Sources */, - D037661619EDA41200A782A9 /* RACValueTransformer.m in Sources */, - D03765CC19EDA41200A782A9 /* RACSequence.m in Sources */, - D037652E19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m in Sources */, - D037652619EDA41200A782A9 /* NSObject+RACPropertySubscribing.m in Sources */, - D037658C19EDA41200A782A9 /* RACEvent.m in Sources */, - D03765B219EDA41200A782A9 /* RACQueueScheduler.m in Sources */, - D037652A19EDA41200A782A9 /* NSObject+RACSelectorSignal.m in Sources */, - D03765AE19EDA41200A782A9 /* RACPassthroughSubscriber.m in Sources */, - D03765BC19EDA41200A782A9 /* RACReturnSignal.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2505,54 +774,10 @@ buildActionMask = 2147483647; files = ( D0A2260E1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */, - D03766C719EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.m in Sources */, - D03766E319EDA60000A782A9 /* RACDelegateProxySpec.m in Sources */, B696FB811A7640C00075236D /* TestError.swift in Sources */, - D03766F919EDA60000A782A9 /* RACSerialDisposableSpec.m in Sources */, - D0C3131E19EF2D9700984962 /* RACTestExampleScheduler.m in Sources */, - D037670B19EDA60000A782A9 /* RACTargetQueueSchedulerSpec.m in Sources */, - D03766DD19EDA60000A782A9 /* RACCommandSpec.m in Sources */, - D037670919EDA60000A782A9 /* RACSubscriptingAssignmentTrampolineSpec.m in Sources */, BFA6B94D1A7604D400C846D1 /* SignalProducerNimbleMatchers.swift in Sources */, - D03766EB19EDA60000A782A9 /* RACKVOWrapperSpec.m in Sources */, - D03766E719EDA60000A782A9 /* RACEventSpec.m in Sources */, - D03766F719EDA60000A782A9 /* RACSequenceSpec.m in Sources */, - D03766C919EDA60000A782A9 /* NSObjectRACPropertySubscribingSpec.m in Sources */, - D03766C319EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m in Sources */, - D03766BD19EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m in Sources */, - D037670119EDA60000A782A9 /* RACSubclassObject.m in Sources */, - D03766CD19EDA60000A782A9 /* NSStringRACKeyPathUtilitiesSpec.m in Sources */, - D037671519EDA60000A782A9 /* RACTupleSpec.m in Sources */, - D03766C519EDA60000A782A9 /* NSObjectRACLiftingSpec.m in Sources */, - D03766D119EDA60000A782A9 /* NSURLConnectionRACSupportSpec.m in Sources */, - D03766F319EDA60000A782A9 /* RACSequenceAdditionsSpec.m in Sources */, - D03766ED19EDA60000A782A9 /* RACMulticastConnectionSpec.m in Sources */, - D03766E919EDA60000A782A9 /* RACKVOChannelSpec.m in Sources */, - D03766FB19EDA60000A782A9 /* RACSignalSpec.m in Sources */, - 7A7065841A3F8967001E8354 /* RACKVOProxySpec.m in Sources */, - D037670719EDA60000A782A9 /* RACSubscriberSpec.m in Sources */, CD8401831CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, - D03766EF19EDA60000A782A9 /* RACPropertySignalExamples.m in Sources */, - D037670519EDA60000A782A9 /* RACSubscriberExamples.m in Sources */, 9A1E72BA1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, - D0C3132219EF2D9700984962 /* RACTestSchedulerSpec.m in Sources */, - D03766D719EDA60000A782A9 /* RACBlockTrampolineSpec.m in Sources */, - D03766FF19EDA60000A782A9 /* RACStreamExamples.m in Sources */, - D03766CB19EDA60000A782A9 /* NSObjectRACSelectorSignalSpec.m in Sources */, - D03766E119EDA60000A782A9 /* RACControlCommandExamples.m in Sources */, - D03766BF19EDA60000A782A9 /* NSNotificationCenterRACSupportSpec.m in Sources */, - D037670319EDA60000A782A9 /* RACSubjectSpec.m in Sources */, - D03766F119EDA60000A782A9 /* RACSchedulerSpec.m in Sources */, - D03766DF19EDA60000A782A9 /* RACCompoundDisposableSpec.m in Sources */, - D03766E519EDA60000A782A9 /* RACDisposableSpec.m in Sources */, - D0C3132019EF2D9700984962 /* RACTestObject.m in Sources */, - D03766D319EDA60000A782A9 /* NSUserDefaultsRACSupportSpec.m in Sources */, - D03766C119EDA60000A782A9 /* NSObjectRACAppKitBindingsSpec.m in Sources */, - D03766DB19EDA60000A782A9 /* RACChannelSpec.m in Sources */, - D0A226111A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift in Sources */, - D03766D919EDA60000A782A9 /* RACChannelExamples.m in Sources */, - D03766F519EDA60000A782A9 /* RACSequenceExamples.m in Sources */, - D03766B919EDA60000A782A9 /* NSControllerRACSupportSpec.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2560,101 +785,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D037659D19EDA41200A782A9 /* RACKVOChannel.m in Sources */, - D037666319EDA41200A782A9 /* UITextView+RACSignalSupport.m in Sources */, - D037662F19EDA41200A782A9 /* UIControl+RACSignalSupport.m in Sources */, - D03765C919EDA41200A782A9 /* RACScopedDisposable.m in Sources */, - D03764FF19EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m in Sources */, - D037664719EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.m in Sources */, - D03764EB19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m in Sources */, - D03765C119EDA41200A782A9 /* RACScheduler.m in Sources */, - D037662B19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.m in Sources */, - D037659919EDA41200A782A9 /* RACIndexSetSequence.m in Sources */, - D03765D919EDA41200A782A9 /* RACSignal+Operations.m in Sources */, - D037661B19EDA41200A782A9 /* UIActionSheet+RACSignalSupport.m in Sources */, - D037650319EDA41200A782A9 /* NSFileHandle+RACSupport.m in Sources */, - D03765E319EDA41200A782A9 /* RACStream.m in Sources */, - D037655719EDA41200A782A9 /* RACBehaviorSubject.m in Sources */, - D037663B19EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.m in Sources */, - D037660319EDA41200A782A9 /* RACTestScheduler.m in Sources */, - D03765B919EDA41200A782A9 /* RACReplaySubject.m in Sources */, - D03765ED19EDA41200A782A9 /* RACSubject.m in Sources */, - D037664F19EDA41200A782A9 /* UIStepper+RACSignalSupport.m in Sources */, - D03765D119EDA41200A782A9 /* RACSerialDisposable.m in Sources */, - D037663F19EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.m in Sources */, - D037653F19EDA41200A782A9 /* NSString+RACSupport.m in Sources */, - D037653719EDA41200A782A9 /* NSString+RACKeyPathUtilities.m in Sources */, - D03764FB19EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m in Sources */, - D037656919EDA41200A782A9 /* RACCompoundDisposableProvider.d in Sources */, - D037653B19EDA41200A782A9 /* NSString+RACSequenceAdditions.m in Sources */, - D037661F19EDA41200A782A9 /* UIAlertView+RACSignalSupport.m in Sources */, - D03765E919EDA41200A782A9 /* RACStringSequence.m in Sources */, - D037660B19EDA41200A782A9 /* RACTupleSequence.m in Sources */, - D03765D519EDA41200A782A9 /* RACSignal.m in Sources */, - D037663319EDA41200A782A9 /* UIControl+RACSignalSupportPrivate.m in Sources */, CD0C45DF1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, - D037664319EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.m in Sources */, - D037651B19EDA41200A782A9 /* NSObject+RACDescription.m in Sources */, - D03765A519EDA41200A782A9 /* RACMulticastConnection.m in Sources */, - D037654F19EDA41200A782A9 /* RACArraySequence.m in Sources */, - D037652319EDA41200A782A9 /* NSObject+RACLifting.m in Sources */, C7142DBD1CDEA194009F402D /* CocoaAction.swift in Sources */, - D037650719EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m in Sources */, - D037665F19EDA41200A782A9 /* UITextField+RACSignalSupport.m in Sources */, - D037650F19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m in Sources */, - D03765FB19EDA41200A782A9 /* RACSubscriptionScheduler.m in Sources */, 4A0E11001D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, - D037658119EDA41200A782A9 /* RACEmptySequence.m in Sources */, - D0C312E019EF2A5800984962 /* ObjectiveCBridging.swift in Sources */, - D037654B19EDA41200A782A9 /* NSUserDefaults+RACSupport.m in Sources */, - D037660F19EDA41200A782A9 /* RACUnarySequence.m in Sources */, - D03765FF19EDA41200A782A9 /* RACTargetQueueScheduler.m in Sources */, - D03765DF19EDA41200A782A9 /* RACSignalSequence.m in Sources */, - D037656D19EDA41200A782A9 /* RACDelegateProxy.m in Sources */, - D037657519EDA41200A782A9 /* RACDynamicSequence.m in Sources */, - D037657119EDA41200A782A9 /* RACDisposable.m in Sources */, - D03765DB19EDA41200A782A9 /* RACSignalProvider.d in Sources */, - D037653319EDA41200A782A9 /* NSSet+RACSequenceAdditions.m in Sources */, - D037665319EDA41200A782A9 /* UISwitch+RACSignalSupport.m in Sources */, - D037664B19EDA41200A782A9 /* UISlider+RACSignalSupport.m in Sources */, - D037656719EDA41200A782A9 /* RACCompoundDisposable.m in Sources */, - D037655B19EDA41200A782A9 /* RACBlockTrampoline.m in Sources */, - D037659119EDA41200A782A9 /* RACGroupedSignal.m in Sources */, - D037655F19EDA41200A782A9 /* RACChannel.m in Sources */, - D037657D19EDA41200A782A9 /* RACEagerSequence.m in Sources */, - D037657919EDA41200A782A9 /* RACDynamicSignal.m in Sources */, - D037659519EDA41200A782A9 /* RACImmediateScheduler.m in Sources */, - D037651719EDA41200A782A9 /* NSObject+RACDeallocating.m in Sources */, - D037658519EDA41200A782A9 /* RACEmptySignal.m in Sources */, - D037663719EDA41200A782A9 /* UIDatePicker+RACSignalSupport.m in Sources */, - D037654719EDA41200A782A9 /* NSURLConnection+RACSupport.m in Sources */, - D03765F119EDA41200A782A9 /* RACSubscriber.m in Sources */, - D03764F719EDA41200A782A9 /* NSData+RACSupport.m in Sources */, - D037656319EDA41200A782A9 /* RACCommand.m in Sources */, - D037658919EDA41200A782A9 /* RACErrorSignal.m in Sources */, - D03765F719EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m in Sources */, - D037661319EDA41200A782A9 /* RACUnit.m in Sources */, - D037662319EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.m in Sources */, - D03765A119EDA41200A782A9 /* RACKVOTrampoline.m in Sources */, - D037665B19EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.m in Sources */, - D037650B19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m in Sources */, - D037660719EDA41200A782A9 /* RACTuple.m in Sources */, - D037667019EDA57100A782A9 /* EXTRuntimeExtensions.m in Sources */, - D037651F19EDA41200A782A9 /* NSObject+RACKVOWrapper.m in Sources */, - D037661719EDA41200A782A9 /* RACValueTransformer.m in Sources */, - D03765CD19EDA41200A782A9 /* RACSequence.m in Sources */, - 314304181ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.m in Sources */, 9AD0F06B1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, - D037652F19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m in Sources */, - D037662719EDA41200A782A9 /* UIButton+RACCommandSupport.m in Sources */, - D037652719EDA41200A782A9 /* NSObject+RACPropertySubscribing.m in Sources */, - 7A7065821A3F88B8001E8354 /* RACKVOProxy.m in Sources */, - D037658D19EDA41200A782A9 /* RACEvent.m in Sources */, - D03765B319EDA41200A782A9 /* RACQueueScheduler.m in Sources */, - D037665719EDA41200A782A9 /* UITableViewCell+RACSignalSupport.m in Sources */, - D037652B19EDA41200A782A9 /* NSObject+RACSelectorSignal.m in Sources */, - D03765AF19EDA41200A782A9 /* RACPassthroughSubscriber.m in Sources */, - D03765BD19EDA41200A782A9 /* RACReturnSignal.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2662,58 +796,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D03766C819EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.m in Sources */, - D037672419EDA60000A782A9 /* UIImagePickerControllerRACSupportSpec.m in Sources */, - D03766E419EDA60000A782A9 /* RACDelegateProxySpec.m in Sources */, D0A2260F1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */, - D03766FA19EDA60000A782A9 /* RACSerialDisposableSpec.m in Sources */, - D037670C19EDA60000A782A9 /* RACTargetQueueSchedulerSpec.m in Sources */, - D03766DE19EDA60000A782A9 /* RACCommandSpec.m in Sources */, - D037670A19EDA60000A782A9 /* RACSubscriptingAssignmentTrampolineSpec.m in Sources */, - D03766EC19EDA60000A782A9 /* RACKVOWrapperSpec.m in Sources */, - D03766E819EDA60000A782A9 /* RACEventSpec.m in Sources */, - D03766F819EDA60000A782A9 /* RACSequenceSpec.m in Sources */, - D0A226121A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift in Sources */, - D037671E19EDA60000A782A9 /* UIBarButtonItemRACSupportSpec.m in Sources */, BFA6B94E1A7604D500C846D1 /* SignalProducerNimbleMatchers.swift in Sources */, - D03766CA19EDA60000A782A9 /* NSObjectRACPropertySubscribingSpec.m in Sources */, - D0C3132319EF2D9700984962 /* RACTestSchedulerSpec.m in Sources */, - D03766C419EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m in Sources */, - D03766BE19EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m in Sources */, - D037672019EDA60000A782A9 /* UIButtonRACSupportSpec.m in Sources */, - D0C3132519EF2D9700984962 /* RACTestUIButton.m in Sources */, - D037670219EDA60000A782A9 /* RACSubclassObject.m in Sources */, - D03766CE19EDA60000A782A9 /* NSStringRACKeyPathUtilitiesSpec.m in Sources */, - D037671619EDA60000A782A9 /* RACTupleSpec.m in Sources */, - 7A7065851A3F8967001E8354 /* RACKVOProxySpec.m in Sources */, - D03766C619EDA60000A782A9 /* NSObjectRACLiftingSpec.m in Sources */, B696FB821A7640C00075236D /* TestError.swift in Sources */, - D0C3131F19EF2D9700984962 /* RACTestExampleScheduler.m in Sources */, - D03766D219EDA60000A782A9 /* NSURLConnectionRACSupportSpec.m in Sources */, - D03766F419EDA60000A782A9 /* RACSequenceAdditionsSpec.m in Sources */, - D03766EE19EDA60000A782A9 /* RACMulticastConnectionSpec.m in Sources */, - D03766EA19EDA60000A782A9 /* RACKVOChannelSpec.m in Sources */, - D0C3132119EF2D9700984962 /* RACTestObject.m in Sources */, - D03766FC19EDA60000A782A9 /* RACSignalSpec.m in Sources */, - D037670819EDA60000A782A9 /* RACSubscriberSpec.m in Sources */, - D037671C19EDA60000A782A9 /* UIAlertViewRACSupportSpec.m in Sources */, - D03766F019EDA60000A782A9 /* RACPropertySignalExamples.m in Sources */, - D037670619EDA60000A782A9 /* RACSubscriberExamples.m in Sources */, - D03766D819EDA60000A782A9 /* RACBlockTrampolineSpec.m in Sources */, - D037670019EDA60000A782A9 /* RACStreamExamples.m in Sources */, - D03766CC19EDA60000A782A9 /* NSObjectRACSelectorSignalSpec.m in Sources */, 9A1E72BB1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, - D03766E219EDA60000A782A9 /* RACControlCommandExamples.m in Sources */, - D03766C019EDA60000A782A9 /* NSNotificationCenterRACSupportSpec.m in Sources */, - D037670419EDA60000A782A9 /* RACSubjectSpec.m in Sources */, - D03766F219EDA60000A782A9 /* RACSchedulerSpec.m in Sources */, - D03766E019EDA60000A782A9 /* RACCompoundDisposableSpec.m in Sources */, - D03766E619EDA60000A782A9 /* RACDisposableSpec.m in Sources */, - D03766D419EDA60000A782A9 /* NSUserDefaultsRACSupportSpec.m in Sources */, - D03766DC19EDA60000A782A9 /* RACChannelSpec.m in Sources */, - D037671A19EDA60000A782A9 /* UIActionSheetRACSupportSpec.m in Sources */, - D03766DA19EDA60000A782A9 /* RACChannelExamples.m in Sources */, - D03766F619EDA60000A782A9 /* RACSequenceExamples.m in Sources */, CD8401841CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/ReactiveCocoa/Swift/CocoaAction.swift b/ReactiveCocoa/CocoaAction.swift similarity index 100% rename from ReactiveCocoa/Swift/CocoaAction.swift rename to ReactiveCocoa/CocoaAction.swift diff --git a/ReactiveCocoa/Swift/DynamicProperty.swift b/ReactiveCocoa/DynamicProperty.swift similarity index 100% rename from ReactiveCocoa/Swift/DynamicProperty.swift rename to ReactiveCocoa/DynamicProperty.swift diff --git a/ReactiveCocoa/Swift/NSObject+KeyValueObserving.swift b/ReactiveCocoa/NSObject+KeyValueObserving.swift similarity index 100% rename from ReactiveCocoa/Swift/NSObject+KeyValueObserving.swift rename to ReactiveCocoa/NSObject+KeyValueObserving.swift diff --git a/ReactiveCocoa/Swift/NSObject+Lifetime.swift b/ReactiveCocoa/NSObject+Lifetime.swift similarity index 100% rename from ReactiveCocoa/Swift/NSObject+Lifetime.swift rename to ReactiveCocoa/NSObject+Lifetime.swift diff --git a/ReactiveCocoa/ReactiveCocoa.h b/ReactiveCocoa/ReactiveCocoa.h index 415ec69d47..733fd418ab 100644 --- a/ReactiveCocoa/ReactiveCocoa.h +++ b/ReactiveCocoa/ReactiveCocoa.h @@ -13,83 +13,3 @@ FOUNDATION_EXPORT double ReactiveCocoaVersionNumber; //! Project version string for ReactiveCocoa. FOUNDATION_EXPORT const unsigned char ReactiveCocoaVersionString[]; - -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import - -#if TARGET_OS_WATCH -#elif TARGET_OS_IOS || TARGET_OS_TV - #import - #import - #import - #import - #import - #import - #import - #import - #import - #import - - #if TARGET_OS_IOS - #import - #import - #import - #import - #import - #import - #import - #import - #import - #import - #endif -#elif TARGET_OS_MAC - #import - #import - #import - #import - #import -#endif diff --git a/ReactiveCocoaTests/Swift/CocoaActionSpec.swift b/ReactiveCocoaTests/CocoaActionSpec.swift similarity index 91% rename from ReactiveCocoaTests/Swift/CocoaActionSpec.swift rename to ReactiveCocoaTests/CocoaActionSpec.swift index d99d777e96..8eb27aec90 100644 --- a/ReactiveCocoaTests/Swift/CocoaActionSpec.swift +++ b/ReactiveCocoaTests/CocoaActionSpec.swift @@ -35,8 +35,7 @@ class CocoaActionSpec: QuickSpec { let cocoaAction = action.unsafeCocoaAction cocoaAction - .rac_values(forKeyPath: #keyPath(CocoaAction.isEnabled), observer: nil) - .toSignalProducer() + .values(forKeyPath: #keyPath(CocoaAction.isEnabled)) .map { $0! as! Bool } .start(Observer(next: { values.append($0) })) @@ -54,8 +53,7 @@ class CocoaActionSpec: QuickSpec { let cocoaAction = action.unsafeCocoaAction cocoaAction - .rac_values(forKeyPath: #keyPath(CocoaAction.isExecuting), observer: nil) - .toSignalProducer() + .values(forKeyPath: #keyPath(CocoaAction.isExecuting)) .map { $0! as! Bool } .start(Observer(next: { values.append($0) })) diff --git a/ReactiveCocoaTests/Swift/DynamicPropertySpec.swift b/ReactiveCocoaTests/DynamicPropertySpec.swift similarity index 100% rename from ReactiveCocoaTests/Swift/DynamicPropertySpec.swift rename to ReactiveCocoaTests/DynamicPropertySpec.swift diff --git a/ReactiveCocoaTests/Swift/KeyValueObservingSpec.swift b/ReactiveCocoaTests/KeyValueObservingSpec.swift similarity index 100% rename from ReactiveCocoaTests/Swift/KeyValueObservingSpec.swift rename to ReactiveCocoaTests/KeyValueObservingSpec.swift diff --git a/ReactiveCocoaTests/Swift/SignalProducerNimbleMatchers.swift b/ReactiveCocoaTests/SignalProducerNimbleMatchers.swift similarity index 100% rename from ReactiveCocoaTests/Swift/SignalProducerNimbleMatchers.swift rename to ReactiveCocoaTests/SignalProducerNimbleMatchers.swift diff --git a/ReactiveCocoaTests/Swift/TestError.swift b/ReactiveCocoaTests/TestError.swift similarity index 100% rename from ReactiveCocoaTests/Swift/TestError.swift rename to ReactiveCocoaTests/TestError.swift diff --git a/ReactiveObjC.xcodeproj/project.pbxproj b/ReactiveObjC.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..5e57bff9be --- /dev/null +++ b/ReactiveObjC.xcodeproj/project.pbxproj @@ -0,0 +1,3164 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 314304171ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 314304151ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 314304181ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 314304161ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.m */; }; + 57A4D1B21BA13D7A00F7D4B1 /* RACCompoundDisposableProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D037646A19EDA41200A782A9 /* RACCompoundDisposableProvider.d */; }; + 57A4D1B31BA13D7A00F7D4B1 /* RACSignalProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D03764A319EDA41200A782A9 /* RACSignalProvider.d */; }; + 57A4D1C11BA13D7A00F7D4B1 /* EXTRuntimeExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037666819EDA57100A782A9 /* EXTRuntimeExtensions.m */; }; + 57A4D1C21BA13D7A00F7D4B1 /* NSArray+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037642B19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m */; }; + 57A4D1C31BA13D7A00F7D4B1 /* NSData+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643119EDA41200A782A9 /* NSData+RACSupport.m */; }; + 57A4D1C41BA13D7A00F7D4B1 /* NSDictionary+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643319EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m */; }; + 57A4D1C51BA13D7A00F7D4B1 /* NSEnumerator+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643519EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m */; }; + 57A4D1C61BA13D7A00F7D4B1 /* NSFileHandle+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643719EDA41200A782A9 /* NSFileHandle+RACSupport.m */; }; + 57A4D1C71BA13D7A00F7D4B1 /* NSIndexSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643919EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m */; }; + 57A4D1C81BA13D7A00F7D4B1 /* NSInvocation+RACTypeParsing.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643B19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m */; }; + 57A4D1C91BA13D7A00F7D4B1 /* NSNotificationCenter+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643D19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m */; }; + 57A4D1CA1BA13D7A00F7D4B1 /* NSObject+RACDeallocating.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644119EDA41200A782A9 /* NSObject+RACDeallocating.m */; }; + 57A4D1CB1BA13D7A00F7D4B1 /* NSObject+RACDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644319EDA41200A782A9 /* NSObject+RACDescription.m */; }; + 57A4D1CC1BA13D7A00F7D4B1 /* NSObject+RACKVOWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644519EDA41200A782A9 /* NSObject+RACKVOWrapper.m */; }; + 57A4D1CD1BA13D7A00F7D4B1 /* NSObject+RACLifting.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644719EDA41200A782A9 /* NSObject+RACLifting.m */; }; + 57A4D1CE1BA13D7A00F7D4B1 /* NSObject+RACPropertySubscribing.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644919EDA41200A782A9 /* NSObject+RACPropertySubscribing.m */; }; + 57A4D1CF1BA13D7A00F7D4B1 /* NSObject+RACSelectorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644B19EDA41200A782A9 /* NSObject+RACSelectorSignal.m */; }; + 57A4D1D01BA13D7A00F7D4B1 /* NSOrderedSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644D19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m */; }; + 57A4D1D11BA13D7A00F7D4B1 /* NSSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644F19EDA41200A782A9 /* NSSet+RACSequenceAdditions.m */; }; + 57A4D1D21BA13D7A00F7D4B1 /* NSString+RACKeyPathUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645119EDA41200A782A9 /* NSString+RACKeyPathUtilities.m */; }; + 57A4D1D31BA13D7A00F7D4B1 /* NSString+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645319EDA41200A782A9 /* NSString+RACSequenceAdditions.m */; }; + 57A4D1D41BA13D7A00F7D4B1 /* NSString+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645519EDA41200A782A9 /* NSString+RACSupport.m */; }; + 57A4D1D61BA13D7A00F7D4B1 /* NSUserDefaults+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645B19EDA41200A782A9 /* NSUserDefaults+RACSupport.m */; }; + 57A4D1D71BA13D7A00F7D4B1 /* RACArraySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645D19EDA41200A782A9 /* RACArraySequence.m */; }; + 57A4D1D81BA13D7A00F7D4B1 /* RACBehaviorSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646119EDA41200A782A9 /* RACBehaviorSubject.m */; }; + 57A4D1D91BA13D7A00F7D4B1 /* RACBlockTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646319EDA41200A782A9 /* RACBlockTrampoline.m */; }; + 57A4D1DA1BA13D7A00F7D4B1 /* RACChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646519EDA41200A782A9 /* RACChannel.m */; }; + 57A4D1DB1BA13D7A00F7D4B1 /* RACCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646719EDA41200A782A9 /* RACCommand.m */; }; + 57A4D1DC1BA13D7A00F7D4B1 /* RACCompoundDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646919EDA41200A782A9 /* RACCompoundDisposable.m */; }; + 57A4D1DD1BA13D7A00F7D4B1 /* RACDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646C19EDA41200A782A9 /* RACDelegateProxy.m */; }; + 57A4D1DE1BA13D7A00F7D4B1 /* RACDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646E19EDA41200A782A9 /* RACDisposable.m */; }; + 57A4D1DF1BA13D7A00F7D4B1 /* RACDynamicSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647019EDA41200A782A9 /* RACDynamicSequence.m */; }; + 57A4D1E01BA13D7A00F7D4B1 /* RACDynamicSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647219EDA41200A782A9 /* RACDynamicSignal.m */; }; + 57A4D1E11BA13D7A00F7D4B1 /* RACEagerSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647419EDA41200A782A9 /* RACEagerSequence.m */; }; + 57A4D1E21BA13D7A00F7D4B1 /* RACEmptySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647619EDA41200A782A9 /* RACEmptySequence.m */; }; + 57A4D1E31BA13D7A00F7D4B1 /* RACEmptySignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647819EDA41200A782A9 /* RACEmptySignal.m */; }; + 57A4D1E41BA13D7A00F7D4B1 /* RACErrorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647A19EDA41200A782A9 /* RACErrorSignal.m */; }; + 57A4D1E51BA13D7A00F7D4B1 /* RACEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647C19EDA41200A782A9 /* RACEvent.m */; }; + 57A4D1E61BA13D7A00F7D4B1 /* RACGroupedSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647E19EDA41200A782A9 /* RACGroupedSignal.m */; }; + 57A4D1E71BA13D7A00F7D4B1 /* RACImmediateScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648019EDA41200A782A9 /* RACImmediateScheduler.m */; }; + 57A4D1E81BA13D7A00F7D4B1 /* RACIndexSetSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648219EDA41200A782A9 /* RACIndexSetSequence.m */; }; + 57A4D1E91BA13D7A00F7D4B1 /* RACKVOChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648419EDA41200A782A9 /* RACKVOChannel.m */; }; + 57A4D1EA1BA13D7A00F7D4B1 /* RACKVOProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A70657E1A3F88B8001E8354 /* RACKVOProxy.m */; }; + 57A4D1EB1BA13D7A00F7D4B1 /* RACKVOTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648619EDA41200A782A9 /* RACKVOTrampoline.m */; }; + 57A4D1EC1BA13D7A00F7D4B1 /* RACMulticastConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648819EDA41200A782A9 /* RACMulticastConnection.m */; }; + 57A4D1EE1BA13D7A00F7D4B1 /* RACPassthroughSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648D19EDA41200A782A9 /* RACPassthroughSubscriber.m */; }; + 57A4D1EF1BA13D7A00F7D4B1 /* RACQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648F19EDA41200A782A9 /* RACQueueScheduler.m */; }; + 57A4D1F01BA13D7A00F7D4B1 /* RACReplaySubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649219EDA41200A782A9 /* RACReplaySubject.m */; }; + 57A4D1F11BA13D7A00F7D4B1 /* RACReturnSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649419EDA41200A782A9 /* RACReturnSignal.m */; }; + 57A4D1F21BA13D7A00F7D4B1 /* RACScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649619EDA41200A782A9 /* RACScheduler.m */; }; + 57A4D1F31BA13D7A00F7D4B1 /* RACScopedDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649A19EDA41200A782A9 /* RACScopedDisposable.m */; }; + 57A4D1F41BA13D7A00F7D4B1 /* RACSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649C19EDA41200A782A9 /* RACSequence.m */; }; + 57A4D1F51BA13D7A00F7D4B1 /* RACSerialDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649E19EDA41200A782A9 /* RACSerialDisposable.m */; }; + 57A4D1F61BA13D7A00F7D4B1 /* RACSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A019EDA41200A782A9 /* RACSignal.m */; }; + 57A4D1F71BA13D7A00F7D4B1 /* RACSignal+Operations.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A219EDA41200A782A9 /* RACSignal+Operations.m */; }; + 57A4D1F81BA13D7A00F7D4B1 /* RACSignalSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A519EDA41200A782A9 /* RACSignalSequence.m */; }; + 57A4D1F91BA13D7A00F7D4B1 /* RACStream.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A719EDA41200A782A9 /* RACStream.m */; }; + 57A4D1FA1BA13D7A00F7D4B1 /* RACStringSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AA19EDA41200A782A9 /* RACStringSequence.m */; }; + 57A4D1FB1BA13D7A00F7D4B1 /* RACSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AC19EDA41200A782A9 /* RACSubject.m */; }; + 57A4D1FC1BA13D7A00F7D4B1 /* RACSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AE19EDA41200A782A9 /* RACSubscriber.m */; }; + 57A4D1FD1BA13D7A00F7D4B1 /* RACSubscriptingAssignmentTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B119EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m */; }; + 57A4D1FE1BA13D7A00F7D4B1 /* RACSubscriptionScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B319EDA41200A782A9 /* RACSubscriptionScheduler.m */; }; + 57A4D1FF1BA13D7A00F7D4B1 /* RACTargetQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B519EDA41200A782A9 /* RACTargetQueueScheduler.m */; }; + 57A4D2001BA13D7A00F7D4B1 /* RACTestScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B719EDA41200A782A9 /* RACTestScheduler.m */; }; + 57A4D2011BA13D7A00F7D4B1 /* RACTuple.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B919EDA41200A782A9 /* RACTuple.m */; }; + 57A4D2021BA13D7A00F7D4B1 /* RACTupleSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BB19EDA41200A782A9 /* RACTupleSequence.m */; }; + 57A4D2031BA13D7A00F7D4B1 /* RACUnarySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BD19EDA41200A782A9 /* RACUnarySequence.m */; }; + 57A4D2041BA13D7A00F7D4B1 /* RACUnit.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BF19EDA41200A782A9 /* RACUnit.m */; }; + 57A4D2051BA13D7A00F7D4B1 /* RACValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C119EDA41200A782A9 /* RACValueTransformer.m */; }; + 57A4D20A1BA13D7A00F7D4B1 /* ReactiveObjC.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveObjC.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D20B1BA13D7A00F7D4B1 /* EXTKeyPathCoding.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666619EDA57100A782A9 /* EXTKeyPathCoding.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D20C1BA13D7A00F7D4B1 /* EXTScope.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666919EDA57100A782A9 /* EXTScope.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D20D1BA13D7A00F7D4B1 /* metamacros.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666A19EDA57100A782A9 /* metamacros.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D20E1BA13D7A00F7D4B1 /* NSArray+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037642A19EDA41200A782A9 /* NSArray+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D20F1BA13D7A00F7D4B1 /* NSData+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643019EDA41200A782A9 /* NSData+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2101BA13D7A00F7D4B1 /* NSDictionary+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643219EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2111BA13D7A00F7D4B1 /* NSEnumerator+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643419EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2121BA13D7A00F7D4B1 /* NSFileHandle+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643619EDA41200A782A9 /* NSFileHandle+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2131BA13D7A00F7D4B1 /* NSIndexSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643819EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2141BA13D7A00F7D4B1 /* NSNotificationCenter+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643C19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2151BA13D7A00F7D4B1 /* NSObject+RACDeallocating.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644019EDA41200A782A9 /* NSObject+RACDeallocating.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2161BA13D7A00F7D4B1 /* NSObject+RACLifting.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644619EDA41200A782A9 /* NSObject+RACLifting.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2171BA13D7A00F7D4B1 /* NSObject+RACPropertySubscribing.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644819EDA41200A782A9 /* NSObject+RACPropertySubscribing.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2181BA13D7A00F7D4B1 /* NSObject+RACSelectorSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644A19EDA41200A782A9 /* NSObject+RACSelectorSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2191BA13D7A00F7D4B1 /* NSOrderedSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644C19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D21A1BA13D7A00F7D4B1 /* NSSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644E19EDA41200A782A9 /* NSSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D21B1BA13D7A00F7D4B1 /* NSString+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645219EDA41200A782A9 /* NSString+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D21C1BA13D7A00F7D4B1 /* NSString+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645419EDA41200A782A9 /* NSString+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D21E1BA13D7A00F7D4B1 /* NSUserDefaults+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645A19EDA41200A782A9 /* NSUserDefaults+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D21F1BA13D7A00F7D4B1 /* RACBehaviorSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646019EDA41200A782A9 /* RACBehaviorSubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2201BA13D7A00F7D4B1 /* RACChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646419EDA41200A782A9 /* RACChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2211BA13D7A00F7D4B1 /* RACCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646619EDA41200A782A9 /* RACCommand.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2221BA13D7A00F7D4B1 /* RACCompoundDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646819EDA41200A782A9 /* RACCompoundDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2231BA13D7A00F7D4B1 /* RACDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646D19EDA41200A782A9 /* RACDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2241BA13D7A00F7D4B1 /* RACEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = D037647B19EDA41200A782A9 /* RACEvent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2251BA13D7A00F7D4B1 /* RACGroupedSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037647D19EDA41200A782A9 /* RACGroupedSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2261BA13D7A00F7D4B1 /* RACKVOChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648319EDA41200A782A9 /* RACKVOChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2271BA13D7A00F7D4B1 /* RACMulticastConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648719EDA41200A782A9 /* RACMulticastConnection.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2281BA13D7A00F7D4B1 /* RACQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648E19EDA41200A782A9 /* RACQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2291BA13D7A00F7D4B1 /* RACQueueScheduler+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649019EDA41200A782A9 /* RACQueueScheduler+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D22A1BA13D7A00F7D4B1 /* RACReplaySubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649119EDA41200A782A9 /* RACReplaySubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D22B1BA13D7A00F7D4B1 /* RACScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649519EDA41200A782A9 /* RACScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D22C1BA13D7A00F7D4B1 /* RACScheduler+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649819EDA41200A782A9 /* RACScheduler+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D22D1BA13D7A00F7D4B1 /* RACScopedDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649919EDA41200A782A9 /* RACScopedDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D22E1BA13D7A00F7D4B1 /* RACSequence.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649B19EDA41200A782A9 /* RACSequence.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D22F1BA13D7A00F7D4B1 /* RACSerialDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649D19EDA41200A782A9 /* RACSerialDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2301BA13D7A00F7D4B1 /* RACSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649F19EDA41200A782A9 /* RACSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2311BA13D7A00F7D4B1 /* RACSignal+Operations.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764A119EDA41200A782A9 /* RACSignal+Operations.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2321BA13D7A00F7D4B1 /* RACStream.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764A619EDA41200A782A9 /* RACStream.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2331BA13D7A00F7D4B1 /* RACSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764AB19EDA41200A782A9 /* RACSubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2341BA13D7A00F7D4B1 /* RACSubscriber.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764AD19EDA41200A782A9 /* RACSubscriber.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2351BA13D7A00F7D4B1 /* RACSubscriptingAssignmentTrampoline.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B019EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2361BA13D7A00F7D4B1 /* RACTargetQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B419EDA41200A782A9 /* RACTargetQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2371BA13D7A00F7D4B1 /* RACTestScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B619EDA41200A782A9 /* RACTestScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2381BA13D7A00F7D4B1 /* RACTuple.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B819EDA41200A782A9 /* RACTuple.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57A4D2391BA13D7A00F7D4B1 /* RACUnit.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764BE19EDA41200A782A9 /* RACUnit.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57D4768D1C42063C00EFE697 /* UIControl+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764CD19EDA41200A782A9 /* UIControl+RACSignalSupport.m */; }; + 57D476901C4206D400EFE697 /* UIControl+RACSignalSupportPrivate.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764CF19EDA41200A782A9 /* UIControl+RACSignalSupportPrivate.m */; }; + 57D476911C4206DA00EFE697 /* UIGestureRecognizer+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764D319EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.m */; }; + 57D476921C4206DF00EFE697 /* UISegmentedControl+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764D919EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.m */; }; + 57D476951C4206EC00EFE697 /* UITableViewCell+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764E119EDA41200A782A9 /* UITableViewCell+RACSignalSupport.m */; }; + 57D476961C4206EC00EFE697 /* UITableViewHeaderFooterView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764E319EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.m */; }; + 57D476971C4206EC00EFE697 /* UITextField+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764E519EDA41200A782A9 /* UITextField+RACSignalSupport.m */; }; + 57D476981C4206EC00EFE697 /* UITextView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764E719EDA41200A782A9 /* UITextView+RACSignalSupport.m */; }; + 57D4769A1C4206F200EFE697 /* UIButton+RACCommandSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C919EDA41200A782A9 /* UIButton+RACCommandSupport.m */; }; + 57D4769B1C4206F200EFE697 /* UICollectionReusableView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764CB19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.m */; }; + 57DC89A01C5066D400E367B7 /* UIGestureRecognizer+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764D219EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57DC89A11C50672B00E367B7 /* UIControl+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764CC19EDA41200A782A9 /* UIControl+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57DC89A21C50673C00E367B7 /* UISegmentedControl+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764D819EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57DC89A31C50674300E367B7 /* UITableViewCell+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764E019EDA41200A782A9 /* UITableViewCell+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57DC89A41C50674D00E367B7 /* UITableViewHeaderFooterView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764E219EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57DC89A51C50675700E367B7 /* UITextField+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764E419EDA41200A782A9 /* UITextField+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57DC89A61C50675F00E367B7 /* UITextView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764E619EDA41200A782A9 /* UITextView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57DC89A71C50679700E367B7 /* UIButton+RACCommandSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764C819EDA41200A782A9 /* UIButton+RACCommandSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57DC89A81C50679E00E367B7 /* UICollectionReusableView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764CA19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7A7065811A3F88B8001E8354 /* RACKVOProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A70657E1A3F88B8001E8354 /* RACKVOProxy.m */; }; + 7A7065821A3F88B8001E8354 /* RACKVOProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A70657E1A3F88B8001E8354 /* RACKVOProxy.m */; }; + 7A7065841A3F8967001E8354 /* RACKVOProxySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A7065831A3F8967001E8354 /* RACKVOProxySpec.m */; }; + 7A7065851A3F8967001E8354 /* RACKVOProxySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A7065831A3F8967001E8354 /* RACKVOProxySpec.m */; }; + 7DFBED081CDB8C9500EE435B /* ReactiveObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57A4D2411BA13D7A00F7D4B1 /* ReactiveObjC.framework */; }; + 7DFBED141CDB8CE600EE435B /* test-data.json in Resources */ = {isa = PBXBuildFile; fileRef = D03766B119EDA60000A782A9 /* test-data.json */; }; + 7DFBED1E1CDB8D7000EE435B /* ReactiveObjC.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 57A4D2411BA13D7A00F7D4B1 /* ReactiveObjC.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 7DFBED1F1CDB8D7800EE435B /* Quick.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 7DFBED201CDB8D7D00EE435B /* Nimble.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 7DFBED211CDB8D8300EE435B /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 7DFBED321CDB8DE300EE435B /* NSEnumeratorRACSequenceAdditionsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667819EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m */; }; + 7DFBED331CDB8DE300EE435B /* NSNotificationCenterRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667919EDA60000A782A9 /* NSNotificationCenterRACSupportSpec.m */; }; + 7DFBED351CDB8DE300EE435B /* NSObjectRACDeallocatingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667B19EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m */; }; + 7DFBED361CDB8DE300EE435B /* NSObjectRACLiftingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667C19EDA60000A782A9 /* NSObjectRACLiftingSpec.m */; }; + 7DFBED381CDB8DE300EE435B /* NSObjectRACPropertySubscribingExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667E19EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.m */; }; + 7DFBED391CDB8DE300EE435B /* NSObjectRACPropertySubscribingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667F19EDA60000A782A9 /* NSObjectRACPropertySubscribingSpec.m */; }; + 7DFBED3A1CDB8DE300EE435B /* NSObjectRACSelectorSignalSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668019EDA60000A782A9 /* NSObjectRACSelectorSignalSpec.m */; }; + 7DFBED3B1CDB8DE300EE435B /* NSStringRACKeyPathUtilitiesSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668119EDA60000A782A9 /* NSStringRACKeyPathUtilitiesSpec.m */; }; + 7DFBED3D1CDB8DE300EE435B /* NSUserDefaultsRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668419EDA60000A782A9 /* NSUserDefaultsRACSupportSpec.m */; }; + 7DFBED3E1CDB8DE300EE435B /* RACBlockTrampolineSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668619EDA60000A782A9 /* RACBlockTrampolineSpec.m */; }; + 7DFBED401CDB8DE300EE435B /* RACChannelExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668819EDA60000A782A9 /* RACChannelExamples.m */; }; + 7DFBED411CDB8DE300EE435B /* RACChannelSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668919EDA60000A782A9 /* RACChannelSpec.m */; }; + 7DFBED421CDB8DE300EE435B /* RACCommandSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668A19EDA60000A782A9 /* RACCommandSpec.m */; }; + 7DFBED431CDB8DE300EE435B /* RACCompoundDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668B19EDA60000A782A9 /* RACCompoundDisposableSpec.m */; }; + 7DFBED451CDB8DE300EE435B /* RACControlCommandExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668D19EDA60000A782A9 /* RACControlCommandExamples.m */; }; + 7DFBED461CDB8DE300EE435B /* RACDelegateProxySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668E19EDA60000A782A9 /* RACDelegateProxySpec.m */; }; + 7DFBED471CDB8DE300EE435B /* RACDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668F19EDA60000A782A9 /* RACDisposableSpec.m */; }; + 7DFBED481CDB8DE300EE435B /* RACEventSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669019EDA60000A782A9 /* RACEventSpec.m */; }; + 7DFBED491CDB8DE300EE435B /* RACKVOChannelSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669119EDA60000A782A9 /* RACKVOChannelSpec.m */; }; + 7DFBED4A1CDB8DE300EE435B /* RACKVOProxySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A7065831A3F8967001E8354 /* RACKVOProxySpec.m */; }; + 7DFBED4B1CDB8DE300EE435B /* RACKVOWrapperSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669219EDA60000A782A9 /* RACKVOWrapperSpec.m */; }; + 7DFBED4C1CDB8DE300EE435B /* RACMulticastConnectionSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669319EDA60000A782A9 /* RACMulticastConnectionSpec.m */; }; + 7DFBED4E1CDB8DE300EE435B /* RACPropertySignalExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669519EDA60000A782A9 /* RACPropertySignalExamples.m */; }; + 7DFBED4F1CDB8DE300EE435B /* RACSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669619EDA60000A782A9 /* RACSchedulerSpec.m */; }; + 7DFBED501CDB8DE300EE435B /* RACSequenceAdditionsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669719EDA60000A782A9 /* RACSequenceAdditionsSpec.m */; }; + 7DFBED521CDB8DE300EE435B /* RACSequenceExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669919EDA60000A782A9 /* RACSequenceExamples.m */; }; + 7DFBED531CDB8DE300EE435B /* RACSequenceSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669A19EDA60000A782A9 /* RACSequenceSpec.m */; }; + 7DFBED541CDB8DE300EE435B /* RACSerialDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669B19EDA60000A782A9 /* RACSerialDisposableSpec.m */; }; + 7DFBED551CDB8DE300EE435B /* RACSignalSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669C19EDA60000A782A9 /* RACSignalSpec.m */; }; + 7DFBED571CDB8DE300EE435B /* RACStreamExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A019EDA60000A782A9 /* RACStreamExamples.m */; }; + 7DFBED591CDB8DE300EE435B /* RACSubclassObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A219EDA60000A782A9 /* RACSubclassObject.m */; }; + 7DFBED5A1CDB8DE300EE435B /* RACSubjectSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A319EDA60000A782A9 /* RACSubjectSpec.m */; }; + 7DFBED5C1CDB8DE300EE435B /* RACSubscriberExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A519EDA60000A782A9 /* RACSubscriberExamples.m */; }; + 7DFBED5D1CDB8DE300EE435B /* RACSubscriberSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A619EDA60000A782A9 /* RACSubscriberSpec.m */; }; + 7DFBED5E1CDB8DE300EE435B /* RACSubscriptingAssignmentTrampolineSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A719EDA60000A782A9 /* RACSubscriptingAssignmentTrampolineSpec.m */; }; + 7DFBED5F1CDB8DE300EE435B /* RACTargetQueueSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A819EDA60000A782A9 /* RACTargetQueueSchedulerSpec.m */; }; + 7DFBED601CDB8DE300EE435B /* RACTupleSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B019EDA60000A782A9 /* RACTupleSpec.m */; }; + 7DFBED631CDB8DE300EE435B /* UIBarButtonItemRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B419EDA60000A782A9 /* UIBarButtonItemRACSupportSpec.m */; }; + 7DFBED641CDB8DE300EE435B /* UIButtonRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B519EDA60000A782A9 /* UIButtonRACSupportSpec.m */; }; + 7DFBED671CDB8DE300EE435B /* RACTestExampleScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131819EF2D9700984962 /* RACTestExampleScheduler.m */; }; + 7DFBED691CDB8DE300EE435B /* RACTestObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131A19EF2D9700984962 /* RACTestObject.m */; }; + 7DFBED6A1CDB8DE300EE435B /* RACTestSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131B19EF2D9700984962 /* RACTestSchedulerSpec.m */; }; + 7DFBED6C1CDB8DE300EE435B /* RACTestUIButton.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131D19EF2D9700984962 /* RACTestUIButton.m */; }; + 7DFBED6E1CDB918900EE435B /* UIBarButtonItem+RACCommandSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C719EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.m */; }; + 7DFBED6F1CDB926400EE435B /* UIBarButtonItem+RACCommandSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764C619EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A1046B7A1BFF5661004D8045 /* EXTRuntimeExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666719EDA57100A782A9 /* EXTRuntimeExtensions.h */; settings = {ATTRIBUTES = (Private, ); }; }; + A1046B7B1BFF5662004D8045 /* EXTRuntimeExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666719EDA57100A782A9 /* EXTRuntimeExtensions.h */; settings = {ATTRIBUTES = (Private, ); }; }; + A1046B7C1BFF5662004D8045 /* EXTRuntimeExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666719EDA57100A782A9 /* EXTRuntimeExtensions.h */; settings = {ATTRIBUTES = (Private, ); }; }; + A1046B7D1BFF5664004D8045 /* EXTRuntimeExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666719EDA57100A782A9 /* EXTRuntimeExtensions.h */; settings = {ATTRIBUTES = (Private, ); }; }; + A9B3155E1B3940750001CB9C /* EXTRuntimeExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037666819EDA57100A782A9 /* EXTRuntimeExtensions.m */; }; + A9B315601B3940750001CB9C /* NSArray+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037642B19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m */; }; + A9B315631B3940750001CB9C /* NSData+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643119EDA41200A782A9 /* NSData+RACSupport.m */; }; + A9B315641B3940750001CB9C /* NSDictionary+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643319EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m */; }; + A9B315651B3940750001CB9C /* NSEnumerator+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643519EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m */; }; + A9B315661B3940750001CB9C /* NSFileHandle+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643719EDA41200A782A9 /* NSFileHandle+RACSupport.m */; }; + A9B315671B3940750001CB9C /* NSIndexSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643919EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m */; }; + A9B315681B3940750001CB9C /* NSInvocation+RACTypeParsing.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643B19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m */; }; + A9B315691B3940750001CB9C /* NSNotificationCenter+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643D19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m */; }; + A9B3156B1B3940750001CB9C /* NSObject+RACDeallocating.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644119EDA41200A782A9 /* NSObject+RACDeallocating.m */; }; + A9B3156C1B3940750001CB9C /* NSObject+RACDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644319EDA41200A782A9 /* NSObject+RACDescription.m */; }; + A9B3156D1B3940750001CB9C /* NSObject+RACKVOWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644519EDA41200A782A9 /* NSObject+RACKVOWrapper.m */; }; + A9B3156E1B3940750001CB9C /* NSObject+RACLifting.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644719EDA41200A782A9 /* NSObject+RACLifting.m */; }; + A9B3156F1B3940750001CB9C /* NSObject+RACPropertySubscribing.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644919EDA41200A782A9 /* NSObject+RACPropertySubscribing.m */; }; + A9B315701B3940750001CB9C /* NSObject+RACSelectorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644B19EDA41200A782A9 /* NSObject+RACSelectorSignal.m */; }; + A9B315711B3940750001CB9C /* NSOrderedSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644D19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m */; }; + A9B315721B3940750001CB9C /* NSSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644F19EDA41200A782A9 /* NSSet+RACSequenceAdditions.m */; }; + A9B315731B3940750001CB9C /* NSString+RACKeyPathUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645119EDA41200A782A9 /* NSString+RACKeyPathUtilities.m */; }; + A9B315741B3940750001CB9C /* NSString+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645319EDA41200A782A9 /* NSString+RACSequenceAdditions.m */; }; + A9B315751B3940750001CB9C /* NSString+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645519EDA41200A782A9 /* NSString+RACSupport.m */; }; + A9B315781B3940750001CB9C /* NSUserDefaults+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645B19EDA41200A782A9 /* NSUserDefaults+RACSupport.m */; }; + A9B315791B3940750001CB9C /* RACArraySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645D19EDA41200A782A9 /* RACArraySequence.m */; }; + A9B3157A1B3940750001CB9C /* RACBehaviorSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646119EDA41200A782A9 /* RACBehaviorSubject.m */; }; + A9B3157B1B3940750001CB9C /* RACBlockTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646319EDA41200A782A9 /* RACBlockTrampoline.m */; }; + A9B3157C1B3940750001CB9C /* RACChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646519EDA41200A782A9 /* RACChannel.m */; }; + A9B3157D1B3940750001CB9C /* RACCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646719EDA41200A782A9 /* RACCommand.m */; }; + A9B3157E1B3940750001CB9C /* RACCompoundDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646919EDA41200A782A9 /* RACCompoundDisposable.m */; }; + A9B3157F1B3940750001CB9C /* RACDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646C19EDA41200A782A9 /* RACDelegateProxy.m */; }; + A9B315801B3940750001CB9C /* RACDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646E19EDA41200A782A9 /* RACDisposable.m */; }; + A9B315811B3940750001CB9C /* RACDynamicSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647019EDA41200A782A9 /* RACDynamicSequence.m */; }; + A9B315821B3940750001CB9C /* RACDynamicSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647219EDA41200A782A9 /* RACDynamicSignal.m */; }; + A9B315831B3940750001CB9C /* RACEagerSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647419EDA41200A782A9 /* RACEagerSequence.m */; }; + A9B315841B3940750001CB9C /* RACEmptySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647619EDA41200A782A9 /* RACEmptySequence.m */; }; + A9B315851B3940750001CB9C /* RACEmptySignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647819EDA41200A782A9 /* RACEmptySignal.m */; }; + A9B315861B3940750001CB9C /* RACErrorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647A19EDA41200A782A9 /* RACErrorSignal.m */; }; + A9B315871B3940750001CB9C /* RACEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647C19EDA41200A782A9 /* RACEvent.m */; }; + A9B315881B3940750001CB9C /* RACGroupedSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647E19EDA41200A782A9 /* RACGroupedSignal.m */; }; + A9B315891B3940750001CB9C /* RACImmediateScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648019EDA41200A782A9 /* RACImmediateScheduler.m */; }; + A9B3158A1B3940750001CB9C /* RACIndexSetSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648219EDA41200A782A9 /* RACIndexSetSequence.m */; }; + A9B3158B1B3940750001CB9C /* RACKVOChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648419EDA41200A782A9 /* RACKVOChannel.m */; }; + A9B3158C1B3940750001CB9C /* RACKVOProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A70657E1A3F88B8001E8354 /* RACKVOProxy.m */; }; + A9B3158D1B3940750001CB9C /* RACKVOTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648619EDA41200A782A9 /* RACKVOTrampoline.m */; }; + A9B3158E1B3940750001CB9C /* RACMulticastConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648819EDA41200A782A9 /* RACMulticastConnection.m */; }; + A9B315901B3940750001CB9C /* RACPassthroughSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648D19EDA41200A782A9 /* RACPassthroughSubscriber.m */; }; + A9B315911B3940750001CB9C /* RACQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648F19EDA41200A782A9 /* RACQueueScheduler.m */; }; + A9B315921B3940750001CB9C /* RACReplaySubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649219EDA41200A782A9 /* RACReplaySubject.m */; }; + A9B315931B3940750001CB9C /* RACReturnSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649419EDA41200A782A9 /* RACReturnSignal.m */; }; + A9B315941B3940750001CB9C /* RACScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649619EDA41200A782A9 /* RACScheduler.m */; }; + A9B315951B3940750001CB9C /* RACScopedDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649A19EDA41200A782A9 /* RACScopedDisposable.m */; }; + A9B315961B3940750001CB9C /* RACSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649C19EDA41200A782A9 /* RACSequence.m */; }; + A9B315971B3940750001CB9C /* RACSerialDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649E19EDA41200A782A9 /* RACSerialDisposable.m */; }; + A9B315981B3940750001CB9C /* RACSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A019EDA41200A782A9 /* RACSignal.m */; }; + A9B315991B3940750001CB9C /* RACSignal+Operations.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A219EDA41200A782A9 /* RACSignal+Operations.m */; }; + A9B3159A1B3940750001CB9C /* RACSignalSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A519EDA41200A782A9 /* RACSignalSequence.m */; }; + A9B3159B1B3940750001CB9C /* RACStream.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A719EDA41200A782A9 /* RACStream.m */; }; + A9B3159C1B3940750001CB9C /* RACStringSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AA19EDA41200A782A9 /* RACStringSequence.m */; }; + A9B3159D1B3940750001CB9C /* RACSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AC19EDA41200A782A9 /* RACSubject.m */; }; + A9B3159E1B3940750001CB9C /* RACSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AE19EDA41200A782A9 /* RACSubscriber.m */; }; + A9B3159F1B3940750001CB9C /* RACSubscriptingAssignmentTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B119EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m */; }; + A9B315A01B3940750001CB9C /* RACSubscriptionScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B319EDA41200A782A9 /* RACSubscriptionScheduler.m */; }; + A9B315A11B3940750001CB9C /* RACTargetQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B519EDA41200A782A9 /* RACTargetQueueScheduler.m */; }; + A9B315A21B3940750001CB9C /* RACTestScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B719EDA41200A782A9 /* RACTestScheduler.m */; }; + A9B315A31B3940750001CB9C /* RACTuple.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B919EDA41200A782A9 /* RACTuple.m */; }; + A9B315A41B3940750001CB9C /* RACTupleSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BB19EDA41200A782A9 /* RACTupleSequence.m */; }; + A9B315A51B3940750001CB9C /* RACUnarySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BD19EDA41200A782A9 /* RACUnarySequence.m */; }; + A9B315A61B3940750001CB9C /* RACUnit.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BF19EDA41200A782A9 /* RACUnit.m */; }; + A9B315A71B3940750001CB9C /* RACValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C119EDA41200A782A9 /* RACValueTransformer.m */; }; + A9B315CA1B3940AB0001CB9C /* ReactiveObjC.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveObjC.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B315CB1B3940AB0001CB9C /* EXTKeyPathCoding.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666619EDA57100A782A9 /* EXTKeyPathCoding.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B315CD1B3940AB0001CB9C /* EXTScope.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666919EDA57100A782A9 /* EXTScope.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B315CE1B3940AB0001CB9C /* metamacros.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666A19EDA57100A782A9 /* metamacros.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B315D01B3940AB0001CB9C /* NSArray+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037642A19EDA41200A782A9 /* NSArray+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B315D31B3940AB0001CB9C /* NSData+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643019EDA41200A782A9 /* NSData+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B315D41B3940AB0001CB9C /* NSDictionary+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643219EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B315D51B3940AB0001CB9C /* NSEnumerator+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643419EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B315D61B3940AB0001CB9C /* NSFileHandle+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643619EDA41200A782A9 /* NSFileHandle+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B315D71B3940AB0001CB9C /* NSIndexSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643819EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B315D91B3940AB0001CB9C /* NSNotificationCenter+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643C19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B315DB1B3940AB0001CB9C /* NSObject+RACDeallocating.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644019EDA41200A782A9 /* NSObject+RACDeallocating.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B315DE1B3940AB0001CB9C /* NSObject+RACLifting.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644619EDA41200A782A9 /* NSObject+RACLifting.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B315DF1B3940AB0001CB9C /* NSObject+RACPropertySubscribing.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644819EDA41200A782A9 /* NSObject+RACPropertySubscribing.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B315E01B3940AB0001CB9C /* NSObject+RACSelectorSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644A19EDA41200A782A9 /* NSObject+RACSelectorSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B315E11B3940AB0001CB9C /* NSOrderedSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644C19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B315E21B3940AB0001CB9C /* NSSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644E19EDA41200A782A9 /* NSSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B315E41B3940AB0001CB9C /* NSString+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645219EDA41200A782A9 /* NSString+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B315E51B3940AB0001CB9C /* NSString+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645419EDA41200A782A9 /* NSString+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B315E81B3940AB0001CB9C /* NSUserDefaults+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645A19EDA41200A782A9 /* NSUserDefaults+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B315EA1B3940AB0001CB9C /* RACBehaviorSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646019EDA41200A782A9 /* RACBehaviorSubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B315EC1B3940AB0001CB9C /* RACChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646419EDA41200A782A9 /* RACChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B315ED1B3940AC0001CB9C /* RACCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646619EDA41200A782A9 /* RACCommand.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B315EE1B3940AC0001CB9C /* RACCompoundDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646819EDA41200A782A9 /* RACCompoundDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B315F01B3940AC0001CB9C /* RACDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646D19EDA41200A782A9 /* RACDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B315F71B3940AC0001CB9C /* RACEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = D037647B19EDA41200A782A9 /* RACEvent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B315F81B3940AC0001CB9C /* RACGroupedSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037647D19EDA41200A782A9 /* RACGroupedSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B315FB1B3940AC0001CB9C /* RACKVOChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648319EDA41200A782A9 /* RACKVOChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B315FE1B3940AC0001CB9C /* RACMulticastConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648719EDA41200A782A9 /* RACMulticastConnection.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B316021B3940AD0001CB9C /* RACQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648E19EDA41200A782A9 /* RACQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B316031B3940AD0001CB9C /* RACQueueScheduler+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649019EDA41200A782A9 /* RACQueueScheduler+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B316041B3940AD0001CB9C /* RACReplaySubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649119EDA41200A782A9 /* RACReplaySubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B316061B3940AD0001CB9C /* RACScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649519EDA41200A782A9 /* RACScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B316081B3940AD0001CB9C /* RACScheduler+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649819EDA41200A782A9 /* RACScheduler+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B316091B3940AD0001CB9C /* RACScopedDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649919EDA41200A782A9 /* RACScopedDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B3160A1B3940AD0001CB9C /* RACSequence.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649B19EDA41200A782A9 /* RACSequence.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B3160B1B3940AD0001CB9C /* RACSerialDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649D19EDA41200A782A9 /* RACSerialDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B3160C1B3940AE0001CB9C /* RACSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649F19EDA41200A782A9 /* RACSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B3160D1B3940AE0001CB9C /* RACSignal+Operations.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764A119EDA41200A782A9 /* RACSignal+Operations.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B3160F1B3940AE0001CB9C /* RACStream.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764A619EDA41200A782A9 /* RACStream.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B316121B3940AE0001CB9C /* RACSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764AB19EDA41200A782A9 /* RACSubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B316131B3940AE0001CB9C /* RACSubscriber.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764AD19EDA41200A782A9 /* RACSubscriber.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B316151B3940AE0001CB9C /* RACSubscriptingAssignmentTrampoline.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B019EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B316171B3940AF0001CB9C /* RACTargetQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B419EDA41200A782A9 /* RACTargetQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B316181B3940AF0001CB9C /* RACTestScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B619EDA41200A782A9 /* RACTestScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B316191B3940AF0001CB9C /* RACTuple.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B819EDA41200A782A9 /* RACTuple.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B3161C1B3940AF0001CB9C /* RACUnit.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764BE19EDA41200A782A9 /* RACUnit.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9B316341B394C7F0001CB9C /* RACCompoundDisposableProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D037646A19EDA41200A782A9 /* RACCompoundDisposableProvider.d */; }; + A9B316351B394C7F0001CB9C /* RACSignalProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D03764A319EDA41200A782A9 /* RACSignalProvider.d */; }; + BEBDD6E51CDC292D009A75A9 /* RACDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646B19EDA41200A782A9 /* RACDelegateProxy.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BEBDD6E61CDC292D009A75A9 /* RACDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646B19EDA41200A782A9 /* RACDelegateProxy.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BEBDD6E71CDC292E009A75A9 /* RACDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646B19EDA41200A782A9 /* RACDelegateProxy.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BEBDD6E81CDC292F009A75A9 /* RACDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646B19EDA41200A782A9 /* RACDelegateProxy.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CDC42E331AE7AC6D00965373 /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + CDF066CA1CDC1CA200199626 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; }; + CDF066CB1CDC1CA200199626 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; }; + D01B7B6219EDD8FE00D26E01 /* Nimble.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D01B7B6319EDD8FE00D26E01 /* Quick.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D01B7B6419EDD94B00D26E01 /* ReactiveObjC.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D047260C19E49F82006002AA /* ReactiveObjC.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D03764E819EDA41200A782A9 /* NSArray+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037642A19EDA41200A782A9 /* NSArray+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03764E919EDA41200A782A9 /* NSArray+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037642A19EDA41200A782A9 /* NSArray+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03764EA19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037642B19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m */; }; + D03764EB19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037642B19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m */; }; + D03764EC19EDA41200A782A9 /* NSControl+RACCommandSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037642C19EDA41200A782A9 /* NSControl+RACCommandSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03764EE19EDA41200A782A9 /* NSControl+RACCommandSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037642D19EDA41200A782A9 /* NSControl+RACCommandSupport.m */; }; + D03764F019EDA41200A782A9 /* NSControl+RACTextSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037642E19EDA41200A782A9 /* NSControl+RACTextSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03764F219EDA41200A782A9 /* NSControl+RACTextSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037642F19EDA41200A782A9 /* NSControl+RACTextSignalSupport.m */; }; + D03764F419EDA41200A782A9 /* NSData+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643019EDA41200A782A9 /* NSData+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03764F519EDA41200A782A9 /* NSData+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643019EDA41200A782A9 /* NSData+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03764F619EDA41200A782A9 /* NSData+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643119EDA41200A782A9 /* NSData+RACSupport.m */; }; + D03764F719EDA41200A782A9 /* NSData+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643119EDA41200A782A9 /* NSData+RACSupport.m */; }; + D03764F819EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643219EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03764F919EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643219EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03764FA19EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643319EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m */; }; + D03764FB19EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643319EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m */; }; + D03764FC19EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643419EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03764FD19EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643419EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03764FE19EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643519EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m */; }; + D03764FF19EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643519EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m */; }; + D037650019EDA41200A782A9 /* NSFileHandle+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643619EDA41200A782A9 /* NSFileHandle+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037650119EDA41200A782A9 /* NSFileHandle+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643619EDA41200A782A9 /* NSFileHandle+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037650219EDA41200A782A9 /* NSFileHandle+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643719EDA41200A782A9 /* NSFileHandle+RACSupport.m */; }; + D037650319EDA41200A782A9 /* NSFileHandle+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643719EDA41200A782A9 /* NSFileHandle+RACSupport.m */; }; + D037650419EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643819EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037650519EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643819EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037650619EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643919EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m */; }; + D037650719EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643919EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m */; }; + D037650A19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643B19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m */; }; + D037650B19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643B19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m */; }; + D037650C19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643C19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037650D19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643C19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037650E19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643D19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m */; }; + D037650F19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643D19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m */; }; + D037651019EDA41200A782A9 /* NSObject+RACAppKitBindings.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643E19EDA41200A782A9 /* NSObject+RACAppKitBindings.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037651219EDA41200A782A9 /* NSObject+RACAppKitBindings.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643F19EDA41200A782A9 /* NSObject+RACAppKitBindings.m */; }; + D037651419EDA41200A782A9 /* NSObject+RACDeallocating.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644019EDA41200A782A9 /* NSObject+RACDeallocating.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037651519EDA41200A782A9 /* NSObject+RACDeallocating.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644019EDA41200A782A9 /* NSObject+RACDeallocating.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037651619EDA41200A782A9 /* NSObject+RACDeallocating.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644119EDA41200A782A9 /* NSObject+RACDeallocating.m */; }; + D037651719EDA41200A782A9 /* NSObject+RACDeallocating.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644119EDA41200A782A9 /* NSObject+RACDeallocating.m */; }; + D037651A19EDA41200A782A9 /* NSObject+RACDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644319EDA41200A782A9 /* NSObject+RACDescription.m */; }; + D037651B19EDA41200A782A9 /* NSObject+RACDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644319EDA41200A782A9 /* NSObject+RACDescription.m */; }; + D037651E19EDA41200A782A9 /* NSObject+RACKVOWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644519EDA41200A782A9 /* NSObject+RACKVOWrapper.m */; }; + D037651F19EDA41200A782A9 /* NSObject+RACKVOWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644519EDA41200A782A9 /* NSObject+RACKVOWrapper.m */; }; + D037652019EDA41200A782A9 /* NSObject+RACLifting.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644619EDA41200A782A9 /* NSObject+RACLifting.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037652119EDA41200A782A9 /* NSObject+RACLifting.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644619EDA41200A782A9 /* NSObject+RACLifting.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037652219EDA41200A782A9 /* NSObject+RACLifting.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644719EDA41200A782A9 /* NSObject+RACLifting.m */; }; + D037652319EDA41200A782A9 /* NSObject+RACLifting.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644719EDA41200A782A9 /* NSObject+RACLifting.m */; }; + D037652419EDA41200A782A9 /* NSObject+RACPropertySubscribing.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644819EDA41200A782A9 /* NSObject+RACPropertySubscribing.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037652519EDA41200A782A9 /* NSObject+RACPropertySubscribing.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644819EDA41200A782A9 /* NSObject+RACPropertySubscribing.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037652619EDA41200A782A9 /* NSObject+RACPropertySubscribing.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644919EDA41200A782A9 /* NSObject+RACPropertySubscribing.m */; }; + D037652719EDA41200A782A9 /* NSObject+RACPropertySubscribing.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644919EDA41200A782A9 /* NSObject+RACPropertySubscribing.m */; }; + D037652819EDA41200A782A9 /* NSObject+RACSelectorSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644A19EDA41200A782A9 /* NSObject+RACSelectorSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037652919EDA41200A782A9 /* NSObject+RACSelectorSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644A19EDA41200A782A9 /* NSObject+RACSelectorSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037652A19EDA41200A782A9 /* NSObject+RACSelectorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644B19EDA41200A782A9 /* NSObject+RACSelectorSignal.m */; }; + D037652B19EDA41200A782A9 /* NSObject+RACSelectorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644B19EDA41200A782A9 /* NSObject+RACSelectorSignal.m */; }; + D037652C19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644C19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037652D19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644C19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037652E19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644D19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m */; }; + D037652F19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644D19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m */; }; + D037653019EDA41200A782A9 /* NSSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644E19EDA41200A782A9 /* NSSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037653119EDA41200A782A9 /* NSSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644E19EDA41200A782A9 /* NSSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037653219EDA41200A782A9 /* NSSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644F19EDA41200A782A9 /* NSSet+RACSequenceAdditions.m */; }; + D037653319EDA41200A782A9 /* NSSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644F19EDA41200A782A9 /* NSSet+RACSequenceAdditions.m */; }; + D037653619EDA41200A782A9 /* NSString+RACKeyPathUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645119EDA41200A782A9 /* NSString+RACKeyPathUtilities.m */; }; + D037653719EDA41200A782A9 /* NSString+RACKeyPathUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645119EDA41200A782A9 /* NSString+RACKeyPathUtilities.m */; }; + D037653819EDA41200A782A9 /* NSString+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645219EDA41200A782A9 /* NSString+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037653919EDA41200A782A9 /* NSString+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645219EDA41200A782A9 /* NSString+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037653A19EDA41200A782A9 /* NSString+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645319EDA41200A782A9 /* NSString+RACSequenceAdditions.m */; }; + D037653B19EDA41200A782A9 /* NSString+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645319EDA41200A782A9 /* NSString+RACSequenceAdditions.m */; }; + D037653C19EDA41200A782A9 /* NSString+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645419EDA41200A782A9 /* NSString+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037653D19EDA41200A782A9 /* NSString+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645419EDA41200A782A9 /* NSString+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037653E19EDA41200A782A9 /* NSString+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645519EDA41200A782A9 /* NSString+RACSupport.m */; }; + D037653F19EDA41200A782A9 /* NSString+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645519EDA41200A782A9 /* NSString+RACSupport.m */; }; + D037654019EDA41200A782A9 /* NSText+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645619EDA41200A782A9 /* NSText+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037654219EDA41200A782A9 /* NSText+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645719EDA41200A782A9 /* NSText+RACSignalSupport.m */; }; + D037654419EDA41200A782A9 /* NSURLConnection+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645819EDA41200A782A9 /* NSURLConnection+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037654519EDA41200A782A9 /* NSURLConnection+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645819EDA41200A782A9 /* NSURLConnection+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037654619EDA41200A782A9 /* NSURLConnection+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645919EDA41200A782A9 /* NSURLConnection+RACSupport.m */; }; + D037654719EDA41200A782A9 /* NSURLConnection+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645919EDA41200A782A9 /* NSURLConnection+RACSupport.m */; }; + D037654819EDA41200A782A9 /* NSUserDefaults+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645A19EDA41200A782A9 /* NSUserDefaults+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037654919EDA41200A782A9 /* NSUserDefaults+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645A19EDA41200A782A9 /* NSUserDefaults+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037654A19EDA41200A782A9 /* NSUserDefaults+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645B19EDA41200A782A9 /* NSUserDefaults+RACSupport.m */; }; + D037654B19EDA41200A782A9 /* NSUserDefaults+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645B19EDA41200A782A9 /* NSUserDefaults+RACSupport.m */; }; + D037654E19EDA41200A782A9 /* RACArraySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645D19EDA41200A782A9 /* RACArraySequence.m */; }; + D037654F19EDA41200A782A9 /* RACArraySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645D19EDA41200A782A9 /* RACArraySequence.m */; }; + D037655619EDA41200A782A9 /* RACBehaviorSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646119EDA41200A782A9 /* RACBehaviorSubject.m */; }; + D037655719EDA41200A782A9 /* RACBehaviorSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646119EDA41200A782A9 /* RACBehaviorSubject.m */; }; + D037655A19EDA41200A782A9 /* RACBlockTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646319EDA41200A782A9 /* RACBlockTrampoline.m */; }; + D037655B19EDA41200A782A9 /* RACBlockTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646319EDA41200A782A9 /* RACBlockTrampoline.m */; }; + D037655C19EDA41200A782A9 /* RACChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646419EDA41200A782A9 /* RACChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037655D19EDA41200A782A9 /* RACChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646419EDA41200A782A9 /* RACChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037655E19EDA41200A782A9 /* RACChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646519EDA41200A782A9 /* RACChannel.m */; }; + D037655F19EDA41200A782A9 /* RACChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646519EDA41200A782A9 /* RACChannel.m */; }; + D037656019EDA41200A782A9 /* RACCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646619EDA41200A782A9 /* RACCommand.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037656119EDA41200A782A9 /* RACCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646619EDA41200A782A9 /* RACCommand.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037656219EDA41200A782A9 /* RACCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646719EDA41200A782A9 /* RACCommand.m */; }; + D037656319EDA41200A782A9 /* RACCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646719EDA41200A782A9 /* RACCommand.m */; }; + D037656419EDA41200A782A9 /* RACCompoundDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646819EDA41200A782A9 /* RACCompoundDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037656519EDA41200A782A9 /* RACCompoundDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646819EDA41200A782A9 /* RACCompoundDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037656619EDA41200A782A9 /* RACCompoundDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646919EDA41200A782A9 /* RACCompoundDisposable.m */; }; + D037656719EDA41200A782A9 /* RACCompoundDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646919EDA41200A782A9 /* RACCompoundDisposable.m */; }; + D037656819EDA41200A782A9 /* RACCompoundDisposableProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D037646A19EDA41200A782A9 /* RACCompoundDisposableProvider.d */; }; + D037656919EDA41200A782A9 /* RACCompoundDisposableProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D037646A19EDA41200A782A9 /* RACCompoundDisposableProvider.d */; }; + D037656C19EDA41200A782A9 /* RACDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646C19EDA41200A782A9 /* RACDelegateProxy.m */; }; + D037656D19EDA41200A782A9 /* RACDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646C19EDA41200A782A9 /* RACDelegateProxy.m */; }; + D037656E19EDA41200A782A9 /* RACDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646D19EDA41200A782A9 /* RACDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037656F19EDA41200A782A9 /* RACDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646D19EDA41200A782A9 /* RACDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037657019EDA41200A782A9 /* RACDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646E19EDA41200A782A9 /* RACDisposable.m */; }; + D037657119EDA41200A782A9 /* RACDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646E19EDA41200A782A9 /* RACDisposable.m */; }; + D037657419EDA41200A782A9 /* RACDynamicSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647019EDA41200A782A9 /* RACDynamicSequence.m */; }; + D037657519EDA41200A782A9 /* RACDynamicSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647019EDA41200A782A9 /* RACDynamicSequence.m */; }; + D037657819EDA41200A782A9 /* RACDynamicSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647219EDA41200A782A9 /* RACDynamicSignal.m */; }; + D037657919EDA41200A782A9 /* RACDynamicSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647219EDA41200A782A9 /* RACDynamicSignal.m */; }; + D037657C19EDA41200A782A9 /* RACEagerSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647419EDA41200A782A9 /* RACEagerSequence.m */; }; + D037657D19EDA41200A782A9 /* RACEagerSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647419EDA41200A782A9 /* RACEagerSequence.m */; }; + D037658019EDA41200A782A9 /* RACEmptySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647619EDA41200A782A9 /* RACEmptySequence.m */; }; + D037658119EDA41200A782A9 /* RACEmptySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647619EDA41200A782A9 /* RACEmptySequence.m */; }; + D037658419EDA41200A782A9 /* RACEmptySignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647819EDA41200A782A9 /* RACEmptySignal.m */; }; + D037658519EDA41200A782A9 /* RACEmptySignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647819EDA41200A782A9 /* RACEmptySignal.m */; }; + D037658819EDA41200A782A9 /* RACErrorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647A19EDA41200A782A9 /* RACErrorSignal.m */; }; + D037658919EDA41200A782A9 /* RACErrorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647A19EDA41200A782A9 /* RACErrorSignal.m */; }; + D037658A19EDA41200A782A9 /* RACEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = D037647B19EDA41200A782A9 /* RACEvent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037658B19EDA41200A782A9 /* RACEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = D037647B19EDA41200A782A9 /* RACEvent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037658C19EDA41200A782A9 /* RACEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647C19EDA41200A782A9 /* RACEvent.m */; }; + D037658D19EDA41200A782A9 /* RACEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647C19EDA41200A782A9 /* RACEvent.m */; }; + D037658E19EDA41200A782A9 /* RACGroupedSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037647D19EDA41200A782A9 /* RACGroupedSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037658F19EDA41200A782A9 /* RACGroupedSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037647D19EDA41200A782A9 /* RACGroupedSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037659019EDA41200A782A9 /* RACGroupedSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647E19EDA41200A782A9 /* RACGroupedSignal.m */; }; + D037659119EDA41200A782A9 /* RACGroupedSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647E19EDA41200A782A9 /* RACGroupedSignal.m */; }; + D037659419EDA41200A782A9 /* RACImmediateScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648019EDA41200A782A9 /* RACImmediateScheduler.m */; }; + D037659519EDA41200A782A9 /* RACImmediateScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648019EDA41200A782A9 /* RACImmediateScheduler.m */; }; + D037659819EDA41200A782A9 /* RACIndexSetSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648219EDA41200A782A9 /* RACIndexSetSequence.m */; }; + D037659919EDA41200A782A9 /* RACIndexSetSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648219EDA41200A782A9 /* RACIndexSetSequence.m */; }; + D037659A19EDA41200A782A9 /* RACKVOChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648319EDA41200A782A9 /* RACKVOChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037659B19EDA41200A782A9 /* RACKVOChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648319EDA41200A782A9 /* RACKVOChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037659C19EDA41200A782A9 /* RACKVOChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648419EDA41200A782A9 /* RACKVOChannel.m */; }; + D037659D19EDA41200A782A9 /* RACKVOChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648419EDA41200A782A9 /* RACKVOChannel.m */; }; + D03765A019EDA41200A782A9 /* RACKVOTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648619EDA41200A782A9 /* RACKVOTrampoline.m */; }; + D03765A119EDA41200A782A9 /* RACKVOTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648619EDA41200A782A9 /* RACKVOTrampoline.m */; }; + D03765A219EDA41200A782A9 /* RACMulticastConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648719EDA41200A782A9 /* RACMulticastConnection.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765A319EDA41200A782A9 /* RACMulticastConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648719EDA41200A782A9 /* RACMulticastConnection.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765A419EDA41200A782A9 /* RACMulticastConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648819EDA41200A782A9 /* RACMulticastConnection.m */; }; + D03765A519EDA41200A782A9 /* RACMulticastConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648819EDA41200A782A9 /* RACMulticastConnection.m */; }; + D03765AE19EDA41200A782A9 /* RACPassthroughSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648D19EDA41200A782A9 /* RACPassthroughSubscriber.m */; }; + D03765AF19EDA41200A782A9 /* RACPassthroughSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648D19EDA41200A782A9 /* RACPassthroughSubscriber.m */; }; + D03765B019EDA41200A782A9 /* RACQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648E19EDA41200A782A9 /* RACQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765B119EDA41200A782A9 /* RACQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648E19EDA41200A782A9 /* RACQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765B219EDA41200A782A9 /* RACQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648F19EDA41200A782A9 /* RACQueueScheduler.m */; }; + D03765B319EDA41200A782A9 /* RACQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648F19EDA41200A782A9 /* RACQueueScheduler.m */; }; + D03765B419EDA41200A782A9 /* RACQueueScheduler+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649019EDA41200A782A9 /* RACQueueScheduler+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765B519EDA41200A782A9 /* RACQueueScheduler+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649019EDA41200A782A9 /* RACQueueScheduler+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765B619EDA41200A782A9 /* RACReplaySubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649119EDA41200A782A9 /* RACReplaySubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765B719EDA41200A782A9 /* RACReplaySubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649119EDA41200A782A9 /* RACReplaySubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765B819EDA41200A782A9 /* RACReplaySubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649219EDA41200A782A9 /* RACReplaySubject.m */; }; + D03765B919EDA41200A782A9 /* RACReplaySubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649219EDA41200A782A9 /* RACReplaySubject.m */; }; + D03765BC19EDA41200A782A9 /* RACReturnSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649419EDA41200A782A9 /* RACReturnSignal.m */; }; + D03765BD19EDA41200A782A9 /* RACReturnSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649419EDA41200A782A9 /* RACReturnSignal.m */; }; + D03765BE19EDA41200A782A9 /* RACScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649519EDA41200A782A9 /* RACScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765BF19EDA41200A782A9 /* RACScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649519EDA41200A782A9 /* RACScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765C019EDA41200A782A9 /* RACScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649619EDA41200A782A9 /* RACScheduler.m */; }; + D03765C119EDA41200A782A9 /* RACScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649619EDA41200A782A9 /* RACScheduler.m */; }; + D03765C419EDA41200A782A9 /* RACScheduler+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649819EDA41200A782A9 /* RACScheduler+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765C519EDA41200A782A9 /* RACScheduler+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649819EDA41200A782A9 /* RACScheduler+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765C619EDA41200A782A9 /* RACScopedDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649919EDA41200A782A9 /* RACScopedDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765C719EDA41200A782A9 /* RACScopedDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649919EDA41200A782A9 /* RACScopedDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765C819EDA41200A782A9 /* RACScopedDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649A19EDA41200A782A9 /* RACScopedDisposable.m */; }; + D03765C919EDA41200A782A9 /* RACScopedDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649A19EDA41200A782A9 /* RACScopedDisposable.m */; }; + D03765CA19EDA41200A782A9 /* RACSequence.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649B19EDA41200A782A9 /* RACSequence.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765CB19EDA41200A782A9 /* RACSequence.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649B19EDA41200A782A9 /* RACSequence.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765CC19EDA41200A782A9 /* RACSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649C19EDA41200A782A9 /* RACSequence.m */; }; + D03765CD19EDA41200A782A9 /* RACSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649C19EDA41200A782A9 /* RACSequence.m */; }; + D03765CE19EDA41200A782A9 /* RACSerialDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649D19EDA41200A782A9 /* RACSerialDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765CF19EDA41200A782A9 /* RACSerialDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649D19EDA41200A782A9 /* RACSerialDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765D019EDA41200A782A9 /* RACSerialDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649E19EDA41200A782A9 /* RACSerialDisposable.m */; }; + D03765D119EDA41200A782A9 /* RACSerialDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649E19EDA41200A782A9 /* RACSerialDisposable.m */; }; + D03765D219EDA41200A782A9 /* RACSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649F19EDA41200A782A9 /* RACSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765D319EDA41200A782A9 /* RACSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649F19EDA41200A782A9 /* RACSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765D419EDA41200A782A9 /* RACSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A019EDA41200A782A9 /* RACSignal.m */; }; + D03765D519EDA41200A782A9 /* RACSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A019EDA41200A782A9 /* RACSignal.m */; }; + D03765D619EDA41200A782A9 /* RACSignal+Operations.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764A119EDA41200A782A9 /* RACSignal+Operations.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765D719EDA41200A782A9 /* RACSignal+Operations.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764A119EDA41200A782A9 /* RACSignal+Operations.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765D819EDA41200A782A9 /* RACSignal+Operations.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A219EDA41200A782A9 /* RACSignal+Operations.m */; }; + D03765D919EDA41200A782A9 /* RACSignal+Operations.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A219EDA41200A782A9 /* RACSignal+Operations.m */; }; + D03765DA19EDA41200A782A9 /* RACSignalProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D03764A319EDA41200A782A9 /* RACSignalProvider.d */; }; + D03765DB19EDA41200A782A9 /* RACSignalProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D03764A319EDA41200A782A9 /* RACSignalProvider.d */; }; + D03765DE19EDA41200A782A9 /* RACSignalSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A519EDA41200A782A9 /* RACSignalSequence.m */; }; + D03765DF19EDA41200A782A9 /* RACSignalSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A519EDA41200A782A9 /* RACSignalSequence.m */; }; + D03765E019EDA41200A782A9 /* RACStream.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764A619EDA41200A782A9 /* RACStream.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765E119EDA41200A782A9 /* RACStream.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764A619EDA41200A782A9 /* RACStream.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765E219EDA41200A782A9 /* RACStream.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A719EDA41200A782A9 /* RACStream.m */; }; + D03765E319EDA41200A782A9 /* RACStream.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A719EDA41200A782A9 /* RACStream.m */; }; + D03765E819EDA41200A782A9 /* RACStringSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AA19EDA41200A782A9 /* RACStringSequence.m */; }; + D03765E919EDA41200A782A9 /* RACStringSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AA19EDA41200A782A9 /* RACStringSequence.m */; }; + D03765EA19EDA41200A782A9 /* RACSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764AB19EDA41200A782A9 /* RACSubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765EB19EDA41200A782A9 /* RACSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764AB19EDA41200A782A9 /* RACSubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765EC19EDA41200A782A9 /* RACSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AC19EDA41200A782A9 /* RACSubject.m */; }; + D03765ED19EDA41200A782A9 /* RACSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AC19EDA41200A782A9 /* RACSubject.m */; }; + D03765EE19EDA41200A782A9 /* RACSubscriber.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764AD19EDA41200A782A9 /* RACSubscriber.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765EF19EDA41200A782A9 /* RACSubscriber.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764AD19EDA41200A782A9 /* RACSubscriber.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765F019EDA41200A782A9 /* RACSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AE19EDA41200A782A9 /* RACSubscriber.m */; }; + D03765F119EDA41200A782A9 /* RACSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AE19EDA41200A782A9 /* RACSubscriber.m */; }; + D03765F419EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B019EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765F519EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B019EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765F619EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B119EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m */; }; + D03765F719EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B119EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m */; }; + D03765FA19EDA41200A782A9 /* RACSubscriptionScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B319EDA41200A782A9 /* RACSubscriptionScheduler.m */; }; + D03765FB19EDA41200A782A9 /* RACSubscriptionScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B319EDA41200A782A9 /* RACSubscriptionScheduler.m */; }; + D03765FC19EDA41200A782A9 /* RACTargetQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B419EDA41200A782A9 /* RACTargetQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765FD19EDA41200A782A9 /* RACTargetQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B419EDA41200A782A9 /* RACTargetQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03765FE19EDA41200A782A9 /* RACTargetQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B519EDA41200A782A9 /* RACTargetQueueScheduler.m */; }; + D03765FF19EDA41200A782A9 /* RACTargetQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B519EDA41200A782A9 /* RACTargetQueueScheduler.m */; }; + D037660019EDA41200A782A9 /* RACTestScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B619EDA41200A782A9 /* RACTestScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037660119EDA41200A782A9 /* RACTestScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B619EDA41200A782A9 /* RACTestScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037660219EDA41200A782A9 /* RACTestScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B719EDA41200A782A9 /* RACTestScheduler.m */; }; + D037660319EDA41200A782A9 /* RACTestScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B719EDA41200A782A9 /* RACTestScheduler.m */; }; + D037660419EDA41200A782A9 /* RACTuple.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B819EDA41200A782A9 /* RACTuple.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037660519EDA41200A782A9 /* RACTuple.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B819EDA41200A782A9 /* RACTuple.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037660619EDA41200A782A9 /* RACTuple.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B919EDA41200A782A9 /* RACTuple.m */; }; + D037660719EDA41200A782A9 /* RACTuple.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B919EDA41200A782A9 /* RACTuple.m */; }; + D037660A19EDA41200A782A9 /* RACTupleSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BB19EDA41200A782A9 /* RACTupleSequence.m */; }; + D037660B19EDA41200A782A9 /* RACTupleSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BB19EDA41200A782A9 /* RACTupleSequence.m */; }; + D037660E19EDA41200A782A9 /* RACUnarySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BD19EDA41200A782A9 /* RACUnarySequence.m */; }; + D037660F19EDA41200A782A9 /* RACUnarySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BD19EDA41200A782A9 /* RACUnarySequence.m */; }; + D037661019EDA41200A782A9 /* RACUnit.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764BE19EDA41200A782A9 /* RACUnit.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037661119EDA41200A782A9 /* RACUnit.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764BE19EDA41200A782A9 /* RACUnit.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037661219EDA41200A782A9 /* RACUnit.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BF19EDA41200A782A9 /* RACUnit.m */; }; + D037661319EDA41200A782A9 /* RACUnit.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BF19EDA41200A782A9 /* RACUnit.m */; }; + D037661619EDA41200A782A9 /* RACValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C119EDA41200A782A9 /* RACValueTransformer.m */; }; + D037661719EDA41200A782A9 /* RACValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C119EDA41200A782A9 /* RACValueTransformer.m */; }; + D037661919EDA41200A782A9 /* UIActionSheet+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764C219EDA41200A782A9 /* UIActionSheet+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037661B19EDA41200A782A9 /* UIActionSheet+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C319EDA41200A782A9 /* UIActionSheet+RACSignalSupport.m */; }; + D037661D19EDA41200A782A9 /* UIAlertView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764C419EDA41200A782A9 /* UIAlertView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037661F19EDA41200A782A9 /* UIAlertView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C519EDA41200A782A9 /* UIAlertView+RACSignalSupport.m */; }; + D037662119EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764C619EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037662319EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C719EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.m */; }; + D037662519EDA41200A782A9 /* UIButton+RACCommandSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764C819EDA41200A782A9 /* UIButton+RACCommandSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037662719EDA41200A782A9 /* UIButton+RACCommandSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C919EDA41200A782A9 /* UIButton+RACCommandSupport.m */; }; + D037662919EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764CA19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037662B19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764CB19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.m */; }; + D037662D19EDA41200A782A9 /* UIControl+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764CC19EDA41200A782A9 /* UIControl+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037662F19EDA41200A782A9 /* UIControl+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764CD19EDA41200A782A9 /* UIControl+RACSignalSupport.m */; }; + D037663319EDA41200A782A9 /* UIControl+RACSignalSupportPrivate.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764CF19EDA41200A782A9 /* UIControl+RACSignalSupportPrivate.m */; }; + D037663519EDA41200A782A9 /* UIDatePicker+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764D019EDA41200A782A9 /* UIDatePicker+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037663719EDA41200A782A9 /* UIDatePicker+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764D119EDA41200A782A9 /* UIDatePicker+RACSignalSupport.m */; }; + D037663919EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764D219EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037663B19EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764D319EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.m */; }; + D037663D19EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764D419EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037663F19EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764D519EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.m */; }; + D037664119EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764D619EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037664319EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764D719EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.m */; }; + D037664519EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764D819EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037664719EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764D919EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.m */; }; + D037664919EDA41200A782A9 /* UISlider+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764DA19EDA41200A782A9 /* UISlider+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037664B19EDA41200A782A9 /* UISlider+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764DB19EDA41200A782A9 /* UISlider+RACSignalSupport.m */; }; + D037664D19EDA41200A782A9 /* UIStepper+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764DC19EDA41200A782A9 /* UIStepper+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037664F19EDA41200A782A9 /* UIStepper+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764DD19EDA41200A782A9 /* UIStepper+RACSignalSupport.m */; }; + D037665119EDA41200A782A9 /* UISwitch+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764DE19EDA41200A782A9 /* UISwitch+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037665319EDA41200A782A9 /* UISwitch+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764DF19EDA41200A782A9 /* UISwitch+RACSignalSupport.m */; }; + D037665519EDA41200A782A9 /* UITableViewCell+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764E019EDA41200A782A9 /* UITableViewCell+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037665719EDA41200A782A9 /* UITableViewCell+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764E119EDA41200A782A9 /* UITableViewCell+RACSignalSupport.m */; }; + D037665919EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764E219EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037665B19EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764E319EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.m */; }; + D037665D19EDA41200A782A9 /* UITextField+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764E419EDA41200A782A9 /* UITextField+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037665F19EDA41200A782A9 /* UITextField+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764E519EDA41200A782A9 /* UITextField+RACSignalSupport.m */; }; + D037666119EDA41200A782A9 /* UITextView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764E619EDA41200A782A9 /* UITextView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037666319EDA41200A782A9 /* UITextView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764E719EDA41200A782A9 /* UITextView+RACSignalSupport.m */; }; + D037666419EDA43C00A782A9 /* ReactiveObjC.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveObjC.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037666B19EDA57100A782A9 /* EXTKeyPathCoding.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666619EDA57100A782A9 /* EXTKeyPathCoding.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037666C19EDA57100A782A9 /* EXTKeyPathCoding.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666619EDA57100A782A9 /* EXTKeyPathCoding.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037666F19EDA57100A782A9 /* EXTRuntimeExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037666819EDA57100A782A9 /* EXTRuntimeExtensions.m */; }; + D037667019EDA57100A782A9 /* EXTRuntimeExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037666819EDA57100A782A9 /* EXTRuntimeExtensions.m */; }; + D037667119EDA57100A782A9 /* EXTScope.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666919EDA57100A782A9 /* EXTScope.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037667219EDA57100A782A9 /* EXTScope.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666919EDA57100A782A9 /* EXTScope.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037667319EDA57100A782A9 /* metamacros.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666A19EDA57100A782A9 /* metamacros.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037667419EDA57100A782A9 /* metamacros.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666A19EDA57100A782A9 /* metamacros.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03766B919EDA60000A782A9 /* NSControllerRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667619EDA60000A782A9 /* NSControllerRACSupportSpec.m */; }; + D03766BD19EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667819EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m */; }; + D03766BE19EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667819EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m */; }; + D03766BF19EDA60000A782A9 /* NSNotificationCenterRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667919EDA60000A782A9 /* NSNotificationCenterRACSupportSpec.m */; }; + D03766C019EDA60000A782A9 /* NSNotificationCenterRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667919EDA60000A782A9 /* NSNotificationCenterRACSupportSpec.m */; }; + D03766C119EDA60000A782A9 /* NSObjectRACAppKitBindingsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667A19EDA60000A782A9 /* NSObjectRACAppKitBindingsSpec.m */; }; + D03766C319EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667B19EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m */; }; + D03766C419EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667B19EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m */; }; + D03766C519EDA60000A782A9 /* NSObjectRACLiftingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667C19EDA60000A782A9 /* NSObjectRACLiftingSpec.m */; }; + D03766C619EDA60000A782A9 /* NSObjectRACLiftingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667C19EDA60000A782A9 /* NSObjectRACLiftingSpec.m */; }; + D03766C719EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667E19EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.m */; }; + D03766C819EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667E19EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.m */; }; + D03766C919EDA60000A782A9 /* NSObjectRACPropertySubscribingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667F19EDA60000A782A9 /* NSObjectRACPropertySubscribingSpec.m */; }; + D03766CA19EDA60000A782A9 /* NSObjectRACPropertySubscribingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667F19EDA60000A782A9 /* NSObjectRACPropertySubscribingSpec.m */; }; + D03766CB19EDA60000A782A9 /* NSObjectRACSelectorSignalSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668019EDA60000A782A9 /* NSObjectRACSelectorSignalSpec.m */; }; + D03766CC19EDA60000A782A9 /* NSObjectRACSelectorSignalSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668019EDA60000A782A9 /* NSObjectRACSelectorSignalSpec.m */; }; + D03766CD19EDA60000A782A9 /* NSStringRACKeyPathUtilitiesSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668119EDA60000A782A9 /* NSStringRACKeyPathUtilitiesSpec.m */; }; + D03766CE19EDA60000A782A9 /* NSStringRACKeyPathUtilitiesSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668119EDA60000A782A9 /* NSStringRACKeyPathUtilitiesSpec.m */; }; + D03766D119EDA60000A782A9 /* NSURLConnectionRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668319EDA60000A782A9 /* NSURLConnectionRACSupportSpec.m */; }; + D03766D219EDA60000A782A9 /* NSURLConnectionRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668319EDA60000A782A9 /* NSURLConnectionRACSupportSpec.m */; }; + D03766D319EDA60000A782A9 /* NSUserDefaultsRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668419EDA60000A782A9 /* NSUserDefaultsRACSupportSpec.m */; }; + D03766D419EDA60000A782A9 /* NSUserDefaultsRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668419EDA60000A782A9 /* NSUserDefaultsRACSupportSpec.m */; }; + D03766D719EDA60000A782A9 /* RACBlockTrampolineSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668619EDA60000A782A9 /* RACBlockTrampolineSpec.m */; }; + D03766D819EDA60000A782A9 /* RACBlockTrampolineSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668619EDA60000A782A9 /* RACBlockTrampolineSpec.m */; }; + D03766D919EDA60000A782A9 /* RACChannelExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668819EDA60000A782A9 /* RACChannelExamples.m */; }; + D03766DA19EDA60000A782A9 /* RACChannelExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668819EDA60000A782A9 /* RACChannelExamples.m */; }; + D03766DB19EDA60000A782A9 /* RACChannelSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668919EDA60000A782A9 /* RACChannelSpec.m */; }; + D03766DC19EDA60000A782A9 /* RACChannelSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668919EDA60000A782A9 /* RACChannelSpec.m */; }; + D03766DD19EDA60000A782A9 /* RACCommandSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668A19EDA60000A782A9 /* RACCommandSpec.m */; }; + D03766DE19EDA60000A782A9 /* RACCommandSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668A19EDA60000A782A9 /* RACCommandSpec.m */; }; + D03766DF19EDA60000A782A9 /* RACCompoundDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668B19EDA60000A782A9 /* RACCompoundDisposableSpec.m */; }; + D03766E019EDA60000A782A9 /* RACCompoundDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668B19EDA60000A782A9 /* RACCompoundDisposableSpec.m */; }; + D03766E119EDA60000A782A9 /* RACControlCommandExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668D19EDA60000A782A9 /* RACControlCommandExamples.m */; }; + D03766E219EDA60000A782A9 /* RACControlCommandExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668D19EDA60000A782A9 /* RACControlCommandExamples.m */; }; + D03766E319EDA60000A782A9 /* RACDelegateProxySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668E19EDA60000A782A9 /* RACDelegateProxySpec.m */; }; + D03766E419EDA60000A782A9 /* RACDelegateProxySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668E19EDA60000A782A9 /* RACDelegateProxySpec.m */; }; + D03766E519EDA60000A782A9 /* RACDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668F19EDA60000A782A9 /* RACDisposableSpec.m */; }; + D03766E619EDA60000A782A9 /* RACDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668F19EDA60000A782A9 /* RACDisposableSpec.m */; }; + D03766E719EDA60000A782A9 /* RACEventSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669019EDA60000A782A9 /* RACEventSpec.m */; }; + D03766E819EDA60000A782A9 /* RACEventSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669019EDA60000A782A9 /* RACEventSpec.m */; }; + D03766E919EDA60000A782A9 /* RACKVOChannelSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669119EDA60000A782A9 /* RACKVOChannelSpec.m */; }; + D03766EA19EDA60000A782A9 /* RACKVOChannelSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669119EDA60000A782A9 /* RACKVOChannelSpec.m */; }; + D03766EB19EDA60000A782A9 /* RACKVOWrapperSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669219EDA60000A782A9 /* RACKVOWrapperSpec.m */; }; + D03766EC19EDA60000A782A9 /* RACKVOWrapperSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669219EDA60000A782A9 /* RACKVOWrapperSpec.m */; }; + D03766ED19EDA60000A782A9 /* RACMulticastConnectionSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669319EDA60000A782A9 /* RACMulticastConnectionSpec.m */; }; + D03766EE19EDA60000A782A9 /* RACMulticastConnectionSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669319EDA60000A782A9 /* RACMulticastConnectionSpec.m */; }; + D03766EF19EDA60000A782A9 /* RACPropertySignalExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669519EDA60000A782A9 /* RACPropertySignalExamples.m */; }; + D03766F019EDA60000A782A9 /* RACPropertySignalExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669519EDA60000A782A9 /* RACPropertySignalExamples.m */; }; + D03766F119EDA60000A782A9 /* RACSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669619EDA60000A782A9 /* RACSchedulerSpec.m */; }; + D03766F219EDA60000A782A9 /* RACSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669619EDA60000A782A9 /* RACSchedulerSpec.m */; }; + D03766F319EDA60000A782A9 /* RACSequenceAdditionsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669719EDA60000A782A9 /* RACSequenceAdditionsSpec.m */; }; + D03766F419EDA60000A782A9 /* RACSequenceAdditionsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669719EDA60000A782A9 /* RACSequenceAdditionsSpec.m */; }; + D03766F519EDA60000A782A9 /* RACSequenceExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669919EDA60000A782A9 /* RACSequenceExamples.m */; }; + D03766F619EDA60000A782A9 /* RACSequenceExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669919EDA60000A782A9 /* RACSequenceExamples.m */; }; + D03766F719EDA60000A782A9 /* RACSequenceSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669A19EDA60000A782A9 /* RACSequenceSpec.m */; }; + D03766F819EDA60000A782A9 /* RACSequenceSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669A19EDA60000A782A9 /* RACSequenceSpec.m */; }; + D03766F919EDA60000A782A9 /* RACSerialDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669B19EDA60000A782A9 /* RACSerialDisposableSpec.m */; }; + D03766FA19EDA60000A782A9 /* RACSerialDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669B19EDA60000A782A9 /* RACSerialDisposableSpec.m */; }; + D03766FB19EDA60000A782A9 /* RACSignalSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669C19EDA60000A782A9 /* RACSignalSpec.m */; }; + D03766FC19EDA60000A782A9 /* RACSignalSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669C19EDA60000A782A9 /* RACSignalSpec.m */; }; + D03766FF19EDA60000A782A9 /* RACStreamExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A019EDA60000A782A9 /* RACStreamExamples.m */; }; + D037670019EDA60000A782A9 /* RACStreamExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A019EDA60000A782A9 /* RACStreamExamples.m */; }; + D037670119EDA60000A782A9 /* RACSubclassObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A219EDA60000A782A9 /* RACSubclassObject.m */; }; + D037670219EDA60000A782A9 /* RACSubclassObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A219EDA60000A782A9 /* RACSubclassObject.m */; }; + D037670319EDA60000A782A9 /* RACSubjectSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A319EDA60000A782A9 /* RACSubjectSpec.m */; }; + D037670419EDA60000A782A9 /* RACSubjectSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A319EDA60000A782A9 /* RACSubjectSpec.m */; }; + D037670519EDA60000A782A9 /* RACSubscriberExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A519EDA60000A782A9 /* RACSubscriberExamples.m */; }; + D037670619EDA60000A782A9 /* RACSubscriberExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A519EDA60000A782A9 /* RACSubscriberExamples.m */; }; + D037670719EDA60000A782A9 /* RACSubscriberSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A619EDA60000A782A9 /* RACSubscriberSpec.m */; }; + D037670819EDA60000A782A9 /* RACSubscriberSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A619EDA60000A782A9 /* RACSubscriberSpec.m */; }; + D037670919EDA60000A782A9 /* RACSubscriptingAssignmentTrampolineSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A719EDA60000A782A9 /* RACSubscriptingAssignmentTrampolineSpec.m */; }; + D037670A19EDA60000A782A9 /* RACSubscriptingAssignmentTrampolineSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A719EDA60000A782A9 /* RACSubscriptingAssignmentTrampolineSpec.m */; }; + D037670B19EDA60000A782A9 /* RACTargetQueueSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A819EDA60000A782A9 /* RACTargetQueueSchedulerSpec.m */; }; + D037670C19EDA60000A782A9 /* RACTargetQueueSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A819EDA60000A782A9 /* RACTargetQueueSchedulerSpec.m */; }; + D037671519EDA60000A782A9 /* RACTupleSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B019EDA60000A782A9 /* RACTupleSpec.m */; }; + D037671619EDA60000A782A9 /* RACTupleSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B019EDA60000A782A9 /* RACTupleSpec.m */; }; + D037671719EDA60000A782A9 /* test-data.json in Resources */ = {isa = PBXBuildFile; fileRef = D03766B119EDA60000A782A9 /* test-data.json */; }; + D037671819EDA60000A782A9 /* test-data.json in Resources */ = {isa = PBXBuildFile; fileRef = D03766B119EDA60000A782A9 /* test-data.json */; }; + D037671A19EDA60000A782A9 /* UIActionSheetRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B219EDA60000A782A9 /* UIActionSheetRACSupportSpec.m */; }; + D037671C19EDA60000A782A9 /* UIAlertViewRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B319EDA60000A782A9 /* UIAlertViewRACSupportSpec.m */; }; + D037671E19EDA60000A782A9 /* UIBarButtonItemRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B419EDA60000A782A9 /* UIBarButtonItemRACSupportSpec.m */; }; + D037672019EDA60000A782A9 /* UIButtonRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B519EDA60000A782A9 /* UIButtonRACSupportSpec.m */; }; + D037672419EDA60000A782A9 /* UIImagePickerControllerRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B719EDA60000A782A9 /* UIImagePickerControllerRACSupportSpec.m */; }; + D037672719EDA63400A782A9 /* RACBehaviorSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646019EDA41200A782A9 /* RACBehaviorSubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037672819EDA63500A782A9 /* RACBehaviorSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646019EDA41200A782A9 /* RACBehaviorSubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037672D19EDA75D00A782A9 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; }; + D037672F19EDA78B00A782A9 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; }; + D04725F019E49ED7006002AA /* ReactiveObjC.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveObjC.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D04725F619E49ED7006002AA /* ReactiveObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D04725EA19E49ED7006002AA /* ReactiveObjC.framework */; }; + D047261719E49F82006002AA /* ReactiveObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D047260C19E49F82006002AA /* ReactiveObjC.framework */; }; + D05E662519EDD82000904ACA /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; }; + D05E662619EDD83000904ACA /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; }; + D0C3131E19EF2D9700984962 /* RACTestExampleScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131819EF2D9700984962 /* RACTestExampleScheduler.m */; }; + D0C3131F19EF2D9700984962 /* RACTestExampleScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131819EF2D9700984962 /* RACTestExampleScheduler.m */; }; + D0C3132019EF2D9700984962 /* RACTestObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131A19EF2D9700984962 /* RACTestObject.m */; }; + D0C3132119EF2D9700984962 /* RACTestObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131A19EF2D9700984962 /* RACTestObject.m */; }; + D0C3132219EF2D9700984962 /* RACTestSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131B19EF2D9700984962 /* RACTestSchedulerSpec.m */; }; + D0C3132319EF2D9700984962 /* RACTestSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131B19EF2D9700984962 /* RACTestSchedulerSpec.m */; }; + D0C3132519EF2D9700984962 /* RACTestUIButton.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131D19EF2D9700984962 /* RACTestUIButton.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 7DFBED091CDB8C9500EE435B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D04725E119E49ED7006002AA /* Project object */; + proxyType = 1; + remoteGlobalIDString = 57A4D1AF1BA13D7A00F7D4B1; + remoteInfo = "ReactiveCocoa-tvOS"; + }; + D04725F719E49ED7006002AA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D04725E119E49ED7006002AA /* Project object */; + proxyType = 1; + remoteGlobalIDString = D04725E919E49ED7006002AA; + remoteInfo = ReactiveCocoa; + }; + D047261819E49F82006002AA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D04725E119E49ED7006002AA /* Project object */; + proxyType = 1; + remoteGlobalIDString = D047260B19E49F82006002AA; + remoteInfo = ReactiveCocoa; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 7DFBED151CDB8CEC00EE435B /* Copy Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 7DFBED211CDB8D8300EE435B /* Result.framework in Copy Frameworks */, + 7DFBED201CDB8D7D00EE435B /* Nimble.framework in Copy Frameworks */, + 7DFBED1F1CDB8D7800EE435B /* Quick.framework in Copy Frameworks */, + 7DFBED1E1CDB8D7000EE435B /* ReactiveObjC.framework in Copy Frameworks */, + ); + name = "Copy Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; + D01B7B6119EDD8F600D26E01 /* Copy Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + CDC42E331AE7AC6D00965373 /* Result.framework in Copy Frameworks */, + D01B7B6219EDD8FE00D26E01 /* Nimble.framework in Copy Frameworks */, + D01B7B6319EDD8FE00D26E01 /* Quick.framework in Copy Frameworks */, + D01B7B6419EDD94B00D26E01 /* ReactiveObjC.framework in Copy Frameworks */, + ); + name = "Copy Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 314304151ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MKAnnotationView+RACSignalSupport.h"; sourceTree = ""; }; + 314304161ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MKAnnotationView+RACSignalSupport.m"; sourceTree = ""; }; + 57A4D2411BA13D7A00F7D4B1 /* ReactiveObjC.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveObjC.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Application.xcconfig"; sourceTree = ""; }; + 57A4D2451BA13F9700F7D4B1 /* tvOS-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Base.xcconfig"; sourceTree = ""; }; + 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Framework.xcconfig"; sourceTree = ""; }; + 57A4D2471BA13F9700F7D4B1 /* tvOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-StaticLibrary.xcconfig"; sourceTree = ""; }; + 7A70657D1A3F88B8001E8354 /* RACKVOProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACKVOProxy.h; sourceTree = ""; }; + 7A70657E1A3F88B8001E8354 /* RACKVOProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACKVOProxy.m; sourceTree = ""; }; + 7A7065831A3F8967001E8354 /* RACKVOProxySpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACKVOProxySpec.m; sourceTree = ""; }; + 7DFBED031CDB8C9500EE435B /* ReactiveObjCTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveObjCTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + A97451331B3A935E00F48E55 /* watchOS-Application.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Application.xcconfig"; sourceTree = ""; }; + A97451341B3A935E00F48E55 /* watchOS-Base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Base.xcconfig"; sourceTree = ""; }; + A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Framework.xcconfig"; sourceTree = ""; }; + A97451361B3A935E00F48E55 /* watchOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-StaticLibrary.xcconfig"; sourceTree = ""; }; + A9B315541B3940610001CB9C /* ReactiveObjC.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveObjC.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BE330A0E1D634F1E00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = build/Debug/ReactiveSwift.framework; sourceTree = ""; }; + BE330A101D634F2900806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = build/Debug/ReactiveSwift.framework; sourceTree = ""; }; + BE330A121D634F2E00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-iphoneos/ReactiveSwift.framework"; sourceTree = ""; }; + BE330A141D634F4000806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-iphoneos/ReactiveSwift.framework"; sourceTree = ""; }; + BE330A161D634F4E00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-watchos/ReactiveSwift.framework"; sourceTree = ""; }; + BE330A181D634F5900806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-appletvos/ReactiveSwift.framework"; sourceTree = ""; }; + BE330A1A1D634F5F00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-appletvos/ReactiveSwift.framework"; sourceTree = ""; }; + CDC42E2E1AE7AB8B00965373 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D037642A19EDA41200A782A9 /* NSArray+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+RACSequenceAdditions.h"; sourceTree = ""; }; + D037642B19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+RACSequenceAdditions.m"; sourceTree = ""; }; + D037642C19EDA41200A782A9 /* NSControl+RACCommandSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSControl+RACCommandSupport.h"; sourceTree = ""; }; + D037642D19EDA41200A782A9 /* NSControl+RACCommandSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSControl+RACCommandSupport.m"; sourceTree = ""; }; + D037642E19EDA41200A782A9 /* NSControl+RACTextSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSControl+RACTextSignalSupport.h"; sourceTree = ""; }; + D037642F19EDA41200A782A9 /* NSControl+RACTextSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSControl+RACTextSignalSupport.m"; sourceTree = ""; }; + D037643019EDA41200A782A9 /* NSData+RACSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+RACSupport.h"; sourceTree = ""; }; + D037643119EDA41200A782A9 /* NSData+RACSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+RACSupport.m"; sourceTree = ""; }; + D037643219EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+RACSequenceAdditions.h"; sourceTree = ""; }; + D037643319EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+RACSequenceAdditions.m"; sourceTree = ""; }; + D037643419EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSEnumerator+RACSequenceAdditions.h"; sourceTree = ""; }; + D037643519EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSEnumerator+RACSequenceAdditions.m"; sourceTree = ""; }; + D037643619EDA41200A782A9 /* NSFileHandle+RACSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSFileHandle+RACSupport.h"; sourceTree = ""; }; + D037643719EDA41200A782A9 /* NSFileHandle+RACSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSFileHandle+RACSupport.m"; sourceTree = ""; }; + D037643819EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSIndexSet+RACSequenceAdditions.h"; sourceTree = ""; }; + D037643919EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSIndexSet+RACSequenceAdditions.m"; sourceTree = ""; }; + D037643A19EDA41200A782A9 /* NSInvocation+RACTypeParsing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSInvocation+RACTypeParsing.h"; sourceTree = ""; }; + D037643B19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSInvocation+RACTypeParsing.m"; sourceTree = ""; }; + D037643C19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNotificationCenter+RACSupport.h"; sourceTree = ""; }; + D037643D19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSNotificationCenter+RACSupport.m"; sourceTree = ""; }; + D037643E19EDA41200A782A9 /* NSObject+RACAppKitBindings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACAppKitBindings.h"; sourceTree = ""; }; + D037643F19EDA41200A782A9 /* NSObject+RACAppKitBindings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACAppKitBindings.m"; sourceTree = ""; }; + D037644019EDA41200A782A9 /* NSObject+RACDeallocating.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACDeallocating.h"; sourceTree = ""; }; + D037644119EDA41200A782A9 /* NSObject+RACDeallocating.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACDeallocating.m"; sourceTree = ""; }; + D037644219EDA41200A782A9 /* NSObject+RACDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACDescription.h"; sourceTree = ""; }; + D037644319EDA41200A782A9 /* NSObject+RACDescription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACDescription.m"; sourceTree = ""; }; + D037644419EDA41200A782A9 /* NSObject+RACKVOWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACKVOWrapper.h"; sourceTree = ""; }; + D037644519EDA41200A782A9 /* NSObject+RACKVOWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACKVOWrapper.m"; sourceTree = ""; }; + D037644619EDA41200A782A9 /* NSObject+RACLifting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACLifting.h"; sourceTree = ""; }; + D037644719EDA41200A782A9 /* NSObject+RACLifting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACLifting.m"; sourceTree = ""; }; + D037644819EDA41200A782A9 /* NSObject+RACPropertySubscribing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACPropertySubscribing.h"; sourceTree = ""; }; + D037644919EDA41200A782A9 /* NSObject+RACPropertySubscribing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACPropertySubscribing.m"; sourceTree = ""; }; + D037644A19EDA41200A782A9 /* NSObject+RACSelectorSignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACSelectorSignal.h"; sourceTree = ""; }; + D037644B19EDA41200A782A9 /* NSObject+RACSelectorSignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACSelectorSignal.m"; sourceTree = ""; }; + D037644C19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSOrderedSet+RACSequenceAdditions.h"; sourceTree = ""; }; + D037644D19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSOrderedSet+RACSequenceAdditions.m"; sourceTree = ""; }; + D037644E19EDA41200A782A9 /* NSSet+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSSet+RACSequenceAdditions.h"; sourceTree = ""; }; + D037644F19EDA41200A782A9 /* NSSet+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSSet+RACSequenceAdditions.m"; sourceTree = ""; }; + D037645019EDA41200A782A9 /* NSString+RACKeyPathUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+RACKeyPathUtilities.h"; sourceTree = ""; }; + D037645119EDA41200A782A9 /* NSString+RACKeyPathUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+RACKeyPathUtilities.m"; sourceTree = ""; }; + D037645219EDA41200A782A9 /* NSString+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+RACSequenceAdditions.h"; sourceTree = ""; }; + D037645319EDA41200A782A9 /* NSString+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+RACSequenceAdditions.m"; sourceTree = ""; }; + D037645419EDA41200A782A9 /* NSString+RACSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+RACSupport.h"; sourceTree = ""; }; + D037645519EDA41200A782A9 /* NSString+RACSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+RACSupport.m"; sourceTree = ""; }; + D037645619EDA41200A782A9 /* NSText+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSText+RACSignalSupport.h"; sourceTree = ""; }; + D037645719EDA41200A782A9 /* NSText+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSText+RACSignalSupport.m"; sourceTree = ""; }; + D037645819EDA41200A782A9 /* NSURLConnection+RACSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSURLConnection+RACSupport.h"; sourceTree = ""; }; + D037645919EDA41200A782A9 /* NSURLConnection+RACSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSURLConnection+RACSupport.m"; sourceTree = ""; }; + D037645A19EDA41200A782A9 /* NSUserDefaults+RACSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSUserDefaults+RACSupport.h"; sourceTree = ""; }; + D037645B19EDA41200A782A9 /* NSUserDefaults+RACSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSUserDefaults+RACSupport.m"; sourceTree = ""; }; + D037645C19EDA41200A782A9 /* RACArraySequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACArraySequence.h; sourceTree = ""; }; + D037645D19EDA41200A782A9 /* RACArraySequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACArraySequence.m; sourceTree = ""; }; + D037646019EDA41200A782A9 /* RACBehaviorSubject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACBehaviorSubject.h; sourceTree = ""; }; + D037646119EDA41200A782A9 /* RACBehaviorSubject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACBehaviorSubject.m; sourceTree = ""; }; + D037646219EDA41200A782A9 /* RACBlockTrampoline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACBlockTrampoline.h; sourceTree = ""; }; + D037646319EDA41200A782A9 /* RACBlockTrampoline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACBlockTrampoline.m; sourceTree = ""; }; + D037646419EDA41200A782A9 /* RACChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACChannel.h; sourceTree = ""; }; + D037646519EDA41200A782A9 /* RACChannel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACChannel.m; sourceTree = ""; }; + D037646619EDA41200A782A9 /* RACCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACCommand.h; sourceTree = ""; }; + D037646719EDA41200A782A9 /* RACCommand.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACCommand.m; sourceTree = ""; }; + D037646819EDA41200A782A9 /* RACCompoundDisposable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACCompoundDisposable.h; sourceTree = ""; }; + D037646919EDA41200A782A9 /* RACCompoundDisposable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACCompoundDisposable.m; sourceTree = ""; }; + D037646A19EDA41200A782A9 /* RACCompoundDisposableProvider.d */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.dtrace; path = RACCompoundDisposableProvider.d; sourceTree = ""; }; + D037646B19EDA41200A782A9 /* RACDelegateProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACDelegateProxy.h; sourceTree = ""; }; + D037646C19EDA41200A782A9 /* RACDelegateProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACDelegateProxy.m; sourceTree = ""; }; + D037646D19EDA41200A782A9 /* RACDisposable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACDisposable.h; sourceTree = ""; }; + D037646E19EDA41200A782A9 /* RACDisposable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACDisposable.m; sourceTree = ""; }; + D037646F19EDA41200A782A9 /* RACDynamicSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACDynamicSequence.h; sourceTree = ""; }; + D037647019EDA41200A782A9 /* RACDynamicSequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACDynamicSequence.m; sourceTree = ""; }; + D037647119EDA41200A782A9 /* RACDynamicSignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACDynamicSignal.h; sourceTree = ""; }; + D037647219EDA41200A782A9 /* RACDynamicSignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACDynamicSignal.m; sourceTree = ""; }; + D037647319EDA41200A782A9 /* RACEagerSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACEagerSequence.h; sourceTree = ""; }; + D037647419EDA41200A782A9 /* RACEagerSequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACEagerSequence.m; sourceTree = ""; }; + D037647519EDA41200A782A9 /* RACEmptySequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACEmptySequence.h; sourceTree = ""; }; + D037647619EDA41200A782A9 /* RACEmptySequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACEmptySequence.m; sourceTree = ""; }; + D037647719EDA41200A782A9 /* RACEmptySignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACEmptySignal.h; sourceTree = ""; }; + D037647819EDA41200A782A9 /* RACEmptySignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACEmptySignal.m; sourceTree = ""; }; + D037647919EDA41200A782A9 /* RACErrorSignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACErrorSignal.h; sourceTree = ""; }; + D037647A19EDA41200A782A9 /* RACErrorSignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACErrorSignal.m; sourceTree = ""; }; + D037647B19EDA41200A782A9 /* RACEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACEvent.h; sourceTree = ""; }; + D037647C19EDA41200A782A9 /* RACEvent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACEvent.m; sourceTree = ""; }; + D037647D19EDA41200A782A9 /* RACGroupedSignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACGroupedSignal.h; sourceTree = ""; }; + D037647E19EDA41200A782A9 /* RACGroupedSignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACGroupedSignal.m; sourceTree = ""; }; + D037647F19EDA41200A782A9 /* RACImmediateScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACImmediateScheduler.h; sourceTree = ""; }; + D037648019EDA41200A782A9 /* RACImmediateScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACImmediateScheduler.m; sourceTree = ""; }; + D037648119EDA41200A782A9 /* RACIndexSetSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACIndexSetSequence.h; sourceTree = ""; }; + D037648219EDA41200A782A9 /* RACIndexSetSequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACIndexSetSequence.m; sourceTree = ""; }; + D037648319EDA41200A782A9 /* RACKVOChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACKVOChannel.h; sourceTree = ""; }; + D037648419EDA41200A782A9 /* RACKVOChannel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACKVOChannel.m; sourceTree = ""; }; + D037648519EDA41200A782A9 /* RACKVOTrampoline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACKVOTrampoline.h; sourceTree = ""; }; + D037648619EDA41200A782A9 /* RACKVOTrampoline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACKVOTrampoline.m; sourceTree = ""; }; + D037648719EDA41200A782A9 /* RACMulticastConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACMulticastConnection.h; sourceTree = ""; }; + D037648819EDA41200A782A9 /* RACMulticastConnection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACMulticastConnection.m; sourceTree = ""; }; + D037648919EDA41200A782A9 /* RACMulticastConnection+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RACMulticastConnection+Private.h"; sourceTree = ""; }; + D037648C19EDA41200A782A9 /* RACPassthroughSubscriber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACPassthroughSubscriber.h; sourceTree = ""; }; + D037648D19EDA41200A782A9 /* RACPassthroughSubscriber.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACPassthroughSubscriber.m; sourceTree = ""; }; + D037648E19EDA41200A782A9 /* RACQueueScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACQueueScheduler.h; sourceTree = ""; }; + D037648F19EDA41200A782A9 /* RACQueueScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACQueueScheduler.m; sourceTree = ""; }; + D037649019EDA41200A782A9 /* RACQueueScheduler+Subclass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RACQueueScheduler+Subclass.h"; sourceTree = ""; }; + D037649119EDA41200A782A9 /* RACReplaySubject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACReplaySubject.h; sourceTree = ""; }; + D037649219EDA41200A782A9 /* RACReplaySubject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACReplaySubject.m; sourceTree = ""; }; + D037649319EDA41200A782A9 /* RACReturnSignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACReturnSignal.h; sourceTree = ""; }; + D037649419EDA41200A782A9 /* RACReturnSignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACReturnSignal.m; sourceTree = ""; }; + D037649519EDA41200A782A9 /* RACScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACScheduler.h; sourceTree = ""; }; + D037649619EDA41200A782A9 /* RACScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACScheduler.m; sourceTree = ""; }; + D037649719EDA41200A782A9 /* RACScheduler+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RACScheduler+Private.h"; sourceTree = ""; }; + D037649819EDA41200A782A9 /* RACScheduler+Subclass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RACScheduler+Subclass.h"; sourceTree = ""; }; + D037649919EDA41200A782A9 /* RACScopedDisposable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACScopedDisposable.h; sourceTree = ""; }; + D037649A19EDA41200A782A9 /* RACScopedDisposable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACScopedDisposable.m; sourceTree = ""; }; + D037649B19EDA41200A782A9 /* RACSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSequence.h; sourceTree = ""; }; + D037649C19EDA41200A782A9 /* RACSequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSequence.m; sourceTree = ""; }; + D037649D19EDA41200A782A9 /* RACSerialDisposable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSerialDisposable.h; sourceTree = ""; }; + D037649E19EDA41200A782A9 /* RACSerialDisposable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSerialDisposable.m; sourceTree = ""; }; + D037649F19EDA41200A782A9 /* RACSignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSignal.h; sourceTree = ""; }; + D03764A019EDA41200A782A9 /* RACSignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSignal.m; sourceTree = ""; }; + D03764A119EDA41200A782A9 /* RACSignal+Operations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RACSignal+Operations.h"; sourceTree = ""; }; + D03764A219EDA41200A782A9 /* RACSignal+Operations.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RACSignal+Operations.m"; sourceTree = ""; }; + D03764A319EDA41200A782A9 /* RACSignalProvider.d */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.dtrace; path = RACSignalProvider.d; sourceTree = ""; }; + D03764A419EDA41200A782A9 /* RACSignalSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSignalSequence.h; sourceTree = ""; }; + D03764A519EDA41200A782A9 /* RACSignalSequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSignalSequence.m; sourceTree = ""; }; + D03764A619EDA41200A782A9 /* RACStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACStream.h; sourceTree = ""; }; + D03764A719EDA41200A782A9 /* RACStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACStream.m; sourceTree = ""; }; + D03764A819EDA41200A782A9 /* RACStream+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RACStream+Private.h"; sourceTree = ""; }; + D03764A919EDA41200A782A9 /* RACStringSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACStringSequence.h; sourceTree = ""; }; + D03764AA19EDA41200A782A9 /* RACStringSequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACStringSequence.m; sourceTree = ""; }; + D03764AB19EDA41200A782A9 /* RACSubject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSubject.h; sourceTree = ""; }; + D03764AC19EDA41200A782A9 /* RACSubject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubject.m; sourceTree = ""; }; + D03764AD19EDA41200A782A9 /* RACSubscriber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSubscriber.h; sourceTree = ""; }; + D03764AE19EDA41200A782A9 /* RACSubscriber.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubscriber.m; sourceTree = ""; }; + D03764AF19EDA41200A782A9 /* RACSubscriber+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RACSubscriber+Private.h"; sourceTree = ""; }; + D03764B019EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSubscriptingAssignmentTrampoline.h; sourceTree = ""; }; + D03764B119EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubscriptingAssignmentTrampoline.m; sourceTree = ""; }; + D03764B219EDA41200A782A9 /* RACSubscriptionScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSubscriptionScheduler.h; sourceTree = ""; }; + D03764B319EDA41200A782A9 /* RACSubscriptionScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubscriptionScheduler.m; sourceTree = ""; }; + D03764B419EDA41200A782A9 /* RACTargetQueueScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTargetQueueScheduler.h; sourceTree = ""; }; + D03764B519EDA41200A782A9 /* RACTargetQueueScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTargetQueueScheduler.m; sourceTree = ""; }; + D03764B619EDA41200A782A9 /* RACTestScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTestScheduler.h; sourceTree = ""; }; + D03764B719EDA41200A782A9 /* RACTestScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTestScheduler.m; sourceTree = ""; }; + D03764B819EDA41200A782A9 /* RACTuple.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTuple.h; sourceTree = ""; }; + D03764B919EDA41200A782A9 /* RACTuple.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTuple.m; sourceTree = ""; }; + D03764BA19EDA41200A782A9 /* RACTupleSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTupleSequence.h; sourceTree = ""; }; + D03764BB19EDA41200A782A9 /* RACTupleSequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTupleSequence.m; sourceTree = ""; }; + D03764BC19EDA41200A782A9 /* RACUnarySequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACUnarySequence.h; sourceTree = ""; }; + D03764BD19EDA41200A782A9 /* RACUnarySequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACUnarySequence.m; sourceTree = ""; }; + D03764BE19EDA41200A782A9 /* RACUnit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACUnit.h; sourceTree = ""; }; + D03764BF19EDA41200A782A9 /* RACUnit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACUnit.m; sourceTree = ""; }; + D03764C019EDA41200A782A9 /* RACValueTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACValueTransformer.h; sourceTree = ""; }; + D03764C119EDA41200A782A9 /* RACValueTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACValueTransformer.m; sourceTree = ""; }; + D03764C219EDA41200A782A9 /* UIActionSheet+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIActionSheet+RACSignalSupport.h"; sourceTree = ""; }; + D03764C319EDA41200A782A9 /* UIActionSheet+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIActionSheet+RACSignalSupport.m"; sourceTree = ""; }; + D03764C419EDA41200A782A9 /* UIAlertView+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIAlertView+RACSignalSupport.h"; sourceTree = ""; }; + D03764C519EDA41200A782A9 /* UIAlertView+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIAlertView+RACSignalSupport.m"; sourceTree = ""; }; + D03764C619EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIBarButtonItem+RACCommandSupport.h"; sourceTree = ""; }; + D03764C719EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIBarButtonItem+RACCommandSupport.m"; sourceTree = ""; }; + D03764C819EDA41200A782A9 /* UIButton+RACCommandSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIButton+RACCommandSupport.h"; sourceTree = ""; }; + D03764C919EDA41200A782A9 /* UIButton+RACCommandSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIButton+RACCommandSupport.m"; sourceTree = ""; }; + D03764CA19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionReusableView+RACSignalSupport.h"; sourceTree = ""; }; + D03764CB19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionReusableView+RACSignalSupport.m"; sourceTree = ""; }; + D03764CC19EDA41200A782A9 /* UIControl+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIControl+RACSignalSupport.h"; sourceTree = ""; }; + D03764CD19EDA41200A782A9 /* UIControl+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIControl+RACSignalSupport.m"; sourceTree = ""; }; + D03764CE19EDA41200A782A9 /* UIControl+RACSignalSupportPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIControl+RACSignalSupportPrivate.h"; sourceTree = ""; }; + D03764CF19EDA41200A782A9 /* UIControl+RACSignalSupportPrivate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIControl+RACSignalSupportPrivate.m"; sourceTree = ""; }; + D03764D019EDA41200A782A9 /* UIDatePicker+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIDatePicker+RACSignalSupport.h"; sourceTree = ""; }; + D03764D119EDA41200A782A9 /* UIDatePicker+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIDatePicker+RACSignalSupport.m"; sourceTree = ""; }; + D03764D219EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIGestureRecognizer+RACSignalSupport.h"; sourceTree = ""; }; + D03764D319EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIGestureRecognizer+RACSignalSupport.m"; sourceTree = ""; }; + D03764D419EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImagePickerController+RACSignalSupport.h"; sourceTree = ""; }; + D03764D519EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImagePickerController+RACSignalSupport.m"; sourceTree = ""; }; + D03764D619EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIRefreshControl+RACCommandSupport.h"; sourceTree = ""; }; + D03764D719EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIRefreshControl+RACCommandSupport.m"; sourceTree = ""; }; + D03764D819EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UISegmentedControl+RACSignalSupport.h"; sourceTree = ""; }; + D03764D919EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UISegmentedControl+RACSignalSupport.m"; sourceTree = ""; }; + D03764DA19EDA41200A782A9 /* UISlider+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UISlider+RACSignalSupport.h"; sourceTree = ""; }; + D03764DB19EDA41200A782A9 /* UISlider+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UISlider+RACSignalSupport.m"; sourceTree = ""; }; + D03764DC19EDA41200A782A9 /* UIStepper+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIStepper+RACSignalSupport.h"; sourceTree = ""; }; + D03764DD19EDA41200A782A9 /* UIStepper+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIStepper+RACSignalSupport.m"; sourceTree = ""; }; + D03764DE19EDA41200A782A9 /* UISwitch+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UISwitch+RACSignalSupport.h"; sourceTree = ""; }; + D03764DF19EDA41200A782A9 /* UISwitch+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UISwitch+RACSignalSupport.m"; sourceTree = ""; }; + D03764E019EDA41200A782A9 /* UITableViewCell+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableViewCell+RACSignalSupport.h"; sourceTree = ""; }; + D03764E119EDA41200A782A9 /* UITableViewCell+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITableViewCell+RACSignalSupport.m"; sourceTree = ""; }; + D03764E219EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableViewHeaderFooterView+RACSignalSupport.h"; sourceTree = ""; }; + D03764E319EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITableViewHeaderFooterView+RACSignalSupport.m"; sourceTree = ""; }; + D03764E419EDA41200A782A9 /* UITextField+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITextField+RACSignalSupport.h"; sourceTree = ""; }; + D03764E519EDA41200A782A9 /* UITextField+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITextField+RACSignalSupport.m"; sourceTree = ""; }; + D03764E619EDA41200A782A9 /* UITextView+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITextView+RACSignalSupport.h"; sourceTree = ""; }; + D03764E719EDA41200A782A9 /* UITextView+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITextView+RACSignalSupport.m"; sourceTree = ""; }; + D037666619EDA57100A782A9 /* EXTKeyPathCoding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EXTKeyPathCoding.h; sourceTree = ""; }; + D037666719EDA57100A782A9 /* EXTRuntimeExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EXTRuntimeExtensions.h; sourceTree = ""; }; + D037666819EDA57100A782A9 /* EXTRuntimeExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EXTRuntimeExtensions.m; sourceTree = ""; }; + D037666919EDA57100A782A9 /* EXTScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EXTScope.h; sourceTree = ""; }; + D037666A19EDA57100A782A9 /* metamacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = metamacros.h; sourceTree = ""; }; + D037667619EDA60000A782A9 /* NSControllerRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSControllerRACSupportSpec.m; sourceTree = ""; }; + D037667819EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSEnumeratorRACSequenceAdditionsSpec.m; sourceTree = ""; }; + D037667919EDA60000A782A9 /* NSNotificationCenterRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSNotificationCenterRACSupportSpec.m; sourceTree = ""; }; + D037667A19EDA60000A782A9 /* NSObjectRACAppKitBindingsSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSObjectRACAppKitBindingsSpec.m; sourceTree = ""; }; + D037667B19EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSObjectRACDeallocatingSpec.m; sourceTree = ""; }; + D037667C19EDA60000A782A9 /* NSObjectRACLiftingSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSObjectRACLiftingSpec.m; sourceTree = ""; }; + D037667D19EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSObjectRACPropertySubscribingExamples.h; sourceTree = ""; }; + D037667E19EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSObjectRACPropertySubscribingExamples.m; sourceTree = ""; }; + D037667F19EDA60000A782A9 /* NSObjectRACPropertySubscribingSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSObjectRACPropertySubscribingSpec.m; sourceTree = ""; }; + D037668019EDA60000A782A9 /* NSObjectRACSelectorSignalSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSObjectRACSelectorSignalSpec.m; sourceTree = ""; }; + D037668119EDA60000A782A9 /* NSStringRACKeyPathUtilitiesSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSStringRACKeyPathUtilitiesSpec.m; sourceTree = ""; }; + D037668319EDA60000A782A9 /* NSURLConnectionRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSURLConnectionRACSupportSpec.m; sourceTree = ""; }; + D037668419EDA60000A782A9 /* NSUserDefaultsRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSUserDefaultsRACSupportSpec.m; sourceTree = ""; }; + D037668619EDA60000A782A9 /* RACBlockTrampolineSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACBlockTrampolineSpec.m; sourceTree = ""; }; + D037668719EDA60000A782A9 /* RACChannelExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACChannelExamples.h; sourceTree = ""; }; + D037668819EDA60000A782A9 /* RACChannelExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACChannelExamples.m; sourceTree = ""; }; + D037668919EDA60000A782A9 /* RACChannelSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACChannelSpec.m; sourceTree = ""; }; + D037668A19EDA60000A782A9 /* RACCommandSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACCommandSpec.m; sourceTree = ""; }; + D037668B19EDA60000A782A9 /* RACCompoundDisposableSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACCompoundDisposableSpec.m; sourceTree = ""; }; + D037668C19EDA60000A782A9 /* RACControlCommandExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACControlCommandExamples.h; sourceTree = ""; }; + D037668D19EDA60000A782A9 /* RACControlCommandExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACControlCommandExamples.m; sourceTree = ""; }; + D037668E19EDA60000A782A9 /* RACDelegateProxySpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACDelegateProxySpec.m; sourceTree = ""; }; + D037668F19EDA60000A782A9 /* RACDisposableSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACDisposableSpec.m; sourceTree = ""; }; + D037669019EDA60000A782A9 /* RACEventSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACEventSpec.m; sourceTree = ""; }; + D037669119EDA60000A782A9 /* RACKVOChannelSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACKVOChannelSpec.m; sourceTree = ""; }; + D037669219EDA60000A782A9 /* RACKVOWrapperSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACKVOWrapperSpec.m; sourceTree = ""; }; + D037669319EDA60000A782A9 /* RACMulticastConnectionSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACMulticastConnectionSpec.m; sourceTree = ""; }; + D037669419EDA60000A782A9 /* RACPropertySignalExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACPropertySignalExamples.h; sourceTree = ""; }; + D037669519EDA60000A782A9 /* RACPropertySignalExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACPropertySignalExamples.m; sourceTree = ""; }; + D037669619EDA60000A782A9 /* RACSchedulerSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSchedulerSpec.m; sourceTree = ""; }; + D037669719EDA60000A782A9 /* RACSequenceAdditionsSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSequenceAdditionsSpec.m; sourceTree = ""; }; + D037669819EDA60000A782A9 /* RACSequenceExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSequenceExamples.h; sourceTree = ""; }; + D037669919EDA60000A782A9 /* RACSequenceExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSequenceExamples.m; sourceTree = ""; }; + D037669A19EDA60000A782A9 /* RACSequenceSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSequenceSpec.m; sourceTree = ""; }; + D037669B19EDA60000A782A9 /* RACSerialDisposableSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSerialDisposableSpec.m; sourceTree = ""; }; + D037669C19EDA60000A782A9 /* RACSignalSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSignalSpec.m; sourceTree = ""; }; + D037669F19EDA60000A782A9 /* RACStreamExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACStreamExamples.h; sourceTree = ""; }; + D03766A019EDA60000A782A9 /* RACStreamExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACStreamExamples.m; sourceTree = ""; }; + D03766A119EDA60000A782A9 /* RACSubclassObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSubclassObject.h; sourceTree = ""; }; + D03766A219EDA60000A782A9 /* RACSubclassObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubclassObject.m; sourceTree = ""; }; + D03766A319EDA60000A782A9 /* RACSubjectSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubjectSpec.m; sourceTree = ""; }; + D03766A419EDA60000A782A9 /* RACSubscriberExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSubscriberExamples.h; sourceTree = ""; }; + D03766A519EDA60000A782A9 /* RACSubscriberExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubscriberExamples.m; sourceTree = ""; }; + D03766A619EDA60000A782A9 /* RACSubscriberSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubscriberSpec.m; sourceTree = ""; }; + D03766A719EDA60000A782A9 /* RACSubscriptingAssignmentTrampolineSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubscriptingAssignmentTrampolineSpec.m; sourceTree = ""; }; + D03766A819EDA60000A782A9 /* RACTargetQueueSchedulerSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTargetQueueSchedulerSpec.m; sourceTree = ""; }; + D03766B019EDA60000A782A9 /* RACTupleSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTupleSpec.m; sourceTree = ""; }; + D03766B119EDA60000A782A9 /* test-data.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "test-data.json"; sourceTree = ""; }; + D03766B219EDA60000A782A9 /* UIActionSheetRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIActionSheetRACSupportSpec.m; sourceTree = ""; }; + D03766B319EDA60000A782A9 /* UIAlertViewRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIAlertViewRACSupportSpec.m; sourceTree = ""; }; + D03766B419EDA60000A782A9 /* UIBarButtonItemRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIBarButtonItemRACSupportSpec.m; sourceTree = ""; }; + D03766B519EDA60000A782A9 /* UIButtonRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIButtonRACSupportSpec.m; sourceTree = ""; }; + D03766B719EDA60000A782A9 /* UIImagePickerControllerRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIImagePickerControllerRACSupportSpec.m; sourceTree = ""; }; + D037672B19EDA75D00A782A9 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Quick.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D04725EA19E49ED7006002AA /* ReactiveObjC.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveObjC.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D04725EE19E49ED7006002AA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D04725EF19E49ED7006002AA /* ReactiveObjC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ReactiveObjC.h; sourceTree = ""; }; + D04725F519E49ED7006002AA /* ReactiveObjCTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveObjCTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + D04725FB19E49ED7006002AA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D047260C19E49F82006002AA /* ReactiveObjC.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveObjC.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D047261619E49F82006002AA /* ReactiveObjCTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveObjCTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + D047262719E49FE8006002AA /* Common.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Common.xcconfig; sourceTree = ""; }; + D047262919E49FE8006002AA /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + D047262A19E49FE8006002AA /* Profile.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Profile.xcconfig; sourceTree = ""; }; + D047262B19E49FE8006002AA /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + D047262C19E49FE8006002AA /* Test.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Test.xcconfig; sourceTree = ""; }; + D047262E19E49FE8006002AA /* Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Application.xcconfig; sourceTree = ""; }; + D047262F19E49FE8006002AA /* Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Framework.xcconfig; sourceTree = ""; }; + D047263019E49FE8006002AA /* StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = StaticLibrary.xcconfig; sourceTree = ""; }; + D047263219E49FE8006002AA /* iOS-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-Application.xcconfig"; sourceTree = ""; }; + D047263319E49FE8006002AA /* iOS-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-Base.xcconfig"; sourceTree = ""; }; + D047263419E49FE8006002AA /* iOS-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-Framework.xcconfig"; sourceTree = ""; }; + D047263519E49FE8006002AA /* iOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-StaticLibrary.xcconfig"; sourceTree = ""; }; + D047263719E49FE8006002AA /* Mac-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Application.xcconfig"; sourceTree = ""; }; + D047263819E49FE8006002AA /* Mac-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Base.xcconfig"; sourceTree = ""; }; + D047263919E49FE8006002AA /* Mac-DynamicLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-DynamicLibrary.xcconfig"; sourceTree = ""; }; + D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Framework.xcconfig"; sourceTree = ""; }; + D047263B19E49FE8006002AA /* Mac-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-StaticLibrary.xcconfig"; sourceTree = ""; }; + D047263C19E49FE8006002AA /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + D05E662419EDD82000904ACA /* Nimble.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Nimble.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D0C3131719EF2D9700984962 /* RACTestExampleScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTestExampleScheduler.h; sourceTree = ""; }; + D0C3131819EF2D9700984962 /* RACTestExampleScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTestExampleScheduler.m; sourceTree = ""; }; + D0C3131919EF2D9700984962 /* RACTestObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTestObject.h; sourceTree = ""; }; + D0C3131A19EF2D9700984962 /* RACTestObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTestObject.m; sourceTree = ""; }; + D0C3131B19EF2D9700984962 /* RACTestSchedulerSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTestSchedulerSpec.m; sourceTree = ""; }; + D0C3131C19EF2D9700984962 /* RACTestUIButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTestUIButton.h; sourceTree = ""; }; + D0C3131D19EF2D9700984962 /* RACTestUIButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTestUIButton.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 57A4D2071BA13D7A00F7D4B1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7DFBED001CDB8C9500EE435B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + CDF066CA1CDC1CA200199626 /* Nimble.framework in Frameworks */, + CDF066CB1CDC1CA200199626 /* Quick.framework in Frameworks */, + 7DFBED081CDB8C9500EE435B /* ReactiveObjC.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A9B315501B3940610001CB9C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D04725E619E49ED7006002AA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D04725F219E49ED7006002AA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D05E662519EDD82000904ACA /* Nimble.framework in Frameworks */, + D037672D19EDA75D00A782A9 /* Quick.framework in Frameworks */, + D04725F619E49ED7006002AA /* ReactiveObjC.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D047260819E49F82006002AA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D047261319E49F82006002AA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D05E662619EDD83000904ACA /* Nimble.framework in Frameworks */, + D037672F19EDA78B00A782A9 /* Quick.framework in Frameworks */, + D047261719E49F82006002AA /* ReactiveObjC.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 57A4D2431BA13F9700F7D4B1 /* tvOS */ = { + isa = PBXGroup; + children = ( + 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */, + 57A4D2451BA13F9700F7D4B1 /* tvOS-Base.xcconfig */, + 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */, + 57A4D2471BA13F9700F7D4B1 /* tvOS-StaticLibrary.xcconfig */, + ); + path = tvOS; + sourceTree = ""; + }; + A97451321B3A935E00F48E55 /* watchOS */ = { + isa = PBXGroup; + children = ( + A97451331B3A935E00F48E55 /* watchOS-Application.xcconfig */, + A97451341B3A935E00F48E55 /* watchOS-Base.xcconfig */, + A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */, + A97451361B3A935E00F48E55 /* watchOS-StaticLibrary.xcconfig */, + ); + path = watchOS; + sourceTree = ""; + }; + BE330A0D1D634F1E00806963 /* Frameworks */ = { + isa = PBXGroup; + children = ( + BE330A1A1D634F5F00806963 /* ReactiveSwift.framework */, + BE330A181D634F5900806963 /* ReactiveSwift.framework */, + BE330A161D634F4E00806963 /* ReactiveSwift.framework */, + BE330A141D634F4000806963 /* ReactiveSwift.framework */, + BE330A121D634F2E00806963 /* ReactiveSwift.framework */, + BE330A101D634F2900806963 /* ReactiveSwift.framework */, + BE330A0E1D634F1E00806963 /* ReactiveSwift.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + D037666519EDA57100A782A9 /* extobjc */ = { + isa = PBXGroup; + children = ( + D037666619EDA57100A782A9 /* EXTKeyPathCoding.h */, + D037666719EDA57100A782A9 /* EXTRuntimeExtensions.h */, + D037666819EDA57100A782A9 /* EXTRuntimeExtensions.m */, + D037666919EDA57100A782A9 /* EXTScope.h */, + D037666A19EDA57100A782A9 /* metamacros.h */, + ); + path = extobjc; + sourceTree = ""; + }; + D04725E019E49ED7006002AA = { + isa = PBXGroup; + children = ( + D04725EC19E49ED7006002AA /* ReactiveObjC */, + D04725F919E49ED7006002AA /* ReactiveObjCTests */, + D047262519E49FE8006002AA /* Configuration */, + D04725EB19E49ED7006002AA /* Products */, + BE330A0D1D634F1E00806963 /* Frameworks */, + ); + sourceTree = ""; + usesTabs = 1; + }; + D04725EB19E49ED7006002AA /* Products */ = { + isa = PBXGroup; + children = ( + D04725EA19E49ED7006002AA /* ReactiveObjC.framework */, + D04725F519E49ED7006002AA /* ReactiveObjCTests.xctest */, + D047260C19E49F82006002AA /* ReactiveObjC.framework */, + D047261619E49F82006002AA /* ReactiveObjCTests.xctest */, + A9B315541B3940610001CB9C /* ReactiveObjC.framework */, + 57A4D2411BA13D7A00F7D4B1 /* ReactiveObjC.framework */, + 7DFBED031CDB8C9500EE435B /* ReactiveObjCTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + D04725EC19E49ED7006002AA /* ReactiveObjC */ = { + isa = PBXGroup; + children = ( + D04725EF19E49ED7006002AA /* ReactiveObjC.h */, + D037666519EDA57100A782A9 /* extobjc */, + 314304151ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.h */, + 314304161ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.m */, + D037642A19EDA41200A782A9 /* NSArray+RACSequenceAdditions.h */, + D037642B19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m */, + D037642C19EDA41200A782A9 /* NSControl+RACCommandSupport.h */, + D037642D19EDA41200A782A9 /* NSControl+RACCommandSupport.m */, + D037642E19EDA41200A782A9 /* NSControl+RACTextSignalSupport.h */, + D037642F19EDA41200A782A9 /* NSControl+RACTextSignalSupport.m */, + D037643019EDA41200A782A9 /* NSData+RACSupport.h */, + D037643119EDA41200A782A9 /* NSData+RACSupport.m */, + D037643219EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h */, + D037643319EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m */, + D037643419EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h */, + D037643519EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m */, + D037643619EDA41200A782A9 /* NSFileHandle+RACSupport.h */, + D037643719EDA41200A782A9 /* NSFileHandle+RACSupport.m */, + D037643819EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h */, + D037643919EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m */, + D037643A19EDA41200A782A9 /* NSInvocation+RACTypeParsing.h */, + D037643B19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m */, + D037643C19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h */, + D037643D19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m */, + D037643E19EDA41200A782A9 /* NSObject+RACAppKitBindings.h */, + D037643F19EDA41200A782A9 /* NSObject+RACAppKitBindings.m */, + D037644019EDA41200A782A9 /* NSObject+RACDeallocating.h */, + D037644119EDA41200A782A9 /* NSObject+RACDeallocating.m */, + D037644219EDA41200A782A9 /* NSObject+RACDescription.h */, + D037644319EDA41200A782A9 /* NSObject+RACDescription.m */, + D037644419EDA41200A782A9 /* NSObject+RACKVOWrapper.h */, + D037644519EDA41200A782A9 /* NSObject+RACKVOWrapper.m */, + D037644619EDA41200A782A9 /* NSObject+RACLifting.h */, + D037644719EDA41200A782A9 /* NSObject+RACLifting.m */, + D037644819EDA41200A782A9 /* NSObject+RACPropertySubscribing.h */, + D037644919EDA41200A782A9 /* NSObject+RACPropertySubscribing.m */, + D037644A19EDA41200A782A9 /* NSObject+RACSelectorSignal.h */, + D037644B19EDA41200A782A9 /* NSObject+RACSelectorSignal.m */, + D037644C19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h */, + D037644D19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m */, + D037644E19EDA41200A782A9 /* NSSet+RACSequenceAdditions.h */, + D037644F19EDA41200A782A9 /* NSSet+RACSequenceAdditions.m */, + D037645019EDA41200A782A9 /* NSString+RACKeyPathUtilities.h */, + D037645119EDA41200A782A9 /* NSString+RACKeyPathUtilities.m */, + D037645219EDA41200A782A9 /* NSString+RACSequenceAdditions.h */, + D037645319EDA41200A782A9 /* NSString+RACSequenceAdditions.m */, + D037645419EDA41200A782A9 /* NSString+RACSupport.h */, + D037645519EDA41200A782A9 /* NSString+RACSupport.m */, + D037645619EDA41200A782A9 /* NSText+RACSignalSupport.h */, + D037645719EDA41200A782A9 /* NSText+RACSignalSupport.m */, + D037645819EDA41200A782A9 /* NSURLConnection+RACSupport.h */, + D037645919EDA41200A782A9 /* NSURLConnection+RACSupport.m */, + D037645A19EDA41200A782A9 /* NSUserDefaults+RACSupport.h */, + D037645B19EDA41200A782A9 /* NSUserDefaults+RACSupport.m */, + D037645C19EDA41200A782A9 /* RACArraySequence.h */, + D037645D19EDA41200A782A9 /* RACArraySequence.m */, + D037646019EDA41200A782A9 /* RACBehaviorSubject.h */, + D037646119EDA41200A782A9 /* RACBehaviorSubject.m */, + D037646219EDA41200A782A9 /* RACBlockTrampoline.h */, + D037646319EDA41200A782A9 /* RACBlockTrampoline.m */, + D037646419EDA41200A782A9 /* RACChannel.h */, + D037646519EDA41200A782A9 /* RACChannel.m */, + D037646619EDA41200A782A9 /* RACCommand.h */, + D037646719EDA41200A782A9 /* RACCommand.m */, + D037646819EDA41200A782A9 /* RACCompoundDisposable.h */, + D037646919EDA41200A782A9 /* RACCompoundDisposable.m */, + D037646A19EDA41200A782A9 /* RACCompoundDisposableProvider.d */, + D037646B19EDA41200A782A9 /* RACDelegateProxy.h */, + D037646C19EDA41200A782A9 /* RACDelegateProxy.m */, + D037646D19EDA41200A782A9 /* RACDisposable.h */, + D037646E19EDA41200A782A9 /* RACDisposable.m */, + D037646F19EDA41200A782A9 /* RACDynamicSequence.h */, + D037647019EDA41200A782A9 /* RACDynamicSequence.m */, + D037647119EDA41200A782A9 /* RACDynamicSignal.h */, + D037647219EDA41200A782A9 /* RACDynamicSignal.m */, + D037647319EDA41200A782A9 /* RACEagerSequence.h */, + D037647419EDA41200A782A9 /* RACEagerSequence.m */, + D037647519EDA41200A782A9 /* RACEmptySequence.h */, + D037647619EDA41200A782A9 /* RACEmptySequence.m */, + D037647719EDA41200A782A9 /* RACEmptySignal.h */, + D037647819EDA41200A782A9 /* RACEmptySignal.m */, + D037647919EDA41200A782A9 /* RACErrorSignal.h */, + D037647A19EDA41200A782A9 /* RACErrorSignal.m */, + D037647B19EDA41200A782A9 /* RACEvent.h */, + D037647C19EDA41200A782A9 /* RACEvent.m */, + D037647D19EDA41200A782A9 /* RACGroupedSignal.h */, + D037647E19EDA41200A782A9 /* RACGroupedSignal.m */, + D037647F19EDA41200A782A9 /* RACImmediateScheduler.h */, + D037648019EDA41200A782A9 /* RACImmediateScheduler.m */, + D037648119EDA41200A782A9 /* RACIndexSetSequence.h */, + D037648219EDA41200A782A9 /* RACIndexSetSequence.m */, + D037648319EDA41200A782A9 /* RACKVOChannel.h */, + D037648419EDA41200A782A9 /* RACKVOChannel.m */, + 7A70657D1A3F88B8001E8354 /* RACKVOProxy.h */, + 7A70657E1A3F88B8001E8354 /* RACKVOProxy.m */, + D037648519EDA41200A782A9 /* RACKVOTrampoline.h */, + D037648619EDA41200A782A9 /* RACKVOTrampoline.m */, + D037648719EDA41200A782A9 /* RACMulticastConnection.h */, + D037648819EDA41200A782A9 /* RACMulticastConnection.m */, + D037648919EDA41200A782A9 /* RACMulticastConnection+Private.h */, + D037648C19EDA41200A782A9 /* RACPassthroughSubscriber.h */, + D037648D19EDA41200A782A9 /* RACPassthroughSubscriber.m */, + D037648E19EDA41200A782A9 /* RACQueueScheduler.h */, + D037648F19EDA41200A782A9 /* RACQueueScheduler.m */, + D037649019EDA41200A782A9 /* RACQueueScheduler+Subclass.h */, + D037649119EDA41200A782A9 /* RACReplaySubject.h */, + D037649219EDA41200A782A9 /* RACReplaySubject.m */, + D037649319EDA41200A782A9 /* RACReturnSignal.h */, + D037649419EDA41200A782A9 /* RACReturnSignal.m */, + D037649519EDA41200A782A9 /* RACScheduler.h */, + D037649619EDA41200A782A9 /* RACScheduler.m */, + D037649719EDA41200A782A9 /* RACScheduler+Private.h */, + D037649819EDA41200A782A9 /* RACScheduler+Subclass.h */, + D037649919EDA41200A782A9 /* RACScopedDisposable.h */, + D037649A19EDA41200A782A9 /* RACScopedDisposable.m */, + D037649B19EDA41200A782A9 /* RACSequence.h */, + D037649C19EDA41200A782A9 /* RACSequence.m */, + D037649D19EDA41200A782A9 /* RACSerialDisposable.h */, + D037649E19EDA41200A782A9 /* RACSerialDisposable.m */, + D037649F19EDA41200A782A9 /* RACSignal.h */, + D03764A019EDA41200A782A9 /* RACSignal.m */, + D03764A119EDA41200A782A9 /* RACSignal+Operations.h */, + D03764A219EDA41200A782A9 /* RACSignal+Operations.m */, + D03764A319EDA41200A782A9 /* RACSignalProvider.d */, + D03764A419EDA41200A782A9 /* RACSignalSequence.h */, + D03764A519EDA41200A782A9 /* RACSignalSequence.m */, + D03764A619EDA41200A782A9 /* RACStream.h */, + D03764A719EDA41200A782A9 /* RACStream.m */, + D03764A819EDA41200A782A9 /* RACStream+Private.h */, + D03764A919EDA41200A782A9 /* RACStringSequence.h */, + D03764AA19EDA41200A782A9 /* RACStringSequence.m */, + D03764AB19EDA41200A782A9 /* RACSubject.h */, + D03764AC19EDA41200A782A9 /* RACSubject.m */, + D03764AD19EDA41200A782A9 /* RACSubscriber.h */, + D03764AE19EDA41200A782A9 /* RACSubscriber.m */, + D03764AF19EDA41200A782A9 /* RACSubscriber+Private.h */, + D03764B019EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h */, + D03764B119EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m */, + D03764B219EDA41200A782A9 /* RACSubscriptionScheduler.h */, + D03764B319EDA41200A782A9 /* RACSubscriptionScheduler.m */, + D03764B419EDA41200A782A9 /* RACTargetQueueScheduler.h */, + D03764B519EDA41200A782A9 /* RACTargetQueueScheduler.m */, + D03764B619EDA41200A782A9 /* RACTestScheduler.h */, + D03764B719EDA41200A782A9 /* RACTestScheduler.m */, + D03764B819EDA41200A782A9 /* RACTuple.h */, + D03764B919EDA41200A782A9 /* RACTuple.m */, + D03764BA19EDA41200A782A9 /* RACTupleSequence.h */, + D03764BB19EDA41200A782A9 /* RACTupleSequence.m */, + D03764BC19EDA41200A782A9 /* RACUnarySequence.h */, + D03764BD19EDA41200A782A9 /* RACUnarySequence.m */, + D03764BE19EDA41200A782A9 /* RACUnit.h */, + D03764BF19EDA41200A782A9 /* RACUnit.m */, + D03764C019EDA41200A782A9 /* RACValueTransformer.h */, + D03764C119EDA41200A782A9 /* RACValueTransformer.m */, + D03764C219EDA41200A782A9 /* UIActionSheet+RACSignalSupport.h */, + D03764C319EDA41200A782A9 /* UIActionSheet+RACSignalSupport.m */, + D03764C419EDA41200A782A9 /* UIAlertView+RACSignalSupport.h */, + D03764C519EDA41200A782A9 /* UIAlertView+RACSignalSupport.m */, + D03764C619EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.h */, + D03764C719EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.m */, + D03764C819EDA41200A782A9 /* UIButton+RACCommandSupport.h */, + D03764C919EDA41200A782A9 /* UIButton+RACCommandSupport.m */, + D03764CA19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.h */, + D03764CB19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.m */, + D03764CC19EDA41200A782A9 /* UIControl+RACSignalSupport.h */, + D03764CD19EDA41200A782A9 /* UIControl+RACSignalSupport.m */, + D03764CE19EDA41200A782A9 /* UIControl+RACSignalSupportPrivate.h */, + D03764CF19EDA41200A782A9 /* UIControl+RACSignalSupportPrivate.m */, + D03764D019EDA41200A782A9 /* UIDatePicker+RACSignalSupport.h */, + D03764D119EDA41200A782A9 /* UIDatePicker+RACSignalSupport.m */, + D03764D219EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.h */, + D03764D319EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.m */, + D03764D419EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.h */, + D03764D519EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.m */, + D03764D619EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.h */, + D03764D719EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.m */, + D03764D819EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.h */, + D03764D919EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.m */, + D03764DA19EDA41200A782A9 /* UISlider+RACSignalSupport.h */, + D03764DB19EDA41200A782A9 /* UISlider+RACSignalSupport.m */, + D03764DC19EDA41200A782A9 /* UIStepper+RACSignalSupport.h */, + D03764DD19EDA41200A782A9 /* UIStepper+RACSignalSupport.m */, + D03764DE19EDA41200A782A9 /* UISwitch+RACSignalSupport.h */, + D03764DF19EDA41200A782A9 /* UISwitch+RACSignalSupport.m */, + D03764E019EDA41200A782A9 /* UITableViewCell+RACSignalSupport.h */, + D03764E119EDA41200A782A9 /* UITableViewCell+RACSignalSupport.m */, + D03764E219EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.h */, + D03764E319EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.m */, + D03764E419EDA41200A782A9 /* UITextField+RACSignalSupport.h */, + D03764E519EDA41200A782A9 /* UITextField+RACSignalSupport.m */, + D03764E619EDA41200A782A9 /* UITextView+RACSignalSupport.h */, + D03764E719EDA41200A782A9 /* UITextView+RACSignalSupport.m */, + D04725ED19E49ED7006002AA /* Supporting Files */, + ); + path = ReactiveObjC; + sourceTree = ""; + }; + D04725ED19E49ED7006002AA /* Supporting Files */ = { + isa = PBXGroup; + children = ( + CDC42E2E1AE7AB8B00965373 /* Result.framework */, + D04725EE19E49ED7006002AA /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + D04725F919E49ED7006002AA /* ReactiveObjCTests */ = { + isa = PBXGroup; + children = ( + D037667619EDA60000A782A9 /* NSControllerRACSupportSpec.m */, + D037667819EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m */, + D037667919EDA60000A782A9 /* NSNotificationCenterRACSupportSpec.m */, + D037667A19EDA60000A782A9 /* NSObjectRACAppKitBindingsSpec.m */, + D037667B19EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m */, + D037667C19EDA60000A782A9 /* NSObjectRACLiftingSpec.m */, + D037667D19EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.h */, + D037667E19EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.m */, + D037667F19EDA60000A782A9 /* NSObjectRACPropertySubscribingSpec.m */, + D037668019EDA60000A782A9 /* NSObjectRACSelectorSignalSpec.m */, + D037668119EDA60000A782A9 /* NSStringRACKeyPathUtilitiesSpec.m */, + D037668319EDA60000A782A9 /* NSURLConnectionRACSupportSpec.m */, + D037668419EDA60000A782A9 /* NSUserDefaultsRACSupportSpec.m */, + D037668619EDA60000A782A9 /* RACBlockTrampolineSpec.m */, + D037668719EDA60000A782A9 /* RACChannelExamples.h */, + D037668819EDA60000A782A9 /* RACChannelExamples.m */, + D037668919EDA60000A782A9 /* RACChannelSpec.m */, + D037668A19EDA60000A782A9 /* RACCommandSpec.m */, + D037668B19EDA60000A782A9 /* RACCompoundDisposableSpec.m */, + D037668C19EDA60000A782A9 /* RACControlCommandExamples.h */, + D037668D19EDA60000A782A9 /* RACControlCommandExamples.m */, + D037668E19EDA60000A782A9 /* RACDelegateProxySpec.m */, + D037668F19EDA60000A782A9 /* RACDisposableSpec.m */, + D037669019EDA60000A782A9 /* RACEventSpec.m */, + D037669119EDA60000A782A9 /* RACKVOChannelSpec.m */, + 7A7065831A3F8967001E8354 /* RACKVOProxySpec.m */, + D037669219EDA60000A782A9 /* RACKVOWrapperSpec.m */, + D037669319EDA60000A782A9 /* RACMulticastConnectionSpec.m */, + D037669419EDA60000A782A9 /* RACPropertySignalExamples.h */, + D037669519EDA60000A782A9 /* RACPropertySignalExamples.m */, + D037669619EDA60000A782A9 /* RACSchedulerSpec.m */, + D037669719EDA60000A782A9 /* RACSequenceAdditionsSpec.m */, + D037669819EDA60000A782A9 /* RACSequenceExamples.h */, + D037669919EDA60000A782A9 /* RACSequenceExamples.m */, + D037669A19EDA60000A782A9 /* RACSequenceSpec.m */, + D037669B19EDA60000A782A9 /* RACSerialDisposableSpec.m */, + D037669C19EDA60000A782A9 /* RACSignalSpec.m */, + D037669F19EDA60000A782A9 /* RACStreamExamples.h */, + D03766A019EDA60000A782A9 /* RACStreamExamples.m */, + D03766A119EDA60000A782A9 /* RACSubclassObject.h */, + D03766A219EDA60000A782A9 /* RACSubclassObject.m */, + D03766A319EDA60000A782A9 /* RACSubjectSpec.m */, + D03766A419EDA60000A782A9 /* RACSubscriberExamples.h */, + D03766A519EDA60000A782A9 /* RACSubscriberExamples.m */, + D03766A619EDA60000A782A9 /* RACSubscriberSpec.m */, + D03766A719EDA60000A782A9 /* RACSubscriptingAssignmentTrampolineSpec.m */, + D03766A819EDA60000A782A9 /* RACTargetQueueSchedulerSpec.m */, + D03766B019EDA60000A782A9 /* RACTupleSpec.m */, + D03766B219EDA60000A782A9 /* UIActionSheetRACSupportSpec.m */, + D03766B319EDA60000A782A9 /* UIAlertViewRACSupportSpec.m */, + D03766B419EDA60000A782A9 /* UIBarButtonItemRACSupportSpec.m */, + D03766B519EDA60000A782A9 /* UIButtonRACSupportSpec.m */, + D03766B719EDA60000A782A9 /* UIImagePickerControllerRACSupportSpec.m */, + D0C3131719EF2D9700984962 /* RACTestExampleScheduler.h */, + D0C3131819EF2D9700984962 /* RACTestExampleScheduler.m */, + D0C3131919EF2D9700984962 /* RACTestObject.h */, + D0C3131A19EF2D9700984962 /* RACTestObject.m */, + D0C3131B19EF2D9700984962 /* RACTestSchedulerSpec.m */, + D0C3131C19EF2D9700984962 /* RACTestUIButton.h */, + D0C3131D19EF2D9700984962 /* RACTestUIButton.m */, + D04725FA19E49ED7006002AA /* Supporting Files */, + ); + path = ReactiveObjCTests; + sourceTree = ""; + }; + D04725FA19E49ED7006002AA /* Supporting Files */ = { + isa = PBXGroup; + children = ( + D05E662419EDD82000904ACA /* Nimble.framework */, + D037672B19EDA75D00A782A9 /* Quick.framework */, + D03766B119EDA60000A782A9 /* test-data.json */, + D04725FB19E49ED7006002AA /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + D047262519E49FE8006002AA /* Configuration */ = { + isa = PBXGroup; + children = ( + D047262619E49FE8006002AA /* Base */, + D047263119E49FE8006002AA /* iOS */, + D047263619E49FE8006002AA /* Mac OS X */, + A97451321B3A935E00F48E55 /* watchOS */, + 57A4D2431BA13F9700F7D4B1 /* tvOS */, + D047263C19E49FE8006002AA /* README.md */, + ); + name = Configuration; + path = Carthage/Checkouts/xcconfigs; + sourceTree = ""; + }; + D047262619E49FE8006002AA /* Base */ = { + isa = PBXGroup; + children = ( + D047262719E49FE8006002AA /* Common.xcconfig */, + D047262819E49FE8006002AA /* Configurations */, + D047262D19E49FE8006002AA /* Targets */, + ); + path = Base; + sourceTree = ""; + }; + D047262819E49FE8006002AA /* Configurations */ = { + isa = PBXGroup; + children = ( + D047262919E49FE8006002AA /* Debug.xcconfig */, + D047262A19E49FE8006002AA /* Profile.xcconfig */, + D047262B19E49FE8006002AA /* Release.xcconfig */, + D047262C19E49FE8006002AA /* Test.xcconfig */, + ); + path = Configurations; + sourceTree = ""; + }; + D047262D19E49FE8006002AA /* Targets */ = { + isa = PBXGroup; + children = ( + D047262E19E49FE8006002AA /* Application.xcconfig */, + D047262F19E49FE8006002AA /* Framework.xcconfig */, + D047263019E49FE8006002AA /* StaticLibrary.xcconfig */, + ); + path = Targets; + sourceTree = ""; + }; + D047263119E49FE8006002AA /* iOS */ = { + isa = PBXGroup; + children = ( + D047263219E49FE8006002AA /* iOS-Application.xcconfig */, + D047263319E49FE8006002AA /* iOS-Base.xcconfig */, + D047263419E49FE8006002AA /* iOS-Framework.xcconfig */, + D047263519E49FE8006002AA /* iOS-StaticLibrary.xcconfig */, + ); + path = iOS; + sourceTree = ""; + }; + D047263619E49FE8006002AA /* Mac OS X */ = { + isa = PBXGroup; + children = ( + D047263719E49FE8006002AA /* Mac-Application.xcconfig */, + D047263819E49FE8006002AA /* Mac-Base.xcconfig */, + D047263919E49FE8006002AA /* Mac-DynamicLibrary.xcconfig */, + D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */, + D047263B19E49FE8006002AA /* Mac-StaticLibrary.xcconfig */, + ); + path = "Mac OS X"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 57A4D2091BA13D7A00F7D4B1 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 57A4D20A1BA13D7A00F7D4B1 /* ReactiveObjC.h in Headers */, + 57A4D20B1BA13D7A00F7D4B1 /* EXTKeyPathCoding.h in Headers */, + A1046B7D1BFF5664004D8045 /* EXTRuntimeExtensions.h in Headers */, + 57A4D20C1BA13D7A00F7D4B1 /* EXTScope.h in Headers */, + 57A4D20D1BA13D7A00F7D4B1 /* metamacros.h in Headers */, + 57A4D20E1BA13D7A00F7D4B1 /* NSArray+RACSequenceAdditions.h in Headers */, + 57A4D20F1BA13D7A00F7D4B1 /* NSData+RACSupport.h in Headers */, + BEBDD6E51CDC292D009A75A9 /* RACDelegateProxy.h in Headers */, + 57A4D2101BA13D7A00F7D4B1 /* NSDictionary+RACSequenceAdditions.h in Headers */, + 57A4D2111BA13D7A00F7D4B1 /* NSEnumerator+RACSequenceAdditions.h in Headers */, + 57A4D2121BA13D7A00F7D4B1 /* NSFileHandle+RACSupport.h in Headers */, + 57A4D2131BA13D7A00F7D4B1 /* NSIndexSet+RACSequenceAdditions.h in Headers */, + 57A4D2141BA13D7A00F7D4B1 /* NSNotificationCenter+RACSupport.h in Headers */, + 57A4D2151BA13D7A00F7D4B1 /* NSObject+RACDeallocating.h in Headers */, + 57A4D2161BA13D7A00F7D4B1 /* NSObject+RACLifting.h in Headers */, + 57A4D2171BA13D7A00F7D4B1 /* NSObject+RACPropertySubscribing.h in Headers */, + 57DC89A01C5066D400E367B7 /* UIGestureRecognizer+RACSignalSupport.h in Headers */, + 57A4D2181BA13D7A00F7D4B1 /* NSObject+RACSelectorSignal.h in Headers */, + 57A4D2191BA13D7A00F7D4B1 /* NSOrderedSet+RACSequenceAdditions.h in Headers */, + 57A4D21A1BA13D7A00F7D4B1 /* NSSet+RACSequenceAdditions.h in Headers */, + 57A4D21B1BA13D7A00F7D4B1 /* NSString+RACSequenceAdditions.h in Headers */, + 57A4D21C1BA13D7A00F7D4B1 /* NSString+RACSupport.h in Headers */, + 57A4D21E1BA13D7A00F7D4B1 /* NSUserDefaults+RACSupport.h in Headers */, + 57A4D21F1BA13D7A00F7D4B1 /* RACBehaviorSubject.h in Headers */, + 57A4D2201BA13D7A00F7D4B1 /* RACChannel.h in Headers */, + 57A4D2211BA13D7A00F7D4B1 /* RACCommand.h in Headers */, + 57A4D2221BA13D7A00F7D4B1 /* RACCompoundDisposable.h in Headers */, + 57A4D2231BA13D7A00F7D4B1 /* RACDisposable.h in Headers */, + 57A4D2241BA13D7A00F7D4B1 /* RACEvent.h in Headers */, + 7DFBED6F1CDB926400EE435B /* UIBarButtonItem+RACCommandSupport.h in Headers */, + 57A4D2251BA13D7A00F7D4B1 /* RACGroupedSignal.h in Headers */, + 57A4D2261BA13D7A00F7D4B1 /* RACKVOChannel.h in Headers */, + 57A4D2271BA13D7A00F7D4B1 /* RACMulticastConnection.h in Headers */, + 57A4D2281BA13D7A00F7D4B1 /* RACQueueScheduler.h in Headers */, + 57DC89A51C50675700E367B7 /* UITextField+RACSignalSupport.h in Headers */, + 57A4D2291BA13D7A00F7D4B1 /* RACQueueScheduler+Subclass.h in Headers */, + 57A4D22A1BA13D7A00F7D4B1 /* RACReplaySubject.h in Headers */, + 57DC89A21C50673C00E367B7 /* UISegmentedControl+RACSignalSupport.h in Headers */, + 57A4D22B1BA13D7A00F7D4B1 /* RACScheduler.h in Headers */, + 57DC89A41C50674D00E367B7 /* UITableViewHeaderFooterView+RACSignalSupport.h in Headers */, + 57A4D22C1BA13D7A00F7D4B1 /* RACScheduler+Subclass.h in Headers */, + 57DC89A11C50672B00E367B7 /* UIControl+RACSignalSupport.h in Headers */, + 57A4D22D1BA13D7A00F7D4B1 /* RACScopedDisposable.h in Headers */, + 57DC89A31C50674300E367B7 /* UITableViewCell+RACSignalSupport.h in Headers */, + 57A4D22E1BA13D7A00F7D4B1 /* RACSequence.h in Headers */, + 57A4D22F1BA13D7A00F7D4B1 /* RACSerialDisposable.h in Headers */, + 57A4D2301BA13D7A00F7D4B1 /* RACSignal.h in Headers */, + 57DC89A81C50679E00E367B7 /* UICollectionReusableView+RACSignalSupport.h in Headers */, + 57DC89A71C50679700E367B7 /* UIButton+RACCommandSupport.h in Headers */, + 57A4D2311BA13D7A00F7D4B1 /* RACSignal+Operations.h in Headers */, + 57A4D2321BA13D7A00F7D4B1 /* RACStream.h in Headers */, + 57A4D2331BA13D7A00F7D4B1 /* RACSubject.h in Headers */, + 57A4D2341BA13D7A00F7D4B1 /* RACSubscriber.h in Headers */, + 57A4D2351BA13D7A00F7D4B1 /* RACSubscriptingAssignmentTrampoline.h in Headers */, + 57A4D2361BA13D7A00F7D4B1 /* RACTargetQueueScheduler.h in Headers */, + 57A4D2371BA13D7A00F7D4B1 /* RACTestScheduler.h in Headers */, + 57A4D2381BA13D7A00F7D4B1 /* RACTuple.h in Headers */, + 57DC89A61C50675F00E367B7 /* UITextView+RACSignalSupport.h in Headers */, + 57A4D2391BA13D7A00F7D4B1 /* RACUnit.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A9B315511B3940610001CB9C /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + A9B315CA1B3940AB0001CB9C /* ReactiveObjC.h in Headers */, + A9B315CB1B3940AB0001CB9C /* EXTKeyPathCoding.h in Headers */, + A1046B7C1BFF5662004D8045 /* EXTRuntimeExtensions.h in Headers */, + A9B315CD1B3940AB0001CB9C /* EXTScope.h in Headers */, + A9B315CE1B3940AB0001CB9C /* metamacros.h in Headers */, + A9B315D01B3940AB0001CB9C /* NSArray+RACSequenceAdditions.h in Headers */, + A9B315D31B3940AB0001CB9C /* NSData+RACSupport.h in Headers */, + A9B315D41B3940AB0001CB9C /* NSDictionary+RACSequenceAdditions.h in Headers */, + A9B315D51B3940AB0001CB9C /* NSEnumerator+RACSequenceAdditions.h in Headers */, + A9B315D61B3940AB0001CB9C /* NSFileHandle+RACSupport.h in Headers */, + A9B315D71B3940AB0001CB9C /* NSIndexSet+RACSequenceAdditions.h in Headers */, + A9B315D91B3940AB0001CB9C /* NSNotificationCenter+RACSupport.h in Headers */, + A9B315DB1B3940AB0001CB9C /* NSObject+RACDeallocating.h in Headers */, + A9B315DE1B3940AB0001CB9C /* NSObject+RACLifting.h in Headers */, + A9B315DF1B3940AB0001CB9C /* NSObject+RACPropertySubscribing.h in Headers */, + A9B315E01B3940AB0001CB9C /* NSObject+RACSelectorSignal.h in Headers */, + A9B315E11B3940AB0001CB9C /* NSOrderedSet+RACSequenceAdditions.h in Headers */, + A9B315E21B3940AB0001CB9C /* NSSet+RACSequenceAdditions.h in Headers */, + A9B315E41B3940AB0001CB9C /* NSString+RACSequenceAdditions.h in Headers */, + A9B315E51B3940AB0001CB9C /* NSString+RACSupport.h in Headers */, + A9B315E81B3940AB0001CB9C /* NSUserDefaults+RACSupport.h in Headers */, + A9B315EA1B3940AB0001CB9C /* RACBehaviorSubject.h in Headers */, + A9B315EC1B3940AB0001CB9C /* RACChannel.h in Headers */, + A9B315ED1B3940AC0001CB9C /* RACCommand.h in Headers */, + A9B315EE1B3940AC0001CB9C /* RACCompoundDisposable.h in Headers */, + A9B315F01B3940AC0001CB9C /* RACDisposable.h in Headers */, + A9B315F71B3940AC0001CB9C /* RACEvent.h in Headers */, + BEBDD6E61CDC292D009A75A9 /* RACDelegateProxy.h in Headers */, + A9B315F81B3940AC0001CB9C /* RACGroupedSignal.h in Headers */, + A9B315FB1B3940AC0001CB9C /* RACKVOChannel.h in Headers */, + A9B315FE1B3940AC0001CB9C /* RACMulticastConnection.h in Headers */, + A9B316021B3940AD0001CB9C /* RACQueueScheduler.h in Headers */, + A9B316031B3940AD0001CB9C /* RACQueueScheduler+Subclass.h in Headers */, + A9B316041B3940AD0001CB9C /* RACReplaySubject.h in Headers */, + A9B316061B3940AD0001CB9C /* RACScheduler.h in Headers */, + A9B316081B3940AD0001CB9C /* RACScheduler+Subclass.h in Headers */, + A9B316091B3940AD0001CB9C /* RACScopedDisposable.h in Headers */, + A9B3160A1B3940AD0001CB9C /* RACSequence.h in Headers */, + A9B3160B1B3940AD0001CB9C /* RACSerialDisposable.h in Headers */, + A9B3160C1B3940AE0001CB9C /* RACSignal.h in Headers */, + A9B3160D1B3940AE0001CB9C /* RACSignal+Operations.h in Headers */, + A9B3160F1B3940AE0001CB9C /* RACStream.h in Headers */, + A9B316121B3940AE0001CB9C /* RACSubject.h in Headers */, + A9B316131B3940AE0001CB9C /* RACSubscriber.h in Headers */, + A9B316151B3940AE0001CB9C /* RACSubscriptingAssignmentTrampoline.h in Headers */, + A9B316171B3940AF0001CB9C /* RACTargetQueueScheduler.h in Headers */, + A9B316181B3940AF0001CB9C /* RACTestScheduler.h in Headers */, + A9B316191B3940AF0001CB9C /* RACTuple.h in Headers */, + A9B3161C1B3940AF0001CB9C /* RACUnit.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D04725E719E49ED7006002AA /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + D04725F019E49ED7006002AA /* ReactiveObjC.h in Headers */, + D037652019EDA41200A782A9 /* NSObject+RACLifting.h in Headers */, + D03764EC19EDA41200A782A9 /* NSControl+RACCommandSupport.h in Headers */, + D037655C19EDA41200A782A9 /* RACChannel.h in Headers */, + D03765EE19EDA41200A782A9 /* RACSubscriber.h in Headers */, + D03765D219EDA41200A782A9 /* RACSignal.h in Headers */, + D037650419EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h in Headers */, + D037659A19EDA41200A782A9 /* RACKVOChannel.h in Headers */, + D037651419EDA41200A782A9 /* NSObject+RACDeallocating.h in Headers */, + D037650C19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h in Headers */, + D037667319EDA57100A782A9 /* metamacros.h in Headers */, + D037666B19EDA57100A782A9 /* EXTKeyPathCoding.h in Headers */, + D03765F419EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h in Headers */, + D03765C419EDA41200A782A9 /* RACScheduler+Subclass.h in Headers */, + D037656E19EDA41200A782A9 /* RACDisposable.h in Headers */, + D03765B019EDA41200A782A9 /* RACQueueScheduler.h in Headers */, + BEBDD6E81CDC292F009A75A9 /* RACDelegateProxy.h in Headers */, + D037652419EDA41200A782A9 /* NSObject+RACPropertySubscribing.h in Headers */, + D037650019EDA41200A782A9 /* NSFileHandle+RACSupport.h in Headers */, + D037653019EDA41200A782A9 /* NSSet+RACSequenceAdditions.h in Headers */, + D037654019EDA41200A782A9 /* NSText+RACSignalSupport.h in Headers */, + D03765E019EDA41200A782A9 /* RACStream.h in Headers */, + D03765FC19EDA41200A782A9 /* RACTargetQueueScheduler.h in Headers */, + D03765B419EDA41200A782A9 /* RACQueueScheduler+Subclass.h in Headers */, + D037661019EDA41200A782A9 /* RACUnit.h in Headers */, + D037656419EDA41200A782A9 /* RACCompoundDisposable.h in Headers */, + D03764F419EDA41200A782A9 /* NSData+RACSupport.h in Headers */, + D03764FC19EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h in Headers */, + D03765CA19EDA41200A782A9 /* RACSequence.h in Headers */, + D037672719EDA63400A782A9 /* RACBehaviorSubject.h in Headers */, + D037653C19EDA41200A782A9 /* NSString+RACSupport.h in Headers */, + D03765CE19EDA41200A782A9 /* RACSerialDisposable.h in Headers */, + D03765D619EDA41200A782A9 /* RACSignal+Operations.h in Headers */, + D03765B619EDA41200A782A9 /* RACReplaySubject.h in Headers */, + D03765A219EDA41200A782A9 /* RACMulticastConnection.h in Headers */, + D037658E19EDA41200A782A9 /* RACGroupedSignal.h in Headers */, + D037654819EDA41200A782A9 /* NSUserDefaults+RACSupport.h in Headers */, + D03765BE19EDA41200A782A9 /* RACScheduler.h in Headers */, + D037656019EDA41200A782A9 /* RACCommand.h in Headers */, + D037660419EDA41200A782A9 /* RACTuple.h in Headers */, + D03765C619EDA41200A782A9 /* RACScopedDisposable.h in Headers */, + D037660019EDA41200A782A9 /* RACTestScheduler.h in Headers */, + D037652C19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h in Headers */, + D03764F019EDA41200A782A9 /* NSControl+RACTextSignalSupport.h in Headers */, + D03765EA19EDA41200A782A9 /* RACSubject.h in Headers */, + A1046B7A1BFF5661004D8045 /* EXTRuntimeExtensions.h in Headers */, + D037652819EDA41200A782A9 /* NSObject+RACSelectorSignal.h in Headers */, + D037654419EDA41200A782A9 /* NSURLConnection+RACSupport.h in Headers */, + D03764E819EDA41200A782A9 /* NSArray+RACSequenceAdditions.h in Headers */, + D037651019EDA41200A782A9 /* NSObject+RACAppKitBindings.h in Headers */, + D037658A19EDA41200A782A9 /* RACEvent.h in Headers */, + D037667119EDA57100A782A9 /* EXTScope.h in Headers */, + D037653819EDA41200A782A9 /* NSString+RACSequenceAdditions.h in Headers */, + D03764F819EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D047260919E49F82006002AA /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + D037664519EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.h in Headers */, + D037666419EDA43C00A782A9 /* ReactiveObjC.h in Headers */, + D037652519EDA41200A782A9 /* NSObject+RACPropertySubscribing.h in Headers */, + D03765B119EDA41200A782A9 /* RACQueueScheduler.h in Headers */, + D037662519EDA41200A782A9 /* UIButton+RACCommandSupport.h in Headers */, + D037672819EDA63500A782A9 /* RACBehaviorSubject.h in Headers */, + D037660119EDA41200A782A9 /* RACTestScheduler.h in Headers */, + D03765A319EDA41200A782A9 /* RACMulticastConnection.h in Headers */, + D03765B719EDA41200A782A9 /* RACReplaySubject.h in Headers */, + D037663D19EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.h in Headers */, + D037656F19EDA41200A782A9 /* RACDisposable.h in Headers */, + 314304171ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.h in Headers */, + D037654519EDA41200A782A9 /* NSURLConnection+RACSupport.h in Headers */, + D037661D19EDA41200A782A9 /* UIAlertView+RACSignalSupport.h in Headers */, + D037650D19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h in Headers */, + D037650119EDA41200A782A9 /* NSFileHandle+RACSupport.h in Headers */, + D037666119EDA41200A782A9 /* UITextView+RACSignalSupport.h in Headers */, + D037659B19EDA41200A782A9 /* RACKVOChannel.h in Headers */, + D037652D19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h in Headers */, + D03764F919EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h in Headers */, + D037667219EDA57100A782A9 /* EXTScope.h in Headers */, + D037663519EDA41200A782A9 /* UIDatePicker+RACSignalSupport.h in Headers */, + D037667419EDA57100A782A9 /* metamacros.h in Headers */, + D03764FD19EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h in Headers */, + D037652119EDA41200A782A9 /* NSObject+RACLifting.h in Headers */, + D037665919EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.h in Headers */, + D037656519EDA41200A782A9 /* RACCompoundDisposable.h in Headers */, + D037653D19EDA41200A782A9 /* NSString+RACSupport.h in Headers */, + D037662919EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.h in Headers */, + D037660519EDA41200A782A9 /* RACTuple.h in Headers */, + D037665519EDA41200A782A9 /* UITableViewCell+RACSignalSupport.h in Headers */, + D03764F519EDA41200A782A9 /* NSData+RACSupport.h in Headers */, + D037653919EDA41200A782A9 /* NSString+RACSequenceAdditions.h in Headers */, + D037651519EDA41200A782A9 /* NSObject+RACDeallocating.h in Headers */, + D037658F19EDA41200A782A9 /* RACGroupedSignal.h in Headers */, + D03765C519EDA41200A782A9 /* RACScheduler+Subclass.h in Headers */, + D03765E119EDA41200A782A9 /* RACStream.h in Headers */, + D03765D719EDA41200A782A9 /* RACSignal+Operations.h in Headers */, + D037665D19EDA41200A782A9 /* UITextField+RACSignalSupport.h in Headers */, + D037664919EDA41200A782A9 /* UISlider+RACSignalSupport.h in Headers */, + D03765BF19EDA41200A782A9 /* RACScheduler.h in Headers */, + D03764E919EDA41200A782A9 /* NSArray+RACSequenceAdditions.h in Headers */, + D037654919EDA41200A782A9 /* NSUserDefaults+RACSupport.h in Headers */, + D037663919EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.h in Headers */, + D037653119EDA41200A782A9 /* NSSet+RACSequenceAdditions.h in Headers */, + D03765CB19EDA41200A782A9 /* RACSequence.h in Headers */, + D037662D19EDA41200A782A9 /* UIControl+RACSignalSupport.h in Headers */, + D037666C19EDA57100A782A9 /* EXTKeyPathCoding.h in Headers */, + D037658B19EDA41200A782A9 /* RACEvent.h in Headers */, + D03765CF19EDA41200A782A9 /* RACSerialDisposable.h in Headers */, + D037650519EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h in Headers */, + D037655D19EDA41200A782A9 /* RACChannel.h in Headers */, + D03765B519EDA41200A782A9 /* RACQueueScheduler+Subclass.h in Headers */, + D037665119EDA41200A782A9 /* UISwitch+RACSignalSupport.h in Headers */, + D037664119EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.h in Headers */, + D037652919EDA41200A782A9 /* NSObject+RACSelectorSignal.h in Headers */, + D03765D319EDA41200A782A9 /* RACSignal.h in Headers */, + D03765F519EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h in Headers */, + D03765C719EDA41200A782A9 /* RACScopedDisposable.h in Headers */, + A1046B7B1BFF5662004D8045 /* EXTRuntimeExtensions.h in Headers */, + D037661119EDA41200A782A9 /* RACUnit.h in Headers */, + D03765FD19EDA41200A782A9 /* RACTargetQueueScheduler.h in Headers */, + D037661919EDA41200A782A9 /* UIActionSheet+RACSignalSupport.h in Headers */, + D037664D19EDA41200A782A9 /* UIStepper+RACSignalSupport.h in Headers */, + D037662119EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.h in Headers */, + D03765EB19EDA41200A782A9 /* RACSubject.h in Headers */, + BEBDD6E71CDC292E009A75A9 /* RACDelegateProxy.h in Headers */, + D037656119EDA41200A782A9 /* RACCommand.h in Headers */, + D03765EF19EDA41200A782A9 /* RACSubscriber.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 57A4D1AF1BA13D7A00F7D4B1 /* ReactiveObjC-tvOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 57A4D23C1BA13D7A00F7D4B1 /* Build configuration list for PBXNativeTarget "ReactiveObjC-tvOS" */; + buildPhases = ( + 57A4D1B01BA13D7A00F7D4B1 /* Sources */, + 57A4D2071BA13D7A00F7D4B1 /* Frameworks */, + 57A4D2091BA13D7A00F7D4B1 /* Headers */, + 57A4D23B1BA13D7A00F7D4B1 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "ReactiveObjC-tvOS"; + productName = ReactiveCocoa; + productReference = 57A4D2411BA13D7A00F7D4B1 /* ReactiveObjC.framework */; + productType = "com.apple.product-type.framework"; + }; + 7DFBED021CDB8C9500EE435B /* ReactiveObjC-tvOSTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7DFBED0F1CDB8C9500EE435B /* Build configuration list for PBXNativeTarget "ReactiveObjC-tvOSTests" */; + buildPhases = ( + 7DFBECFF1CDB8C9500EE435B /* Sources */, + 7DFBED001CDB8C9500EE435B /* Frameworks */, + 7DFBED011CDB8C9500EE435B /* Resources */, + 7DFBED151CDB8CEC00EE435B /* Copy Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 7DFBED0A1CDB8C9500EE435B /* PBXTargetDependency */, + ); + name = "ReactiveObjC-tvOSTests"; + productName = "ReactiveCocoa-tvOSTests"; + productReference = 7DFBED031CDB8C9500EE435B /* ReactiveObjCTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + A9B315531B3940610001CB9C /* ReactiveObjC-watchOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = A9B3155D1B3940610001CB9C /* Build configuration list for PBXNativeTarget "ReactiveObjC-watchOS" */; + buildPhases = ( + A9B3154F1B3940610001CB9C /* Sources */, + A9B315501B3940610001CB9C /* Frameworks */, + A9B315511B3940610001CB9C /* Headers */, + A9B315521B3940610001CB9C /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "ReactiveObjC-watchOS"; + productName = ReactiveCocoa; + productReference = A9B315541B3940610001CB9C /* ReactiveObjC.framework */; + productType = "com.apple.product-type.framework"; + }; + D04725E919E49ED7006002AA /* ReactiveObjC-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = D047260019E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjC-macOS" */; + buildPhases = ( + D04725E519E49ED7006002AA /* Sources */, + D04725E619E49ED7006002AA /* Frameworks */, + D04725E719E49ED7006002AA /* Headers */, + D04725E819E49ED7006002AA /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "ReactiveObjC-macOS"; + productName = ReactiveCocoa; + productReference = D04725EA19E49ED7006002AA /* ReactiveObjC.framework */; + productType = "com.apple.product-type.framework"; + }; + D04725F419E49ED7006002AA /* ReactiveObjC-macOSTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = D047260319E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjC-macOSTests" */; + buildPhases = ( + D04725F119E49ED7006002AA /* Sources */, + D04725F219E49ED7006002AA /* Frameworks */, + D04725F319E49ED7006002AA /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + D04725F819E49ED7006002AA /* PBXTargetDependency */, + ); + name = "ReactiveObjC-macOSTests"; + productName = ReactiveCocoaTests; + productReference = D04725F519E49ED7006002AA /* ReactiveObjCTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + D047260B19E49F82006002AA /* ReactiveObjC-iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = D047261F19E49F82006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjC-iOS" */; + buildPhases = ( + D047260719E49F82006002AA /* Sources */, + D047260819E49F82006002AA /* Frameworks */, + D047260919E49F82006002AA /* Headers */, + D047260A19E49F82006002AA /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "ReactiveObjC-iOS"; + productName = ReactiveCocoa; + productReference = D047260C19E49F82006002AA /* ReactiveObjC.framework */; + productType = "com.apple.product-type.framework"; + }; + D047261519E49F82006002AA /* ReactiveObjC-iOSTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = D047262219E49F82006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjC-iOSTests" */; + buildPhases = ( + D047261219E49F82006002AA /* Sources */, + D047261319E49F82006002AA /* Frameworks */, + D047261419E49F82006002AA /* Resources */, + D01B7B6119EDD8F600D26E01 /* Copy Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + D047261919E49F82006002AA /* PBXTargetDependency */, + ); + name = "ReactiveObjC-iOSTests"; + productName = ReactiveCocoaTests; + productReference = D047261619E49F82006002AA /* ReactiveObjCTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + D04725E119E49ED7006002AA /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0730; + LastUpgradeCheck = 0800; + ORGANIZATIONNAME = GitHub; + TargetAttributes = { + 57A4D1AF1BA13D7A00F7D4B1 = { + LastSwiftMigration = 0800; + }; + 7DFBED021CDB8C9500EE435B = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 0800; + }; + A9B315531B3940610001CB9C = { + CreatedOnToolsVersion = 7.0; + LastSwiftMigration = 0800; + }; + D04725E919E49ED7006002AA = { + CreatedOnToolsVersion = 6.1; + LastSwiftMigration = 0800; + }; + D04725F419E49ED7006002AA = { + CreatedOnToolsVersion = 6.1; + LastSwiftMigration = 0800; + }; + D047260B19E49F82006002AA = { + CreatedOnToolsVersion = 6.1; + LastSwiftMigration = 0800; + }; + D047261519E49F82006002AA = { + CreatedOnToolsVersion = 6.1; + LastSwiftMigration = 0800; + }; + }; + }; + buildConfigurationList = D04725E419E49ED7006002AA /* Build configuration list for PBXProject "ReactiveObjC" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = D04725E019E49ED7006002AA; + productRefGroup = D04725EB19E49ED7006002AA /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + D04725E919E49ED7006002AA /* ReactiveObjC-macOS */, + D04725F419E49ED7006002AA /* ReactiveObjC-macOSTests */, + D047260B19E49F82006002AA /* ReactiveObjC-iOS */, + D047261519E49F82006002AA /* ReactiveObjC-iOSTests */, + A9B315531B3940610001CB9C /* ReactiveObjC-watchOS */, + 57A4D1AF1BA13D7A00F7D4B1 /* ReactiveObjC-tvOS */, + 7DFBED021CDB8C9500EE435B /* ReactiveObjC-tvOSTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 57A4D23B1BA13D7A00F7D4B1 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7DFBED011CDB8C9500EE435B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7DFBED141CDB8CE600EE435B /* test-data.json in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A9B315521B3940610001CB9C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D04725E819E49ED7006002AA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D04725F319E49ED7006002AA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D037671719EDA60000A782A9 /* test-data.json in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D047260A19E49F82006002AA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D047261419E49F82006002AA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D037671819EDA60000A782A9 /* test-data.json in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 57A4D1B01BA13D7A00F7D4B1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 57D476951C4206EC00EFE697 /* UITableViewCell+RACSignalSupport.m in Sources */, + 57A4D1B21BA13D7A00F7D4B1 /* RACCompoundDisposableProvider.d in Sources */, + 57D476901C4206D400EFE697 /* UIControl+RACSignalSupportPrivate.m in Sources */, + 57A4D1B31BA13D7A00F7D4B1 /* RACSignalProvider.d in Sources */, + 57A4D1C11BA13D7A00F7D4B1 /* EXTRuntimeExtensions.m in Sources */, + 57A4D1C21BA13D7A00F7D4B1 /* NSArray+RACSequenceAdditions.m in Sources */, + 57A4D1C31BA13D7A00F7D4B1 /* NSData+RACSupport.m in Sources */, + 57A4D1C41BA13D7A00F7D4B1 /* NSDictionary+RACSequenceAdditions.m in Sources */, + 57A4D1C51BA13D7A00F7D4B1 /* NSEnumerator+RACSequenceAdditions.m in Sources */, + 57D476961C4206EC00EFE697 /* UITableViewHeaderFooterView+RACSignalSupport.m in Sources */, + 57A4D1C61BA13D7A00F7D4B1 /* NSFileHandle+RACSupport.m in Sources */, + 57A4D1C71BA13D7A00F7D4B1 /* NSIndexSet+RACSequenceAdditions.m in Sources */, + 57A4D1C81BA13D7A00F7D4B1 /* NSInvocation+RACTypeParsing.m in Sources */, + 57D4769B1C4206F200EFE697 /* UICollectionReusableView+RACSignalSupport.m in Sources */, + 57A4D1C91BA13D7A00F7D4B1 /* NSNotificationCenter+RACSupport.m in Sources */, + 7DFBED6E1CDB918900EE435B /* UIBarButtonItem+RACCommandSupport.m in Sources */, + 57A4D1CA1BA13D7A00F7D4B1 /* NSObject+RACDeallocating.m in Sources */, + 57A4D1CB1BA13D7A00F7D4B1 /* NSObject+RACDescription.m in Sources */, + 57A4D1CC1BA13D7A00F7D4B1 /* NSObject+RACKVOWrapper.m in Sources */, + 57A4D1CD1BA13D7A00F7D4B1 /* NSObject+RACLifting.m in Sources */, + 57A4D1CE1BA13D7A00F7D4B1 /* NSObject+RACPropertySubscribing.m in Sources */, + 57A4D1CF1BA13D7A00F7D4B1 /* NSObject+RACSelectorSignal.m in Sources */, + 57D476981C4206EC00EFE697 /* UITextView+RACSignalSupport.m in Sources */, + 57A4D1D01BA13D7A00F7D4B1 /* NSOrderedSet+RACSequenceAdditions.m in Sources */, + 57A4D1D11BA13D7A00F7D4B1 /* NSSet+RACSequenceAdditions.m in Sources */, + 57D476911C4206DA00EFE697 /* UIGestureRecognizer+RACSignalSupport.m in Sources */, + 57A4D1D21BA13D7A00F7D4B1 /* NSString+RACKeyPathUtilities.m in Sources */, + 57D4769A1C4206F200EFE697 /* UIButton+RACCommandSupport.m in Sources */, + 57A4D1D31BA13D7A00F7D4B1 /* NSString+RACSequenceAdditions.m in Sources */, + 57A4D1D41BA13D7A00F7D4B1 /* NSString+RACSupport.m in Sources */, + 57A4D1D61BA13D7A00F7D4B1 /* NSUserDefaults+RACSupport.m in Sources */, + 57A4D1D71BA13D7A00F7D4B1 /* RACArraySequence.m in Sources */, + 57A4D1D81BA13D7A00F7D4B1 /* RACBehaviorSubject.m in Sources */, + 57A4D1D91BA13D7A00F7D4B1 /* RACBlockTrampoline.m in Sources */, + 57A4D1DA1BA13D7A00F7D4B1 /* RACChannel.m in Sources */, + 57A4D1DB1BA13D7A00F7D4B1 /* RACCommand.m in Sources */, + 57A4D1DC1BA13D7A00F7D4B1 /* RACCompoundDisposable.m in Sources */, + 57A4D1DD1BA13D7A00F7D4B1 /* RACDelegateProxy.m in Sources */, + 57A4D1DE1BA13D7A00F7D4B1 /* RACDisposable.m in Sources */, + 57A4D1DF1BA13D7A00F7D4B1 /* RACDynamicSequence.m in Sources */, + 57A4D1E01BA13D7A00F7D4B1 /* RACDynamicSignal.m in Sources */, + 57A4D1E11BA13D7A00F7D4B1 /* RACEagerSequence.m in Sources */, + 57D4768D1C42063C00EFE697 /* UIControl+RACSignalSupport.m in Sources */, + 57A4D1E21BA13D7A00F7D4B1 /* RACEmptySequence.m in Sources */, + 57A4D1E31BA13D7A00F7D4B1 /* RACEmptySignal.m in Sources */, + 57A4D1E41BA13D7A00F7D4B1 /* RACErrorSignal.m in Sources */, + 57A4D1E51BA13D7A00F7D4B1 /* RACEvent.m in Sources */, + 57A4D1E61BA13D7A00F7D4B1 /* RACGroupedSignal.m in Sources */, + 57A4D1E71BA13D7A00F7D4B1 /* RACImmediateScheduler.m in Sources */, + 57D476971C4206EC00EFE697 /* UITextField+RACSignalSupport.m in Sources */, + 57A4D1E81BA13D7A00F7D4B1 /* RACIndexSetSequence.m in Sources */, + 57A4D1E91BA13D7A00F7D4B1 /* RACKVOChannel.m in Sources */, + 57A4D1EA1BA13D7A00F7D4B1 /* RACKVOProxy.m in Sources */, + 57A4D1EB1BA13D7A00F7D4B1 /* RACKVOTrampoline.m in Sources */, + 57A4D1EC1BA13D7A00F7D4B1 /* RACMulticastConnection.m in Sources */, + 57A4D1EE1BA13D7A00F7D4B1 /* RACPassthroughSubscriber.m in Sources */, + 57A4D1EF1BA13D7A00F7D4B1 /* RACQueueScheduler.m in Sources */, + 57A4D1F01BA13D7A00F7D4B1 /* RACReplaySubject.m in Sources */, + 57A4D1F11BA13D7A00F7D4B1 /* RACReturnSignal.m in Sources */, + 57A4D1F21BA13D7A00F7D4B1 /* RACScheduler.m in Sources */, + 57A4D1F31BA13D7A00F7D4B1 /* RACScopedDisposable.m in Sources */, + 57A4D1F41BA13D7A00F7D4B1 /* RACSequence.m in Sources */, + 57A4D1F51BA13D7A00F7D4B1 /* RACSerialDisposable.m in Sources */, + 57A4D1F61BA13D7A00F7D4B1 /* RACSignal.m in Sources */, + 57D476921C4206DF00EFE697 /* UISegmentedControl+RACSignalSupport.m in Sources */, + 57A4D1F71BA13D7A00F7D4B1 /* RACSignal+Operations.m in Sources */, + 57A4D1F81BA13D7A00F7D4B1 /* RACSignalSequence.m in Sources */, + 57A4D1F91BA13D7A00F7D4B1 /* RACStream.m in Sources */, + 57A4D1FA1BA13D7A00F7D4B1 /* RACStringSequence.m in Sources */, + 57A4D1FB1BA13D7A00F7D4B1 /* RACSubject.m in Sources */, + 57A4D1FC1BA13D7A00F7D4B1 /* RACSubscriber.m in Sources */, + 57A4D1FD1BA13D7A00F7D4B1 /* RACSubscriptingAssignmentTrampoline.m in Sources */, + 57A4D1FE1BA13D7A00F7D4B1 /* RACSubscriptionScheduler.m in Sources */, + 57A4D1FF1BA13D7A00F7D4B1 /* RACTargetQueueScheduler.m in Sources */, + 57A4D2001BA13D7A00F7D4B1 /* RACTestScheduler.m in Sources */, + 57A4D2011BA13D7A00F7D4B1 /* RACTuple.m in Sources */, + 57A4D2021BA13D7A00F7D4B1 /* RACTupleSequence.m in Sources */, + 57A4D2031BA13D7A00F7D4B1 /* RACUnarySequence.m in Sources */, + 57A4D2041BA13D7A00F7D4B1 /* RACUnit.m in Sources */, + 57A4D2051BA13D7A00F7D4B1 /* RACValueTransformer.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7DFBECFF1CDB8C9500EE435B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7DFBED321CDB8DE300EE435B /* NSEnumeratorRACSequenceAdditionsSpec.m in Sources */, + 7DFBED331CDB8DE300EE435B /* NSNotificationCenterRACSupportSpec.m in Sources */, + 7DFBED351CDB8DE300EE435B /* NSObjectRACDeallocatingSpec.m in Sources */, + 7DFBED361CDB8DE300EE435B /* NSObjectRACLiftingSpec.m in Sources */, + 7DFBED381CDB8DE300EE435B /* NSObjectRACPropertySubscribingExamples.m in Sources */, + 7DFBED391CDB8DE300EE435B /* NSObjectRACPropertySubscribingSpec.m in Sources */, + 7DFBED3A1CDB8DE300EE435B /* NSObjectRACSelectorSignalSpec.m in Sources */, + 7DFBED3B1CDB8DE300EE435B /* NSStringRACKeyPathUtilitiesSpec.m in Sources */, + 7DFBED3D1CDB8DE300EE435B /* NSUserDefaultsRACSupportSpec.m in Sources */, + 7DFBED3E1CDB8DE300EE435B /* RACBlockTrampolineSpec.m in Sources */, + 7DFBED401CDB8DE300EE435B /* RACChannelExamples.m in Sources */, + 7DFBED411CDB8DE300EE435B /* RACChannelSpec.m in Sources */, + 7DFBED421CDB8DE300EE435B /* RACCommandSpec.m in Sources */, + 7DFBED431CDB8DE300EE435B /* RACCompoundDisposableSpec.m in Sources */, + 7DFBED451CDB8DE300EE435B /* RACControlCommandExamples.m in Sources */, + 7DFBED461CDB8DE300EE435B /* RACDelegateProxySpec.m in Sources */, + 7DFBED471CDB8DE300EE435B /* RACDisposableSpec.m in Sources */, + 7DFBED481CDB8DE300EE435B /* RACEventSpec.m in Sources */, + 7DFBED491CDB8DE300EE435B /* RACKVOChannelSpec.m in Sources */, + 7DFBED4A1CDB8DE300EE435B /* RACKVOProxySpec.m in Sources */, + 7DFBED4B1CDB8DE300EE435B /* RACKVOWrapperSpec.m in Sources */, + 7DFBED4C1CDB8DE300EE435B /* RACMulticastConnectionSpec.m in Sources */, + 7DFBED4E1CDB8DE300EE435B /* RACPropertySignalExamples.m in Sources */, + 7DFBED4F1CDB8DE300EE435B /* RACSchedulerSpec.m in Sources */, + 7DFBED501CDB8DE300EE435B /* RACSequenceAdditionsSpec.m in Sources */, + 7DFBED521CDB8DE300EE435B /* RACSequenceExamples.m in Sources */, + 7DFBED531CDB8DE300EE435B /* RACSequenceSpec.m in Sources */, + 7DFBED541CDB8DE300EE435B /* RACSerialDisposableSpec.m in Sources */, + 7DFBED551CDB8DE300EE435B /* RACSignalSpec.m in Sources */, + 7DFBED571CDB8DE300EE435B /* RACStreamExamples.m in Sources */, + 7DFBED591CDB8DE300EE435B /* RACSubclassObject.m in Sources */, + 7DFBED5A1CDB8DE300EE435B /* RACSubjectSpec.m in Sources */, + 7DFBED5C1CDB8DE300EE435B /* RACSubscriberExamples.m in Sources */, + 7DFBED5D1CDB8DE300EE435B /* RACSubscriberSpec.m in Sources */, + 7DFBED5E1CDB8DE300EE435B /* RACSubscriptingAssignmentTrampolineSpec.m in Sources */, + 7DFBED5F1CDB8DE300EE435B /* RACTargetQueueSchedulerSpec.m in Sources */, + 7DFBED601CDB8DE300EE435B /* RACTupleSpec.m in Sources */, + 7DFBED631CDB8DE300EE435B /* UIBarButtonItemRACSupportSpec.m in Sources */, + 7DFBED641CDB8DE300EE435B /* UIButtonRACSupportSpec.m in Sources */, + 7DFBED671CDB8DE300EE435B /* RACTestExampleScheduler.m in Sources */, + 7DFBED691CDB8DE300EE435B /* RACTestObject.m in Sources */, + 7DFBED6A1CDB8DE300EE435B /* RACTestSchedulerSpec.m in Sources */, + 7DFBED6C1CDB8DE300EE435B /* RACTestUIButton.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A9B3154F1B3940610001CB9C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A9B316341B394C7F0001CB9C /* RACCompoundDisposableProvider.d in Sources */, + A9B316351B394C7F0001CB9C /* RACSignalProvider.d in Sources */, + A9B3155E1B3940750001CB9C /* EXTRuntimeExtensions.m in Sources */, + A9B315601B3940750001CB9C /* NSArray+RACSequenceAdditions.m in Sources */, + A9B315631B3940750001CB9C /* NSData+RACSupport.m in Sources */, + A9B315641B3940750001CB9C /* NSDictionary+RACSequenceAdditions.m in Sources */, + A9B315651B3940750001CB9C /* NSEnumerator+RACSequenceAdditions.m in Sources */, + A9B315661B3940750001CB9C /* NSFileHandle+RACSupport.m in Sources */, + A9B315671B3940750001CB9C /* NSIndexSet+RACSequenceAdditions.m in Sources */, + A9B315681B3940750001CB9C /* NSInvocation+RACTypeParsing.m in Sources */, + A9B315691B3940750001CB9C /* NSNotificationCenter+RACSupport.m in Sources */, + A9B3156B1B3940750001CB9C /* NSObject+RACDeallocating.m in Sources */, + A9B3156C1B3940750001CB9C /* NSObject+RACDescription.m in Sources */, + A9B3156D1B3940750001CB9C /* NSObject+RACKVOWrapper.m in Sources */, + A9B3156E1B3940750001CB9C /* NSObject+RACLifting.m in Sources */, + A9B3156F1B3940750001CB9C /* NSObject+RACPropertySubscribing.m in Sources */, + A9B315701B3940750001CB9C /* NSObject+RACSelectorSignal.m in Sources */, + A9B315711B3940750001CB9C /* NSOrderedSet+RACSequenceAdditions.m in Sources */, + A9B315721B3940750001CB9C /* NSSet+RACSequenceAdditions.m in Sources */, + A9B315731B3940750001CB9C /* NSString+RACKeyPathUtilities.m in Sources */, + A9B315741B3940750001CB9C /* NSString+RACSequenceAdditions.m in Sources */, + A9B315751B3940750001CB9C /* NSString+RACSupport.m in Sources */, + A9B315781B3940750001CB9C /* NSUserDefaults+RACSupport.m in Sources */, + A9B315791B3940750001CB9C /* RACArraySequence.m in Sources */, + A9B3157A1B3940750001CB9C /* RACBehaviorSubject.m in Sources */, + A9B3157B1B3940750001CB9C /* RACBlockTrampoline.m in Sources */, + A9B3157C1B3940750001CB9C /* RACChannel.m in Sources */, + A9B3157D1B3940750001CB9C /* RACCommand.m in Sources */, + A9B3157E1B3940750001CB9C /* RACCompoundDisposable.m in Sources */, + A9B3157F1B3940750001CB9C /* RACDelegateProxy.m in Sources */, + A9B315801B3940750001CB9C /* RACDisposable.m in Sources */, + A9B315811B3940750001CB9C /* RACDynamicSequence.m in Sources */, + A9B315821B3940750001CB9C /* RACDynamicSignal.m in Sources */, + A9B315831B3940750001CB9C /* RACEagerSequence.m in Sources */, + A9B315841B3940750001CB9C /* RACEmptySequence.m in Sources */, + A9B315851B3940750001CB9C /* RACEmptySignal.m in Sources */, + A9B315861B3940750001CB9C /* RACErrorSignal.m in Sources */, + A9B315871B3940750001CB9C /* RACEvent.m in Sources */, + A9B315881B3940750001CB9C /* RACGroupedSignal.m in Sources */, + A9B315891B3940750001CB9C /* RACImmediateScheduler.m in Sources */, + A9B3158A1B3940750001CB9C /* RACIndexSetSequence.m in Sources */, + A9B3158B1B3940750001CB9C /* RACKVOChannel.m in Sources */, + A9B3158C1B3940750001CB9C /* RACKVOProxy.m in Sources */, + A9B3158D1B3940750001CB9C /* RACKVOTrampoline.m in Sources */, + A9B3158E1B3940750001CB9C /* RACMulticastConnection.m in Sources */, + A9B315901B3940750001CB9C /* RACPassthroughSubscriber.m in Sources */, + A9B315911B3940750001CB9C /* RACQueueScheduler.m in Sources */, + A9B315921B3940750001CB9C /* RACReplaySubject.m in Sources */, + A9B315931B3940750001CB9C /* RACReturnSignal.m in Sources */, + A9B315941B3940750001CB9C /* RACScheduler.m in Sources */, + A9B315951B3940750001CB9C /* RACScopedDisposable.m in Sources */, + A9B315961B3940750001CB9C /* RACSequence.m in Sources */, + A9B315971B3940750001CB9C /* RACSerialDisposable.m in Sources */, + A9B315981B3940750001CB9C /* RACSignal.m in Sources */, + A9B315991B3940750001CB9C /* RACSignal+Operations.m in Sources */, + A9B3159A1B3940750001CB9C /* RACSignalSequence.m in Sources */, + A9B3159B1B3940750001CB9C /* RACStream.m in Sources */, + A9B3159C1B3940750001CB9C /* RACStringSequence.m in Sources */, + A9B3159D1B3940750001CB9C /* RACSubject.m in Sources */, + A9B3159E1B3940750001CB9C /* RACSubscriber.m in Sources */, + A9B3159F1B3940750001CB9C /* RACSubscriptingAssignmentTrampoline.m in Sources */, + A9B315A01B3940750001CB9C /* RACSubscriptionScheduler.m in Sources */, + A9B315A11B3940750001CB9C /* RACTargetQueueScheduler.m in Sources */, + A9B315A21B3940750001CB9C /* RACTestScheduler.m in Sources */, + A9B315A31B3940750001CB9C /* RACTuple.m in Sources */, + A9B315A41B3940750001CB9C /* RACTupleSequence.m in Sources */, + A9B315A51B3940750001CB9C /* RACUnarySequence.m in Sources */, + A9B315A61B3940750001CB9C /* RACUnit.m in Sources */, + A9B315A71B3940750001CB9C /* RACValueTransformer.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D04725E519E49ED7006002AA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D037654219EDA41200A782A9 /* NSText+RACSignalSupport.m in Sources */, + D037659C19EDA41200A782A9 /* RACKVOChannel.m in Sources */, + D03765C819EDA41200A782A9 /* RACScopedDisposable.m in Sources */, + D03764FE19EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m in Sources */, + D03764EA19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m in Sources */, + D03765C019EDA41200A782A9 /* RACScheduler.m in Sources */, + D037659819EDA41200A782A9 /* RACIndexSetSequence.m in Sources */, + D03765D819EDA41200A782A9 /* RACSignal+Operations.m in Sources */, + D03764F219EDA41200A782A9 /* NSControl+RACTextSignalSupport.m in Sources */, + D037650219EDA41200A782A9 /* NSFileHandle+RACSupport.m in Sources */, + D03765E219EDA41200A782A9 /* RACStream.m in Sources */, + D037655619EDA41200A782A9 /* RACBehaviorSubject.m in Sources */, + D037660219EDA41200A782A9 /* RACTestScheduler.m in Sources */, + D03765B819EDA41200A782A9 /* RACReplaySubject.m in Sources */, + D03765EC19EDA41200A782A9 /* RACSubject.m in Sources */, + D03765D019EDA41200A782A9 /* RACSerialDisposable.m in Sources */, + D037666F19EDA57100A782A9 /* EXTRuntimeExtensions.m in Sources */, + D037653E19EDA41200A782A9 /* NSString+RACSupport.m in Sources */, + D037653619EDA41200A782A9 /* NSString+RACKeyPathUtilities.m in Sources */, + D03764FA19EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m in Sources */, + D037656819EDA41200A782A9 /* RACCompoundDisposableProvider.d in Sources */, + D037653A19EDA41200A782A9 /* NSString+RACSequenceAdditions.m in Sources */, + D03765E819EDA41200A782A9 /* RACStringSequence.m in Sources */, + D03764EE19EDA41200A782A9 /* NSControl+RACCommandSupport.m in Sources */, + D037660A19EDA41200A782A9 /* RACTupleSequence.m in Sources */, + D03765D419EDA41200A782A9 /* RACSignal.m in Sources */, + D037651A19EDA41200A782A9 /* NSObject+RACDescription.m in Sources */, + D03765A419EDA41200A782A9 /* RACMulticastConnection.m in Sources */, + D037654E19EDA41200A782A9 /* RACArraySequence.m in Sources */, + D037652219EDA41200A782A9 /* NSObject+RACLifting.m in Sources */, + D037650619EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m in Sources */, + D037650E19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m in Sources */, + D03765FA19EDA41200A782A9 /* RACSubscriptionScheduler.m in Sources */, + D037658019EDA41200A782A9 /* RACEmptySequence.m in Sources */, + D037654A19EDA41200A782A9 /* NSUserDefaults+RACSupport.m in Sources */, + D037660E19EDA41200A782A9 /* RACUnarySequence.m in Sources */, + D03765FE19EDA41200A782A9 /* RACTargetQueueScheduler.m in Sources */, + D03765DE19EDA41200A782A9 /* RACSignalSequence.m in Sources */, + D037656C19EDA41200A782A9 /* RACDelegateProxy.m in Sources */, + D037657419EDA41200A782A9 /* RACDynamicSequence.m in Sources */, + D037657019EDA41200A782A9 /* RACDisposable.m in Sources */, + D03765DA19EDA41200A782A9 /* RACSignalProvider.d in Sources */, + D037653219EDA41200A782A9 /* NSSet+RACSequenceAdditions.m in Sources */, + D037651219EDA41200A782A9 /* NSObject+RACAppKitBindings.m in Sources */, + D037656619EDA41200A782A9 /* RACCompoundDisposable.m in Sources */, + D037655A19EDA41200A782A9 /* RACBlockTrampoline.m in Sources */, + D037659019EDA41200A782A9 /* RACGroupedSignal.m in Sources */, + D037655E19EDA41200A782A9 /* RACChannel.m in Sources */, + D037657C19EDA41200A782A9 /* RACEagerSequence.m in Sources */, + D037657819EDA41200A782A9 /* RACDynamicSignal.m in Sources */, + D037659419EDA41200A782A9 /* RACImmediateScheduler.m in Sources */, + 7A7065811A3F88B8001E8354 /* RACKVOProxy.m in Sources */, + D037651619EDA41200A782A9 /* NSObject+RACDeallocating.m in Sources */, + D037658419EDA41200A782A9 /* RACEmptySignal.m in Sources */, + D037654619EDA41200A782A9 /* NSURLConnection+RACSupport.m in Sources */, + D03765F019EDA41200A782A9 /* RACSubscriber.m in Sources */, + D03764F619EDA41200A782A9 /* NSData+RACSupport.m in Sources */, + D037656219EDA41200A782A9 /* RACCommand.m in Sources */, + D037658819EDA41200A782A9 /* RACErrorSignal.m in Sources */, + D03765F619EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m in Sources */, + D037661219EDA41200A782A9 /* RACUnit.m in Sources */, + D03765A019EDA41200A782A9 /* RACKVOTrampoline.m in Sources */, + D037650A19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m in Sources */, + D037660619EDA41200A782A9 /* RACTuple.m in Sources */, + D037651E19EDA41200A782A9 /* NSObject+RACKVOWrapper.m in Sources */, + D037661619EDA41200A782A9 /* RACValueTransformer.m in Sources */, + D03765CC19EDA41200A782A9 /* RACSequence.m in Sources */, + D037652E19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m in Sources */, + D037652619EDA41200A782A9 /* NSObject+RACPropertySubscribing.m in Sources */, + D037658C19EDA41200A782A9 /* RACEvent.m in Sources */, + D03765B219EDA41200A782A9 /* RACQueueScheduler.m in Sources */, + D037652A19EDA41200A782A9 /* NSObject+RACSelectorSignal.m in Sources */, + D03765AE19EDA41200A782A9 /* RACPassthroughSubscriber.m in Sources */, + D03765BC19EDA41200A782A9 /* RACReturnSignal.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D04725F119E49ED7006002AA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D03766C719EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.m in Sources */, + D03766E319EDA60000A782A9 /* RACDelegateProxySpec.m in Sources */, + D03766F919EDA60000A782A9 /* RACSerialDisposableSpec.m in Sources */, + D0C3131E19EF2D9700984962 /* RACTestExampleScheduler.m in Sources */, + D037670B19EDA60000A782A9 /* RACTargetQueueSchedulerSpec.m in Sources */, + D03766DD19EDA60000A782A9 /* RACCommandSpec.m in Sources */, + D037670919EDA60000A782A9 /* RACSubscriptingAssignmentTrampolineSpec.m in Sources */, + D03766EB19EDA60000A782A9 /* RACKVOWrapperSpec.m in Sources */, + D03766E719EDA60000A782A9 /* RACEventSpec.m in Sources */, + D03766F719EDA60000A782A9 /* RACSequenceSpec.m in Sources */, + D03766C919EDA60000A782A9 /* NSObjectRACPropertySubscribingSpec.m in Sources */, + D03766C319EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m in Sources */, + D03766BD19EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m in Sources */, + D037670119EDA60000A782A9 /* RACSubclassObject.m in Sources */, + D03766CD19EDA60000A782A9 /* NSStringRACKeyPathUtilitiesSpec.m in Sources */, + D037671519EDA60000A782A9 /* RACTupleSpec.m in Sources */, + D03766C519EDA60000A782A9 /* NSObjectRACLiftingSpec.m in Sources */, + D03766D119EDA60000A782A9 /* NSURLConnectionRACSupportSpec.m in Sources */, + D03766F319EDA60000A782A9 /* RACSequenceAdditionsSpec.m in Sources */, + D03766ED19EDA60000A782A9 /* RACMulticastConnectionSpec.m in Sources */, + D03766E919EDA60000A782A9 /* RACKVOChannelSpec.m in Sources */, + D03766FB19EDA60000A782A9 /* RACSignalSpec.m in Sources */, + 7A7065841A3F8967001E8354 /* RACKVOProxySpec.m in Sources */, + D037670719EDA60000A782A9 /* RACSubscriberSpec.m in Sources */, + D03766EF19EDA60000A782A9 /* RACPropertySignalExamples.m in Sources */, + D037670519EDA60000A782A9 /* RACSubscriberExamples.m in Sources */, + D0C3132219EF2D9700984962 /* RACTestSchedulerSpec.m in Sources */, + D03766D719EDA60000A782A9 /* RACBlockTrampolineSpec.m in Sources */, + D03766FF19EDA60000A782A9 /* RACStreamExamples.m in Sources */, + D03766CB19EDA60000A782A9 /* NSObjectRACSelectorSignalSpec.m in Sources */, + D03766E119EDA60000A782A9 /* RACControlCommandExamples.m in Sources */, + D03766BF19EDA60000A782A9 /* NSNotificationCenterRACSupportSpec.m in Sources */, + D037670319EDA60000A782A9 /* RACSubjectSpec.m in Sources */, + D03766F119EDA60000A782A9 /* RACSchedulerSpec.m in Sources */, + D03766DF19EDA60000A782A9 /* RACCompoundDisposableSpec.m in Sources */, + D03766E519EDA60000A782A9 /* RACDisposableSpec.m in Sources */, + D0C3132019EF2D9700984962 /* RACTestObject.m in Sources */, + D03766D319EDA60000A782A9 /* NSUserDefaultsRACSupportSpec.m in Sources */, + D03766C119EDA60000A782A9 /* NSObjectRACAppKitBindingsSpec.m in Sources */, + D03766DB19EDA60000A782A9 /* RACChannelSpec.m in Sources */, + D03766D919EDA60000A782A9 /* RACChannelExamples.m in Sources */, + D03766F519EDA60000A782A9 /* RACSequenceExamples.m in Sources */, + D03766B919EDA60000A782A9 /* NSControllerRACSupportSpec.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D047260719E49F82006002AA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D037659D19EDA41200A782A9 /* RACKVOChannel.m in Sources */, + D037666319EDA41200A782A9 /* UITextView+RACSignalSupport.m in Sources */, + D037662F19EDA41200A782A9 /* UIControl+RACSignalSupport.m in Sources */, + D03765C919EDA41200A782A9 /* RACScopedDisposable.m in Sources */, + D03764FF19EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m in Sources */, + D037664719EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.m in Sources */, + D03764EB19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m in Sources */, + D03765C119EDA41200A782A9 /* RACScheduler.m in Sources */, + D037662B19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.m in Sources */, + D037659919EDA41200A782A9 /* RACIndexSetSequence.m in Sources */, + D03765D919EDA41200A782A9 /* RACSignal+Operations.m in Sources */, + D037661B19EDA41200A782A9 /* UIActionSheet+RACSignalSupport.m in Sources */, + D037650319EDA41200A782A9 /* NSFileHandle+RACSupport.m in Sources */, + D03765E319EDA41200A782A9 /* RACStream.m in Sources */, + D037655719EDA41200A782A9 /* RACBehaviorSubject.m in Sources */, + D037663B19EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.m in Sources */, + D037660319EDA41200A782A9 /* RACTestScheduler.m in Sources */, + D03765B919EDA41200A782A9 /* RACReplaySubject.m in Sources */, + D03765ED19EDA41200A782A9 /* RACSubject.m in Sources */, + D037664F19EDA41200A782A9 /* UIStepper+RACSignalSupport.m in Sources */, + D03765D119EDA41200A782A9 /* RACSerialDisposable.m in Sources */, + D037663F19EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.m in Sources */, + D037653F19EDA41200A782A9 /* NSString+RACSupport.m in Sources */, + D037653719EDA41200A782A9 /* NSString+RACKeyPathUtilities.m in Sources */, + D03764FB19EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m in Sources */, + D037656919EDA41200A782A9 /* RACCompoundDisposableProvider.d in Sources */, + D037653B19EDA41200A782A9 /* NSString+RACSequenceAdditions.m in Sources */, + D037661F19EDA41200A782A9 /* UIAlertView+RACSignalSupport.m in Sources */, + D03765E919EDA41200A782A9 /* RACStringSequence.m in Sources */, + D037660B19EDA41200A782A9 /* RACTupleSequence.m in Sources */, + D03765D519EDA41200A782A9 /* RACSignal.m in Sources */, + D037663319EDA41200A782A9 /* UIControl+RACSignalSupportPrivate.m in Sources */, + D037664319EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.m in Sources */, + D037651B19EDA41200A782A9 /* NSObject+RACDescription.m in Sources */, + D03765A519EDA41200A782A9 /* RACMulticastConnection.m in Sources */, + D037654F19EDA41200A782A9 /* RACArraySequence.m in Sources */, + D037652319EDA41200A782A9 /* NSObject+RACLifting.m in Sources */, + D037650719EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m in Sources */, + D037665F19EDA41200A782A9 /* UITextField+RACSignalSupport.m in Sources */, + D037650F19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m in Sources */, + D03765FB19EDA41200A782A9 /* RACSubscriptionScheduler.m in Sources */, + D037658119EDA41200A782A9 /* RACEmptySequence.m in Sources */, + D037654B19EDA41200A782A9 /* NSUserDefaults+RACSupport.m in Sources */, + D037660F19EDA41200A782A9 /* RACUnarySequence.m in Sources */, + D03765FF19EDA41200A782A9 /* RACTargetQueueScheduler.m in Sources */, + D03765DF19EDA41200A782A9 /* RACSignalSequence.m in Sources */, + D037656D19EDA41200A782A9 /* RACDelegateProxy.m in Sources */, + D037657519EDA41200A782A9 /* RACDynamicSequence.m in Sources */, + D037657119EDA41200A782A9 /* RACDisposable.m in Sources */, + D03765DB19EDA41200A782A9 /* RACSignalProvider.d in Sources */, + D037653319EDA41200A782A9 /* NSSet+RACSequenceAdditions.m in Sources */, + D037665319EDA41200A782A9 /* UISwitch+RACSignalSupport.m in Sources */, + D037664B19EDA41200A782A9 /* UISlider+RACSignalSupport.m in Sources */, + D037656719EDA41200A782A9 /* RACCompoundDisposable.m in Sources */, + D037655B19EDA41200A782A9 /* RACBlockTrampoline.m in Sources */, + D037659119EDA41200A782A9 /* RACGroupedSignal.m in Sources */, + D037655F19EDA41200A782A9 /* RACChannel.m in Sources */, + D037657D19EDA41200A782A9 /* RACEagerSequence.m in Sources */, + D037657919EDA41200A782A9 /* RACDynamicSignal.m in Sources */, + D037659519EDA41200A782A9 /* RACImmediateScheduler.m in Sources */, + D037651719EDA41200A782A9 /* NSObject+RACDeallocating.m in Sources */, + D037658519EDA41200A782A9 /* RACEmptySignal.m in Sources */, + D037663719EDA41200A782A9 /* UIDatePicker+RACSignalSupport.m in Sources */, + D037654719EDA41200A782A9 /* NSURLConnection+RACSupport.m in Sources */, + D03765F119EDA41200A782A9 /* RACSubscriber.m in Sources */, + D03764F719EDA41200A782A9 /* NSData+RACSupport.m in Sources */, + D037656319EDA41200A782A9 /* RACCommand.m in Sources */, + D037658919EDA41200A782A9 /* RACErrorSignal.m in Sources */, + D03765F719EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m in Sources */, + D037661319EDA41200A782A9 /* RACUnit.m in Sources */, + D037662319EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.m in Sources */, + D03765A119EDA41200A782A9 /* RACKVOTrampoline.m in Sources */, + D037665B19EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.m in Sources */, + D037650B19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m in Sources */, + D037660719EDA41200A782A9 /* RACTuple.m in Sources */, + D037667019EDA57100A782A9 /* EXTRuntimeExtensions.m in Sources */, + D037651F19EDA41200A782A9 /* NSObject+RACKVOWrapper.m in Sources */, + D037661719EDA41200A782A9 /* RACValueTransformer.m in Sources */, + D03765CD19EDA41200A782A9 /* RACSequence.m in Sources */, + 314304181ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.m in Sources */, + D037652F19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m in Sources */, + D037662719EDA41200A782A9 /* UIButton+RACCommandSupport.m in Sources */, + D037652719EDA41200A782A9 /* NSObject+RACPropertySubscribing.m in Sources */, + 7A7065821A3F88B8001E8354 /* RACKVOProxy.m in Sources */, + D037658D19EDA41200A782A9 /* RACEvent.m in Sources */, + D03765B319EDA41200A782A9 /* RACQueueScheduler.m in Sources */, + D037665719EDA41200A782A9 /* UITableViewCell+RACSignalSupport.m in Sources */, + D037652B19EDA41200A782A9 /* NSObject+RACSelectorSignal.m in Sources */, + D03765AF19EDA41200A782A9 /* RACPassthroughSubscriber.m in Sources */, + D03765BD19EDA41200A782A9 /* RACReturnSignal.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D047261219E49F82006002AA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D03766C819EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.m in Sources */, + D037672419EDA60000A782A9 /* UIImagePickerControllerRACSupportSpec.m in Sources */, + D03766E419EDA60000A782A9 /* RACDelegateProxySpec.m in Sources */, + D03766FA19EDA60000A782A9 /* RACSerialDisposableSpec.m in Sources */, + D037670C19EDA60000A782A9 /* RACTargetQueueSchedulerSpec.m in Sources */, + D03766DE19EDA60000A782A9 /* RACCommandSpec.m in Sources */, + D037670A19EDA60000A782A9 /* RACSubscriptingAssignmentTrampolineSpec.m in Sources */, + D03766EC19EDA60000A782A9 /* RACKVOWrapperSpec.m in Sources */, + D03766E819EDA60000A782A9 /* RACEventSpec.m in Sources */, + D03766F819EDA60000A782A9 /* RACSequenceSpec.m in Sources */, + D037671E19EDA60000A782A9 /* UIBarButtonItemRACSupportSpec.m in Sources */, + D03766CA19EDA60000A782A9 /* NSObjectRACPropertySubscribingSpec.m in Sources */, + D0C3132319EF2D9700984962 /* RACTestSchedulerSpec.m in Sources */, + D03766C419EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m in Sources */, + D03766BE19EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m in Sources */, + D037672019EDA60000A782A9 /* UIButtonRACSupportSpec.m in Sources */, + D0C3132519EF2D9700984962 /* RACTestUIButton.m in Sources */, + D037670219EDA60000A782A9 /* RACSubclassObject.m in Sources */, + D03766CE19EDA60000A782A9 /* NSStringRACKeyPathUtilitiesSpec.m in Sources */, + D037671619EDA60000A782A9 /* RACTupleSpec.m in Sources */, + 7A7065851A3F8967001E8354 /* RACKVOProxySpec.m in Sources */, + D03766C619EDA60000A782A9 /* NSObjectRACLiftingSpec.m in Sources */, + D0C3131F19EF2D9700984962 /* RACTestExampleScheduler.m in Sources */, + D03766D219EDA60000A782A9 /* NSURLConnectionRACSupportSpec.m in Sources */, + D03766F419EDA60000A782A9 /* RACSequenceAdditionsSpec.m in Sources */, + D03766EE19EDA60000A782A9 /* RACMulticastConnectionSpec.m in Sources */, + D03766EA19EDA60000A782A9 /* RACKVOChannelSpec.m in Sources */, + D0C3132119EF2D9700984962 /* RACTestObject.m in Sources */, + D03766FC19EDA60000A782A9 /* RACSignalSpec.m in Sources */, + D037670819EDA60000A782A9 /* RACSubscriberSpec.m in Sources */, + D037671C19EDA60000A782A9 /* UIAlertViewRACSupportSpec.m in Sources */, + D03766F019EDA60000A782A9 /* RACPropertySignalExamples.m in Sources */, + D037670619EDA60000A782A9 /* RACSubscriberExamples.m in Sources */, + D03766D819EDA60000A782A9 /* RACBlockTrampolineSpec.m in Sources */, + D037670019EDA60000A782A9 /* RACStreamExamples.m in Sources */, + D03766CC19EDA60000A782A9 /* NSObjectRACSelectorSignalSpec.m in Sources */, + D03766E219EDA60000A782A9 /* RACControlCommandExamples.m in Sources */, + D03766C019EDA60000A782A9 /* NSNotificationCenterRACSupportSpec.m in Sources */, + D037670419EDA60000A782A9 /* RACSubjectSpec.m in Sources */, + D03766F219EDA60000A782A9 /* RACSchedulerSpec.m in Sources */, + D03766E019EDA60000A782A9 /* RACCompoundDisposableSpec.m in Sources */, + D03766E619EDA60000A782A9 /* RACDisposableSpec.m in Sources */, + D03766D419EDA60000A782A9 /* NSUserDefaultsRACSupportSpec.m in Sources */, + D03766DC19EDA60000A782A9 /* RACChannelSpec.m in Sources */, + D037671A19EDA60000A782A9 /* UIActionSheetRACSupportSpec.m in Sources */, + D03766DA19EDA60000A782A9 /* RACChannelExamples.m in Sources */, + D03766F619EDA60000A782A9 /* RACSequenceExamples.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 7DFBED0A1CDB8C9500EE435B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 57A4D1AF1BA13D7A00F7D4B1 /* ReactiveObjC-tvOS */; + targetProxy = 7DFBED091CDB8C9500EE435B /* PBXContainerItemProxy */; + }; + D04725F819E49ED7006002AA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D04725E919E49ED7006002AA /* ReactiveObjC-macOS */; + targetProxy = D04725F719E49ED7006002AA /* PBXContainerItemProxy */; + }; + D047261919E49F82006002AA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D047260B19E49F82006002AA /* ReactiveObjC-iOS */; + targetProxy = D047261819E49F82006002AA /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 57A4D23D1BA13D7A00F7D4B1 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DTRACE_PROBES_DISABLED=1", + ); + INFOPLIST_FILE = ReactiveObjC/Info.plist; + }; + name = Debug; + }; + 57A4D23E1BA13D7A00F7D4B1 /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DTRACE_PROBES_DISABLED=1", + ); + INFOPLIST_FILE = ReactiveObjC/Info.plist; + }; + name = Test; + }; + 57A4D23F1BA13D7A00F7D4B1 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DTRACE_PROBES_DISABLED=1", + ); + INFOPLIST_FILE = ReactiveObjC/Info.plist; + }; + name = Release; + }; + 57A4D2401BA13D7A00F7D4B1 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DTRACE_PROBES_DISABLED=1", + ); + INFOPLIST_FILE = ReactiveObjC/Info.plist; + }; + name = Profile; + }; + 7DFBED0B1CDB8C9500EE435B /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_IDENTITY = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + INFOPLIST_FILE = ReactiveObjCTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Debug; + }; + 7DFBED0C1CDB8C9500EE435B /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_IDENTITY = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + INFOPLIST_FILE = ReactiveObjCTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Test; + }; + 7DFBED0D1CDB8C9500EE435B /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_IDENTITY = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + INFOPLIST_FILE = ReactiveObjCTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Release; + }; + 7DFBED0E1CDB8C9500EE435B /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_IDENTITY = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + INFOPLIST_FILE = ReactiveObjCTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Profile; + }; + A9B315591B3940610001CB9C /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DTRACE_PROBES_DISABLED=1", + ); + INFOPLIST_FILE = ReactiveObjC/Info.plist; + }; + name = Debug; + }; + A9B3155A1B3940610001CB9C /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DTRACE_PROBES_DISABLED=1", + ); + INFOPLIST_FILE = ReactiveObjC/Info.plist; + }; + name = Test; + }; + A9B3155B1B3940610001CB9C /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DTRACE_PROBES_DISABLED=1", + ); + INFOPLIST_FILE = ReactiveObjC/Info.plist; + }; + name = Release; + }; + A9B3155C1B3940610001CB9C /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DTRACE_PROBES_DISABLED=1", + ); + INFOPLIST_FILE = ReactiveObjC/Info.plist; + }; + name = Profile; + }; + D04725FE19E49ED7006002AA /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047262919E49FE8006002AA /* Debug.xcconfig */; + buildSettings = { + BITCODE_GENERATION_MODE = bitcode; + CODE_SIGNING_REQUIRED = NO; + CURRENT_PROJECT_VERSION = 1; + ENABLE_TESTABILITY = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.9; + PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(PROJECT_NAME)"; + TVOS_DEPLOYMENT_TARGET = 9.0; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + WATCHOS_DEPLOYMENT_TARGET = 2.0; + }; + name = Debug; + }; + D04725FF19E49ED7006002AA /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047262B19E49FE8006002AA /* Release.xcconfig */; + buildSettings = { + BITCODE_GENERATION_MODE = bitcode; + CODE_SIGNING_REQUIRED = NO; + CURRENT_PROJECT_VERSION = 1; + GCC_OPTIMIZATION_LEVEL = 0; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.9; + PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(PROJECT_NAME)"; + TVOS_DEPLOYMENT_TARGET = 9.0; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + WATCHOS_DEPLOYMENT_TARGET = 2.0; + }; + name = Release; + }; + D047260119E49ED7006002AA /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = ReactiveObjC/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + USER_HEADER_SEARCH_PATHS = ReactiveObjC/extobjc; + }; + name = Debug; + }; + D047260219E49ED7006002AA /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = ReactiveObjC/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + USER_HEADER_SEARCH_PATHS = ReactiveObjC/extobjc; + }; + name = Release; + }; + D047260419E49ED7006002AA /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + INFOPLIST_FILE = ReactiveObjCTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Debug; + }; + D047260519E49ED7006002AA /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + INFOPLIST_FILE = ReactiveObjCTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Release; + }; + D047262019E49F82006002AA /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + INFOPLIST_FILE = ReactiveObjC/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + USER_HEADER_SEARCH_PATHS = ReactiveObjC/extobjc; + }; + name = Debug; + }; + D047262119E49F82006002AA /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + INFOPLIST_FILE = ReactiveObjC/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + USER_HEADER_SEARCH_PATHS = ReactiveObjC/extobjc; + }; + name = Release; + }; + D047262319E49F82006002AA /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + INFOPLIST_FILE = ReactiveObjCTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Debug; + }; + D047262419E49F82006002AA /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + INFOPLIST_FILE = ReactiveObjCTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Release; + }; + D047263D19E4A008006002AA /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047262A19E49FE8006002AA /* Profile.xcconfig */; + buildSettings = { + BITCODE_GENERATION_MODE = bitcode; + CODE_SIGNING_REQUIRED = NO; + CURRENT_PROJECT_VERSION = 1; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.9; + PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(PROJECT_NAME)"; + TVOS_DEPLOYMENT_TARGET = 9.0; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + WATCHOS_DEPLOYMENT_TARGET = 2.0; + }; + name = Profile; + }; + D047263E19E4A008006002AA /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = ReactiveObjC/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + USER_HEADER_SEARCH_PATHS = ReactiveObjC/extobjc; + }; + name = Profile; + }; + D047263F19E4A008006002AA /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + INFOPLIST_FILE = ReactiveObjCTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Profile; + }; + D047264019E4A008006002AA /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + INFOPLIST_FILE = ReactiveObjC/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + USER_HEADER_SEARCH_PATHS = ReactiveObjC/extobjc; + }; + name = Profile; + }; + D047264119E4A008006002AA /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + INFOPLIST_FILE = ReactiveObjCTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Profile; + }; + D047264219E4A00B006002AA /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047262C19E49FE8006002AA /* Test.xcconfig */; + buildSettings = { + BITCODE_GENERATION_MODE = bitcode; + CODE_SIGNING_REQUIRED = NO; + CURRENT_PROJECT_VERSION = 1; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.9; + PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)-Tests"; + PRODUCT_NAME = "$(PROJECT_NAME)"; + TVOS_DEPLOYMENT_TARGET = 9.0; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + WATCHOS_DEPLOYMENT_TARGET = 2.0; + }; + name = Test; + }; + D047264319E4A00B006002AA /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = ReactiveObjC/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + USER_HEADER_SEARCH_PATHS = ReactiveObjC/extobjc; + }; + name = Test; + }; + D047264419E4A00B006002AA /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + INFOPLIST_FILE = ReactiveObjCTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Test; + }; + D047264519E4A00B006002AA /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + INFOPLIST_FILE = ReactiveObjC/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + USER_HEADER_SEARCH_PATHS = ReactiveObjC/extobjc; + }; + name = Test; + }; + D047264619E4A00B006002AA /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + INFOPLIST_FILE = ReactiveObjCTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Test; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 57A4D23C1BA13D7A00F7D4B1 /* Build configuration list for PBXNativeTarget "ReactiveObjC-tvOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 57A4D23D1BA13D7A00F7D4B1 /* Debug */, + 57A4D23E1BA13D7A00F7D4B1 /* Test */, + 57A4D23F1BA13D7A00F7D4B1 /* Release */, + 57A4D2401BA13D7A00F7D4B1 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7DFBED0F1CDB8C9500EE435B /* Build configuration list for PBXNativeTarget "ReactiveObjC-tvOSTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7DFBED0B1CDB8C9500EE435B /* Debug */, + 7DFBED0C1CDB8C9500EE435B /* Test */, + 7DFBED0D1CDB8C9500EE435B /* Release */, + 7DFBED0E1CDB8C9500EE435B /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A9B3155D1B3940610001CB9C /* Build configuration list for PBXNativeTarget "ReactiveObjC-watchOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A9B315591B3940610001CB9C /* Debug */, + A9B3155A1B3940610001CB9C /* Test */, + A9B3155B1B3940610001CB9C /* Release */, + A9B3155C1B3940610001CB9C /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D04725E419E49ED7006002AA /* Build configuration list for PBXProject "ReactiveObjC" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D04725FE19E49ED7006002AA /* Debug */, + D047264219E4A00B006002AA /* Test */, + D04725FF19E49ED7006002AA /* Release */, + D047263D19E4A008006002AA /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D047260019E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjC-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D047260119E49ED7006002AA /* Debug */, + D047264319E4A00B006002AA /* Test */, + D047260219E49ED7006002AA /* Release */, + D047263E19E4A008006002AA /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D047260319E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjC-macOSTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D047260419E49ED7006002AA /* Debug */, + D047264419E4A00B006002AA /* Test */, + D047260519E49ED7006002AA /* Release */, + D047263F19E4A008006002AA /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D047261F19E49F82006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjC-iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D047262019E49F82006002AA /* Debug */, + D047264519E4A00B006002AA /* Test */, + D047262119E49F82006002AA /* Release */, + D047264019E4A008006002AA /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D047262219E49F82006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjC-iOSTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D047262319E49F82006002AA /* Debug */, + D047264619E4A00B006002AA /* Test */, + D047262419E49F82006002AA /* Release */, + D047264119E4A008006002AA /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = D04725E119E49ED7006002AA /* Project object */; +} diff --git a/ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-iOS.xcscheme b/ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-iOS.xcscheme new file mode 100644 index 0000000000..52e154e943 --- /dev/null +++ b/ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-iOS.xcscheme @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-macOS.xcscheme b/ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-macOS.xcscheme new file mode 100644 index 0000000000..fbf4e6c545 --- /dev/null +++ b/ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-macOS.xcscheme @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-tvOS.xcscheme b/ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-tvOS.xcscheme new file mode 100644 index 0000000000..f171d1a28a --- /dev/null +++ b/ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-tvOS.xcscheme @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-watchOS.xcscheme b/ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-watchOS.xcscheme new file mode 100644 index 0000000000..e396f10362 --- /dev/null +++ b/ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-watchOS.xcscheme @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ReactiveObjC.xcworkspace/contents.xcworkspacedata b/ReactiveObjC.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..b62e938513 --- /dev/null +++ b/ReactiveObjC.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/ReactiveObjC/Info.plist b/ReactiveObjC/Info.plist new file mode 100644 index 0000000000..9a11afdc43 --- /dev/null +++ b/ReactiveObjC/Info.plist @@ -0,0 +1,28 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSHumanReadableCopyright + Copyright © 2014 GitHub. All rights reserved. + NSPrincipalClass + + + diff --git a/ReactiveCocoa/Objective-C/MKAnnotationView+RACSignalSupport.h b/ReactiveObjC/MKAnnotationView+RACSignalSupport.h similarity index 97% rename from ReactiveCocoa/Objective-C/MKAnnotationView+RACSignalSupport.h rename to ReactiveObjC/MKAnnotationView+RACSignalSupport.h index 2158972760..e0a3001bca 100644 --- a/ReactiveCocoa/Objective-C/MKAnnotationView+RACSignalSupport.h +++ b/ReactiveObjC/MKAnnotationView+RACSignalSupport.h @@ -1,6 +1,6 @@ // // MKAnnotationView+RACSignalSupport.h -// ReactiveCocoa +// ReactiveObjC // // Created by Zak Remer on 3/31/15. // Copyright (c) 2015 GitHub. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/MKAnnotationView+RACSignalSupport.m b/ReactiveObjC/MKAnnotationView+RACSignalSupport.m similarity index 97% rename from ReactiveCocoa/Objective-C/MKAnnotationView+RACSignalSupport.m rename to ReactiveObjC/MKAnnotationView+RACSignalSupport.m index 931f81a039..d138a79f48 100644 --- a/ReactiveCocoa/Objective-C/MKAnnotationView+RACSignalSupport.m +++ b/ReactiveObjC/MKAnnotationView+RACSignalSupport.m @@ -1,6 +1,6 @@ // // MKAnnotationView+RACSignalSupport.m -// ReactiveCocoa +// ReactiveObjC // // Created by Zak Remer on 3/31/15. // Copyright (c) 2015 GitHub. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSArray+RACSequenceAdditions.h b/ReactiveObjC/NSArray+RACSequenceAdditions.h similarity index 96% rename from ReactiveCocoa/Objective-C/NSArray+RACSequenceAdditions.h rename to ReactiveObjC/NSArray+RACSequenceAdditions.h index d2d0b3fe61..2dcc596b31 100644 --- a/ReactiveCocoa/Objective-C/NSArray+RACSequenceAdditions.h +++ b/ReactiveObjC/NSArray+RACSequenceAdditions.h @@ -1,6 +1,6 @@ // // NSArray+RACSequenceAdditions.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-10-29. // Copyright (c) 2012 GitHub. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSArray+RACSequenceAdditions.m b/ReactiveObjC/NSArray+RACSequenceAdditions.m similarity index 95% rename from ReactiveCocoa/Objective-C/NSArray+RACSequenceAdditions.m rename to ReactiveObjC/NSArray+RACSequenceAdditions.m index ca2b9511a4..4eaebb8a12 100644 --- a/ReactiveCocoa/Objective-C/NSArray+RACSequenceAdditions.m +++ b/ReactiveObjC/NSArray+RACSequenceAdditions.m @@ -1,6 +1,6 @@ // // NSArray+RACSequenceAdditions.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-10-29. // Copyright (c) 2012 GitHub. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSControl+RACCommandSupport.h b/ReactiveObjC/NSControl+RACCommandSupport.h similarity index 97% rename from ReactiveCocoa/Objective-C/NSControl+RACCommandSupport.h rename to ReactiveObjC/NSControl+RACCommandSupport.h index fecc511352..cef6126959 100644 --- a/ReactiveCocoa/Objective-C/NSControl+RACCommandSupport.h +++ b/ReactiveObjC/NSControl+RACCommandSupport.h @@ -1,6 +1,6 @@ // // NSControl+RACCommandSupport.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/3/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSControl+RACCommandSupport.m b/ReactiveObjC/NSControl+RACCommandSupport.m similarity index 99% rename from ReactiveCocoa/Objective-C/NSControl+RACCommandSupport.m rename to ReactiveObjC/NSControl+RACCommandSupport.m index 5d9db01f8f..1d8f6ba250 100644 --- a/ReactiveCocoa/Objective-C/NSControl+RACCommandSupport.m +++ b/ReactiveObjC/NSControl+RACCommandSupport.m @@ -1,6 +1,6 @@ // // NSControl+RACCommandSupport.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/3/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSControl+RACTextSignalSupport.h b/ReactiveObjC/NSControl+RACTextSignalSupport.h similarity index 96% rename from ReactiveCocoa/Objective-C/NSControl+RACTextSignalSupport.h rename to ReactiveObjC/NSControl+RACTextSignalSupport.h index 3d3618d849..b7440f899a 100644 --- a/ReactiveCocoa/Objective-C/NSControl+RACTextSignalSupport.h +++ b/ReactiveObjC/NSControl+RACTextSignalSupport.h @@ -1,6 +1,6 @@ // // NSControl+RACTextSignalSupport.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-03-08. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSControl+RACTextSignalSupport.m b/ReactiveObjC/NSControl+RACTextSignalSupport.m similarity index 95% rename from ReactiveCocoa/Objective-C/NSControl+RACTextSignalSupport.m rename to ReactiveObjC/NSControl+RACTextSignalSupport.m index 6a75402976..88da38f6e8 100644 --- a/ReactiveCocoa/Objective-C/NSControl+RACTextSignalSupport.m +++ b/ReactiveObjC/NSControl+RACTextSignalSupport.m @@ -1,13 +1,13 @@ // // NSControl+RACTextSignalSupport.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-03-08. // Copyright (c) 2013 GitHub, Inc. All rights reserved. // #import "NSControl+RACTextSignalSupport.h" -#import +#import #import "NSObject+RACDescription.h" #import "RACDisposable.h" #import "RACSignal.h" diff --git a/ReactiveCocoa/Objective-C/NSData+RACSupport.h b/ReactiveObjC/NSData+RACSupport.h similarity index 96% rename from ReactiveCocoa/Objective-C/NSData+RACSupport.h rename to ReactiveObjC/NSData+RACSupport.h index 0e47f8a39f..55edcd8216 100644 --- a/ReactiveCocoa/Objective-C/NSData+RACSupport.h +++ b/ReactiveObjC/NSData+RACSupport.h @@ -1,6 +1,6 @@ // // NSData+RACSupport.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 5/11/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSData+RACSupport.m b/ReactiveObjC/NSData+RACSupport.m similarity index 98% rename from ReactiveCocoa/Objective-C/NSData+RACSupport.m rename to ReactiveObjC/NSData+RACSupport.m index 21b5185340..977ba4aea7 100644 --- a/ReactiveCocoa/Objective-C/NSData+RACSupport.m +++ b/ReactiveObjC/NSData+RACSupport.m @@ -1,6 +1,6 @@ // // NSData+RACSupport.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 5/11/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSDictionary+RACSequenceAdditions.h b/ReactiveObjC/NSDictionary+RACSequenceAdditions.h similarity index 98% rename from ReactiveCocoa/Objective-C/NSDictionary+RACSequenceAdditions.h rename to ReactiveObjC/NSDictionary+RACSequenceAdditions.h index 4871fe722a..1eb0534c29 100644 --- a/ReactiveCocoa/Objective-C/NSDictionary+RACSequenceAdditions.h +++ b/ReactiveObjC/NSDictionary+RACSequenceAdditions.h @@ -1,6 +1,6 @@ // // NSDictionary+RACSequenceAdditions.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-10-29. // Copyright (c) 2012 GitHub. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSDictionary+RACSequenceAdditions.m b/ReactiveObjC/NSDictionary+RACSequenceAdditions.m similarity index 97% rename from ReactiveCocoa/Objective-C/NSDictionary+RACSequenceAdditions.m rename to ReactiveObjC/NSDictionary+RACSequenceAdditions.m index e0e3b9297d..2998b9a154 100644 --- a/ReactiveCocoa/Objective-C/NSDictionary+RACSequenceAdditions.m +++ b/ReactiveObjC/NSDictionary+RACSequenceAdditions.m @@ -1,6 +1,6 @@ // // NSDictionary+RACSequenceAdditions.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-10-29. // Copyright (c) 2012 GitHub. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSEnumerator+RACSequenceAdditions.h b/ReactiveObjC/NSEnumerator+RACSequenceAdditions.h similarity index 96% rename from ReactiveCocoa/Objective-C/NSEnumerator+RACSequenceAdditions.h rename to ReactiveObjC/NSEnumerator+RACSequenceAdditions.h index 1d8fc84523..4c172a69f8 100644 --- a/ReactiveCocoa/Objective-C/NSEnumerator+RACSequenceAdditions.h +++ b/ReactiveObjC/NSEnumerator+RACSequenceAdditions.h @@ -1,6 +1,6 @@ // // NSEnumerator+RACSequenceAdditions.h -// ReactiveCocoa +// ReactiveObjC // // Created by Uri Baghin on 07/01/2013. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSEnumerator+RACSequenceAdditions.m b/ReactiveObjC/NSEnumerator+RACSequenceAdditions.m similarity index 96% rename from ReactiveCocoa/Objective-C/NSEnumerator+RACSequenceAdditions.m rename to ReactiveObjC/NSEnumerator+RACSequenceAdditions.m index aa56eaaa69..59b6380009 100644 --- a/ReactiveCocoa/Objective-C/NSEnumerator+RACSequenceAdditions.m +++ b/ReactiveObjC/NSEnumerator+RACSequenceAdditions.m @@ -1,6 +1,6 @@ // // NSEnumerator+RACSequenceAdditions.m -// ReactiveCocoa +// ReactiveObjC // // Created by Uri Baghin on 07/01/2013. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSFileHandle+RACSupport.h b/ReactiveObjC/NSFileHandle+RACSupport.h similarity index 95% rename from ReactiveCocoa/Objective-C/NSFileHandle+RACSupport.h rename to ReactiveObjC/NSFileHandle+RACSupport.h index 985398de6b..705745909d 100644 --- a/ReactiveCocoa/Objective-C/NSFileHandle+RACSupport.h +++ b/ReactiveObjC/NSFileHandle+RACSupport.h @@ -1,6 +1,6 @@ // // NSFileHandle+RACSupport.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 5/10/12. // Copyright (c) 2012 GitHub. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSFileHandle+RACSupport.m b/ReactiveObjC/NSFileHandle+RACSupport.m similarity index 98% rename from ReactiveCocoa/Objective-C/NSFileHandle+RACSupport.m rename to ReactiveObjC/NSFileHandle+RACSupport.m index eaee72ec59..9d1c42f37d 100644 --- a/ReactiveCocoa/Objective-C/NSFileHandle+RACSupport.m +++ b/ReactiveObjC/NSFileHandle+RACSupport.m @@ -1,6 +1,6 @@ // // NSFileHandle+RACSupport.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 5/10/12. // Copyright (c) 2012 GitHub. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSIndexSet+RACSequenceAdditions.h b/ReactiveObjC/NSIndexSet+RACSequenceAdditions.h similarity index 96% rename from ReactiveCocoa/Objective-C/NSIndexSet+RACSequenceAdditions.h rename to ReactiveObjC/NSIndexSet+RACSequenceAdditions.h index 4fe3820fca..e431d68bc7 100644 --- a/ReactiveCocoa/Objective-C/NSIndexSet+RACSequenceAdditions.h +++ b/ReactiveObjC/NSIndexSet+RACSequenceAdditions.h @@ -1,6 +1,6 @@ // // NSIndexSet+RACSequenceAdditions.h -// ReactiveCocoa +// ReactiveObjC // // Created by Sergey Gavrilyuk on 12/17/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSIndexSet+RACSequenceAdditions.m b/ReactiveObjC/NSIndexSet+RACSequenceAdditions.m similarity index 95% rename from ReactiveCocoa/Objective-C/NSIndexSet+RACSequenceAdditions.m rename to ReactiveObjC/NSIndexSet+RACSequenceAdditions.m index b3557e64b4..e2866b823b 100644 --- a/ReactiveCocoa/Objective-C/NSIndexSet+RACSequenceAdditions.m +++ b/ReactiveObjC/NSIndexSet+RACSequenceAdditions.m @@ -1,6 +1,6 @@ // // NSIndexSet+RACSequenceAdditions.m -// ReactiveCocoa +// ReactiveObjC // // Created by Sergey Gavrilyuk on 12/17/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSInvocation+RACTypeParsing.h b/ReactiveObjC/NSInvocation+RACTypeParsing.h similarity index 99% rename from ReactiveCocoa/Objective-C/NSInvocation+RACTypeParsing.h rename to ReactiveObjC/NSInvocation+RACTypeParsing.h index d1e72bf1ff..a7ac23b731 100644 --- a/ReactiveCocoa/Objective-C/NSInvocation+RACTypeParsing.h +++ b/ReactiveObjC/NSInvocation+RACTypeParsing.h @@ -1,6 +1,6 @@ // // NSInvocation+RACTypeParsing.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 11/17/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSInvocation+RACTypeParsing.m b/ReactiveObjC/NSInvocation+RACTypeParsing.m similarity index 99% rename from ReactiveCocoa/Objective-C/NSInvocation+RACTypeParsing.m rename to ReactiveObjC/NSInvocation+RACTypeParsing.m index 9edcdd7c53..92b5b7d064 100644 --- a/ReactiveCocoa/Objective-C/NSInvocation+RACTypeParsing.m +++ b/ReactiveObjC/NSInvocation+RACTypeParsing.m @@ -1,6 +1,6 @@ // // NSInvocation+RACTypeParsing.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 11/17/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSNotificationCenter+RACSupport.h b/ReactiveObjC/NSNotificationCenter+RACSupport.h similarity index 95% rename from ReactiveCocoa/Objective-C/NSNotificationCenter+RACSupport.h rename to ReactiveObjC/NSNotificationCenter+RACSupport.h index ddb3954d01..06b8dc8637 100644 --- a/ReactiveCocoa/Objective-C/NSNotificationCenter+RACSupport.h +++ b/ReactiveObjC/NSNotificationCenter+RACSupport.h @@ -1,6 +1,6 @@ // // NSNotificationCenter+RACSupport.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 5/10/12. // Copyright (c) 2012 GitHub. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSNotificationCenter+RACSupport.m b/ReactiveObjC/NSNotificationCenter+RACSupport.m similarity index 94% rename from ReactiveCocoa/Objective-C/NSNotificationCenter+RACSupport.m rename to ReactiveObjC/NSNotificationCenter+RACSupport.m index 7147f0b5e1..6d6f94384e 100644 --- a/ReactiveCocoa/Objective-C/NSNotificationCenter+RACSupport.m +++ b/ReactiveObjC/NSNotificationCenter+RACSupport.m @@ -1,13 +1,13 @@ // // NSNotificationCenter+RACSupport.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 5/10/12. // Copyright (c) 2012 GitHub. All rights reserved. // #import "NSNotificationCenter+RACSupport.h" -#import +#import #import "RACSignal.h" #import "RACSubscriber.h" #import "RACDisposable.h" diff --git a/ReactiveCocoa/Objective-C/NSObject+RACAppKitBindings.h b/ReactiveObjC/NSObject+RACAppKitBindings.h similarity index 98% rename from ReactiveCocoa/Objective-C/NSObject+RACAppKitBindings.h rename to ReactiveObjC/NSObject+RACAppKitBindings.h index 5583d4a6cf..19d45c43c4 100644 --- a/ReactiveCocoa/Objective-C/NSObject+RACAppKitBindings.h +++ b/ReactiveObjC/NSObject+RACAppKitBindings.h @@ -1,6 +1,6 @@ // // NSObject+RACAppKitBindings.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 4/17/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSObject+RACAppKitBindings.m b/ReactiveObjC/NSObject+RACAppKitBindings.m similarity index 97% rename from ReactiveCocoa/Objective-C/NSObject+RACAppKitBindings.m rename to ReactiveObjC/NSObject+RACAppKitBindings.m index 3c2e2fc880..dde3d13101 100644 --- a/ReactiveCocoa/Objective-C/NSObject+RACAppKitBindings.m +++ b/ReactiveObjC/NSObject+RACAppKitBindings.m @@ -1,14 +1,14 @@ // // NSObject+RACAppKitBindings.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 4/17/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. // #import "NSObject+RACAppKitBindings.h" -#import -#import +#import +#import #import "NSObject+RACDeallocating.h" #import "RACChannel.h" #import "RACCompoundDisposable.h" diff --git a/ReactiveCocoa/Objective-C/NSObject+RACDeallocating.h b/ReactiveObjC/NSObject+RACDeallocating.h similarity index 98% rename from ReactiveCocoa/Objective-C/NSObject+RACDeallocating.h rename to ReactiveObjC/NSObject+RACDeallocating.h index 1daccf8971..5e9a529934 100644 --- a/ReactiveCocoa/Objective-C/NSObject+RACDeallocating.h +++ b/ReactiveObjC/NSObject+RACDeallocating.h @@ -1,6 +1,6 @@ // // NSObject+RACDeallocating.h -// ReactiveCocoa +// ReactiveObjC // // Created by Kazuo Koga on 2013/03/15. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSObject+RACDeallocating.m b/ReactiveObjC/NSObject+RACDeallocating.m similarity index 99% rename from ReactiveCocoa/Objective-C/NSObject+RACDeallocating.m rename to ReactiveObjC/NSObject+RACDeallocating.m index 01b402a786..49ed176718 100644 --- a/ReactiveCocoa/Objective-C/NSObject+RACDeallocating.m +++ b/ReactiveObjC/NSObject+RACDeallocating.m @@ -1,6 +1,6 @@ // // NSObject+RACDeallocating.m -// ReactiveCocoa +// ReactiveObjC // // Created by Kazuo Koga on 2013/03/15. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSObject+RACDescription.h b/ReactiveObjC/NSObject+RACDescription.h similarity index 96% rename from ReactiveCocoa/Objective-C/NSObject+RACDescription.h rename to ReactiveObjC/NSObject+RACDescription.h index 761205ea0f..066e102759 100644 --- a/ReactiveCocoa/Objective-C/NSObject+RACDescription.h +++ b/ReactiveObjC/NSObject+RACDescription.h @@ -1,6 +1,6 @@ // // NSObject+RACDescription.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-05-13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSObject+RACDescription.m b/ReactiveObjC/NSObject+RACDescription.m similarity index 98% rename from ReactiveCocoa/Objective-C/NSObject+RACDescription.m rename to ReactiveObjC/NSObject+RACDescription.m index ac7a735fff..1b214277eb 100644 --- a/ReactiveCocoa/Objective-C/NSObject+RACDescription.m +++ b/ReactiveObjC/NSObject+RACDescription.m @@ -1,6 +1,6 @@ // // NSObject+RACDescription.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-05-13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSObject+RACKVOWrapper.h b/ReactiveObjC/NSObject+RACKVOWrapper.h similarity index 99% rename from ReactiveCocoa/Objective-C/NSObject+RACKVOWrapper.h rename to ReactiveObjC/NSObject+RACKVOWrapper.h index 4f7177df6b..48e6a0403f 100644 --- a/ReactiveCocoa/Objective-C/NSObject+RACKVOWrapper.h +++ b/ReactiveObjC/NSObject+RACKVOWrapper.h @@ -1,6 +1,6 @@ // // NSObject+RACKVOWrapper.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 10/11/11. // Copyright (c) 2011 GitHub. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSObject+RACKVOWrapper.m b/ReactiveObjC/NSObject+RACKVOWrapper.m similarity index 98% rename from ReactiveCocoa/Objective-C/NSObject+RACKVOWrapper.m rename to ReactiveObjC/NSObject+RACKVOWrapper.m index 4a75d8116f..32e99e2669 100644 --- a/ReactiveCocoa/Objective-C/NSObject+RACKVOWrapper.m +++ b/ReactiveObjC/NSObject+RACKVOWrapper.m @@ -1,14 +1,14 @@ // // NSObject+RACKVOWrapper.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 10/11/11. // Copyright (c) 2011 GitHub. All rights reserved. // #import "NSObject+RACKVOWrapper.h" -#import -#import +#import +#import #import "NSObject+RACDeallocating.h" #import "NSString+RACKeyPathUtilities.h" #import "RACCompoundDisposable.h" diff --git a/ReactiveCocoa/Objective-C/NSObject+RACLifting.h b/ReactiveObjC/NSObject+RACLifting.h similarity index 99% rename from ReactiveCocoa/Objective-C/NSObject+RACLifting.h rename to ReactiveObjC/NSObject+RACLifting.h index 5c0c29755d..8f93563793 100644 --- a/ReactiveCocoa/Objective-C/NSObject+RACLifting.h +++ b/ReactiveObjC/NSObject+RACLifting.h @@ -1,6 +1,6 @@ // // NSObject+RACLifting.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 10/13/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSObject+RACLifting.m b/ReactiveObjC/NSObject+RACLifting.m similarity index 98% rename from ReactiveCocoa/Objective-C/NSObject+RACLifting.m rename to ReactiveObjC/NSObject+RACLifting.m index 0cb209a0f9..a0c09d5c08 100644 --- a/ReactiveCocoa/Objective-C/NSObject+RACLifting.m +++ b/ReactiveObjC/NSObject+RACLifting.m @@ -1,13 +1,13 @@ // // NSObject+RACLifting.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 10/13/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. // #import "NSObject+RACLifting.h" -#import +#import #import "NSInvocation+RACTypeParsing.h" #import "NSObject+RACDeallocating.h" #import "NSObject+RACDescription.h" diff --git a/ReactiveCocoa/Objective-C/NSObject+RACPropertySubscribing.h b/ReactiveObjC/NSObject+RACPropertySubscribing.h similarity index 98% rename from ReactiveCocoa/Objective-C/NSObject+RACPropertySubscribing.h rename to ReactiveObjC/NSObject+RACPropertySubscribing.h index fd64f0c77b..ca9d474b26 100644 --- a/ReactiveCocoa/Objective-C/NSObject+RACPropertySubscribing.h +++ b/ReactiveObjC/NSObject+RACPropertySubscribing.h @@ -1,13 +1,13 @@ // // NSObject+RACPropertySubscribing.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/2/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. // #import -#import +#import #import "metamacros.h" /// Creates a signal which observes `KEYPATH` on `TARGET` for changes. diff --git a/ReactiveCocoa/Objective-C/NSObject+RACPropertySubscribing.m b/ReactiveObjC/NSObject+RACPropertySubscribing.m similarity index 95% rename from ReactiveCocoa/Objective-C/NSObject+RACPropertySubscribing.m rename to ReactiveObjC/NSObject+RACPropertySubscribing.m index a5fcecdd7f..29e3df89e4 100644 --- a/ReactiveCocoa/Objective-C/NSObject+RACPropertySubscribing.m +++ b/ReactiveObjC/NSObject+RACPropertySubscribing.m @@ -1,13 +1,13 @@ // // NSObject+RACPropertySubscribing.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/2/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. // #import "NSObject+RACPropertySubscribing.h" -#import +#import #import "NSObject+RACDeallocating.h" #import "NSObject+RACDescription.h" #import "NSObject+RACKVOWrapper.h" @@ -36,7 +36,7 @@ - (RACSignal *)rac_valuesAndChangesForKeyPath:(NSString *)keyPath options:(NSKey keyPath = [keyPath copy]; NSRecursiveLock *objectLock = [[NSRecursiveLock alloc] init]; - objectLock.name = @"org.reactivecocoa.ReactiveCocoa.NSObjectRACPropertySubscribing"; + objectLock.name = @"org.reactivecocoa.ReactiveObjC.NSObjectRACPropertySubscribing"; __weak NSObject *weakSelf = self; diff --git a/ReactiveCocoa/Objective-C/NSObject+RACSelectorSignal.h b/ReactiveObjC/NSObject+RACSelectorSignal.h similarity index 99% rename from ReactiveCocoa/Objective-C/NSObject+RACSelectorSignal.h rename to ReactiveObjC/NSObject+RACSelectorSignal.h index c6f3de5d79..0353e105ec 100644 --- a/ReactiveCocoa/Objective-C/NSObject+RACSelectorSignal.h +++ b/ReactiveObjC/NSObject+RACSelectorSignal.h @@ -1,6 +1,6 @@ // // NSObject+RACSelectorSignal.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/18/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSObject+RACSelectorSignal.m b/ReactiveObjC/NSObject+RACSelectorSignal.m similarity index 99% rename from ReactiveCocoa/Objective-C/NSObject+RACSelectorSignal.m rename to ReactiveObjC/NSObject+RACSelectorSignal.m index ed5432ed4f..63e2320035 100644 --- a/ReactiveCocoa/Objective-C/NSObject+RACSelectorSignal.m +++ b/ReactiveObjC/NSObject+RACSelectorSignal.m @@ -1,13 +1,13 @@ // // NSObject+RACSelectorSignal.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/18/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. // #import "NSObject+RACSelectorSignal.h" -#import +#import #import "NSInvocation+RACTypeParsing.h" #import "NSObject+RACDeallocating.h" #import "RACCompoundDisposable.h" diff --git a/ReactiveCocoa/Objective-C/NSOrderedSet+RACSequenceAdditions.h b/ReactiveObjC/NSOrderedSet+RACSequenceAdditions.h similarity index 96% rename from ReactiveCocoa/Objective-C/NSOrderedSet+RACSequenceAdditions.h rename to ReactiveObjC/NSOrderedSet+RACSequenceAdditions.h index 8bea2db774..15a487d087 100644 --- a/ReactiveCocoa/Objective-C/NSOrderedSet+RACSequenceAdditions.h +++ b/ReactiveObjC/NSOrderedSet+RACSequenceAdditions.h @@ -1,6 +1,6 @@ // // NSOrderedSet+RACSequenceAdditions.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-10-29. // Copyright (c) 2012 GitHub. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSOrderedSet+RACSequenceAdditions.m b/ReactiveObjC/NSOrderedSet+RACSequenceAdditions.m similarity index 95% rename from ReactiveCocoa/Objective-C/NSOrderedSet+RACSequenceAdditions.m rename to ReactiveObjC/NSOrderedSet+RACSequenceAdditions.m index 55dfd0b7e9..31792d4573 100644 --- a/ReactiveCocoa/Objective-C/NSOrderedSet+RACSequenceAdditions.m +++ b/ReactiveObjC/NSOrderedSet+RACSequenceAdditions.m @@ -1,6 +1,6 @@ // // NSOrderedSet+RACSequenceAdditions.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-10-29. // Copyright (c) 2012 GitHub. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSSet+RACSequenceAdditions.h b/ReactiveObjC/NSSet+RACSequenceAdditions.h similarity index 96% rename from ReactiveCocoa/Objective-C/NSSet+RACSequenceAdditions.h rename to ReactiveObjC/NSSet+RACSequenceAdditions.h index 66655016d2..a6de6dc343 100644 --- a/ReactiveCocoa/Objective-C/NSSet+RACSequenceAdditions.h +++ b/ReactiveObjC/NSSet+RACSequenceAdditions.h @@ -1,6 +1,6 @@ // // NSSet+RACSequenceAdditions.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-10-29. // Copyright (c) 2012 GitHub. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSSet+RACSequenceAdditions.m b/ReactiveObjC/NSSet+RACSequenceAdditions.m similarity index 95% rename from ReactiveCocoa/Objective-C/NSSet+RACSequenceAdditions.m rename to ReactiveObjC/NSSet+RACSequenceAdditions.m index cc07f75ce6..8c7355fac4 100644 --- a/ReactiveCocoa/Objective-C/NSSet+RACSequenceAdditions.m +++ b/ReactiveObjC/NSSet+RACSequenceAdditions.m @@ -1,6 +1,6 @@ // // NSSet+RACSequenceAdditions.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-10-29. // Copyright (c) 2012 GitHub. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSString+RACKeyPathUtilities.h b/ReactiveObjC/NSString+RACKeyPathUtilities.h similarity index 98% rename from ReactiveCocoa/Objective-C/NSString+RACKeyPathUtilities.h rename to ReactiveObjC/NSString+RACKeyPathUtilities.h index d56cf59e5c..d9377042d7 100644 --- a/ReactiveCocoa/Objective-C/NSString+RACKeyPathUtilities.h +++ b/ReactiveObjC/NSString+RACKeyPathUtilities.h @@ -1,6 +1,6 @@ // // NSString+RACKeyPathUtilities.h -// ReactiveCocoa +// ReactiveObjC // // Created by Uri Baghin on 05/05/2013. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSString+RACKeyPathUtilities.m b/ReactiveObjC/NSString+RACKeyPathUtilities.m similarity index 97% rename from ReactiveCocoa/Objective-C/NSString+RACKeyPathUtilities.m rename to ReactiveObjC/NSString+RACKeyPathUtilities.m index 63a100c6cf..d62c0521b8 100644 --- a/ReactiveCocoa/Objective-C/NSString+RACKeyPathUtilities.m +++ b/ReactiveObjC/NSString+RACKeyPathUtilities.m @@ -1,6 +1,6 @@ // // NSString+RACKeyPathUtilities.m -// ReactiveCocoa +// ReactiveObjC // // Created by Uri Baghin on 05/05/2013. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSString+RACSequenceAdditions.h b/ReactiveObjC/NSString+RACSequenceAdditions.h similarity index 96% rename from ReactiveCocoa/Objective-C/NSString+RACSequenceAdditions.h rename to ReactiveObjC/NSString+RACSequenceAdditions.h index 0116231f4c..e2f9e8bd00 100644 --- a/ReactiveCocoa/Objective-C/NSString+RACSequenceAdditions.h +++ b/ReactiveObjC/NSString+RACSequenceAdditions.h @@ -1,6 +1,6 @@ // // NSString+RACSequenceAdditions.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-10-29. // Copyright (c) 2012 GitHub. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSString+RACSequenceAdditions.m b/ReactiveObjC/NSString+RACSequenceAdditions.m similarity index 95% rename from ReactiveCocoa/Objective-C/NSString+RACSequenceAdditions.m rename to ReactiveObjC/NSString+RACSequenceAdditions.m index eb6a76ef3b..cbcc31b07e 100644 --- a/ReactiveCocoa/Objective-C/NSString+RACSequenceAdditions.m +++ b/ReactiveObjC/NSString+RACSequenceAdditions.m @@ -1,6 +1,6 @@ // // NSString+RACSequenceAdditions.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-10-29. // Copyright (c) 2012 GitHub. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSString+RACSupport.h b/ReactiveObjC/NSString+RACSupport.h similarity index 97% rename from ReactiveCocoa/Objective-C/NSString+RACSupport.h rename to ReactiveObjC/NSString+RACSupport.h index 4c7fc72baa..a54c0e0823 100644 --- a/ReactiveCocoa/Objective-C/NSString+RACSupport.h +++ b/ReactiveObjC/NSString+RACSupport.h @@ -1,6 +1,6 @@ // // NSString+RACSupport.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 5/11/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSString+RACSupport.m b/ReactiveObjC/NSString+RACSupport.m similarity index 98% rename from ReactiveCocoa/Objective-C/NSString+RACSupport.m rename to ReactiveObjC/NSString+RACSupport.m index ed1058a2f4..5e98062c13 100644 --- a/ReactiveCocoa/Objective-C/NSString+RACSupport.m +++ b/ReactiveObjC/NSString+RACSupport.m @@ -1,6 +1,6 @@ // // NSString+RACSupport.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 5/11/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSText+RACSignalSupport.h b/ReactiveObjC/NSText+RACSignalSupport.h similarity index 95% rename from ReactiveCocoa/Objective-C/NSText+RACSignalSupport.h rename to ReactiveObjC/NSText+RACSignalSupport.h index e3fc8ed884..fd9afd7781 100644 --- a/ReactiveCocoa/Objective-C/NSText+RACSignalSupport.h +++ b/ReactiveObjC/NSText+RACSignalSupport.h @@ -1,6 +1,6 @@ // // NSText+RACSignalSupport.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-03-08. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSText+RACSignalSupport.m b/ReactiveObjC/NSText+RACSignalSupport.m similarity index 94% rename from ReactiveCocoa/Objective-C/NSText+RACSignalSupport.m rename to ReactiveObjC/NSText+RACSignalSupport.m index cdb3c7ab83..dfeff1ee23 100644 --- a/ReactiveCocoa/Objective-C/NSText+RACSignalSupport.m +++ b/ReactiveObjC/NSText+RACSignalSupport.m @@ -1,13 +1,13 @@ // // NSText+RACSignalSupport.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-03-08. // Copyright (c) 2013 GitHub, Inc. All rights reserved. // #import "NSText+RACSignalSupport.h" -#import +#import #import "NSObject+RACDescription.h" #import "RACDisposable.h" #import "RACSignal.h" diff --git a/ReactiveCocoa/Objective-C/NSURLConnection+RACSupport.h b/ReactiveObjC/NSURLConnection+RACSupport.h similarity index 97% rename from ReactiveCocoa/Objective-C/NSURLConnection+RACSupport.h rename to ReactiveObjC/NSURLConnection+RACSupport.h index e9bf04f314..20bb61e276 100644 --- a/ReactiveCocoa/Objective-C/NSURLConnection+RACSupport.h +++ b/ReactiveObjC/NSURLConnection+RACSupport.h @@ -1,6 +1,6 @@ // // NSURLConnection+RACSupport.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-10-01. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSURLConnection+RACSupport.m b/ReactiveObjC/NSURLConnection+RACSupport.m similarity index 94% rename from ReactiveCocoa/Objective-C/NSURLConnection+RACSupport.m rename to ReactiveObjC/NSURLConnection+RACSupport.m index ed1102dd29..7acd322aea 100644 --- a/ReactiveCocoa/Objective-C/NSURLConnection+RACSupport.m +++ b/ReactiveObjC/NSURLConnection+RACSupport.m @@ -1,6 +1,6 @@ // // NSURLConnection+RACSupport.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-10-01. // Copyright (c) 2013 GitHub, Inc. All rights reserved. @@ -20,7 +20,7 @@ + (RACSignal *)rac_sendAsynchronousRequest:(NSURLRequest *)request { return [[RACSignal createSignal:^(id subscriber) { NSOperationQueue *queue = [[NSOperationQueue alloc] init]; - queue.name = @"org.reactivecocoa.ReactiveCocoa.NSURLConnectionRACSupport"; + queue.name = @"org.reactivecocoa.ReactiveObjC.NSURLConnectionRACSupport"; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) { diff --git a/ReactiveCocoa/Objective-C/NSUserDefaults+RACSupport.h b/ReactiveObjC/NSUserDefaults+RACSupport.h similarity index 97% rename from ReactiveCocoa/Objective-C/NSUserDefaults+RACSupport.h rename to ReactiveObjC/NSUserDefaults+RACSupport.h index 8482fb3fe1..c13aa9c1d3 100644 --- a/ReactiveCocoa/Objective-C/NSUserDefaults+RACSupport.h +++ b/ReactiveObjC/NSUserDefaults+RACSupport.h @@ -1,6 +1,6 @@ // // NSUserDefaults+RACSupport.h -// ReactiveCocoa +// ReactiveObjC // // Created by Matt Diephouse on 12/19/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/NSUserDefaults+RACSupport.m b/ReactiveObjC/NSUserDefaults+RACSupport.m similarity index 96% rename from ReactiveCocoa/Objective-C/NSUserDefaults+RACSupport.m rename to ReactiveObjC/NSUserDefaults+RACSupport.m index e76f12d75e..ebb7533674 100644 --- a/ReactiveCocoa/Objective-C/NSUserDefaults+RACSupport.m +++ b/ReactiveObjC/NSUserDefaults+RACSupport.m @@ -1,13 +1,13 @@ // // NSUserDefaults+RACSupport.m -// ReactiveCocoa +// ReactiveObjC // // Created by Matt Diephouse on 12/19/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. // #import "NSUserDefaults+RACSupport.h" -#import +#import #import "NSNotificationCenter+RACSupport.h" #import "NSObject+RACDeallocating.h" #import "RACChannel.h" diff --git a/ReactiveCocoa/Objective-C/RACArraySequence.h b/ReactiveObjC/RACArraySequence.h similarity index 96% rename from ReactiveCocoa/Objective-C/RACArraySequence.h rename to ReactiveObjC/RACArraySequence.h index e9d948e059..a77959622c 100644 --- a/ReactiveCocoa/Objective-C/RACArraySequence.h +++ b/ReactiveObjC/RACArraySequence.h @@ -1,6 +1,6 @@ // // RACArraySequence.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-10-29. // Copyright (c) 2012 GitHub. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACArraySequence.m b/ReactiveObjC/RACArraySequence.m similarity index 99% rename from ReactiveCocoa/Objective-C/RACArraySequence.m rename to ReactiveObjC/RACArraySequence.m index a94ac2e991..cc9a63b122 100644 --- a/ReactiveCocoa/Objective-C/RACArraySequence.m +++ b/ReactiveObjC/RACArraySequence.m @@ -1,6 +1,6 @@ // // RACArraySequence.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-10-29. // Copyright (c) 2012 GitHub. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACBehaviorSubject.h b/ReactiveObjC/RACBehaviorSubject.h similarity index 96% rename from ReactiveCocoa/Objective-C/RACBehaviorSubject.h rename to ReactiveObjC/RACBehaviorSubject.h index e95fe6d151..9d273643c6 100644 --- a/ReactiveCocoa/Objective-C/RACBehaviorSubject.h +++ b/ReactiveObjC/RACBehaviorSubject.h @@ -1,6 +1,6 @@ // // RACBehaviorSubject.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/16/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACBehaviorSubject.m b/ReactiveObjC/RACBehaviorSubject.m similarity index 98% rename from ReactiveCocoa/Objective-C/RACBehaviorSubject.m rename to ReactiveObjC/RACBehaviorSubject.m index dfda2ac073..be0a60091a 100644 --- a/ReactiveCocoa/Objective-C/RACBehaviorSubject.m +++ b/ReactiveObjC/RACBehaviorSubject.m @@ -1,6 +1,6 @@ // // RACBehaviorSubject.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/16/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACBlockTrampoline.h b/ReactiveObjC/RACBlockTrampoline.h similarity index 98% rename from ReactiveCocoa/Objective-C/RACBlockTrampoline.h rename to ReactiveObjC/RACBlockTrampoline.h index 3857d7c85f..c0e89bca4a 100644 --- a/ReactiveCocoa/Objective-C/RACBlockTrampoline.h +++ b/ReactiveObjC/RACBlockTrampoline.h @@ -1,6 +1,6 @@ // // RACBlockTrampoline.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 10/21/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACBlockTrampoline.m b/ReactiveObjC/RACBlockTrampoline.m similarity index 99% rename from ReactiveCocoa/Objective-C/RACBlockTrampoline.m rename to ReactiveObjC/RACBlockTrampoline.m index 07903dd9c0..483a89b772 100644 --- a/ReactiveCocoa/Objective-C/RACBlockTrampoline.m +++ b/ReactiveObjC/RACBlockTrampoline.m @@ -1,6 +1,6 @@ // // RACBlockTrampoline.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 10/21/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACChannel.h b/ReactiveObjC/RACChannel.h similarity index 99% rename from ReactiveCocoa/Objective-C/RACChannel.h rename to ReactiveObjC/RACChannel.h index ef1d6f1ba9..67a7306afd 100644 --- a/ReactiveCocoa/Objective-C/RACChannel.h +++ b/ReactiveObjC/RACChannel.h @@ -1,6 +1,6 @@ // // RACChannel.h -// ReactiveCocoa +// ReactiveObjC // // Created by Uri Baghin on 01/01/2013. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACChannel.m b/ReactiveObjC/RACChannel.m similarity index 99% rename from ReactiveCocoa/Objective-C/RACChannel.m rename to ReactiveObjC/RACChannel.m index afe850a0b1..10138eeac0 100644 --- a/ReactiveCocoa/Objective-C/RACChannel.m +++ b/ReactiveObjC/RACChannel.m @@ -1,6 +1,6 @@ // // RACChannel.m -// ReactiveCocoa +// ReactiveObjC // // Created by Uri Baghin on 01/01/2013. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACCommand.h b/ReactiveObjC/RACCommand.h similarity index 99% rename from ReactiveCocoa/Objective-C/RACCommand.h rename to ReactiveObjC/RACCommand.h index d59ca67305..23e20dc618 100644 --- a/ReactiveCocoa/Objective-C/RACCommand.h +++ b/ReactiveObjC/RACCommand.h @@ -1,6 +1,6 @@ // // RACCommand.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/3/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACCommand.m b/ReactiveObjC/RACCommand.m similarity index 99% rename from ReactiveCocoa/Objective-C/RACCommand.m rename to ReactiveObjC/RACCommand.m index ea79754066..b7f77d0fd9 100644 --- a/ReactiveCocoa/Objective-C/RACCommand.m +++ b/ReactiveObjC/RACCommand.m @@ -1,13 +1,13 @@ // // RACCommand.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/3/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. // #import "RACCommand.h" -#import +#import #import "NSArray+RACSequenceAdditions.h" #import "NSObject+RACDeallocating.h" #import "NSObject+RACDescription.h" diff --git a/ReactiveCocoa/Objective-C/RACCompoundDisposable.h b/ReactiveObjC/RACCompoundDisposable.h similarity index 98% rename from ReactiveCocoa/Objective-C/RACCompoundDisposable.h rename to ReactiveObjC/RACCompoundDisposable.h index bb25f7d2b5..ca3fc202c1 100644 --- a/ReactiveCocoa/Objective-C/RACCompoundDisposable.h +++ b/ReactiveObjC/RACCompoundDisposable.h @@ -1,6 +1,6 @@ // // RACCompoundDisposable.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 11/30/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACCompoundDisposable.m b/ReactiveObjC/RACCompoundDisposable.m similarity index 99% rename from ReactiveCocoa/Objective-C/RACCompoundDisposable.m rename to ReactiveObjC/RACCompoundDisposable.m index 3c3575e69c..c081938d03 100644 --- a/ReactiveCocoa/Objective-C/RACCompoundDisposable.m +++ b/ReactiveObjC/RACCompoundDisposable.m @@ -1,6 +1,6 @@ // // RACCompoundDisposable.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 11/30/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACCompoundDisposableProvider.d b/ReactiveObjC/RACCompoundDisposableProvider.d similarity index 100% rename from ReactiveCocoa/Objective-C/RACCompoundDisposableProvider.d rename to ReactiveObjC/RACCompoundDisposableProvider.d diff --git a/ReactiveCocoa/Objective-C/RACDelegateProxy.h b/ReactiveObjC/RACDelegateProxy.h similarity index 97% rename from ReactiveCocoa/Objective-C/RACDelegateProxy.h rename to ReactiveObjC/RACDelegateProxy.h index 9ec96bd033..60b94b9ff7 100644 --- a/ReactiveCocoa/Objective-C/RACDelegateProxy.h +++ b/ReactiveObjC/RACDelegateProxy.h @@ -1,6 +1,6 @@ // // RACDelegateProxy.h -// ReactiveCocoa +// ReactiveObjC // // Created by Cody Krieger on 5/19/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACDelegateProxy.m b/ReactiveObjC/RACDelegateProxy.m similarity index 99% rename from ReactiveCocoa/Objective-C/RACDelegateProxy.m rename to ReactiveObjC/RACDelegateProxy.m index 91cac4b58d..12a13a6fb5 100644 --- a/ReactiveCocoa/Objective-C/RACDelegateProxy.m +++ b/ReactiveObjC/RACDelegateProxy.m @@ -1,6 +1,6 @@ // // RACDelegateProxy.m -// ReactiveCocoa +// ReactiveObjC // // Created by Cody Krieger on 5/19/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACDisposable.h b/ReactiveObjC/RACDisposable.h similarity index 98% rename from ReactiveCocoa/Objective-C/RACDisposable.h rename to ReactiveObjC/RACDisposable.h index 5b4cf0bd39..19ba0b6fd8 100644 --- a/ReactiveCocoa/Objective-C/RACDisposable.h +++ b/ReactiveObjC/RACDisposable.h @@ -1,6 +1,6 @@ // // RACDisposable.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/16/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACDisposable.m b/ReactiveObjC/RACDisposable.m similarity index 99% rename from ReactiveCocoa/Objective-C/RACDisposable.m rename to ReactiveObjC/RACDisposable.m index 08eceb228e..6bd569826c 100644 --- a/ReactiveCocoa/Objective-C/RACDisposable.m +++ b/ReactiveObjC/RACDisposable.m @@ -1,6 +1,6 @@ // // RACDisposable.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/16/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACDynamicSequence.h b/ReactiveObjC/RACDynamicSequence.h similarity index 97% rename from ReactiveCocoa/Objective-C/RACDynamicSequence.h rename to ReactiveObjC/RACDynamicSequence.h index 1e103e393c..545ea18afe 100644 --- a/ReactiveCocoa/Objective-C/RACDynamicSequence.h +++ b/ReactiveObjC/RACDynamicSequence.h @@ -1,6 +1,6 @@ // // RACDynamicSequence.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-10-29. // Copyright (c) 2012 GitHub. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACDynamicSequence.m b/ReactiveObjC/RACDynamicSequence.m similarity index 99% rename from ReactiveCocoa/Objective-C/RACDynamicSequence.m rename to ReactiveObjC/RACDynamicSequence.m index 48a977b84b..86d8be9c2c 100644 --- a/ReactiveCocoa/Objective-C/RACDynamicSequence.m +++ b/ReactiveObjC/RACDynamicSequence.m @@ -1,6 +1,6 @@ // // RACDynamicSequence.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-10-29. // Copyright (c) 2012 GitHub. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACDynamicSignal.h b/ReactiveObjC/RACDynamicSignal.h similarity index 95% rename from ReactiveCocoa/Objective-C/RACDynamicSignal.h rename to ReactiveObjC/RACDynamicSignal.h index 81ac6db8ba..cadd7bc02b 100644 --- a/ReactiveCocoa/Objective-C/RACDynamicSignal.h +++ b/ReactiveObjC/RACDynamicSignal.h @@ -1,6 +1,6 @@ // // RACDynamicSignal.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-10-10. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACDynamicSignal.m b/ReactiveObjC/RACDynamicSignal.m similarity index 96% rename from ReactiveCocoa/Objective-C/RACDynamicSignal.m rename to ReactiveObjC/RACDynamicSignal.m index c51c873c0d..3bee2f7c2b 100644 --- a/ReactiveCocoa/Objective-C/RACDynamicSignal.m +++ b/ReactiveObjC/RACDynamicSignal.m @@ -1,13 +1,13 @@ // // RACDynamicSignal.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-10-10. // Copyright (c) 2013 GitHub, Inc. All rights reserved. // #import "RACDynamicSignal.h" -#import +#import #import "RACCompoundDisposable.h" #import "RACPassthroughSubscriber.h" #import "RACScheduler+Private.h" diff --git a/ReactiveCocoa/Objective-C/RACEagerSequence.h b/ReactiveObjC/RACEagerSequence.h similarity index 93% rename from ReactiveCocoa/Objective-C/RACEagerSequence.h rename to ReactiveObjC/RACEagerSequence.h index ab5aa9f925..04ddf7e34b 100644 --- a/ReactiveCocoa/Objective-C/RACEagerSequence.h +++ b/ReactiveObjC/RACEagerSequence.h @@ -1,6 +1,6 @@ // // RACEagerSequence.h -// ReactiveCocoa +// ReactiveObjC // // Created by Uri Baghin on 02/01/2013. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACEagerSequence.m b/ReactiveObjC/RACEagerSequence.m similarity index 99% rename from ReactiveCocoa/Objective-C/RACEagerSequence.m rename to ReactiveObjC/RACEagerSequence.m index b433990fbd..56815ec993 100644 --- a/ReactiveCocoa/Objective-C/RACEagerSequence.m +++ b/ReactiveObjC/RACEagerSequence.m @@ -1,6 +1,6 @@ // // RACEagerSequence.m -// ReactiveCocoa +// ReactiveObjC // // Created by Uri Baghin on 02/01/2013. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACEmptySequence.h b/ReactiveObjC/RACEmptySequence.h similarity index 93% rename from ReactiveCocoa/Objective-C/RACEmptySequence.h rename to ReactiveObjC/RACEmptySequence.h index 140c78b7d1..ea91e41c82 100644 --- a/ReactiveCocoa/Objective-C/RACEmptySequence.h +++ b/ReactiveObjC/RACEmptySequence.h @@ -1,6 +1,6 @@ // // RACEmptySequence.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-10-29. // Copyright (c) 2012 GitHub. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACEmptySequence.m b/ReactiveObjC/RACEmptySequence.m similarity index 98% rename from ReactiveCocoa/Objective-C/RACEmptySequence.m rename to ReactiveObjC/RACEmptySequence.m index e4df150777..7413ac24e0 100644 --- a/ReactiveCocoa/Objective-C/RACEmptySequence.m +++ b/ReactiveObjC/RACEmptySequence.m @@ -1,6 +1,6 @@ // // RACEmptySequence.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-10-29. // Copyright (c) 2012 GitHub. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACEmptySignal.h b/ReactiveObjC/RACEmptySignal.h similarity index 94% rename from ReactiveCocoa/Objective-C/RACEmptySignal.h rename to ReactiveObjC/RACEmptySignal.h index 90b4f00aea..854749f00b 100644 --- a/ReactiveCocoa/Objective-C/RACEmptySignal.h +++ b/ReactiveObjC/RACEmptySignal.h @@ -1,6 +1,6 @@ // // RACEmptySignal.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-10-10. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACEmptySignal.m b/ReactiveObjC/RACEmptySignal.m similarity index 98% rename from ReactiveCocoa/Objective-C/RACEmptySignal.m rename to ReactiveObjC/RACEmptySignal.m index 8bc8f41eb5..d47ac27336 100644 --- a/ReactiveCocoa/Objective-C/RACEmptySignal.m +++ b/ReactiveObjC/RACEmptySignal.m @@ -1,6 +1,6 @@ // // RACEmptySignal.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-10-10. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACErrorSignal.h b/ReactiveObjC/RACErrorSignal.h similarity index 94% rename from ReactiveCocoa/Objective-C/RACErrorSignal.h rename to ReactiveObjC/RACErrorSignal.h index c942f9e3a8..fdb600ecf0 100644 --- a/ReactiveCocoa/Objective-C/RACErrorSignal.h +++ b/ReactiveObjC/RACErrorSignal.h @@ -1,6 +1,6 @@ // // RACErrorSignal.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-10-10. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACErrorSignal.m b/ReactiveObjC/RACErrorSignal.m similarity index 98% rename from ReactiveCocoa/Objective-C/RACErrorSignal.m rename to ReactiveObjC/RACErrorSignal.m index fd6778cb67..1d08a2f930 100644 --- a/ReactiveCocoa/Objective-C/RACErrorSignal.m +++ b/ReactiveObjC/RACErrorSignal.m @@ -1,6 +1,6 @@ // // RACErrorSignal.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-10-10. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACEvent.h b/ReactiveObjC/RACEvent.h similarity index 98% rename from ReactiveCocoa/Objective-C/RACEvent.h rename to ReactiveObjC/RACEvent.h index a8f10e11f2..3b2ce96016 100644 --- a/ReactiveCocoa/Objective-C/RACEvent.h +++ b/ReactiveObjC/RACEvent.h @@ -1,6 +1,6 @@ // // RACEvent.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-01-07. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACEvent.m b/ReactiveObjC/RACEvent.m similarity index 99% rename from ReactiveCocoa/Objective-C/RACEvent.m rename to ReactiveObjC/RACEvent.m index 270a95d76c..da1e185434 100644 --- a/ReactiveCocoa/Objective-C/RACEvent.m +++ b/ReactiveObjC/RACEvent.m @@ -1,6 +1,6 @@ // // RACEvent.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-01-07. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACGroupedSignal.h b/ReactiveObjC/RACGroupedSignal.h similarity index 95% rename from ReactiveCocoa/Objective-C/RACGroupedSignal.h rename to ReactiveObjC/RACGroupedSignal.h index 04c151b704..ef342874c2 100644 --- a/ReactiveCocoa/Objective-C/RACGroupedSignal.h +++ b/ReactiveObjC/RACGroupedSignal.h @@ -1,6 +1,6 @@ // // RACGroupedSignal.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 5/2/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACGroupedSignal.m b/ReactiveObjC/RACGroupedSignal.m similarity index 96% rename from ReactiveCocoa/Objective-C/RACGroupedSignal.m rename to ReactiveObjC/RACGroupedSignal.m index 00e03784d5..a391ef25bd 100644 --- a/ReactiveCocoa/Objective-C/RACGroupedSignal.m +++ b/ReactiveObjC/RACGroupedSignal.m @@ -1,6 +1,6 @@ // // RACGroupedSignal.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 5/2/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACImmediateScheduler.h b/ReactiveObjC/RACImmediateScheduler.h similarity index 94% rename from ReactiveCocoa/Objective-C/RACImmediateScheduler.h rename to ReactiveObjC/RACImmediateScheduler.h index 76b5b5014f..edafe062b0 100644 --- a/ReactiveCocoa/Objective-C/RACImmediateScheduler.h +++ b/ReactiveObjC/RACImmediateScheduler.h @@ -1,6 +1,6 @@ // // RACImmediateScheduler.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 11/30/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACImmediateScheduler.m b/ReactiveObjC/RACImmediateScheduler.m similarity index 90% rename from ReactiveCocoa/Objective-C/RACImmediateScheduler.m rename to ReactiveObjC/RACImmediateScheduler.m index 0dfa06095d..920a29fdf7 100644 --- a/ReactiveCocoa/Objective-C/RACImmediateScheduler.m +++ b/ReactiveObjC/RACImmediateScheduler.m @@ -1,6 +1,6 @@ // // RACImmediateScheduler.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 11/30/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. @@ -14,7 +14,7 @@ @implementation RACImmediateScheduler #pragma mark Lifecycle - (id)init { - return [super initWithName:@"com.ReactiveCocoa.RACScheduler.immediateScheduler"]; + return [super initWithName:@"org.reactivecocoa.ReactiveObjC.RACScheduler.immediateScheduler"]; } #pragma mark RACScheduler diff --git a/ReactiveCocoa/Objective-C/RACIndexSetSequence.h b/ReactiveObjC/RACIndexSetSequence.h similarity index 95% rename from ReactiveCocoa/Objective-C/RACIndexSetSequence.h rename to ReactiveObjC/RACIndexSetSequence.h index eab84a39f3..020dbb27f3 100644 --- a/ReactiveCocoa/Objective-C/RACIndexSetSequence.h +++ b/ReactiveObjC/RACIndexSetSequence.h @@ -1,6 +1,6 @@ // // RACIndexSetSequence.h -// ReactiveCocoa +// ReactiveObjC // // Created by Sergey Gavrilyuk on 12/18/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACIndexSetSequence.m b/ReactiveObjC/RACIndexSetSequence.m similarity index 99% rename from ReactiveCocoa/Objective-C/RACIndexSetSequence.m rename to ReactiveObjC/RACIndexSetSequence.m index d9393db20e..2fe98a6eae 100644 --- a/ReactiveCocoa/Objective-C/RACIndexSetSequence.m +++ b/ReactiveObjC/RACIndexSetSequence.m @@ -1,6 +1,6 @@ // // RACIndexSetSequence.m -// ReactiveCocoa +// ReactiveObjC // // Created by Sergey Gavrilyuk on 12/18/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACKVOChannel.h b/ReactiveObjC/RACKVOChannel.h similarity index 98% rename from ReactiveCocoa/Objective-C/RACKVOChannel.h rename to ReactiveObjC/RACKVOChannel.h index 314d68b41a..0cc6efc8db 100644 --- a/ReactiveCocoa/Objective-C/RACKVOChannel.h +++ b/ReactiveObjC/RACKVOChannel.h @@ -1,13 +1,13 @@ // // RACKVOChannel.h -// ReactiveCocoa +// ReactiveObjC // // Created by Uri Baghin on 27/12/2012. // Copyright (c) 2012 GitHub, Inc. All rights reserved. // #import "RACChannel.h" -#import +#import #import "metamacros.h" /// Creates a RACKVOChannel to the given key path. When the targeted object diff --git a/ReactiveCocoa/Objective-C/RACKVOChannel.m b/ReactiveObjC/RACKVOChannel.m similarity index 99% rename from ReactiveCocoa/Objective-C/RACKVOChannel.m rename to ReactiveObjC/RACKVOChannel.m index 76b6cd191a..0b3ad89731 100644 --- a/ReactiveCocoa/Objective-C/RACKVOChannel.m +++ b/ReactiveObjC/RACKVOChannel.m @@ -1,13 +1,13 @@ // // RACKVOChannel.m -// ReactiveCocoa +// ReactiveObjC // // Created by Uri Baghin on 27/12/2012. // Copyright (c) 2012 GitHub, Inc. All rights reserved. // #import "RACKVOChannel.h" -#import +#import #import "NSObject+RACDeallocating.h" #import "NSObject+RACKVOWrapper.h" #import "NSString+RACKeyPathUtilities.h" diff --git a/ReactiveCocoa/Objective-C/RACKVOProxy.h b/ReactiveObjC/RACKVOProxy.h similarity index 98% rename from ReactiveCocoa/Objective-C/RACKVOProxy.h rename to ReactiveObjC/RACKVOProxy.h index 61adc8cafc..7e49793600 100644 --- a/ReactiveCocoa/Objective-C/RACKVOProxy.h +++ b/ReactiveObjC/RACKVOProxy.h @@ -1,6 +1,6 @@ // // RACKVOProxy.h -// ReactiveCocoa +// ReactiveObjC // // Created by Richard Speyer on 4/10/14. // Copyright (c) 2014 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACKVOProxy.m b/ReactiveObjC/RACKVOProxy.m similarity index 92% rename from ReactiveCocoa/Objective-C/RACKVOProxy.m rename to ReactiveObjC/RACKVOProxy.m index 0fde1961fd..c0b7debecb 100644 --- a/ReactiveCocoa/Objective-C/RACKVOProxy.m +++ b/ReactiveObjC/RACKVOProxy.m @@ -1,6 +1,6 @@ // // RACKVOProxy.m -// ReactiveCocoa +// ReactiveObjC // // Created by Richard Speyer on 4/10/14. // Copyright (c) 2014 GitHub, Inc. All rights reserved. @@ -32,7 +32,7 @@ - (instancetype)init { self = [super init]; if (self == nil) return nil; - _queue = dispatch_queue_create("org.reactivecocoa.ReactiveCocoa.RACKVOProxy", DISPATCH_QUEUE_SERIAL); + _queue = dispatch_queue_create("org.reactivecocoa.ReactiveObjC.RACKVOProxy", DISPATCH_QUEUE_SERIAL); _trampolines = [NSMapTable strongToWeakObjectsMapTable]; return self; diff --git a/ReactiveCocoa/Objective-C/RACKVOTrampoline.h b/ReactiveObjC/RACKVOTrampoline.h similarity index 98% rename from ReactiveCocoa/Objective-C/RACKVOTrampoline.h rename to ReactiveObjC/RACKVOTrampoline.h index 8de99dc5f0..dd79fb97c8 100644 --- a/ReactiveCocoa/Objective-C/RACKVOTrampoline.h +++ b/ReactiveObjC/RACKVOTrampoline.h @@ -1,6 +1,6 @@ // // RACKVOTrampoline.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 1/15/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACKVOTrampoline.m b/ReactiveObjC/RACKVOTrampoline.m similarity index 99% rename from ReactiveCocoa/Objective-C/RACKVOTrampoline.m rename to ReactiveObjC/RACKVOTrampoline.m index a9194cbf34..1f3a1fe726 100644 --- a/ReactiveCocoa/Objective-C/RACKVOTrampoline.m +++ b/ReactiveObjC/RACKVOTrampoline.m @@ -1,6 +1,6 @@ // // RACKVOTrampoline.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 1/15/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACMulticastConnection+Private.h b/ReactiveObjC/RACMulticastConnection+Private.h similarity index 94% rename from ReactiveCocoa/Objective-C/RACMulticastConnection+Private.h rename to ReactiveObjC/RACMulticastConnection+Private.h index 43931f85f5..29ee5a42fa 100644 --- a/ReactiveCocoa/Objective-C/RACMulticastConnection+Private.h +++ b/ReactiveObjC/RACMulticastConnection+Private.h @@ -1,6 +1,6 @@ // // RACMulticastConnection+Private.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 4/11/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACMulticastConnection.h b/ReactiveObjC/RACMulticastConnection.h similarity index 98% rename from ReactiveCocoa/Objective-C/RACMulticastConnection.h rename to ReactiveObjC/RACMulticastConnection.h index 67beff6bd5..9577fe812f 100644 --- a/ReactiveCocoa/Objective-C/RACMulticastConnection.h +++ b/ReactiveObjC/RACMulticastConnection.h @@ -1,6 +1,6 @@ // // RACMulticastConnection.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 4/11/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACMulticastConnection.m b/ReactiveObjC/RACMulticastConnection.m similarity index 99% rename from ReactiveCocoa/Objective-C/RACMulticastConnection.m rename to ReactiveObjC/RACMulticastConnection.m index 1534d96f46..e0da72a6e4 100644 --- a/ReactiveCocoa/Objective-C/RACMulticastConnection.m +++ b/ReactiveObjC/RACMulticastConnection.m @@ -1,6 +1,6 @@ // // RACMulticastConnection.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 4/11/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACPassthroughSubscriber.h b/ReactiveObjC/RACPassthroughSubscriber.h similarity index 98% rename from ReactiveCocoa/Objective-C/RACPassthroughSubscriber.h rename to ReactiveObjC/RACPassthroughSubscriber.h index 9ae547bc8b..3f9eddbe0d 100644 --- a/ReactiveCocoa/Objective-C/RACPassthroughSubscriber.h +++ b/ReactiveObjC/RACPassthroughSubscriber.h @@ -1,6 +1,6 @@ // // RACPassthroughSubscriber.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-06-13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACPassthroughSubscriber.m b/ReactiveObjC/RACPassthroughSubscriber.m similarity index 99% rename from ReactiveCocoa/Objective-C/RACPassthroughSubscriber.m rename to ReactiveObjC/RACPassthroughSubscriber.m index 7857e553c8..85018f5b11 100644 --- a/ReactiveCocoa/Objective-C/RACPassthroughSubscriber.m +++ b/ReactiveObjC/RACPassthroughSubscriber.m @@ -1,6 +1,6 @@ // // RACPassthroughSubscriber.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-06-13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACQueueScheduler+Subclass.h b/ReactiveObjC/RACQueueScheduler+Subclass.h similarity index 98% rename from ReactiveCocoa/Objective-C/RACQueueScheduler+Subclass.h rename to ReactiveObjC/RACQueueScheduler+Subclass.h index b0f5da60f6..8e9e88136f 100644 --- a/ReactiveCocoa/Objective-C/RACQueueScheduler+Subclass.h +++ b/ReactiveObjC/RACQueueScheduler+Subclass.h @@ -1,6 +1,6 @@ // // RACQueueScheduler+Subclass.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 6/6/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACQueueScheduler.h b/ReactiveObjC/RACQueueScheduler.h similarity index 96% rename from ReactiveCocoa/Objective-C/RACQueueScheduler.h rename to ReactiveObjC/RACQueueScheduler.h index ef42512f9f..cf0de462f5 100644 --- a/ReactiveCocoa/Objective-C/RACQueueScheduler.h +++ b/ReactiveObjC/RACQueueScheduler.h @@ -1,6 +1,6 @@ // // RACQueueScheduler.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 11/30/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACQueueScheduler.m b/ReactiveObjC/RACQueueScheduler.m similarity index 99% rename from ReactiveCocoa/Objective-C/RACQueueScheduler.m rename to ReactiveObjC/RACQueueScheduler.m index 7cc1ce4351..44f5e698c0 100644 --- a/ReactiveCocoa/Objective-C/RACQueueScheduler.m +++ b/ReactiveObjC/RACQueueScheduler.m @@ -1,6 +1,6 @@ // // RACQueueScheduler.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 11/30/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACReplaySubject.h b/ReactiveObjC/RACReplaySubject.h similarity index 97% rename from ReactiveCocoa/Objective-C/RACReplaySubject.h rename to ReactiveObjC/RACReplaySubject.h index 3d429ea47b..62753f8fb6 100644 --- a/ReactiveCocoa/Objective-C/RACReplaySubject.h +++ b/ReactiveObjC/RACReplaySubject.h @@ -1,6 +1,6 @@ // // RACReplaySubject.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/14/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACReplaySubject.m b/ReactiveObjC/RACReplaySubject.m similarity index 99% rename from ReactiveCocoa/Objective-C/RACReplaySubject.m rename to ReactiveObjC/RACReplaySubject.m index 4cf1078090..6916ff8b1f 100644 --- a/ReactiveCocoa/Objective-C/RACReplaySubject.m +++ b/ReactiveObjC/RACReplaySubject.m @@ -1,6 +1,6 @@ // // RACReplaySubject.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/14/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACReturnSignal.h b/ReactiveObjC/RACReturnSignal.h similarity index 95% rename from ReactiveCocoa/Objective-C/RACReturnSignal.h rename to ReactiveObjC/RACReturnSignal.h index 73e56746aa..1952847444 100644 --- a/ReactiveCocoa/Objective-C/RACReturnSignal.h +++ b/ReactiveObjC/RACReturnSignal.h @@ -1,6 +1,6 @@ // // RACReturnSignal.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-10-10. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACReturnSignal.m b/ReactiveObjC/RACReturnSignal.m similarity index 99% rename from ReactiveCocoa/Objective-C/RACReturnSignal.m rename to ReactiveObjC/RACReturnSignal.m index 300663457f..dca319d080 100644 --- a/ReactiveCocoa/Objective-C/RACReturnSignal.m +++ b/ReactiveObjC/RACReturnSignal.m @@ -1,6 +1,6 @@ // // RACReturnSignal.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-10-10. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACScheduler+Private.h b/ReactiveObjC/RACScheduler+Private.h similarity index 98% rename from ReactiveCocoa/Objective-C/RACScheduler+Private.h rename to ReactiveObjC/RACScheduler+Private.h index 2c91e66c76..d14956c584 100644 --- a/ReactiveCocoa/Objective-C/RACScheduler+Private.h +++ b/ReactiveObjC/RACScheduler+Private.h @@ -1,6 +1,6 @@ // // RACScheduler+Private.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 11/29/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACScheduler+Subclass.h b/ReactiveObjC/RACScheduler+Subclass.h similarity index 98% rename from ReactiveCocoa/Objective-C/RACScheduler+Subclass.h rename to ReactiveObjC/RACScheduler+Subclass.h index b6e8a9e953..8694c6fac5 100644 --- a/ReactiveCocoa/Objective-C/RACScheduler+Subclass.h +++ b/ReactiveObjC/RACScheduler+Subclass.h @@ -1,6 +1,6 @@ // // RACScheduler.m -// ReactiveCocoa +// ReactiveObjC // // Created by Miķelis Vindavs on 5/27/14. // Copyright (c) 2014 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACScheduler.h b/ReactiveObjC/RACScheduler.h similarity index 99% rename from ReactiveCocoa/Objective-C/RACScheduler.h rename to ReactiveObjC/RACScheduler.h index 81e9f1806c..4dcc22966f 100644 --- a/ReactiveCocoa/Objective-C/RACScheduler.h +++ b/ReactiveObjC/RACScheduler.h @@ -1,6 +1,6 @@ // // RACScheduler.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 4/16/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACScheduler.m b/ReactiveObjC/RACScheduler.m similarity index 94% rename from ReactiveCocoa/Objective-C/RACScheduler.m rename to ReactiveObjC/RACScheduler.m index b414041f77..3d90578993 100644 --- a/ReactiveCocoa/Objective-C/RACScheduler.m +++ b/ReactiveObjC/RACScheduler.m @@ -1,6 +1,6 @@ // // RACScheduler.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 4/16/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. @@ -36,7 +36,7 @@ - (id)initWithName:(NSString *)name { if (self == nil) return nil; if (name == nil) { - _name = [NSString stringWithFormat:@"com.ReactiveCocoa.%@.anonymousScheduler", self.class]; + _name = [NSString stringWithFormat:@"org.reactivecocoa.ReactiveObjC.%@.anonymousScheduler", self.class]; } else { _name = [name copy]; } @@ -60,7 +60,7 @@ + (instancetype)mainThreadScheduler { static dispatch_once_t onceToken; static RACScheduler *mainThreadScheduler; dispatch_once(&onceToken, ^{ - mainThreadScheduler = [[RACTargetQueueScheduler alloc] initWithName:@"com.ReactiveCocoa.RACScheduler.mainThreadScheduler" targetQueue:dispatch_get_main_queue()]; + mainThreadScheduler = [[RACTargetQueueScheduler alloc] initWithName:@"org.reactivecocoa.ReactiveObjC.RACScheduler.mainThreadScheduler" targetQueue:dispatch_get_main_queue()]; }); return mainThreadScheduler; @@ -71,7 +71,7 @@ + (instancetype)schedulerWithPriority:(RACSchedulerPriority)priority name:(NSStr } + (instancetype)schedulerWithPriority:(RACSchedulerPriority)priority { - return [self schedulerWithPriority:priority name:@"com.ReactiveCocoa.RACScheduler.backgroundScheduler"]; + return [self schedulerWithPriority:priority name:@"org.reactivecocoa.ReactiveObjC.RACScheduler.backgroundScheduler"]; } + (instancetype)scheduler { diff --git a/ReactiveCocoa/Objective-C/RACScopedDisposable.h b/ReactiveObjC/RACScopedDisposable.h similarity index 96% rename from ReactiveCocoa/Objective-C/RACScopedDisposable.h rename to ReactiveObjC/RACScopedDisposable.h index 69c4a125e5..a845849710 100644 --- a/ReactiveCocoa/Objective-C/RACScopedDisposable.h +++ b/ReactiveObjC/RACScopedDisposable.h @@ -1,6 +1,6 @@ // // RACScopedDisposable.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/28/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACScopedDisposable.m b/ReactiveObjC/RACScopedDisposable.m similarity index 96% rename from ReactiveCocoa/Objective-C/RACScopedDisposable.m rename to ReactiveObjC/RACScopedDisposable.m index 91115be5ef..2da50cb91d 100644 --- a/ReactiveCocoa/Objective-C/RACScopedDisposable.m +++ b/ReactiveObjC/RACScopedDisposable.m @@ -1,6 +1,6 @@ // // RACScopedDisposable.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/28/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACSequence.h b/ReactiveObjC/RACSequence.h similarity index 99% rename from ReactiveCocoa/Objective-C/RACSequence.h rename to ReactiveObjC/RACSequence.h index bd3b51c8bc..80d4765c2d 100644 --- a/ReactiveCocoa/Objective-C/RACSequence.h +++ b/ReactiveObjC/RACSequence.h @@ -1,6 +1,6 @@ // // RACSequence.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-10-29. // Copyright (c) 2012 GitHub. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACSequence.m b/ReactiveObjC/RACSequence.m similarity index 99% rename from ReactiveCocoa/Objective-C/RACSequence.m rename to ReactiveObjC/RACSequence.m index 94491c5e86..b9b1ee011a 100644 --- a/ReactiveCocoa/Objective-C/RACSequence.m +++ b/ReactiveObjC/RACSequence.m @@ -1,6 +1,6 @@ // // RACSequence.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-10-29. // Copyright (c) 2012 GitHub. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACSerialDisposable.h b/ReactiveObjC/RACSerialDisposable.h similarity index 98% rename from ReactiveCocoa/Objective-C/RACSerialDisposable.h rename to ReactiveObjC/RACSerialDisposable.h index a3fc1d45eb..992196e73f 100644 --- a/ReactiveCocoa/Objective-C/RACSerialDisposable.h +++ b/ReactiveObjC/RACSerialDisposable.h @@ -1,6 +1,6 @@ // // RACSerialDisposable.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-07-22. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACSerialDisposable.m b/ReactiveObjC/RACSerialDisposable.m similarity index 99% rename from ReactiveCocoa/Objective-C/RACSerialDisposable.m rename to ReactiveObjC/RACSerialDisposable.m index a6fca221d8..a91c053c73 100644 --- a/ReactiveCocoa/Objective-C/RACSerialDisposable.m +++ b/ReactiveObjC/RACSerialDisposable.m @@ -1,6 +1,6 @@ // // RACSerialDisposable.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-07-22. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACSignal+Operations.h b/ReactiveObjC/RACSignal+Operations.h similarity index 99% rename from ReactiveCocoa/Objective-C/RACSignal+Operations.h rename to ReactiveObjC/RACSignal+Operations.h index ff04a8d634..1ae92986bf 100644 --- a/ReactiveCocoa/Objective-C/RACSignal+Operations.h +++ b/ReactiveObjC/RACSignal+Operations.h @@ -1,6 +1,6 @@ // // RACSignal+Operations.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-09-06. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACSignal+Operations.m b/ReactiveObjC/RACSignal+Operations.m similarity index 99% rename from ReactiveCocoa/Objective-C/RACSignal+Operations.m rename to ReactiveObjC/RACSignal+Operations.m index e8c33ca6fe..23a9fa6f5f 100644 --- a/ReactiveCocoa/Objective-C/RACSignal+Operations.m +++ b/ReactiveObjC/RACSignal+Operations.m @@ -1,6 +1,6 @@ // // RACSignal+Operations.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-09-06. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACSignal.h b/ReactiveObjC/RACSignal.h similarity index 99% rename from ReactiveCocoa/Objective-C/RACSignal.h rename to ReactiveObjC/RACSignal.h index 48cdcf2745..9461b64f9c 100644 --- a/ReactiveCocoa/Objective-C/RACSignal.h +++ b/ReactiveObjC/RACSignal.h @@ -1,6 +1,6 @@ // // RACSignal.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/1/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACSignal.m b/ReactiveObjC/RACSignal.m similarity index 99% rename from ReactiveCocoa/Objective-C/RACSignal.m rename to ReactiveObjC/RACSignal.m index 9102432426..7df3a276b7 100644 --- a/ReactiveCocoa/Objective-C/RACSignal.m +++ b/ReactiveObjC/RACSignal.m @@ -1,6 +1,6 @@ // // RACSignal.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/15/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACSignalProvider.d b/ReactiveObjC/RACSignalProvider.d similarity index 100% rename from ReactiveCocoa/Objective-C/RACSignalProvider.d rename to ReactiveObjC/RACSignalProvider.d diff --git a/ReactiveCocoa/Objective-C/RACSignalSequence.h b/ReactiveObjC/RACSignalSequence.h similarity index 95% rename from ReactiveCocoa/Objective-C/RACSignalSequence.h rename to ReactiveObjC/RACSignalSequence.h index e3ab77d85b..fd0c487e8a 100644 --- a/ReactiveCocoa/Objective-C/RACSignalSequence.h +++ b/ReactiveObjC/RACSignalSequence.h @@ -1,6 +1,6 @@ // // RACSignalSequence.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-11-09. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACSignalSequence.m b/ReactiveObjC/RACSignalSequence.m similarity index 98% rename from ReactiveCocoa/Objective-C/RACSignalSequence.m rename to ReactiveObjC/RACSignalSequence.m index 52ea1b9104..14cea6b14e 100644 --- a/ReactiveCocoa/Objective-C/RACSignalSequence.m +++ b/ReactiveObjC/RACSignalSequence.m @@ -1,6 +1,6 @@ // // RACSignalSequence.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-11-09. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACStream+Private.h b/ReactiveObjC/RACStream+Private.h similarity index 97% rename from ReactiveCocoa/Objective-C/RACStream+Private.h rename to ReactiveObjC/RACStream+Private.h index f6c04079b6..385baf19cb 100644 --- a/ReactiveCocoa/Objective-C/RACStream+Private.h +++ b/ReactiveObjC/RACStream+Private.h @@ -1,6 +1,6 @@ // // RACStream+Private.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-07-22. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACStream.h b/ReactiveObjC/RACStream.h similarity index 99% rename from ReactiveCocoa/Objective-C/RACStream.h rename to ReactiveObjC/RACStream.h index 50fb18f2bd..7ae3ce9f7a 100644 --- a/ReactiveCocoa/Objective-C/RACStream.h +++ b/ReactiveObjC/RACStream.h @@ -1,6 +1,6 @@ // // RACStream.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-10-31. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACStream.m b/ReactiveObjC/RACStream.m similarity index 99% rename from ReactiveCocoa/Objective-C/RACStream.m rename to ReactiveObjC/RACStream.m index efac5d3729..0c896178ee 100644 --- a/ReactiveCocoa/Objective-C/RACStream.m +++ b/ReactiveObjC/RACStream.m @@ -1,6 +1,6 @@ // // RACStream.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-10-31. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACStringSequence.h b/ReactiveObjC/RACStringSequence.h similarity index 96% rename from ReactiveCocoa/Objective-C/RACStringSequence.h rename to ReactiveObjC/RACStringSequence.h index b37ddea89a..6597fc84ec 100644 --- a/ReactiveCocoa/Objective-C/RACStringSequence.h +++ b/ReactiveObjC/RACStringSequence.h @@ -1,6 +1,6 @@ // // RACStringSequence.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-10-29. // Copyright (c) 2012 GitHub. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACStringSequence.m b/ReactiveObjC/RACStringSequence.m similarity index 98% rename from ReactiveCocoa/Objective-C/RACStringSequence.m rename to ReactiveObjC/RACStringSequence.m index dd10f3c38b..809db5d30e 100644 --- a/ReactiveCocoa/Objective-C/RACStringSequence.m +++ b/ReactiveObjC/RACStringSequence.m @@ -1,6 +1,6 @@ // // RACStringSequence.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-10-29. // Copyright (c) 2012 GitHub. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACSubject.h b/ReactiveObjC/RACSubject.h similarity index 96% rename from ReactiveCocoa/Objective-C/RACSubject.h rename to ReactiveObjC/RACSubject.h index 30c100bfa5..a76cf15014 100644 --- a/ReactiveCocoa/Objective-C/RACSubject.h +++ b/ReactiveObjC/RACSubject.h @@ -1,6 +1,6 @@ // // RACSubject.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/9/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACSubject.m b/ReactiveObjC/RACSubject.m similarity index 98% rename from ReactiveCocoa/Objective-C/RACSubject.m rename to ReactiveObjC/RACSubject.m index 9b76fa7d1d..3833dfcb63 100644 --- a/ReactiveCocoa/Objective-C/RACSubject.m +++ b/ReactiveObjC/RACSubject.m @@ -1,13 +1,13 @@ // // RACSubject.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/9/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. // #import "RACSubject.h" -#import +#import #import "RACCompoundDisposable.h" #import "RACPassthroughSubscriber.h" diff --git a/ReactiveCocoa/Objective-C/RACSubscriber+Private.h b/ReactiveObjC/RACSubscriber+Private.h similarity index 96% rename from ReactiveCocoa/Objective-C/RACSubscriber+Private.h rename to ReactiveObjC/RACSubscriber+Private.h index 0ad81d2fca..f898d1c2ee 100644 --- a/ReactiveCocoa/Objective-C/RACSubscriber+Private.h +++ b/ReactiveObjC/RACSubscriber+Private.h @@ -1,6 +1,6 @@ // // RACSubscriber+Private.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-06-13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACSubscriber.h b/ReactiveObjC/RACSubscriber.h similarity index 98% rename from ReactiveCocoa/Objective-C/RACSubscriber.h rename to ReactiveObjC/RACSubscriber.h index b62ea35548..a4950674b5 100644 --- a/ReactiveCocoa/Objective-C/RACSubscriber.h +++ b/ReactiveObjC/RACSubscriber.h @@ -1,6 +1,6 @@ // // RACSubscriber.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/1/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACSubscriber.m b/ReactiveObjC/RACSubscriber.m similarity index 97% rename from ReactiveCocoa/Objective-C/RACSubscriber.m rename to ReactiveObjC/RACSubscriber.m index 53a7407dc8..46a9a0c24e 100644 --- a/ReactiveCocoa/Objective-C/RACSubscriber.m +++ b/ReactiveObjC/RACSubscriber.m @@ -1,6 +1,6 @@ // // RACSubscriber.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/1/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. @@ -8,7 +8,7 @@ #import "RACSubscriber.h" #import "RACSubscriber+Private.h" -#import +#import #import "RACCompoundDisposable.h" @interface RACSubscriber () diff --git a/ReactiveCocoa/Objective-C/RACSubscriptingAssignmentTrampoline.h b/ReactiveObjC/RACSubscriptingAssignmentTrampoline.h similarity index 97% rename from ReactiveCocoa/Objective-C/RACSubscriptingAssignmentTrampoline.h rename to ReactiveObjC/RACSubscriptingAssignmentTrampoline.h index 699dea897a..7a0f585151 100644 --- a/ReactiveCocoa/Objective-C/RACSubscriptingAssignmentTrampoline.h +++ b/ReactiveObjC/RACSubscriptingAssignmentTrampoline.h @@ -1,13 +1,13 @@ // // RACSubscriptingAssignmentTrampoline.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 9/24/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. // #import -#import +#import @class RACSignal; diff --git a/ReactiveCocoa/Objective-C/RACSubscriptingAssignmentTrampoline.m b/ReactiveObjC/RACSubscriptingAssignmentTrampoline.m similarity index 98% rename from ReactiveCocoa/Objective-C/RACSubscriptingAssignmentTrampoline.m rename to ReactiveObjC/RACSubscriptingAssignmentTrampoline.m index 43fdee008f..f9bc5a0b8c 100644 --- a/ReactiveCocoa/Objective-C/RACSubscriptingAssignmentTrampoline.m +++ b/ReactiveObjC/RACSubscriptingAssignmentTrampoline.m @@ -1,6 +1,6 @@ // // RACSubscriptingAssignmentTrampoline.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 9/24/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACSubscriptionScheduler.h b/ReactiveObjC/RACSubscriptionScheduler.h similarity index 95% rename from ReactiveCocoa/Objective-C/RACSubscriptionScheduler.h rename to ReactiveObjC/RACSubscriptionScheduler.h index 34f5dc5d0a..8398e727bb 100644 --- a/ReactiveCocoa/Objective-C/RACSubscriptionScheduler.h +++ b/ReactiveObjC/RACSubscriptionScheduler.h @@ -1,6 +1,6 @@ // // RACSubscriptionScheduler.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 11/30/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACSubscriptionScheduler.m b/ReactiveObjC/RACSubscriptionScheduler.m similarity index 92% rename from ReactiveCocoa/Objective-C/RACSubscriptionScheduler.m rename to ReactiveObjC/RACSubscriptionScheduler.m index 2aab9c3e19..58ddca8b0c 100644 --- a/ReactiveCocoa/Objective-C/RACSubscriptionScheduler.m +++ b/ReactiveObjC/RACSubscriptionScheduler.m @@ -1,6 +1,6 @@ // // RACSubscriptionScheduler.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 11/30/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. @@ -22,7 +22,7 @@ @implementation RACSubscriptionScheduler #pragma mark Lifecycle - (id)init { - self = [super initWithName:@"com.ReactiveCocoa.RACScheduler.subscriptionScheduler"]; + self = [super initWithName:@"org.reactivecocoa.ReactiveObjC.RACScheduler.subscriptionScheduler"]; if (self == nil) return nil; _backgroundScheduler = [RACScheduler scheduler]; diff --git a/ReactiveCocoa/Objective-C/RACTargetQueueScheduler.h b/ReactiveObjC/RACTargetQueueScheduler.h similarity index 97% rename from ReactiveCocoa/Objective-C/RACTargetQueueScheduler.h rename to ReactiveObjC/RACTargetQueueScheduler.h index 429e59554b..0fe8a6024c 100644 --- a/ReactiveCocoa/Objective-C/RACTargetQueueScheduler.h +++ b/ReactiveObjC/RACTargetQueueScheduler.h @@ -1,6 +1,6 @@ // // RACTargetQueueScheduler.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 6/6/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACTargetQueueScheduler.m b/ReactiveObjC/RACTargetQueueScheduler.m similarity index 80% rename from ReactiveCocoa/Objective-C/RACTargetQueueScheduler.m rename to ReactiveObjC/RACTargetQueueScheduler.m index f90dfd6704..fde6b1d26b 100644 --- a/ReactiveCocoa/Objective-C/RACTargetQueueScheduler.m +++ b/ReactiveObjC/RACTargetQueueScheduler.m @@ -1,6 +1,6 @@ // // RACTargetQueueScheduler.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 6/6/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. @@ -17,7 +17,7 @@ - (id)initWithName:(NSString *)name targetQueue:(dispatch_queue_t)targetQueue { NSCParameterAssert(targetQueue != NULL); if (name == nil) { - name = [NSString stringWithFormat:@"com.ReactiveCocoa.RACTargetQueueScheduler(%s)", dispatch_queue_get_label(targetQueue)]; + name = [NSString stringWithFormat:@"org.reactivecocoa.ReactiveObjC.RACTargetQueueScheduler(%s)", dispatch_queue_get_label(targetQueue)]; } dispatch_queue_t queue = dispatch_queue_create(name.UTF8String, DISPATCH_QUEUE_SERIAL); diff --git a/ReactiveCocoa/Objective-C/RACTestScheduler.h b/ReactiveObjC/RACTestScheduler.h similarity index 98% rename from ReactiveCocoa/Objective-C/RACTestScheduler.h rename to ReactiveObjC/RACTestScheduler.h index a790f5bb84..2975f126df 100644 --- a/ReactiveCocoa/Objective-C/RACTestScheduler.h +++ b/ReactiveObjC/RACTestScheduler.h @@ -1,6 +1,6 @@ // // RACTestScheduler.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-07-06. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACTestScheduler.m b/ReactiveObjC/RACTestScheduler.m similarity index 97% rename from ReactiveCocoa/Objective-C/RACTestScheduler.m rename to ReactiveObjC/RACTestScheduler.m index 06c0072036..cc248e8a90 100644 --- a/ReactiveCocoa/Objective-C/RACTestScheduler.m +++ b/ReactiveObjC/RACTestScheduler.m @@ -1,13 +1,13 @@ // // RACTestScheduler.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-07-06. // Copyright (c) 2013 GitHub, Inc. All rights reserved. // #import "RACTestScheduler.h" -#import +#import #import "RACCompoundDisposable.h" #import "RACDisposable.h" #import "RACScheduler+Private.h" @@ -73,7 +73,7 @@ @implementation RACTestScheduler #pragma mark Lifecycle - (instancetype)init { - self = [super initWithName:@"org.reactivecocoa.ReactiveCocoa.RACTestScheduler"]; + self = [super initWithName:@"org.reactivecocoa.ReactiveObjC.RACTestScheduler"]; if (self == nil) return nil; CFBinaryHeapCallBacks callbacks = (CFBinaryHeapCallBacks){ diff --git a/ReactiveCocoa/Objective-C/RACTuple.h b/ReactiveObjC/RACTuple.h similarity index 99% rename from ReactiveCocoa/Objective-C/RACTuple.h rename to ReactiveObjC/RACTuple.h index 647b42c2e5..50383325bc 100644 --- a/ReactiveCocoa/Objective-C/RACTuple.h +++ b/ReactiveObjC/RACTuple.h @@ -1,6 +1,6 @@ // // RACTuple.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 4/12/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACTuple.m b/ReactiveObjC/RACTuple.m similarity index 98% rename from ReactiveCocoa/Objective-C/RACTuple.m rename to ReactiveObjC/RACTuple.m index ca88d12662..2848a2223b 100644 --- a/ReactiveCocoa/Objective-C/RACTuple.m +++ b/ReactiveObjC/RACTuple.m @@ -1,13 +1,13 @@ // // RACTuple.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 4/12/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. // #import "RACTuple.h" -#import +#import #import "RACTupleSequence.h" @implementation RACTupleNil diff --git a/ReactiveCocoa/Objective-C/RACTupleSequence.h b/ReactiveObjC/RACTupleSequence.h similarity index 96% rename from ReactiveCocoa/Objective-C/RACTupleSequence.h rename to ReactiveObjC/RACTupleSequence.h index f6cd021fdc..c933684672 100644 --- a/ReactiveCocoa/Objective-C/RACTupleSequence.h +++ b/ReactiveObjC/RACTupleSequence.h @@ -1,6 +1,6 @@ // // RACTupleSequence.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-05-01. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACTupleSequence.m b/ReactiveObjC/RACTupleSequence.m similarity index 99% rename from ReactiveCocoa/Objective-C/RACTupleSequence.m rename to ReactiveObjC/RACTupleSequence.m index 65c139fcbb..e83b749617 100644 --- a/ReactiveCocoa/Objective-C/RACTupleSequence.m +++ b/ReactiveObjC/RACTupleSequence.m @@ -1,6 +1,6 @@ // // RACTupleSequence.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-05-01. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACUnarySequence.h b/ReactiveObjC/RACUnarySequence.h similarity index 93% rename from ReactiveCocoa/Objective-C/RACUnarySequence.h rename to ReactiveObjC/RACUnarySequence.h index 86a514ba6e..1606425e1c 100644 --- a/ReactiveCocoa/Objective-C/RACUnarySequence.h +++ b/ReactiveObjC/RACUnarySequence.h @@ -1,6 +1,6 @@ // // RACUnarySequence.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-05-01. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACUnarySequence.m b/ReactiveObjC/RACUnarySequence.m similarity index 96% rename from ReactiveCocoa/Objective-C/RACUnarySequence.m rename to ReactiveObjC/RACUnarySequence.m index a06f722757..ff5d333bba 100644 --- a/ReactiveCocoa/Objective-C/RACUnarySequence.m +++ b/ReactiveObjC/RACUnarySequence.m @@ -1,13 +1,13 @@ // // RACUnarySequence.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-05-01. // Copyright (c) 2013 GitHub, Inc. All rights reserved. // #import "RACUnarySequence.h" -#import +#import #import "NSObject+RACDescription.h" @interface RACUnarySequence () diff --git a/ReactiveCocoa/Objective-C/RACUnit.h b/ReactiveObjC/RACUnit.h similarity index 95% rename from ReactiveCocoa/Objective-C/RACUnit.h rename to ReactiveObjC/RACUnit.h index a04e2b1abe..aae3defde2 100644 --- a/ReactiveCocoa/Objective-C/RACUnit.h +++ b/ReactiveObjC/RACUnit.h @@ -1,6 +1,6 @@ // // RACUnit.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/27/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACUnit.m b/ReactiveObjC/RACUnit.m similarity index 95% rename from ReactiveCocoa/Objective-C/RACUnit.m rename to ReactiveObjC/RACUnit.m index 9e31246889..691e5da427 100644 --- a/ReactiveCocoa/Objective-C/RACUnit.m +++ b/ReactiveObjC/RACUnit.m @@ -1,6 +1,6 @@ // // RACUnit.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/27/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACValueTransformer.h b/ReactiveObjC/RACValueTransformer.h similarity index 94% rename from ReactiveCocoa/Objective-C/RACValueTransformer.h rename to ReactiveObjC/RACValueTransformer.h index 740b8611b2..c827e6cd80 100644 --- a/ReactiveCocoa/Objective-C/RACValueTransformer.h +++ b/ReactiveObjC/RACValueTransformer.h @@ -1,6 +1,6 @@ // // RACValueTransformer.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/6/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/RACValueTransformer.m b/ReactiveObjC/RACValueTransformer.m similarity index 97% rename from ReactiveCocoa/Objective-C/RACValueTransformer.m rename to ReactiveObjC/RACValueTransformer.m index ccec07d457..6f44e0f2b9 100644 --- a/ReactiveCocoa/Objective-C/RACValueTransformer.m +++ b/ReactiveObjC/RACValueTransformer.m @@ -1,6 +1,6 @@ // // RACValueTransformer.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/6/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/ReactiveCocoa-Bridging-Header.h b/ReactiveObjC/ReactiveCocoa-Bridging-Header.h similarity index 100% rename from ReactiveCocoa/Objective-C/ReactiveCocoa-Bridging-Header.h rename to ReactiveObjC/ReactiveCocoa-Bridging-Header.h diff --git a/ReactiveObjC/ReactiveObjC.h b/ReactiveObjC/ReactiveObjC.h new file mode 100644 index 0000000000..6defe8aa87 --- /dev/null +++ b/ReactiveObjC/ReactiveObjC.h @@ -0,0 +1,95 @@ +// +// ReactiveObjC.h +// ReactiveObjC +// +// Created by Josh Abernathy on 3/5/12. +// Copyright (c) 2012 GitHub, Inc. All rights reserved. +// + +#import + +//! Project version number for ReactiveObjC. +FOUNDATION_EXPORT double ReactiveObjCVersionNumber; + +//! Project version string for ReactiveObjC. +FOUNDATION_EXPORT const unsigned char ReactiveObjCVersionString[]; + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#if TARGET_OS_WATCH +#elif TARGET_OS_IOS || TARGET_OS_TV + #import + #import + #import + #import + #import + #import + #import + #import + #import + #import + + #if TARGET_OS_IOS + #import + #import + #import + #import + #import + #import + #import + #import + #import + #import + #endif +#elif TARGET_OS_MAC + #import + #import + #import + #import + #import +#endif diff --git a/ReactiveCocoa/Objective-C/UIActionSheet+RACSignalSupport.h b/ReactiveObjC/UIActionSheet+RACSignalSupport.h similarity index 98% rename from ReactiveCocoa/Objective-C/UIActionSheet+RACSignalSupport.h rename to ReactiveObjC/UIActionSheet+RACSignalSupport.h index 3d667e956d..8975f42ae9 100644 --- a/ReactiveCocoa/Objective-C/UIActionSheet+RACSignalSupport.h +++ b/ReactiveObjC/UIActionSheet+RACSignalSupport.h @@ -1,6 +1,6 @@ // // UIActionSheet+RACSignalSupport.h -// ReactiveCocoa +// ReactiveObjC // // Created by Dave Lee on 2013-06-22. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/UIActionSheet+RACSignalSupport.m b/ReactiveObjC/UIActionSheet+RACSignalSupport.m similarity index 98% rename from ReactiveCocoa/Objective-C/UIActionSheet+RACSignalSupport.m rename to ReactiveObjC/UIActionSheet+RACSignalSupport.m index 251a5189c4..6a1a4102d7 100644 --- a/ReactiveCocoa/Objective-C/UIActionSheet+RACSignalSupport.m +++ b/ReactiveObjC/UIActionSheet+RACSignalSupport.m @@ -1,6 +1,6 @@ // // UIActionSheet+RACSignalSupport.m -// ReactiveCocoa +// ReactiveObjC // // Created by Dave Lee on 2013-06-22. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/UIAlertView+RACSignalSupport.h b/ReactiveObjC/UIAlertView+RACSignalSupport.h similarity index 98% rename from ReactiveCocoa/Objective-C/UIAlertView+RACSignalSupport.h rename to ReactiveObjC/UIAlertView+RACSignalSupport.h index 9a1ecb91cd..848db46a89 100644 --- a/ReactiveCocoa/Objective-C/UIAlertView+RACSignalSupport.h +++ b/ReactiveObjC/UIAlertView+RACSignalSupport.h @@ -1,6 +1,6 @@ // // UIAlertView+RACSignalSupport.h -// ReactiveCocoa +// ReactiveObjC // // Created by Henrik Hodne on 6/16/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/UIAlertView+RACSignalSupport.m b/ReactiveObjC/UIAlertView+RACSignalSupport.m similarity index 98% rename from ReactiveCocoa/Objective-C/UIAlertView+RACSignalSupport.m rename to ReactiveObjC/UIAlertView+RACSignalSupport.m index a533dd6303..0afc94a8d6 100644 --- a/ReactiveCocoa/Objective-C/UIAlertView+RACSignalSupport.m +++ b/ReactiveObjC/UIAlertView+RACSignalSupport.m @@ -1,6 +1,6 @@ // // UIAlertView+RACSignalSupport.m -// ReactiveCocoa +// ReactiveObjC // // Created by Henrik Hodne on 6/16/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/UIBarButtonItem+RACCommandSupport.h b/ReactiveObjC/UIBarButtonItem+RACCommandSupport.h similarity index 97% rename from ReactiveCocoa/Objective-C/UIBarButtonItem+RACCommandSupport.h rename to ReactiveObjC/UIBarButtonItem+RACCommandSupport.h index 759b29816b..0061934245 100644 --- a/ReactiveCocoa/Objective-C/UIBarButtonItem+RACCommandSupport.h +++ b/ReactiveObjC/UIBarButtonItem+RACCommandSupport.h @@ -1,6 +1,6 @@ // // UIBarButtonItem+RACCommandSupport.h -// ReactiveCocoa +// ReactiveObjC // // Created by Kyle LeNeau on 3/27/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/UIBarButtonItem+RACCommandSupport.m b/ReactiveObjC/UIBarButtonItem+RACCommandSupport.m similarity index 96% rename from ReactiveCocoa/Objective-C/UIBarButtonItem+RACCommandSupport.m rename to ReactiveObjC/UIBarButtonItem+RACCommandSupport.m index 3ce99569c4..225e8b751a 100644 --- a/ReactiveCocoa/Objective-C/UIBarButtonItem+RACCommandSupport.m +++ b/ReactiveObjC/UIBarButtonItem+RACCommandSupport.m @@ -1,13 +1,13 @@ // // UIBarButtonItem+RACCommandSupport.m -// ReactiveCocoa +// ReactiveObjC // // Created by Kyle LeNeau on 3/27/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. // #import "UIBarButtonItem+RACCommandSupport.h" -#import +#import #import "RACCommand.h" #import "RACDisposable.h" #import "RACSignal+Operations.h" diff --git a/ReactiveCocoa/Objective-C/UIButton+RACCommandSupport.h b/ReactiveObjC/UIButton+RACCommandSupport.h similarity index 96% rename from ReactiveCocoa/Objective-C/UIButton+RACCommandSupport.h rename to ReactiveObjC/UIButton+RACCommandSupport.h index 8ec89ce825..69af7c7577 100644 --- a/ReactiveCocoa/Objective-C/UIButton+RACCommandSupport.h +++ b/ReactiveObjC/UIButton+RACCommandSupport.h @@ -1,6 +1,6 @@ // // UIButton+RACCommandSupport.h -// ReactiveCocoa +// ReactiveObjC // // Created by Ash Furrow on 2013-06-06. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/UIButton+RACCommandSupport.m b/ReactiveObjC/UIButton+RACCommandSupport.m similarity index 96% rename from ReactiveCocoa/Objective-C/UIButton+RACCommandSupport.m rename to ReactiveObjC/UIButton+RACCommandSupport.m index 05de9e0e85..6b11dad276 100644 --- a/ReactiveCocoa/Objective-C/UIButton+RACCommandSupport.m +++ b/ReactiveObjC/UIButton+RACCommandSupport.m @@ -1,13 +1,13 @@ // // UIButton+RACCommandSupport.m -// ReactiveCocoa +// ReactiveObjC // // Created by Ash Furrow on 2013-06-06. // Copyright (c) 2013 GitHub, Inc. All rights reserved. // #import "UIButton+RACCommandSupport.h" -#import +#import #import "RACCommand.h" #import "RACDisposable.h" #import "RACSignal+Operations.h" diff --git a/ReactiveCocoa/Objective-C/UICollectionReusableView+RACSignalSupport.h b/ReactiveObjC/UICollectionReusableView+RACSignalSupport.h similarity index 97% rename from ReactiveCocoa/Objective-C/UICollectionReusableView+RACSignalSupport.h rename to ReactiveObjC/UICollectionReusableView+RACSignalSupport.h index 96b3dfe182..94f4890504 100644 --- a/ReactiveCocoa/Objective-C/UICollectionReusableView+RACSignalSupport.h +++ b/ReactiveObjC/UICollectionReusableView+RACSignalSupport.h @@ -1,6 +1,6 @@ // // UICollectionReusableView+RACSignalSupport.h -// ReactiveCocoa +// ReactiveObjC // // Created by Kent Wong on 2013-10-04. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/UICollectionReusableView+RACSignalSupport.m b/ReactiveObjC/UICollectionReusableView+RACSignalSupport.m similarity index 97% rename from ReactiveCocoa/Objective-C/UICollectionReusableView+RACSignalSupport.m rename to ReactiveObjC/UICollectionReusableView+RACSignalSupport.m index 84cac49188..9215c162ba 100644 --- a/ReactiveCocoa/Objective-C/UICollectionReusableView+RACSignalSupport.m +++ b/ReactiveObjC/UICollectionReusableView+RACSignalSupport.m @@ -1,6 +1,6 @@ // // UICollectionReusableView+RACSignalSupport.m -// ReactiveCocoa +// ReactiveObjC // // Created by Kent Wong on 2013-10-04. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/UIControl+RACSignalSupport.h b/ReactiveObjC/UIControl+RACSignalSupport.h similarity index 96% rename from ReactiveCocoa/Objective-C/UIControl+RACSignalSupport.h rename to ReactiveObjC/UIControl+RACSignalSupport.h index 2de86cf8d9..524ba049b4 100644 --- a/ReactiveCocoa/Objective-C/UIControl+RACSignalSupport.h +++ b/ReactiveObjC/UIControl+RACSignalSupport.h @@ -1,6 +1,6 @@ // // UIControl+RACSignalSupport.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 4/17/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/UIControl+RACSignalSupport.m b/ReactiveObjC/UIControl+RACSignalSupport.m similarity index 95% rename from ReactiveCocoa/Objective-C/UIControl+RACSignalSupport.m rename to ReactiveObjC/UIControl+RACSignalSupport.m index 08ec067cc4..df3108eb71 100644 --- a/ReactiveCocoa/Objective-C/UIControl+RACSignalSupport.m +++ b/ReactiveObjC/UIControl+RACSignalSupport.m @@ -1,13 +1,13 @@ // // UIControl+RACSignalSupport.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 4/17/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. // #import "UIControl+RACSignalSupport.h" -#import +#import #import "RACCompoundDisposable.h" #import "RACDisposable.h" #import "RACSignal.h" diff --git a/ReactiveCocoa/Objective-C/UIControl+RACSignalSupportPrivate.h b/ReactiveObjC/UIControl+RACSignalSupportPrivate.h similarity index 98% rename from ReactiveCocoa/Objective-C/UIControl+RACSignalSupportPrivate.h rename to ReactiveObjC/UIControl+RACSignalSupportPrivate.h index 7f778a47bf..3f0ac348c9 100644 --- a/ReactiveCocoa/Objective-C/UIControl+RACSignalSupportPrivate.h +++ b/ReactiveObjC/UIControl+RACSignalSupportPrivate.h @@ -1,6 +1,6 @@ // // UIControl+RACSignalSupportPrivate.h -// ReactiveCocoa +// ReactiveObjC // // Created by Uri Baghin on 06/08/2013. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/UIControl+RACSignalSupportPrivate.m b/ReactiveObjC/UIControl+RACSignalSupportPrivate.m similarity index 98% rename from ReactiveCocoa/Objective-C/UIControl+RACSignalSupportPrivate.m rename to ReactiveObjC/UIControl+RACSignalSupportPrivate.m index 445398cf5c..867f57e74d 100644 --- a/ReactiveCocoa/Objective-C/UIControl+RACSignalSupportPrivate.m +++ b/ReactiveObjC/UIControl+RACSignalSupportPrivate.m @@ -1,6 +1,6 @@ // // UIControl+RACSignalSupportPrivate.m -// ReactiveCocoa +// ReactiveObjC // // Created by Uri Baghin on 06/08/2013. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/UIDatePicker+RACSignalSupport.h b/ReactiveObjC/UIDatePicker+RACSignalSupport.h similarity index 97% rename from ReactiveCocoa/Objective-C/UIDatePicker+RACSignalSupport.h rename to ReactiveObjC/UIDatePicker+RACSignalSupport.h index e620dfc3cc..cc03460b96 100644 --- a/ReactiveCocoa/Objective-C/UIDatePicker+RACSignalSupport.h +++ b/ReactiveObjC/UIDatePicker+RACSignalSupport.h @@ -1,6 +1,6 @@ // // UIDatePicker+RACSignalSupport.h -// ReactiveCocoa +// ReactiveObjC // // Created by Uri Baghin on 20/07/2013. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/UIDatePicker+RACSignalSupport.m b/ReactiveObjC/UIDatePicker+RACSignalSupport.m similarity index 88% rename from ReactiveCocoa/Objective-C/UIDatePicker+RACSignalSupport.m rename to ReactiveObjC/UIDatePicker+RACSignalSupport.m index 50db7985c9..5c815c2f24 100644 --- a/ReactiveCocoa/Objective-C/UIDatePicker+RACSignalSupport.m +++ b/ReactiveObjC/UIDatePicker+RACSignalSupport.m @@ -1,13 +1,13 @@ // // UIDatePicker+RACSignalSupport.m -// ReactiveCocoa +// ReactiveObjC // // Created by Uri Baghin on 20/07/2013. // Copyright (c) 2013 GitHub, Inc. All rights reserved. // #import "UIDatePicker+RACSignalSupport.h" -#import +#import #import "UIControl+RACSignalSupportPrivate.h" @implementation UIDatePicker (RACSignalSupport) diff --git a/ReactiveCocoa/Objective-C/UIGestureRecognizer+RACSignalSupport.h b/ReactiveObjC/UIGestureRecognizer+RACSignalSupport.h similarity index 95% rename from ReactiveCocoa/Objective-C/UIGestureRecognizer+RACSignalSupport.h rename to ReactiveObjC/UIGestureRecognizer+RACSignalSupport.h index f93c9d9f8e..ecafdb0b9e 100644 --- a/ReactiveCocoa/Objective-C/UIGestureRecognizer+RACSignalSupport.h +++ b/ReactiveObjC/UIGestureRecognizer+RACSignalSupport.h @@ -1,6 +1,6 @@ // // UIGestureRecognizer+RACSignalSupport.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Vera on 5/5/13. // Copyright (c) 2013 GitHub. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/UIGestureRecognizer+RACSignalSupport.m b/ReactiveObjC/UIGestureRecognizer+RACSignalSupport.m similarity index 94% rename from ReactiveCocoa/Objective-C/UIGestureRecognizer+RACSignalSupport.m rename to ReactiveObjC/UIGestureRecognizer+RACSignalSupport.m index 5b1298f8bc..4f816eb8e6 100644 --- a/ReactiveCocoa/Objective-C/UIGestureRecognizer+RACSignalSupport.m +++ b/ReactiveObjC/UIGestureRecognizer+RACSignalSupport.m @@ -1,13 +1,13 @@ // // UIGestureRecognizer+RACSignalSupport.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Vera on 5/5/13. // Copyright (c) 2013 GitHub. All rights reserved. // #import "UIGestureRecognizer+RACSignalSupport.h" -#import +#import #import "NSObject+RACDeallocating.h" #import "NSObject+RACDescription.h" #import "RACCompoundDisposable.h" diff --git a/ReactiveCocoa/Objective-C/UIImagePickerController+RACSignalSupport.h b/ReactiveObjC/UIImagePickerController+RACSignalSupport.h similarity index 98% rename from ReactiveCocoa/Objective-C/UIImagePickerController+RACSignalSupport.h rename to ReactiveObjC/UIImagePickerController+RACSignalSupport.h index 17440774ce..84d90476f3 100644 --- a/ReactiveCocoa/Objective-C/UIImagePickerController+RACSignalSupport.h +++ b/ReactiveObjC/UIImagePickerController+RACSignalSupport.h @@ -1,6 +1,6 @@ // // UIImagePickerController+RACSignalSupport.h -// ReactiveCocoa +// ReactiveObjC // // Created by Timur Kuchkarov on 28.03.14. // Copyright (c) 2014 GitHub. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/UIImagePickerController+RACSignalSupport.m b/ReactiveObjC/UIImagePickerController+RACSignalSupport.m similarity index 98% rename from ReactiveCocoa/Objective-C/UIImagePickerController+RACSignalSupport.m rename to ReactiveObjC/UIImagePickerController+RACSignalSupport.m index d567be53a6..a5df4eec76 100644 --- a/ReactiveCocoa/Objective-C/UIImagePickerController+RACSignalSupport.m +++ b/ReactiveObjC/UIImagePickerController+RACSignalSupport.m @@ -1,6 +1,6 @@ // // UIImagePickerController+RACSignalSupport.m -// ReactiveCocoa +// ReactiveObjC // // Created by Timur Kuchkarov on 28.03.14. // Copyright (c) 2014 GitHub. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/UIRefreshControl+RACCommandSupport.h b/ReactiveObjC/UIRefreshControl+RACCommandSupport.h similarity index 97% rename from ReactiveCocoa/Objective-C/UIRefreshControl+RACCommandSupport.h rename to ReactiveObjC/UIRefreshControl+RACCommandSupport.h index 460b4cdeb5..09b3413449 100644 --- a/ReactiveCocoa/Objective-C/UIRefreshControl+RACCommandSupport.h +++ b/ReactiveObjC/UIRefreshControl+RACCommandSupport.h @@ -1,6 +1,6 @@ // // UIRefreshControl+RACCommandSupport.h -// ReactiveCocoa +// ReactiveObjC // // Created by Dave Lee on 2013-10-17. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/UIRefreshControl+RACCommandSupport.m b/ReactiveObjC/UIRefreshControl+RACCommandSupport.m similarity index 96% rename from ReactiveCocoa/Objective-C/UIRefreshControl+RACCommandSupport.m rename to ReactiveObjC/UIRefreshControl+RACCommandSupport.m index ba5a0dbb9a..3301ca194b 100644 --- a/ReactiveCocoa/Objective-C/UIRefreshControl+RACCommandSupport.m +++ b/ReactiveObjC/UIRefreshControl+RACCommandSupport.m @@ -1,13 +1,13 @@ // // UIRefreshControl+RACCommandSupport.m -// ReactiveCocoa +// ReactiveObjC // // Created by Dave Lee on 2013-10-17. // Copyright (c) 2013 GitHub, Inc. All rights reserved. // #import "UIRefreshControl+RACCommandSupport.h" -#import +#import #import "RACCommand.h" #import "RACCompoundDisposable.h" #import "RACDisposable.h" diff --git a/ReactiveCocoa/Objective-C/UISegmentedControl+RACSignalSupport.h b/ReactiveObjC/UISegmentedControl+RACSignalSupport.h similarity index 97% rename from ReactiveCocoa/Objective-C/UISegmentedControl+RACSignalSupport.h rename to ReactiveObjC/UISegmentedControl+RACSignalSupport.h index 2d3c3e7af4..c4c70627ae 100644 --- a/ReactiveCocoa/Objective-C/UISegmentedControl+RACSignalSupport.h +++ b/ReactiveObjC/UISegmentedControl+RACSignalSupport.h @@ -1,6 +1,6 @@ // // UISegmentedControl+RACSignalSupport.h -// ReactiveCocoa +// ReactiveObjC // // Created by Uri Baghin on 20/07/2013. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/UISegmentedControl+RACSignalSupport.m b/ReactiveObjC/UISegmentedControl+RACSignalSupport.m similarity index 89% rename from ReactiveCocoa/Objective-C/UISegmentedControl+RACSignalSupport.m rename to ReactiveObjC/UISegmentedControl+RACSignalSupport.m index baacbab0df..782cabca08 100644 --- a/ReactiveCocoa/Objective-C/UISegmentedControl+RACSignalSupport.m +++ b/ReactiveObjC/UISegmentedControl+RACSignalSupport.m @@ -1,13 +1,13 @@ // // UISegmentedControl+RACSignalSupport.m -// ReactiveCocoa +// ReactiveObjC // // Created by Uri Baghin on 20/07/2013. // Copyright (c) 2013 GitHub, Inc. All rights reserved. // #import "UISegmentedControl+RACSignalSupport.h" -#import +#import #import "UIControl+RACSignalSupportPrivate.h" @implementation UISegmentedControl (RACSignalSupport) diff --git a/ReactiveCocoa/Objective-C/UISlider+RACSignalSupport.h b/ReactiveObjC/UISlider+RACSignalSupport.h similarity index 97% rename from ReactiveCocoa/Objective-C/UISlider+RACSignalSupport.h rename to ReactiveObjC/UISlider+RACSignalSupport.h index 75626ad976..841bbad9d2 100644 --- a/ReactiveCocoa/Objective-C/UISlider+RACSignalSupport.h +++ b/ReactiveObjC/UISlider+RACSignalSupport.h @@ -1,6 +1,6 @@ // // UISlider+RACSignalSupport.h -// ReactiveCocoa +// ReactiveObjC // // Created by Uri Baghin on 20/07/2013. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/UISlider+RACSignalSupport.m b/ReactiveObjC/UISlider+RACSignalSupport.m similarity index 88% rename from ReactiveCocoa/Objective-C/UISlider+RACSignalSupport.m rename to ReactiveObjC/UISlider+RACSignalSupport.m index c4b110f506..3583d843c5 100644 --- a/ReactiveCocoa/Objective-C/UISlider+RACSignalSupport.m +++ b/ReactiveObjC/UISlider+RACSignalSupport.m @@ -1,13 +1,13 @@ // // UISlider+RACSignalSupport.m -// ReactiveCocoa +// ReactiveObjC // // Created by Uri Baghin on 20/07/2013. // Copyright (c) 2013 GitHub, Inc. All rights reserved. // #import "UISlider+RACSignalSupport.h" -#import +#import #import "UIControl+RACSignalSupportPrivate.h" @implementation UISlider (RACSignalSupport) diff --git a/ReactiveCocoa/Objective-C/UIStepper+RACSignalSupport.h b/ReactiveObjC/UIStepper+RACSignalSupport.h similarity index 97% rename from ReactiveCocoa/Objective-C/UIStepper+RACSignalSupport.h rename to ReactiveObjC/UIStepper+RACSignalSupport.h index da6d97b51c..b0b49416ec 100644 --- a/ReactiveCocoa/Objective-C/UIStepper+RACSignalSupport.h +++ b/ReactiveObjC/UIStepper+RACSignalSupport.h @@ -1,6 +1,6 @@ // // UIStepper+RACSignalSupport.h -// ReactiveCocoa +// ReactiveObjC // // Created by Uri Baghin on 20/07/2013. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/UIStepper+RACSignalSupport.m b/ReactiveObjC/UIStepper+RACSignalSupport.m similarity index 88% rename from ReactiveCocoa/Objective-C/UIStepper+RACSignalSupport.m rename to ReactiveObjC/UIStepper+RACSignalSupport.m index 1d932817e7..5eee8f8534 100644 --- a/ReactiveCocoa/Objective-C/UIStepper+RACSignalSupport.m +++ b/ReactiveObjC/UIStepper+RACSignalSupport.m @@ -1,13 +1,13 @@ // // UIStepper+RACSignalSupport.m -// ReactiveCocoa +// ReactiveObjC // // Created by Uri Baghin on 20/07/2013. // Copyright (c) 2013 GitHub, Inc. All rights reserved. // #import "UIStepper+RACSignalSupport.h" -#import +#import #import "UIControl+RACSignalSupportPrivate.h" @implementation UIStepper (RACSignalSupport) diff --git a/ReactiveCocoa/Objective-C/UISwitch+RACSignalSupport.h b/ReactiveObjC/UISwitch+RACSignalSupport.h similarity index 96% rename from ReactiveCocoa/Objective-C/UISwitch+RACSignalSupport.h rename to ReactiveObjC/UISwitch+RACSignalSupport.h index 1313a2c4f8..53b7b6765f 100644 --- a/ReactiveCocoa/Objective-C/UISwitch+RACSignalSupport.h +++ b/ReactiveObjC/UISwitch+RACSignalSupport.h @@ -1,6 +1,6 @@ // // UISwitch+RACSignalSupport.h -// ReactiveCocoa +// ReactiveObjC // // Created by Uri Baghin on 20/07/2013. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/UISwitch+RACSignalSupport.m b/ReactiveObjC/UISwitch+RACSignalSupport.m similarity index 87% rename from ReactiveCocoa/Objective-C/UISwitch+RACSignalSupport.m rename to ReactiveObjC/UISwitch+RACSignalSupport.m index 545d78efc2..a5f847d805 100644 --- a/ReactiveCocoa/Objective-C/UISwitch+RACSignalSupport.m +++ b/ReactiveObjC/UISwitch+RACSignalSupport.m @@ -1,13 +1,13 @@ // // UISwitch+RACSignalSupport.m -// ReactiveCocoa +// ReactiveObjC // // Created by Uri Baghin on 20/07/2013. // Copyright (c) 2013 GitHub, Inc. All rights reserved. // #import "UISwitch+RACSignalSupport.h" -#import +#import #import "UIControl+RACSignalSupportPrivate.h" @implementation UISwitch (RACSignalSupport) diff --git a/ReactiveCocoa/Objective-C/UITableViewCell+RACSignalSupport.h b/ReactiveObjC/UITableViewCell+RACSignalSupport.h similarity index 97% rename from ReactiveCocoa/Objective-C/UITableViewCell+RACSignalSupport.h rename to ReactiveObjC/UITableViewCell+RACSignalSupport.h index c29d47cdda..144cbbaab0 100644 --- a/ReactiveCocoa/Objective-C/UITableViewCell+RACSignalSupport.h +++ b/ReactiveObjC/UITableViewCell+RACSignalSupport.h @@ -1,6 +1,6 @@ // // UITableViewCell+RACSignalSupport.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-07-22. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/UITableViewCell+RACSignalSupport.m b/ReactiveObjC/UITableViewCell+RACSignalSupport.m similarity index 97% rename from ReactiveCocoa/Objective-C/UITableViewCell+RACSignalSupport.m rename to ReactiveObjC/UITableViewCell+RACSignalSupport.m index 8ca2e9bd96..5cf9dfa443 100644 --- a/ReactiveCocoa/Objective-C/UITableViewCell+RACSignalSupport.m +++ b/ReactiveObjC/UITableViewCell+RACSignalSupport.m @@ -1,6 +1,6 @@ // // UITableViewCell+RACSignalSupport.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-07-22. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/UITableViewHeaderFooterView+RACSignalSupport.h b/ReactiveObjC/UITableViewHeaderFooterView+RACSignalSupport.h similarity index 97% rename from ReactiveCocoa/Objective-C/UITableViewHeaderFooterView+RACSignalSupport.h rename to ReactiveObjC/UITableViewHeaderFooterView+RACSignalSupport.h index 6c5d6b8651..db15b26f0e 100644 --- a/ReactiveCocoa/Objective-C/UITableViewHeaderFooterView+RACSignalSupport.h +++ b/ReactiveObjC/UITableViewHeaderFooterView+RACSignalSupport.h @@ -1,6 +1,6 @@ // // UITableViewHeaderFooterView+RACSignalSupport.h -// ReactiveCocoa +// ReactiveObjC // // Created by Syo Ikeda on 12/30/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/UITableViewHeaderFooterView+RACSignalSupport.m b/ReactiveObjC/UITableViewHeaderFooterView+RACSignalSupport.m similarity index 97% rename from ReactiveCocoa/Objective-C/UITableViewHeaderFooterView+RACSignalSupport.m rename to ReactiveObjC/UITableViewHeaderFooterView+RACSignalSupport.m index 44728b0468..a0ffb436a4 100644 --- a/ReactiveCocoa/Objective-C/UITableViewHeaderFooterView+RACSignalSupport.m +++ b/ReactiveObjC/UITableViewHeaderFooterView+RACSignalSupport.m @@ -1,6 +1,6 @@ // // UITableViewHeaderFooterView+RACSignalSupport.m -// ReactiveCocoa +// ReactiveObjC // // Created by Syo Ikeda on 12/30/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/UITextField+RACSignalSupport.h b/ReactiveObjC/UITextField+RACSignalSupport.h similarity index 97% rename from ReactiveCocoa/Objective-C/UITextField+RACSignalSupport.h rename to ReactiveObjC/UITextField+RACSignalSupport.h index 23aca7d35f..dd7b50ea18 100644 --- a/ReactiveCocoa/Objective-C/UITextField+RACSignalSupport.h +++ b/ReactiveObjC/UITextField+RACSignalSupport.h @@ -1,6 +1,6 @@ // // UITextField+RACSignalSupport.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 4/17/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/UITextField+RACSignalSupport.m b/ReactiveObjC/UITextField+RACSignalSupport.m similarity index 90% rename from ReactiveCocoa/Objective-C/UITextField+RACSignalSupport.m rename to ReactiveObjC/UITextField+RACSignalSupport.m index 3c9818f93d..b02444858f 100644 --- a/ReactiveCocoa/Objective-C/UITextField+RACSignalSupport.m +++ b/ReactiveObjC/UITextField+RACSignalSupport.m @@ -1,14 +1,14 @@ // // UITextField+RACSignalSupport.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 4/17/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. // #import "UITextField+RACSignalSupport.h" -#import -#import +#import +#import #import "NSObject+RACDeallocating.h" #import "NSObject+RACDescription.h" #import "RACSignal+Operations.h" diff --git a/ReactiveCocoa/Objective-C/UITextView+RACSignalSupport.h b/ReactiveObjC/UITextView+RACSignalSupport.h similarity index 98% rename from ReactiveCocoa/Objective-C/UITextView+RACSignalSupport.h rename to ReactiveObjC/UITextView+RACSignalSupport.h index 174b1bac6e..c2f7e8230b 100644 --- a/ReactiveCocoa/Objective-C/UITextView+RACSignalSupport.h +++ b/ReactiveObjC/UITextView+RACSignalSupport.h @@ -1,6 +1,6 @@ // // UITextView+RACSignalSupport.h -// ReactiveCocoa +// ReactiveObjC // // Created by Cody Krieger on 5/18/12. // Copyright (c) 2012 Cody Krieger. All rights reserved. diff --git a/ReactiveCocoa/Objective-C/UITextView+RACSignalSupport.m b/ReactiveObjC/UITextView+RACSignalSupport.m similarity index 96% rename from ReactiveCocoa/Objective-C/UITextView+RACSignalSupport.m rename to ReactiveObjC/UITextView+RACSignalSupport.m index 8d20c1ca53..d47f6e59c6 100644 --- a/ReactiveCocoa/Objective-C/UITextView+RACSignalSupport.m +++ b/ReactiveObjC/UITextView+RACSignalSupport.m @@ -1,13 +1,13 @@ // // UITextView+RACSignalSupport.m -// ReactiveCocoa +// ReactiveObjC // // Created by Cody Krieger on 5/18/12. // Copyright (c) 2012 Cody Krieger. All rights reserved. // #import "UITextView+RACSignalSupport.h" -#import +#import #import "NSObject+RACDeallocating.h" #import "NSObject+RACDescription.h" #import "RACDelegateProxy.h" diff --git a/ReactiveCocoa/Objective-C/extobjc/EXTKeyPathCoding.h b/ReactiveObjC/extobjc/EXTKeyPathCoding.h similarity index 100% rename from ReactiveCocoa/Objective-C/extobjc/EXTKeyPathCoding.h rename to ReactiveObjC/extobjc/EXTKeyPathCoding.h diff --git a/ReactiveCocoa/Objective-C/extobjc/EXTRuntimeExtensions.h b/ReactiveObjC/extobjc/EXTRuntimeExtensions.h similarity index 100% rename from ReactiveCocoa/Objective-C/extobjc/EXTRuntimeExtensions.h rename to ReactiveObjC/extobjc/EXTRuntimeExtensions.h diff --git a/ReactiveCocoa/Objective-C/extobjc/EXTRuntimeExtensions.m b/ReactiveObjC/extobjc/EXTRuntimeExtensions.m similarity index 99% rename from ReactiveCocoa/Objective-C/extobjc/EXTRuntimeExtensions.m rename to ReactiveObjC/extobjc/EXTRuntimeExtensions.m index bf4055cfe9..0e1a3f937e 100644 --- a/ReactiveCocoa/Objective-C/extobjc/EXTRuntimeExtensions.m +++ b/ReactiveObjC/extobjc/EXTRuntimeExtensions.m @@ -7,7 +7,7 @@ // Released under the MIT license. // -#import +#import #import #import diff --git a/ReactiveCocoa/Objective-C/extobjc/EXTScope.h b/ReactiveObjC/extobjc/EXTScope.h similarity index 100% rename from ReactiveCocoa/Objective-C/extobjc/EXTScope.h rename to ReactiveObjC/extobjc/EXTScope.h diff --git a/ReactiveCocoa/Objective-C/extobjc/metamacros.h b/ReactiveObjC/extobjc/metamacros.h similarity index 100% rename from ReactiveCocoa/Objective-C/extobjc/metamacros.h rename to ReactiveObjC/extobjc/metamacros.h diff --git a/ReactiveObjCBridge.xcodeproj/project.pbxproj b/ReactiveObjCBridge.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..f5e5d60697 --- /dev/null +++ b/ReactiveObjCBridge.xcodeproj/project.pbxproj @@ -0,0 +1,1430 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 57A4D1B71BA13D7A00F7D4B1 /* ObjectiveCBridging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312C419EF2A5800984962 /* ObjectiveCBridging.swift */; }; + 57A4D2081BA13D7A00F7D4B1 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; + 57A4D20A1BA13D7A00F7D4B1 /* ReactiveObjCBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveObjCBridge.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7DFBED081CDB8C9500EE435B /* ReactiveObjCBridge.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57A4D2411BA13D7A00F7D4B1 /* ReactiveObjCBridge.framework */; }; + 7DFBED1E1CDB8D7000EE435B /* ReactiveObjCBridge.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 57A4D2411BA13D7A00F7D4B1 /* ReactiveObjCBridge.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 7DFBED1F1CDB8D7800EE435B /* Quick.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 7DFBED201CDB8D7D00EE435B /* Nimble.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 7DFBED211CDB8D8300EE435B /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 7DFBED271CDB8DE300EE435B /* ObjectiveCBridgingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A226101A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift */; }; + A9B315BF1B3940810001CB9C /* ObjectiveCBridging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312C419EF2A5800984962 /* ObjectiveCBridging.swift */; }; + A9B315C91B3940980001CB9C /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; + A9B315CA1B3940AB0001CB9C /* ReactiveObjCBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveObjCBridge.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BE330A0F1D634F1E00806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A0E1D634F1E00806963 /* ReactiveSwift.framework */; }; + BE330A111D634F2900806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A101D634F2900806963 /* ReactiveSwift.framework */; }; + BE330A131D634F2E00806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A121D634F2E00806963 /* ReactiveSwift.framework */; }; + BE330A151D634F4000806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A141D634F4000806963 /* ReactiveSwift.framework */; }; + BE330A171D634F4E00806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A161D634F4E00806963 /* ReactiveSwift.framework */; }; + BE330A191D634F5900806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A181D634F5900806963 /* ReactiveSwift.framework */; }; + BE330A1B1D634F5F00806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A1A1D634F5F00806963 /* ReactiveSwift.framework */; }; + BEA5E5D91D864DC900A1F0F2 /* ReactiveObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BEA5E5D81D864DC900A1F0F2 /* ReactiveObjC.framework */; }; + BEA5E5DB1D864DD100A1F0F2 /* ReactiveObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BEA5E5DA1D864DD100A1F0F2 /* ReactiveObjC.framework */; }; + BEA5E5DD1D864DD800A1F0F2 /* ReactiveObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BEA5E5DC1D864DD800A1F0F2 /* ReactiveObjC.framework */; }; + BEA5E5DF1D864DDE00A1F0F2 /* ReactiveObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BEA5E5DE1D864DDE00A1F0F2 /* ReactiveObjC.framework */; }; + BEA5E5E11D864DE800A1F0F2 /* ReactiveObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BEA5E5E01D864DE800A1F0F2 /* ReactiveObjC.framework */; }; + BEA5E5E31D864DED00A1F0F2 /* ReactiveObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BEA5E5E21D864DED00A1F0F2 /* ReactiveObjC.framework */; }; + BEA5E5E51D864DF400A1F0F2 /* ReactiveObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BEA5E5E41D864DF400A1F0F2 /* ReactiveObjC.framework */; }; + BEA5E5E81D864E7600A1F0F2 /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEA5E5E61D864E7200A1F0F2 /* TestError.swift */; }; + BEA5E5E91D864E7700A1F0F2 /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEA5E5E61D864E7200A1F0F2 /* TestError.swift */; }; + BEA5E5EA1D864E7900A1F0F2 /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEA5E5E61D864E7200A1F0F2 /* TestError.swift */; }; + CDC42E2F1AE7AB8B00965373 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; + CDC42E301AE7AB8B00965373 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; + CDC42E311AE7AB8B00965373 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; + CDC42E331AE7AC6D00965373 /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + CDF066CA1CDC1CA200199626 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; }; + CDF066CB1CDC1CA200199626 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; }; + D01B7B6219EDD8FE00D26E01 /* Nimble.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D01B7B6319EDD8FE00D26E01 /* Quick.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D01B7B6419EDD94B00D26E01 /* ReactiveObjCBridge.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D047260C19E49F82006002AA /* ReactiveObjCBridge.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D037666419EDA43C00A782A9 /* ReactiveObjCBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveObjCBridge.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D037672D19EDA75D00A782A9 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; }; + D037672F19EDA78B00A782A9 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; }; + D04725F019E49ED7006002AA /* ReactiveObjCBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveObjCBridge.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D04725F619E49ED7006002AA /* ReactiveObjCBridge.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D04725EA19E49ED7006002AA /* ReactiveObjCBridge.framework */; }; + D047261719E49F82006002AA /* ReactiveObjCBridge.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D047260C19E49F82006002AA /* ReactiveObjCBridge.framework */; }; + D05E662519EDD82000904ACA /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; }; + D05E662619EDD83000904ACA /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; }; + D0A226111A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A226101A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift */; }; + D0A226121A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A226101A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift */; }; + D0C312DF19EF2A5800984962 /* ObjectiveCBridging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312C419EF2A5800984962 /* ObjectiveCBridging.swift */; }; + D0C312E019EF2A5800984962 /* ObjectiveCBridging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312C419EF2A5800984962 /* ObjectiveCBridging.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 7DFBED091CDB8C9500EE435B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D04725E119E49ED7006002AA /* Project object */; + proxyType = 1; + remoteGlobalIDString = 57A4D1AF1BA13D7A00F7D4B1; + remoteInfo = "ReactiveCocoa-tvOS"; + }; + D04725F719E49ED7006002AA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D04725E119E49ED7006002AA /* Project object */; + proxyType = 1; + remoteGlobalIDString = D04725E919E49ED7006002AA; + remoteInfo = ReactiveCocoa; + }; + D047261819E49F82006002AA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D04725E119E49ED7006002AA /* Project object */; + proxyType = 1; + remoteGlobalIDString = D047260B19E49F82006002AA; + remoteInfo = ReactiveCocoa; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 7DFBED151CDB8CEC00EE435B /* Copy Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 7DFBED211CDB8D8300EE435B /* Result.framework in Copy Frameworks */, + 7DFBED201CDB8D7D00EE435B /* Nimble.framework in Copy Frameworks */, + 7DFBED1F1CDB8D7800EE435B /* Quick.framework in Copy Frameworks */, + 7DFBED1E1CDB8D7000EE435B /* ReactiveObjCBridge.framework in Copy Frameworks */, + ); + name = "Copy Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; + D01B7B6119EDD8F600D26E01 /* Copy Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + CDC42E331AE7AC6D00965373 /* Result.framework in Copy Frameworks */, + D01B7B6219EDD8FE00D26E01 /* Nimble.framework in Copy Frameworks */, + D01B7B6319EDD8FE00D26E01 /* Quick.framework in Copy Frameworks */, + D01B7B6419EDD94B00D26E01 /* ReactiveObjCBridge.framework in Copy Frameworks */, + ); + name = "Copy Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 57A4D2411BA13D7A00F7D4B1 /* ReactiveObjCBridge.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveObjCBridge.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Application.xcconfig"; sourceTree = ""; }; + 57A4D2451BA13F9700F7D4B1 /* tvOS-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Base.xcconfig"; sourceTree = ""; }; + 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Framework.xcconfig"; sourceTree = ""; }; + 57A4D2471BA13F9700F7D4B1 /* tvOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-StaticLibrary.xcconfig"; sourceTree = ""; }; + 7DFBED031CDB8C9500EE435B /* ReactiveObjCBridgeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveObjCBridgeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + A97451331B3A935E00F48E55 /* watchOS-Application.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Application.xcconfig"; sourceTree = ""; }; + A97451341B3A935E00F48E55 /* watchOS-Base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Base.xcconfig"; sourceTree = ""; }; + A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Framework.xcconfig"; sourceTree = ""; }; + A97451361B3A935E00F48E55 /* watchOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-StaticLibrary.xcconfig"; sourceTree = ""; }; + A9B315541B3940610001CB9C /* ReactiveObjCBridge.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveObjCBridge.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BE330A0E1D634F1E00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = build/Debug/ReactiveSwift.framework; sourceTree = ""; }; + BE330A101D634F2900806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = build/Debug/ReactiveSwift.framework; sourceTree = ""; }; + BE330A121D634F2E00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-iphoneos/ReactiveSwift.framework"; sourceTree = ""; }; + BE330A141D634F4000806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-iphoneos/ReactiveSwift.framework"; sourceTree = ""; }; + BE330A161D634F4E00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-watchos/ReactiveSwift.framework"; sourceTree = ""; }; + BE330A181D634F5900806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-appletvos/ReactiveSwift.framework"; sourceTree = ""; }; + BE330A1A1D634F5F00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-appletvos/ReactiveSwift.framework"; sourceTree = ""; }; + BEA5E5D81D864DC900A1F0F2 /* ReactiveObjC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveObjC.framework; path = build/Debug/ReactiveObjC.framework; sourceTree = ""; }; + BEA5E5DA1D864DD100A1F0F2 /* ReactiveObjC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveObjC.framework; path = build/Debug/ReactiveObjC.framework; sourceTree = ""; }; + BEA5E5DC1D864DD800A1F0F2 /* ReactiveObjC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveObjC.framework; path = "build/Debug-iphoneos/ReactiveObjC.framework"; sourceTree = ""; }; + BEA5E5DE1D864DDE00A1F0F2 /* ReactiveObjC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveObjC.framework; path = "build/Debug-iphoneos/ReactiveObjC.framework"; sourceTree = ""; }; + BEA5E5E01D864DE800A1F0F2 /* ReactiveObjC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveObjC.framework; path = "build/Debug-watchos/ReactiveObjC.framework"; sourceTree = ""; }; + BEA5E5E21D864DED00A1F0F2 /* ReactiveObjC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveObjC.framework; path = "build/Debug-appletvos/ReactiveObjC.framework"; sourceTree = ""; }; + BEA5E5E41D864DF400A1F0F2 /* ReactiveObjC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveObjC.framework; path = "build/Debug-appletvos/ReactiveObjC.framework"; sourceTree = ""; }; + BEA5E5E61D864E7200A1F0F2 /* TestError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestError.swift; sourceTree = ""; }; + CDC42E2E1AE7AB8B00965373 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D037672B19EDA75D00A782A9 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Quick.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D04725EA19E49ED7006002AA /* ReactiveObjCBridge.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveObjCBridge.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D04725EE19E49ED7006002AA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D04725EF19E49ED7006002AA /* ReactiveObjCBridge.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ReactiveObjCBridge.h; sourceTree = ""; }; + D04725F519E49ED7006002AA /* ReactiveObjCBridgeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveObjCBridgeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + D04725FB19E49ED7006002AA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D047260C19E49F82006002AA /* ReactiveObjCBridge.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveObjCBridge.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D047261619E49F82006002AA /* ReactiveObjCBridgeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveObjCBridgeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + D047262719E49FE8006002AA /* Common.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Common.xcconfig; sourceTree = ""; }; + D047262919E49FE8006002AA /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + D047262A19E49FE8006002AA /* Profile.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Profile.xcconfig; sourceTree = ""; }; + D047262B19E49FE8006002AA /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + D047262C19E49FE8006002AA /* Test.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Test.xcconfig; sourceTree = ""; }; + D047262E19E49FE8006002AA /* Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Application.xcconfig; sourceTree = ""; }; + D047262F19E49FE8006002AA /* Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Framework.xcconfig; sourceTree = ""; }; + D047263019E49FE8006002AA /* StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = StaticLibrary.xcconfig; sourceTree = ""; }; + D047263219E49FE8006002AA /* iOS-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-Application.xcconfig"; sourceTree = ""; }; + D047263319E49FE8006002AA /* iOS-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-Base.xcconfig"; sourceTree = ""; }; + D047263419E49FE8006002AA /* iOS-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-Framework.xcconfig"; sourceTree = ""; }; + D047263519E49FE8006002AA /* iOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-StaticLibrary.xcconfig"; sourceTree = ""; }; + D047263719E49FE8006002AA /* Mac-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Application.xcconfig"; sourceTree = ""; }; + D047263819E49FE8006002AA /* Mac-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Base.xcconfig"; sourceTree = ""; }; + D047263919E49FE8006002AA /* Mac-DynamicLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-DynamicLibrary.xcconfig"; sourceTree = ""; }; + D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Framework.xcconfig"; sourceTree = ""; }; + D047263B19E49FE8006002AA /* Mac-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-StaticLibrary.xcconfig"; sourceTree = ""; }; + D047263C19E49FE8006002AA /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + D05E662419EDD82000904ACA /* Nimble.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Nimble.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D0A226101A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ObjectiveCBridgingSpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + D0C312C419EF2A5800984962 /* ObjectiveCBridging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjectiveCBridging.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 57A4D2071BA13D7A00F7D4B1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BEA5E5E31D864DED00A1F0F2 /* ReactiveObjC.framework in Frameworks */, + BE330A191D634F5900806963 /* ReactiveSwift.framework in Frameworks */, + 57A4D2081BA13D7A00F7D4B1 /* Result.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7DFBED001CDB8C9500EE435B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BEA5E5E51D864DF400A1F0F2 /* ReactiveObjC.framework in Frameworks */, + BE330A1B1D634F5F00806963 /* ReactiveSwift.framework in Frameworks */, + CDF066CA1CDC1CA200199626 /* Nimble.framework in Frameworks */, + CDF066CB1CDC1CA200199626 /* Quick.framework in Frameworks */, + 7DFBED081CDB8C9500EE435B /* ReactiveObjCBridge.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A9B315501B3940610001CB9C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BEA5E5E11D864DE800A1F0F2 /* ReactiveObjC.framework in Frameworks */, + BE330A171D634F4E00806963 /* ReactiveSwift.framework in Frameworks */, + A9B315C91B3940980001CB9C /* Result.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D04725E619E49ED7006002AA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BEA5E5D91D864DC900A1F0F2 /* ReactiveObjC.framework in Frameworks */, + BE330A0F1D634F1E00806963 /* ReactiveSwift.framework in Frameworks */, + CDC42E2F1AE7AB8B00965373 /* Result.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D04725F219E49ED7006002AA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BEA5E5DB1D864DD100A1F0F2 /* ReactiveObjC.framework in Frameworks */, + BE330A111D634F2900806963 /* ReactiveSwift.framework in Frameworks */, + CDC42E301AE7AB8B00965373 /* Result.framework in Frameworks */, + D05E662519EDD82000904ACA /* Nimble.framework in Frameworks */, + D037672D19EDA75D00A782A9 /* Quick.framework in Frameworks */, + D04725F619E49ED7006002AA /* ReactiveObjCBridge.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D047260819E49F82006002AA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BEA5E5DD1D864DD800A1F0F2 /* ReactiveObjC.framework in Frameworks */, + BE330A131D634F2E00806963 /* ReactiveSwift.framework in Frameworks */, + CDC42E311AE7AB8B00965373 /* Result.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D047261319E49F82006002AA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BEA5E5DF1D864DDE00A1F0F2 /* ReactiveObjC.framework in Frameworks */, + BE330A151D634F4000806963 /* ReactiveSwift.framework in Frameworks */, + D05E662619EDD83000904ACA /* Nimble.framework in Frameworks */, + D037672F19EDA78B00A782A9 /* Quick.framework in Frameworks */, + D047261719E49F82006002AA /* ReactiveObjCBridge.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 57A4D2431BA13F9700F7D4B1 /* tvOS */ = { + isa = PBXGroup; + children = ( + 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */, + 57A4D2451BA13F9700F7D4B1 /* tvOS-Base.xcconfig */, + 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */, + 57A4D2471BA13F9700F7D4B1 /* tvOS-StaticLibrary.xcconfig */, + ); + path = tvOS; + sourceTree = ""; + }; + A97451321B3A935E00F48E55 /* watchOS */ = { + isa = PBXGroup; + children = ( + A97451331B3A935E00F48E55 /* watchOS-Application.xcconfig */, + A97451341B3A935E00F48E55 /* watchOS-Base.xcconfig */, + A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */, + A97451361B3A935E00F48E55 /* watchOS-StaticLibrary.xcconfig */, + ); + path = watchOS; + sourceTree = ""; + }; + BE330A0D1D634F1E00806963 /* Frameworks */ = { + isa = PBXGroup; + children = ( + BEA5E5E41D864DF400A1F0F2 /* ReactiveObjC.framework */, + BEA5E5E21D864DED00A1F0F2 /* ReactiveObjC.framework */, + BEA5E5E01D864DE800A1F0F2 /* ReactiveObjC.framework */, + BEA5E5DE1D864DDE00A1F0F2 /* ReactiveObjC.framework */, + BEA5E5DC1D864DD800A1F0F2 /* ReactiveObjC.framework */, + BEA5E5DA1D864DD100A1F0F2 /* ReactiveObjC.framework */, + BEA5E5D81D864DC900A1F0F2 /* ReactiveObjC.framework */, + BE330A1A1D634F5F00806963 /* ReactiveSwift.framework */, + BE330A181D634F5900806963 /* ReactiveSwift.framework */, + BE330A161D634F4E00806963 /* ReactiveSwift.framework */, + BE330A141D634F4000806963 /* ReactiveSwift.framework */, + BE330A121D634F2E00806963 /* ReactiveSwift.framework */, + BE330A101D634F2900806963 /* ReactiveSwift.framework */, + BE330A0E1D634F1E00806963 /* ReactiveSwift.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + D04725E019E49ED7006002AA = { + isa = PBXGroup; + children = ( + D04725EC19E49ED7006002AA /* ReactiveObjCBridge */, + D04725F919E49ED7006002AA /* ReactiveObjCBridgeTests */, + D047262519E49FE8006002AA /* Configuration */, + D04725EB19E49ED7006002AA /* Products */, + BE330A0D1D634F1E00806963 /* Frameworks */, + ); + sourceTree = ""; + usesTabs = 1; + }; + D04725EB19E49ED7006002AA /* Products */ = { + isa = PBXGroup; + children = ( + D04725EA19E49ED7006002AA /* ReactiveObjCBridge.framework */, + D04725F519E49ED7006002AA /* ReactiveObjCBridgeTests.xctest */, + D047260C19E49F82006002AA /* ReactiveObjCBridge.framework */, + D047261619E49F82006002AA /* ReactiveObjCBridgeTests.xctest */, + A9B315541B3940610001CB9C /* ReactiveObjCBridge.framework */, + 57A4D2411BA13D7A00F7D4B1 /* ReactiveObjCBridge.framework */, + 7DFBED031CDB8C9500EE435B /* ReactiveObjCBridgeTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + D04725EC19E49ED7006002AA /* ReactiveObjCBridge */ = { + isa = PBXGroup; + children = ( + D04725EF19E49ED7006002AA /* ReactiveObjCBridge.h */, + D0C312C419EF2A5800984962 /* ObjectiveCBridging.swift */, + D04725ED19E49ED7006002AA /* Supporting Files */, + ); + path = ReactiveObjCBridge; + sourceTree = ""; + }; + D04725ED19E49ED7006002AA /* Supporting Files */ = { + isa = PBXGroup; + children = ( + CDC42E2E1AE7AB8B00965373 /* Result.framework */, + D04725EE19E49ED7006002AA /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + D04725F919E49ED7006002AA /* ReactiveObjCBridgeTests */ = { + isa = PBXGroup; + children = ( + D0A226101A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift */, + BEA5E5E61D864E7200A1F0F2 /* TestError.swift */, + D04725FA19E49ED7006002AA /* Supporting Files */, + ); + path = ReactiveObjCBridgeTests; + sourceTree = ""; + }; + D04725FA19E49ED7006002AA /* Supporting Files */ = { + isa = PBXGroup; + children = ( + D05E662419EDD82000904ACA /* Nimble.framework */, + D037672B19EDA75D00A782A9 /* Quick.framework */, + D04725FB19E49ED7006002AA /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + D047262519E49FE8006002AA /* Configuration */ = { + isa = PBXGroup; + children = ( + D047262619E49FE8006002AA /* Base */, + D047263119E49FE8006002AA /* iOS */, + D047263619E49FE8006002AA /* Mac OS X */, + A97451321B3A935E00F48E55 /* watchOS */, + 57A4D2431BA13F9700F7D4B1 /* tvOS */, + D047263C19E49FE8006002AA /* README.md */, + ); + name = Configuration; + path = Carthage/Checkouts/xcconfigs; + sourceTree = ""; + }; + D047262619E49FE8006002AA /* Base */ = { + isa = PBXGroup; + children = ( + D047262719E49FE8006002AA /* Common.xcconfig */, + D047262819E49FE8006002AA /* Configurations */, + D047262D19E49FE8006002AA /* Targets */, + ); + path = Base; + sourceTree = ""; + }; + D047262819E49FE8006002AA /* Configurations */ = { + isa = PBXGroup; + children = ( + D047262919E49FE8006002AA /* Debug.xcconfig */, + D047262A19E49FE8006002AA /* Profile.xcconfig */, + D047262B19E49FE8006002AA /* Release.xcconfig */, + D047262C19E49FE8006002AA /* Test.xcconfig */, + ); + path = Configurations; + sourceTree = ""; + }; + D047262D19E49FE8006002AA /* Targets */ = { + isa = PBXGroup; + children = ( + D047262E19E49FE8006002AA /* Application.xcconfig */, + D047262F19E49FE8006002AA /* Framework.xcconfig */, + D047263019E49FE8006002AA /* StaticLibrary.xcconfig */, + ); + path = Targets; + sourceTree = ""; + }; + D047263119E49FE8006002AA /* iOS */ = { + isa = PBXGroup; + children = ( + D047263219E49FE8006002AA /* iOS-Application.xcconfig */, + D047263319E49FE8006002AA /* iOS-Base.xcconfig */, + D047263419E49FE8006002AA /* iOS-Framework.xcconfig */, + D047263519E49FE8006002AA /* iOS-StaticLibrary.xcconfig */, + ); + path = iOS; + sourceTree = ""; + }; + D047263619E49FE8006002AA /* Mac OS X */ = { + isa = PBXGroup; + children = ( + D047263719E49FE8006002AA /* Mac-Application.xcconfig */, + D047263819E49FE8006002AA /* Mac-Base.xcconfig */, + D047263919E49FE8006002AA /* Mac-DynamicLibrary.xcconfig */, + D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */, + D047263B19E49FE8006002AA /* Mac-StaticLibrary.xcconfig */, + ); + path = "Mac OS X"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 57A4D2091BA13D7A00F7D4B1 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 57A4D20A1BA13D7A00F7D4B1 /* ReactiveObjCBridge.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A9B315511B3940610001CB9C /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + A9B315CA1B3940AB0001CB9C /* ReactiveObjCBridge.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D04725E719E49ED7006002AA /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + D04725F019E49ED7006002AA /* ReactiveObjCBridge.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D047260919E49F82006002AA /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + D037666419EDA43C00A782A9 /* ReactiveObjCBridge.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 57A4D1AF1BA13D7A00F7D4B1 /* ReactiveObjCBridge-tvOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 57A4D23C1BA13D7A00F7D4B1 /* Build configuration list for PBXNativeTarget "ReactiveObjCBridge-tvOS" */; + buildPhases = ( + 57A4D1B01BA13D7A00F7D4B1 /* Sources */, + 57A4D2071BA13D7A00F7D4B1 /* Frameworks */, + 57A4D2091BA13D7A00F7D4B1 /* Headers */, + 57A4D23B1BA13D7A00F7D4B1 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "ReactiveObjCBridge-tvOS"; + productName = ReactiveCocoa; + productReference = 57A4D2411BA13D7A00F7D4B1 /* ReactiveObjCBridge.framework */; + productType = "com.apple.product-type.framework"; + }; + 7DFBED021CDB8C9500EE435B /* ReactiveObjCBridge-tvOSTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7DFBED0F1CDB8C9500EE435B /* Build configuration list for PBXNativeTarget "ReactiveObjCBridge-tvOSTests" */; + buildPhases = ( + 7DFBECFF1CDB8C9500EE435B /* Sources */, + 7DFBED001CDB8C9500EE435B /* Frameworks */, + 7DFBED011CDB8C9500EE435B /* Resources */, + 7DFBED151CDB8CEC00EE435B /* Copy Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 7DFBED0A1CDB8C9500EE435B /* PBXTargetDependency */, + ); + name = "ReactiveObjCBridge-tvOSTests"; + productName = "ReactiveCocoa-tvOSTests"; + productReference = 7DFBED031CDB8C9500EE435B /* ReactiveObjCBridgeTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + A9B315531B3940610001CB9C /* ReactiveObjCBridge-watchOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = A9B3155D1B3940610001CB9C /* Build configuration list for PBXNativeTarget "ReactiveObjCBridge-watchOS" */; + buildPhases = ( + A9B3154F1B3940610001CB9C /* Sources */, + A9B315501B3940610001CB9C /* Frameworks */, + A9B315511B3940610001CB9C /* Headers */, + A9B315521B3940610001CB9C /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "ReactiveObjCBridge-watchOS"; + productName = ReactiveCocoa; + productReference = A9B315541B3940610001CB9C /* ReactiveObjCBridge.framework */; + productType = "com.apple.product-type.framework"; + }; + D04725E919E49ED7006002AA /* ReactiveObjCBridge-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = D047260019E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjCBridge-macOS" */; + buildPhases = ( + D04725E519E49ED7006002AA /* Sources */, + D04725E619E49ED7006002AA /* Frameworks */, + D04725E719E49ED7006002AA /* Headers */, + D04725E819E49ED7006002AA /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "ReactiveObjCBridge-macOS"; + productName = ReactiveCocoa; + productReference = D04725EA19E49ED7006002AA /* ReactiveObjCBridge.framework */; + productType = "com.apple.product-type.framework"; + }; + D04725F419E49ED7006002AA /* ReactiveObjCBridge-macOSTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = D047260319E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjCBridge-macOSTests" */; + buildPhases = ( + D04725F119E49ED7006002AA /* Sources */, + D04725F219E49ED7006002AA /* Frameworks */, + D04725F319E49ED7006002AA /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + D04725F819E49ED7006002AA /* PBXTargetDependency */, + ); + name = "ReactiveObjCBridge-macOSTests"; + productName = ReactiveCocoaTests; + productReference = D04725F519E49ED7006002AA /* ReactiveObjCBridgeTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + D047260B19E49F82006002AA /* ReactiveObjCBridge-iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = D047261F19E49F82006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjCBridge-iOS" */; + buildPhases = ( + D047260719E49F82006002AA /* Sources */, + D047260819E49F82006002AA /* Frameworks */, + D047260919E49F82006002AA /* Headers */, + D047260A19E49F82006002AA /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "ReactiveObjCBridge-iOS"; + productName = ReactiveCocoa; + productReference = D047260C19E49F82006002AA /* ReactiveObjCBridge.framework */; + productType = "com.apple.product-type.framework"; + }; + D047261519E49F82006002AA /* ReactiveObjCBridge-iOSTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = D047262219E49F82006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjCBridge-iOSTests" */; + buildPhases = ( + D047261219E49F82006002AA /* Sources */, + D047261319E49F82006002AA /* Frameworks */, + D047261419E49F82006002AA /* Resources */, + D01B7B6119EDD8F600D26E01 /* Copy Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + D047261919E49F82006002AA /* PBXTargetDependency */, + ); + name = "ReactiveObjCBridge-iOSTests"; + productName = ReactiveCocoaTests; + productReference = D047261619E49F82006002AA /* ReactiveObjCBridgeTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + D04725E119E49ED7006002AA /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0730; + LastUpgradeCheck = 0800; + ORGANIZATIONNAME = GitHub; + TargetAttributes = { + 57A4D1AF1BA13D7A00F7D4B1 = { + LastSwiftMigration = 0800; + }; + 7DFBED021CDB8C9500EE435B = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 0800; + }; + A9B315531B3940610001CB9C = { + CreatedOnToolsVersion = 7.0; + LastSwiftMigration = 0800; + }; + D04725E919E49ED7006002AA = { + CreatedOnToolsVersion = 6.1; + LastSwiftMigration = 0800; + }; + D04725F419E49ED7006002AA = { + CreatedOnToolsVersion = 6.1; + LastSwiftMigration = 0800; + }; + D047260B19E49F82006002AA = { + CreatedOnToolsVersion = 6.1; + LastSwiftMigration = 0800; + }; + D047261519E49F82006002AA = { + CreatedOnToolsVersion = 6.1; + LastSwiftMigration = 0800; + }; + }; + }; + buildConfigurationList = D04725E419E49ED7006002AA /* Build configuration list for PBXProject "ReactiveObjCBridge" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = D04725E019E49ED7006002AA; + productRefGroup = D04725EB19E49ED7006002AA /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + D04725E919E49ED7006002AA /* ReactiveObjCBridge-macOS */, + D04725F419E49ED7006002AA /* ReactiveObjCBridge-macOSTests */, + D047260B19E49F82006002AA /* ReactiveObjCBridge-iOS */, + D047261519E49F82006002AA /* ReactiveObjCBridge-iOSTests */, + A9B315531B3940610001CB9C /* ReactiveObjCBridge-watchOS */, + 57A4D1AF1BA13D7A00F7D4B1 /* ReactiveObjCBridge-tvOS */, + 7DFBED021CDB8C9500EE435B /* ReactiveObjCBridge-tvOSTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 57A4D23B1BA13D7A00F7D4B1 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7DFBED011CDB8C9500EE435B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A9B315521B3940610001CB9C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D04725E819E49ED7006002AA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D04725F319E49ED7006002AA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D047260A19E49F82006002AA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D047261419E49F82006002AA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 57A4D1B01BA13D7A00F7D4B1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 57A4D1B71BA13D7A00F7D4B1 /* ObjectiveCBridging.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7DFBECFF1CDB8C9500EE435B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BEA5E5EA1D864E7900A1F0F2 /* TestError.swift in Sources */, + 7DFBED271CDB8DE300EE435B /* ObjectiveCBridgingSpec.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A9B3154F1B3940610001CB9C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A9B315BF1B3940810001CB9C /* ObjectiveCBridging.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D04725E519E49ED7006002AA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D0C312DF19EF2A5800984962 /* ObjectiveCBridging.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D04725F119E49ED7006002AA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BEA5E5E81D864E7600A1F0F2 /* TestError.swift in Sources */, + D0A226111A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D047260719E49F82006002AA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D0C312E019EF2A5800984962 /* ObjectiveCBridging.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D047261219E49F82006002AA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BEA5E5E91D864E7700A1F0F2 /* TestError.swift in Sources */, + D0A226121A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 7DFBED0A1CDB8C9500EE435B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 57A4D1AF1BA13D7A00F7D4B1 /* ReactiveObjCBridge-tvOS */; + targetProxy = 7DFBED091CDB8C9500EE435B /* PBXContainerItemProxy */; + }; + D04725F819E49ED7006002AA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D04725E919E49ED7006002AA /* ReactiveObjCBridge-macOS */; + targetProxy = D04725F719E49ED7006002AA /* PBXContainerItemProxy */; + }; + D047261919E49F82006002AA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D047260B19E49F82006002AA /* ReactiveObjCBridge-iOS */; + targetProxy = D047261819E49F82006002AA /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 57A4D23D1BA13D7A00F7D4B1 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/build/Debug-appletvos", + ); + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DTRACE_PROBES_DISABLED=1", + ); + INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; + }; + name = Debug; + }; + 57A4D23E1BA13D7A00F7D4B1 /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/build/Debug-appletvos", + ); + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DTRACE_PROBES_DISABLED=1", + ); + INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; + }; + name = Test; + }; + 57A4D23F1BA13D7A00F7D4B1 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/build/Debug-appletvos", + ); + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DTRACE_PROBES_DISABLED=1", + ); + INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; + }; + name = Release; + }; + 57A4D2401BA13D7A00F7D4B1 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/build/Debug-appletvos", + ); + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DTRACE_PROBES_DISABLED=1", + ); + INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; + }; + name = Profile; + }; + 7DFBED0B1CDB8C9500EE435B /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + "$(PROJECT_DIR)/build/Debug-appletvos", + ); + INFOPLIST_FILE = ReactiveObjCBridgeTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Debug; + }; + 7DFBED0C1CDB8C9500EE435B /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + "$(PROJECT_DIR)/build/Debug-appletvos", + ); + INFOPLIST_FILE = ReactiveObjCBridgeTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Test; + }; + 7DFBED0D1CDB8C9500EE435B /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + "$(PROJECT_DIR)/build/Debug-appletvos", + ); + INFOPLIST_FILE = ReactiveObjCBridgeTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Release; + }; + 7DFBED0E1CDB8C9500EE435B /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + "$(PROJECT_DIR)/build/Debug-appletvos", + ); + INFOPLIST_FILE = ReactiveObjCBridgeTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Profile; + }; + A9B315591B3940610001CB9C /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/build/Debug-watchos", + ); + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DTRACE_PROBES_DISABLED=1", + ); + INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; + }; + name = Debug; + }; + A9B3155A1B3940610001CB9C /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/build/Debug-watchos", + ); + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DTRACE_PROBES_DISABLED=1", + ); + INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; + }; + name = Test; + }; + A9B3155B1B3940610001CB9C /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/build/Debug-watchos", + ); + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DTRACE_PROBES_DISABLED=1", + ); + INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; + }; + name = Release; + }; + A9B3155C1B3940610001CB9C /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/build/Debug-watchos", + ); + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DTRACE_PROBES_DISABLED=1", + ); + INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; + }; + name = Profile; + }; + D04725FE19E49ED7006002AA /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047262919E49FE8006002AA /* Debug.xcconfig */; + buildSettings = { + BITCODE_GENERATION_MODE = bitcode; + CODE_SIGNING_REQUIRED = NO; + CURRENT_PROJECT_VERSION = 1; + ENABLE_TESTABILITY = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.9; + PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(PROJECT_NAME)"; + SWIFT_VERSION = 3.0; + TVOS_DEPLOYMENT_TARGET = 9.0; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + WATCHOS_DEPLOYMENT_TARGET = 2.0; + }; + name = Debug; + }; + D04725FF19E49ED7006002AA /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047262B19E49FE8006002AA /* Release.xcconfig */; + buildSettings = { + BITCODE_GENERATION_MODE = bitcode; + CODE_SIGNING_REQUIRED = NO; + CURRENT_PROJECT_VERSION = 1; + GCC_OPTIMIZATION_LEVEL = 0; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.9; + PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(PROJECT_NAME)"; + SWIFT_VERSION = 3.0; + TVOS_DEPLOYMENT_TARGET = 9.0; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + WATCHOS_DEPLOYMENT_TARGET = 2.0; + }; + name = Release; + }; + D047260119E49ED7006002AA /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/build/Debug", + ); + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + }; + name = Debug; + }; + D047260219E49ED7006002AA /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/build/Debug", + ); + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + }; + name = Release; + }; + D047260419E49ED7006002AA /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + "$(PROJECT_DIR)/build/Debug", + ); + INFOPLIST_FILE = ReactiveObjCBridgeTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Debug; + }; + D047260519E49ED7006002AA /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + "$(PROJECT_DIR)/build/Debug", + ); + INFOPLIST_FILE = ReactiveObjCBridgeTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Release; + }; + D047262019E49F82006002AA /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/build/Debug-iphoneos", + ); + INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + }; + name = Debug; + }; + D047262119E49F82006002AA /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/build/Debug-iphoneos", + ); + INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + }; + name = Release; + }; + D047262319E49F82006002AA /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + "$(PROJECT_DIR)/build/Debug-iphoneos", + ); + INFOPLIST_FILE = ReactiveObjCBridgeTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Debug; + }; + D047262419E49F82006002AA /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + "$(PROJECT_DIR)/build/Debug-iphoneos", + ); + INFOPLIST_FILE = ReactiveObjCBridgeTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Release; + }; + D047263D19E4A008006002AA /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047262A19E49FE8006002AA /* Profile.xcconfig */; + buildSettings = { + BITCODE_GENERATION_MODE = bitcode; + CODE_SIGNING_REQUIRED = NO; + CURRENT_PROJECT_VERSION = 1; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.9; + PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(PROJECT_NAME)"; + SWIFT_VERSION = 3.0; + TVOS_DEPLOYMENT_TARGET = 9.0; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + WATCHOS_DEPLOYMENT_TARGET = 2.0; + }; + name = Profile; + }; + D047263E19E4A008006002AA /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/build/Debug", + ); + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + }; + name = Profile; + }; + D047263F19E4A008006002AA /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + "$(PROJECT_DIR)/build/Debug", + ); + INFOPLIST_FILE = ReactiveObjCBridgeTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Profile; + }; + D047264019E4A008006002AA /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/build/Debug-iphoneos", + ); + INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + }; + name = Profile; + }; + D047264119E4A008006002AA /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + "$(PROJECT_DIR)/build/Debug-iphoneos", + ); + INFOPLIST_FILE = ReactiveObjCBridgeTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Profile; + }; + D047264219E4A00B006002AA /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047262C19E49FE8006002AA /* Test.xcconfig */; + buildSettings = { + BITCODE_GENERATION_MODE = bitcode; + CODE_SIGNING_REQUIRED = NO; + CURRENT_PROJECT_VERSION = 1; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.9; + PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)-Tests"; + PRODUCT_NAME = "$(PROJECT_NAME)"; + SWIFT_VERSION = 3.0; + TVOS_DEPLOYMENT_TARGET = 9.0; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + WATCHOS_DEPLOYMENT_TARGET = 2.0; + }; + name = Test; + }; + D047264319E4A00B006002AA /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/build/Debug", + ); + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + }; + name = Test; + }; + D047264419E4A00B006002AA /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + "$(PROJECT_DIR)/build/Debug", + ); + INFOPLIST_FILE = ReactiveObjCBridgeTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Test; + }; + D047264519E4A00B006002AA /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_BITCODE = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/build/Debug-iphoneos", + ); + INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + }; + name = Test; + }; + D047264619E4A00B006002AA /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + "$(PROJECT_DIR)/build/Debug-iphoneos", + ); + INFOPLIST_FILE = ReactiveObjCBridgeTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + }; + name = Test; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 57A4D23C1BA13D7A00F7D4B1 /* Build configuration list for PBXNativeTarget "ReactiveObjCBridge-tvOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 57A4D23D1BA13D7A00F7D4B1 /* Debug */, + 57A4D23E1BA13D7A00F7D4B1 /* Test */, + 57A4D23F1BA13D7A00F7D4B1 /* Release */, + 57A4D2401BA13D7A00F7D4B1 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7DFBED0F1CDB8C9500EE435B /* Build configuration list for PBXNativeTarget "ReactiveObjCBridge-tvOSTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7DFBED0B1CDB8C9500EE435B /* Debug */, + 7DFBED0C1CDB8C9500EE435B /* Test */, + 7DFBED0D1CDB8C9500EE435B /* Release */, + 7DFBED0E1CDB8C9500EE435B /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A9B3155D1B3940610001CB9C /* Build configuration list for PBXNativeTarget "ReactiveObjCBridge-watchOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A9B315591B3940610001CB9C /* Debug */, + A9B3155A1B3940610001CB9C /* Test */, + A9B3155B1B3940610001CB9C /* Release */, + A9B3155C1B3940610001CB9C /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D04725E419E49ED7006002AA /* Build configuration list for PBXProject "ReactiveObjCBridge" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D04725FE19E49ED7006002AA /* Debug */, + D047264219E4A00B006002AA /* Test */, + D04725FF19E49ED7006002AA /* Release */, + D047263D19E4A008006002AA /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D047260019E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjCBridge-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D047260119E49ED7006002AA /* Debug */, + D047264319E4A00B006002AA /* Test */, + D047260219E49ED7006002AA /* Release */, + D047263E19E4A008006002AA /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D047260319E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjCBridge-macOSTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D047260419E49ED7006002AA /* Debug */, + D047264419E4A00B006002AA /* Test */, + D047260519E49ED7006002AA /* Release */, + D047263F19E4A008006002AA /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D047261F19E49F82006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjCBridge-iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D047262019E49F82006002AA /* Debug */, + D047264519E4A00B006002AA /* Test */, + D047262119E49F82006002AA /* Release */, + D047264019E4A008006002AA /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D047262219E49F82006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjCBridge-iOSTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D047262319E49F82006002AA /* Debug */, + D047264619E4A00B006002AA /* Test */, + D047262419E49F82006002AA /* Release */, + D047264119E4A008006002AA /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = D04725E119E49ED7006002AA /* Project object */; +} diff --git a/ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-iOS.xcscheme b/ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-iOS.xcscheme new file mode 100644 index 0000000000..37c8f6be2b --- /dev/null +++ b/ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-iOS.xcscheme @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-macOS.xcscheme b/ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-macOS.xcscheme new file mode 100644 index 0000000000..ad34ab2e88 --- /dev/null +++ b/ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-macOS.xcscheme @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-tvOS.xcscheme b/ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-tvOS.xcscheme new file mode 100644 index 0000000000..ffed7e2b63 --- /dev/null +++ b/ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-tvOS.xcscheme @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-watchOS.xcscheme b/ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-watchOS.xcscheme new file mode 100644 index 0000000000..3086c245e7 --- /dev/null +++ b/ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-watchOS.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ReactiveObjCBridge.xcworkspace/contents.xcworkspacedata b/ReactiveObjCBridge.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..7eda06116a --- /dev/null +++ b/ReactiveObjCBridge.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + diff --git a/ReactiveObjCBridge/Info.plist b/ReactiveObjCBridge/Info.plist new file mode 100644 index 0000000000..9a11afdc43 --- /dev/null +++ b/ReactiveObjCBridge/Info.plist @@ -0,0 +1,28 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSHumanReadableCopyright + Copyright © 2014 GitHub. All rights reserved. + NSPrincipalClass + + + diff --git a/ReactiveCocoa/Swift/ObjectiveCBridging.swift b/ReactiveObjCBridge/ObjectiveCBridging.swift similarity index 98% rename from ReactiveCocoa/Swift/ObjectiveCBridging.swift rename to ReactiveObjCBridge/ObjectiveCBridging.swift index 9ffd87e0ac..646b519580 100644 --- a/ReactiveCocoa/Swift/ObjectiveCBridging.swift +++ b/ReactiveObjCBridge/ObjectiveCBridging.swift @@ -1,12 +1,13 @@ // // ObjectiveCBridging.swift -// ReactiveCocoa +// ReactiveObjCBridge // // Created by Justin Spahr-Summers on 2014-07-02. // Copyright (c) 2014 GitHub, Inc. All rights reserved. // import Foundation +import ReactiveObjC import ReactiveSwift import Result @@ -101,7 +102,7 @@ extension QueueScheduler { /// - returns: Instance `RACScheduler` that queues events on /// `QueueScheduler`'s queue. public func toRACScheduler() -> RACScheduler { - return RACTargetQueueScheduler(name: "org.reactivecocoa.ReactiveCocoa.QueueScheduler.toRACScheduler()", targetQueue: queue) + return RACTargetQueueScheduler(name: "org.reactivecocoa.ReactiveObjCBridge.QueueScheduler.toRACScheduler()", targetQueue: queue) } } diff --git a/ReactiveObjCBridge/ReactiveObjCBridge.h b/ReactiveObjCBridge/ReactiveObjCBridge.h new file mode 100644 index 0000000000..eae278b2c3 --- /dev/null +++ b/ReactiveObjCBridge/ReactiveObjCBridge.h @@ -0,0 +1,15 @@ +// +// ReactiveObjCBridge.h +// ReactiveObjCBridge +// +// Created by Josh Abernathy on 3/5/12. +// Copyright (c) 2012 GitHub, Inc. All rights reserved. +// + +#import + +//! Project version number for ReactiveObjCBridge. +FOUNDATION_EXPORT double ReactiveObjCBridgeVersionNumber; + +//! Project version string for ReactiveObjCBridge. +FOUNDATION_EXPORT const unsigned char ReactiveObjCBridgeVersionString[]; diff --git a/ReactiveObjCBridgeTests/Info.plist b/ReactiveObjCBridgeTests/Info.plist new file mode 100644 index 0000000000..ba72822e87 --- /dev/null +++ b/ReactiveObjCBridgeTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/ReactiveCocoaTests/Swift/ObjectiveCBridgingSpec.swift b/ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift similarity index 99% rename from ReactiveCocoaTests/Swift/ObjectiveCBridgingSpec.swift rename to ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift index 5853f2787e..a692ce13ec 100644 --- a/ReactiveCocoaTests/Swift/ObjectiveCBridgingSpec.swift +++ b/ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift @@ -1,16 +1,17 @@ // // ObjectiveCBridgingSpec.swift -// ReactiveCocoa +// ReactiveObjCBridge // // Created by Justin Spahr-Summers on 2015-01-23. // Copyright (c) 2015 GitHub. All rights reserved. // +import ReactiveObjC +import ReactiveObjCBridge import ReactiveSwift import Result import Nimble import Quick -import ReactiveCocoa import XCTest class ObjectiveCBridgingSpec: QuickSpec { diff --git a/ReactiveObjCBridgeTests/TestError.swift b/ReactiveObjCBridgeTests/TestError.swift new file mode 100644 index 0000000000..b9d59df8ab --- /dev/null +++ b/ReactiveObjCBridgeTests/TestError.swift @@ -0,0 +1,43 @@ +// +// TestError.swift +// ReactiveSwift +// +// Created by Almas Sapargali on 1/26/15. +// Copyright (c) 2015 GitHub. All rights reserved. +// + +import ReactiveSwift +import Result + +internal enum TestError: Int { + case `default` = 0 + case error1 = 1 + case error2 = 2 +} + +extension TestError: Error { +} + + +internal extension SignalProducerProtocol { + /// Halts if an error is emitted in the receiver signal. + /// This is useful in tests to be able to just use `startWithNext` + /// in cases where we know that an error won't be emitted. + func assumeNoErrors() -> SignalProducer { + return self.lift { $0.assumeNoErrors() } + } +} + +internal extension SignalProtocol { + /// Halts if an error is emitted in the receiver signal. + /// This is useful in tests to be able to just use `startWithNext` + /// in cases where we know that an error won't be emitted. + func assumeNoErrors() -> Signal { + return self.mapError { error in + fatalError("Unexpected error: \(error)") + + () + } + } +} + diff --git a/ReactiveObjCTests/Info.plist b/ReactiveObjCTests/Info.plist new file mode 100644 index 0000000000..ba72822e87 --- /dev/null +++ b/ReactiveObjCTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/ReactiveCocoaTests/Objective-C/NSControllerRACSupportSpec.m b/ReactiveObjCTests/NSControllerRACSupportSpec.m similarity index 98% rename from ReactiveCocoaTests/Objective-C/NSControllerRACSupportSpec.m rename to ReactiveObjCTests/NSControllerRACSupportSpec.m index 97baede437..4a6cfa18ec 100644 --- a/ReactiveCocoaTests/Objective-C/NSControllerRACSupportSpec.m +++ b/ReactiveObjCTests/NSControllerRACSupportSpec.m @@ -1,6 +1,6 @@ // // NSControllerRACSupportSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Uri Baghin on 26/10/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/NSEnumeratorRACSequenceAdditionsSpec.m b/ReactiveObjCTests/NSEnumeratorRACSequenceAdditionsSpec.m similarity index 97% rename from ReactiveCocoaTests/Objective-C/NSEnumeratorRACSequenceAdditionsSpec.m rename to ReactiveObjCTests/NSEnumeratorRACSequenceAdditionsSpec.m index 04c141aaa0..c1cf616a5e 100644 --- a/ReactiveCocoaTests/Objective-C/NSEnumeratorRACSequenceAdditionsSpec.m +++ b/ReactiveObjCTests/NSEnumeratorRACSequenceAdditionsSpec.m @@ -1,6 +1,6 @@ // // NSEnumeratorRACSequenceAdditionsSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Uri Baghin on 07/01/2013. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/NSNotificationCenterRACSupportSpec.m b/ReactiveObjCTests/NSNotificationCenterRACSupportSpec.m similarity index 99% rename from ReactiveCocoaTests/Objective-C/NSNotificationCenterRACSupportSpec.m rename to ReactiveObjCTests/NSNotificationCenterRACSupportSpec.m index e1ad5476a9..b3126eeea0 100644 --- a/ReactiveCocoaTests/Objective-C/NSNotificationCenterRACSupportSpec.m +++ b/ReactiveObjCTests/NSNotificationCenterRACSupportSpec.m @@ -1,6 +1,6 @@ // // NSNotificationCenterRACSupportSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-12-07. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/NSObjectRACAppKitBindingsSpec.m b/ReactiveObjCTests/NSObjectRACAppKitBindingsSpec.m similarity index 94% rename from ReactiveCocoaTests/Objective-C/NSObjectRACAppKitBindingsSpec.m rename to ReactiveObjCTests/NSObjectRACAppKitBindingsSpec.m index f940cd5778..7ca2f391f5 100644 --- a/ReactiveCocoaTests/Objective-C/NSObjectRACAppKitBindingsSpec.m +++ b/ReactiveObjCTests/NSObjectRACAppKitBindingsSpec.m @@ -1,6 +1,6 @@ // // NSObjectRACAppKitBindingsSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-07-01. // Copyright (c) 2013 GitHub, Inc. All rights reserved. @@ -11,7 +11,7 @@ #import "RACChannelExamples.h" -#import +#import #import "NSObject+RACAppKitBindings.h" QuickSpecBegin(NSObjectRACAppKitBindingsSpec) diff --git a/ReactiveCocoaTests/Objective-C/NSObjectRACDeallocatingSpec.m b/ReactiveObjCTests/NSObjectRACDeallocatingSpec.m similarity index 99% rename from ReactiveCocoaTests/Objective-C/NSObjectRACDeallocatingSpec.m rename to ReactiveObjCTests/NSObjectRACDeallocatingSpec.m index 5d162ac6af..8f017f7ff2 100644 --- a/ReactiveCocoaTests/Objective-C/NSObjectRACDeallocatingSpec.m +++ b/ReactiveObjCTests/NSObjectRACDeallocatingSpec.m @@ -1,6 +1,6 @@ // // NSObject+RACDeallocating.m -// ReactiveCocoa +// ReactiveObjC // // Created by Kazuo Koga on 2013/03/15. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/NSObjectRACLiftingSpec.m b/ReactiveObjCTests/NSObjectRACLiftingSpec.m similarity index 99% rename from ReactiveCocoaTests/Objective-C/NSObjectRACLiftingSpec.m rename to ReactiveObjCTests/NSObjectRACLiftingSpec.m index d730a811ba..071f8b421b 100644 --- a/ReactiveCocoaTests/Objective-C/NSObjectRACLiftingSpec.m +++ b/ReactiveObjCTests/NSObjectRACLiftingSpec.m @@ -1,6 +1,6 @@ // // NSObjectRACLifting.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 10/2/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/NSObjectRACPropertySubscribingExamples.h b/ReactiveObjCTests/NSObjectRACPropertySubscribingExamples.h similarity index 97% rename from ReactiveCocoaTests/Objective-C/NSObjectRACPropertySubscribingExamples.h rename to ReactiveObjCTests/NSObjectRACPropertySubscribingExamples.h index 694ce597f6..6f0fa8809b 100644 --- a/ReactiveCocoaTests/Objective-C/NSObjectRACPropertySubscribingExamples.h +++ b/ReactiveObjCTests/NSObjectRACPropertySubscribingExamples.h @@ -1,6 +1,6 @@ // // NSObjectRACPropertySubscribingExamples.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Vera on 4/10/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/NSObjectRACPropertySubscribingExamples.m b/ReactiveObjCTests/NSObjectRACPropertySubscribingExamples.m similarity index 99% rename from ReactiveCocoaTests/Objective-C/NSObjectRACPropertySubscribingExamples.m rename to ReactiveObjCTests/NSObjectRACPropertySubscribingExamples.m index e73b6ad428..bc19a22ce0 100644 --- a/ReactiveCocoaTests/Objective-C/NSObjectRACPropertySubscribingExamples.m +++ b/ReactiveObjCTests/NSObjectRACPropertySubscribingExamples.m @@ -1,6 +1,6 @@ // // NSObjectRACPropertySubscribingExamples.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Vera on 4/10/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. @@ -12,7 +12,7 @@ #import "RACTestObject.h" #import "NSObjectRACPropertySubscribingExamples.h" -#import +#import #import "NSObject+RACDeallocating.h" #import "NSObject+RACPropertySubscribing.h" #import "RACCompoundDisposable.h" diff --git a/ReactiveCocoaTests/Objective-C/NSObjectRACPropertySubscribingSpec.m b/ReactiveObjCTests/NSObjectRACPropertySubscribingSpec.m similarity index 99% rename from ReactiveCocoaTests/Objective-C/NSObjectRACPropertySubscribingSpec.m rename to ReactiveObjCTests/NSObjectRACPropertySubscribingSpec.m index bbd7de457e..09af494271 100644 --- a/ReactiveCocoaTests/Objective-C/NSObjectRACPropertySubscribingSpec.m +++ b/ReactiveObjCTests/NSObjectRACPropertySubscribingSpec.m @@ -1,6 +1,6 @@ // // NSObjectRACPropertySubscribingSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 9/28/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/NSObjectRACSelectorSignalSpec.m b/ReactiveObjCTests/NSObjectRACSelectorSignalSpec.m similarity index 99% rename from ReactiveCocoaTests/Objective-C/NSObjectRACSelectorSignalSpec.m rename to ReactiveObjCTests/NSObjectRACSelectorSignalSpec.m index 926bd858d9..2ca24aed76 100644 --- a/ReactiveCocoaTests/Objective-C/NSObjectRACSelectorSignalSpec.m +++ b/ReactiveObjCTests/NSObjectRACSelectorSignalSpec.m @@ -1,6 +1,6 @@ // // NSObjectRACSelectorSignalSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/18/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/NSStringRACKeyPathUtilitiesSpec.m b/ReactiveObjCTests/NSStringRACKeyPathUtilitiesSpec.m similarity index 98% rename from ReactiveCocoaTests/Objective-C/NSStringRACKeyPathUtilitiesSpec.m rename to ReactiveObjCTests/NSStringRACKeyPathUtilitiesSpec.m index f39497a5ed..1062436668 100644 --- a/ReactiveCocoaTests/Objective-C/NSStringRACKeyPathUtilitiesSpec.m +++ b/ReactiveObjCTests/NSStringRACKeyPathUtilitiesSpec.m @@ -1,6 +1,6 @@ // // NSStringRACKeyPathUtilitiesSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Uri Baghin on 05/05/2013. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/NSURLConnectionRACSupportSpec.m b/ReactiveObjCTests/NSURLConnectionRACSupportSpec.m similarity index 98% rename from ReactiveCocoaTests/Objective-C/NSURLConnectionRACSupportSpec.m rename to ReactiveObjCTests/NSURLConnectionRACSupportSpec.m index 68c2e0af7b..a5e47e1440 100644 --- a/ReactiveCocoaTests/Objective-C/NSURLConnectionRACSupportSpec.m +++ b/ReactiveObjCTests/NSURLConnectionRACSupportSpec.m @@ -1,6 +1,6 @@ // // NSURLConnectionRACSupportSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-10-01. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/NSUserDefaultsRACSupportSpec.m b/ReactiveObjCTests/NSUserDefaultsRACSupportSpec.m similarity index 99% rename from ReactiveCocoaTests/Objective-C/NSUserDefaultsRACSupportSpec.m rename to ReactiveObjCTests/NSUserDefaultsRACSupportSpec.m index 01d2cdf0fd..a2a0a36120 100644 --- a/ReactiveCocoaTests/Objective-C/NSUserDefaultsRACSupportSpec.m +++ b/ReactiveObjCTests/NSUserDefaultsRACSupportSpec.m @@ -1,6 +1,6 @@ // // NSUserDefaultsRACSupportSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Matt Diephouse on 12/19/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACBlockTrampolineSpec.m b/ReactiveObjCTests/RACBlockTrampolineSpec.m similarity index 98% rename from ReactiveCocoaTests/Objective-C/RACBlockTrampolineSpec.m rename to ReactiveObjCTests/RACBlockTrampolineSpec.m index 06744ef800..ffe4def6c2 100644 --- a/ReactiveCocoaTests/Objective-C/RACBlockTrampolineSpec.m +++ b/ReactiveObjCTests/RACBlockTrampolineSpec.m @@ -1,6 +1,6 @@ // // RACBlockTrampolineSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 10/28/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACChannelExamples.h b/ReactiveObjCTests/RACChannelExamples.h similarity index 98% rename from ReactiveCocoaTests/Objective-C/RACChannelExamples.h rename to ReactiveObjCTests/RACChannelExamples.h index 0952fedbba..16a44a6c20 100644 --- a/ReactiveCocoaTests/Objective-C/RACChannelExamples.h +++ b/ReactiveObjCTests/RACChannelExamples.h @@ -1,6 +1,6 @@ // // RACChannelExamples.h -// ReactiveCocoa +// ReactiveObjC // // Created by Uri Baghin on 30/12/2012. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACChannelExamples.m b/ReactiveObjCTests/RACChannelExamples.m similarity index 99% rename from ReactiveCocoaTests/Objective-C/RACChannelExamples.m rename to ReactiveObjCTests/RACChannelExamples.m index 1ddb0d9bad..ac672cef59 100644 --- a/ReactiveCocoaTests/Objective-C/RACChannelExamples.m +++ b/ReactiveObjCTests/RACChannelExamples.m @@ -1,6 +1,6 @@ // // RACChannelExamples.m -// ReactiveCocoa +// ReactiveObjC // // Created by Uri Baghin on 30/12/2012. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACChannelSpec.m b/ReactiveObjCTests/RACChannelSpec.m similarity index 99% rename from ReactiveCocoaTests/Objective-C/RACChannelSpec.m rename to ReactiveObjCTests/RACChannelSpec.m index 9f6cb7acc9..40ea077872 100644 --- a/ReactiveCocoaTests/Objective-C/RACChannelSpec.m +++ b/ReactiveObjCTests/RACChannelSpec.m @@ -1,6 +1,6 @@ // // RACChannelSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Uri Baghin on 30/12/2012. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACCommandSpec.m b/ReactiveObjCTests/RACCommandSpec.m similarity index 99% rename from ReactiveCocoaTests/Objective-C/RACCommandSpec.m rename to ReactiveObjCTests/RACCommandSpec.m index 81a02f2561..c55de11738 100644 --- a/ReactiveCocoaTests/Objective-C/RACCommandSpec.m +++ b/ReactiveObjCTests/RACCommandSpec.m @@ -1,6 +1,6 @@ // // RACCommandSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 8/31/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACCompoundDisposableSpec.m b/ReactiveObjCTests/RACCompoundDisposableSpec.m similarity index 99% rename from ReactiveCocoaTests/Objective-C/RACCompoundDisposableSpec.m rename to ReactiveObjCTests/RACCompoundDisposableSpec.m index c135018a45..a3f0704c49 100644 --- a/ReactiveCocoaTests/Objective-C/RACCompoundDisposableSpec.m +++ b/ReactiveObjCTests/RACCompoundDisposableSpec.m @@ -1,6 +1,6 @@ // // RACCompoundDisposableSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 11/30/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACControlCommandExamples.h b/ReactiveObjCTests/RACControlCommandExamples.h similarity index 97% rename from ReactiveCocoaTests/Objective-C/RACControlCommandExamples.h rename to ReactiveObjCTests/RACControlCommandExamples.h index 3fbaa34ca6..edafe42085 100644 --- a/ReactiveCocoaTests/Objective-C/RACControlCommandExamples.h +++ b/ReactiveObjCTests/RACControlCommandExamples.h @@ -1,6 +1,6 @@ // // RACControlCommandExamples.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-08-15. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACControlCommandExamples.m b/ReactiveObjCTests/RACControlCommandExamples.m similarity index 99% rename from ReactiveCocoaTests/Objective-C/RACControlCommandExamples.m rename to ReactiveObjCTests/RACControlCommandExamples.m index 6749bd481f..ce1b9da572 100644 --- a/ReactiveCocoaTests/Objective-C/RACControlCommandExamples.m +++ b/ReactiveObjCTests/RACControlCommandExamples.m @@ -1,6 +1,6 @@ // // RACControlCommandExamples.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-08-15. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACDelegateProxySpec.m b/ReactiveObjCTests/RACDelegateProxySpec.m similarity index 99% rename from ReactiveCocoaTests/Objective-C/RACDelegateProxySpec.m rename to ReactiveObjCTests/RACDelegateProxySpec.m index 9b8d2e4c30..135b96f7a2 100644 --- a/ReactiveCocoaTests/Objective-C/RACDelegateProxySpec.m +++ b/ReactiveObjCTests/RACDelegateProxySpec.m @@ -1,6 +1,6 @@ // // RACDelegateProxySpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-06-22. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACDisposableSpec.m b/ReactiveObjCTests/RACDisposableSpec.m similarity index 99% rename from ReactiveCocoaTests/Objective-C/RACDisposableSpec.m rename to ReactiveObjCTests/RACDisposableSpec.m index 6d6e374c35..1d2a6216d4 100644 --- a/ReactiveCocoaTests/Objective-C/RACDisposableSpec.m +++ b/ReactiveObjCTests/RACDisposableSpec.m @@ -1,6 +1,6 @@ // // RACDisposableSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-06-13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACEventSpec.m b/ReactiveObjCTests/RACEventSpec.m similarity index 99% rename from ReactiveCocoaTests/Objective-C/RACEventSpec.m rename to ReactiveObjCTests/RACEventSpec.m index 0a1d3e6a21..d9eec6ae26 100644 --- a/ReactiveCocoaTests/Objective-C/RACEventSpec.m +++ b/ReactiveObjCTests/RACEventSpec.m @@ -1,6 +1,6 @@ // // RACEventSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-01-07. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACKVOChannelSpec.m b/ReactiveObjCTests/RACKVOChannelSpec.m similarity index 99% rename from ReactiveCocoaTests/Objective-C/RACKVOChannelSpec.m rename to ReactiveObjCTests/RACKVOChannelSpec.m index 70e94df285..782d42558a 100644 --- a/ReactiveCocoaTests/Objective-C/RACKVOChannelSpec.m +++ b/ReactiveObjCTests/RACKVOChannelSpec.m @@ -1,6 +1,6 @@ // // RACKVOChannelSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Uri Baghin on 16/12/2012. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACKVOProxySpec.m b/ReactiveObjCTests/RACKVOProxySpec.m similarity index 97% rename from ReactiveCocoaTests/Objective-C/RACKVOProxySpec.m rename to ReactiveObjCTests/RACKVOProxySpec.m index c08a75a244..6be2039f8d 100644 --- a/ReactiveCocoaTests/Objective-C/RACKVOProxySpec.m +++ b/ReactiveObjCTests/RACKVOProxySpec.m @@ -1,6 +1,6 @@ // // RACKVOProxySpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Richard Speyer on 4/24/14. // Copyright (c) 2014 GitHub, Inc. All rights reserved. @@ -70,7 +70,7 @@ + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key { qck_beforeEach(^{ testObject = [[TestObject alloc] init]; - concurrentQueue = dispatch_queue_create("org.reactivecocoa.ReactiveCocoa.RACKVOProxySpec.concurrentQueue", DISPATCH_QUEUE_CONCURRENT); + concurrentQueue = dispatch_queue_create("org.reactivecocoa.ReactiveObjC.RACKVOProxySpec.concurrentQueue", DISPATCH_QUEUE_CONCURRENT); }); qck_afterEach(^{ @@ -189,7 +189,7 @@ + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key { __block dispatch_queue_t iterationQueue; beforeEach(^{ - iterationQueue = dispatch_queue_create("org.reactivecocoa.ReactiveCocoa.RACKVOProxySpec.iterationQueue", DISPATCH_QUEUE_CONCURRENT); + iterationQueue = dispatch_queue_create("org.reactivecocoa.ReactiveObjC.RACKVOProxySpec.iterationQueue", DISPATCH_QUEUE_CONCURRENT); }); // ReactiveCocoa/ReactiveCocoa#1122 diff --git a/ReactiveCocoaTests/Objective-C/RACKVOWrapperSpec.m b/ReactiveObjCTests/RACKVOWrapperSpec.m similarity index 99% rename from ReactiveCocoaTests/Objective-C/RACKVOWrapperSpec.m rename to ReactiveObjCTests/RACKVOWrapperSpec.m index d013ccc8d4..8f75fc3b29 100644 --- a/ReactiveCocoaTests/Objective-C/RACKVOWrapperSpec.m +++ b/ReactiveObjCTests/RACKVOWrapperSpec.m @@ -1,6 +1,6 @@ // // RACKVOWrapperSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-08-07. // Copyright (c) 2012 GitHub, Inc. All rights reserved. @@ -11,7 +11,7 @@ #import "NSObject+RACKVOWrapper.h" -#import +#import #import "NSObject+RACDeallocating.h" #import "RACCompoundDisposable.h" #import "RACDisposable.h" diff --git a/ReactiveCocoaTests/Objective-C/RACMulticastConnectionSpec.m b/ReactiveObjCTests/RACMulticastConnectionSpec.m similarity index 99% rename from ReactiveCocoaTests/Objective-C/RACMulticastConnectionSpec.m rename to ReactiveObjCTests/RACMulticastConnectionSpec.m index 7a50ca2093..52ad88593e 100644 --- a/ReactiveCocoaTests/Objective-C/RACMulticastConnectionSpec.m +++ b/ReactiveObjCTests/RACMulticastConnectionSpec.m @@ -1,6 +1,6 @@ // // RACMulticastConnectionSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 10/8/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACPropertySignalExamples.h b/ReactiveObjCTests/RACPropertySignalExamples.h similarity index 97% rename from ReactiveCocoaTests/Objective-C/RACPropertySignalExamples.h rename to ReactiveObjCTests/RACPropertySignalExamples.h index 59a2b4305b..06b7c51072 100644 --- a/ReactiveCocoaTests/Objective-C/RACPropertySignalExamples.h +++ b/ReactiveObjCTests/RACPropertySignalExamples.h @@ -1,6 +1,6 @@ // // RACPropertySignalExamples.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 9/28/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACPropertySignalExamples.m b/ReactiveObjCTests/RACPropertySignalExamples.m similarity index 98% rename from ReactiveCocoaTests/Objective-C/RACPropertySignalExamples.m rename to ReactiveObjCTests/RACPropertySignalExamples.m index 092e015370..a6339f7d0c 100644 --- a/ReactiveCocoaTests/Objective-C/RACPropertySignalExamples.m +++ b/ReactiveObjCTests/RACPropertySignalExamples.m @@ -1,6 +1,6 @@ // // RACPropertySignalExamples.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 9/28/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. @@ -11,7 +11,7 @@ #import "RACTestObject.h" -#import +#import #import "NSObject+RACDeallocating.h" #import "NSObject+RACPropertySubscribing.h" #import "NSObject+RACSelectorSignal.h" diff --git a/ReactiveCocoaTests/Objective-C/RACSchedulerSpec.m b/ReactiveObjCTests/RACSchedulerSpec.m similarity index 99% rename from ReactiveCocoaTests/Objective-C/RACSchedulerSpec.m rename to ReactiveObjCTests/RACSchedulerSpec.m index a2a1f3c740..ba44b11c1e 100644 --- a/ReactiveCocoaTests/Objective-C/RACSchedulerSpec.m +++ b/ReactiveObjCTests/RACSchedulerSpec.m @@ -1,6 +1,6 @@ // // RACSchedulerSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 11/29/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. @@ -13,7 +13,7 @@ #import "RACScheduler+Private.h" #import "RACQueueScheduler+Subclass.h" #import "RACDisposable.h" -#import +#import #import "RACTestExampleScheduler.h" #import diff --git a/ReactiveCocoaTests/Objective-C/RACSequenceAdditionsSpec.m b/ReactiveObjCTests/RACSequenceAdditionsSpec.m similarity index 99% rename from ReactiveCocoaTests/Objective-C/RACSequenceAdditionsSpec.m rename to ReactiveObjCTests/RACSequenceAdditionsSpec.m index 6cb744509b..170aeb67f6 100644 --- a/ReactiveCocoaTests/Objective-C/RACSequenceAdditionsSpec.m +++ b/ReactiveObjCTests/RACSequenceAdditionsSpec.m @@ -1,6 +1,6 @@ // // RACSequenceAdditionsSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-11-01. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACSequenceExamples.h b/ReactiveObjCTests/RACSequenceExamples.h similarity index 95% rename from ReactiveCocoaTests/Objective-C/RACSequenceExamples.h rename to ReactiveObjCTests/RACSequenceExamples.h index 922b056e74..2e56acc0f5 100644 --- a/ReactiveCocoaTests/Objective-C/RACSequenceExamples.h +++ b/ReactiveObjCTests/RACSequenceExamples.h @@ -1,6 +1,6 @@ // // RACSequenceExamples.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-11-01. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACSequenceExamples.m b/ReactiveObjCTests/RACSequenceExamples.m similarity index 99% rename from ReactiveCocoaTests/Objective-C/RACSequenceExamples.m rename to ReactiveObjCTests/RACSequenceExamples.m index 469c1d44f0..c8e209732d 100644 --- a/ReactiveCocoaTests/Objective-C/RACSequenceExamples.m +++ b/ReactiveObjCTests/RACSequenceExamples.m @@ -1,6 +1,6 @@ // // RACSequenceExamples.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-11-01. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACSequenceSpec.m b/ReactiveObjCTests/RACSequenceSpec.m similarity index 99% rename from ReactiveCocoaTests/Objective-C/RACSequenceSpec.m rename to ReactiveObjCTests/RACSequenceSpec.m index efbd781b0b..c6f8e00cf2 100644 --- a/ReactiveCocoaTests/Objective-C/RACSequenceSpec.m +++ b/ReactiveObjCTests/RACSequenceSpec.m @@ -1,6 +1,6 @@ // // RACSequenceSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-11-01. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACSerialDisposableSpec.m b/ReactiveObjCTests/RACSerialDisposableSpec.m similarity index 99% rename from ReactiveCocoaTests/Objective-C/RACSerialDisposableSpec.m rename to ReactiveObjCTests/RACSerialDisposableSpec.m index 896fd04fed..3980fa8133 100644 --- a/ReactiveCocoaTests/Objective-C/RACSerialDisposableSpec.m +++ b/ReactiveObjCTests/RACSerialDisposableSpec.m @@ -1,6 +1,6 @@ // // RACSerialDisposableSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-07-22. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACSignalSpec.m b/ReactiveObjCTests/RACSignalSpec.m similarity index 99% rename from ReactiveCocoaTests/Objective-C/RACSignalSpec.m rename to ReactiveObjCTests/RACSignalSpec.m index 66c8628d7c..d4faca5296 100644 --- a/ReactiveCocoaTests/Objective-C/RACSignalSpec.m +++ b/ReactiveObjCTests/RACSignalSpec.m @@ -1,6 +1,6 @@ // // RACSignalSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/2/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. @@ -14,7 +14,7 @@ #import "RACStreamExamples.h" #import "RACTestObject.h" -#import +#import #import "NSObject+RACDeallocating.h" #import "NSObject+RACPropertySubscribing.h" #import "RACBehaviorSubject.h" diff --git a/ReactiveCocoaTests/Objective-C/RACStreamExamples.h b/ReactiveObjCTests/RACStreamExamples.h similarity index 97% rename from ReactiveCocoaTests/Objective-C/RACStreamExamples.h rename to ReactiveObjCTests/RACStreamExamples.h index 318a6a85e8..ddd1446c52 100644 --- a/ReactiveCocoaTests/Objective-C/RACStreamExamples.h +++ b/ReactiveObjCTests/RACStreamExamples.h @@ -1,6 +1,6 @@ // // RACStreamExamples.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-11-01. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACStreamExamples.m b/ReactiveObjCTests/RACStreamExamples.m similarity index 99% rename from ReactiveCocoaTests/Objective-C/RACStreamExamples.m rename to ReactiveObjCTests/RACStreamExamples.m index 7e23f50e67..2a682d9462 100644 --- a/ReactiveCocoaTests/Objective-C/RACStreamExamples.m +++ b/ReactiveObjCTests/RACStreamExamples.m @@ -1,6 +1,6 @@ // // RACStreamExamples.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-11-01. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACSubclassObject.h b/ReactiveObjCTests/RACSubclassObject.h similarity index 97% rename from ReactiveCocoaTests/Objective-C/RACSubclassObject.h rename to ReactiveObjCTests/RACSubclassObject.h index 062caffb88..7af3fe185d 100644 --- a/ReactiveCocoaTests/Objective-C/RACSubclassObject.h +++ b/ReactiveObjCTests/RACSubclassObject.h @@ -1,6 +1,6 @@ // // RACSubclassObject.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/18/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACSubclassObject.m b/ReactiveObjCTests/RACSubclassObject.m similarity index 98% rename from ReactiveCocoaTests/Objective-C/RACSubclassObject.m rename to ReactiveObjCTests/RACSubclassObject.m index 41e61f7d64..dd8ae6c9e7 100644 --- a/ReactiveCocoaTests/Objective-C/RACSubclassObject.m +++ b/ReactiveObjCTests/RACSubclassObject.m @@ -1,6 +1,6 @@ // // RACSubclassObject.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 3/18/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACSubjectSpec.m b/ReactiveObjCTests/RACSubjectSpec.m similarity index 98% rename from ReactiveCocoaTests/Objective-C/RACSubjectSpec.m rename to ReactiveObjCTests/RACSubjectSpec.m index 88e588f87e..3a5766a8bb 100644 --- a/ReactiveCocoaTests/Objective-C/RACSubjectSpec.m +++ b/ReactiveObjCTests/RACSubjectSpec.m @@ -1,6 +1,6 @@ // // RACSubjectSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 6/24/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. @@ -12,7 +12,7 @@ #import "RACSubscriberExamples.h" #import -#import +#import #import "RACBehaviorSubject.h" #import "RACCompoundDisposable.h" #import "RACDisposable.h" @@ -247,7 +247,7 @@ - (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable { values[indexPlusOne - 1] = value; }]; - dispatch_queue_t queue = dispatch_queue_create("org.reactivecocoa.ReactiveCocoa.RACSubjectSpec", DISPATCH_QUEUE_CONCURRENT); + dispatch_queue_t queue = dispatch_queue_create("org.reactivecocoa.ReactiveObjC.RACSubjectSpec", DISPATCH_QUEUE_CONCURRENT); dispatch_suspend(queue); for (NSUInteger i = 0; i < count; i++) { diff --git a/ReactiveCocoaTests/Objective-C/RACSubscriberExamples.h b/ReactiveObjCTests/RACSubscriberExamples.h similarity index 97% rename from ReactiveCocoaTests/Objective-C/RACSubscriberExamples.h rename to ReactiveObjCTests/RACSubscriberExamples.h index edc9e5abf9..f2b0ccf84f 100644 --- a/ReactiveCocoaTests/Objective-C/RACSubscriberExamples.h +++ b/ReactiveObjCTests/RACSubscriberExamples.h @@ -1,6 +1,6 @@ // // RACSubscriberExamples.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-11-27. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACSubscriberExamples.m b/ReactiveObjCTests/RACSubscriberExamples.m similarity index 99% rename from ReactiveCocoaTests/Objective-C/RACSubscriberExamples.m rename to ReactiveObjCTests/RACSubscriberExamples.m index c24a74a0f5..769084a677 100644 --- a/ReactiveCocoaTests/Objective-C/RACSubscriberExamples.m +++ b/ReactiveObjCTests/RACSubscriberExamples.m @@ -1,6 +1,6 @@ // // RACSubscriberExamples.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-11-27. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACSubscriberSpec.m b/ReactiveObjCTests/RACSubscriberSpec.m similarity index 97% rename from ReactiveCocoaTests/Objective-C/RACSubscriberSpec.m rename to ReactiveObjCTests/RACSubscriberSpec.m index 8da595a8cc..1b99580067 100644 --- a/ReactiveCocoaTests/Objective-C/RACSubscriberSpec.m +++ b/ReactiveObjCTests/RACSubscriberSpec.m @@ -1,6 +1,6 @@ // // RACSubscriberSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-11-27. // Copyright (c) 2012 GitHub, Inc. All rights reserved. @@ -67,7 +67,7 @@ dispatchGroup = dispatch_group_create(); expect(dispatchGroup).notTo(beNil()); - concurrentQueue = dispatch_queue_create("org.reactivecocoa.ReactiveCocoa.RACSubscriberSpec", DISPATCH_QUEUE_CONCURRENT); + concurrentQueue = dispatch_queue_create("org.reactivecocoa.ReactiveObjC.RACSubscriberSpec", DISPATCH_QUEUE_CONCURRENT); expect(concurrentQueue).notTo(beNil()); dispatch_suspend(concurrentQueue); diff --git a/ReactiveCocoaTests/Objective-C/RACSubscriptingAssignmentTrampolineSpec.m b/ReactiveObjCTests/RACSubscriptingAssignmentTrampolineSpec.m similarity index 98% rename from ReactiveCocoaTests/Objective-C/RACSubscriptingAssignmentTrampolineSpec.m rename to ReactiveObjCTests/RACSubscriptingAssignmentTrampolineSpec.m index e83d02bcad..87f3ea44aa 100644 --- a/ReactiveCocoaTests/Objective-C/RACSubscriptingAssignmentTrampolineSpec.m +++ b/ReactiveObjCTests/RACSubscriptingAssignmentTrampolineSpec.m @@ -1,6 +1,6 @@ // // RACSubscriptingAssignmentTrampolineSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 9/24/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACTargetQueueSchedulerSpec.m b/ReactiveObjCTests/RACTargetQueueSchedulerSpec.m similarity index 98% rename from ReactiveCocoaTests/Objective-C/RACTargetQueueSchedulerSpec.m rename to ReactiveObjCTests/RACTargetQueueSchedulerSpec.m index cdf0b9663c..d5a9d29d7c 100644 --- a/ReactiveCocoaTests/Objective-C/RACTargetQueueSchedulerSpec.m +++ b/ReactiveObjCTests/RACTargetQueueSchedulerSpec.m @@ -1,6 +1,6 @@ // // RACTargetQueueSchedulerSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 6/7/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACTestExampleScheduler.h b/ReactiveObjCTests/RACTestExampleScheduler.h similarity index 80% rename from ReactiveCocoaTests/Objective-C/RACTestExampleScheduler.h rename to ReactiveObjCTests/RACTestExampleScheduler.h index 0470d98c44..46250bde9e 100644 --- a/ReactiveCocoaTests/Objective-C/RACTestExampleScheduler.h +++ b/ReactiveObjCTests/RACTestExampleScheduler.h @@ -1,12 +1,12 @@ // // RACTestExampleScheduler.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 6/7/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. // -#import +#import @interface RACTestExampleScheduler : RACQueueScheduler diff --git a/ReactiveCocoaTests/Objective-C/RACTestExampleScheduler.m b/ReactiveObjCTests/RACTestExampleScheduler.m similarity index 97% rename from ReactiveCocoaTests/Objective-C/RACTestExampleScheduler.m rename to ReactiveObjCTests/RACTestExampleScheduler.m index 859055c9bb..14ef5ee50c 100644 --- a/ReactiveCocoaTests/Objective-C/RACTestExampleScheduler.m +++ b/ReactiveObjCTests/RACTestExampleScheduler.m @@ -1,6 +1,6 @@ // // RACTestExampleScheduler.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 6/7/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACTestObject.h b/ReactiveObjCTests/RACTestObject.h similarity index 99% rename from ReactiveCocoaTests/Objective-C/RACTestObject.h rename to ReactiveObjCTests/RACTestObject.h index 06ae31d900..98dd7393cc 100644 --- a/ReactiveCocoaTests/Objective-C/RACTestObject.h +++ b/ReactiveObjCTests/RACTestObject.h @@ -1,6 +1,6 @@ // // RACTestObject.h -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 9/18/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACTestObject.m b/ReactiveObjCTests/RACTestObject.m similarity index 99% rename from ReactiveCocoaTests/Objective-C/RACTestObject.m rename to ReactiveObjCTests/RACTestObject.m index f590703f2b..66aac010a1 100644 --- a/ReactiveCocoaTests/Objective-C/RACTestObject.m +++ b/ReactiveObjCTests/RACTestObject.m @@ -1,6 +1,6 @@ // // RACTestObject.m -// ReactiveCocoa +// ReactiveObjC // // Created by Josh Abernathy on 9/18/12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACTestSchedulerSpec.m b/ReactiveObjCTests/RACTestSchedulerSpec.m similarity index 99% rename from ReactiveCocoaTests/Objective-C/RACTestSchedulerSpec.m rename to ReactiveObjCTests/RACTestSchedulerSpec.m index 4c5460bc4b..2ae51739f9 100644 --- a/ReactiveCocoaTests/Objective-C/RACTestSchedulerSpec.m +++ b/ReactiveObjCTests/RACTestSchedulerSpec.m @@ -1,6 +1,6 @@ // // RACTestSchedulerSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-07-06. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACTestUIButton.h b/ReactiveObjCTests/RACTestUIButton.h similarity index 94% rename from ReactiveCocoaTests/Objective-C/RACTestUIButton.h rename to ReactiveObjCTests/RACTestUIButton.h index 03e97161e1..5160af112b 100644 --- a/ReactiveCocoaTests/Objective-C/RACTestUIButton.h +++ b/ReactiveObjCTests/RACTestUIButton.h @@ -1,6 +1,6 @@ // // RACTestUIButton.h -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-06-15. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACTestUIButton.m b/ReactiveObjCTests/RACTestUIButton.m similarity index 97% rename from ReactiveCocoaTests/Objective-C/RACTestUIButton.m rename to ReactiveObjCTests/RACTestUIButton.m index 48b2674908..cff413f683 100644 --- a/ReactiveCocoaTests/Objective-C/RACTestUIButton.m +++ b/ReactiveObjCTests/RACTestUIButton.m @@ -1,6 +1,6 @@ // // RACTestUIButton.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2013-06-15. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/RACTupleSpec.m b/ReactiveObjCTests/RACTupleSpec.m similarity index 99% rename from ReactiveCocoaTests/Objective-C/RACTupleSpec.m rename to ReactiveObjCTests/RACTupleSpec.m index 5cbd49e922..c8658fbb5f 100644 --- a/ReactiveCocoaTests/Objective-C/RACTupleSpec.m +++ b/ReactiveObjCTests/RACTupleSpec.m @@ -1,6 +1,6 @@ // // RACTupleSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Justin Spahr-Summers on 2012-12-12. // Copyright (c) 2012 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/UIActionSheetRACSupportSpec.m b/ReactiveObjCTests/UIActionSheetRACSupportSpec.m similarity index 98% rename from ReactiveCocoaTests/Objective-C/UIActionSheetRACSupportSpec.m rename to ReactiveObjCTests/UIActionSheetRACSupportSpec.m index 7bac73c926..848fc4cde9 100644 --- a/ReactiveCocoaTests/Objective-C/UIActionSheetRACSupportSpec.m +++ b/ReactiveObjCTests/UIActionSheetRACSupportSpec.m @@ -1,6 +1,6 @@ // // UIActionSheetRACSupportSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Dave Lee on 2013-06-22. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/UIAlertViewRACSupportSpec.m b/ReactiveObjCTests/UIAlertViewRACSupportSpec.m similarity index 98% rename from ReactiveCocoaTests/Objective-C/UIAlertViewRACSupportSpec.m rename to ReactiveObjCTests/UIAlertViewRACSupportSpec.m index 29bab43b6a..c2a7ff846b 100644 --- a/ReactiveCocoaTests/Objective-C/UIAlertViewRACSupportSpec.m +++ b/ReactiveObjCTests/UIAlertViewRACSupportSpec.m @@ -1,6 +1,6 @@ // // UIAlertViewRACSupportSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Henrik Hodne on 6/16/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/UIBarButtonItemRACSupportSpec.m b/ReactiveObjCTests/UIBarButtonItemRACSupportSpec.m similarity index 98% rename from ReactiveCocoaTests/Objective-C/UIBarButtonItemRACSupportSpec.m rename to ReactiveObjCTests/UIBarButtonItemRACSupportSpec.m index 783dbbd27f..dd90fd2b96 100644 --- a/ReactiveCocoaTests/Objective-C/UIBarButtonItemRACSupportSpec.m +++ b/ReactiveObjCTests/UIBarButtonItemRACSupportSpec.m @@ -1,6 +1,6 @@ // // UIBarButtonItemRACSupportSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Kyle LeNeau on 4/13/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/UIButtonRACSupportSpec.m b/ReactiveObjCTests/UIButtonRACSupportSpec.m similarity index 98% rename from ReactiveCocoaTests/Objective-C/UIButtonRACSupportSpec.m rename to ReactiveObjCTests/UIButtonRACSupportSpec.m index 10094c4def..e50176cd31 100644 --- a/ReactiveCocoaTests/Objective-C/UIButtonRACSupportSpec.m +++ b/ReactiveObjCTests/UIButtonRACSupportSpec.m @@ -1,6 +1,6 @@ // // UIButtonRACSupportSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Ash Furrow on 2013-06-06. // Copyright (c) 2013 GitHub, Inc. All rights reserved. diff --git a/ReactiveCocoaTests/Objective-C/UIImagePickerControllerRACSupportSpec.m b/ReactiveObjCTests/UIImagePickerControllerRACSupportSpec.m similarity index 98% rename from ReactiveCocoaTests/Objective-C/UIImagePickerControllerRACSupportSpec.m rename to ReactiveObjCTests/UIImagePickerControllerRACSupportSpec.m index beafc0589a..52a2d9a0a9 100644 --- a/ReactiveCocoaTests/Objective-C/UIImagePickerControllerRACSupportSpec.m +++ b/ReactiveObjCTests/UIImagePickerControllerRACSupportSpec.m @@ -1,6 +1,6 @@ // // UIImagePickerControllerRACSupportSpec.m -// ReactiveCocoa +// ReactiveObjC // // Created by Timur Kuchkarov on 17.04.14. // Copyright (c) 2014 GitHub, Inc. All rights reserved. diff --git a/ReactiveObjCTests/test-data.json b/ReactiveObjCTests/test-data.json new file mode 100644 index 0000000000..0599ee5fe0 --- /dev/null +++ b/ReactiveObjCTests/test-data.json @@ -0,0 +1,5 @@ +[ + { "item": 1 }, + { "item": 2 }, + { "item": 3 } +] From a04351723430a81745f8c66c0f4e4970e774bc63 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Mon, 12 Sep 2016 08:02:49 -0400 Subject: [PATCH 0363/1028] Remove bridging header from ReactiveObjC --- ReactiveObjC/ReactiveCocoa-Bridging-Header.h | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 ReactiveObjC/ReactiveCocoa-Bridging-Header.h diff --git a/ReactiveObjC/ReactiveCocoa-Bridging-Header.h b/ReactiveObjC/ReactiveCocoa-Bridging-Header.h deleted file mode 100644 index eabf5c9443..0000000000 --- a/ReactiveObjC/ReactiveCocoa-Bridging-Header.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// Use this file to import your target's public headers that you would like to expose to Swift. -// - -#import "RACCommand.h" -#import "RACDisposable.h" -#import "RACEvent.h" -#import "RACScheduler.h" -#import "RACTargetQueueScheduler.h" -#import "RACSignal.h" -#import "RACSignal+Operations.h" -#import "RACStream.h" -#import "RACSubscriber.h" From f40229c278c5c9d29a2db561c01de8b339936678 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Mon, 12 Sep 2016 08:04:13 -0400 Subject: [PATCH 0364/1028] Only valid playgrounds when a target is set --- script/build | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/script/build b/script/build index e36726ee19..1c0b904a5a 100755 --- a/script/build +++ b/script/build @@ -45,8 +45,8 @@ if [ "$result" -ne 0 ]; then exit $result fi -# Compile code in playgrounds -if [[ $XCODE_SDK = "macosx" ]]; then - echo "SDK is $XCODE_SDK, validating playground..." +# Compile code in playgrounds +if [[ -z $XCODE_PLAYGROUND_TARGET ]]; then + echo "Validating playground..." . script/validate-playground.sh fi From 19e16fa9b6033ac9cc9b5ebe4e5a4f80c05d3101 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Mon, 12 Sep 2016 08:04:25 -0400 Subject: [PATCH 0365/1028] Don't set playground target for ReactiveObjC or ReactiveObjCBridge --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ffc6d524d6..8763dc672d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,7 +38,6 @@ matrix: - XCODE_SDK=macosx - XCODE_ACTION="build test" - XCODE_DESTINATION="arch=x86_64" - - XCODE_PLAYGROUND_TARGET="x86_64-apple-macosx10.10" - xcode_scheme: ReactiveObjC-iOS xcode_workspace: ReactiveObjC.xcworkspace env: @@ -63,7 +62,6 @@ matrix: - XCODE_SDK=macosx - XCODE_ACTION="build test" - XCODE_DESTINATION="arch=x86_64" - - XCODE_PLAYGROUND_TARGET="x86_64-apple-macosx10.10" - xcode_scheme: ReactiveObjCBridge-iOS xcode_workspace: ReactiveObjCBridge.xcworkspace env: From 120321ed8eb231781771f4c4fccd591415a76421 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Mon, 12 Sep 2016 08:08:21 -0400 Subject: [PATCH 0366/1028] Remove the rest of Result from ReactiveObjC --- ReactiveObjC.xcodeproj/project.pbxproj | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ReactiveObjC.xcodeproj/project.pbxproj b/ReactiveObjC.xcodeproj/project.pbxproj index 5e57bff9be..500c3153f1 100644 --- a/ReactiveObjC.xcodeproj/project.pbxproj +++ b/ReactiveObjC.xcodeproj/project.pbxproj @@ -153,7 +153,6 @@ 7DFBED1E1CDB8D7000EE435B /* ReactiveObjC.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 57A4D2411BA13D7A00F7D4B1 /* ReactiveObjC.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 7DFBED1F1CDB8D7800EE435B /* Quick.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 7DFBED201CDB8D7D00EE435B /* Nimble.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 7DFBED211CDB8D8300EE435B /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 7DFBED321CDB8DE300EE435B /* NSEnumeratorRACSequenceAdditionsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667819EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m */; }; 7DFBED331CDB8DE300EE435B /* NSNotificationCenterRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667919EDA60000A782A9 /* NSNotificationCenterRACSupportSpec.m */; }; 7DFBED351CDB8DE300EE435B /* NSObjectRACDeallocatingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667B19EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m */; }; @@ -323,7 +322,6 @@ BEBDD6E61CDC292D009A75A9 /* RACDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646B19EDA41200A782A9 /* RACDelegateProxy.h */; settings = {ATTRIBUTES = (Public, ); }; }; BEBDD6E71CDC292E009A75A9 /* RACDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646B19EDA41200A782A9 /* RACDelegateProxy.h */; settings = {ATTRIBUTES = (Public, ); }; }; BEBDD6E81CDC292F009A75A9 /* RACDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646B19EDA41200A782A9 /* RACDelegateProxy.h */; settings = {ATTRIBUTES = (Public, ); }; }; - CDC42E331AE7AC6D00965373 /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; CDF066CA1CDC1CA200199626 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; }; CDF066CB1CDC1CA200199626 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; }; D01B7B6219EDD8FE00D26E01 /* Nimble.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -737,7 +735,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 7DFBED211CDB8D8300EE435B /* Result.framework in Copy Frameworks */, 7DFBED201CDB8D7D00EE435B /* Nimble.framework in Copy Frameworks */, 7DFBED1F1CDB8D7800EE435B /* Quick.framework in Copy Frameworks */, 7DFBED1E1CDB8D7000EE435B /* ReactiveObjC.framework in Copy Frameworks */, @@ -751,7 +748,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - CDC42E331AE7AC6D00965373 /* Result.framework in Copy Frameworks */, D01B7B6219EDD8FE00D26E01 /* Nimble.framework in Copy Frameworks */, D01B7B6319EDD8FE00D26E01 /* Quick.framework in Copy Frameworks */, D01B7B6419EDD94B00D26E01 /* ReactiveObjC.framework in Copy Frameworks */, @@ -785,7 +781,6 @@ BE330A161D634F4E00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-watchos/ReactiveSwift.framework"; sourceTree = ""; }; BE330A181D634F5900806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-appletvos/ReactiveSwift.framework"; sourceTree = ""; }; BE330A1A1D634F5F00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-appletvos/ReactiveSwift.framework"; sourceTree = ""; }; - CDC42E2E1AE7AB8B00965373 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D037642A19EDA41200A782A9 /* NSArray+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+RACSequenceAdditions.h"; sourceTree = ""; }; D037642B19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+RACSequenceAdditions.m"; sourceTree = ""; }; D037642C19EDA41200A782A9 /* NSControl+RACCommandSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSControl+RACCommandSupport.h"; sourceTree = ""; }; @@ -1405,7 +1400,6 @@ D04725ED19E49ED7006002AA /* Supporting Files */ = { isa = PBXGroup; children = ( - CDC42E2E1AE7AB8B00965373 /* Result.framework */, D04725EE19E49ED7006002AA /* Info.plist */, ); name = "Supporting Files"; From 5872c3d2439e4351e49c4fe0cdd7cf367f3d2899 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Mon, 12 Sep 2016 08:09:24 -0400 Subject: [PATCH 0367/1028] Xcode has ideas --- .../xcshareddata/xcschemes/ReactiveObjC-watchOS.xcscheme | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-watchOS.xcscheme b/ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-watchOS.xcscheme index e396f10362..4193c26877 100644 --- a/ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-watchOS.xcscheme +++ b/ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-watchOS.xcscheme @@ -51,6 +51,15 @@ debugDocumentVersioning = "YES" debugServiceExtension = "internal" allowLocationSimulation = "YES"> + + + + From 9dd763bd8e99e03689c1011746f1dbc82845e98e Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Mon, 12 Sep 2016 08:47:50 -0400 Subject: [PATCH 0368/1028] Fix inverted logic --- script/build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/build b/script/build index 1c0b904a5a..6b1f69cf79 100755 --- a/script/build +++ b/script/build @@ -46,7 +46,7 @@ if [ "$result" -ne 0 ]; then fi # Compile code in playgrounds -if [[ -z $XCODE_PLAYGROUND_TARGET ]]; then +if [[ "$XCODE_PLAYGROUND_TARGET" ]]; then echo "Validating playground..." . script/validate-playground.sh fi From ee70b743efd2e3eabc4c8c722d95ad87da1655eb Mon Sep 17 00:00:00 2001 From: Anders Date: Mon, 12 Sep 2016 13:50:15 +0100 Subject: [PATCH 0369/1028] Updated for the ReactiveSwift spin-off. --- Cartfile.resolved | 5 +- Deprecations+Removals.swift | 1 + Rex.xcodeproj/project.pbxproj | 75 +++++++++++++------ .../xcshareddata/xcschemes/Rex-Mac.xcscheme | 16 ++-- Source/Action.swift | 1 + Source/AppKit/NSTextField.swift | 1 + Source/Foundation/Association.swift | 1 + Source/Foundation/Data.swift | 2 +- Source/Foundation/NSObject.swift | 1 + Source/Foundation/UserDefaults.swift | 1 + Source/Property.swift | 1 + Source/RACSignal.swift | 1 + Source/Reusable.swift | 1 + Source/Signal.swift | 1 + Source/SignalProducer.swift | 1 + Source/UIKit/UIActivityIndicatorView.swift | 1 + Source/UIKit/UIBarButtonItem.swift | 1 + Source/UIKit/UIBarItem.swift | 1 + Source/UIKit/UIButton.swift | 1 + Source/UIKit/UIControl.swift | 1 + Source/UIKit/UIDatePicker.swift | 1 + Source/UIKit/UIImageView.swift | 1 + Source/UIKit/UILabel.swift | 1 + Source/UIKit/UIProgressView.swift | 3 +- Source/UIKit/UISegmentedControl.swift | 1 + Source/UIKit/UISwitch.swift | 1 + Source/UIKit/UITextField.swift | 2 + Source/UIKit/UITextView.swift | 1 + Source/UIKit/UIView.swift | 1 + Source/UIKit/UIViewController.swift | 1 + Tests/ActionTests.swift | 1 + Tests/Foundation/NSObjectTests.swift | 1 + Tests/PropertyTests.swift | 1 + Tests/SignalProducerTests.swift | 1 + Tests/SignalTests.swift | 1 + .../UIKit/UIActivityIndicatorViewTests.swift | 1 + Tests/UIKit/UIBarButtonItemTests.swift | 1 + Tests/UIKit/UIButtonTests.swift | 1 + .../UIKit/UICollectionReusableViewTests.swift | 2 +- Tests/UIKit/UIControlTests.swift | 1 + Tests/UIKit/UIDatePickerTests.swift | 1 + Tests/UIKit/UIImageViewTests.swift | 1 + Tests/UIKit/UILabelTests.swift | 1 + Tests/UIKit/UIProgressViewTests.swift | 1 + Tests/UIKit/UISegmentedControlTests.swift | 1 + Tests/UIKit/UISwitchTests.swift | 1 + Tests/UIKit/UITableViewCellTests.swift | 2 +- .../UITableViewHeaderFooterViewTests.swift | 2 +- Tests/UIKit/UITextFieldTests.swift | 1 + Tests/UIKit/UITextViewTests.swift | 1 + Tests/UIKit/UIViewControllerTests.swift | 1 + Tests/UIKit/UIViewTests.swift | 1 + 52 files changed, 114 insertions(+), 38 deletions(-) diff --git a/Cartfile.resolved b/Cartfile.resolved index 7acf5240d8..118b9955d8 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,3 @@ -github "antitypical/Result" "3.0.0-alpha.3" -github "ReactiveCocoa/ReactiveCocoa" "b711611ad9906b1bfc5027824f57a2889e34204d" +github "antitypical/Result" "3.0.0" +github "ReactiveCocoa/ReactiveSwift" "598ce311be21911d3d365c6b9c5245fdc9e72394" +github "ReactiveCocoa/ReactiveCocoa" "26997cf6f0eda7d64b76ba82013525e6ab900e8e" diff --git a/Deprecations+Removals.swift b/Deprecations+Removals.swift index a6de0129d9..5b62761a3c 100644 --- a/Deprecations+Removals.swift +++ b/Deprecations+Removals.swift @@ -1,4 +1,5 @@ // MARK: Renamed APIs in Swift 3.0 +import ReactiveSwift import ReactiveCocoa import enum Result.NoError diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 5785e327e7..4ca606f411 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -39,6 +39,17 @@ 8295FD871B87309F007C9000 /* UIControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8295FD851B873081007C9000 /* UIControlTests.swift */; }; 8295FD8A1B87352D007C9000 /* UIButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8295FD881B873490007C9000 /* UIButtonTests.swift */; }; 8295FD8D1B87374A007C9000 /* UIBarButtonItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */; }; + 9A0501931D86D6D9006BBAE8 /* ReactiveSwift.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9A0501921D86D6D9006BBAE8 /* ReactiveSwift.framework */; }; + 9A0501941D86D6F0006BBAE8 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A0501921D86D6D9006BBAE8 /* ReactiveSwift.framework */; }; + 9A0501951D86D6FB006BBAE8 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A0501921D86D6D9006BBAE8 /* ReactiveSwift.framework */; }; + 9A0501971D86D713006BBAE8 /* ReactiveSwift.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = 9A0501961D86D713006BBAE8 /* ReactiveSwift.framework */; }; + 9A0501981D86D71D006BBAE8 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A0501961D86D713006BBAE8 /* ReactiveSwift.framework */; }; + 9A05019B1D86D737006BBAE8 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A05019A1D86D737006BBAE8 /* ReactiveSwift.framework */; }; + 9A05019D1D86D751006BBAE8 /* ReactiveSwift.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9A05019A1D86D737006BBAE8 /* ReactiveSwift.framework */; }; + 9A05019E1D86D759006BBAE8 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A05019A1D86D737006BBAE8 /* ReactiveSwift.framework */; }; + 9A0501A01D86D76C006BBAE8 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A05019F1D86D765006BBAE8 /* ReactiveSwift.framework */; }; + 9A4375B41D86DBA000F04A59 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A0501961D86D713006BBAE8 /* ReactiveSwift.framework */; }; + 9A4375B51D86DBA000F04A59 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; }; 9A5492121D33387D009A8D44 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5492111D33387D009A8D44 /* Deprecations+Removals.swift */; }; 9A5492131D33387D009A8D44 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5492111D33387D009A8D44 /* Deprecations+Removals.swift */; }; 9A5492141D33387D009A8D44 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5492111D33387D009A8D44 /* Deprecations+Removals.swift */; }; @@ -60,8 +71,8 @@ CC02C18B1CCA704F0025CC04 /* ActionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC02C1881CCA704C0025CC04 /* ActionTests.swift */; }; CC02C18C1CCA704F0025CC04 /* ActionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC02C1881CCA704C0025CC04 /* ActionTests.swift */; }; D8003E941AFEC3D400D7D3C5 /* Rex.h in Headers */ = {isa = PBXBuildFile; fileRef = D8003E931AFEC3D400D7D3C5 /* Rex.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - D8003EB51AFEC6B000D7D3C5 /* Result.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = D8003EAE1AFEC68A00D7D3C5 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + D8003EB51AFEC6B000D7D3C5 /* Result.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = D8003EAE1AFEC68A00D7D3C5 /* Result.framework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */; }; D8003EBD1AFED01000D7D3C5 /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBC1AFED01000D7D3C5 /* Signal.swift */; }; D8003EC21AFED30F00D7D3C5 /* SignalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */; }; @@ -71,7 +82,6 @@ D834572D1AFEE45B0070616A /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBC1AFED01000D7D3C5 /* Signal.swift */; }; D834572E1AFEE45B0070616A /* SignalProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */; }; D83457301AFEE45E0070616A /* SignalProducerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EC01AFED30100D7D3C5 /* SignalProducerTests.swift */; }; - D83457321AFEE4930070616A /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; }; D83457331AFEE4930070616A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAE1AFEC68A00D7D3C5 /* Result.framework */; }; D83457351AFEE4B20070616A /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */; }; D83457361AFEE4B20070616A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */; }; @@ -184,6 +194,7 @@ dstSubfolderSpec = 16; files = ( D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in Copy Files */, + 9A0501971D86D713006BBAE8 /* ReactiveSwift.framework in Copy Files */, D8003EB51AFEC6B000D7D3C5 /* Result.framework in Copy Files */, ); name = "Copy Files"; @@ -196,6 +207,7 @@ dstSubfolderSpec = 16; files = ( D83457391AFEE4BE0070616A /* ReactiveCocoa.framework in CopyFiles */, + 9A0501931D86D6D9006BBAE8 /* ReactiveSwift.framework in CopyFiles */, D834573A1AFEE4BE0070616A /* Result.framework in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; @@ -206,6 +218,7 @@ dstPath = ""; dstSubfolderSpec = 16; files = ( + 9A05019D1D86D751006BBAE8 /* ReactiveSwift.framework in CopyFiles */, D8715DE91C211739005F4191 /* ReactiveCocoa.framework in CopyFiles */, D8715DEA1C211739005F4191 /* Result.framework in CopyFiles */, ); @@ -238,6 +251,10 @@ 8295FD851B873081007C9000 /* UIControlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIControlTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 8295FD881B873490007C9000 /* UIButtonTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIButtonTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIBarButtonItemTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 9A0501921D86D6D9006BBAE8 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ReactiveSwift.framework; sourceTree = ""; }; + 9A0501961D86D713006BBAE8 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ReactiveSwift.framework; sourceTree = ""; }; + 9A05019A1D86D737006BBAE8 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = tvOS/ReactiveSwift.framework; sourceTree = ""; }; + 9A05019F1D86D765006BBAE8 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = watchOS/ReactiveSwift.framework; sourceTree = ""; }; 9A5492111D33387D009A8D44 /* Deprecations+Removals.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deprecations+Removals.swift"; sourceTree = SOURCE_ROOT; }; 9DA915A31CA6301C003723B9 /* UIDatePicker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIDatePicker.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 9DA915A51CA63046003723B9 /* UIDatePickerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIDatePickerTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; @@ -261,7 +278,7 @@ D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ReactiveCocoa.framework; sourceTree = ""; }; D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = ""; }; D86E77A91AFEE646004BF23D /* Rex.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Rex.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D86E77AA1AFEE646004BF23D /* RexTests-Mac.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RexTests-Mac.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + D86E77AA1AFEE646004BF23D /* RexTests-macOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RexTests-macOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; D86E77AB1AFEE646004BF23D /* Rex.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Rex.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D86E77AC1AFEE646004BF23D /* RexTests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RexTests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; D86FFBD01B34AD6F001A89B3 /* Association.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Association.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; @@ -292,8 +309,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D83457321AFEE4930070616A /* ReactiveCocoa.framework in Frameworks */, D83457331AFEE4930070616A /* Result.framework in Frameworks */, + 9A4375B41D86DBA000F04A59 /* ReactiveSwift.framework in Frameworks */, + 9A4375B51D86DBA000F04A59 /* ReactiveCocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -302,6 +320,7 @@ buildActionMask = 2147483647; files = ( D8003EC51AFED36F00D7D3C5 /* ReactiveCocoa.framework in Frameworks */, + 9A0501981D86D71D006BBAE8 /* ReactiveSwift.framework in Frameworks */, D8003EC61AFED36F00D7D3C5 /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -310,8 +329,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D83457351AFEE4B20070616A /* ReactiveCocoa.framework in Frameworks */, D83457361AFEE4B20070616A /* Result.framework in Frameworks */, + 9A0501951D86D6FB006BBAE8 /* ReactiveSwift.framework in Frameworks */, + D83457351AFEE4B20070616A /* ReactiveCocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -320,6 +340,7 @@ buildActionMask = 2147483647; files = ( D834573C1AFEE57E0070616A /* ReactiveCocoa.framework in Frameworks */, + 9A0501941D86D6F0006BBAE8 /* ReactiveSwift.framework in Frameworks */, D834573D1AFEE57E0070616A /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -328,8 +349,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D8715DA91C2110DA005F4191 /* ReactiveCocoa.framework in Frameworks */, D8715DAA1C2110DA005F4191 /* Result.framework in Frameworks */, + 9A0501A01D86D76C006BBAE8 /* ReactiveSwift.framework in Frameworks */, + D8715DA91C2110DA005F4191 /* ReactiveCocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -337,8 +359,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D8715DC41C211310005F4191 /* ReactiveCocoa.framework in Frameworks */, D8715DC51C211310005F4191 /* Result.framework in Frameworks */, + 9A05019E1D86D759006BBAE8 /* ReactiveSwift.framework in Frameworks */, + D8715DC41C211310005F4191 /* ReactiveCocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -348,6 +371,7 @@ files = ( D8715DE61C21170C005F4191 /* ReactiveCocoa.framework in Frameworks */, D8715DE71C21170C005F4191 /* Result.framework in Frameworks */, + 9A05019B1D86D737006BBAE8 /* ReactiveSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -436,7 +460,7 @@ isa = PBXGroup; children = ( D8003EC71AFEE39000D7D3C5 /* iOS */, - D8003EAB1AFEC67700D7D3C5 /* Mac */, + D8003EAB1AFEC67700D7D3C5 /* macOS */, D8715DB81C21124B005F4191 /* tvOS */, D8715D9C1C210FA1005F4191 /* watchOS */, ); @@ -444,14 +468,16 @@ path = Carthage/Build; sourceTree = ""; }; - D8003EAB1AFEC67700D7D3C5 /* Mac */ = { + D8003EAB1AFEC67700D7D3C5 /* macOS */ = { isa = PBXGroup; children = ( D86E77A91AFEE646004BF23D /* Rex.framework */, - D86E77AA1AFEE646004BF23D /* RexTests-Mac.xctest */, + D86E77AA1AFEE646004BF23D /* RexTests-macOS.xctest */, + 9A0501961D86D713006BBAE8 /* ReactiveSwift.framework */, D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */, D8003EAE1AFEC68A00D7D3C5 /* Result.framework */, ); + name = macOS; path = Mac; sourceTree = ""; }; @@ -460,6 +486,7 @@ children = ( D86E77AB1AFEE646004BF23D /* Rex.framework */, D86E77AC1AFEE646004BF23D /* RexTests-iOS.xctest */, + 9A0501921D86D6D9006BBAE8 /* ReactiveSwift.framework */, D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */, D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */, ); @@ -496,6 +523,7 @@ children = ( D8715D941C210F97005F4191 /* Rex.framework */, D8715DA71C2110DA005F4191 /* ReactiveCocoa.framework */, + 9A05019F1D86D765006BBAE8 /* ReactiveSwift.framework */, D8715DA81C2110DA005F4191 /* Result.framework */, ); name = watchOS; @@ -506,6 +534,7 @@ children = ( D8715DB01C21123E005F4191 /* Rex.framework */, D8715DD11C21160A005F4191 /* RexTests-tvOS.xctest */, + 9A05019A1D86D737006BBAE8 /* ReactiveSwift.framework */, D8715DC21C211310005F4191 /* ReactiveCocoa.framework */, D8715DC31C211310005F4191 /* Result.framework */, ); @@ -593,9 +622,9 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ - D8003E8D1AFEC3D400D7D3C5 /* Rex-Mac */ = { + D8003E8D1AFEC3D400D7D3C5 /* Rex-macOS */ = { isa = PBXNativeTarget; - buildConfigurationList = D8003EA41AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "Rex-Mac" */; + buildConfigurationList = D8003EA41AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "Rex-macOS" */; buildPhases = ( D8003E891AFEC3D400D7D3C5 /* Sources */, D8003E8A1AFEC3D400D7D3C5 /* Frameworks */, @@ -606,14 +635,14 @@ ); dependencies = ( ); - name = "Rex-Mac"; + name = "Rex-macOS"; productName = Rex; productReference = D86E77A91AFEE646004BF23D /* Rex.framework */; productType = "com.apple.product-type.framework"; }; - D8003E981AFEC3D400D7D3C5 /* RexTests-Mac */ = { + D8003E981AFEC3D400D7D3C5 /* RexTests-macOS */ = { isa = PBXNativeTarget; - buildConfigurationList = D8003EA71AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "RexTests-Mac" */; + buildConfigurationList = D8003EA71AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "RexTests-macOS" */; buildPhases = ( D8003E951AFEC3D400D7D3C5 /* Sources */, D8003E961AFEC3D400D7D3C5 /* Frameworks */, @@ -625,9 +654,9 @@ dependencies = ( D8003E9C1AFEC3D400D7D3C5 /* PBXTargetDependency */, ); - name = "RexTests-Mac"; + name = "RexTests-macOS"; productName = RexTests; - productReference = D86E77AA1AFEE646004BF23D /* RexTests-Mac.xctest */; + productReference = D86E77AA1AFEE646004BF23D /* RexTests-macOS.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; D83457131AFEE44E0070616A /* Rex-iOS */ = { @@ -774,8 +803,8 @@ projectDirPath = ""; projectRoot = ""; targets = ( - D8003E8D1AFEC3D400D7D3C5 /* Rex-Mac */, - D8003E981AFEC3D400D7D3C5 /* RexTests-Mac */, + D8003E8D1AFEC3D400D7D3C5 /* Rex-macOS */, + D8003E981AFEC3D400D7D3C5 /* RexTests-macOS */, D83457131AFEE44E0070616A /* Rex-iOS */, D834571D1AFEE44E0070616A /* RexTests-iOS */, D8715DAF1C21123E005F4191 /* Rex-tvOS */, @@ -1011,7 +1040,7 @@ /* Begin PBXTargetDependency section */ D8003E9C1AFEC3D400D7D3C5 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = D8003E8D1AFEC3D400D7D3C5 /* Rex-Mac */; + target = D8003E8D1AFEC3D400D7D3C5 /* Rex-macOS */; targetProxy = D8003E9B1AFEC3D400D7D3C5 /* PBXContainerItemProxy */; }; D83457211AFEE44E0070616A /* PBXTargetDependency */ = { @@ -1451,7 +1480,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - D8003EA41AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "Rex-Mac" */ = { + D8003EA41AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "Rex-macOS" */ = { isa = XCConfigurationList; buildConfigurations = ( D8003EA51AFEC3D400D7D3C5 /* Debug */, @@ -1460,7 +1489,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - D8003EA71AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "RexTests-Mac" */ = { + D8003EA71AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "RexTests-macOS" */ = { isa = XCConfigurationList; buildConfigurations = ( D8003EA81AFEC3D400D7D3C5 /* Debug */, diff --git a/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme index 3a84fae0dc..475a6e087e 100644 --- a/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme +++ b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme @@ -16,7 +16,7 @@ BuildableIdentifier = "primary" BlueprintIdentifier = "D8003E8D1AFEC3D400D7D3C5" BuildableName = "Rex.framework" - BlueprintName = "Rex-Mac" + BlueprintName = "Rex-macOS" ReferencedContainer = "container:Rex.xcodeproj"> @@ -29,8 +29,8 @@ @@ -47,8 +47,8 @@ @@ -58,7 +58,7 @@ BuildableIdentifier = "primary" BlueprintIdentifier = "D8003E8D1AFEC3D400D7D3C5" BuildableName = "Rex.framework" - BlueprintName = "Rex-Mac" + BlueprintName = "Rex-macOS" ReferencedContainer = "container:Rex.xcodeproj"> @@ -80,7 +80,7 @@ BuildableIdentifier = "primary" BlueprintIdentifier = "D8003E8D1AFEC3D400D7D3C5" BuildableName = "Rex.framework" - BlueprintName = "Rex-Mac" + BlueprintName = "Rex-macOS" ReferencedContainer = "container:Rex.xcodeproj"> @@ -98,7 +98,7 @@ BuildableIdentifier = "primary" BlueprintIdentifier = "D8003E8D1AFEC3D400D7D3C5" BuildableName = "Rex.framework" - BlueprintName = "Rex-Mac" + BlueprintName = "Rex-macOS" ReferencedContainer = "container:Rex.xcodeproj"> diff --git a/Source/Action.swift b/Source/Action.swift index 13f9289e5e..e46eb987ff 100644 --- a/Source/Action.swift +++ b/Source/Action.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import enum Result.NoError diff --git a/Source/AppKit/NSTextField.swift b/Source/AppKit/NSTextField.swift index 4f2ba9875a..67c33218d4 100644 --- a/Source/AppKit/NSTextField.swift +++ b/Source/AppKit/NSTextField.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import AppKit import enum Result.NoError diff --git a/Source/Foundation/Association.swift b/Source/Foundation/Association.swift index c7ce80e586..485ab32736 100644 --- a/Source/Foundation/Association.swift +++ b/Source/Foundation/Association.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa /// Attaches a `MutableProperty` value to the `host` object using KVC to get the initial diff --git a/Source/Foundation/Data.swift b/Source/Foundation/Data.swift index 8f9527ce87..4f77d0b8eb 100644 --- a/Source/Foundation/Data.swift +++ b/Source/Foundation/Data.swift @@ -6,7 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // -import ReactiveCocoa +import ReactiveSwift extension Data { /// Read the data at the URL, sending the result or an error. diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index 531085f4f2..68e118b4ac 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import enum Result.NoError diff --git a/Source/Foundation/UserDefaults.swift b/Source/Foundation/UserDefaults.swift index a67e4cca89..061e0e2544 100644 --- a/Source/Foundation/UserDefaults.swift +++ b/Source/Foundation/UserDefaults.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import enum Result.NoError diff --git a/Source/Property.swift b/Source/Property.swift index a263569f7e..d3831450ae 100644 --- a/Source/Property.swift +++ b/Source/Property.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import enum Result.NoError diff --git a/Source/RACSignal.swift b/Source/RACSignal.swift index 14f4f29bbc..c1220bd4a9 100644 --- a/Source/RACSignal.swift +++ b/Source/RACSignal.swift @@ -6,6 +6,7 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import enum Result.NoError diff --git a/Source/Reusable.swift b/Source/Reusable.swift index a71197f131..4fc3dce2ff 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 cfef9b6bcc..2e796ea496 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import enum Result.NoError diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index d436a742b7..46f08dc5c6 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import enum Result.NoError diff --git a/Source/UIKit/UIActivityIndicatorView.swift b/Source/UIKit/UIActivityIndicatorView.swift index 85d5848f90..62fc2903a6 100644 --- a/Source/UIKit/UIActivityIndicatorView.swift +++ b/Source/UIKit/UIActivityIndicatorView.swift @@ -6,6 +6,7 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/Source/UIKit/UIBarButtonItem.swift b/Source/UIKit/UIBarButtonItem.swift index d2f696073b..fd9239f17f 100644 --- a/Source/UIKit/UIBarButtonItem.swift +++ b/Source/UIKit/UIBarButtonItem.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/Source/UIKit/UIBarItem.swift b/Source/UIKit/UIBarItem.swift index 5ff61c6235..710ee88976 100644 --- a/Source/UIKit/UIBarItem.swift +++ b/Source/UIKit/UIBarItem.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/Source/UIKit/UIButton.swift b/Source/UIKit/UIButton.swift index c3229718f2..82361eccc7 100644 --- a/Source/UIKit/UIButton.swift +++ b/Source/UIKit/UIButton.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/Source/UIKit/UIControl.swift b/Source/UIKit/UIControl.swift index 09ae8d96d6..c9b5053f3a 100644 --- a/Source/UIKit/UIControl.swift +++ b/Source/UIKit/UIControl.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit import enum Result.NoError diff --git a/Source/UIKit/UIDatePicker.swift b/Source/UIKit/UIDatePicker.swift index 3fbbd32b2e..3b14724431 100644 --- a/Source/UIKit/UIDatePicker.swift +++ b/Source/UIKit/UIDatePicker.swift @@ -6,6 +6,7 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/Source/UIKit/UIImageView.swift b/Source/UIKit/UIImageView.swift index db6d09ea77..75094a71d0 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 c47746f326..a77bcafa61 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 52119d4ad4..871d2871d5 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 acd990a413..4a7f4a1566 100644 --- a/Source/UIKit/UISegmentedControl.swift +++ b/Source/UIKit/UISegmentedControl.swift @@ -6,6 +6,7 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/Source/UIKit/UISwitch.swift b/Source/UIKit/UISwitch.swift index e1b661283c..9c06c48eab 100644 --- a/Source/UIKit/UISwitch.swift +++ b/Source/UIKit/UISwitch.swift @@ -6,6 +6,7 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/Source/UIKit/UITextField.swift b/Source/UIKit/UITextField.swift index 8842995cf4..4beb5361ca 100644 --- a/Source/UIKit/UITextField.swift +++ b/Source/UIKit/UITextField.swift @@ -6,6 +6,8 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift +import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/Source/UIKit/UITextView.swift b/Source/UIKit/UITextView.swift index c78df6df7b..7d02851fcb 100644 --- a/Source/UIKit/UITextView.swift +++ b/Source/UIKit/UITextView.swift @@ -6,6 +6,7 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit import enum Result.NoError diff --git a/Source/UIKit/UIView.swift b/Source/UIKit/UIView.swift index 2284087c01..51bb64725b 100644 --- a/Source/UIKit/UIView.swift +++ b/Source/UIKit/UIView.swift @@ -6,6 +6,7 @@ // Copyright © 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/Source/UIKit/UIViewController.swift b/Source/UIKit/UIViewController.swift index ca3aa4871e..add6634257 100644 --- a/Source/UIKit/UIViewController.swift +++ b/Source/UIKit/UIViewController.swift @@ -6,6 +6,7 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift import Result import ReactiveCocoa import UIKit diff --git a/Tests/ActionTests.swift b/Tests/ActionTests.swift index 2c4673a5b4..87f41356e3 100644 --- a/Tests/ActionTests.swift +++ b/Tests/ActionTests.swift @@ -7,6 +7,7 @@ // @testable import Rex +import ReactiveSwift import ReactiveCocoa import XCTest import enum Result.NoError diff --git a/Tests/Foundation/NSObjectTests.swift b/Tests/Foundation/NSObjectTests.swift index a453989fe2..e04158f903 100644 --- a/Tests/Foundation/NSObjectTests.swift +++ b/Tests/Foundation/NSObjectTests.swift @@ -7,6 +7,7 @@ // import Rex +import ReactiveSwift import ReactiveCocoa import XCTest diff --git a/Tests/PropertyTests.swift b/Tests/PropertyTests.swift index 7ee93128f1..a5429c7261 100644 --- a/Tests/PropertyTests.swift +++ b/Tests/PropertyTests.swift @@ -7,6 +7,7 @@ // @testable import Rex +import ReactiveSwift import ReactiveCocoa import XCTest import enum Result.NoError diff --git a/Tests/SignalProducerTests.swift b/Tests/SignalProducerTests.swift index f2cbff6b8c..41094dc84f 100644 --- a/Tests/SignalProducerTests.swift +++ b/Tests/SignalProducerTests.swift @@ -7,6 +7,7 @@ // import Rex +import ReactiveSwift import ReactiveCocoa import XCTest import enum Result.NoError diff --git a/Tests/SignalTests.swift b/Tests/SignalTests.swift index 26860472b5..54a4189010 100644 --- a/Tests/SignalTests.swift +++ b/Tests/SignalTests.swift @@ -7,6 +7,7 @@ // import Rex +import ReactiveSwift import ReactiveCocoa import XCTest import enum Result.NoError diff --git a/Tests/UIKit/UIActivityIndicatorViewTests.swift b/Tests/UIKit/UIActivityIndicatorViewTests.swift index df5051d349..67e7edde80 100644 --- a/Tests/UIKit/UIActivityIndicatorViewTests.swift +++ b/Tests/UIKit/UIActivityIndicatorViewTests.swift @@ -7,6 +7,7 @@ // import XCTest +import ReactiveSwift import ReactiveCocoa import Result diff --git a/Tests/UIKit/UIBarButtonItemTests.swift b/Tests/UIKit/UIBarButtonItemTests.swift index d20553b4f5..97e79d9636 100644 --- a/Tests/UIKit/UIBarButtonItemTests.swift +++ b/Tests/UIKit/UIBarButtonItemTests.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit import XCTest diff --git a/Tests/UIKit/UIButtonTests.swift b/Tests/UIKit/UIButtonTests.swift index a03a28a65c..1be613dc23 100644 --- a/Tests/UIKit/UIButtonTests.swift +++ b/Tests/UIKit/UIButtonTests.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit import XCTest diff --git a/Tests/UIKit/UICollectionReusableViewTests.swift b/Tests/UIKit/UICollectionReusableViewTests.swift index 9c4fea99f7..68b3c93eb0 100644 --- a/Tests/UIKit/UICollectionReusableViewTests.swift +++ b/Tests/UIKit/UICollectionReusableViewTests.swift @@ -7,8 +7,8 @@ // import XCTest +import ReactiveSwift import ReactiveCocoa - class UICollectionReusableViewTests: XCTestCase { func testPrepareForReuse() { diff --git a/Tests/UIKit/UIControlTests.swift b/Tests/UIKit/UIControlTests.swift index 07f35d0ea3..9715c80100 100644 --- a/Tests/UIKit/UIControlTests.swift +++ b/Tests/UIKit/UIControlTests.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit import XCTest diff --git a/Tests/UIKit/UIDatePickerTests.swift b/Tests/UIKit/UIDatePickerTests.swift index 55d9362631..f7dac8f377 100644 --- a/Tests/UIKit/UIDatePickerTests.swift +++ b/Tests/UIKit/UIDatePickerTests.swift @@ -6,6 +6,7 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit import XCTest diff --git a/Tests/UIKit/UIImageViewTests.swift b/Tests/UIKit/UIImageViewTests.swift index b8f4e0fa19..ccb1fcc2c7 100644 --- a/Tests/UIKit/UIImageViewTests.swift +++ b/Tests/UIKit/UIImageViewTests.swift @@ -6,6 +6,7 @@ // Copyright © 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit import XCTest diff --git a/Tests/UIKit/UILabelTests.swift b/Tests/UIKit/UILabelTests.swift index 056d1ec60c..8c826c3e9c 100644 --- a/Tests/UIKit/UILabelTests.swift +++ b/Tests/UIKit/UILabelTests.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit import XCTest diff --git a/Tests/UIKit/UIProgressViewTests.swift b/Tests/UIKit/UIProgressViewTests.swift index 9e81afdb94..c8300d9326 100644 --- a/Tests/UIKit/UIProgressViewTests.swift +++ b/Tests/UIKit/UIProgressViewTests.swift @@ -6,6 +6,7 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit import XCTest diff --git a/Tests/UIKit/UISegmentedControlTests.swift b/Tests/UIKit/UISegmentedControlTests.swift index d7c7bfa7b1..9a9f8e7206 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 5098ff7c70..684914e4b6 100644 --- a/Tests/UIKit/UISwitchTests.swift +++ b/Tests/UIKit/UISwitchTests.swift @@ -7,6 +7,7 @@ // import XCTest +import ReactiveSwift import ReactiveCocoa import Result diff --git a/Tests/UIKit/UITableViewCellTests.swift b/Tests/UIKit/UITableViewCellTests.swift index a6a4baa28b..2ff7ccadc3 100644 --- a/Tests/UIKit/UITableViewCellTests.swift +++ b/Tests/UIKit/UITableViewCellTests.swift @@ -7,8 +7,8 @@ // import XCTest +import ReactiveSwift import ReactiveCocoa - class UITableViewCellTests: XCTestCase { func testPrepareForReuse() { diff --git a/Tests/UIKit/UITableViewHeaderFooterViewTests.swift b/Tests/UIKit/UITableViewHeaderFooterViewTests.swift index 01ef6acfa7..20eb2eeed2 100644 --- a/Tests/UIKit/UITableViewHeaderFooterViewTests.swift +++ b/Tests/UIKit/UITableViewHeaderFooterViewTests.swift @@ -7,8 +7,8 @@ // import XCTest +import ReactiveSwift import ReactiveCocoa - class UITableViewHeaderFooterViewTests: XCTestCase { func testPrepareForReuse() { diff --git a/Tests/UIKit/UITextFieldTests.swift b/Tests/UIKit/UITextFieldTests.swift index 8ce8d830d1..67434e0b96 100644 --- a/Tests/UIKit/UITextFieldTests.swift +++ b/Tests/UIKit/UITextFieldTests.swift @@ -6,6 +6,7 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit import XCTest diff --git a/Tests/UIKit/UITextViewTests.swift b/Tests/UIKit/UITextViewTests.swift index d99f0e9a9f..28a0836add 100644 --- a/Tests/UIKit/UITextViewTests.swift +++ b/Tests/UIKit/UITextViewTests.swift @@ -6,6 +6,7 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit import XCTest diff --git a/Tests/UIKit/UIViewControllerTests.swift b/Tests/UIKit/UIViewControllerTests.swift index 315b723d3e..931992ea3a 100644 --- a/Tests/UIKit/UIViewControllerTests.swift +++ b/Tests/UIKit/UIViewControllerTests.swift @@ -6,6 +6,7 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit import XCTest diff --git a/Tests/UIKit/UIViewTests.swift b/Tests/UIKit/UIViewTests.swift index 552717712e..6a5b9c6df6 100644 --- a/Tests/UIKit/UIViewTests.swift +++ b/Tests/UIKit/UIViewTests.swift @@ -6,6 +6,7 @@ // Copyright © 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit import XCTest From 42522ce2f5b7891451aa1143a6f4a9914d3b91ca Mon Sep 17 00:00:00 2001 From: Anders Date: Mon, 12 Sep 2016 13:55:52 +0100 Subject: [PATCH 0370/1028] Fixed the failing `UIButton.rex_pressed` test. --- Rex.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 4ca606f411..33106c09a0 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -50,6 +50,7 @@ 9A0501A01D86D76C006BBAE8 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A05019F1D86D765006BBAE8 /* ReactiveSwift.framework */; }; 9A4375B41D86DBA000F04A59 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A0501961D86D713006BBAE8 /* ReactiveSwift.framework */; }; 9A4375B51D86DBA000F04A59 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; }; + 9A4375B61D86DD1D00F04A59 /* UIControl+EnableSendActionsForControlEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D0DABA91CCC381F00B6CD2B /* UIControl+EnableSendActionsForControlEvents.swift */; }; 9A5492121D33387D009A8D44 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5492111D33387D009A8D44 /* Deprecations+Removals.swift */; }; 9A5492131D33387D009A8D44 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5492111D33387D009A8D44 /* Deprecations+Removals.swift */; }; 9A5492141D33387D009A8D44 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5492111D33387D009A8D44 /* Deprecations+Removals.swift */; }; @@ -1015,6 +1016,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9A4375B61D86DD1D00F04A59 /* UIControl+EnableSendActionsForControlEvents.swift in Sources */, E6933BEB1CD9C363006F7CE7 /* UIProgressViewTests.swift in Sources */, D8715DE51C211643005F4191 /* UIViewTests.swift in Sources */, 7DCF5B371CC80E8E004AEE75 /* UICollectionReusableViewTests.swift in Sources */, From e95b087ee6054e0620ca2d6d812fe844457a23f0 Mon Sep 17 00:00:00 2001 From: Anders Date: Mon, 12 Sep 2016 13:58:44 +0100 Subject: [PATCH 0371/1028] Updated for Swift 3 GM. --- Source/Foundation/Association.swift | 6 +++--- Source/Property.swift | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/Foundation/Association.swift b/Source/Foundation/Association.swift index 485ab32736..77858c642c 100644 --- a/Source/Foundation/Association.swift +++ b/Source/Foundation/Association.swift @@ -33,7 +33,7 @@ public func associatedProperty(_ host: AnyObject, keyPath: StaticString) -> Muta /// /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. -public func associatedProperty(_ host: AnyObject, keyPath: StaticString, placeholder: @noescape () -> T) -> MutableProperty { +public func associatedProperty(_ host: AnyObject, keyPath: StaticString, placeholder: () -> T) -> MutableProperty { let setter: (AnyObject, T) -> () = { host, newValue in host.setValue(newValue, forKeyPath: String(describing: keyPath)) } @@ -48,7 +48,7 @@ public func associatedProperty(_ host: AnyObject, keyPath: StaticS /// /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. -public func associatedProperty(_ host: Host, key: UnsafePointer<()>, initial: @noescape (Host) -> T, setter: @escaping (Host, T) -> (), setUp: @noescape (MutableProperty) -> () = { _ in }) -> MutableProperty { +public func associatedProperty(_ host: Host, key: UnsafeRawPointer, initial: (Host) -> T, setter: @escaping (Host, T) -> (), setUp: (MutableProperty) -> () = { _ in }) -> MutableProperty { return associatedObject(host, key: key) { host in let property = MutableProperty(initial(host)) @@ -67,7 +67,7 @@ public func associatedProperty(_ host: Host, key: UnsafePoin /// On first use attaches the object returned from `initial` to the `host` object using /// `key` via `objc_setAssociatedObject`. On subsequent usage, returns said object via /// `objc_getAssociatedObject`. -public func associatedObject(_ host: Host, key: UnsafePointer<()>, initial: @noescape (Host) -> T) -> T { +public func associatedObject(_ host: Host, key: UnsafeRawPointer, initial: (Host) -> T) -> T { var value = objc_getAssociatedObject(host, key) as? T if value == nil { value = initial(host) diff --git a/Source/Property.swift b/Source/Property.swift index d3831450ae..067c629f79 100644 --- a/Source/Property.swift +++ b/Source/Property.swift @@ -12,7 +12,7 @@ import enum Result.NoError extension PropertyProtocol where Value == Bool { /// The conjunction of `self` and `other`. - public func and(_ other: P) -> AndProperty { + public func and(_ other: P) -> AndProperty where P.Value == Bool { return AndProperty(terms: [Property(self), Property(other)]) } @@ -22,7 +22,7 @@ extension PropertyProtocol where Value == Bool { } /// The disjunction of `self` and `other`. - public func or(_ other: P) -> OrProperty { + public func or(_ other: P) -> OrProperty where P.Value == Bool { return OrProperty(terms: [Property(self), Property(other)]) } @@ -60,7 +60,7 @@ public class AndProperty: PropertyProtocol { } /// Creates a new property with an additional conjunctive term. - public func and

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

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

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

(_ other: P) -> OrProperty where P.Value == Bool { return OrProperty(terms: terms + [Property(other)]) } From 729f881e1b430e89d05e5ee9cc284d6cf81b1625 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Mon, 12 Sep 2016 18:22:16 -0400 Subject: [PATCH 0372/1028] Adjust the timeout for Travis --- ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift b/ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift index a692ce13ec..708a77f839 100644 --- a/ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift +++ b/ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift @@ -26,7 +26,7 @@ class ObjectiveCBridgingSpec: QuickSpec { } it("gives current date") { - expect(scheduler.currentDate).to(beCloseTo(Date(), within: 0.0002)) + expect(scheduler.currentDate).to(beCloseTo(Date(), within: 0.0003)) } it("schedules actions") { From 546d5ea8958b0bce02a217f778fc927b4f47532a Mon Sep 17 00:00:00 2001 From: Yannick Heinrich Date: Wed, 14 Sep 2016 11:55:57 +0200 Subject: [PATCH 0373/1028] Remove warning on XCode8 for #3183 The option `receiver-is-weak` seems to be ignored according to the llvm mailing list: http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20160418/156202.html. --- .../NSObject+RACPropertySubscribing.h | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/ReactiveCocoa/Objective-C/NSObject+RACPropertySubscribing.h b/ReactiveCocoa/Objective-C/NSObject+RACPropertySubscribing.h index fd64f0c77b..9a7c8e81b7 100644 --- a/ReactiveCocoa/Objective-C/NSObject+RACPropertySubscribing.h +++ b/ReactiveCocoa/Objective-C/NSObject+RACPropertySubscribing.h @@ -46,14 +46,22 @@ /// Returns a signal which sends the current value of the key path on /// subscription, then sends the new value every time it changes, and sends /// completed if self or observer is deallocated. +#if __clang__ && (__clang_major__ >= 8) #define RACObserve(TARGET, KEYPATH) \ - ({ \ - _Pragma("clang diagnostic push") \ - _Pragma("clang diagnostic ignored \"-Wreceiver-is-weak\"") \ - __weak id target_ = (TARGET); \ - [target_ rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self]; \ - _Pragma("clang diagnostic pop") \ - }) +({ \ + __weak id target_ = (TARGET); \ + [target_ rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self]; \ +}) +#else +#define RACObserve(TARGET, KEYPATH) \ +({ \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wreceiver-is-weak\"") \ + __weak id target_ = (TARGET); \ + [target_ rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self]; \ + _Pragma("clang diagnostic pop") \ +}) +#endif @class RACDisposable; @class RACSignal; From 9401daa1d468c7124a2e190e497d35ce4c0a8633 Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Wed, 14 Sep 2016 13:59:31 +0100 Subject: [PATCH 0374/1028] Switch Nimble to master and update dependencies Nimble's swift-3.0 branch has been merged to master and deleted. --- Cartfile.private | 2 +- Cartfile.resolved | 6 +++--- Carthage/Checkouts/Nimble | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- Carthage/Checkouts/Result | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cartfile.private b/Cartfile.private index 6c9286a58b..abf3a1a9ef 100644 --- a/Cartfile.private +++ b/Cartfile.private @@ -1,3 +1,3 @@ github "jspahrsummers/xcconfigs" "3d9d996" github "Quick/Quick" "swift-3.0" -github "Quick/Nimble" "swift-3.0" +github "Quick/Nimble" "master" diff --git a/Cartfile.resolved b/Cartfile.resolved index c58b82e551..20af7ad46e 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,5 +1,5 @@ -github "Quick/Nimble" "9dc342cdffef660414d17f27aec897d0cacfffea" +github "Quick/Nimble" "fcc28b23f57b30382e5f182c238674694d7174cb" github "Quick/Quick" "8f2bc636ecfa2cc20696f62548b38d4ab943e299" -github "antitypical/Result" "3.0.0-alpha.4" +github "antitypical/Result" "3.0.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" -github "ReactiveCocoa/ReactiveSwift" "d6e4b21d01e863ae4406ea8834b12c1a6caf7d6b" +github "ReactiveCocoa/ReactiveSwift" "d4de1ff81c12124d148f726b8b44dd6887b41ae0" diff --git a/Carthage/Checkouts/Nimble b/Carthage/Checkouts/Nimble index 9dc342cdff..fcc28b23f5 160000 --- a/Carthage/Checkouts/Nimble +++ b/Carthage/Checkouts/Nimble @@ -1 +1 @@ -Subproject commit 9dc342cdffef660414d17f27aec897d0cacfffea +Subproject commit fcc28b23f57b30382e5f182c238674694d7174cb diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index d6e4b21d01..d4de1ff81c 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit d6e4b21d01e863ae4406ea8834b12c1a6caf7d6b +Subproject commit d4de1ff81c12124d148f726b8b44dd6887b41ae0 diff --git a/Carthage/Checkouts/Result b/Carthage/Checkouts/Result index cc1699dbd8..df233a8d23 160000 --- a/Carthage/Checkouts/Result +++ b/Carthage/Checkouts/Result @@ -1 +1 @@ -Subproject commit cc1699dbd812c71bee7c44c8cbd75497713bc279 +Subproject commit df233a8d23a545153d5b5de13b1ac8db2503f088 From 317056a1990379360198bc0bf90b89751e6610d3 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Wed, 14 Sep 2016 21:50:42 -0400 Subject: [PATCH 0375/1028] Add a workaround for Nimble issue --- ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift b/ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift index 708a77f839..8e2bc0c923 100644 --- a/ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift +++ b/ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift @@ -14,6 +14,13 @@ import Nimble import Quick import XCTest +// https://github.com/ReactiveCocoa/ReactiveCocoa/pull/3182#issuecomment-246866803 +extension NSDate: NMBDoubleConvertible { + public var doubleValue: CDouble { + return self.timeIntervalSinceReferenceDate + } +} + class ObjectiveCBridgingSpec: QuickSpec { override func spec() { describe("RACScheduler") { From 2227fcc43e2f752ec277f5ca0c16fe9f4c91aedf Mon Sep 17 00:00:00 2001 From: Yannick Heinrich Date: Thu, 15 Sep 2016 08:08:19 +0200 Subject: [PATCH 0376/1028] Create a common macro. I created a common macro to avoid duplication of code. --- .../Objective-C/NSObject+RACPropertySubscribing.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ReactiveCocoa/Objective-C/NSObject+RACPropertySubscribing.h b/ReactiveCocoa/Objective-C/NSObject+RACPropertySubscribing.h index 9a7c8e81b7..ccf7a907dd 100644 --- a/ReactiveCocoa/Objective-C/NSObject+RACPropertySubscribing.h +++ b/ReactiveCocoa/Objective-C/NSObject+RACPropertySubscribing.h @@ -46,19 +46,20 @@ /// Returns a signal which sends the current value of the key path on /// subscription, then sends the new value every time it changes, and sends /// completed if self or observer is deallocated. -#if __clang__ && (__clang_major__ >= 8) -#define RACObserve(TARGET, KEYPATH) \ +#define _RACObserve(TARGET, KEYPATH) \ ({ \ __weak id target_ = (TARGET); \ [target_ rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self]; \ }) + +#if __clang__ && (__clang_major__ >= 8) +#define RACObserve(TARGET, KEYPATH) _RACObserve(TARGET, KEYPATH) #else #define RACObserve(TARGET, KEYPATH) \ ({ \ _Pragma("clang diagnostic push") \ _Pragma("clang diagnostic ignored \"-Wreceiver-is-weak\"") \ - __weak id target_ = (TARGET); \ - [target_ rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self]; \ + _RACObserve(TARGET, KEYPATH) \ _Pragma("clang diagnostic pop") \ }) #endif From ebd1ead5eb112a24712788be1850d2f948f7b412 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Fri, 16 Sep 2016 22:06:18 -0400 Subject: [PATCH 0377/1028] Revert "Add a workaround for Nimble issue" This reverts commit 317056a1990379360198bc0bf90b89751e6610d3. --- ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift | 7 ------- 1 file changed, 7 deletions(-) diff --git a/ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift b/ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift index 8e2bc0c923..708a77f839 100644 --- a/ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift +++ b/ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift @@ -14,13 +14,6 @@ import Nimble import Quick import XCTest -// https://github.com/ReactiveCocoa/ReactiveCocoa/pull/3182#issuecomment-246866803 -extension NSDate: NMBDoubleConvertible { - public var doubleValue: CDouble { - return self.timeIntervalSinceReferenceDate - } -} - class ObjectiveCBridgingSpec: QuickSpec { override func spec() { describe("RACScheduler") { From 747bba927b99c5a9951270ca389986806639bb51 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Fri, 16 Sep 2016 22:07:31 -0400 Subject: [PATCH 0378/1028] Update Nimble --- Cartfile.resolved | 2 +- Carthage/Checkouts/Nimble | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cartfile.resolved b/Cartfile.resolved index 20af7ad46e..0f60c16f05 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,4 +1,4 @@ -github "Quick/Nimble" "fcc28b23f57b30382e5f182c238674694d7174cb" +github "Quick/Nimble" "db706fc1d7130f6ac96c56aaf0e635fa3217fe57" github "Quick/Quick" "8f2bc636ecfa2cc20696f62548b38d4ab943e299" github "antitypical/Result" "3.0.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" diff --git a/Carthage/Checkouts/Nimble b/Carthage/Checkouts/Nimble index fcc28b23f5..db706fc1d7 160000 --- a/Carthage/Checkouts/Nimble +++ b/Carthage/Checkouts/Nimble @@ -1 +1 @@ -Subproject commit fcc28b23f57b30382e5f182c238674694d7174cb +Subproject commit db706fc1d7130f6ac96c56aaf0e635fa3217fe57 From b89f7993d28c645d525ea5d0d9c64fdcc72eece0 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Sat, 17 Sep 2016 13:34:43 -0400 Subject: [PATCH 0379/1028] Try to work around CI failure --- ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift b/ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift index 708a77f839..f286dd30bd 100644 --- a/ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift +++ b/ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift @@ -26,7 +26,9 @@ class ObjectiveCBridgingSpec: QuickSpec { } it("gives current date") { - expect(scheduler.currentDate).to(beCloseTo(Date(), within: 0.0003)) + let expected = Date().timeIntervalSinceReferenceDate + let actual = scheduler.currentDate.timeIntervalSinceReferenceDate + expect(actual).to(beCloseTo(expected, within: 0.0003)) } it("schedules actions") { From ad7a910ecf5dbc59d4c097d9a3404d587b0e3855 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Sat, 17 Sep 2016 14:41:21 -0400 Subject: [PATCH 0380/1028] Try to get the dates closer together --- ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift b/ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift index f286dd30bd..1f87acbc5c 100644 --- a/ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift +++ b/ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift @@ -26,9 +26,9 @@ class ObjectiveCBridgingSpec: QuickSpec { } it("gives current date") { - let expected = Date().timeIntervalSinceReferenceDate - let actual = scheduler.currentDate.timeIntervalSinceReferenceDate - expect(actual).to(beCloseTo(expected, within: 0.0003)) + let expected = Date() + let actual = scheduler.currentDate + expect(actual.timeIntervalSinceReferenceDate).to(beCloseTo(expected.timeIntervalSinceReferenceDate, within: 0.0003)) } it("schedules actions") { From b99eb0cf0df5667e116167f5ae38470a4d952bb9 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Sat, 17 Sep 2016 17:24:09 -0400 Subject: [PATCH 0381/1028] Remove ReactiveObjC and ReactiveObjCBridge --- .travis.yml | 48 - Documentation/Legacy/BasicOperators.md | 364 -- Documentation/Legacy/DesignGuidelines.md | 751 --- Documentation/Legacy/FrameworkOverview.md | 203 - Documentation/Legacy/MemoryManagement.md | 139 - Documentation/Legacy/README.md | 552 --- ReactiveObjC.xcodeproj/project.pbxproj | 3158 ------------- .../xcschemes/ReactiveObjC-iOS.xcscheme | 142 - .../xcschemes/ReactiveObjC-macOS.xcscheme | 142 - .../xcschemes/ReactiveObjC-tvOS.xcscheme | 142 - .../xcschemes/ReactiveObjC-watchOS.xcscheme | 80 - .../contents.xcworkspacedata | 13 - ReactiveObjC/Info.plist | 28 - .../MKAnnotationView+RACSignalSupport.h | 29 - .../MKAnnotationView+RACSignalSupport.m | 31 - ReactiveObjC/NSArray+RACSequenceAdditions.h | 20 - ReactiveObjC/NSArray+RACSequenceAdditions.m | 18 - ReactiveObjC/NSControl+RACCommandSupport.h | 22 - ReactiveObjC/NSControl+RACCommandSupport.m | 57 - ReactiveObjC/NSControl+RACTextSignalSupport.h | 24 - ReactiveObjC/NSControl+RACTextSignalSupport.m | 38 - ReactiveObjC/NSData+RACSupport.h | 22 - ReactiveObjC/NSData+RACSupport.m | 35 - .../NSDictionary+RACSequenceAdditions.h | 31 - .../NSDictionary+RACSequenceAdditions.m | 34 - .../NSEnumerator+RACSequenceAdditions.h | 20 - .../NSEnumerator+RACSequenceAdditions.m | 22 - ReactiveObjC/NSFileHandle+RACSupport.h | 19 - ReactiveObjC/NSFileHandle+RACSupport.m | 40 - .../NSIndexSet+RACSequenceAdditions.h | 21 - .../NSIndexSet+RACSequenceAdditions.m | 18 - ReactiveObjC/NSInvocation+RACTypeParsing.h | 56 - ReactiveObjC/NSInvocation+RACTypeParsing.m | 232 - .../NSNotificationCenter+RACSupport.h | 18 - .../NSNotificationCenter+RACSupport.m | 31 - ReactiveObjC/NSObject+RACAppKitBindings.h | 40 - ReactiveObjC/NSObject+RACAppKitBindings.m | 147 - ReactiveObjC/NSObject+RACDeallocating.h | 33 - ReactiveObjC/NSObject+RACDeallocating.m | 103 - ReactiveObjC/NSObject+RACDescription.h | 16 - ReactiveObjC/NSObject+RACDescription.m | 50 - ReactiveObjC/NSObject+RACKVOWrapper.h | 46 - ReactiveObjC/NSObject+RACKVOWrapper.m | 200 - ReactiveObjC/NSObject+RACLifting.h | 57 - ReactiveObjC/NSObject+RACLifting.m | 78 - .../NSObject+RACPropertySubscribing.h | 126 - .../NSObject+RACPropertySubscribing.m | 84 - ReactiveObjC/NSObject+RACSelectorSignal.h | 79 - ReactiveObjC/NSObject+RACSelectorSignal.m | 329 -- .../NSOrderedSet+RACSequenceAdditions.h | 20 - .../NSOrderedSet+RACSequenceAdditions.m | 19 - ReactiveObjC/NSSet+RACSequenceAdditions.h | 20 - ReactiveObjC/NSSet+RACSequenceAdditions.m | 19 - ReactiveObjC/NSString+RACKeyPathUtilities.h | 34 - ReactiveObjC/NSString+RACKeyPathUtilities.m | 36 - ReactiveObjC/NSString+RACSequenceAdditions.h | 21 - ReactiveObjC/NSString+RACSequenceAdditions.m | 18 - ReactiveObjC/NSString+RACSupport.h | 22 - ReactiveObjC/NSString+RACSupport.m | 35 - ReactiveObjC/NSText+RACSignalSupport.h | 19 - ReactiveObjC/NSText+RACSignalSupport.m | 38 - ReactiveObjC/NSURLConnection+RACSupport.h | 25 - ReactiveObjC/NSURLConnection+RACSupport.m | 54 - ReactiveObjC/NSUserDefaults+RACSupport.h | 27 - ReactiveObjC/NSUserDefaults+RACSupport.m | 56 - ReactiveObjC/RACArraySequence.h | 18 - ReactiveObjC/RACArraySequence.m | 125 - ReactiveObjC/RACBehaviorSubject.h | 18 - ReactiveObjC/RACBehaviorSubject.m | 56 - ReactiveObjC/RACBlockTrampoline.h | 30 - ReactiveObjC/RACBlockTrampoline.m | 156 - ReactiveObjC/RACChannel.h | 70 - ReactiveObjC/RACChannel.m | 90 - ReactiveObjC/RACCommand.h | 127 - ReactiveObjC/RACCommand.m | 203 - ReactiveObjC/RACCompoundDisposable.h | 48 - ReactiveObjC/RACCompoundDisposable.m | 252 - ReactiveObjC/RACCompoundDisposableProvider.d | 4 - ReactiveObjC/RACDelegateProxy.h | 28 - ReactiveObjC/RACDelegateProxy.m | 76 - ReactiveObjC/RACDisposable.h | 35 - ReactiveObjC/RACDisposable.m | 92 - ReactiveObjC/RACDynamicSequence.h | 20 - ReactiveObjC/RACDynamicSequence.m | 197 - ReactiveObjC/RACDynamicSignal.h | 17 - ReactiveObjC/RACDynamicSignal.m | 54 - ReactiveObjC/RACEagerSequence.h | 14 - ReactiveObjC/RACEagerSequence.m | 66 - ReactiveObjC/RACEmptySequence.h | 14 - ReactiveObjC/RACEmptySequence.m | 71 - ReactiveObjC/RACEmptySignal.h | 17 - ReactiveObjC/RACEmptySignal.m | 62 - ReactiveObjC/RACErrorSignal.h | 17 - ReactiveObjC/RACErrorSignal.m | 47 - ReactiveObjC/RACEvent.h | 51 - ReactiveObjC/RACEvent.m | 113 - ReactiveObjC/RACGroupedSignal.h | 19 - ReactiveObjC/RACGroupedSignal.m | 25 - ReactiveObjC/RACImmediateScheduler.h | 14 - ReactiveObjC/RACImmediateScheduler.m | 54 - ReactiveObjC/RACIndexSetSequence.h | 16 - ReactiveObjC/RACIndexSetSequence.m | 111 - ReactiveObjC/RACKVOChannel.h | 102 - ReactiveObjC/RACKVOChannel.m | 208 - ReactiveObjC/RACKVOProxy.h | 34 - ReactiveObjC/RACKVOProxy.m | 70 - ReactiveObjC/RACKVOTrampoline.h | 31 - ReactiveObjC/RACKVOTrampoline.m | 110 - ReactiveObjC/RACMulticastConnection+Private.h | 17 - ReactiveObjC/RACMulticastConnection.h | 48 - ReactiveObjC/RACMulticastConnection.m | 85 - ReactiveObjC/RACPassthroughSubscriber.h | 29 - ReactiveObjC/RACPassthroughSubscriber.m | 107 - ReactiveObjC/RACQueueScheduler+Subclass.h | 40 - ReactiveObjC/RACQueueScheduler.h | 18 - ReactiveObjC/RACQueueScheduler.m | 107 - ReactiveObjC/RACReplaySubject.h | 22 - ReactiveObjC/RACReplaySubject.m | 112 - ReactiveObjC/RACReturnSignal.h | 17 - ReactiveObjC/RACReturnSignal.m | 90 - ReactiveObjC/RACScheduler+Private.h | 34 - ReactiveObjC/RACScheduler+Subclass.h | 29 - ReactiveObjC/RACScheduler.h | 148 - ReactiveObjC/RACScheduler.m | 212 - ReactiveObjC/RACScopedDisposable.h | 18 - ReactiveObjC/RACScopedDisposable.m | 32 - ReactiveObjC/RACSequence.h | 155 - ReactiveObjC/RACSequence.m | 371 -- ReactiveObjC/RACSerialDisposable.h | 43 - ReactiveObjC/RACSerialDisposable.m | 121 - ReactiveObjC/RACSignal+Operations.h | 728 --- ReactiveObjC/RACSignal+Operations.m | 1333 ------ ReactiveObjC/RACSignal.h | 223 - ReactiveObjC/RACSignal.m | 401 -- ReactiveObjC/RACSignalProvider.d | 5 - ReactiveObjC/RACSignalSequence.h | 19 - ReactiveObjC/RACSignalSequence.m | 79 - ReactiveObjC/RACStream+Private.h | 23 - ReactiveObjC/RACStream.h | 335 -- ReactiveObjC/RACStream.m | 351 -- ReactiveObjC/RACStringSequence.h | 18 - ReactiveObjC/RACStringSequence.m | 65 - ReactiveObjC/RACSubject.h | 22 - ReactiveObjC/RACSubject.m | 126 - ReactiveObjC/RACSubscriber+Private.h | 17 - ReactiveObjC/RACSubscriber.h | 51 - ReactiveObjC/RACSubscriber.m | 112 - .../RACSubscriptingAssignmentTrampoline.h | 54 - .../RACSubscriptingAssignmentTrampoline.m | 42 - ReactiveObjC/RACSubscriptionScheduler.h | 15 - ReactiveObjC/RACSubscriptionScheduler.m | 54 - ReactiveObjC/RACTargetQueueScheduler.h | 24 - ReactiveObjC/RACTargetQueueScheduler.m | 31 - ReactiveObjC/RACTestScheduler.h | 42 - ReactiveObjC/RACTestScheduler.m | 223 - ReactiveObjC/RACTuple.h | 159 - ReactiveObjC/RACTuple.m | 252 - ReactiveObjC/RACTupleSequence.h | 18 - ReactiveObjC/RACTupleSequence.m | 68 - ReactiveObjC/RACUnarySequence.h | 14 - ReactiveObjC/RACUnarySequence.m | 81 - ReactiveObjC/RACUnit.h | 19 - ReactiveObjC/RACUnit.m | 25 - ReactiveObjC/RACValueTransformer.h | 16 - ReactiveObjC/RACValueTransformer.m | 42 - ReactiveObjC/ReactiveObjC.h | 95 - ReactiveObjC/UIActionSheet+RACSignalSupport.h | 32 - ReactiveObjC/UIActionSheet+RACSignalSupport.m | 49 - ReactiveObjC/UIAlertView+RACSignalSupport.h | 47 - ReactiveObjC/UIAlertView+RACSignalSupport.m | 63 - .../UIBarButtonItem+RACCommandSupport.h | 22 - .../UIBarButtonItem+RACCommandSupport.m | 54 - ReactiveObjC/UIButton+RACCommandSupport.h | 20 - ReactiveObjC/UIButton+RACCommandSupport.m | 56 - ...ICollectionReusableView+RACSignalSupport.h | 29 - ...ICollectionReusableView+RACSignalSupport.m | 31 - ReactiveObjC/UIControl+RACSignalSupport.h | 19 - ReactiveObjC/UIControl+RACSignalSupport.m | 43 - .../UIControl+RACSignalSupportPrivate.h | 29 - .../UIControl+RACSignalSupportPrivate.m | 48 - ReactiveObjC/UIDatePicker+RACSignalSupport.h | 24 - ReactiveObjC/UIDatePicker+RACSignalSupport.m | 19 - .../UIGestureRecognizer+RACSignalSupport.h | 18 - .../UIGestureRecognizer+RACSignalSupport.m | 40 - ...UIImagePickerController+RACSignalSupport.h | 33 - ...UIImagePickerController+RACSignalSupport.m | 53 - .../UIRefreshControl+RACCommandSupport.h | 22 - .../UIRefreshControl+RACCommandSupport.m | 59 - .../UISegmentedControl+RACSignalSupport.h | 24 - .../UISegmentedControl+RACSignalSupport.m | 19 - ReactiveObjC/UISlider+RACSignalSupport.h | 24 - ReactiveObjC/UISlider+RACSignalSupport.m | 19 - ReactiveObjC/UIStepper+RACSignalSupport.h | 24 - ReactiveObjC/UIStepper+RACSignalSupport.m | 19 - ReactiveObjC/UISwitch+RACSignalSupport.h | 22 - ReactiveObjC/UISwitch+RACSignalSupport.m | 19 - .../UITableViewCell+RACSignalSupport.h | 28 - .../UITableViewCell+RACSignalSupport.m | 31 - ...bleViewHeaderFooterView+RACSignalSupport.h | 29 - ...bleViewHeaderFooterView+RACSignalSupport.m | 31 - ReactiveObjC/UITextField+RACSignalSupport.h | 28 - ReactiveObjC/UITextField+RACSignalSupport.m | 39 - ReactiveObjC/UITextView+RACSignalSupport.h | 39 - ReactiveObjC/UITextView+RACSignalSupport.m | 56 - ReactiveObjC/extobjc/EXTKeyPathCoding.h | 68 - ReactiveObjC/extobjc/EXTRuntimeExtensions.h | 122 - ReactiveObjC/extobjc/EXTRuntimeExtensions.m | 234 - ReactiveObjC/extobjc/EXTScope.h | 118 - ReactiveObjC/extobjc/metamacros.h | 666 --- ReactiveObjCBridge.xcodeproj/project.pbxproj | 1430 ------ .../xcschemes/ReactiveObjCBridge-iOS.xcscheme | 184 - .../ReactiveObjCBridge-macOS.xcscheme | 184 - .../ReactiveObjCBridge-tvOS.xcscheme | 184 - .../ReactiveObjCBridge-watchOS.xcscheme | 80 - .../contents.xcworkspacedata | 22 - ReactiveObjCBridge/Info.plist | 28 - ReactiveObjCBridge/ObjectiveCBridging.swift | 298 -- ReactiveObjCBridge/ReactiveObjCBridge.h | 15 - ReactiveObjCBridgeTests/Info.plist | 24 - .../ObjectiveCBridgingSpec.swift | 304 -- ReactiveObjCBridgeTests/TestError.swift | 43 - ReactiveObjCTests/Info.plist | 24 - .../NSControllerRACSupportSpec.m | 47 - .../NSEnumeratorRACSequenceAdditionsSpec.m | 28 - .../NSNotificationCenterRACSupportSpec.m | 87 - .../NSObjectRACAppKitBindingsSpec.m | 39 - .../NSObjectRACDeallocatingSpec.m | 198 - ReactiveObjCTests/NSObjectRACLiftingSpec.m | 410 -- .../NSObjectRACPropertySubscribingExamples.h | 16 - .../NSObjectRACPropertySubscribingExamples.m | 224 - .../NSObjectRACPropertySubscribingSpec.m | 158 - .../NSObjectRACSelectorSignalSpec.m | 501 -- .../NSStringRACKeyPathUtilitiesSpec.m | 54 - .../NSURLConnectionRACSupportSpec.m | 39 - .../NSUserDefaultsRACSupportSpec.m | 137 - ReactiveObjCTests/RACBlockTrampolineSpec.m | 51 - ReactiveObjCTests/RACChannelExamples.h | 34 - ReactiveObjCTests/RACChannelExamples.m | 299 -- ReactiveObjCTests/RACChannelSpec.m | 76 - ReactiveObjCTests/RACCommandSpec.m | 545 --- ReactiveObjCTests/RACCompoundDisposableSpec.m | 112 - ReactiveObjCTests/RACControlCommandExamples.h | 18 - ReactiveObjCTests/RACControlCommandExamples.m | 86 - ReactiveObjCTests/RACDelegateProxySpec.m | 92 - ReactiveObjCTests/RACDisposableSpec.m | 76 - ReactiveObjCTests/RACEventSpec.m | 83 - ReactiveObjCTests/RACKVOChannelSpec.m | 394 -- ReactiveObjCTests/RACKVOProxySpec.m | 237 - ReactiveObjCTests/RACKVOWrapperSpec.m | 675 --- .../RACMulticastConnectionSpec.m | 150 - ReactiveObjCTests/RACPropertySignalExamples.h | 18 - ReactiveObjCTests/RACPropertySignalExamples.m | 144 - ReactiveObjCTests/RACSchedulerSpec.m | 439 -- ReactiveObjCTests/RACSequenceAdditionsSpec.m | 359 -- ReactiveObjCTests/RACSequenceExamples.h | 16 - ReactiveObjCTests/RACSequenceExamples.m | 131 - ReactiveObjCTests/RACSequenceSpec.m | 443 -- ReactiveObjCTests/RACSerialDisposableSpec.m | 162 - ReactiveObjCTests/RACSignalSpec.m | 4080 ----------------- ReactiveObjCTests/RACStreamExamples.h | 26 - ReactiveObjCTests/RACStreamExamples.m | 670 --- ReactiveObjCTests/RACSubclassObject.h | 26 - ReactiveObjCTests/RACSubclassObject.m | 38 - ReactiveObjCTests/RACSubjectSpec.m | 366 -- ReactiveObjCTests/RACSubscriberExamples.h | 23 - ReactiveObjCTests/RACSubscriberExamples.m | 190 - ReactiveObjCTests/RACSubscriberSpec.m | 130 - .../RACSubscriptingAssignmentTrampolineSpec.m | 36 - .../RACTargetQueueSchedulerSpec.m | 49 - ReactiveObjCTests/RACTestExampleScheduler.h | 15 - ReactiveObjCTests/RACTestExampleScheduler.m | 39 - ReactiveObjCTests/RACTestObject.h | 87 - ReactiveObjCTests/RACTestObject.m | 121 - ReactiveObjCTests/RACTestSchedulerSpec.m | 178 - ReactiveObjCTests/RACTestUIButton.h | 16 - ReactiveObjCTests/RACTestUIButton.m | 27 - ReactiveObjCTests/RACTupleSpec.m | 123 - .../UIActionSheetRACSupportSpec.m | 39 - ReactiveObjCTests/UIAlertViewRACSupportSpec.m | 47 - .../UIBarButtonItemRACSupportSpec.m | 43 - ReactiveObjCTests/UIButtonRACSupportSpec.m | 42 - .../UIImagePickerControllerRACSupportSpec.m | 54 - ReactiveObjCTests/test-data.json | 5 - 283 files changed, 36807 deletions(-) delete mode 100644 Documentation/Legacy/BasicOperators.md delete mode 100644 Documentation/Legacy/DesignGuidelines.md delete mode 100644 Documentation/Legacy/FrameworkOverview.md delete mode 100644 Documentation/Legacy/MemoryManagement.md delete mode 100644 Documentation/Legacy/README.md delete mode 100644 ReactiveObjC.xcodeproj/project.pbxproj delete mode 100644 ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-iOS.xcscheme delete mode 100644 ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-macOS.xcscheme delete mode 100644 ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-tvOS.xcscheme delete mode 100644 ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-watchOS.xcscheme delete mode 100644 ReactiveObjC.xcworkspace/contents.xcworkspacedata delete mode 100644 ReactiveObjC/Info.plist delete mode 100644 ReactiveObjC/MKAnnotationView+RACSignalSupport.h delete mode 100644 ReactiveObjC/MKAnnotationView+RACSignalSupport.m delete mode 100644 ReactiveObjC/NSArray+RACSequenceAdditions.h delete mode 100644 ReactiveObjC/NSArray+RACSequenceAdditions.m delete mode 100644 ReactiveObjC/NSControl+RACCommandSupport.h delete mode 100644 ReactiveObjC/NSControl+RACCommandSupport.m delete mode 100644 ReactiveObjC/NSControl+RACTextSignalSupport.h delete mode 100644 ReactiveObjC/NSControl+RACTextSignalSupport.m delete mode 100644 ReactiveObjC/NSData+RACSupport.h delete mode 100644 ReactiveObjC/NSData+RACSupport.m delete mode 100644 ReactiveObjC/NSDictionary+RACSequenceAdditions.h delete mode 100644 ReactiveObjC/NSDictionary+RACSequenceAdditions.m delete mode 100644 ReactiveObjC/NSEnumerator+RACSequenceAdditions.h delete mode 100644 ReactiveObjC/NSEnumerator+RACSequenceAdditions.m delete mode 100644 ReactiveObjC/NSFileHandle+RACSupport.h delete mode 100644 ReactiveObjC/NSFileHandle+RACSupport.m delete mode 100644 ReactiveObjC/NSIndexSet+RACSequenceAdditions.h delete mode 100644 ReactiveObjC/NSIndexSet+RACSequenceAdditions.m delete mode 100644 ReactiveObjC/NSInvocation+RACTypeParsing.h delete mode 100644 ReactiveObjC/NSInvocation+RACTypeParsing.m delete mode 100644 ReactiveObjC/NSNotificationCenter+RACSupport.h delete mode 100644 ReactiveObjC/NSNotificationCenter+RACSupport.m delete mode 100644 ReactiveObjC/NSObject+RACAppKitBindings.h delete mode 100644 ReactiveObjC/NSObject+RACAppKitBindings.m delete mode 100644 ReactiveObjC/NSObject+RACDeallocating.h delete mode 100644 ReactiveObjC/NSObject+RACDeallocating.m delete mode 100644 ReactiveObjC/NSObject+RACDescription.h delete mode 100644 ReactiveObjC/NSObject+RACDescription.m delete mode 100644 ReactiveObjC/NSObject+RACKVOWrapper.h delete mode 100644 ReactiveObjC/NSObject+RACKVOWrapper.m delete mode 100644 ReactiveObjC/NSObject+RACLifting.h delete mode 100644 ReactiveObjC/NSObject+RACLifting.m delete mode 100644 ReactiveObjC/NSObject+RACPropertySubscribing.h delete mode 100644 ReactiveObjC/NSObject+RACPropertySubscribing.m delete mode 100644 ReactiveObjC/NSObject+RACSelectorSignal.h delete mode 100644 ReactiveObjC/NSObject+RACSelectorSignal.m delete mode 100644 ReactiveObjC/NSOrderedSet+RACSequenceAdditions.h delete mode 100644 ReactiveObjC/NSOrderedSet+RACSequenceAdditions.m delete mode 100644 ReactiveObjC/NSSet+RACSequenceAdditions.h delete mode 100644 ReactiveObjC/NSSet+RACSequenceAdditions.m delete mode 100644 ReactiveObjC/NSString+RACKeyPathUtilities.h delete mode 100644 ReactiveObjC/NSString+RACKeyPathUtilities.m delete mode 100644 ReactiveObjC/NSString+RACSequenceAdditions.h delete mode 100644 ReactiveObjC/NSString+RACSequenceAdditions.m delete mode 100644 ReactiveObjC/NSString+RACSupport.h delete mode 100644 ReactiveObjC/NSString+RACSupport.m delete mode 100644 ReactiveObjC/NSText+RACSignalSupport.h delete mode 100644 ReactiveObjC/NSText+RACSignalSupport.m delete mode 100644 ReactiveObjC/NSURLConnection+RACSupport.h delete mode 100644 ReactiveObjC/NSURLConnection+RACSupport.m delete mode 100644 ReactiveObjC/NSUserDefaults+RACSupport.h delete mode 100644 ReactiveObjC/NSUserDefaults+RACSupport.m delete mode 100644 ReactiveObjC/RACArraySequence.h delete mode 100644 ReactiveObjC/RACArraySequence.m delete mode 100644 ReactiveObjC/RACBehaviorSubject.h delete mode 100644 ReactiveObjC/RACBehaviorSubject.m delete mode 100644 ReactiveObjC/RACBlockTrampoline.h delete mode 100644 ReactiveObjC/RACBlockTrampoline.m delete mode 100644 ReactiveObjC/RACChannel.h delete mode 100644 ReactiveObjC/RACChannel.m delete mode 100644 ReactiveObjC/RACCommand.h delete mode 100644 ReactiveObjC/RACCommand.m delete mode 100644 ReactiveObjC/RACCompoundDisposable.h delete mode 100644 ReactiveObjC/RACCompoundDisposable.m delete mode 100644 ReactiveObjC/RACCompoundDisposableProvider.d delete mode 100644 ReactiveObjC/RACDelegateProxy.h delete mode 100644 ReactiveObjC/RACDelegateProxy.m delete mode 100644 ReactiveObjC/RACDisposable.h delete mode 100644 ReactiveObjC/RACDisposable.m delete mode 100644 ReactiveObjC/RACDynamicSequence.h delete mode 100644 ReactiveObjC/RACDynamicSequence.m delete mode 100644 ReactiveObjC/RACDynamicSignal.h delete mode 100644 ReactiveObjC/RACDynamicSignal.m delete mode 100644 ReactiveObjC/RACEagerSequence.h delete mode 100644 ReactiveObjC/RACEagerSequence.m delete mode 100644 ReactiveObjC/RACEmptySequence.h delete mode 100644 ReactiveObjC/RACEmptySequence.m delete mode 100644 ReactiveObjC/RACEmptySignal.h delete mode 100644 ReactiveObjC/RACEmptySignal.m delete mode 100644 ReactiveObjC/RACErrorSignal.h delete mode 100644 ReactiveObjC/RACErrorSignal.m delete mode 100644 ReactiveObjC/RACEvent.h delete mode 100644 ReactiveObjC/RACEvent.m delete mode 100644 ReactiveObjC/RACGroupedSignal.h delete mode 100644 ReactiveObjC/RACGroupedSignal.m delete mode 100644 ReactiveObjC/RACImmediateScheduler.h delete mode 100644 ReactiveObjC/RACImmediateScheduler.m delete mode 100644 ReactiveObjC/RACIndexSetSequence.h delete mode 100644 ReactiveObjC/RACIndexSetSequence.m delete mode 100644 ReactiveObjC/RACKVOChannel.h delete mode 100644 ReactiveObjC/RACKVOChannel.m delete mode 100644 ReactiveObjC/RACKVOProxy.h delete mode 100644 ReactiveObjC/RACKVOProxy.m delete mode 100644 ReactiveObjC/RACKVOTrampoline.h delete mode 100644 ReactiveObjC/RACKVOTrampoline.m delete mode 100644 ReactiveObjC/RACMulticastConnection+Private.h delete mode 100644 ReactiveObjC/RACMulticastConnection.h delete mode 100644 ReactiveObjC/RACMulticastConnection.m delete mode 100644 ReactiveObjC/RACPassthroughSubscriber.h delete mode 100644 ReactiveObjC/RACPassthroughSubscriber.m delete mode 100644 ReactiveObjC/RACQueueScheduler+Subclass.h delete mode 100644 ReactiveObjC/RACQueueScheduler.h delete mode 100644 ReactiveObjC/RACQueueScheduler.m delete mode 100644 ReactiveObjC/RACReplaySubject.h delete mode 100644 ReactiveObjC/RACReplaySubject.m delete mode 100644 ReactiveObjC/RACReturnSignal.h delete mode 100644 ReactiveObjC/RACReturnSignal.m delete mode 100644 ReactiveObjC/RACScheduler+Private.h delete mode 100644 ReactiveObjC/RACScheduler+Subclass.h delete mode 100644 ReactiveObjC/RACScheduler.h delete mode 100644 ReactiveObjC/RACScheduler.m delete mode 100644 ReactiveObjC/RACScopedDisposable.h delete mode 100644 ReactiveObjC/RACScopedDisposable.m delete mode 100644 ReactiveObjC/RACSequence.h delete mode 100644 ReactiveObjC/RACSequence.m delete mode 100644 ReactiveObjC/RACSerialDisposable.h delete mode 100644 ReactiveObjC/RACSerialDisposable.m delete mode 100644 ReactiveObjC/RACSignal+Operations.h delete mode 100644 ReactiveObjC/RACSignal+Operations.m delete mode 100644 ReactiveObjC/RACSignal.h delete mode 100644 ReactiveObjC/RACSignal.m delete mode 100644 ReactiveObjC/RACSignalProvider.d delete mode 100644 ReactiveObjC/RACSignalSequence.h delete mode 100644 ReactiveObjC/RACSignalSequence.m delete mode 100644 ReactiveObjC/RACStream+Private.h delete mode 100644 ReactiveObjC/RACStream.h delete mode 100644 ReactiveObjC/RACStream.m delete mode 100644 ReactiveObjC/RACStringSequence.h delete mode 100644 ReactiveObjC/RACStringSequence.m delete mode 100644 ReactiveObjC/RACSubject.h delete mode 100644 ReactiveObjC/RACSubject.m delete mode 100644 ReactiveObjC/RACSubscriber+Private.h delete mode 100644 ReactiveObjC/RACSubscriber.h delete mode 100644 ReactiveObjC/RACSubscriber.m delete mode 100644 ReactiveObjC/RACSubscriptingAssignmentTrampoline.h delete mode 100644 ReactiveObjC/RACSubscriptingAssignmentTrampoline.m delete mode 100644 ReactiveObjC/RACSubscriptionScheduler.h delete mode 100644 ReactiveObjC/RACSubscriptionScheduler.m delete mode 100644 ReactiveObjC/RACTargetQueueScheduler.h delete mode 100644 ReactiveObjC/RACTargetQueueScheduler.m delete mode 100644 ReactiveObjC/RACTestScheduler.h delete mode 100644 ReactiveObjC/RACTestScheduler.m delete mode 100644 ReactiveObjC/RACTuple.h delete mode 100644 ReactiveObjC/RACTuple.m delete mode 100644 ReactiveObjC/RACTupleSequence.h delete mode 100644 ReactiveObjC/RACTupleSequence.m delete mode 100644 ReactiveObjC/RACUnarySequence.h delete mode 100644 ReactiveObjC/RACUnarySequence.m delete mode 100644 ReactiveObjC/RACUnit.h delete mode 100644 ReactiveObjC/RACUnit.m delete mode 100644 ReactiveObjC/RACValueTransformer.h delete mode 100644 ReactiveObjC/RACValueTransformer.m delete mode 100644 ReactiveObjC/ReactiveObjC.h delete mode 100644 ReactiveObjC/UIActionSheet+RACSignalSupport.h delete mode 100644 ReactiveObjC/UIActionSheet+RACSignalSupport.m delete mode 100644 ReactiveObjC/UIAlertView+RACSignalSupport.h delete mode 100644 ReactiveObjC/UIAlertView+RACSignalSupport.m delete mode 100644 ReactiveObjC/UIBarButtonItem+RACCommandSupport.h delete mode 100644 ReactiveObjC/UIBarButtonItem+RACCommandSupport.m delete mode 100644 ReactiveObjC/UIButton+RACCommandSupport.h delete mode 100644 ReactiveObjC/UIButton+RACCommandSupport.m delete mode 100644 ReactiveObjC/UICollectionReusableView+RACSignalSupport.h delete mode 100644 ReactiveObjC/UICollectionReusableView+RACSignalSupport.m delete mode 100644 ReactiveObjC/UIControl+RACSignalSupport.h delete mode 100644 ReactiveObjC/UIControl+RACSignalSupport.m delete mode 100644 ReactiveObjC/UIControl+RACSignalSupportPrivate.h delete mode 100644 ReactiveObjC/UIControl+RACSignalSupportPrivate.m delete mode 100644 ReactiveObjC/UIDatePicker+RACSignalSupport.h delete mode 100644 ReactiveObjC/UIDatePicker+RACSignalSupport.m delete mode 100644 ReactiveObjC/UIGestureRecognizer+RACSignalSupport.h delete mode 100644 ReactiveObjC/UIGestureRecognizer+RACSignalSupport.m delete mode 100644 ReactiveObjC/UIImagePickerController+RACSignalSupport.h delete mode 100644 ReactiveObjC/UIImagePickerController+RACSignalSupport.m delete mode 100644 ReactiveObjC/UIRefreshControl+RACCommandSupport.h delete mode 100644 ReactiveObjC/UIRefreshControl+RACCommandSupport.m delete mode 100644 ReactiveObjC/UISegmentedControl+RACSignalSupport.h delete mode 100644 ReactiveObjC/UISegmentedControl+RACSignalSupport.m delete mode 100644 ReactiveObjC/UISlider+RACSignalSupport.h delete mode 100644 ReactiveObjC/UISlider+RACSignalSupport.m delete mode 100644 ReactiveObjC/UIStepper+RACSignalSupport.h delete mode 100644 ReactiveObjC/UIStepper+RACSignalSupport.m delete mode 100644 ReactiveObjC/UISwitch+RACSignalSupport.h delete mode 100644 ReactiveObjC/UISwitch+RACSignalSupport.m delete mode 100644 ReactiveObjC/UITableViewCell+RACSignalSupport.h delete mode 100644 ReactiveObjC/UITableViewCell+RACSignalSupport.m delete mode 100644 ReactiveObjC/UITableViewHeaderFooterView+RACSignalSupport.h delete mode 100644 ReactiveObjC/UITableViewHeaderFooterView+RACSignalSupport.m delete mode 100644 ReactiveObjC/UITextField+RACSignalSupport.h delete mode 100644 ReactiveObjC/UITextField+RACSignalSupport.m delete mode 100644 ReactiveObjC/UITextView+RACSignalSupport.h delete mode 100644 ReactiveObjC/UITextView+RACSignalSupport.m delete mode 100644 ReactiveObjC/extobjc/EXTKeyPathCoding.h delete mode 100644 ReactiveObjC/extobjc/EXTRuntimeExtensions.h delete mode 100644 ReactiveObjC/extobjc/EXTRuntimeExtensions.m delete mode 100644 ReactiveObjC/extobjc/EXTScope.h delete mode 100644 ReactiveObjC/extobjc/metamacros.h delete mode 100644 ReactiveObjCBridge.xcodeproj/project.pbxproj delete mode 100644 ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-iOS.xcscheme delete mode 100644 ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-macOS.xcscheme delete mode 100644 ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-tvOS.xcscheme delete mode 100644 ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-watchOS.xcscheme delete mode 100644 ReactiveObjCBridge.xcworkspace/contents.xcworkspacedata delete mode 100644 ReactiveObjCBridge/Info.plist delete mode 100644 ReactiveObjCBridge/ObjectiveCBridging.swift delete mode 100644 ReactiveObjCBridge/ReactiveObjCBridge.h delete mode 100644 ReactiveObjCBridgeTests/Info.plist delete mode 100644 ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift delete mode 100644 ReactiveObjCBridgeTests/TestError.swift delete mode 100644 ReactiveObjCTests/Info.plist delete mode 100644 ReactiveObjCTests/NSControllerRACSupportSpec.m delete mode 100644 ReactiveObjCTests/NSEnumeratorRACSequenceAdditionsSpec.m delete mode 100644 ReactiveObjCTests/NSNotificationCenterRACSupportSpec.m delete mode 100644 ReactiveObjCTests/NSObjectRACAppKitBindingsSpec.m delete mode 100644 ReactiveObjCTests/NSObjectRACDeallocatingSpec.m delete mode 100644 ReactiveObjCTests/NSObjectRACLiftingSpec.m delete mode 100644 ReactiveObjCTests/NSObjectRACPropertySubscribingExamples.h delete mode 100644 ReactiveObjCTests/NSObjectRACPropertySubscribingExamples.m delete mode 100644 ReactiveObjCTests/NSObjectRACPropertySubscribingSpec.m delete mode 100644 ReactiveObjCTests/NSObjectRACSelectorSignalSpec.m delete mode 100644 ReactiveObjCTests/NSStringRACKeyPathUtilitiesSpec.m delete mode 100644 ReactiveObjCTests/NSURLConnectionRACSupportSpec.m delete mode 100644 ReactiveObjCTests/NSUserDefaultsRACSupportSpec.m delete mode 100644 ReactiveObjCTests/RACBlockTrampolineSpec.m delete mode 100644 ReactiveObjCTests/RACChannelExamples.h delete mode 100644 ReactiveObjCTests/RACChannelExamples.m delete mode 100644 ReactiveObjCTests/RACChannelSpec.m delete mode 100644 ReactiveObjCTests/RACCommandSpec.m delete mode 100644 ReactiveObjCTests/RACCompoundDisposableSpec.m delete mode 100644 ReactiveObjCTests/RACControlCommandExamples.h delete mode 100644 ReactiveObjCTests/RACControlCommandExamples.m delete mode 100644 ReactiveObjCTests/RACDelegateProxySpec.m delete mode 100644 ReactiveObjCTests/RACDisposableSpec.m delete mode 100644 ReactiveObjCTests/RACEventSpec.m delete mode 100644 ReactiveObjCTests/RACKVOChannelSpec.m delete mode 100644 ReactiveObjCTests/RACKVOProxySpec.m delete mode 100644 ReactiveObjCTests/RACKVOWrapperSpec.m delete mode 100644 ReactiveObjCTests/RACMulticastConnectionSpec.m delete mode 100644 ReactiveObjCTests/RACPropertySignalExamples.h delete mode 100644 ReactiveObjCTests/RACPropertySignalExamples.m delete mode 100644 ReactiveObjCTests/RACSchedulerSpec.m delete mode 100644 ReactiveObjCTests/RACSequenceAdditionsSpec.m delete mode 100644 ReactiveObjCTests/RACSequenceExamples.h delete mode 100644 ReactiveObjCTests/RACSequenceExamples.m delete mode 100644 ReactiveObjCTests/RACSequenceSpec.m delete mode 100644 ReactiveObjCTests/RACSerialDisposableSpec.m delete mode 100644 ReactiveObjCTests/RACSignalSpec.m delete mode 100644 ReactiveObjCTests/RACStreamExamples.h delete mode 100644 ReactiveObjCTests/RACStreamExamples.m delete mode 100644 ReactiveObjCTests/RACSubclassObject.h delete mode 100644 ReactiveObjCTests/RACSubclassObject.m delete mode 100644 ReactiveObjCTests/RACSubjectSpec.m delete mode 100644 ReactiveObjCTests/RACSubscriberExamples.h delete mode 100644 ReactiveObjCTests/RACSubscriberExamples.m delete mode 100644 ReactiveObjCTests/RACSubscriberSpec.m delete mode 100644 ReactiveObjCTests/RACSubscriptingAssignmentTrampolineSpec.m delete mode 100644 ReactiveObjCTests/RACTargetQueueSchedulerSpec.m delete mode 100644 ReactiveObjCTests/RACTestExampleScheduler.h delete mode 100644 ReactiveObjCTests/RACTestExampleScheduler.m delete mode 100644 ReactiveObjCTests/RACTestObject.h delete mode 100644 ReactiveObjCTests/RACTestObject.m delete mode 100644 ReactiveObjCTests/RACTestSchedulerSpec.m delete mode 100644 ReactiveObjCTests/RACTestUIButton.h delete mode 100644 ReactiveObjCTests/RACTestUIButton.m delete mode 100644 ReactiveObjCTests/RACTupleSpec.m delete mode 100644 ReactiveObjCTests/UIActionSheetRACSupportSpec.m delete mode 100644 ReactiveObjCTests/UIAlertViewRACSupportSpec.m delete mode 100644 ReactiveObjCTests/UIBarButtonItemRACSupportSpec.m delete mode 100644 ReactiveObjCTests/UIButtonRACSupportSpec.m delete mode 100644 ReactiveObjCTests/UIImagePickerControllerRACSupportSpec.m delete mode 100644 ReactiveObjCTests/test-data.json diff --git a/.travis.yml b/.travis.yml index 8763dc672d..5056f2bf63 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,54 +32,6 @@ matrix: - XCODE_SDK=watchsimulator - XCODE_ACTION=build - XCODE_DESTINATION="platform=watchOS Simulator,name=Apple Watch - 38mm" - - xcode_scheme: ReactiveObjC-macOS - xcode_workspace: ReactiveObjC.xcworkspace - env: - - XCODE_SDK=macosx - - XCODE_ACTION="build test" - - XCODE_DESTINATION="arch=x86_64" - - xcode_scheme: ReactiveObjC-iOS - xcode_workspace: ReactiveObjC.xcworkspace - env: - - XCODE_SDK=iphonesimulator - - XCODE_ACTION="build-for-testing test-without-building" - - XCODE_DESTINATION="platform=iOS Simulator,name=iPhone 6s" - - xcode_scheme: ReactiveObjC-tvOS - xcode_workspace: ReactiveObjC.xcworkspace - env: - - XCODE_SDK=appletvsimulator - - XCODE_ACTION="build-for-testing test-without-building" - - XCODE_DESTINATION="platform=tvOS Simulator,name=Apple TV 1080p" - - xcode_scheme: ReactiveObjC-watchOS - xcode_workspace: ReactiveObjC.xcworkspace - env: - - XCODE_SDK=watchsimulator - - XCODE_ACTION=build - - XCODE_DESTINATION="platform=watchOS Simulator,name=Apple Watch - 38mm" - - xcode_scheme: ReactiveObjCBridge-macOS - xcode_workspace: ReactiveObjCBridge.xcworkspace - env: - - XCODE_SDK=macosx - - XCODE_ACTION="build test" - - XCODE_DESTINATION="arch=x86_64" - - xcode_scheme: ReactiveObjCBridge-iOS - xcode_workspace: ReactiveObjCBridge.xcworkspace - env: - - XCODE_SDK=iphonesimulator - - XCODE_ACTION="build-for-testing test-without-building" - - XCODE_DESTINATION="platform=iOS Simulator,name=iPhone 6s" - - xcode_scheme: ReactiveObjCBridge-tvOS - xcode_workspace: ReactiveObjCBridge.xcworkspace - env: - - XCODE_SDK=appletvsimulator - - XCODE_ACTION="build-for-testing test-without-building" - - XCODE_DESTINATION="platform=tvOS Simulator,name=Apple TV 1080p" - - xcode_scheme: ReactiveObjCBridge-watchOS - xcode_workspace: ReactiveObjCBridge.xcworkspace - env: - - XCODE_SDK=watchsimulator - - XCODE_ACTION=build - - XCODE_DESTINATION="platform=watchOS Simulator,name=Apple Watch - 38mm" - script: - brew update - brew outdated carthage || brew upgrade carthage diff --git a/Documentation/Legacy/BasicOperators.md b/Documentation/Legacy/BasicOperators.md deleted file mode 100644 index 5e4e1f3bab..0000000000 --- a/Documentation/Legacy/BasicOperators.md +++ /dev/null @@ -1,364 +0,0 @@ -# Basic Operators - -This document explains some of the most common operators used in ReactiveCocoa, -and includes examples demonstrating their use. - -Operators that apply to [sequences][Sequences] _and_ [signals][Signals] are -known as [stream][Streams] operators. - -**[Performing side effects with signals](#performing-side-effects-with-signals)** - - 1. [Subscription](#subscription) - 1. [Injecting effects](#injecting-effects) - -**[Transforming streams](#transforming-streams)** - - 1. [Mapping](#mapping) - 1. [Filtering](#filtering) - -**[Combining streams](#combining-streams)** - - 1. [Concatenating](#concatenating) - 1. [Flattening](#flattening) - 1. [Mapping and flattening](#mapping-and-flattening) - -**[Combining signals](#combining-signals)** - - 1. [Sequencing](#sequencing) - 1. [Merging](#merging) - 1. [Combining latest values](#combining-latest-values) - 1. [Switching](#switching) - -## Performing side effects with signals - -Most signals start out "cold," which means that they will not do any work until -[subscription](#subscription). - -Upon subscription, a signal or its [subscribers][Subscription] can perform _side -effects_, like logging to the console, making a network request, updating the -user interface, etc. - -Side effects can also be [injected](#injecting-effects) into a signal, where -they won't be performed immediately, but will instead take effect with each -subscription later. - -### Subscription - -The [-subscribe…][RACSignal] methods give you access to the current and future values in a signal: - -```objc -RACSignal *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence.signal; - -// Outputs: A B C D E F G H I -[letters subscribeNext:^(NSString *x) { - NSLog(@"%@", x); -}]; -``` - -For a cold signal, side effects will be performed once _per subscription_: - -```objc -__block unsigned subscriptions = 0; - -RACSignal *loggingSignal = [RACSignal createSignal:^ RACDisposable * (id subscriber) { - subscriptions++; - [subscriber sendCompleted]; - return nil; -}]; - -// Outputs: -// subscription 1 -[loggingSignal subscribeCompleted:^{ - NSLog(@"subscription %u", subscriptions); -}]; - -// Outputs: -// subscription 2 -[loggingSignal subscribeCompleted:^{ - NSLog(@"subscription %u", subscriptions); -}]; -``` - -This behavior can be changed using a [connection][Connections]. - -### Injecting effects - -The [-do…][RACSignal+Operations] methods add side effects to a signal without actually -subscribing to it: - -```objc -__block unsigned subscriptions = 0; - -RACSignal *loggingSignal = [RACSignal createSignal:^ RACDisposable * (id subscriber) { - subscriptions++; - [subscriber sendCompleted]; - return nil; -}]; - -// Does not output anything yet -loggingSignal = [loggingSignal doCompleted:^{ - NSLog(@"about to complete subscription %u", subscriptions); -}]; - -// Outputs: -// about to complete subscription 1 -// subscription 1 -[loggingSignal subscribeCompleted:^{ - NSLog(@"subscription %u", subscriptions); -}]; -``` - -## Transforming streams - -These operators transform a single stream into a new stream. - -### Mapping - -The [-map:][RACStream] method is used to transform the values in a stream, and -create a new stream with the results: - -```objc -RACSequence *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence; - -// Contains: AA BB CC DD EE FF GG HH II -RACSequence *mapped = [letters map:^(NSString *value) { - return [value stringByAppendingString:value]; -}]; -``` - -### Filtering - -The [-filter:][RACStream] method uses a block to test each value, including it -into the resulting stream only if the test passes: - -```objc -RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence; - -// Contains: 2 4 6 8 -RACSequence *filtered = [numbers filter:^ BOOL (NSString *value) { - return (value.intValue % 2) == 0; -}]; -``` - -## Combining streams - -These operators combine multiple streams into a single new stream. - -### Concatenating - -The [-concat:][RACStream] method appends one stream's values to another: - -```objc -RACSequence *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence; -RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence; - -// Contains: A B C D E F G H I 1 2 3 4 5 6 7 8 9 -RACSequence *concatenated = [letters concat:numbers]; -``` - -### Flattening - -The [-flatten][RACStream] operator is applied to a stream-of-streams, and -combines their values into a single new stream. - -Sequences are [concatenated](#concatenating): - -```objc -RACSequence *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence; -RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence; -RACSequence *sequenceOfSequences = @[ letters, numbers ].rac_sequence; - -// Contains: A B C D E F G H I 1 2 3 4 5 6 7 8 9 -RACSequence *flattened = [sequenceOfSequences flatten]; -``` - -Signals are [merged](#merging): - -```objc -RACSubject *letters = [RACSubject subject]; -RACSubject *numbers = [RACSubject subject]; -RACSignal *signalOfSignals = [RACSignal createSignal:^ RACDisposable * (id subscriber) { - [subscriber sendNext:letters]; - [subscriber sendNext:numbers]; - [subscriber sendCompleted]; - return nil; -}]; - -RACSignal *flattened = [signalOfSignals flatten]; - -// Outputs: A 1 B C 2 -[flattened subscribeNext:^(NSString *x) { - NSLog(@"%@", x); -}]; - -[letters sendNext:@"A"]; -[numbers sendNext:@"1"]; -[letters sendNext:@"B"]; -[letters sendNext:@"C"]; -[numbers sendNext:@"2"]; -``` - -### Mapping and flattening - -[Flattening](#flattening) isn't that interesting on its own, but understanding -how it works is important for [-flattenMap:][RACStream]. - -`-flattenMap:` is used to transform each of a stream's values into _a new -stream_. Then, all of the streams returned will be flattened down into a single -stream. In other words, it's [-map:](#mapping) followed by [-flatten](#flattening). - -This can be used to extend or edit sequences: - -```objc -RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence; - -// Contains: 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 -RACSequence *extended = [numbers flattenMap:^(NSString *num) { - return @[ num, num ].rac_sequence; -}]; - -// Contains: 1_ 3_ 5_ 7_ 9_ -RACSequence *edited = [numbers flattenMap:^(NSString *num) { - if (num.intValue % 2 == 0) { - return [RACSequence empty]; - } else { - NSString *newNum = [num stringByAppendingString:@"_"]; - return [RACSequence return:newNum]; - } -}]; -``` - -Or create multiple signals of work which are automatically recombined: - -```objc -RACSignal *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence.signal; - -[[letters - flattenMap:^(NSString *letter) { - return [database saveEntriesForLetter:letter]; - }] - subscribeCompleted:^{ - NSLog(@"All database entries saved successfully."); - }]; -``` - -## Combining signals - -These operators combine multiple signals into a single new [RACSignal][]. - -### Sequencing - -[-then:][RACSignal+Operations] starts the original signal, -waits for it to complete, and then only forwards the values from a new signal: - -```objc -RACSignal *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence.signal; - -// The new signal only contains: 1 2 3 4 5 6 7 8 9 -// -// But when subscribed to, it also outputs: A B C D E F G H I -RACSignal *sequenced = [[letters - doNext:^(NSString *letter) { - NSLog(@"%@", letter); - }] - then:^{ - return [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence.signal; - }]; -``` - -This is most useful for executing all the side effects of one signal, then -starting another, and only returning the second signal's values. - -### Merging - -The [+merge:][RACSignal+Operations] method will forward the values from many -signals into a single stream, as soon as those values arrive: - -```objc -RACSubject *letters = [RACSubject subject]; -RACSubject *numbers = [RACSubject subject]; -RACSignal *merged = [RACSignal merge:@[ letters, numbers ]]; - -// Outputs: A 1 B C 2 -[merged subscribeNext:^(NSString *x) { - NSLog(@"%@", x); -}]; - -[letters sendNext:@"A"]; -[numbers sendNext:@"1"]; -[letters sendNext:@"B"]; -[letters sendNext:@"C"]; -[numbers sendNext:@"2"]; -``` - -### Combining latest values - -The [+combineLatest:][RACSignal+Operations] and `+combineLatest:reduce:` methods -will watch multiple signals for changes, and then send the latest values from -_all_ of them when a change occurs: - -```objc -RACSubject *letters = [RACSubject subject]; -RACSubject *numbers = [RACSubject subject]; -RACSignal *combined = [RACSignal - combineLatest:@[ letters, numbers ] - reduce:^(NSString *letter, NSString *number) { - return [letter stringByAppendingString:number]; - }]; - -// Outputs: B1 B2 C2 C3 -[combined subscribeNext:^(id x) { - NSLog(@"%@", x); -}]; - -[letters sendNext:@"A"]; -[letters sendNext:@"B"]; -[numbers sendNext:@"1"]; -[numbers sendNext:@"2"]; -[letters sendNext:@"C"]; -[numbers sendNext:@"3"]; -``` - -Note that the combined signal will only send its first value when all of the -inputs have sent at least one. In the example above, `@"A"` was never -forwarded because `numbers` had not sent a value yet. - -### Switching - -The [-switchToLatest][RACSignal+Operations] operator is applied to -a signal-of-signals, and always forwards the values from the latest signal: - -```objc -RACSubject *letters = [RACSubject subject]; -RACSubject *numbers = [RACSubject subject]; -RACSubject *signalOfSignals = [RACSubject subject]; - -RACSignal *switched = [signalOfSignals switchToLatest]; - -// Outputs: A B 1 D -[switched subscribeNext:^(NSString *x) { - NSLog(@"%@", x); -}]; - -[signalOfSignals sendNext:letters]; -[letters sendNext:@"A"]; -[letters sendNext:@"B"]; - -[signalOfSignals sendNext:numbers]; -[letters sendNext:@"C"]; -[numbers sendNext:@"1"]; - -[signalOfSignals sendNext:letters]; -[numbers sendNext:@"2"]; -[letters sendNext:@"D"]; -``` - -[Connections]: FrameworkOverview.md#connections -[RACSequence]: ../../ReactiveCocoa/Objective-C/RACSequence.h -[RACSignal]: ../../ReactiveCocoa/Objective-C/RACSignal.h -[RACSignal+Operations]: ../../ReactiveCocoa/Objective-C/RACSignal+Operations.h -[RACStream]: ../../ReactiveCocoa/Objective-C/RACStream.h -[Sequences]: FrameworkOverview.md#sequences -[Signals]: FrameworkOverview.md#signals -[Streams]: FrameworkOverview.md#streams -[Subscription]: FrameworkOverview.md#subscription diff --git a/Documentation/Legacy/DesignGuidelines.md b/Documentation/Legacy/DesignGuidelines.md deleted file mode 100644 index 8198f73e69..0000000000 --- a/Documentation/Legacy/DesignGuidelines.md +++ /dev/null @@ -1,751 +0,0 @@ -# Design Guidelines - -This document contains guidelines for projects that want to make use of -ReactiveCocoa. The content here is heavily inspired by the [Rx Design -Guidelines](http://blogs.msdn.com/b/rxteam/archive/2010/10/28/rx-design-guidelines.aspx). - -This document assumes basic familiarity -with the features of ReactiveCocoa. The [Framework Overview][] is a better -resource for getting up to speed on the functionality provided by RAC. - -**[The RACSequence contract](#the-racsequence-contract)** - - 1. [Evaluation occurs lazily by default](#evaluation-occurs-lazily-by-default) - 1. [Evaluation blocks the caller](#evaluation-blocks-the-caller) - 1. [Side effects occur only once](#side-effects-occur-only-once) - -**[The RACSignal contract](#the-racsignal-contract)** - - 1. [Signal events are serialized](#signal-events-are-serialized) - 1. [Subscription will always occur on a scheduler](#subscription-will-always-occur-on-a-scheduler) - 1. [Errors are propagated immediately](#errors-are-propagated-immediately) - 1. [Side effects occur for each subscription](#side-effects-occur-for-each-subscription) - 1. [Subscriptions are automatically disposed upon completion or error](#subscriptions-are-automatically-disposed-upon-completion-or-error) - 1. [Disposal cancels in-progress work and cleans up resources](#disposal-cancels-in-progress-work-and-cleans-up-resources) - -**[Best practices](#best-practices)** - - 1. [Use descriptive declarations for methods and properties that return a signal](#use-descriptive-declarations-for-methods-and-properties-that-return-a-signal) - 1. [Indent stream operations consistently](#indent-stream-operations-consistently) - 1. [Use the same type for all the values of a stream](#use-the-same-type-for-all-the-values-of-a-stream) - 1. [Avoid retaining streams for too long](#avoid-retaining-streams-for-too-long) - 1. [Process only as much of a stream as needed](#process-only-as-much-of-a-stream-as-needed) - 1. [Deliver signal events onto a known scheduler](#deliver-signal-events-onto-a-known-scheduler) - 1. [Switch schedulers in as few places as possible](#switch-schedulers-in-as-few-places-as-possible) - 1. [Make the side effects of a signal explicit](#make-the-side-effects-of-a-signal-explicit) - 1. [Share the side effects of a signal by multicasting](#share-the-side-effects-of-a-signal-by-multicasting) - 1. [Debug streams by giving them names](#debug-streams-by-giving-them-names) - 1. [Avoid explicit subscriptions and disposal](#avoid-explicit-subscriptions-and-disposal) - 1. [Avoid using subjects when possible](#avoid-using-subjects-when-possible) - -**[Implementing new operators](#implementing-new-operators)** - - 1. [Prefer building on RACStream methods](#prefer-building-on-racstream-methods) - 1. [Compose existing operators when possible](#compose-existing-operators-when-possible) - 1. [Avoid introducing concurrency](#avoid-introducing-concurrency) - 1. [Cancel work and clean up all resources in a disposable](#cancel-work-and-clean-up-all-resources-in-a-disposable) - 1. [Do not block in an operator](#do-not-block-in-an-operator) - 1. [Avoid stack overflow from deep recursion](#avoid-stack-overflow-from-deep-recursion) - -## The RACSequence contract - -[RACSequence][] is a _pull-driven_ stream. Sequences behave similarly to -built-in collections, but with a few unique twists. - -### Evaluation occurs lazily by default - -Sequences are evaluated lazily by default. For example, in this sequence: - -```objc -NSArray *strings = @[ @"A", @"B", @"C" ]; -RACSequence *sequence = [strings.rac_sequence map:^(NSString *str) { - return [str stringByAppendingString:@"_"]; -}]; -``` - -… no string appending is actually performed until the values of the sequence are -needed. Accessing `sequence.head` will perform the concatenation of `A_`, -accessing `sequence.tail.head` will perform the concatenation of `B_`, and so -on. - -This generally avoids performing unnecessary work (since values that are never -used are never calculated), but means that sequence processing [should be -limited only to what's actually -needed](#process-only-as-much-of-a-stream-as-needed). - -Once evaluated, the values in a sequence are memoized and do not need to be -recalculated. Accessing `sequence.head` multiple times will only do the work of -one string concatenation. - -If lazy evaluation is undesirable – for instance, because limiting memory usage -is more important than avoiding unnecessary work – the -[eagerSequence][RACSequence] property can be used to force a sequence (and any -sequences derived from it afterward) to evaluate eagerly. - -### Evaluation blocks the caller - -Regardless of whether a sequence is lazy or eager, evaluation of any part of -a sequence will block the calling thread until completed. This is necessary -because values must be synchronously retrieved from a sequence. - -If evaluating a sequence is expensive enough that it might block the thread for -a significant amount of time, consider creating a signal with -[-signalWithScheduler:][RACSequence] and using that instead. - -### Side effects occur only once - -When the block passed to a sequence operator involves side effects, it is -important to realize that those side effects will only occur once per value -– namely, when the value is evaluated: - -```objc -NSArray *strings = @[ @"A", @"B", @"C" ]; -RACSequence *sequence = [strings.rac_sequence map:^(NSString *str) { - NSLog(@"%@", str); - return [str stringByAppendingString:@"_"]; -}]; - -// Logs "A" during this call. -NSString *concatA = sequence.head; - -// Logs "B" during this call. -NSString *concatB = sequence.tail.head; - -// Does not log anything. -NSString *concatB2 = sequence.tail.head; - -RACSequence *derivedSequence = [sequence map:^(NSString *str) { - return [@"_" stringByAppendingString:str]; -}]; - -// Still does not log anything, because "B_" was already evaluated, and the log -// statement associated with it will never be re-executed. -NSString *concatB3 = derivedSequence.tail.head; -``` - -## The RACSignal contract - -[RACSignal][] is a _push-driven_ stream with a focus on asynchronous event -delivery through _subscriptions_. For more information about signals and -subscriptions, see the [Framework Overview][]. - -### Signal events are serialized - -A signal may choose to deliver its events on any thread. Consecutive events are -even allowed to arrive on different threads or schedulers, unless explicitly -[delivered onto a particular -scheduler](#deliver-signal-events-onto-a-known-scheduler). - -However, RAC guarantees that no two signal events will ever arrive concurrently. -While an event is being processed, no other events will be delivered. The -senders of any other events will be forced to wait until the current event has -been handled. - -Most notably, this means that the blocks passed to -[-subscribeNext:error:completed:][RACSignal] do not need to be synchronized with -respect to each other, because they will never be invoked simultaneously. - -### Subscription will always occur on a scheduler - -To ensure consistent behavior for the `+createSignal:` and `-subscribe:` -methods, each [RACSignal][] subscription is guaranteed to take place on -a valid [RACScheduler][]. - -If the subscriber's thread already has a [+currentScheduler][RACScheduler], -scheduling takes place immediately; otherwise, scheduling occurs as soon as -possible on a background scheduler. Note that the main thread is always -associated with the [+mainThreadScheduler][RACScheduler], so subscription will -always be immediate there. - -See the documentation for [-subscribe:][RACSignal] for more information. - -### Errors are propagated immediately - -In RAC, `error` events have exception semantics. When an error is sent on -a signal, it will be immediately forwarded to all dependent signals, causing the -entire chain to terminate. - -[Operators][RACSignal+Operations] whose primary purpose is to change -error-handling behavior – like `-catch:`, `-catchTo:`, or `-materialize` – are -obviously not subject to this rule. - -### Side effects occur for each subscription - -Each new subscription to a [RACSignal][] will trigger its side effects. This -means that any side effects will happen as many times as subscriptions to the -signal itself. - -Consider this example: -```objc -__block int aNumber = 0; - -// Signal that will have the side effect of incrementing `aNumber` block -// variable for each subscription before sending it. -RACSignal *aSignal = [RACSignal createSignal:^ RACDisposable * (id subscriber) { - aNumber++; - [subscriber sendNext:@(aNumber)]; - [subscriber sendCompleted]; - return nil; -}]; - -// This will print "subscriber one: 1" -[aSignal subscribeNext:^(id x) { - NSLog(@"subscriber one: %@", x); -}]; - -// This will print "subscriber two: 2" -[aSignal subscribeNext:^(id x) { - NSLog(@"subscriber two: %@", x); -}]; -``` - -Side effects are repeated for each subscription. The same applies to -[stream][RACStream] and [signal][RACSignal+Operations] operators: - -```objc -__block int missilesToLaunch = 0; - -// Signal that will have the side effect of changing `missilesToLaunch` on -// subscription. -RACSignal *processedSignal = [[RACSignal - return:@"missiles"] - map:^(id x) { - missilesToLaunch++; - return [NSString stringWithFormat:@"will launch %d %@", missilesToLaunch, x]; - }]; - -// This will print "First will launch 1 missiles" -[processedSignal subscribeNext:^(id x) { - NSLog(@"First %@", x); -}]; - -// This will print "Second will launch 2 missiles" -[processedSignal subscribeNext:^(id x) { - NSLog(@"Second %@", x); -}]; -``` - -To suppress this behavior and have multiple subscriptions to a signal execute -its side effects only once, a signal can be -[multicasted](#share-the-side-effects-of-a-signal-by-multicasting). - -Side effects can be insidious and produce problems that are difficult to -diagnose. For this reason it is suggested to -[make side effects explicit](#make-the-side-effects-of-a-signal-explicit) when -possible. - -### Subscriptions are automatically disposed upon completion or error - -When a [subscriber][RACSubscriber] is sent a `completed` or `error` event, the -associated subscription will automatically be disposed. This behavior usually -eliminates the need to manually dispose of subscriptions. - -See the [Memory Management][] document for more information about signal -lifetime. - -### Disposal cancels in-progress work and cleans up resources - -When a subscription is disposed, manually or automatically, any in-progress or -outstanding work associated with that subscription is gracefully cancelled as -soon as possible, and any resources associated with the subscription are cleaned -up. - -Disposing of the subscription to a signal representing a file upload, for -example, would cancel any in-flight network request, and free the file data from -memory. - -## Best practices - -The following recommendations are intended to help keep RAC-based code -predictable, understandable, and performant. - -They are, however, only guidelines. Use best judgement when determining whether -to apply the recommendations here to a given piece of code. - -### Use descriptive declarations for methods and properties that return a signal - -When a method or property has a return type of [RACSignal][], it can be -difficult to understand the signal's semantics at a glance. - -There are three key questions that can inform a declaration: - - 1. Is the signal _hot_ (already activated by the time it's returned to the - caller) or _cold_ (activated when subscribed to)? - 1. Will the signal include zero, one, or more values? - 1. Does the signal have side effects? - -**Hot signals without side effects** should typically be properties instead of -methods. The use of a property indicates that no initialization is needed before -subscribing to the signal's events, and that additional subscribers will not -change the semantics. Signal properties should usually be named after events -(e.g., `textChanged`). - -**Cold signals without side effects** should be returned from methods that have -noun-like names (e.g., `-currentText`). A method declaration indicates that the -signal might not be kept around, hinting that work is performed at the time of -subscription. If the signal sends multiple values, the noun should be pluralized -(e.g., `-currentModels`). - -**Signals with side effects** should be returned from methods that have -verb-like names (e.g., `-logIn`). The verb indicates that the method is not -idempotent and that callers must be careful to call it only when the side -effects are desired. If the signal will send one or more values, include a noun -that describes them (e.g., `-loadConfiguration`, `-fetchLatestEvents`). - -### Indent stream operations consistently - -It's easy for stream-heavy code to become very dense and confusing if not -properly formatted. Use consistent indentation to highlight where chains of -streams begin and end. - -When invoking a single method upon a stream, no additional indentation is -necessary (block arguments aside): - -```objc -RACStream *result = [stream startWith:@0]; - -RACStream *result2 = [stream map:^(NSNumber *value) { - return @(value.integerValue + 1); -}]; -``` - -When transforming the same stream multiple times, ensure that all of the -steps are aligned. Complex operators like [+zip:reduce:][RACStream] or -[+combineLatest:reduce:][RACSignal+Operations] may be split over multiple lines -for readability: - -```objc -RACStream *result = [[[RACStream - zip:@[ firstStream, secondStream ] - reduce:^(NSNumber *first, NSNumber *second) { - return @(first.integerValue + second.integerValue); - }] - filter:^ BOOL (NSNumber *value) { - return value.integerValue >= 0; - }] - map:^(NSNumber *value) { - return @(value.integerValue + 1); - }]; -``` - -Of course, streams nested within block arguments should start at the natural -indentation of the block: - -```objc -[[signal - then:^{ - @strongify(self); - - return [[self - doSomethingElse] - catch:^(NSError *error) { - @strongify(self); - [self presentError:error]; - - return [RACSignal empty]; - }]; - }] - subscribeCompleted:^{ - NSLog(@"All done."); - }]; -``` - -### Use the same type for all the values of a stream - -[RACStream][] (and, by extension, [RACSignal][] and [RACSequence][]) allows -streams to be composed of heterogenous objects, just like Cocoa collections do. -However, using different object types within the same stream complicates the use -of operators and -puts an additional burden on any consumers of that stream, who must be careful to -only invoke supported methods. - -Whenever possible, streams should only contain objects of the same type. - -### Avoid retaining streams for too long - -Retaining any [RACStream][] longer than it's needed will cause any dependencies -to be retained as well, potentially keeping memory usage much higher than it -would be otherwise. - -A [RACSequence][] should be retained only for as long as the `head` of the -sequence is needed. If the head will no longer be used, retain the `tail` of the -node instead of the node itself. - -See the [Memory Management][] guide for more information on object lifetime. - -### Process only as much of a stream as needed - -As well as [consuming additional -memory](#avoid-retaining-streams-for-too-long), unnecessarily -keeping a stream or [RACSignal][] subscription alive can result in increased CPU -usage, as unnecessary work is performed for results that will never be used. - -If only a certain number of values are needed from a stream, the -[-take:][RACStream] operator can be used to retrieve only that many values, and -then automatically terminate the stream immediately thereafter. - -Operators like `-take:` and [-takeUntil:][RACSignal+Operations] automatically propagate cancellation -up the stack as well. If nothing else needs the rest of the values, any -dependencies will be terminated too, potentially saving a significant amount of -work. - -### Deliver signal events onto a known scheduler - -When a signal is returned from a method, or combined with such a signal, it can -be difficult to know which thread events will be delivered upon. Although -events are [guaranteed to be serial](#signal-events-are-serialized), sometimes -stronger guarantees are needed, like when performing UI updates (which must -occur on the main thread). - -Whenever such a guarantee is important, the [-deliverOn:][RACSignal+Operations] -operator should be used to force a signal's events to arrive on a specific -[RACScheduler][]. - -### Switch schedulers in as few places as possible - -Notwithstanding the above, events should only be delivered to a specific -[scheduler][RACScheduler] when absolutely necessary. Switching schedulers can -introduce unnecessary delays and cause an increase in CPU load. - -Generally, the use of [-deliverOn:][RACSignal+Operations] should be restricted -to the end of a signal chain – e.g., before subscription, or before the values -are bound to a property. - -### Make the side effects of a signal explicit - -As much as possible, [RACSignal][] side effects should be avoided, because -subscribers may find the [behavior of side -effects](#side-effects-occur-for-each-subscription) unexpected. - -However, because Cocoa is predominantly imperative, it is sometimes useful to -perform side effects when signal events occur. Although most [RACStream][] and -[RACSignal][RACSignal+Operations] operators accept arbitrary blocks (which can -contain side effects), the use of `-doNext:`, `-doError:`, and `-doCompleted:` -will make side effects more explicit and self-documenting: - -```objc -NSMutableArray *nexts = [NSMutableArray array]; -__block NSError *receivedError = nil; -__block BOOL success = NO; - -RACSignal *bookkeepingSignal = [[[valueSignal - doNext:^(id x) { - [nexts addObject:x]; - }] - doError:^(NSError *error) { - receivedError = error; - }] - doCompleted:^{ - success = YES; - }]; - -RAC(self, value) = bookkeepingSignal; -``` - -### Share the side effects of a signal by multicasting - -[Side effects occur for each -subscription](#side-effects-occur-for-each-subscription) by default, but there -are certain situations where side effects should only occur once – for example, -a network request typically should not be repeated when a new subscriber is -added. - -The `-publish` and `-multicast:` operators of [RACSignal][RACSignal+Operations] -allow a single subscription to be shared to any number of subscribers by using -a [RACMulticastConnection][]: - -```objc -// This signal starts a new request on each subscription. -RACSignal *networkRequest = [RACSignal createSignal:^(id subscriber) { - AFHTTPRequestOperation *operation = [client - HTTPRequestOperationWithRequest:request - success:^(AFHTTPRequestOperation *operation, id response) { - [subscriber sendNext:response]; - [subscriber sendCompleted]; - } - failure:^(AFHTTPRequestOperation *operation, NSError *error) { - [subscriber sendError:error]; - }]; - - [client enqueueHTTPRequestOperation:operation]; - return [RACDisposable disposableWithBlock:^{ - [operation cancel]; - }]; -}]; - -// Starts a single request, no matter how many subscriptions `connection.signal` -// gets. This is equivalent to the -replay operator, or similar to -// +startEagerlyWithScheduler:block:. -RACMulticastConnection *connection = [networkRequest multicast:[RACReplaySubject subject]]; -[connection connect]; - -[connection.signal subscribeNext:^(id response) { - NSLog(@"subscriber one: %@", response); -}]; - -[connection.signal subscribeNext:^(id response) { - NSLog(@"subscriber two: %@", response); -}]; -``` - -### Debug streams by giving them names - -Every [RACStream][] has a `name` property to assist with debugging. A stream's -`-description` includes its name, and all operators provided by RAC will -automatically add to the name. This usually makes it possible to identify -a stream from its default name alone. - -For example, this snippet: - -```objc -RACSignal *signal = [[[RACObserve(self, username) - distinctUntilChanged] - take:3] - filter:^(NSString *newUsername) { - return [newUsername isEqualToString:@"joshaber"]; - }]; - -NSLog(@"%@", signal); -``` - -… would log a name similar to `[[[RACObserve(self, username)] -distinctUntilChanged] --take: 3] -filter:`. - -Names can also be manually applied by using [-setNameWithFormat:][RACStream]. - -[RACSignal][] also offers `-logNext`, `-logError`, -`-logCompleted`, and `-logAll` methods, which will automatically log signal -events as they occur, and include the name of the signal in the messages. This -can be used to conveniently inspect a signal in real-time. - -### Avoid explicit subscriptions and disposal - -Although [-subscribeNext:error:completed:][RACSignal] and its variants are the -most basic way to process a signal, their use can complicate code by -being less declarative, encouraging the use of side effects, and potentially -duplicating built-in functionality. - -Likewise, explicit use of the [RACDisposable][] class can quickly lead to -a rat's nest of resource management and cleanup code. - -There are almost always higher-level patterns that can be used instead of manual -subscriptions and disposal: - - * The [RAC()][RAC] or [RACChannelTo()][RACChannelTo] macros can be used to bind - a signal to a property, instead of performing manual updates when changes - occur. - * The [-rac_liftSelector:withSignals:][NSObject+RACLifting] method can be used - to automatically invoke a selector when one or more signals fire. - * Operators like [-takeUntil:][RACSignal+Operations] can be used to - automatically dispose of a subscription when an event occurs (like a "Cancel" - button being pressed in the UI). - -Generally, the use of built-in [stream][RACStream] and -[signal][RACSignal+Operations] operators will lead to simpler and less -error-prone code than replicating the same behaviors in a subscription callback. - -### Avoid using subjects when possible - -[Subjects][] are a powerful tool for bridging imperative code -into the world of signals, but, as the "mutable variables" of RAC, they can -quickly lead to complexity when overused. - -Since they can be manipulated from anywhere, at any time, subjects often break -the linear flow of stream processing and make logic much harder to follow. They -also don't support meaningful -[disposal](#disposal-cancels-in-progress-work-and-cleans-up-resources), which -can result in unnecessary work. - -Subjects can usually be replaced with other patterns from ReactiveCocoa: - - * Instead of feeding initial values into a subject, consider generating the - values in a [+createSignal:][RACSignal] block instead. - * Instead of delivering intermediate results to a subject, try combining the - output of multiple signals with operators like - [+combineLatest:][RACSignal+Operations] or [+zip:][RACStream]. - * Instead of using subjects to share results with multiple subscribers, - [multicast](#share-the-side-effects-of-a-signal-by-multicasting) a base - signal instead. - * Instead of implementing an action method which simply controls a subject, use - a [command][RACCommand] or - [-rac_signalForSelector:][NSObject+RACSelectorSignal] instead. - -When subjects _are_ necessary, they should almost always be the "base" input -for a signal chain, not used in the middle of one. - -## Implementing new operators - -RAC provides a long list of built-in operators for [streams][RACStream] and -[signals][RACSignal+Operations] that should cover most use cases; however, RAC -is not a closed system. It's entirely valid to implement additional operators -for specialized uses, or for consideration in ReactiveCocoa itself. - -Implementing a new operator requires a careful attention to detail and a focus -on simplicity, to avoid introducing bugs into the calling code. - -These guidelines cover some of the common pitfalls and help preserve the -expected API contracts. - -### Prefer building on RACStream methods - -[RACStream][] offers a simpler interface than [RACSequence][] and [RACSignal][], -and all stream operators are automatically applicable to sequences and signals -as well. - -For these reasons, new operators should be implemented using only [RACStream][] -methods whenever possible. The minimal required methods of the class, including -`-bind:`, `-zipWith:`, and `-concat:`, are quite powerful, and many tasks can -be accomplished without needing anything else. - -If a new [RACSignal][] operator needs to handle `error` and `completed` events, -consider using the [-materialize][RACSignal+Operations] method to bring the -events into the stream. All of the events of a materialized signal can be -manipulated by stream operators, which helps minimize the use of non-stream -operators. - -### Compose existing operators when possible - -Considerable thought has been put into the operators provided by RAC, and they -have been validated through automated tests and through their real world use in -other projects. An operator that has been written from scratch may not be as -robust, or might not handle a special case that the built-in operators are aware -of. - -To minimize duplication and possible bugs, use the provided operators as much as -possible in a custom operator implementation. Generally, there should be very -little code written from scratch. - -### Avoid introducing concurrency - -Concurrency is an extremely common source of bugs in programming. To minimize -the potential for deadlocks and race conditions, operators should not -concurrently perform their work. - -Callers always have the ability to subscribe or deliver events on a specific -[RACScheduler][], and RAC offers powerful ways to [parallelize -work][Parallelizing Independent Work] without making operators unnecessarily -complex. - -### Cancel work and clean up all resources in a disposable - -When implementing a signal with the [+createSignal:][RACSignal] method, the -provided block is expected to return a [RACDisposable][]. This disposable -should: - - * As soon as it is convenient, gracefully cancel any in-progress work that was - started by the signal. - * Immediately dispose of any subscriptions to other signals, thus triggering - their cancellation and cleanup code as well. - * Release any memory or other resources that were allocated by the signal. - -This helps fulfill [the RACSignal -contract](#disposal-cancels-in-progress-work-and-cleans-up-resources). - -### Do not block in an operator - -Stream operators should return a new stream more-or-less immediately. Any work -that the operator needs to perform should be part of evaluating the new stream, -_not_ part of the operator invocation itself. - -```objc -// WRONG! -- (RACSequence *)map:(id (^)(id))block { - RACSequence *result = [RACSequence empty]; - for (id obj in self) { - id mappedObj = block(obj); - result = [result concat:[RACSequence return:mappedObj]]; - } - - return result; -} - -// Right! -- (RACSequence *)map:(id (^)(id))block { - return [self flattenMap:^(id obj) { - id mappedObj = block(obj); - return [RACSequence return:mappedObj]; - }]; -} -``` - -This guideline can be safely ignored when the purpose of an operator is to -synchronously retrieve one or more values from a stream (like -[-first][RACSignal+Operations]). - -### Avoid stack overflow from deep recursion - -Any operator that might recurse indefinitely should use the -`-scheduleRecursiveBlock:` method of [RACScheduler][]. This method will -transform recursion into iteration instead, preventing a stack overflow. - -For example, this would be an incorrect implementation of -[-repeat][RACSignal+Operations], due to its potential to overflow the call stack -and cause a crash: - -```objc -- (RACSignal *)repeat { - return [RACSignal createSignal:^(id subscriber) { - RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable]; - - __block void (^resubscribe)(void) = ^{ - RACDisposable *disposable = [self subscribeNext:^(id x) { - [subscriber sendNext:x]; - } error:^(NSError *error) { - [subscriber sendError:error]; - } completed:^{ - resubscribe(); - }]; - - [compoundDisposable addDisposable:disposable]; - }; - - return compoundDisposable; - }]; -} -``` - -By contrast, this version will avoid a stack overflow: - -```objc -- (RACSignal *)repeat { - return [RACSignal createSignal:^(id subscriber) { - RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable]; - - RACScheduler *scheduler = RACScheduler.currentScheduler ?: [RACScheduler scheduler]; - RACDisposable *disposable = [scheduler scheduleRecursiveBlock:^(void (^reschedule)(void)) { - RACDisposable *disposable = [self subscribeNext:^(id x) { - [subscriber sendNext:x]; - } error:^(NSError *error) { - [subscriber sendError:error]; - } completed:^{ - reschedule(); - }]; - - [compoundDisposable addDisposable:disposable]; - }]; - - [compoundDisposable addDisposable:disposable]; - return compoundDisposable; - }]; -} -``` - -[Framework Overview]: FrameworkOverview.md -[Memory Management]: MemoryManagement.md -[NSObject+RACLifting]: ../../ReactiveCocoa/Objective-C/NSObject+RACLifting.h -[NSObject+RACSelectorSignal]: ../../ReactiveCocoa/Objective-C/NSObject+RACSelectorSignal.h -[RAC]: ../../ReactiveCocoa/Objective-C/RACSubscriptingAssignmentTrampoline.h -[RACChannelTo]: ../../ReactiveCocoa/Objective-C/RACKVOChannel.h -[RACCommand]: ../../ReactiveCocoa/Objective-C/RACCommand.h -[RACDisposable]: ../../ReactiveCocoa/Objective-C/RACDisposable.h -[RACEvent]: ../../ReactiveCocoa/Objective-C/RACEvent.h -[RACMulticastConnection]: ../../ReactiveCocoa/Objective-C/RACMulticastConnection.h -[RACObserve]: ../../ReactiveCocoa/Objective-C/NSObject+RACPropertySubscribing.h -[RACScheduler]: ../../ReactiveCocoa/Objective-C/RACScheduler.h -[RACSequence]: ../../ReactiveCocoa/Objective-C/RACSequence.h -[RACSignal]: ../../ReactiveCocoa/Objective-C/RACSignal.h -[RACSignal+Operations]: ../../ReactiveCocoa/Objective-C/RACSignal+Operations.h -[RACStream]: ../../ReactiveCocoa/Objective-C/RACStream.h -[RACSubscriber]: ../../ReactiveCocoa/Objective-C/RACSubscriber.h -[Subjects]: FrameworkOverview.md#subjects -[Parallelizing Independent Work]: ../README.md#parallelizing-independent-work diff --git a/Documentation/Legacy/FrameworkOverview.md b/Documentation/Legacy/FrameworkOverview.md deleted file mode 100644 index ed5b88c0d8..0000000000 --- a/Documentation/Legacy/FrameworkOverview.md +++ /dev/null @@ -1,203 +0,0 @@ -# Framework Overview - -This document contains a high-level description of the different components -within the ReactiveCocoa framework, and an attempt to explain how they work -together and divide responsibilities. This is meant to be a starting point for -learning about new modules and finding more specific documentation. - -For examples and help understanding how to use RAC, see the [README][] or -the [Design Guidelines][]. - -## Streams - -A **stream**, represented by the [RACStream][] abstract class, is any series of -object values. - -Values may be available immediately or in the future, but must be retrieved -sequentially. There is no way to retrieve the second value of a stream without -evaluating or waiting for the first value. - -Streams are [monads][]. Among other things, this allows complex operations to be -built on a few basic primitives (`-bind:` in particular). [RACStream][] also -implements the equivalent of the [Monoid][] and [MonadZip][] typeclasses from -[Haskell][]. - -[RACStream][] isn't terribly useful on its own. Most streams are treated as -[signals](#signals) or [sequences](#sequences) instead. - -## Signals - -A **signal**, represented by the [RACSignal][] class, is a _push-driven_ -[stream](#streams). - -Signals generally represent data that will be delivered in the future. As work -is performed or data is received, values are _sent_ on the signal, which pushes -them out to any subscribers. Users must [subscribe](#subscription) to a signal -in order to access its values. - -Signals send three different types of events to their subscribers: - - * The **next** event provides a new value from the stream. [RACStream][] - methods only operate on events of this type. Unlike Cocoa collections, it is - completely valid for a signal to include `nil`. - * The **error** event indicates that an error occurred before the signal could - finish. The event may include an `NSError` object that indicates what went - wrong. Errors must be handled specially – they are not included in the - stream's values. - * The **completed** event indicates that the signal finished successfully, and - that no more values will be added to the stream. Completion must be handled - specially – it is not included in the stream of values. - -The lifetime of a signal consists of any number of `next` events, followed by -one `error` or `completed` event (but not both). - -### Subscription - -A **subscriber** is anything that is waiting or capable of waiting for events -from a [signal](#signals). Within RAC, a subscriber is represented as any object -that conforms to the [RACSubscriber][] protocol. - -A **subscription** is created through any call to -[-subscribeNext:error:completed:][RACSignal], or one of the corresponding -convenience methods. Technically, most [RACStream][] and -[RACSignal][RACSignal+Operations] operators create subscriptions as well, but -these intermediate subscriptions are usually an implementation detail. - -Subscriptions [retain their signals][Memory Management], and are automatically -disposed of when the signal completes or errors. Subscriptions can also be -[disposed of manually](#disposables). - -### Subjects - -A **subject**, represented by the [RACSubject][] class, is a [signal](#signals) -that can be manually controlled. - -Subjects can be thought of as the "mutable" variant of a signal, much like -`NSMutableArray` is for `NSArray`. They are extremely useful for bridging -non-RAC code into the world of signals. - -For example, instead of handling application logic in block callbacks, the -blocks can simply send events to a shared subject instead. The subject can then -be returned as a [RACSignal][], hiding the implementation detail of the -callbacks. - -Some subjects offer additional behaviors as well. In particular, -[RACReplaySubject][] can be used to buffer events for future -[subscribers](#subscription), like when a network request finishes before -anything is ready to handle the result. - -### Commands - -A **command**, represented by the [RACCommand][] class, creates and subscribes -to a signal in response to some action. This makes it easy to perform -side-effecting work as the user interacts with the app. - -Usually the action triggering a command is UI-driven, like when a button is -clicked. Commands can also be automatically disabled based on a signal, and this -disabled state can be represented in a UI by disabling any controls associated -with the command. - -On OS X, RAC adds a `rac_command` property to -[NSButton][NSButton+RACCommandSupport] for setting up these behaviors -automatically. - -### Connections - -A **connection**, represented by the [RACMulticastConnection][] class, is -a [subscription](#subscription) that is shared between any number of -subscribers. - -[Signals](#signals) are _cold_ by default, meaning that they start doing work -_each_ time a new subscription is added. This behavior is usually desirable, -because it means that data will be freshly recalculated for each subscriber, but -it can be problematic if the signal has side effects or the work is expensive -(for example, sending a network request). - -A connection is created through the `-publish` or `-multicast:` methods on -[RACSignal][RACSignal+Operations], and ensures that only one underlying -subscription is created, no matter how many times the connection is subscribed -to. Once connected, the connection's signal is said to be _hot_, and the -underlying subscription will remain active until _all_ subscriptions to the -connection are [disposed](#disposables). - -## Sequences - -A **sequence**, represented by the [RACSequence][] class, is a _pull-driven_ -[stream](#streams). - -Sequences are a kind of collection, similar in purpose to `NSArray`. Unlike -an array, the values in a sequence are evaluated _lazily_ (i.e., only when they -are needed) by default, potentially improving performance if only part of -a sequence is used. Just like Cocoa collections, sequences cannot contain `nil`. - -Sequences are similar to [Clojure's sequences][seq] ([lazy-seq][] in particular), or -the [List][] type in [Haskell][]. - -RAC adds a `-rac_sequence` method to most of Cocoa's collection classes, -allowing them to be used as [RACSequences][RACSequence] instead. - -## Disposables - -The **[RACDisposable][]** class is used for cancellation and resource cleanup. - -Disposables are most commonly used to unsubscribe from a [signal](#signals). -When a [subscription](#subscription) is disposed, the corresponding subscriber -will not receive _any_ further events from the signal. Additionally, any work -associated with the subscription (background processing, network requests, etc.) -will be cancelled, since the results are no longer needed. - -For more information about cancellation, see the RAC [Design Guidelines][]. - -## Schedulers - -A **scheduler**, represented by the [RACScheduler][] class, is a serial -execution queue for [signals](#signals) to perform work or deliver their results upon. - -Schedulers are similar to Grand Central Dispatch queues, but schedulers support -cancellation (via [disposables](#disposables)), and always execute serially. -With the exception of the [+immediateScheduler][RACScheduler], schedulers do not -offer synchronous execution. This helps avoid deadlocks, and encourages the use -of [signal operators][RACSignal+Operations] instead of blocking work. - -[RACScheduler][] is also somewhat similar to `NSOperationQueue`, but schedulers -do not allow tasks to be reordered or depend on one another. - -## Value types - -RAC offers a few miscellaneous classes for conveniently representing values in -a [stream](#streams): - - * **[RACTuple][]** is a small, constant-sized collection that can contain - `nil` (represented by `RACTupleNil`). It is generally used to represent - the combined values of multiple streams. - * **[RACUnit][]** is a singleton "empty" value. It is used as a value in - a stream for those times when more meaningful data doesn't exist. - * **[RACEvent][]** represents any [signal event](#signals) as a single value. - It is primarily used by the `-materialize` method of - [RACSignal][RACSignal+Operations]. - -[Design Guidelines]: DesignGuidelines.md -[Haskell]: http://www.haskell.org -[lazy-seq]: http://clojure.github.com/clojure/clojure.core-api.html#clojure.core/lazy-seq -[List]: https://downloads.haskell.org/~ghc/latest/docs/html/libraries/Data-List.html -[Memory Management]: MemoryManagement.md -[monads]: http://en.wikipedia.org/wiki/Monad_(functional_programming) -[Monoid]: http://downloads.haskell.org/~ghc/latest/docs/html/libraries/Data-Monoid.html -[MonadZip]: http://downloads.haskell.org/~ghc/latest/docs/html/libraries/Control-Monad-Zip.html -[NSButton+RACCommandSupport]: ../../ReactiveCocoa/Objective-C/NSButton+RACCommandSupport.h -[RACCommand]: ../../ReactiveCocoa/Objective-C/RACCommand.h -[RACDisposable]: ../../ReactiveCocoa/Objective-C/RACDisposable.h -[RACEvent]: ../../ReactiveCocoa/Objective-C/RACEvent.h -[RACMulticastConnection]: ../../ReactiveCocoa/Objective-C/RACMulticastConnection.h -[RACReplaySubject]: ../../ReactiveCocoa/Objective-C/RACReplaySubject.h -[RACScheduler]: ../../ReactiveCocoa/Objective-C/RACScheduler.h -[RACSequence]: ../../ReactiveCocoa/Objective-C/RACSequence.h -[RACSignal]: ../../ReactiveCocoa/Objective-C/RACSignal.h -[RACSignal+Operations]: ../../ReactiveCocoa/Objective-C/RACSignal+Operations.h -[RACStream]: ../../ReactiveCocoa/Objective-C/RACStream.h -[RACSubject]: ../../ReactiveCocoa/Objective-C/RACSubject.h -[RACSubscriber]: ../../ReactiveCocoa/Objective-C/RACSubscriber.h -[RACTuple]: ../../ReactiveCocoa/Objective-C/RACTuple.h -[RACUnit]: ../../ReactiveCocoa/Objective-C/RACUnit.h -[README]: README.md -[seq]: http://clojure.org/sequences diff --git a/Documentation/Legacy/MemoryManagement.md b/Documentation/Legacy/MemoryManagement.md deleted file mode 100644 index e442b1f6e0..0000000000 --- a/Documentation/Legacy/MemoryManagement.md +++ /dev/null @@ -1,139 +0,0 @@ -# Memory Management - -ReactiveCocoa's memory management is quite complex, but the end result is that -**you don't need to retain signals in order to process them**. - -If the framework required you to retain every signal, it'd be much more unwieldy -to use, especially for one-shot signals that are used like futures (e.g., -network requests). You'd have to save any long-lived signal into a property, and -then also make sure to clear it out when you're done with it. Not fun. - -## Subscribers - -Before going any further, it's worth noting that -`subscribeNext:error:completed:` (and all variants thereof) create an _implicit_ -subscriber using the given blocks. Any objects referenced from those blocks will -therefore be retained as part of the subscription. Just like any other object, -`self` won't be retained without a direct or indirect reference to it. - -## Finite or Short-Lived Signals - -The most important guideline to RAC memory management is that a **subscription -is automatically terminated upon completion or error, and the subscriber -removed**. - -For example, if you have some code like this in your view controller: - -```objc -self.disposable = [signal subscribeCompleted:^{ - doSomethingPossiblyInvolving(self); -}]; -``` - -… the memory management will look something like the following: - -``` -view controller -> RACDisposable -> RACSignal -> RACSubscriber -> view controller -``` - -However, the `RACSignal -> RACSubscriber` relationship is torn down as soon as -`signal` finishes, breaking the retain cycle. - -**This is often all you need**, because the lifetime of the `RACSignal` in -memory will naturally match the logical lifetime of the event stream. - -## Infinite Signals - -Infinite signals (or signals that live so long that they might as well be -infinite), however, will never tear down naturally. This is where disposables -shine. - -**Disposing of a subscription will remove the associated subscriber**, and just -generally clean up any resources associated with that subscription. To that one -subscriber, it's just as if the signal had completed or errored, except no final -event is sent on the signal. All other subscribers will remain intact. - -However, as a general rule of thumb, if you have to manually manage -a subscription's lifecycle, [there's probably a better way to do what you want][avoid-explicit-subscriptions-and-disposal]. - -## Signals Derived from `self` - -There's still a bit of a tricky middle case here, though. Any time a signal's -lifetime is tied to the calling scope, you'll have a much harder cycle to break. - -This commonly occurs when using `RACObserve()` on a key -path that's relative to `self`, and then applying a block that needs to capture -`self`. - -The easiest answer here is just to **capture `self` weakly**: - -```objc -__weak id weakSelf = self; -[RACObserve(self, username) subscribeNext:^(NSString *username) { - id strongSelf = weakSelf; - [strongSelf validateUsername]; -}]; -``` - -Or, after importing the included -[EXTScope.h](https://github.com/jspahrsummers/libextobjc/blob/master/extobjc/EXTScope.h) -header: - -```objc -@weakify(self); -[RACObserve(self, username) subscribeNext:^(NSString *username) { - @strongify(self); - [self validateUsername]; -}]; -``` - -*(Replace `__weak` or `@weakify` with `__unsafe_unretained` or `@unsafeify`, -respectively, if the object doesn't support weak references.)* - -However, [there's probably a better pattern you could use instead][avoid-explicit-subscriptions-and-disposal]. For -example, the above sample could perhaps be written like: - -```objc -[self rac_liftSelector:@selector(validateUsername:) withSignals:RACObserve(self, username), nil]; -``` - -or: - -```objc -RACSignal *validated = [RACObserve(self, username) map:^(NSString *username) { - // Put validation logic here. - return @YES; -}]; -``` - -As with infinite signals, there are generally ways you can avoid referencing -`self` (or any object) from blocks in a signal chain. - ----- - -The above information is really all you should need in order to use -ReactiveCocoa effectively. However, there's one more point to address, just for -the technically curious or for anyone interested in contributing to RAC. - -The design goal of "no retaining necessary" begs the question: how do we know -when a signal should be deallocated? What if it was just created, escaped an -autorelease pool, and hasn't been retained yet? - -The real answer is _we don't_, BUT we can usually assume that the caller will -retain the signal within the current run loop iteration if they want to keep it. - -Consequently: - - 1. A created signal is automatically added to a global set of active signals. - 2. The signal will wait for a single pass of the main run loop, and then remove - itself from the active set _if it has no subscribers_. Unless the signal was - retained somehow, it would deallocate at this point. - 3. If something did subscribe in that run loop iteration, the signal stays in - the set. - 4. Later, when all the subscribers are gone, step 2 is triggered again. - -This could backfire if the run loop is spun recursively (like in a modal event -loop on OS X), but it makes the life of the framework consumer much easier for -most or all other cases. - -[avoid-explicit-subscriptions-and-disposal]: DesignGuidelines.md#avoid-explicit-subscriptions-and-disposal diff --git a/Documentation/Legacy/README.md b/Documentation/Legacy/README.md deleted file mode 100644 index 99892c42ca..0000000000 --- a/Documentation/Legacy/README.md +++ /dev/null @@ -1,552 +0,0 @@ -# ReactiveCocoa - -_NOTE: This is legacy introduction to the Objective-C ReactiveCocoa. For the -updated version that uses Swift please see the main [README][]_ - -ReactiveCocoa (RAC) is an Objective-C framework inspired by [Functional Reactive -Programming][]. It provides APIs for **composing and transforming streams of -values**. - -If you're already familiar with functional reactive programming or know the basic -premise of ReactiveCocoa, check out the other documentation in this folder for a -framework overview and more in-depth information about how it all works in practice. - -## New to ReactiveCocoa? - -ReactiveCocoa is documented like crazy, and there's a wealth of introductory -material available to explain what RAC is and how you can use it. - -If you want to learn more, we recommend these resources, roughly in order: - - 1. [Introduction](#introduction) - 1. [When to use ReactiveCocoa](#when-to-use-reactivecocoa) - 1. [Framework Overview][] - 1. [Basic Operators][] - 1. [Header documentation](../../ReactiveCocoa/Objective-C) - 1. Previously answered [Stack Overflow](https://github.com/ReactiveCocoa/ReactiveCocoa/wiki) - questions and [GitHub issues](https://github.com/ReactiveCocoa/ReactiveCocoa/issues?labels=question&state=closed) - 1. The rest of this folder - 1. [Functional Reactive Programming on iOS](https://leanpub.com/iosfrp/) - (eBook) - -If you have any further questions, please feel free to [file an issue](https://github.com/ReactiveCocoa/ReactiveCocoa/issues/new). - -## Introduction - -ReactiveCocoa is inspired by [functional reactive -programming](http://blog.maybeapps.com/post/42894317939/input-and-output). -Rather than using mutable variables which are replaced and modified in-place, -RAC provides signals (represented by `RACSignal`) that capture present and -future values. - -By chaining, combining, and reacting to signals, software can be written -declaratively, without the need for code that continually observes and updates -values. - -For example, a text field can be bound to the latest time, even as it changes, -instead of using additional code that watches the clock and updates the -text field every second. It works much like KVO, but with blocks instead of -overriding `-observeValueForKeyPath:ofObject:change:context:`. - -Signals can also represent asynchronous operations, much like [futures and -promises][]. This greatly simplifies asynchronous software, including networking -code. - -One of the major advantages of RAC is that it provides a single, unified -approach to dealing with asynchronous behaviors, including delegate methods, -callback blocks, target-action mechanisms, notifications, and KVO. - -Here's a simple example: - -```objc -// When self.username changes, logs the new name to the console. -// -// RACObserve(self, username) creates a new RACSignal that sends the current -// value of self.username, then the new value whenever it changes. -// -subscribeNext: will execute the block whenever the signal sends a value. -[RACObserve(self, username) subscribeNext:^(NSString *newName) { - NSLog(@"%@", newName); -}]; -``` - -But unlike KVO notifications, signals can be chained together and operated on: - -```objc -// Only logs names that starts with "j". -// -// -filter returns a new RACSignal that only sends a new value when its block -// returns YES. -[[RACObserve(self, username) - filter:^(NSString *newName) { - return [newName hasPrefix:@"j"]; - }] - subscribeNext:^(NSString *newName) { - NSLog(@"%@", newName); - }]; -``` - -Signals can also be used to derive state. Instead of observing properties and -setting other properties in response to the new values, RAC makes it possible to -express properties in terms of signals and operations: - -```objc -// Creates a one-way binding so that self.createEnabled will be -// true whenever self.password and self.passwordConfirmation -// are equal. -// -// RAC() is a macro that makes the binding look nicer. -// -// +combineLatest:reduce: takes an array of signals, executes the block with the -// latest value from each signal whenever any of them changes, and returns a new -// RACSignal that sends the return value of that block as values. -RAC(self, createEnabled) = [RACSignal - combineLatest:@[ RACObserve(self, password), RACObserve(self, passwordConfirmation) ] - reduce:^(NSString *password, NSString *passwordConfirm) { - return @([passwordConfirm isEqualToString:password]); - }]; -``` - -Signals can be built on any stream of values over time, not just KVO. For -example, they can also represent button presses: - -```objc -// Logs a message whenever the button is pressed. -// -// RACCommand creates signals to represent UI actions. Each signal can -// represent a button press, for example, and have additional work associated -// with it. -// -// -rac_command is an addition to NSButton. The button will send itself on that -// command whenever it's pressed. -self.button.rac_command = [[RACCommand alloc] initWithSignalBlock:^(id _) { - NSLog(@"button was pressed!"); - return [RACSignal empty]; -}]; -``` - -Or asynchronous network operations: - -```objc -// Hooks up a "Log in" button to log in over the network. -// -// This block will be run whenever the login command is executed, starting -// the login process. -self.loginCommand = [[RACCommand alloc] initWithSignalBlock:^(id sender) { - // The hypothetical -logIn method returns a signal that sends a value when - // the network request finishes. - return [client logIn]; -}]; - -// -executionSignals returns a signal that includes the signals returned from -// the above block, one for each time the command is executed. -[self.loginCommand.executionSignals subscribeNext:^(RACSignal *loginSignal) { - // Log a message whenever we log in successfully. - [loginSignal subscribeCompleted:^{ - NSLog(@"Logged in successfully!"); - }]; -}]; - -// Executes the login command when the button is pressed. -self.loginButton.rac_command = self.loginCommand; -``` - -Signals can also represent timers, other UI events, or anything else that -changes over time. - -Using signals for asynchronous operations makes it possible to build up more -complex behavior by chaining and transforming those signals. Work can easily be -triggered after a group of operations completes: - -```objc -// Performs 2 network operations and logs a message to the console when they are -// both completed. -// -// +merge: takes an array of signals and returns a new RACSignal that passes -// through the values of all of the signals and completes when all of the -// signals complete. -// -// -subscribeCompleted: will execute the block when the signal completes. -[[RACSignal - merge:@[ [client fetchUserRepos], [client fetchOrgRepos] ]] - subscribeCompleted:^{ - NSLog(@"They're both done!"); - }]; -``` - -Signals can be chained to sequentially execute asynchronous operations, instead -of nesting callbacks with blocks. This is similar to how [futures and promises][] -are usually used: - -```objc -// Logs in the user, then loads any cached messages, then fetches the remaining -// messages from the server. After that's all done, logs a message to the -// console. -// -// The hypothetical -logInUser methods returns a signal that completes after -// logging in. -// -// -flattenMap: will execute its block whenever the signal sends a value, and -// returns a new RACSignal that merges all of the signals returned from the block -// into a single signal. -[[[[client - logInUser] - flattenMap:^(User *user) { - // Return a signal that loads cached messages for the user. - return [client loadCachedMessagesForUser:user]; - }] - flattenMap:^(NSArray *messages) { - // Return a signal that fetches any remaining messages. - return [client fetchMessagesAfterMessage:messages.lastObject]; - }] - subscribeNext:^(NSArray *newMessages) { - NSLog(@"New messages: %@", newMessages); - } completed:^{ - NSLog(@"Fetched all messages."); - }]; -``` - -RAC even makes it easy to bind to the result of an asynchronous operation: - -```objc -// Creates a one-way binding so that self.imageView.image will be set as the user's -// avatar as soon as it's downloaded. -// -// The hypothetical -fetchUserWithUsername: method returns a signal which sends -// the user. -// -// -deliverOn: creates new signals that will do their work on other queues. In -// this example, it's used to move work to a background queue and then back to the main thread. -// -// -map: calls its block with each user that's fetched and returns a new -// RACSignal that sends values returned from the block. -RAC(self.imageView, image) = [[[[client - fetchUserWithUsername:@"joshaber"] - deliverOn:[RACScheduler scheduler]] - map:^(User *user) { - // Download the avatar (this is done on a background queue). - return [[NSImage alloc] initWithContentsOfURL:user.avatarURL]; - }] - // Now the assignment will be done on the main thread. - deliverOn:RACScheduler.mainThreadScheduler]; -``` - -That demonstrates some of what RAC can do, but it doesn't demonstrate why RAC is -so powerful. It's hard to appreciate RAC from README-sized examples, but it -makes it possible to write code with less state, less boilerplate, better code -locality, and better expression of intent. - -For more sample code, check out [C-41][] or [GroceryList][], which are real iOS -apps written using ReactiveCocoa. Additional information about RAC can be found -in this folder. - -## When to use ReactiveCocoa - -Upon first glance, ReactiveCocoa is very abstract, and it can be difficult to -understand how to apply it to concrete problems. - -Here are some of the use cases that RAC excels at. - -### Handling asynchronous or event-driven data sources - -Much of Cocoa programming is focused on reacting to user events or changes in -application state. Code that deals with such events can quickly become very -complex and spaghetti-like, with lots of callbacks and state variables to handle -ordering issues. - -Patterns that seem superficially different, like UI callbacks, network -responses, and KVO notifications, actually have a lot in common. [RACSignal][] -unifies all these different APIs so that they can be composed together and -manipulated in the same way. - -For example, the following code: - -```objc - -static void *ObservationContext = &ObservationContext; - -- (void)viewDidLoad { - [super viewDidLoad]; - - [LoginManager.sharedManager addObserver:self forKeyPath:@"loggingIn" options:NSKeyValueObservingOptionInitial context:&ObservationContext]; - [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(loggedOut:) name:UserDidLogOutNotification object:LoginManager.sharedManager]; - - [self.usernameTextField addTarget:self action:@selector(updateLogInButton) forControlEvents:UIControlEventEditingChanged]; - [self.passwordTextField addTarget:self action:@selector(updateLogInButton) forControlEvents:UIControlEventEditingChanged]; - [self.logInButton addTarget:self action:@selector(logInPressed:) forControlEvents:UIControlEventTouchUpInside]; -} - -- (void)dealloc { - [LoginManager.sharedManager removeObserver:self forKeyPath:@"loggingIn" context:ObservationContext]; - [NSNotificationCenter.defaultCenter removeObserver:self]; -} - -- (void)updateLogInButton { - BOOL textFieldsNonEmpty = self.usernameTextField.text.length > 0 && self.passwordTextField.text.length > 0; - BOOL readyToLogIn = !LoginManager.sharedManager.isLoggingIn && !self.loggedIn; - self.logInButton.enabled = textFieldsNonEmpty && readyToLogIn; -} - -- (IBAction)logInPressed:(UIButton *)sender { - [[LoginManager sharedManager] - logInWithUsername:self.usernameTextField.text - password:self.passwordTextField.text - success:^{ - self.loggedIn = YES; - } failure:^(NSError *error) { - [self presentError:error]; - }]; -} - -- (void)loggedOut:(NSNotification *)notification { - self.loggedIn = NO; -} - -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - if (context == ObservationContext) { - [self updateLogInButton]; - } else { - [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; - } -} -``` - -… could be expressed in RAC like so: - -```objc -- (void)viewDidLoad { - [super viewDidLoad]; - - @weakify(self); - - RAC(self.logInButton, enabled) = [RACSignal - combineLatest:@[ - self.usernameTextField.rac_textSignal, - self.passwordTextField.rac_textSignal, - RACObserve(LoginManager.sharedManager, loggingIn), - RACObserve(self, loggedIn) - ] reduce:^(NSString *username, NSString *password, NSNumber *loggingIn, NSNumber *loggedIn) { - return @(username.length > 0 && password.length > 0 && !loggingIn.boolValue && !loggedIn.boolValue); - }]; - - [[self.logInButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(UIButton *sender) { - @strongify(self); - - RACSignal *loginSignal = [LoginManager.sharedManager - logInWithUsername:self.usernameTextField.text - password:self.passwordTextField.text]; - - [loginSignal subscribeError:^(NSError *error) { - @strongify(self); - [self presentError:error]; - } completed:^{ - @strongify(self); - self.loggedIn = YES; - }]; - }]; - - RAC(self, loggedIn) = [[NSNotificationCenter.defaultCenter - rac_addObserverForName:UserDidLogOutNotification object:nil] - mapReplace:@NO]; -} -``` - -### Chaining dependent operations - -Dependencies are most often found in network requests, where a previous request -to the server needs to complete before the next one can be constructed, and so -on: - -```objc -[client logInWithSuccess:^{ - [client loadCachedMessagesWithSuccess:^(NSArray *messages) { - [client fetchMessagesAfterMessage:messages.lastObject success:^(NSArray *nextMessages) { - NSLog(@"Fetched all messages."); - } failure:^(NSError *error) { - [self presentError:error]; - }]; - } failure:^(NSError *error) { - [self presentError:error]; - }]; -} failure:^(NSError *error) { - [self presentError:error]; -}]; -``` - -ReactiveCocoa makes this pattern particularly easy: - -```objc -[[[[client logIn] - then:^{ - return [client loadCachedMessages]; - }] - flattenMap:^(NSArray *messages) { - return [client fetchMessagesAfterMessage:messages.lastObject]; - }] - subscribeError:^(NSError *error) { - [self presentError:error]; - } completed:^{ - NSLog(@"Fetched all messages."); - }]; -``` - -### Parallelizing independent work - -Working with independent data sets in parallel and then combining them into -a final result is non-trivial in Cocoa, and often involves a lot of -synchronization: - -```objc -__block NSArray *databaseObjects; -__block NSArray *fileContents; - -NSOperationQueue *backgroundQueue = [[NSOperationQueue alloc] init]; -NSBlockOperation *databaseOperation = [NSBlockOperation blockOperationWithBlock:^{ - databaseObjects = [databaseClient fetchObjectsMatchingPredicate:predicate]; -}]; - -NSBlockOperation *filesOperation = [NSBlockOperation blockOperationWithBlock:^{ - NSMutableArray *filesInProgress = [NSMutableArray array]; - for (NSString *path in files) { - [filesInProgress addObject:[NSData dataWithContentsOfFile:path]]; - } - - fileContents = [filesInProgress copy]; -}]; - -NSBlockOperation *finishOperation = [NSBlockOperation blockOperationWithBlock:^{ - [self finishProcessingDatabaseObjects:databaseObjects fileContents:fileContents]; - NSLog(@"Done processing"); -}]; - -[finishOperation addDependency:databaseOperation]; -[finishOperation addDependency:filesOperation]; -[backgroundQueue addOperation:databaseOperation]; -[backgroundQueue addOperation:filesOperation]; -[backgroundQueue addOperation:finishOperation]; -``` - -The above code can be cleaned up and optimized by simply composing signals: - -```objc -RACSignal *databaseSignal = [[databaseClient - fetchObjectsMatchingPredicate:predicate] - subscribeOn:[RACScheduler scheduler]]; - -RACSignal *fileSignal = [RACSignal startEagerlyWithScheduler:[RACScheduler scheduler] block:^(id subscriber) { - NSMutableArray *filesInProgress = [NSMutableArray array]; - for (NSString *path in files) { - [filesInProgress addObject:[NSData dataWithContentsOfFile:path]]; - } - - [subscriber sendNext:[filesInProgress copy]]; - [subscriber sendCompleted]; -}]; - -[[RACSignal - combineLatest:@[ databaseSignal, fileSignal ] - reduce:^ id (NSArray *databaseObjects, NSArray *fileContents) { - [self finishProcessingDatabaseObjects:databaseObjects fileContents:fileContents]; - return nil; - }] - subscribeCompleted:^{ - NSLog(@"Done processing"); - }]; -``` - -### Simplifying collection transformations - -Higher-order functions like `map`, `filter`, `fold`/`reduce` are sorely missing -from Foundation, leading to loop-focused code like this: - -```objc -NSMutableArray *results = [NSMutableArray array]; -for (NSString *str in strings) { - if (str.length < 2) { - continue; - } - - NSString *newString = [str stringByAppendingString:@"foobar"]; - [results addObject:newString]; -} -``` - -[RACSequence][] allows any Cocoa collection to be manipulated in a uniform and -declarative way: - -```objc -RACSequence *results = [[strings.rac_sequence - filter:^ BOOL (NSString *str) { - return str.length >= 2; - }] - map:^(NSString *str) { - return [str stringByAppendingString:@"foobar"]; - }]; -``` - -## System Requirements - -ReactiveCocoa supports OS X 10.8+ and iOS 8.0+. - -## Importing ReactiveCocoa - -To add RAC to your application: - - 1. Add the ReactiveCocoa repository as a submodule of your application's - repository. - 1. Run `script/bootstrap` from within the ReactiveCocoa folder. - 1. Drag and drop `ReactiveCocoa.xcodeproj` into your - application's Xcode project or workspace. - 1. On the "Build Phases" tab of your application target, add RAC to the "Link - Binary With Libraries" phase. - * **On iOS**, add `libReactiveCocoa-iOS.a`. - * **On OS X**, add `ReactiveCocoa.framework`. RAC must also be added to any - "Copy Frameworks" build phase. If you don't already have one, simply add - a "Copy Files" build phase and target the "Frameworks" destination. - 1. Add `"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/include" - $(inherited)` to the "Header Search Paths" build setting (this is only - necessary for archive builds, but it has no negative effect otherwise). - 1. **For iOS targets**, add `-ObjC` to the "Other Linker Flags" build setting. - 1. **If you added RAC to a project (not a workspace)**, you will also need to - add the appropriate RAC target to the "Target Dependencies" of your - application. - -If you would prefer to use [CocoaPods](http://cocoapods.org), there are some -[ReactiveCocoa -podspecs](https://github.com/CocoaPods/Specs/tree/master/Specs/ReactiveCocoa) that -have been generously contributed by third parties. - -To see a project already set up with RAC, check out [C-41][] or [GroceryList][], -which are real iOS apps written using ReactiveCocoa. - -## More Info - -ReactiveCocoa is inspired by .NET's [Reactive -Extensions](http://msdn.microsoft.com/en-us/data/gg577609) (Rx). Most of the -principles of Rx apply to RAC as well. There are some really good Rx resources -out there: - -* [Reactive Extensions MSDN entry](http://msdn.microsoft.com/en-us/library/hh242985.aspx) -* [Reactive Extensions for .NET Introduction](http://leecampbell.blogspot.com/2010/08/reactive-extensions-for-net.html) -* [Rx - Channel 9 videos](http://channel9.msdn.com/tags/Rx/) -* [Reactive Extensions wiki](http://rxwiki.wikidot.com/) -* [101 Rx Samples](http://rxwiki.wikidot.com/101samples) -* [Programming Reactive Extensions and LINQ](http://www.amazon.com/Programming-Reactive-Extensions-Jesse-Liberty/dp/1430237473) - -RAC and Rx are both frameworks inspired by functional reactive programming. Here -are some resources related to FRP: - -* [What is FRP? - Elm Language](http://elm-lang.org/learn/What-is-FRP.elm) -* [What is Functional Reactive Programming - Stack Overflow](http://stackoverflow.com/questions/1028250/what-is-functional-reactive-programming/1030631#1030631) -* [Specification for a Functional Reactive Language - Stack Overflow](http://stackoverflow.com/questions/5875929/specification-for-a-functional-reactive-programming-language#5878525) -* [Escape from Callback Hell](http://elm-lang.org/learn/Escape-from-Callback-Hell.elm) -* [Principles of Reactive Programming on Coursera](https://www.coursera.org/course/reactive) - -[README]: ../../README.md -[Basic Operators]: BasicOperators.md -[Framework Overview]: FrameworkOverview.md -[Functional Reactive Programming]: http://en.wikipedia.org/wiki/Functional_reactive_programming -[GroceryList]: https://github.com/jspahrsummers/GroceryList -[RACSequence]: ../../ReactiveCocoa/Objective-C/RACSequence.h -[RACSignal]: ../../ReactiveCocoa/Objective-C/RACSignal.h -[futures and promises]: http://en.wikipedia.org/wiki/Futures_and_promises -[C-41]: https://github.com/AshFurrow/C-41 diff --git a/ReactiveObjC.xcodeproj/project.pbxproj b/ReactiveObjC.xcodeproj/project.pbxproj deleted file mode 100644 index 500c3153f1..0000000000 --- a/ReactiveObjC.xcodeproj/project.pbxproj +++ /dev/null @@ -1,3158 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 314304171ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 314304151ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 314304181ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 314304161ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.m */; }; - 57A4D1B21BA13D7A00F7D4B1 /* RACCompoundDisposableProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D037646A19EDA41200A782A9 /* RACCompoundDisposableProvider.d */; }; - 57A4D1B31BA13D7A00F7D4B1 /* RACSignalProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D03764A319EDA41200A782A9 /* RACSignalProvider.d */; }; - 57A4D1C11BA13D7A00F7D4B1 /* EXTRuntimeExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037666819EDA57100A782A9 /* EXTRuntimeExtensions.m */; }; - 57A4D1C21BA13D7A00F7D4B1 /* NSArray+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037642B19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m */; }; - 57A4D1C31BA13D7A00F7D4B1 /* NSData+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643119EDA41200A782A9 /* NSData+RACSupport.m */; }; - 57A4D1C41BA13D7A00F7D4B1 /* NSDictionary+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643319EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m */; }; - 57A4D1C51BA13D7A00F7D4B1 /* NSEnumerator+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643519EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m */; }; - 57A4D1C61BA13D7A00F7D4B1 /* NSFileHandle+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643719EDA41200A782A9 /* NSFileHandle+RACSupport.m */; }; - 57A4D1C71BA13D7A00F7D4B1 /* NSIndexSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643919EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m */; }; - 57A4D1C81BA13D7A00F7D4B1 /* NSInvocation+RACTypeParsing.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643B19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m */; }; - 57A4D1C91BA13D7A00F7D4B1 /* NSNotificationCenter+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643D19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m */; }; - 57A4D1CA1BA13D7A00F7D4B1 /* NSObject+RACDeallocating.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644119EDA41200A782A9 /* NSObject+RACDeallocating.m */; }; - 57A4D1CB1BA13D7A00F7D4B1 /* NSObject+RACDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644319EDA41200A782A9 /* NSObject+RACDescription.m */; }; - 57A4D1CC1BA13D7A00F7D4B1 /* NSObject+RACKVOWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644519EDA41200A782A9 /* NSObject+RACKVOWrapper.m */; }; - 57A4D1CD1BA13D7A00F7D4B1 /* NSObject+RACLifting.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644719EDA41200A782A9 /* NSObject+RACLifting.m */; }; - 57A4D1CE1BA13D7A00F7D4B1 /* NSObject+RACPropertySubscribing.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644919EDA41200A782A9 /* NSObject+RACPropertySubscribing.m */; }; - 57A4D1CF1BA13D7A00F7D4B1 /* NSObject+RACSelectorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644B19EDA41200A782A9 /* NSObject+RACSelectorSignal.m */; }; - 57A4D1D01BA13D7A00F7D4B1 /* NSOrderedSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644D19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m */; }; - 57A4D1D11BA13D7A00F7D4B1 /* NSSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644F19EDA41200A782A9 /* NSSet+RACSequenceAdditions.m */; }; - 57A4D1D21BA13D7A00F7D4B1 /* NSString+RACKeyPathUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645119EDA41200A782A9 /* NSString+RACKeyPathUtilities.m */; }; - 57A4D1D31BA13D7A00F7D4B1 /* NSString+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645319EDA41200A782A9 /* NSString+RACSequenceAdditions.m */; }; - 57A4D1D41BA13D7A00F7D4B1 /* NSString+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645519EDA41200A782A9 /* NSString+RACSupport.m */; }; - 57A4D1D61BA13D7A00F7D4B1 /* NSUserDefaults+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645B19EDA41200A782A9 /* NSUserDefaults+RACSupport.m */; }; - 57A4D1D71BA13D7A00F7D4B1 /* RACArraySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645D19EDA41200A782A9 /* RACArraySequence.m */; }; - 57A4D1D81BA13D7A00F7D4B1 /* RACBehaviorSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646119EDA41200A782A9 /* RACBehaviorSubject.m */; }; - 57A4D1D91BA13D7A00F7D4B1 /* RACBlockTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646319EDA41200A782A9 /* RACBlockTrampoline.m */; }; - 57A4D1DA1BA13D7A00F7D4B1 /* RACChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646519EDA41200A782A9 /* RACChannel.m */; }; - 57A4D1DB1BA13D7A00F7D4B1 /* RACCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646719EDA41200A782A9 /* RACCommand.m */; }; - 57A4D1DC1BA13D7A00F7D4B1 /* RACCompoundDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646919EDA41200A782A9 /* RACCompoundDisposable.m */; }; - 57A4D1DD1BA13D7A00F7D4B1 /* RACDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646C19EDA41200A782A9 /* RACDelegateProxy.m */; }; - 57A4D1DE1BA13D7A00F7D4B1 /* RACDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646E19EDA41200A782A9 /* RACDisposable.m */; }; - 57A4D1DF1BA13D7A00F7D4B1 /* RACDynamicSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647019EDA41200A782A9 /* RACDynamicSequence.m */; }; - 57A4D1E01BA13D7A00F7D4B1 /* RACDynamicSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647219EDA41200A782A9 /* RACDynamicSignal.m */; }; - 57A4D1E11BA13D7A00F7D4B1 /* RACEagerSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647419EDA41200A782A9 /* RACEagerSequence.m */; }; - 57A4D1E21BA13D7A00F7D4B1 /* RACEmptySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647619EDA41200A782A9 /* RACEmptySequence.m */; }; - 57A4D1E31BA13D7A00F7D4B1 /* RACEmptySignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647819EDA41200A782A9 /* RACEmptySignal.m */; }; - 57A4D1E41BA13D7A00F7D4B1 /* RACErrorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647A19EDA41200A782A9 /* RACErrorSignal.m */; }; - 57A4D1E51BA13D7A00F7D4B1 /* RACEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647C19EDA41200A782A9 /* RACEvent.m */; }; - 57A4D1E61BA13D7A00F7D4B1 /* RACGroupedSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647E19EDA41200A782A9 /* RACGroupedSignal.m */; }; - 57A4D1E71BA13D7A00F7D4B1 /* RACImmediateScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648019EDA41200A782A9 /* RACImmediateScheduler.m */; }; - 57A4D1E81BA13D7A00F7D4B1 /* RACIndexSetSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648219EDA41200A782A9 /* RACIndexSetSequence.m */; }; - 57A4D1E91BA13D7A00F7D4B1 /* RACKVOChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648419EDA41200A782A9 /* RACKVOChannel.m */; }; - 57A4D1EA1BA13D7A00F7D4B1 /* RACKVOProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A70657E1A3F88B8001E8354 /* RACKVOProxy.m */; }; - 57A4D1EB1BA13D7A00F7D4B1 /* RACKVOTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648619EDA41200A782A9 /* RACKVOTrampoline.m */; }; - 57A4D1EC1BA13D7A00F7D4B1 /* RACMulticastConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648819EDA41200A782A9 /* RACMulticastConnection.m */; }; - 57A4D1EE1BA13D7A00F7D4B1 /* RACPassthroughSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648D19EDA41200A782A9 /* RACPassthroughSubscriber.m */; }; - 57A4D1EF1BA13D7A00F7D4B1 /* RACQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648F19EDA41200A782A9 /* RACQueueScheduler.m */; }; - 57A4D1F01BA13D7A00F7D4B1 /* RACReplaySubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649219EDA41200A782A9 /* RACReplaySubject.m */; }; - 57A4D1F11BA13D7A00F7D4B1 /* RACReturnSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649419EDA41200A782A9 /* RACReturnSignal.m */; }; - 57A4D1F21BA13D7A00F7D4B1 /* RACScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649619EDA41200A782A9 /* RACScheduler.m */; }; - 57A4D1F31BA13D7A00F7D4B1 /* RACScopedDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649A19EDA41200A782A9 /* RACScopedDisposable.m */; }; - 57A4D1F41BA13D7A00F7D4B1 /* RACSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649C19EDA41200A782A9 /* RACSequence.m */; }; - 57A4D1F51BA13D7A00F7D4B1 /* RACSerialDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649E19EDA41200A782A9 /* RACSerialDisposable.m */; }; - 57A4D1F61BA13D7A00F7D4B1 /* RACSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A019EDA41200A782A9 /* RACSignal.m */; }; - 57A4D1F71BA13D7A00F7D4B1 /* RACSignal+Operations.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A219EDA41200A782A9 /* RACSignal+Operations.m */; }; - 57A4D1F81BA13D7A00F7D4B1 /* RACSignalSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A519EDA41200A782A9 /* RACSignalSequence.m */; }; - 57A4D1F91BA13D7A00F7D4B1 /* RACStream.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A719EDA41200A782A9 /* RACStream.m */; }; - 57A4D1FA1BA13D7A00F7D4B1 /* RACStringSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AA19EDA41200A782A9 /* RACStringSequence.m */; }; - 57A4D1FB1BA13D7A00F7D4B1 /* RACSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AC19EDA41200A782A9 /* RACSubject.m */; }; - 57A4D1FC1BA13D7A00F7D4B1 /* RACSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AE19EDA41200A782A9 /* RACSubscriber.m */; }; - 57A4D1FD1BA13D7A00F7D4B1 /* RACSubscriptingAssignmentTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B119EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m */; }; - 57A4D1FE1BA13D7A00F7D4B1 /* RACSubscriptionScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B319EDA41200A782A9 /* RACSubscriptionScheduler.m */; }; - 57A4D1FF1BA13D7A00F7D4B1 /* RACTargetQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B519EDA41200A782A9 /* RACTargetQueueScheduler.m */; }; - 57A4D2001BA13D7A00F7D4B1 /* RACTestScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B719EDA41200A782A9 /* RACTestScheduler.m */; }; - 57A4D2011BA13D7A00F7D4B1 /* RACTuple.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B919EDA41200A782A9 /* RACTuple.m */; }; - 57A4D2021BA13D7A00F7D4B1 /* RACTupleSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BB19EDA41200A782A9 /* RACTupleSequence.m */; }; - 57A4D2031BA13D7A00F7D4B1 /* RACUnarySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BD19EDA41200A782A9 /* RACUnarySequence.m */; }; - 57A4D2041BA13D7A00F7D4B1 /* RACUnit.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BF19EDA41200A782A9 /* RACUnit.m */; }; - 57A4D2051BA13D7A00F7D4B1 /* RACValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C119EDA41200A782A9 /* RACValueTransformer.m */; }; - 57A4D20A1BA13D7A00F7D4B1 /* ReactiveObjC.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveObjC.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D20B1BA13D7A00F7D4B1 /* EXTKeyPathCoding.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666619EDA57100A782A9 /* EXTKeyPathCoding.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D20C1BA13D7A00F7D4B1 /* EXTScope.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666919EDA57100A782A9 /* EXTScope.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D20D1BA13D7A00F7D4B1 /* metamacros.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666A19EDA57100A782A9 /* metamacros.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D20E1BA13D7A00F7D4B1 /* NSArray+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037642A19EDA41200A782A9 /* NSArray+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D20F1BA13D7A00F7D4B1 /* NSData+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643019EDA41200A782A9 /* NSData+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2101BA13D7A00F7D4B1 /* NSDictionary+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643219EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2111BA13D7A00F7D4B1 /* NSEnumerator+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643419EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2121BA13D7A00F7D4B1 /* NSFileHandle+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643619EDA41200A782A9 /* NSFileHandle+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2131BA13D7A00F7D4B1 /* NSIndexSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643819EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2141BA13D7A00F7D4B1 /* NSNotificationCenter+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643C19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2151BA13D7A00F7D4B1 /* NSObject+RACDeallocating.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644019EDA41200A782A9 /* NSObject+RACDeallocating.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2161BA13D7A00F7D4B1 /* NSObject+RACLifting.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644619EDA41200A782A9 /* NSObject+RACLifting.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2171BA13D7A00F7D4B1 /* NSObject+RACPropertySubscribing.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644819EDA41200A782A9 /* NSObject+RACPropertySubscribing.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2181BA13D7A00F7D4B1 /* NSObject+RACSelectorSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644A19EDA41200A782A9 /* NSObject+RACSelectorSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2191BA13D7A00F7D4B1 /* NSOrderedSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644C19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D21A1BA13D7A00F7D4B1 /* NSSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644E19EDA41200A782A9 /* NSSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D21B1BA13D7A00F7D4B1 /* NSString+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645219EDA41200A782A9 /* NSString+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D21C1BA13D7A00F7D4B1 /* NSString+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645419EDA41200A782A9 /* NSString+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D21E1BA13D7A00F7D4B1 /* NSUserDefaults+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645A19EDA41200A782A9 /* NSUserDefaults+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D21F1BA13D7A00F7D4B1 /* RACBehaviorSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646019EDA41200A782A9 /* RACBehaviorSubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2201BA13D7A00F7D4B1 /* RACChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646419EDA41200A782A9 /* RACChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2211BA13D7A00F7D4B1 /* RACCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646619EDA41200A782A9 /* RACCommand.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2221BA13D7A00F7D4B1 /* RACCompoundDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646819EDA41200A782A9 /* RACCompoundDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2231BA13D7A00F7D4B1 /* RACDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646D19EDA41200A782A9 /* RACDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2241BA13D7A00F7D4B1 /* RACEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = D037647B19EDA41200A782A9 /* RACEvent.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2251BA13D7A00F7D4B1 /* RACGroupedSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037647D19EDA41200A782A9 /* RACGroupedSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2261BA13D7A00F7D4B1 /* RACKVOChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648319EDA41200A782A9 /* RACKVOChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2271BA13D7A00F7D4B1 /* RACMulticastConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648719EDA41200A782A9 /* RACMulticastConnection.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2281BA13D7A00F7D4B1 /* RACQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648E19EDA41200A782A9 /* RACQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2291BA13D7A00F7D4B1 /* RACQueueScheduler+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649019EDA41200A782A9 /* RACQueueScheduler+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D22A1BA13D7A00F7D4B1 /* RACReplaySubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649119EDA41200A782A9 /* RACReplaySubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D22B1BA13D7A00F7D4B1 /* RACScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649519EDA41200A782A9 /* RACScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D22C1BA13D7A00F7D4B1 /* RACScheduler+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649819EDA41200A782A9 /* RACScheduler+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D22D1BA13D7A00F7D4B1 /* RACScopedDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649919EDA41200A782A9 /* RACScopedDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D22E1BA13D7A00F7D4B1 /* RACSequence.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649B19EDA41200A782A9 /* RACSequence.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D22F1BA13D7A00F7D4B1 /* RACSerialDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649D19EDA41200A782A9 /* RACSerialDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2301BA13D7A00F7D4B1 /* RACSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649F19EDA41200A782A9 /* RACSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2311BA13D7A00F7D4B1 /* RACSignal+Operations.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764A119EDA41200A782A9 /* RACSignal+Operations.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2321BA13D7A00F7D4B1 /* RACStream.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764A619EDA41200A782A9 /* RACStream.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2331BA13D7A00F7D4B1 /* RACSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764AB19EDA41200A782A9 /* RACSubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2341BA13D7A00F7D4B1 /* RACSubscriber.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764AD19EDA41200A782A9 /* RACSubscriber.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2351BA13D7A00F7D4B1 /* RACSubscriptingAssignmentTrampoline.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B019EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2361BA13D7A00F7D4B1 /* RACTargetQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B419EDA41200A782A9 /* RACTargetQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2371BA13D7A00F7D4B1 /* RACTestScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B619EDA41200A782A9 /* RACTestScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2381BA13D7A00F7D4B1 /* RACTuple.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B819EDA41200A782A9 /* RACTuple.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57A4D2391BA13D7A00F7D4B1 /* RACUnit.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764BE19EDA41200A782A9 /* RACUnit.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57D4768D1C42063C00EFE697 /* UIControl+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764CD19EDA41200A782A9 /* UIControl+RACSignalSupport.m */; }; - 57D476901C4206D400EFE697 /* UIControl+RACSignalSupportPrivate.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764CF19EDA41200A782A9 /* UIControl+RACSignalSupportPrivate.m */; }; - 57D476911C4206DA00EFE697 /* UIGestureRecognizer+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764D319EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.m */; }; - 57D476921C4206DF00EFE697 /* UISegmentedControl+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764D919EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.m */; }; - 57D476951C4206EC00EFE697 /* UITableViewCell+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764E119EDA41200A782A9 /* UITableViewCell+RACSignalSupport.m */; }; - 57D476961C4206EC00EFE697 /* UITableViewHeaderFooterView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764E319EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.m */; }; - 57D476971C4206EC00EFE697 /* UITextField+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764E519EDA41200A782A9 /* UITextField+RACSignalSupport.m */; }; - 57D476981C4206EC00EFE697 /* UITextView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764E719EDA41200A782A9 /* UITextView+RACSignalSupport.m */; }; - 57D4769A1C4206F200EFE697 /* UIButton+RACCommandSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C919EDA41200A782A9 /* UIButton+RACCommandSupport.m */; }; - 57D4769B1C4206F200EFE697 /* UICollectionReusableView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764CB19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.m */; }; - 57DC89A01C5066D400E367B7 /* UIGestureRecognizer+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764D219EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57DC89A11C50672B00E367B7 /* UIControl+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764CC19EDA41200A782A9 /* UIControl+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57DC89A21C50673C00E367B7 /* UISegmentedControl+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764D819EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57DC89A31C50674300E367B7 /* UITableViewCell+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764E019EDA41200A782A9 /* UITableViewCell+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57DC89A41C50674D00E367B7 /* UITableViewHeaderFooterView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764E219EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57DC89A51C50675700E367B7 /* UITextField+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764E419EDA41200A782A9 /* UITextField+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57DC89A61C50675F00E367B7 /* UITextView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764E619EDA41200A782A9 /* UITextView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57DC89A71C50679700E367B7 /* UIButton+RACCommandSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764C819EDA41200A782A9 /* UIButton+RACCommandSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 57DC89A81C50679E00E367B7 /* UICollectionReusableView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764CA19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 7A7065811A3F88B8001E8354 /* RACKVOProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A70657E1A3F88B8001E8354 /* RACKVOProxy.m */; }; - 7A7065821A3F88B8001E8354 /* RACKVOProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A70657E1A3F88B8001E8354 /* RACKVOProxy.m */; }; - 7A7065841A3F8967001E8354 /* RACKVOProxySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A7065831A3F8967001E8354 /* RACKVOProxySpec.m */; }; - 7A7065851A3F8967001E8354 /* RACKVOProxySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A7065831A3F8967001E8354 /* RACKVOProxySpec.m */; }; - 7DFBED081CDB8C9500EE435B /* ReactiveObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57A4D2411BA13D7A00F7D4B1 /* ReactiveObjC.framework */; }; - 7DFBED141CDB8CE600EE435B /* test-data.json in Resources */ = {isa = PBXBuildFile; fileRef = D03766B119EDA60000A782A9 /* test-data.json */; }; - 7DFBED1E1CDB8D7000EE435B /* ReactiveObjC.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 57A4D2411BA13D7A00F7D4B1 /* ReactiveObjC.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 7DFBED1F1CDB8D7800EE435B /* Quick.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 7DFBED201CDB8D7D00EE435B /* Nimble.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 7DFBED321CDB8DE300EE435B /* NSEnumeratorRACSequenceAdditionsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667819EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m */; }; - 7DFBED331CDB8DE300EE435B /* NSNotificationCenterRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667919EDA60000A782A9 /* NSNotificationCenterRACSupportSpec.m */; }; - 7DFBED351CDB8DE300EE435B /* NSObjectRACDeallocatingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667B19EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m */; }; - 7DFBED361CDB8DE300EE435B /* NSObjectRACLiftingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667C19EDA60000A782A9 /* NSObjectRACLiftingSpec.m */; }; - 7DFBED381CDB8DE300EE435B /* NSObjectRACPropertySubscribingExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667E19EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.m */; }; - 7DFBED391CDB8DE300EE435B /* NSObjectRACPropertySubscribingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667F19EDA60000A782A9 /* NSObjectRACPropertySubscribingSpec.m */; }; - 7DFBED3A1CDB8DE300EE435B /* NSObjectRACSelectorSignalSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668019EDA60000A782A9 /* NSObjectRACSelectorSignalSpec.m */; }; - 7DFBED3B1CDB8DE300EE435B /* NSStringRACKeyPathUtilitiesSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668119EDA60000A782A9 /* NSStringRACKeyPathUtilitiesSpec.m */; }; - 7DFBED3D1CDB8DE300EE435B /* NSUserDefaultsRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668419EDA60000A782A9 /* NSUserDefaultsRACSupportSpec.m */; }; - 7DFBED3E1CDB8DE300EE435B /* RACBlockTrampolineSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668619EDA60000A782A9 /* RACBlockTrampolineSpec.m */; }; - 7DFBED401CDB8DE300EE435B /* RACChannelExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668819EDA60000A782A9 /* RACChannelExamples.m */; }; - 7DFBED411CDB8DE300EE435B /* RACChannelSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668919EDA60000A782A9 /* RACChannelSpec.m */; }; - 7DFBED421CDB8DE300EE435B /* RACCommandSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668A19EDA60000A782A9 /* RACCommandSpec.m */; }; - 7DFBED431CDB8DE300EE435B /* RACCompoundDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668B19EDA60000A782A9 /* RACCompoundDisposableSpec.m */; }; - 7DFBED451CDB8DE300EE435B /* RACControlCommandExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668D19EDA60000A782A9 /* RACControlCommandExamples.m */; }; - 7DFBED461CDB8DE300EE435B /* RACDelegateProxySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668E19EDA60000A782A9 /* RACDelegateProxySpec.m */; }; - 7DFBED471CDB8DE300EE435B /* RACDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668F19EDA60000A782A9 /* RACDisposableSpec.m */; }; - 7DFBED481CDB8DE300EE435B /* RACEventSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669019EDA60000A782A9 /* RACEventSpec.m */; }; - 7DFBED491CDB8DE300EE435B /* RACKVOChannelSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669119EDA60000A782A9 /* RACKVOChannelSpec.m */; }; - 7DFBED4A1CDB8DE300EE435B /* RACKVOProxySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A7065831A3F8967001E8354 /* RACKVOProxySpec.m */; }; - 7DFBED4B1CDB8DE300EE435B /* RACKVOWrapperSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669219EDA60000A782A9 /* RACKVOWrapperSpec.m */; }; - 7DFBED4C1CDB8DE300EE435B /* RACMulticastConnectionSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669319EDA60000A782A9 /* RACMulticastConnectionSpec.m */; }; - 7DFBED4E1CDB8DE300EE435B /* RACPropertySignalExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669519EDA60000A782A9 /* RACPropertySignalExamples.m */; }; - 7DFBED4F1CDB8DE300EE435B /* RACSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669619EDA60000A782A9 /* RACSchedulerSpec.m */; }; - 7DFBED501CDB8DE300EE435B /* RACSequenceAdditionsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669719EDA60000A782A9 /* RACSequenceAdditionsSpec.m */; }; - 7DFBED521CDB8DE300EE435B /* RACSequenceExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669919EDA60000A782A9 /* RACSequenceExamples.m */; }; - 7DFBED531CDB8DE300EE435B /* RACSequenceSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669A19EDA60000A782A9 /* RACSequenceSpec.m */; }; - 7DFBED541CDB8DE300EE435B /* RACSerialDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669B19EDA60000A782A9 /* RACSerialDisposableSpec.m */; }; - 7DFBED551CDB8DE300EE435B /* RACSignalSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669C19EDA60000A782A9 /* RACSignalSpec.m */; }; - 7DFBED571CDB8DE300EE435B /* RACStreamExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A019EDA60000A782A9 /* RACStreamExamples.m */; }; - 7DFBED591CDB8DE300EE435B /* RACSubclassObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A219EDA60000A782A9 /* RACSubclassObject.m */; }; - 7DFBED5A1CDB8DE300EE435B /* RACSubjectSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A319EDA60000A782A9 /* RACSubjectSpec.m */; }; - 7DFBED5C1CDB8DE300EE435B /* RACSubscriberExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A519EDA60000A782A9 /* RACSubscriberExamples.m */; }; - 7DFBED5D1CDB8DE300EE435B /* RACSubscriberSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A619EDA60000A782A9 /* RACSubscriberSpec.m */; }; - 7DFBED5E1CDB8DE300EE435B /* RACSubscriptingAssignmentTrampolineSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A719EDA60000A782A9 /* RACSubscriptingAssignmentTrampolineSpec.m */; }; - 7DFBED5F1CDB8DE300EE435B /* RACTargetQueueSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A819EDA60000A782A9 /* RACTargetQueueSchedulerSpec.m */; }; - 7DFBED601CDB8DE300EE435B /* RACTupleSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B019EDA60000A782A9 /* RACTupleSpec.m */; }; - 7DFBED631CDB8DE300EE435B /* UIBarButtonItemRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B419EDA60000A782A9 /* UIBarButtonItemRACSupportSpec.m */; }; - 7DFBED641CDB8DE300EE435B /* UIButtonRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B519EDA60000A782A9 /* UIButtonRACSupportSpec.m */; }; - 7DFBED671CDB8DE300EE435B /* RACTestExampleScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131819EF2D9700984962 /* RACTestExampleScheduler.m */; }; - 7DFBED691CDB8DE300EE435B /* RACTestObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131A19EF2D9700984962 /* RACTestObject.m */; }; - 7DFBED6A1CDB8DE300EE435B /* RACTestSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131B19EF2D9700984962 /* RACTestSchedulerSpec.m */; }; - 7DFBED6C1CDB8DE300EE435B /* RACTestUIButton.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131D19EF2D9700984962 /* RACTestUIButton.m */; }; - 7DFBED6E1CDB918900EE435B /* UIBarButtonItem+RACCommandSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C719EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.m */; }; - 7DFBED6F1CDB926400EE435B /* UIBarButtonItem+RACCommandSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764C619EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A1046B7A1BFF5661004D8045 /* EXTRuntimeExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666719EDA57100A782A9 /* EXTRuntimeExtensions.h */; settings = {ATTRIBUTES = (Private, ); }; }; - A1046B7B1BFF5662004D8045 /* EXTRuntimeExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666719EDA57100A782A9 /* EXTRuntimeExtensions.h */; settings = {ATTRIBUTES = (Private, ); }; }; - A1046B7C1BFF5662004D8045 /* EXTRuntimeExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666719EDA57100A782A9 /* EXTRuntimeExtensions.h */; settings = {ATTRIBUTES = (Private, ); }; }; - A1046B7D1BFF5664004D8045 /* EXTRuntimeExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666719EDA57100A782A9 /* EXTRuntimeExtensions.h */; settings = {ATTRIBUTES = (Private, ); }; }; - A9B3155E1B3940750001CB9C /* EXTRuntimeExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037666819EDA57100A782A9 /* EXTRuntimeExtensions.m */; }; - A9B315601B3940750001CB9C /* NSArray+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037642B19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m */; }; - A9B315631B3940750001CB9C /* NSData+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643119EDA41200A782A9 /* NSData+RACSupport.m */; }; - A9B315641B3940750001CB9C /* NSDictionary+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643319EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m */; }; - A9B315651B3940750001CB9C /* NSEnumerator+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643519EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m */; }; - A9B315661B3940750001CB9C /* NSFileHandle+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643719EDA41200A782A9 /* NSFileHandle+RACSupport.m */; }; - A9B315671B3940750001CB9C /* NSIndexSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643919EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m */; }; - A9B315681B3940750001CB9C /* NSInvocation+RACTypeParsing.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643B19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m */; }; - A9B315691B3940750001CB9C /* NSNotificationCenter+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643D19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m */; }; - A9B3156B1B3940750001CB9C /* NSObject+RACDeallocating.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644119EDA41200A782A9 /* NSObject+RACDeallocating.m */; }; - A9B3156C1B3940750001CB9C /* NSObject+RACDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644319EDA41200A782A9 /* NSObject+RACDescription.m */; }; - A9B3156D1B3940750001CB9C /* NSObject+RACKVOWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644519EDA41200A782A9 /* NSObject+RACKVOWrapper.m */; }; - A9B3156E1B3940750001CB9C /* NSObject+RACLifting.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644719EDA41200A782A9 /* NSObject+RACLifting.m */; }; - A9B3156F1B3940750001CB9C /* NSObject+RACPropertySubscribing.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644919EDA41200A782A9 /* NSObject+RACPropertySubscribing.m */; }; - A9B315701B3940750001CB9C /* NSObject+RACSelectorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644B19EDA41200A782A9 /* NSObject+RACSelectorSignal.m */; }; - A9B315711B3940750001CB9C /* NSOrderedSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644D19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m */; }; - A9B315721B3940750001CB9C /* NSSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644F19EDA41200A782A9 /* NSSet+RACSequenceAdditions.m */; }; - A9B315731B3940750001CB9C /* NSString+RACKeyPathUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645119EDA41200A782A9 /* NSString+RACKeyPathUtilities.m */; }; - A9B315741B3940750001CB9C /* NSString+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645319EDA41200A782A9 /* NSString+RACSequenceAdditions.m */; }; - A9B315751B3940750001CB9C /* NSString+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645519EDA41200A782A9 /* NSString+RACSupport.m */; }; - A9B315781B3940750001CB9C /* NSUserDefaults+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645B19EDA41200A782A9 /* NSUserDefaults+RACSupport.m */; }; - A9B315791B3940750001CB9C /* RACArraySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645D19EDA41200A782A9 /* RACArraySequence.m */; }; - A9B3157A1B3940750001CB9C /* RACBehaviorSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646119EDA41200A782A9 /* RACBehaviorSubject.m */; }; - A9B3157B1B3940750001CB9C /* RACBlockTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646319EDA41200A782A9 /* RACBlockTrampoline.m */; }; - A9B3157C1B3940750001CB9C /* RACChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646519EDA41200A782A9 /* RACChannel.m */; }; - A9B3157D1B3940750001CB9C /* RACCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646719EDA41200A782A9 /* RACCommand.m */; }; - A9B3157E1B3940750001CB9C /* RACCompoundDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646919EDA41200A782A9 /* RACCompoundDisposable.m */; }; - A9B3157F1B3940750001CB9C /* RACDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646C19EDA41200A782A9 /* RACDelegateProxy.m */; }; - A9B315801B3940750001CB9C /* RACDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646E19EDA41200A782A9 /* RACDisposable.m */; }; - A9B315811B3940750001CB9C /* RACDynamicSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647019EDA41200A782A9 /* RACDynamicSequence.m */; }; - A9B315821B3940750001CB9C /* RACDynamicSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647219EDA41200A782A9 /* RACDynamicSignal.m */; }; - A9B315831B3940750001CB9C /* RACEagerSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647419EDA41200A782A9 /* RACEagerSequence.m */; }; - A9B315841B3940750001CB9C /* RACEmptySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647619EDA41200A782A9 /* RACEmptySequence.m */; }; - A9B315851B3940750001CB9C /* RACEmptySignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647819EDA41200A782A9 /* RACEmptySignal.m */; }; - A9B315861B3940750001CB9C /* RACErrorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647A19EDA41200A782A9 /* RACErrorSignal.m */; }; - A9B315871B3940750001CB9C /* RACEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647C19EDA41200A782A9 /* RACEvent.m */; }; - A9B315881B3940750001CB9C /* RACGroupedSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647E19EDA41200A782A9 /* RACGroupedSignal.m */; }; - A9B315891B3940750001CB9C /* RACImmediateScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648019EDA41200A782A9 /* RACImmediateScheduler.m */; }; - A9B3158A1B3940750001CB9C /* RACIndexSetSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648219EDA41200A782A9 /* RACIndexSetSequence.m */; }; - A9B3158B1B3940750001CB9C /* RACKVOChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648419EDA41200A782A9 /* RACKVOChannel.m */; }; - A9B3158C1B3940750001CB9C /* RACKVOProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A70657E1A3F88B8001E8354 /* RACKVOProxy.m */; }; - A9B3158D1B3940750001CB9C /* RACKVOTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648619EDA41200A782A9 /* RACKVOTrampoline.m */; }; - A9B3158E1B3940750001CB9C /* RACMulticastConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648819EDA41200A782A9 /* RACMulticastConnection.m */; }; - A9B315901B3940750001CB9C /* RACPassthroughSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648D19EDA41200A782A9 /* RACPassthroughSubscriber.m */; }; - A9B315911B3940750001CB9C /* RACQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648F19EDA41200A782A9 /* RACQueueScheduler.m */; }; - A9B315921B3940750001CB9C /* RACReplaySubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649219EDA41200A782A9 /* RACReplaySubject.m */; }; - A9B315931B3940750001CB9C /* RACReturnSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649419EDA41200A782A9 /* RACReturnSignal.m */; }; - A9B315941B3940750001CB9C /* RACScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649619EDA41200A782A9 /* RACScheduler.m */; }; - A9B315951B3940750001CB9C /* RACScopedDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649A19EDA41200A782A9 /* RACScopedDisposable.m */; }; - A9B315961B3940750001CB9C /* RACSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649C19EDA41200A782A9 /* RACSequence.m */; }; - A9B315971B3940750001CB9C /* RACSerialDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649E19EDA41200A782A9 /* RACSerialDisposable.m */; }; - A9B315981B3940750001CB9C /* RACSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A019EDA41200A782A9 /* RACSignal.m */; }; - A9B315991B3940750001CB9C /* RACSignal+Operations.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A219EDA41200A782A9 /* RACSignal+Operations.m */; }; - A9B3159A1B3940750001CB9C /* RACSignalSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A519EDA41200A782A9 /* RACSignalSequence.m */; }; - A9B3159B1B3940750001CB9C /* RACStream.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A719EDA41200A782A9 /* RACStream.m */; }; - A9B3159C1B3940750001CB9C /* RACStringSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AA19EDA41200A782A9 /* RACStringSequence.m */; }; - A9B3159D1B3940750001CB9C /* RACSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AC19EDA41200A782A9 /* RACSubject.m */; }; - A9B3159E1B3940750001CB9C /* RACSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AE19EDA41200A782A9 /* RACSubscriber.m */; }; - A9B3159F1B3940750001CB9C /* RACSubscriptingAssignmentTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B119EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m */; }; - A9B315A01B3940750001CB9C /* RACSubscriptionScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B319EDA41200A782A9 /* RACSubscriptionScheduler.m */; }; - A9B315A11B3940750001CB9C /* RACTargetQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B519EDA41200A782A9 /* RACTargetQueueScheduler.m */; }; - A9B315A21B3940750001CB9C /* RACTestScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B719EDA41200A782A9 /* RACTestScheduler.m */; }; - A9B315A31B3940750001CB9C /* RACTuple.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B919EDA41200A782A9 /* RACTuple.m */; }; - A9B315A41B3940750001CB9C /* RACTupleSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BB19EDA41200A782A9 /* RACTupleSequence.m */; }; - A9B315A51B3940750001CB9C /* RACUnarySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BD19EDA41200A782A9 /* RACUnarySequence.m */; }; - A9B315A61B3940750001CB9C /* RACUnit.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BF19EDA41200A782A9 /* RACUnit.m */; }; - A9B315A71B3940750001CB9C /* RACValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C119EDA41200A782A9 /* RACValueTransformer.m */; }; - A9B315CA1B3940AB0001CB9C /* ReactiveObjC.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveObjC.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315CB1B3940AB0001CB9C /* EXTKeyPathCoding.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666619EDA57100A782A9 /* EXTKeyPathCoding.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315CD1B3940AB0001CB9C /* EXTScope.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666919EDA57100A782A9 /* EXTScope.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315CE1B3940AB0001CB9C /* metamacros.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666A19EDA57100A782A9 /* metamacros.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315D01B3940AB0001CB9C /* NSArray+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037642A19EDA41200A782A9 /* NSArray+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315D31B3940AB0001CB9C /* NSData+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643019EDA41200A782A9 /* NSData+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315D41B3940AB0001CB9C /* NSDictionary+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643219EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315D51B3940AB0001CB9C /* NSEnumerator+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643419EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315D61B3940AB0001CB9C /* NSFileHandle+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643619EDA41200A782A9 /* NSFileHandle+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315D71B3940AB0001CB9C /* NSIndexSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643819EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315D91B3940AB0001CB9C /* NSNotificationCenter+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643C19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315DB1B3940AB0001CB9C /* NSObject+RACDeallocating.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644019EDA41200A782A9 /* NSObject+RACDeallocating.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315DE1B3940AB0001CB9C /* NSObject+RACLifting.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644619EDA41200A782A9 /* NSObject+RACLifting.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315DF1B3940AB0001CB9C /* NSObject+RACPropertySubscribing.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644819EDA41200A782A9 /* NSObject+RACPropertySubscribing.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315E01B3940AB0001CB9C /* NSObject+RACSelectorSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644A19EDA41200A782A9 /* NSObject+RACSelectorSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315E11B3940AB0001CB9C /* NSOrderedSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644C19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315E21B3940AB0001CB9C /* NSSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644E19EDA41200A782A9 /* NSSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315E41B3940AB0001CB9C /* NSString+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645219EDA41200A782A9 /* NSString+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315E51B3940AB0001CB9C /* NSString+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645419EDA41200A782A9 /* NSString+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315E81B3940AB0001CB9C /* NSUserDefaults+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645A19EDA41200A782A9 /* NSUserDefaults+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315EA1B3940AB0001CB9C /* RACBehaviorSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646019EDA41200A782A9 /* RACBehaviorSubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315EC1B3940AB0001CB9C /* RACChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646419EDA41200A782A9 /* RACChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315ED1B3940AC0001CB9C /* RACCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646619EDA41200A782A9 /* RACCommand.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315EE1B3940AC0001CB9C /* RACCompoundDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646819EDA41200A782A9 /* RACCompoundDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315F01B3940AC0001CB9C /* RACDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646D19EDA41200A782A9 /* RACDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315F71B3940AC0001CB9C /* RACEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = D037647B19EDA41200A782A9 /* RACEvent.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315F81B3940AC0001CB9C /* RACGroupedSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037647D19EDA41200A782A9 /* RACGroupedSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315FB1B3940AC0001CB9C /* RACKVOChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648319EDA41200A782A9 /* RACKVOChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B315FE1B3940AC0001CB9C /* RACMulticastConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648719EDA41200A782A9 /* RACMulticastConnection.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B316021B3940AD0001CB9C /* RACQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648E19EDA41200A782A9 /* RACQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B316031B3940AD0001CB9C /* RACQueueScheduler+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649019EDA41200A782A9 /* RACQueueScheduler+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B316041B3940AD0001CB9C /* RACReplaySubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649119EDA41200A782A9 /* RACReplaySubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B316061B3940AD0001CB9C /* RACScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649519EDA41200A782A9 /* RACScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B316081B3940AD0001CB9C /* RACScheduler+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649819EDA41200A782A9 /* RACScheduler+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B316091B3940AD0001CB9C /* RACScopedDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649919EDA41200A782A9 /* RACScopedDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B3160A1B3940AD0001CB9C /* RACSequence.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649B19EDA41200A782A9 /* RACSequence.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B3160B1B3940AD0001CB9C /* RACSerialDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649D19EDA41200A782A9 /* RACSerialDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B3160C1B3940AE0001CB9C /* RACSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649F19EDA41200A782A9 /* RACSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B3160D1B3940AE0001CB9C /* RACSignal+Operations.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764A119EDA41200A782A9 /* RACSignal+Operations.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B3160F1B3940AE0001CB9C /* RACStream.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764A619EDA41200A782A9 /* RACStream.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B316121B3940AE0001CB9C /* RACSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764AB19EDA41200A782A9 /* RACSubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B316131B3940AE0001CB9C /* RACSubscriber.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764AD19EDA41200A782A9 /* RACSubscriber.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B316151B3940AE0001CB9C /* RACSubscriptingAssignmentTrampoline.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B019EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B316171B3940AF0001CB9C /* RACTargetQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B419EDA41200A782A9 /* RACTargetQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B316181B3940AF0001CB9C /* RACTestScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B619EDA41200A782A9 /* RACTestScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B316191B3940AF0001CB9C /* RACTuple.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B819EDA41200A782A9 /* RACTuple.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B3161C1B3940AF0001CB9C /* RACUnit.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764BE19EDA41200A782A9 /* RACUnit.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A9B316341B394C7F0001CB9C /* RACCompoundDisposableProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D037646A19EDA41200A782A9 /* RACCompoundDisposableProvider.d */; }; - A9B316351B394C7F0001CB9C /* RACSignalProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D03764A319EDA41200A782A9 /* RACSignalProvider.d */; }; - BEBDD6E51CDC292D009A75A9 /* RACDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646B19EDA41200A782A9 /* RACDelegateProxy.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BEBDD6E61CDC292D009A75A9 /* RACDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646B19EDA41200A782A9 /* RACDelegateProxy.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BEBDD6E71CDC292E009A75A9 /* RACDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646B19EDA41200A782A9 /* RACDelegateProxy.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BEBDD6E81CDC292F009A75A9 /* RACDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646B19EDA41200A782A9 /* RACDelegateProxy.h */; settings = {ATTRIBUTES = (Public, ); }; }; - CDF066CA1CDC1CA200199626 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; }; - CDF066CB1CDC1CA200199626 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; }; - D01B7B6219EDD8FE00D26E01 /* Nimble.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - D01B7B6319EDD8FE00D26E01 /* Quick.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - D01B7B6419EDD94B00D26E01 /* ReactiveObjC.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D047260C19E49F82006002AA /* ReactiveObjC.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - D03764E819EDA41200A782A9 /* NSArray+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037642A19EDA41200A782A9 /* NSArray+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03764E919EDA41200A782A9 /* NSArray+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037642A19EDA41200A782A9 /* NSArray+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03764EA19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037642B19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m */; }; - D03764EB19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037642B19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m */; }; - D03764EC19EDA41200A782A9 /* NSControl+RACCommandSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037642C19EDA41200A782A9 /* NSControl+RACCommandSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03764EE19EDA41200A782A9 /* NSControl+RACCommandSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037642D19EDA41200A782A9 /* NSControl+RACCommandSupport.m */; }; - D03764F019EDA41200A782A9 /* NSControl+RACTextSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037642E19EDA41200A782A9 /* NSControl+RACTextSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03764F219EDA41200A782A9 /* NSControl+RACTextSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037642F19EDA41200A782A9 /* NSControl+RACTextSignalSupport.m */; }; - D03764F419EDA41200A782A9 /* NSData+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643019EDA41200A782A9 /* NSData+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03764F519EDA41200A782A9 /* NSData+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643019EDA41200A782A9 /* NSData+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03764F619EDA41200A782A9 /* NSData+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643119EDA41200A782A9 /* NSData+RACSupport.m */; }; - D03764F719EDA41200A782A9 /* NSData+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643119EDA41200A782A9 /* NSData+RACSupport.m */; }; - D03764F819EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643219EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03764F919EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643219EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03764FA19EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643319EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m */; }; - D03764FB19EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643319EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m */; }; - D03764FC19EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643419EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03764FD19EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643419EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03764FE19EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643519EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m */; }; - D03764FF19EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643519EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m */; }; - D037650019EDA41200A782A9 /* NSFileHandle+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643619EDA41200A782A9 /* NSFileHandle+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037650119EDA41200A782A9 /* NSFileHandle+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643619EDA41200A782A9 /* NSFileHandle+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037650219EDA41200A782A9 /* NSFileHandle+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643719EDA41200A782A9 /* NSFileHandle+RACSupport.m */; }; - D037650319EDA41200A782A9 /* NSFileHandle+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643719EDA41200A782A9 /* NSFileHandle+RACSupport.m */; }; - D037650419EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643819EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037650519EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643819EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037650619EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643919EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m */; }; - D037650719EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643919EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m */; }; - D037650A19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643B19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m */; }; - D037650B19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643B19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m */; }; - D037650C19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643C19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037650D19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643C19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037650E19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643D19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m */; }; - D037650F19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643D19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m */; }; - D037651019EDA41200A782A9 /* NSObject+RACAppKitBindings.h in Headers */ = {isa = PBXBuildFile; fileRef = D037643E19EDA41200A782A9 /* NSObject+RACAppKitBindings.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037651219EDA41200A782A9 /* NSObject+RACAppKitBindings.m in Sources */ = {isa = PBXBuildFile; fileRef = D037643F19EDA41200A782A9 /* NSObject+RACAppKitBindings.m */; }; - D037651419EDA41200A782A9 /* NSObject+RACDeallocating.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644019EDA41200A782A9 /* NSObject+RACDeallocating.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037651519EDA41200A782A9 /* NSObject+RACDeallocating.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644019EDA41200A782A9 /* NSObject+RACDeallocating.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037651619EDA41200A782A9 /* NSObject+RACDeallocating.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644119EDA41200A782A9 /* NSObject+RACDeallocating.m */; }; - D037651719EDA41200A782A9 /* NSObject+RACDeallocating.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644119EDA41200A782A9 /* NSObject+RACDeallocating.m */; }; - D037651A19EDA41200A782A9 /* NSObject+RACDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644319EDA41200A782A9 /* NSObject+RACDescription.m */; }; - D037651B19EDA41200A782A9 /* NSObject+RACDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644319EDA41200A782A9 /* NSObject+RACDescription.m */; }; - D037651E19EDA41200A782A9 /* NSObject+RACKVOWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644519EDA41200A782A9 /* NSObject+RACKVOWrapper.m */; }; - D037651F19EDA41200A782A9 /* NSObject+RACKVOWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644519EDA41200A782A9 /* NSObject+RACKVOWrapper.m */; }; - D037652019EDA41200A782A9 /* NSObject+RACLifting.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644619EDA41200A782A9 /* NSObject+RACLifting.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037652119EDA41200A782A9 /* NSObject+RACLifting.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644619EDA41200A782A9 /* NSObject+RACLifting.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037652219EDA41200A782A9 /* NSObject+RACLifting.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644719EDA41200A782A9 /* NSObject+RACLifting.m */; }; - D037652319EDA41200A782A9 /* NSObject+RACLifting.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644719EDA41200A782A9 /* NSObject+RACLifting.m */; }; - D037652419EDA41200A782A9 /* NSObject+RACPropertySubscribing.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644819EDA41200A782A9 /* NSObject+RACPropertySubscribing.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037652519EDA41200A782A9 /* NSObject+RACPropertySubscribing.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644819EDA41200A782A9 /* NSObject+RACPropertySubscribing.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037652619EDA41200A782A9 /* NSObject+RACPropertySubscribing.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644919EDA41200A782A9 /* NSObject+RACPropertySubscribing.m */; }; - D037652719EDA41200A782A9 /* NSObject+RACPropertySubscribing.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644919EDA41200A782A9 /* NSObject+RACPropertySubscribing.m */; }; - D037652819EDA41200A782A9 /* NSObject+RACSelectorSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644A19EDA41200A782A9 /* NSObject+RACSelectorSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037652919EDA41200A782A9 /* NSObject+RACSelectorSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644A19EDA41200A782A9 /* NSObject+RACSelectorSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037652A19EDA41200A782A9 /* NSObject+RACSelectorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644B19EDA41200A782A9 /* NSObject+RACSelectorSignal.m */; }; - D037652B19EDA41200A782A9 /* NSObject+RACSelectorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644B19EDA41200A782A9 /* NSObject+RACSelectorSignal.m */; }; - D037652C19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644C19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037652D19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644C19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037652E19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644D19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m */; }; - D037652F19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644D19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m */; }; - D037653019EDA41200A782A9 /* NSSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644E19EDA41200A782A9 /* NSSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037653119EDA41200A782A9 /* NSSet+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037644E19EDA41200A782A9 /* NSSet+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037653219EDA41200A782A9 /* NSSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644F19EDA41200A782A9 /* NSSet+RACSequenceAdditions.m */; }; - D037653319EDA41200A782A9 /* NSSet+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037644F19EDA41200A782A9 /* NSSet+RACSequenceAdditions.m */; }; - D037653619EDA41200A782A9 /* NSString+RACKeyPathUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645119EDA41200A782A9 /* NSString+RACKeyPathUtilities.m */; }; - D037653719EDA41200A782A9 /* NSString+RACKeyPathUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645119EDA41200A782A9 /* NSString+RACKeyPathUtilities.m */; }; - D037653819EDA41200A782A9 /* NSString+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645219EDA41200A782A9 /* NSString+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037653919EDA41200A782A9 /* NSString+RACSequenceAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645219EDA41200A782A9 /* NSString+RACSequenceAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037653A19EDA41200A782A9 /* NSString+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645319EDA41200A782A9 /* NSString+RACSequenceAdditions.m */; }; - D037653B19EDA41200A782A9 /* NSString+RACSequenceAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645319EDA41200A782A9 /* NSString+RACSequenceAdditions.m */; }; - D037653C19EDA41200A782A9 /* NSString+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645419EDA41200A782A9 /* NSString+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037653D19EDA41200A782A9 /* NSString+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645419EDA41200A782A9 /* NSString+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037653E19EDA41200A782A9 /* NSString+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645519EDA41200A782A9 /* NSString+RACSupport.m */; }; - D037653F19EDA41200A782A9 /* NSString+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645519EDA41200A782A9 /* NSString+RACSupport.m */; }; - D037654019EDA41200A782A9 /* NSText+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645619EDA41200A782A9 /* NSText+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037654219EDA41200A782A9 /* NSText+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645719EDA41200A782A9 /* NSText+RACSignalSupport.m */; }; - D037654419EDA41200A782A9 /* NSURLConnection+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645819EDA41200A782A9 /* NSURLConnection+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037654519EDA41200A782A9 /* NSURLConnection+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645819EDA41200A782A9 /* NSURLConnection+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037654619EDA41200A782A9 /* NSURLConnection+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645919EDA41200A782A9 /* NSURLConnection+RACSupport.m */; }; - D037654719EDA41200A782A9 /* NSURLConnection+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645919EDA41200A782A9 /* NSURLConnection+RACSupport.m */; }; - D037654819EDA41200A782A9 /* NSUserDefaults+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645A19EDA41200A782A9 /* NSUserDefaults+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037654919EDA41200A782A9 /* NSUserDefaults+RACSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D037645A19EDA41200A782A9 /* NSUserDefaults+RACSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037654A19EDA41200A782A9 /* NSUserDefaults+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645B19EDA41200A782A9 /* NSUserDefaults+RACSupport.m */; }; - D037654B19EDA41200A782A9 /* NSUserDefaults+RACSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645B19EDA41200A782A9 /* NSUserDefaults+RACSupport.m */; }; - D037654E19EDA41200A782A9 /* RACArraySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645D19EDA41200A782A9 /* RACArraySequence.m */; }; - D037654F19EDA41200A782A9 /* RACArraySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037645D19EDA41200A782A9 /* RACArraySequence.m */; }; - D037655619EDA41200A782A9 /* RACBehaviorSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646119EDA41200A782A9 /* RACBehaviorSubject.m */; }; - D037655719EDA41200A782A9 /* RACBehaviorSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646119EDA41200A782A9 /* RACBehaviorSubject.m */; }; - D037655A19EDA41200A782A9 /* RACBlockTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646319EDA41200A782A9 /* RACBlockTrampoline.m */; }; - D037655B19EDA41200A782A9 /* RACBlockTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646319EDA41200A782A9 /* RACBlockTrampoline.m */; }; - D037655C19EDA41200A782A9 /* RACChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646419EDA41200A782A9 /* RACChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037655D19EDA41200A782A9 /* RACChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646419EDA41200A782A9 /* RACChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037655E19EDA41200A782A9 /* RACChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646519EDA41200A782A9 /* RACChannel.m */; }; - D037655F19EDA41200A782A9 /* RACChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646519EDA41200A782A9 /* RACChannel.m */; }; - D037656019EDA41200A782A9 /* RACCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646619EDA41200A782A9 /* RACCommand.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037656119EDA41200A782A9 /* RACCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646619EDA41200A782A9 /* RACCommand.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037656219EDA41200A782A9 /* RACCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646719EDA41200A782A9 /* RACCommand.m */; }; - D037656319EDA41200A782A9 /* RACCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646719EDA41200A782A9 /* RACCommand.m */; }; - D037656419EDA41200A782A9 /* RACCompoundDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646819EDA41200A782A9 /* RACCompoundDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037656519EDA41200A782A9 /* RACCompoundDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646819EDA41200A782A9 /* RACCompoundDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037656619EDA41200A782A9 /* RACCompoundDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646919EDA41200A782A9 /* RACCompoundDisposable.m */; }; - D037656719EDA41200A782A9 /* RACCompoundDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646919EDA41200A782A9 /* RACCompoundDisposable.m */; }; - D037656819EDA41200A782A9 /* RACCompoundDisposableProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D037646A19EDA41200A782A9 /* RACCompoundDisposableProvider.d */; }; - D037656919EDA41200A782A9 /* RACCompoundDisposableProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D037646A19EDA41200A782A9 /* RACCompoundDisposableProvider.d */; }; - D037656C19EDA41200A782A9 /* RACDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646C19EDA41200A782A9 /* RACDelegateProxy.m */; }; - D037656D19EDA41200A782A9 /* RACDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646C19EDA41200A782A9 /* RACDelegateProxy.m */; }; - D037656E19EDA41200A782A9 /* RACDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646D19EDA41200A782A9 /* RACDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037656F19EDA41200A782A9 /* RACDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646D19EDA41200A782A9 /* RACDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037657019EDA41200A782A9 /* RACDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646E19EDA41200A782A9 /* RACDisposable.m */; }; - D037657119EDA41200A782A9 /* RACDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037646E19EDA41200A782A9 /* RACDisposable.m */; }; - D037657419EDA41200A782A9 /* RACDynamicSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647019EDA41200A782A9 /* RACDynamicSequence.m */; }; - D037657519EDA41200A782A9 /* RACDynamicSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647019EDA41200A782A9 /* RACDynamicSequence.m */; }; - D037657819EDA41200A782A9 /* RACDynamicSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647219EDA41200A782A9 /* RACDynamicSignal.m */; }; - D037657919EDA41200A782A9 /* RACDynamicSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647219EDA41200A782A9 /* RACDynamicSignal.m */; }; - D037657C19EDA41200A782A9 /* RACEagerSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647419EDA41200A782A9 /* RACEagerSequence.m */; }; - D037657D19EDA41200A782A9 /* RACEagerSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647419EDA41200A782A9 /* RACEagerSequence.m */; }; - D037658019EDA41200A782A9 /* RACEmptySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647619EDA41200A782A9 /* RACEmptySequence.m */; }; - D037658119EDA41200A782A9 /* RACEmptySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647619EDA41200A782A9 /* RACEmptySequence.m */; }; - D037658419EDA41200A782A9 /* RACEmptySignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647819EDA41200A782A9 /* RACEmptySignal.m */; }; - D037658519EDA41200A782A9 /* RACEmptySignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647819EDA41200A782A9 /* RACEmptySignal.m */; }; - D037658819EDA41200A782A9 /* RACErrorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647A19EDA41200A782A9 /* RACErrorSignal.m */; }; - D037658919EDA41200A782A9 /* RACErrorSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647A19EDA41200A782A9 /* RACErrorSignal.m */; }; - D037658A19EDA41200A782A9 /* RACEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = D037647B19EDA41200A782A9 /* RACEvent.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037658B19EDA41200A782A9 /* RACEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = D037647B19EDA41200A782A9 /* RACEvent.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037658C19EDA41200A782A9 /* RACEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647C19EDA41200A782A9 /* RACEvent.m */; }; - D037658D19EDA41200A782A9 /* RACEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647C19EDA41200A782A9 /* RACEvent.m */; }; - D037658E19EDA41200A782A9 /* RACGroupedSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037647D19EDA41200A782A9 /* RACGroupedSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037658F19EDA41200A782A9 /* RACGroupedSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037647D19EDA41200A782A9 /* RACGroupedSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037659019EDA41200A782A9 /* RACGroupedSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647E19EDA41200A782A9 /* RACGroupedSignal.m */; }; - D037659119EDA41200A782A9 /* RACGroupedSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037647E19EDA41200A782A9 /* RACGroupedSignal.m */; }; - D037659419EDA41200A782A9 /* RACImmediateScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648019EDA41200A782A9 /* RACImmediateScheduler.m */; }; - D037659519EDA41200A782A9 /* RACImmediateScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648019EDA41200A782A9 /* RACImmediateScheduler.m */; }; - D037659819EDA41200A782A9 /* RACIndexSetSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648219EDA41200A782A9 /* RACIndexSetSequence.m */; }; - D037659919EDA41200A782A9 /* RACIndexSetSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648219EDA41200A782A9 /* RACIndexSetSequence.m */; }; - D037659A19EDA41200A782A9 /* RACKVOChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648319EDA41200A782A9 /* RACKVOChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037659B19EDA41200A782A9 /* RACKVOChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648319EDA41200A782A9 /* RACKVOChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037659C19EDA41200A782A9 /* RACKVOChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648419EDA41200A782A9 /* RACKVOChannel.m */; }; - D037659D19EDA41200A782A9 /* RACKVOChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648419EDA41200A782A9 /* RACKVOChannel.m */; }; - D03765A019EDA41200A782A9 /* RACKVOTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648619EDA41200A782A9 /* RACKVOTrampoline.m */; }; - D03765A119EDA41200A782A9 /* RACKVOTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648619EDA41200A782A9 /* RACKVOTrampoline.m */; }; - D03765A219EDA41200A782A9 /* RACMulticastConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648719EDA41200A782A9 /* RACMulticastConnection.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765A319EDA41200A782A9 /* RACMulticastConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648719EDA41200A782A9 /* RACMulticastConnection.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765A419EDA41200A782A9 /* RACMulticastConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648819EDA41200A782A9 /* RACMulticastConnection.m */; }; - D03765A519EDA41200A782A9 /* RACMulticastConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648819EDA41200A782A9 /* RACMulticastConnection.m */; }; - D03765AE19EDA41200A782A9 /* RACPassthroughSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648D19EDA41200A782A9 /* RACPassthroughSubscriber.m */; }; - D03765AF19EDA41200A782A9 /* RACPassthroughSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648D19EDA41200A782A9 /* RACPassthroughSubscriber.m */; }; - D03765B019EDA41200A782A9 /* RACQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648E19EDA41200A782A9 /* RACQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765B119EDA41200A782A9 /* RACQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D037648E19EDA41200A782A9 /* RACQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765B219EDA41200A782A9 /* RACQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648F19EDA41200A782A9 /* RACQueueScheduler.m */; }; - D03765B319EDA41200A782A9 /* RACQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037648F19EDA41200A782A9 /* RACQueueScheduler.m */; }; - D03765B419EDA41200A782A9 /* RACQueueScheduler+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649019EDA41200A782A9 /* RACQueueScheduler+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765B519EDA41200A782A9 /* RACQueueScheduler+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649019EDA41200A782A9 /* RACQueueScheduler+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765B619EDA41200A782A9 /* RACReplaySubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649119EDA41200A782A9 /* RACReplaySubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765B719EDA41200A782A9 /* RACReplaySubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649119EDA41200A782A9 /* RACReplaySubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765B819EDA41200A782A9 /* RACReplaySubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649219EDA41200A782A9 /* RACReplaySubject.m */; }; - D03765B919EDA41200A782A9 /* RACReplaySubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649219EDA41200A782A9 /* RACReplaySubject.m */; }; - D03765BC19EDA41200A782A9 /* RACReturnSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649419EDA41200A782A9 /* RACReturnSignal.m */; }; - D03765BD19EDA41200A782A9 /* RACReturnSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649419EDA41200A782A9 /* RACReturnSignal.m */; }; - D03765BE19EDA41200A782A9 /* RACScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649519EDA41200A782A9 /* RACScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765BF19EDA41200A782A9 /* RACScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649519EDA41200A782A9 /* RACScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765C019EDA41200A782A9 /* RACScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649619EDA41200A782A9 /* RACScheduler.m */; }; - D03765C119EDA41200A782A9 /* RACScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649619EDA41200A782A9 /* RACScheduler.m */; }; - D03765C419EDA41200A782A9 /* RACScheduler+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649819EDA41200A782A9 /* RACScheduler+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765C519EDA41200A782A9 /* RACScheduler+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649819EDA41200A782A9 /* RACScheduler+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765C619EDA41200A782A9 /* RACScopedDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649919EDA41200A782A9 /* RACScopedDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765C719EDA41200A782A9 /* RACScopedDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649919EDA41200A782A9 /* RACScopedDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765C819EDA41200A782A9 /* RACScopedDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649A19EDA41200A782A9 /* RACScopedDisposable.m */; }; - D03765C919EDA41200A782A9 /* RACScopedDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649A19EDA41200A782A9 /* RACScopedDisposable.m */; }; - D03765CA19EDA41200A782A9 /* RACSequence.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649B19EDA41200A782A9 /* RACSequence.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765CB19EDA41200A782A9 /* RACSequence.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649B19EDA41200A782A9 /* RACSequence.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765CC19EDA41200A782A9 /* RACSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649C19EDA41200A782A9 /* RACSequence.m */; }; - D03765CD19EDA41200A782A9 /* RACSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649C19EDA41200A782A9 /* RACSequence.m */; }; - D03765CE19EDA41200A782A9 /* RACSerialDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649D19EDA41200A782A9 /* RACSerialDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765CF19EDA41200A782A9 /* RACSerialDisposable.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649D19EDA41200A782A9 /* RACSerialDisposable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765D019EDA41200A782A9 /* RACSerialDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649E19EDA41200A782A9 /* RACSerialDisposable.m */; }; - D03765D119EDA41200A782A9 /* RACSerialDisposable.m in Sources */ = {isa = PBXBuildFile; fileRef = D037649E19EDA41200A782A9 /* RACSerialDisposable.m */; }; - D03765D219EDA41200A782A9 /* RACSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649F19EDA41200A782A9 /* RACSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765D319EDA41200A782A9 /* RACSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = D037649F19EDA41200A782A9 /* RACSignal.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765D419EDA41200A782A9 /* RACSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A019EDA41200A782A9 /* RACSignal.m */; }; - D03765D519EDA41200A782A9 /* RACSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A019EDA41200A782A9 /* RACSignal.m */; }; - D03765D619EDA41200A782A9 /* RACSignal+Operations.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764A119EDA41200A782A9 /* RACSignal+Operations.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765D719EDA41200A782A9 /* RACSignal+Operations.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764A119EDA41200A782A9 /* RACSignal+Operations.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765D819EDA41200A782A9 /* RACSignal+Operations.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A219EDA41200A782A9 /* RACSignal+Operations.m */; }; - D03765D919EDA41200A782A9 /* RACSignal+Operations.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A219EDA41200A782A9 /* RACSignal+Operations.m */; }; - D03765DA19EDA41200A782A9 /* RACSignalProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D03764A319EDA41200A782A9 /* RACSignalProvider.d */; }; - D03765DB19EDA41200A782A9 /* RACSignalProvider.d in Sources */ = {isa = PBXBuildFile; fileRef = D03764A319EDA41200A782A9 /* RACSignalProvider.d */; }; - D03765DE19EDA41200A782A9 /* RACSignalSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A519EDA41200A782A9 /* RACSignalSequence.m */; }; - D03765DF19EDA41200A782A9 /* RACSignalSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A519EDA41200A782A9 /* RACSignalSequence.m */; }; - D03765E019EDA41200A782A9 /* RACStream.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764A619EDA41200A782A9 /* RACStream.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765E119EDA41200A782A9 /* RACStream.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764A619EDA41200A782A9 /* RACStream.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765E219EDA41200A782A9 /* RACStream.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A719EDA41200A782A9 /* RACStream.m */; }; - D03765E319EDA41200A782A9 /* RACStream.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764A719EDA41200A782A9 /* RACStream.m */; }; - D03765E819EDA41200A782A9 /* RACStringSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AA19EDA41200A782A9 /* RACStringSequence.m */; }; - D03765E919EDA41200A782A9 /* RACStringSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AA19EDA41200A782A9 /* RACStringSequence.m */; }; - D03765EA19EDA41200A782A9 /* RACSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764AB19EDA41200A782A9 /* RACSubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765EB19EDA41200A782A9 /* RACSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764AB19EDA41200A782A9 /* RACSubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765EC19EDA41200A782A9 /* RACSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AC19EDA41200A782A9 /* RACSubject.m */; }; - D03765ED19EDA41200A782A9 /* RACSubject.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AC19EDA41200A782A9 /* RACSubject.m */; }; - D03765EE19EDA41200A782A9 /* RACSubscriber.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764AD19EDA41200A782A9 /* RACSubscriber.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765EF19EDA41200A782A9 /* RACSubscriber.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764AD19EDA41200A782A9 /* RACSubscriber.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765F019EDA41200A782A9 /* RACSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AE19EDA41200A782A9 /* RACSubscriber.m */; }; - D03765F119EDA41200A782A9 /* RACSubscriber.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764AE19EDA41200A782A9 /* RACSubscriber.m */; }; - D03765F419EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B019EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765F519EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B019EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765F619EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B119EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m */; }; - D03765F719EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B119EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m */; }; - D03765FA19EDA41200A782A9 /* RACSubscriptionScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B319EDA41200A782A9 /* RACSubscriptionScheduler.m */; }; - D03765FB19EDA41200A782A9 /* RACSubscriptionScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B319EDA41200A782A9 /* RACSubscriptionScheduler.m */; }; - D03765FC19EDA41200A782A9 /* RACTargetQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B419EDA41200A782A9 /* RACTargetQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765FD19EDA41200A782A9 /* RACTargetQueueScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B419EDA41200A782A9 /* RACTargetQueueScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03765FE19EDA41200A782A9 /* RACTargetQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B519EDA41200A782A9 /* RACTargetQueueScheduler.m */; }; - D03765FF19EDA41200A782A9 /* RACTargetQueueScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B519EDA41200A782A9 /* RACTargetQueueScheduler.m */; }; - D037660019EDA41200A782A9 /* RACTestScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B619EDA41200A782A9 /* RACTestScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037660119EDA41200A782A9 /* RACTestScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B619EDA41200A782A9 /* RACTestScheduler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037660219EDA41200A782A9 /* RACTestScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B719EDA41200A782A9 /* RACTestScheduler.m */; }; - D037660319EDA41200A782A9 /* RACTestScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B719EDA41200A782A9 /* RACTestScheduler.m */; }; - D037660419EDA41200A782A9 /* RACTuple.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B819EDA41200A782A9 /* RACTuple.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037660519EDA41200A782A9 /* RACTuple.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764B819EDA41200A782A9 /* RACTuple.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037660619EDA41200A782A9 /* RACTuple.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B919EDA41200A782A9 /* RACTuple.m */; }; - D037660719EDA41200A782A9 /* RACTuple.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764B919EDA41200A782A9 /* RACTuple.m */; }; - D037660A19EDA41200A782A9 /* RACTupleSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BB19EDA41200A782A9 /* RACTupleSequence.m */; }; - D037660B19EDA41200A782A9 /* RACTupleSequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BB19EDA41200A782A9 /* RACTupleSequence.m */; }; - D037660E19EDA41200A782A9 /* RACUnarySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BD19EDA41200A782A9 /* RACUnarySequence.m */; }; - D037660F19EDA41200A782A9 /* RACUnarySequence.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BD19EDA41200A782A9 /* RACUnarySequence.m */; }; - D037661019EDA41200A782A9 /* RACUnit.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764BE19EDA41200A782A9 /* RACUnit.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037661119EDA41200A782A9 /* RACUnit.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764BE19EDA41200A782A9 /* RACUnit.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037661219EDA41200A782A9 /* RACUnit.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BF19EDA41200A782A9 /* RACUnit.m */; }; - D037661319EDA41200A782A9 /* RACUnit.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764BF19EDA41200A782A9 /* RACUnit.m */; }; - D037661619EDA41200A782A9 /* RACValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C119EDA41200A782A9 /* RACValueTransformer.m */; }; - D037661719EDA41200A782A9 /* RACValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C119EDA41200A782A9 /* RACValueTransformer.m */; }; - D037661919EDA41200A782A9 /* UIActionSheet+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764C219EDA41200A782A9 /* UIActionSheet+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037661B19EDA41200A782A9 /* UIActionSheet+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C319EDA41200A782A9 /* UIActionSheet+RACSignalSupport.m */; }; - D037661D19EDA41200A782A9 /* UIAlertView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764C419EDA41200A782A9 /* UIAlertView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037661F19EDA41200A782A9 /* UIAlertView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C519EDA41200A782A9 /* UIAlertView+RACSignalSupport.m */; }; - D037662119EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764C619EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037662319EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C719EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.m */; }; - D037662519EDA41200A782A9 /* UIButton+RACCommandSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764C819EDA41200A782A9 /* UIButton+RACCommandSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037662719EDA41200A782A9 /* UIButton+RACCommandSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764C919EDA41200A782A9 /* UIButton+RACCommandSupport.m */; }; - D037662919EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764CA19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037662B19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764CB19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.m */; }; - D037662D19EDA41200A782A9 /* UIControl+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764CC19EDA41200A782A9 /* UIControl+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037662F19EDA41200A782A9 /* UIControl+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764CD19EDA41200A782A9 /* UIControl+RACSignalSupport.m */; }; - D037663319EDA41200A782A9 /* UIControl+RACSignalSupportPrivate.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764CF19EDA41200A782A9 /* UIControl+RACSignalSupportPrivate.m */; }; - D037663519EDA41200A782A9 /* UIDatePicker+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764D019EDA41200A782A9 /* UIDatePicker+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037663719EDA41200A782A9 /* UIDatePicker+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764D119EDA41200A782A9 /* UIDatePicker+RACSignalSupport.m */; }; - D037663919EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764D219EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037663B19EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764D319EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.m */; }; - D037663D19EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764D419EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037663F19EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764D519EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.m */; }; - D037664119EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764D619EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037664319EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764D719EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.m */; }; - D037664519EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764D819EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037664719EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764D919EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.m */; }; - D037664919EDA41200A782A9 /* UISlider+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764DA19EDA41200A782A9 /* UISlider+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037664B19EDA41200A782A9 /* UISlider+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764DB19EDA41200A782A9 /* UISlider+RACSignalSupport.m */; }; - D037664D19EDA41200A782A9 /* UIStepper+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764DC19EDA41200A782A9 /* UIStepper+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037664F19EDA41200A782A9 /* UIStepper+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764DD19EDA41200A782A9 /* UIStepper+RACSignalSupport.m */; }; - D037665119EDA41200A782A9 /* UISwitch+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764DE19EDA41200A782A9 /* UISwitch+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037665319EDA41200A782A9 /* UISwitch+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764DF19EDA41200A782A9 /* UISwitch+RACSignalSupport.m */; }; - D037665519EDA41200A782A9 /* UITableViewCell+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764E019EDA41200A782A9 /* UITableViewCell+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037665719EDA41200A782A9 /* UITableViewCell+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764E119EDA41200A782A9 /* UITableViewCell+RACSignalSupport.m */; }; - D037665919EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764E219EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037665B19EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764E319EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.m */; }; - D037665D19EDA41200A782A9 /* UITextField+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764E419EDA41200A782A9 /* UITextField+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037665F19EDA41200A782A9 /* UITextField+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764E519EDA41200A782A9 /* UITextField+RACSignalSupport.m */; }; - D037666119EDA41200A782A9 /* UITextView+RACSignalSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = D03764E619EDA41200A782A9 /* UITextView+RACSignalSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037666319EDA41200A782A9 /* UITextView+RACSignalSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = D03764E719EDA41200A782A9 /* UITextView+RACSignalSupport.m */; }; - D037666419EDA43C00A782A9 /* ReactiveObjC.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveObjC.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037666B19EDA57100A782A9 /* EXTKeyPathCoding.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666619EDA57100A782A9 /* EXTKeyPathCoding.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037666C19EDA57100A782A9 /* EXTKeyPathCoding.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666619EDA57100A782A9 /* EXTKeyPathCoding.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037666F19EDA57100A782A9 /* EXTRuntimeExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037666819EDA57100A782A9 /* EXTRuntimeExtensions.m */; }; - D037667019EDA57100A782A9 /* EXTRuntimeExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D037666819EDA57100A782A9 /* EXTRuntimeExtensions.m */; }; - D037667119EDA57100A782A9 /* EXTScope.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666919EDA57100A782A9 /* EXTScope.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037667219EDA57100A782A9 /* EXTScope.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666919EDA57100A782A9 /* EXTScope.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037667319EDA57100A782A9 /* metamacros.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666A19EDA57100A782A9 /* metamacros.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037667419EDA57100A782A9 /* metamacros.h in Headers */ = {isa = PBXBuildFile; fileRef = D037666A19EDA57100A782A9 /* metamacros.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03766B919EDA60000A782A9 /* NSControllerRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667619EDA60000A782A9 /* NSControllerRACSupportSpec.m */; }; - D03766BD19EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667819EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m */; }; - D03766BE19EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667819EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m */; }; - D03766BF19EDA60000A782A9 /* NSNotificationCenterRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667919EDA60000A782A9 /* NSNotificationCenterRACSupportSpec.m */; }; - D03766C019EDA60000A782A9 /* NSNotificationCenterRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667919EDA60000A782A9 /* NSNotificationCenterRACSupportSpec.m */; }; - D03766C119EDA60000A782A9 /* NSObjectRACAppKitBindingsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667A19EDA60000A782A9 /* NSObjectRACAppKitBindingsSpec.m */; }; - D03766C319EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667B19EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m */; }; - D03766C419EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667B19EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m */; }; - D03766C519EDA60000A782A9 /* NSObjectRACLiftingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667C19EDA60000A782A9 /* NSObjectRACLiftingSpec.m */; }; - D03766C619EDA60000A782A9 /* NSObjectRACLiftingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667C19EDA60000A782A9 /* NSObjectRACLiftingSpec.m */; }; - D03766C719EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667E19EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.m */; }; - D03766C819EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667E19EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.m */; }; - D03766C919EDA60000A782A9 /* NSObjectRACPropertySubscribingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667F19EDA60000A782A9 /* NSObjectRACPropertySubscribingSpec.m */; }; - D03766CA19EDA60000A782A9 /* NSObjectRACPropertySubscribingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037667F19EDA60000A782A9 /* NSObjectRACPropertySubscribingSpec.m */; }; - D03766CB19EDA60000A782A9 /* NSObjectRACSelectorSignalSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668019EDA60000A782A9 /* NSObjectRACSelectorSignalSpec.m */; }; - D03766CC19EDA60000A782A9 /* NSObjectRACSelectorSignalSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668019EDA60000A782A9 /* NSObjectRACSelectorSignalSpec.m */; }; - D03766CD19EDA60000A782A9 /* NSStringRACKeyPathUtilitiesSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668119EDA60000A782A9 /* NSStringRACKeyPathUtilitiesSpec.m */; }; - D03766CE19EDA60000A782A9 /* NSStringRACKeyPathUtilitiesSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668119EDA60000A782A9 /* NSStringRACKeyPathUtilitiesSpec.m */; }; - D03766D119EDA60000A782A9 /* NSURLConnectionRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668319EDA60000A782A9 /* NSURLConnectionRACSupportSpec.m */; }; - D03766D219EDA60000A782A9 /* NSURLConnectionRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668319EDA60000A782A9 /* NSURLConnectionRACSupportSpec.m */; }; - D03766D319EDA60000A782A9 /* NSUserDefaultsRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668419EDA60000A782A9 /* NSUserDefaultsRACSupportSpec.m */; }; - D03766D419EDA60000A782A9 /* NSUserDefaultsRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668419EDA60000A782A9 /* NSUserDefaultsRACSupportSpec.m */; }; - D03766D719EDA60000A782A9 /* RACBlockTrampolineSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668619EDA60000A782A9 /* RACBlockTrampolineSpec.m */; }; - D03766D819EDA60000A782A9 /* RACBlockTrampolineSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668619EDA60000A782A9 /* RACBlockTrampolineSpec.m */; }; - D03766D919EDA60000A782A9 /* RACChannelExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668819EDA60000A782A9 /* RACChannelExamples.m */; }; - D03766DA19EDA60000A782A9 /* RACChannelExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668819EDA60000A782A9 /* RACChannelExamples.m */; }; - D03766DB19EDA60000A782A9 /* RACChannelSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668919EDA60000A782A9 /* RACChannelSpec.m */; }; - D03766DC19EDA60000A782A9 /* RACChannelSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668919EDA60000A782A9 /* RACChannelSpec.m */; }; - D03766DD19EDA60000A782A9 /* RACCommandSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668A19EDA60000A782A9 /* RACCommandSpec.m */; }; - D03766DE19EDA60000A782A9 /* RACCommandSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668A19EDA60000A782A9 /* RACCommandSpec.m */; }; - D03766DF19EDA60000A782A9 /* RACCompoundDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668B19EDA60000A782A9 /* RACCompoundDisposableSpec.m */; }; - D03766E019EDA60000A782A9 /* RACCompoundDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668B19EDA60000A782A9 /* RACCompoundDisposableSpec.m */; }; - D03766E119EDA60000A782A9 /* RACControlCommandExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668D19EDA60000A782A9 /* RACControlCommandExamples.m */; }; - D03766E219EDA60000A782A9 /* RACControlCommandExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668D19EDA60000A782A9 /* RACControlCommandExamples.m */; }; - D03766E319EDA60000A782A9 /* RACDelegateProxySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668E19EDA60000A782A9 /* RACDelegateProxySpec.m */; }; - D03766E419EDA60000A782A9 /* RACDelegateProxySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668E19EDA60000A782A9 /* RACDelegateProxySpec.m */; }; - D03766E519EDA60000A782A9 /* RACDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668F19EDA60000A782A9 /* RACDisposableSpec.m */; }; - D03766E619EDA60000A782A9 /* RACDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037668F19EDA60000A782A9 /* RACDisposableSpec.m */; }; - D03766E719EDA60000A782A9 /* RACEventSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669019EDA60000A782A9 /* RACEventSpec.m */; }; - D03766E819EDA60000A782A9 /* RACEventSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669019EDA60000A782A9 /* RACEventSpec.m */; }; - D03766E919EDA60000A782A9 /* RACKVOChannelSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669119EDA60000A782A9 /* RACKVOChannelSpec.m */; }; - D03766EA19EDA60000A782A9 /* RACKVOChannelSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669119EDA60000A782A9 /* RACKVOChannelSpec.m */; }; - D03766EB19EDA60000A782A9 /* RACKVOWrapperSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669219EDA60000A782A9 /* RACKVOWrapperSpec.m */; }; - D03766EC19EDA60000A782A9 /* RACKVOWrapperSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669219EDA60000A782A9 /* RACKVOWrapperSpec.m */; }; - D03766ED19EDA60000A782A9 /* RACMulticastConnectionSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669319EDA60000A782A9 /* RACMulticastConnectionSpec.m */; }; - D03766EE19EDA60000A782A9 /* RACMulticastConnectionSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669319EDA60000A782A9 /* RACMulticastConnectionSpec.m */; }; - D03766EF19EDA60000A782A9 /* RACPropertySignalExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669519EDA60000A782A9 /* RACPropertySignalExamples.m */; }; - D03766F019EDA60000A782A9 /* RACPropertySignalExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669519EDA60000A782A9 /* RACPropertySignalExamples.m */; }; - D03766F119EDA60000A782A9 /* RACSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669619EDA60000A782A9 /* RACSchedulerSpec.m */; }; - D03766F219EDA60000A782A9 /* RACSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669619EDA60000A782A9 /* RACSchedulerSpec.m */; }; - D03766F319EDA60000A782A9 /* RACSequenceAdditionsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669719EDA60000A782A9 /* RACSequenceAdditionsSpec.m */; }; - D03766F419EDA60000A782A9 /* RACSequenceAdditionsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669719EDA60000A782A9 /* RACSequenceAdditionsSpec.m */; }; - D03766F519EDA60000A782A9 /* RACSequenceExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669919EDA60000A782A9 /* RACSequenceExamples.m */; }; - D03766F619EDA60000A782A9 /* RACSequenceExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669919EDA60000A782A9 /* RACSequenceExamples.m */; }; - D03766F719EDA60000A782A9 /* RACSequenceSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669A19EDA60000A782A9 /* RACSequenceSpec.m */; }; - D03766F819EDA60000A782A9 /* RACSequenceSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669A19EDA60000A782A9 /* RACSequenceSpec.m */; }; - D03766F919EDA60000A782A9 /* RACSerialDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669B19EDA60000A782A9 /* RACSerialDisposableSpec.m */; }; - D03766FA19EDA60000A782A9 /* RACSerialDisposableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669B19EDA60000A782A9 /* RACSerialDisposableSpec.m */; }; - D03766FB19EDA60000A782A9 /* RACSignalSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669C19EDA60000A782A9 /* RACSignalSpec.m */; }; - D03766FC19EDA60000A782A9 /* RACSignalSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D037669C19EDA60000A782A9 /* RACSignalSpec.m */; }; - D03766FF19EDA60000A782A9 /* RACStreamExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A019EDA60000A782A9 /* RACStreamExamples.m */; }; - D037670019EDA60000A782A9 /* RACStreamExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A019EDA60000A782A9 /* RACStreamExamples.m */; }; - D037670119EDA60000A782A9 /* RACSubclassObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A219EDA60000A782A9 /* RACSubclassObject.m */; }; - D037670219EDA60000A782A9 /* RACSubclassObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A219EDA60000A782A9 /* RACSubclassObject.m */; }; - D037670319EDA60000A782A9 /* RACSubjectSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A319EDA60000A782A9 /* RACSubjectSpec.m */; }; - D037670419EDA60000A782A9 /* RACSubjectSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A319EDA60000A782A9 /* RACSubjectSpec.m */; }; - D037670519EDA60000A782A9 /* RACSubscriberExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A519EDA60000A782A9 /* RACSubscriberExamples.m */; }; - D037670619EDA60000A782A9 /* RACSubscriberExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A519EDA60000A782A9 /* RACSubscriberExamples.m */; }; - D037670719EDA60000A782A9 /* RACSubscriberSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A619EDA60000A782A9 /* RACSubscriberSpec.m */; }; - D037670819EDA60000A782A9 /* RACSubscriberSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A619EDA60000A782A9 /* RACSubscriberSpec.m */; }; - D037670919EDA60000A782A9 /* RACSubscriptingAssignmentTrampolineSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A719EDA60000A782A9 /* RACSubscriptingAssignmentTrampolineSpec.m */; }; - D037670A19EDA60000A782A9 /* RACSubscriptingAssignmentTrampolineSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A719EDA60000A782A9 /* RACSubscriptingAssignmentTrampolineSpec.m */; }; - D037670B19EDA60000A782A9 /* RACTargetQueueSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A819EDA60000A782A9 /* RACTargetQueueSchedulerSpec.m */; }; - D037670C19EDA60000A782A9 /* RACTargetQueueSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766A819EDA60000A782A9 /* RACTargetQueueSchedulerSpec.m */; }; - D037671519EDA60000A782A9 /* RACTupleSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B019EDA60000A782A9 /* RACTupleSpec.m */; }; - D037671619EDA60000A782A9 /* RACTupleSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B019EDA60000A782A9 /* RACTupleSpec.m */; }; - D037671719EDA60000A782A9 /* test-data.json in Resources */ = {isa = PBXBuildFile; fileRef = D03766B119EDA60000A782A9 /* test-data.json */; }; - D037671819EDA60000A782A9 /* test-data.json in Resources */ = {isa = PBXBuildFile; fileRef = D03766B119EDA60000A782A9 /* test-data.json */; }; - D037671A19EDA60000A782A9 /* UIActionSheetRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B219EDA60000A782A9 /* UIActionSheetRACSupportSpec.m */; }; - D037671C19EDA60000A782A9 /* UIAlertViewRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B319EDA60000A782A9 /* UIAlertViewRACSupportSpec.m */; }; - D037671E19EDA60000A782A9 /* UIBarButtonItemRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B419EDA60000A782A9 /* UIBarButtonItemRACSupportSpec.m */; }; - D037672019EDA60000A782A9 /* UIButtonRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B519EDA60000A782A9 /* UIButtonRACSupportSpec.m */; }; - D037672419EDA60000A782A9 /* UIImagePickerControllerRACSupportSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03766B719EDA60000A782A9 /* UIImagePickerControllerRACSupportSpec.m */; }; - D037672719EDA63400A782A9 /* RACBehaviorSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646019EDA41200A782A9 /* RACBehaviorSubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037672819EDA63500A782A9 /* RACBehaviorSubject.h in Headers */ = {isa = PBXBuildFile; fileRef = D037646019EDA41200A782A9 /* RACBehaviorSubject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037672D19EDA75D00A782A9 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; }; - D037672F19EDA78B00A782A9 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; }; - D04725F019E49ED7006002AA /* ReactiveObjC.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveObjC.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D04725F619E49ED7006002AA /* ReactiveObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D04725EA19E49ED7006002AA /* ReactiveObjC.framework */; }; - D047261719E49F82006002AA /* ReactiveObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D047260C19E49F82006002AA /* ReactiveObjC.framework */; }; - D05E662519EDD82000904ACA /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; }; - D05E662619EDD83000904ACA /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; }; - D0C3131E19EF2D9700984962 /* RACTestExampleScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131819EF2D9700984962 /* RACTestExampleScheduler.m */; }; - D0C3131F19EF2D9700984962 /* RACTestExampleScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131819EF2D9700984962 /* RACTestExampleScheduler.m */; }; - D0C3132019EF2D9700984962 /* RACTestObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131A19EF2D9700984962 /* RACTestObject.m */; }; - D0C3132119EF2D9700984962 /* RACTestObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131A19EF2D9700984962 /* RACTestObject.m */; }; - D0C3132219EF2D9700984962 /* RACTestSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131B19EF2D9700984962 /* RACTestSchedulerSpec.m */; }; - D0C3132319EF2D9700984962 /* RACTestSchedulerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131B19EF2D9700984962 /* RACTestSchedulerSpec.m */; }; - D0C3132519EF2D9700984962 /* RACTestUIButton.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C3131D19EF2D9700984962 /* RACTestUIButton.m */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 7DFBED091CDB8C9500EE435B /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D04725E119E49ED7006002AA /* Project object */; - proxyType = 1; - remoteGlobalIDString = 57A4D1AF1BA13D7A00F7D4B1; - remoteInfo = "ReactiveCocoa-tvOS"; - }; - D04725F719E49ED7006002AA /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D04725E119E49ED7006002AA /* Project object */; - proxyType = 1; - remoteGlobalIDString = D04725E919E49ED7006002AA; - remoteInfo = ReactiveCocoa; - }; - D047261819E49F82006002AA /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D04725E119E49ED7006002AA /* Project object */; - proxyType = 1; - remoteGlobalIDString = D047260B19E49F82006002AA; - remoteInfo = ReactiveCocoa; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 7DFBED151CDB8CEC00EE435B /* Copy Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 7DFBED201CDB8D7D00EE435B /* Nimble.framework in Copy Frameworks */, - 7DFBED1F1CDB8D7800EE435B /* Quick.framework in Copy Frameworks */, - 7DFBED1E1CDB8D7000EE435B /* ReactiveObjC.framework in Copy Frameworks */, - ); - name = "Copy Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; - D01B7B6119EDD8F600D26E01 /* Copy Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - D01B7B6219EDD8FE00D26E01 /* Nimble.framework in Copy Frameworks */, - D01B7B6319EDD8FE00D26E01 /* Quick.framework in Copy Frameworks */, - D01B7B6419EDD94B00D26E01 /* ReactiveObjC.framework in Copy Frameworks */, - ); - name = "Copy Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 314304151ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MKAnnotationView+RACSignalSupport.h"; sourceTree = ""; }; - 314304161ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MKAnnotationView+RACSignalSupport.m"; sourceTree = ""; }; - 57A4D2411BA13D7A00F7D4B1 /* ReactiveObjC.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveObjC.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Application.xcconfig"; sourceTree = ""; }; - 57A4D2451BA13F9700F7D4B1 /* tvOS-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Base.xcconfig"; sourceTree = ""; }; - 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Framework.xcconfig"; sourceTree = ""; }; - 57A4D2471BA13F9700F7D4B1 /* tvOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-StaticLibrary.xcconfig"; sourceTree = ""; }; - 7A70657D1A3F88B8001E8354 /* RACKVOProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACKVOProxy.h; sourceTree = ""; }; - 7A70657E1A3F88B8001E8354 /* RACKVOProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACKVOProxy.m; sourceTree = ""; }; - 7A7065831A3F8967001E8354 /* RACKVOProxySpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACKVOProxySpec.m; sourceTree = ""; }; - 7DFBED031CDB8C9500EE435B /* ReactiveObjCTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveObjCTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - A97451331B3A935E00F48E55 /* watchOS-Application.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Application.xcconfig"; sourceTree = ""; }; - A97451341B3A935E00F48E55 /* watchOS-Base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Base.xcconfig"; sourceTree = ""; }; - A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Framework.xcconfig"; sourceTree = ""; }; - A97451361B3A935E00F48E55 /* watchOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-StaticLibrary.xcconfig"; sourceTree = ""; }; - A9B315541B3940610001CB9C /* ReactiveObjC.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveObjC.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - BE330A0E1D634F1E00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = build/Debug/ReactiveSwift.framework; sourceTree = ""; }; - BE330A101D634F2900806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = build/Debug/ReactiveSwift.framework; sourceTree = ""; }; - BE330A121D634F2E00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-iphoneos/ReactiveSwift.framework"; sourceTree = ""; }; - BE330A141D634F4000806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-iphoneos/ReactiveSwift.framework"; sourceTree = ""; }; - BE330A161D634F4E00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-watchos/ReactiveSwift.framework"; sourceTree = ""; }; - BE330A181D634F5900806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-appletvos/ReactiveSwift.framework"; sourceTree = ""; }; - BE330A1A1D634F5F00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-appletvos/ReactiveSwift.framework"; sourceTree = ""; }; - D037642A19EDA41200A782A9 /* NSArray+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+RACSequenceAdditions.h"; sourceTree = ""; }; - D037642B19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+RACSequenceAdditions.m"; sourceTree = ""; }; - D037642C19EDA41200A782A9 /* NSControl+RACCommandSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSControl+RACCommandSupport.h"; sourceTree = ""; }; - D037642D19EDA41200A782A9 /* NSControl+RACCommandSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSControl+RACCommandSupport.m"; sourceTree = ""; }; - D037642E19EDA41200A782A9 /* NSControl+RACTextSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSControl+RACTextSignalSupport.h"; sourceTree = ""; }; - D037642F19EDA41200A782A9 /* NSControl+RACTextSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSControl+RACTextSignalSupport.m"; sourceTree = ""; }; - D037643019EDA41200A782A9 /* NSData+RACSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+RACSupport.h"; sourceTree = ""; }; - D037643119EDA41200A782A9 /* NSData+RACSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+RACSupport.m"; sourceTree = ""; }; - D037643219EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+RACSequenceAdditions.h"; sourceTree = ""; }; - D037643319EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+RACSequenceAdditions.m"; sourceTree = ""; }; - D037643419EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSEnumerator+RACSequenceAdditions.h"; sourceTree = ""; }; - D037643519EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSEnumerator+RACSequenceAdditions.m"; sourceTree = ""; }; - D037643619EDA41200A782A9 /* NSFileHandle+RACSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSFileHandle+RACSupport.h"; sourceTree = ""; }; - D037643719EDA41200A782A9 /* NSFileHandle+RACSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSFileHandle+RACSupport.m"; sourceTree = ""; }; - D037643819EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSIndexSet+RACSequenceAdditions.h"; sourceTree = ""; }; - D037643919EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSIndexSet+RACSequenceAdditions.m"; sourceTree = ""; }; - D037643A19EDA41200A782A9 /* NSInvocation+RACTypeParsing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSInvocation+RACTypeParsing.h"; sourceTree = ""; }; - D037643B19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSInvocation+RACTypeParsing.m"; sourceTree = ""; }; - D037643C19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNotificationCenter+RACSupport.h"; sourceTree = ""; }; - D037643D19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSNotificationCenter+RACSupport.m"; sourceTree = ""; }; - D037643E19EDA41200A782A9 /* NSObject+RACAppKitBindings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACAppKitBindings.h"; sourceTree = ""; }; - D037643F19EDA41200A782A9 /* NSObject+RACAppKitBindings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACAppKitBindings.m"; sourceTree = ""; }; - D037644019EDA41200A782A9 /* NSObject+RACDeallocating.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACDeallocating.h"; sourceTree = ""; }; - D037644119EDA41200A782A9 /* NSObject+RACDeallocating.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACDeallocating.m"; sourceTree = ""; }; - D037644219EDA41200A782A9 /* NSObject+RACDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACDescription.h"; sourceTree = ""; }; - D037644319EDA41200A782A9 /* NSObject+RACDescription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACDescription.m"; sourceTree = ""; }; - D037644419EDA41200A782A9 /* NSObject+RACKVOWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACKVOWrapper.h"; sourceTree = ""; }; - D037644519EDA41200A782A9 /* NSObject+RACKVOWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACKVOWrapper.m"; sourceTree = ""; }; - D037644619EDA41200A782A9 /* NSObject+RACLifting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACLifting.h"; sourceTree = ""; }; - D037644719EDA41200A782A9 /* NSObject+RACLifting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACLifting.m"; sourceTree = ""; }; - D037644819EDA41200A782A9 /* NSObject+RACPropertySubscribing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACPropertySubscribing.h"; sourceTree = ""; }; - D037644919EDA41200A782A9 /* NSObject+RACPropertySubscribing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACPropertySubscribing.m"; sourceTree = ""; }; - D037644A19EDA41200A782A9 /* NSObject+RACSelectorSignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+RACSelectorSignal.h"; sourceTree = ""; }; - D037644B19EDA41200A782A9 /* NSObject+RACSelectorSignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+RACSelectorSignal.m"; sourceTree = ""; }; - D037644C19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSOrderedSet+RACSequenceAdditions.h"; sourceTree = ""; }; - D037644D19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSOrderedSet+RACSequenceAdditions.m"; sourceTree = ""; }; - D037644E19EDA41200A782A9 /* NSSet+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSSet+RACSequenceAdditions.h"; sourceTree = ""; }; - D037644F19EDA41200A782A9 /* NSSet+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSSet+RACSequenceAdditions.m"; sourceTree = ""; }; - D037645019EDA41200A782A9 /* NSString+RACKeyPathUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+RACKeyPathUtilities.h"; sourceTree = ""; }; - D037645119EDA41200A782A9 /* NSString+RACKeyPathUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+RACKeyPathUtilities.m"; sourceTree = ""; }; - D037645219EDA41200A782A9 /* NSString+RACSequenceAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+RACSequenceAdditions.h"; sourceTree = ""; }; - D037645319EDA41200A782A9 /* NSString+RACSequenceAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+RACSequenceAdditions.m"; sourceTree = ""; }; - D037645419EDA41200A782A9 /* NSString+RACSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+RACSupport.h"; sourceTree = ""; }; - D037645519EDA41200A782A9 /* NSString+RACSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+RACSupport.m"; sourceTree = ""; }; - D037645619EDA41200A782A9 /* NSText+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSText+RACSignalSupport.h"; sourceTree = ""; }; - D037645719EDA41200A782A9 /* NSText+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSText+RACSignalSupport.m"; sourceTree = ""; }; - D037645819EDA41200A782A9 /* NSURLConnection+RACSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSURLConnection+RACSupport.h"; sourceTree = ""; }; - D037645919EDA41200A782A9 /* NSURLConnection+RACSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSURLConnection+RACSupport.m"; sourceTree = ""; }; - D037645A19EDA41200A782A9 /* NSUserDefaults+RACSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSUserDefaults+RACSupport.h"; sourceTree = ""; }; - D037645B19EDA41200A782A9 /* NSUserDefaults+RACSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSUserDefaults+RACSupport.m"; sourceTree = ""; }; - D037645C19EDA41200A782A9 /* RACArraySequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACArraySequence.h; sourceTree = ""; }; - D037645D19EDA41200A782A9 /* RACArraySequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACArraySequence.m; sourceTree = ""; }; - D037646019EDA41200A782A9 /* RACBehaviorSubject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACBehaviorSubject.h; sourceTree = ""; }; - D037646119EDA41200A782A9 /* RACBehaviorSubject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACBehaviorSubject.m; sourceTree = ""; }; - D037646219EDA41200A782A9 /* RACBlockTrampoline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACBlockTrampoline.h; sourceTree = ""; }; - D037646319EDA41200A782A9 /* RACBlockTrampoline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACBlockTrampoline.m; sourceTree = ""; }; - D037646419EDA41200A782A9 /* RACChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACChannel.h; sourceTree = ""; }; - D037646519EDA41200A782A9 /* RACChannel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACChannel.m; sourceTree = ""; }; - D037646619EDA41200A782A9 /* RACCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACCommand.h; sourceTree = ""; }; - D037646719EDA41200A782A9 /* RACCommand.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACCommand.m; sourceTree = ""; }; - D037646819EDA41200A782A9 /* RACCompoundDisposable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACCompoundDisposable.h; sourceTree = ""; }; - D037646919EDA41200A782A9 /* RACCompoundDisposable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACCompoundDisposable.m; sourceTree = ""; }; - D037646A19EDA41200A782A9 /* RACCompoundDisposableProvider.d */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.dtrace; path = RACCompoundDisposableProvider.d; sourceTree = ""; }; - D037646B19EDA41200A782A9 /* RACDelegateProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACDelegateProxy.h; sourceTree = ""; }; - D037646C19EDA41200A782A9 /* RACDelegateProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACDelegateProxy.m; sourceTree = ""; }; - D037646D19EDA41200A782A9 /* RACDisposable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACDisposable.h; sourceTree = ""; }; - D037646E19EDA41200A782A9 /* RACDisposable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACDisposable.m; sourceTree = ""; }; - D037646F19EDA41200A782A9 /* RACDynamicSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACDynamicSequence.h; sourceTree = ""; }; - D037647019EDA41200A782A9 /* RACDynamicSequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACDynamicSequence.m; sourceTree = ""; }; - D037647119EDA41200A782A9 /* RACDynamicSignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACDynamicSignal.h; sourceTree = ""; }; - D037647219EDA41200A782A9 /* RACDynamicSignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACDynamicSignal.m; sourceTree = ""; }; - D037647319EDA41200A782A9 /* RACEagerSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACEagerSequence.h; sourceTree = ""; }; - D037647419EDA41200A782A9 /* RACEagerSequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACEagerSequence.m; sourceTree = ""; }; - D037647519EDA41200A782A9 /* RACEmptySequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACEmptySequence.h; sourceTree = ""; }; - D037647619EDA41200A782A9 /* RACEmptySequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACEmptySequence.m; sourceTree = ""; }; - D037647719EDA41200A782A9 /* RACEmptySignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACEmptySignal.h; sourceTree = ""; }; - D037647819EDA41200A782A9 /* RACEmptySignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACEmptySignal.m; sourceTree = ""; }; - D037647919EDA41200A782A9 /* RACErrorSignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACErrorSignal.h; sourceTree = ""; }; - D037647A19EDA41200A782A9 /* RACErrorSignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACErrorSignal.m; sourceTree = ""; }; - D037647B19EDA41200A782A9 /* RACEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACEvent.h; sourceTree = ""; }; - D037647C19EDA41200A782A9 /* RACEvent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACEvent.m; sourceTree = ""; }; - D037647D19EDA41200A782A9 /* RACGroupedSignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACGroupedSignal.h; sourceTree = ""; }; - D037647E19EDA41200A782A9 /* RACGroupedSignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACGroupedSignal.m; sourceTree = ""; }; - D037647F19EDA41200A782A9 /* RACImmediateScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACImmediateScheduler.h; sourceTree = ""; }; - D037648019EDA41200A782A9 /* RACImmediateScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACImmediateScheduler.m; sourceTree = ""; }; - D037648119EDA41200A782A9 /* RACIndexSetSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACIndexSetSequence.h; sourceTree = ""; }; - D037648219EDA41200A782A9 /* RACIndexSetSequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACIndexSetSequence.m; sourceTree = ""; }; - D037648319EDA41200A782A9 /* RACKVOChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACKVOChannel.h; sourceTree = ""; }; - D037648419EDA41200A782A9 /* RACKVOChannel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACKVOChannel.m; sourceTree = ""; }; - D037648519EDA41200A782A9 /* RACKVOTrampoline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACKVOTrampoline.h; sourceTree = ""; }; - D037648619EDA41200A782A9 /* RACKVOTrampoline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACKVOTrampoline.m; sourceTree = ""; }; - D037648719EDA41200A782A9 /* RACMulticastConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACMulticastConnection.h; sourceTree = ""; }; - D037648819EDA41200A782A9 /* RACMulticastConnection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACMulticastConnection.m; sourceTree = ""; }; - D037648919EDA41200A782A9 /* RACMulticastConnection+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RACMulticastConnection+Private.h"; sourceTree = ""; }; - D037648C19EDA41200A782A9 /* RACPassthroughSubscriber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACPassthroughSubscriber.h; sourceTree = ""; }; - D037648D19EDA41200A782A9 /* RACPassthroughSubscriber.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACPassthroughSubscriber.m; sourceTree = ""; }; - D037648E19EDA41200A782A9 /* RACQueueScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACQueueScheduler.h; sourceTree = ""; }; - D037648F19EDA41200A782A9 /* RACQueueScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACQueueScheduler.m; sourceTree = ""; }; - D037649019EDA41200A782A9 /* RACQueueScheduler+Subclass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RACQueueScheduler+Subclass.h"; sourceTree = ""; }; - D037649119EDA41200A782A9 /* RACReplaySubject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACReplaySubject.h; sourceTree = ""; }; - D037649219EDA41200A782A9 /* RACReplaySubject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACReplaySubject.m; sourceTree = ""; }; - D037649319EDA41200A782A9 /* RACReturnSignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACReturnSignal.h; sourceTree = ""; }; - D037649419EDA41200A782A9 /* RACReturnSignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACReturnSignal.m; sourceTree = ""; }; - D037649519EDA41200A782A9 /* RACScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACScheduler.h; sourceTree = ""; }; - D037649619EDA41200A782A9 /* RACScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACScheduler.m; sourceTree = ""; }; - D037649719EDA41200A782A9 /* RACScheduler+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RACScheduler+Private.h"; sourceTree = ""; }; - D037649819EDA41200A782A9 /* RACScheduler+Subclass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RACScheduler+Subclass.h"; sourceTree = ""; }; - D037649919EDA41200A782A9 /* RACScopedDisposable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACScopedDisposable.h; sourceTree = ""; }; - D037649A19EDA41200A782A9 /* RACScopedDisposable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACScopedDisposable.m; sourceTree = ""; }; - D037649B19EDA41200A782A9 /* RACSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSequence.h; sourceTree = ""; }; - D037649C19EDA41200A782A9 /* RACSequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSequence.m; sourceTree = ""; }; - D037649D19EDA41200A782A9 /* RACSerialDisposable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSerialDisposable.h; sourceTree = ""; }; - D037649E19EDA41200A782A9 /* RACSerialDisposable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSerialDisposable.m; sourceTree = ""; }; - D037649F19EDA41200A782A9 /* RACSignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSignal.h; sourceTree = ""; }; - D03764A019EDA41200A782A9 /* RACSignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSignal.m; sourceTree = ""; }; - D03764A119EDA41200A782A9 /* RACSignal+Operations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RACSignal+Operations.h"; sourceTree = ""; }; - D03764A219EDA41200A782A9 /* RACSignal+Operations.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RACSignal+Operations.m"; sourceTree = ""; }; - D03764A319EDA41200A782A9 /* RACSignalProvider.d */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.dtrace; path = RACSignalProvider.d; sourceTree = ""; }; - D03764A419EDA41200A782A9 /* RACSignalSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSignalSequence.h; sourceTree = ""; }; - D03764A519EDA41200A782A9 /* RACSignalSequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSignalSequence.m; sourceTree = ""; }; - D03764A619EDA41200A782A9 /* RACStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACStream.h; sourceTree = ""; }; - D03764A719EDA41200A782A9 /* RACStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACStream.m; sourceTree = ""; }; - D03764A819EDA41200A782A9 /* RACStream+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RACStream+Private.h"; sourceTree = ""; }; - D03764A919EDA41200A782A9 /* RACStringSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACStringSequence.h; sourceTree = ""; }; - D03764AA19EDA41200A782A9 /* RACStringSequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACStringSequence.m; sourceTree = ""; }; - D03764AB19EDA41200A782A9 /* RACSubject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSubject.h; sourceTree = ""; }; - D03764AC19EDA41200A782A9 /* RACSubject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubject.m; sourceTree = ""; }; - D03764AD19EDA41200A782A9 /* RACSubscriber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSubscriber.h; sourceTree = ""; }; - D03764AE19EDA41200A782A9 /* RACSubscriber.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubscriber.m; sourceTree = ""; }; - D03764AF19EDA41200A782A9 /* RACSubscriber+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RACSubscriber+Private.h"; sourceTree = ""; }; - D03764B019EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSubscriptingAssignmentTrampoline.h; sourceTree = ""; }; - D03764B119EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubscriptingAssignmentTrampoline.m; sourceTree = ""; }; - D03764B219EDA41200A782A9 /* RACSubscriptionScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSubscriptionScheduler.h; sourceTree = ""; }; - D03764B319EDA41200A782A9 /* RACSubscriptionScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubscriptionScheduler.m; sourceTree = ""; }; - D03764B419EDA41200A782A9 /* RACTargetQueueScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTargetQueueScheduler.h; sourceTree = ""; }; - D03764B519EDA41200A782A9 /* RACTargetQueueScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTargetQueueScheduler.m; sourceTree = ""; }; - D03764B619EDA41200A782A9 /* RACTestScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTestScheduler.h; sourceTree = ""; }; - D03764B719EDA41200A782A9 /* RACTestScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTestScheduler.m; sourceTree = ""; }; - D03764B819EDA41200A782A9 /* RACTuple.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTuple.h; sourceTree = ""; }; - D03764B919EDA41200A782A9 /* RACTuple.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTuple.m; sourceTree = ""; }; - D03764BA19EDA41200A782A9 /* RACTupleSequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTupleSequence.h; sourceTree = ""; }; - D03764BB19EDA41200A782A9 /* RACTupleSequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTupleSequence.m; sourceTree = ""; }; - D03764BC19EDA41200A782A9 /* RACUnarySequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACUnarySequence.h; sourceTree = ""; }; - D03764BD19EDA41200A782A9 /* RACUnarySequence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACUnarySequence.m; sourceTree = ""; }; - D03764BE19EDA41200A782A9 /* RACUnit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACUnit.h; sourceTree = ""; }; - D03764BF19EDA41200A782A9 /* RACUnit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACUnit.m; sourceTree = ""; }; - D03764C019EDA41200A782A9 /* RACValueTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACValueTransformer.h; sourceTree = ""; }; - D03764C119EDA41200A782A9 /* RACValueTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACValueTransformer.m; sourceTree = ""; }; - D03764C219EDA41200A782A9 /* UIActionSheet+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIActionSheet+RACSignalSupport.h"; sourceTree = ""; }; - D03764C319EDA41200A782A9 /* UIActionSheet+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIActionSheet+RACSignalSupport.m"; sourceTree = ""; }; - D03764C419EDA41200A782A9 /* UIAlertView+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIAlertView+RACSignalSupport.h"; sourceTree = ""; }; - D03764C519EDA41200A782A9 /* UIAlertView+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIAlertView+RACSignalSupport.m"; sourceTree = ""; }; - D03764C619EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIBarButtonItem+RACCommandSupport.h"; sourceTree = ""; }; - D03764C719EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIBarButtonItem+RACCommandSupport.m"; sourceTree = ""; }; - D03764C819EDA41200A782A9 /* UIButton+RACCommandSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIButton+RACCommandSupport.h"; sourceTree = ""; }; - D03764C919EDA41200A782A9 /* UIButton+RACCommandSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIButton+RACCommandSupport.m"; sourceTree = ""; }; - D03764CA19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionReusableView+RACSignalSupport.h"; sourceTree = ""; }; - D03764CB19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionReusableView+RACSignalSupport.m"; sourceTree = ""; }; - D03764CC19EDA41200A782A9 /* UIControl+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIControl+RACSignalSupport.h"; sourceTree = ""; }; - D03764CD19EDA41200A782A9 /* UIControl+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIControl+RACSignalSupport.m"; sourceTree = ""; }; - D03764CE19EDA41200A782A9 /* UIControl+RACSignalSupportPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIControl+RACSignalSupportPrivate.h"; sourceTree = ""; }; - D03764CF19EDA41200A782A9 /* UIControl+RACSignalSupportPrivate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIControl+RACSignalSupportPrivate.m"; sourceTree = ""; }; - D03764D019EDA41200A782A9 /* UIDatePicker+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIDatePicker+RACSignalSupport.h"; sourceTree = ""; }; - D03764D119EDA41200A782A9 /* UIDatePicker+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIDatePicker+RACSignalSupport.m"; sourceTree = ""; }; - D03764D219EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIGestureRecognizer+RACSignalSupport.h"; sourceTree = ""; }; - D03764D319EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIGestureRecognizer+RACSignalSupport.m"; sourceTree = ""; }; - D03764D419EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImagePickerController+RACSignalSupport.h"; sourceTree = ""; }; - D03764D519EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImagePickerController+RACSignalSupport.m"; sourceTree = ""; }; - D03764D619EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIRefreshControl+RACCommandSupport.h"; sourceTree = ""; }; - D03764D719EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIRefreshControl+RACCommandSupport.m"; sourceTree = ""; }; - D03764D819EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UISegmentedControl+RACSignalSupport.h"; sourceTree = ""; }; - D03764D919EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UISegmentedControl+RACSignalSupport.m"; sourceTree = ""; }; - D03764DA19EDA41200A782A9 /* UISlider+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UISlider+RACSignalSupport.h"; sourceTree = ""; }; - D03764DB19EDA41200A782A9 /* UISlider+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UISlider+RACSignalSupport.m"; sourceTree = ""; }; - D03764DC19EDA41200A782A9 /* UIStepper+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIStepper+RACSignalSupport.h"; sourceTree = ""; }; - D03764DD19EDA41200A782A9 /* UIStepper+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIStepper+RACSignalSupport.m"; sourceTree = ""; }; - D03764DE19EDA41200A782A9 /* UISwitch+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UISwitch+RACSignalSupport.h"; sourceTree = ""; }; - D03764DF19EDA41200A782A9 /* UISwitch+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UISwitch+RACSignalSupport.m"; sourceTree = ""; }; - D03764E019EDA41200A782A9 /* UITableViewCell+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableViewCell+RACSignalSupport.h"; sourceTree = ""; }; - D03764E119EDA41200A782A9 /* UITableViewCell+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITableViewCell+RACSignalSupport.m"; sourceTree = ""; }; - D03764E219EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableViewHeaderFooterView+RACSignalSupport.h"; sourceTree = ""; }; - D03764E319EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITableViewHeaderFooterView+RACSignalSupport.m"; sourceTree = ""; }; - D03764E419EDA41200A782A9 /* UITextField+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITextField+RACSignalSupport.h"; sourceTree = ""; }; - D03764E519EDA41200A782A9 /* UITextField+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITextField+RACSignalSupport.m"; sourceTree = ""; }; - D03764E619EDA41200A782A9 /* UITextView+RACSignalSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITextView+RACSignalSupport.h"; sourceTree = ""; }; - D03764E719EDA41200A782A9 /* UITextView+RACSignalSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITextView+RACSignalSupport.m"; sourceTree = ""; }; - D037666619EDA57100A782A9 /* EXTKeyPathCoding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EXTKeyPathCoding.h; sourceTree = ""; }; - D037666719EDA57100A782A9 /* EXTRuntimeExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EXTRuntimeExtensions.h; sourceTree = ""; }; - D037666819EDA57100A782A9 /* EXTRuntimeExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EXTRuntimeExtensions.m; sourceTree = ""; }; - D037666919EDA57100A782A9 /* EXTScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EXTScope.h; sourceTree = ""; }; - D037666A19EDA57100A782A9 /* metamacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = metamacros.h; sourceTree = ""; }; - D037667619EDA60000A782A9 /* NSControllerRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSControllerRACSupportSpec.m; sourceTree = ""; }; - D037667819EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSEnumeratorRACSequenceAdditionsSpec.m; sourceTree = ""; }; - D037667919EDA60000A782A9 /* NSNotificationCenterRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSNotificationCenterRACSupportSpec.m; sourceTree = ""; }; - D037667A19EDA60000A782A9 /* NSObjectRACAppKitBindingsSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSObjectRACAppKitBindingsSpec.m; sourceTree = ""; }; - D037667B19EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSObjectRACDeallocatingSpec.m; sourceTree = ""; }; - D037667C19EDA60000A782A9 /* NSObjectRACLiftingSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSObjectRACLiftingSpec.m; sourceTree = ""; }; - D037667D19EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSObjectRACPropertySubscribingExamples.h; sourceTree = ""; }; - D037667E19EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSObjectRACPropertySubscribingExamples.m; sourceTree = ""; }; - D037667F19EDA60000A782A9 /* NSObjectRACPropertySubscribingSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSObjectRACPropertySubscribingSpec.m; sourceTree = ""; }; - D037668019EDA60000A782A9 /* NSObjectRACSelectorSignalSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSObjectRACSelectorSignalSpec.m; sourceTree = ""; }; - D037668119EDA60000A782A9 /* NSStringRACKeyPathUtilitiesSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSStringRACKeyPathUtilitiesSpec.m; sourceTree = ""; }; - D037668319EDA60000A782A9 /* NSURLConnectionRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSURLConnectionRACSupportSpec.m; sourceTree = ""; }; - D037668419EDA60000A782A9 /* NSUserDefaultsRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSUserDefaultsRACSupportSpec.m; sourceTree = ""; }; - D037668619EDA60000A782A9 /* RACBlockTrampolineSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACBlockTrampolineSpec.m; sourceTree = ""; }; - D037668719EDA60000A782A9 /* RACChannelExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACChannelExamples.h; sourceTree = ""; }; - D037668819EDA60000A782A9 /* RACChannelExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACChannelExamples.m; sourceTree = ""; }; - D037668919EDA60000A782A9 /* RACChannelSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACChannelSpec.m; sourceTree = ""; }; - D037668A19EDA60000A782A9 /* RACCommandSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACCommandSpec.m; sourceTree = ""; }; - D037668B19EDA60000A782A9 /* RACCompoundDisposableSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACCompoundDisposableSpec.m; sourceTree = ""; }; - D037668C19EDA60000A782A9 /* RACControlCommandExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACControlCommandExamples.h; sourceTree = ""; }; - D037668D19EDA60000A782A9 /* RACControlCommandExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACControlCommandExamples.m; sourceTree = ""; }; - D037668E19EDA60000A782A9 /* RACDelegateProxySpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACDelegateProxySpec.m; sourceTree = ""; }; - D037668F19EDA60000A782A9 /* RACDisposableSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACDisposableSpec.m; sourceTree = ""; }; - D037669019EDA60000A782A9 /* RACEventSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACEventSpec.m; sourceTree = ""; }; - D037669119EDA60000A782A9 /* RACKVOChannelSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACKVOChannelSpec.m; sourceTree = ""; }; - D037669219EDA60000A782A9 /* RACKVOWrapperSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACKVOWrapperSpec.m; sourceTree = ""; }; - D037669319EDA60000A782A9 /* RACMulticastConnectionSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACMulticastConnectionSpec.m; sourceTree = ""; }; - D037669419EDA60000A782A9 /* RACPropertySignalExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACPropertySignalExamples.h; sourceTree = ""; }; - D037669519EDA60000A782A9 /* RACPropertySignalExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACPropertySignalExamples.m; sourceTree = ""; }; - D037669619EDA60000A782A9 /* RACSchedulerSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSchedulerSpec.m; sourceTree = ""; }; - D037669719EDA60000A782A9 /* RACSequenceAdditionsSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSequenceAdditionsSpec.m; sourceTree = ""; }; - D037669819EDA60000A782A9 /* RACSequenceExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSequenceExamples.h; sourceTree = ""; }; - D037669919EDA60000A782A9 /* RACSequenceExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSequenceExamples.m; sourceTree = ""; }; - D037669A19EDA60000A782A9 /* RACSequenceSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSequenceSpec.m; sourceTree = ""; }; - D037669B19EDA60000A782A9 /* RACSerialDisposableSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSerialDisposableSpec.m; sourceTree = ""; }; - D037669C19EDA60000A782A9 /* RACSignalSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSignalSpec.m; sourceTree = ""; }; - D037669F19EDA60000A782A9 /* RACStreamExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACStreamExamples.h; sourceTree = ""; }; - D03766A019EDA60000A782A9 /* RACStreamExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACStreamExamples.m; sourceTree = ""; }; - D03766A119EDA60000A782A9 /* RACSubclassObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSubclassObject.h; sourceTree = ""; }; - D03766A219EDA60000A782A9 /* RACSubclassObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubclassObject.m; sourceTree = ""; }; - D03766A319EDA60000A782A9 /* RACSubjectSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubjectSpec.m; sourceTree = ""; }; - D03766A419EDA60000A782A9 /* RACSubscriberExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACSubscriberExamples.h; sourceTree = ""; }; - D03766A519EDA60000A782A9 /* RACSubscriberExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubscriberExamples.m; sourceTree = ""; }; - D03766A619EDA60000A782A9 /* RACSubscriberSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubscriberSpec.m; sourceTree = ""; }; - D03766A719EDA60000A782A9 /* RACSubscriptingAssignmentTrampolineSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACSubscriptingAssignmentTrampolineSpec.m; sourceTree = ""; }; - D03766A819EDA60000A782A9 /* RACTargetQueueSchedulerSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTargetQueueSchedulerSpec.m; sourceTree = ""; }; - D03766B019EDA60000A782A9 /* RACTupleSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTupleSpec.m; sourceTree = ""; }; - D03766B119EDA60000A782A9 /* test-data.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "test-data.json"; sourceTree = ""; }; - D03766B219EDA60000A782A9 /* UIActionSheetRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIActionSheetRACSupportSpec.m; sourceTree = ""; }; - D03766B319EDA60000A782A9 /* UIAlertViewRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIAlertViewRACSupportSpec.m; sourceTree = ""; }; - D03766B419EDA60000A782A9 /* UIBarButtonItemRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIBarButtonItemRACSupportSpec.m; sourceTree = ""; }; - D03766B519EDA60000A782A9 /* UIButtonRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIButtonRACSupportSpec.m; sourceTree = ""; }; - D03766B719EDA60000A782A9 /* UIImagePickerControllerRACSupportSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIImagePickerControllerRACSupportSpec.m; sourceTree = ""; }; - D037672B19EDA75D00A782A9 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Quick.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D04725EA19E49ED7006002AA /* ReactiveObjC.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveObjC.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D04725EE19E49ED7006002AA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - D04725EF19E49ED7006002AA /* ReactiveObjC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ReactiveObjC.h; sourceTree = ""; }; - D04725F519E49ED7006002AA /* ReactiveObjCTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveObjCTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - D04725FB19E49ED7006002AA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - D047260C19E49F82006002AA /* ReactiveObjC.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveObjC.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D047261619E49F82006002AA /* ReactiveObjCTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveObjCTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - D047262719E49FE8006002AA /* Common.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Common.xcconfig; sourceTree = ""; }; - D047262919E49FE8006002AA /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; - D047262A19E49FE8006002AA /* Profile.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Profile.xcconfig; sourceTree = ""; }; - D047262B19E49FE8006002AA /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; - D047262C19E49FE8006002AA /* Test.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Test.xcconfig; sourceTree = ""; }; - D047262E19E49FE8006002AA /* Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Application.xcconfig; sourceTree = ""; }; - D047262F19E49FE8006002AA /* Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Framework.xcconfig; sourceTree = ""; }; - D047263019E49FE8006002AA /* StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = StaticLibrary.xcconfig; sourceTree = ""; }; - D047263219E49FE8006002AA /* iOS-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-Application.xcconfig"; sourceTree = ""; }; - D047263319E49FE8006002AA /* iOS-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-Base.xcconfig"; sourceTree = ""; }; - D047263419E49FE8006002AA /* iOS-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-Framework.xcconfig"; sourceTree = ""; }; - D047263519E49FE8006002AA /* iOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-StaticLibrary.xcconfig"; sourceTree = ""; }; - D047263719E49FE8006002AA /* Mac-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Application.xcconfig"; sourceTree = ""; }; - D047263819E49FE8006002AA /* Mac-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Base.xcconfig"; sourceTree = ""; }; - D047263919E49FE8006002AA /* Mac-DynamicLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-DynamicLibrary.xcconfig"; sourceTree = ""; }; - D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Framework.xcconfig"; sourceTree = ""; }; - D047263B19E49FE8006002AA /* Mac-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-StaticLibrary.xcconfig"; sourceTree = ""; }; - D047263C19E49FE8006002AA /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - D05E662419EDD82000904ACA /* Nimble.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Nimble.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D0C3131719EF2D9700984962 /* RACTestExampleScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTestExampleScheduler.h; sourceTree = ""; }; - D0C3131819EF2D9700984962 /* RACTestExampleScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTestExampleScheduler.m; sourceTree = ""; }; - D0C3131919EF2D9700984962 /* RACTestObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTestObject.h; sourceTree = ""; }; - D0C3131A19EF2D9700984962 /* RACTestObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTestObject.m; sourceTree = ""; }; - D0C3131B19EF2D9700984962 /* RACTestSchedulerSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTestSchedulerSpec.m; sourceTree = ""; }; - D0C3131C19EF2D9700984962 /* RACTestUIButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RACTestUIButton.h; sourceTree = ""; }; - D0C3131D19EF2D9700984962 /* RACTestUIButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RACTestUIButton.m; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 57A4D2071BA13D7A00F7D4B1 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 7DFBED001CDB8C9500EE435B /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - CDF066CA1CDC1CA200199626 /* Nimble.framework in Frameworks */, - CDF066CB1CDC1CA200199626 /* Quick.framework in Frameworks */, - 7DFBED081CDB8C9500EE435B /* ReactiveObjC.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A9B315501B3940610001CB9C /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D04725E619E49ED7006002AA /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D04725F219E49ED7006002AA /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - D05E662519EDD82000904ACA /* Nimble.framework in Frameworks */, - D037672D19EDA75D00A782A9 /* Quick.framework in Frameworks */, - D04725F619E49ED7006002AA /* ReactiveObjC.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D047260819E49F82006002AA /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D047261319E49F82006002AA /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - D05E662619EDD83000904ACA /* Nimble.framework in Frameworks */, - D037672F19EDA78B00A782A9 /* Quick.framework in Frameworks */, - D047261719E49F82006002AA /* ReactiveObjC.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 57A4D2431BA13F9700F7D4B1 /* tvOS */ = { - isa = PBXGroup; - children = ( - 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */, - 57A4D2451BA13F9700F7D4B1 /* tvOS-Base.xcconfig */, - 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */, - 57A4D2471BA13F9700F7D4B1 /* tvOS-StaticLibrary.xcconfig */, - ); - path = tvOS; - sourceTree = ""; - }; - A97451321B3A935E00F48E55 /* watchOS */ = { - isa = PBXGroup; - children = ( - A97451331B3A935E00F48E55 /* watchOS-Application.xcconfig */, - A97451341B3A935E00F48E55 /* watchOS-Base.xcconfig */, - A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */, - A97451361B3A935E00F48E55 /* watchOS-StaticLibrary.xcconfig */, - ); - path = watchOS; - sourceTree = ""; - }; - BE330A0D1D634F1E00806963 /* Frameworks */ = { - isa = PBXGroup; - children = ( - BE330A1A1D634F5F00806963 /* ReactiveSwift.framework */, - BE330A181D634F5900806963 /* ReactiveSwift.framework */, - BE330A161D634F4E00806963 /* ReactiveSwift.framework */, - BE330A141D634F4000806963 /* ReactiveSwift.framework */, - BE330A121D634F2E00806963 /* ReactiveSwift.framework */, - BE330A101D634F2900806963 /* ReactiveSwift.framework */, - BE330A0E1D634F1E00806963 /* ReactiveSwift.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - D037666519EDA57100A782A9 /* extobjc */ = { - isa = PBXGroup; - children = ( - D037666619EDA57100A782A9 /* EXTKeyPathCoding.h */, - D037666719EDA57100A782A9 /* EXTRuntimeExtensions.h */, - D037666819EDA57100A782A9 /* EXTRuntimeExtensions.m */, - D037666919EDA57100A782A9 /* EXTScope.h */, - D037666A19EDA57100A782A9 /* metamacros.h */, - ); - path = extobjc; - sourceTree = ""; - }; - D04725E019E49ED7006002AA = { - isa = PBXGroup; - children = ( - D04725EC19E49ED7006002AA /* ReactiveObjC */, - D04725F919E49ED7006002AA /* ReactiveObjCTests */, - D047262519E49FE8006002AA /* Configuration */, - D04725EB19E49ED7006002AA /* Products */, - BE330A0D1D634F1E00806963 /* Frameworks */, - ); - sourceTree = ""; - usesTabs = 1; - }; - D04725EB19E49ED7006002AA /* Products */ = { - isa = PBXGroup; - children = ( - D04725EA19E49ED7006002AA /* ReactiveObjC.framework */, - D04725F519E49ED7006002AA /* ReactiveObjCTests.xctest */, - D047260C19E49F82006002AA /* ReactiveObjC.framework */, - D047261619E49F82006002AA /* ReactiveObjCTests.xctest */, - A9B315541B3940610001CB9C /* ReactiveObjC.framework */, - 57A4D2411BA13D7A00F7D4B1 /* ReactiveObjC.framework */, - 7DFBED031CDB8C9500EE435B /* ReactiveObjCTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - D04725EC19E49ED7006002AA /* ReactiveObjC */ = { - isa = PBXGroup; - children = ( - D04725EF19E49ED7006002AA /* ReactiveObjC.h */, - D037666519EDA57100A782A9 /* extobjc */, - 314304151ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.h */, - 314304161ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.m */, - D037642A19EDA41200A782A9 /* NSArray+RACSequenceAdditions.h */, - D037642B19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m */, - D037642C19EDA41200A782A9 /* NSControl+RACCommandSupport.h */, - D037642D19EDA41200A782A9 /* NSControl+RACCommandSupport.m */, - D037642E19EDA41200A782A9 /* NSControl+RACTextSignalSupport.h */, - D037642F19EDA41200A782A9 /* NSControl+RACTextSignalSupport.m */, - D037643019EDA41200A782A9 /* NSData+RACSupport.h */, - D037643119EDA41200A782A9 /* NSData+RACSupport.m */, - D037643219EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h */, - D037643319EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m */, - D037643419EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h */, - D037643519EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m */, - D037643619EDA41200A782A9 /* NSFileHandle+RACSupport.h */, - D037643719EDA41200A782A9 /* NSFileHandle+RACSupport.m */, - D037643819EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h */, - D037643919EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m */, - D037643A19EDA41200A782A9 /* NSInvocation+RACTypeParsing.h */, - D037643B19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m */, - D037643C19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h */, - D037643D19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m */, - D037643E19EDA41200A782A9 /* NSObject+RACAppKitBindings.h */, - D037643F19EDA41200A782A9 /* NSObject+RACAppKitBindings.m */, - D037644019EDA41200A782A9 /* NSObject+RACDeallocating.h */, - D037644119EDA41200A782A9 /* NSObject+RACDeallocating.m */, - D037644219EDA41200A782A9 /* NSObject+RACDescription.h */, - D037644319EDA41200A782A9 /* NSObject+RACDescription.m */, - D037644419EDA41200A782A9 /* NSObject+RACKVOWrapper.h */, - D037644519EDA41200A782A9 /* NSObject+RACKVOWrapper.m */, - D037644619EDA41200A782A9 /* NSObject+RACLifting.h */, - D037644719EDA41200A782A9 /* NSObject+RACLifting.m */, - D037644819EDA41200A782A9 /* NSObject+RACPropertySubscribing.h */, - D037644919EDA41200A782A9 /* NSObject+RACPropertySubscribing.m */, - D037644A19EDA41200A782A9 /* NSObject+RACSelectorSignal.h */, - D037644B19EDA41200A782A9 /* NSObject+RACSelectorSignal.m */, - D037644C19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h */, - D037644D19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m */, - D037644E19EDA41200A782A9 /* NSSet+RACSequenceAdditions.h */, - D037644F19EDA41200A782A9 /* NSSet+RACSequenceAdditions.m */, - D037645019EDA41200A782A9 /* NSString+RACKeyPathUtilities.h */, - D037645119EDA41200A782A9 /* NSString+RACKeyPathUtilities.m */, - D037645219EDA41200A782A9 /* NSString+RACSequenceAdditions.h */, - D037645319EDA41200A782A9 /* NSString+RACSequenceAdditions.m */, - D037645419EDA41200A782A9 /* NSString+RACSupport.h */, - D037645519EDA41200A782A9 /* NSString+RACSupport.m */, - D037645619EDA41200A782A9 /* NSText+RACSignalSupport.h */, - D037645719EDA41200A782A9 /* NSText+RACSignalSupport.m */, - D037645819EDA41200A782A9 /* NSURLConnection+RACSupport.h */, - D037645919EDA41200A782A9 /* NSURLConnection+RACSupport.m */, - D037645A19EDA41200A782A9 /* NSUserDefaults+RACSupport.h */, - D037645B19EDA41200A782A9 /* NSUserDefaults+RACSupport.m */, - D037645C19EDA41200A782A9 /* RACArraySequence.h */, - D037645D19EDA41200A782A9 /* RACArraySequence.m */, - D037646019EDA41200A782A9 /* RACBehaviorSubject.h */, - D037646119EDA41200A782A9 /* RACBehaviorSubject.m */, - D037646219EDA41200A782A9 /* RACBlockTrampoline.h */, - D037646319EDA41200A782A9 /* RACBlockTrampoline.m */, - D037646419EDA41200A782A9 /* RACChannel.h */, - D037646519EDA41200A782A9 /* RACChannel.m */, - D037646619EDA41200A782A9 /* RACCommand.h */, - D037646719EDA41200A782A9 /* RACCommand.m */, - D037646819EDA41200A782A9 /* RACCompoundDisposable.h */, - D037646919EDA41200A782A9 /* RACCompoundDisposable.m */, - D037646A19EDA41200A782A9 /* RACCompoundDisposableProvider.d */, - D037646B19EDA41200A782A9 /* RACDelegateProxy.h */, - D037646C19EDA41200A782A9 /* RACDelegateProxy.m */, - D037646D19EDA41200A782A9 /* RACDisposable.h */, - D037646E19EDA41200A782A9 /* RACDisposable.m */, - D037646F19EDA41200A782A9 /* RACDynamicSequence.h */, - D037647019EDA41200A782A9 /* RACDynamicSequence.m */, - D037647119EDA41200A782A9 /* RACDynamicSignal.h */, - D037647219EDA41200A782A9 /* RACDynamicSignal.m */, - D037647319EDA41200A782A9 /* RACEagerSequence.h */, - D037647419EDA41200A782A9 /* RACEagerSequence.m */, - D037647519EDA41200A782A9 /* RACEmptySequence.h */, - D037647619EDA41200A782A9 /* RACEmptySequence.m */, - D037647719EDA41200A782A9 /* RACEmptySignal.h */, - D037647819EDA41200A782A9 /* RACEmptySignal.m */, - D037647919EDA41200A782A9 /* RACErrorSignal.h */, - D037647A19EDA41200A782A9 /* RACErrorSignal.m */, - D037647B19EDA41200A782A9 /* RACEvent.h */, - D037647C19EDA41200A782A9 /* RACEvent.m */, - D037647D19EDA41200A782A9 /* RACGroupedSignal.h */, - D037647E19EDA41200A782A9 /* RACGroupedSignal.m */, - D037647F19EDA41200A782A9 /* RACImmediateScheduler.h */, - D037648019EDA41200A782A9 /* RACImmediateScheduler.m */, - D037648119EDA41200A782A9 /* RACIndexSetSequence.h */, - D037648219EDA41200A782A9 /* RACIndexSetSequence.m */, - D037648319EDA41200A782A9 /* RACKVOChannel.h */, - D037648419EDA41200A782A9 /* RACKVOChannel.m */, - 7A70657D1A3F88B8001E8354 /* RACKVOProxy.h */, - 7A70657E1A3F88B8001E8354 /* RACKVOProxy.m */, - D037648519EDA41200A782A9 /* RACKVOTrampoline.h */, - D037648619EDA41200A782A9 /* RACKVOTrampoline.m */, - D037648719EDA41200A782A9 /* RACMulticastConnection.h */, - D037648819EDA41200A782A9 /* RACMulticastConnection.m */, - D037648919EDA41200A782A9 /* RACMulticastConnection+Private.h */, - D037648C19EDA41200A782A9 /* RACPassthroughSubscriber.h */, - D037648D19EDA41200A782A9 /* RACPassthroughSubscriber.m */, - D037648E19EDA41200A782A9 /* RACQueueScheduler.h */, - D037648F19EDA41200A782A9 /* RACQueueScheduler.m */, - D037649019EDA41200A782A9 /* RACQueueScheduler+Subclass.h */, - D037649119EDA41200A782A9 /* RACReplaySubject.h */, - D037649219EDA41200A782A9 /* RACReplaySubject.m */, - D037649319EDA41200A782A9 /* RACReturnSignal.h */, - D037649419EDA41200A782A9 /* RACReturnSignal.m */, - D037649519EDA41200A782A9 /* RACScheduler.h */, - D037649619EDA41200A782A9 /* RACScheduler.m */, - D037649719EDA41200A782A9 /* RACScheduler+Private.h */, - D037649819EDA41200A782A9 /* RACScheduler+Subclass.h */, - D037649919EDA41200A782A9 /* RACScopedDisposable.h */, - D037649A19EDA41200A782A9 /* RACScopedDisposable.m */, - D037649B19EDA41200A782A9 /* RACSequence.h */, - D037649C19EDA41200A782A9 /* RACSequence.m */, - D037649D19EDA41200A782A9 /* RACSerialDisposable.h */, - D037649E19EDA41200A782A9 /* RACSerialDisposable.m */, - D037649F19EDA41200A782A9 /* RACSignal.h */, - D03764A019EDA41200A782A9 /* RACSignal.m */, - D03764A119EDA41200A782A9 /* RACSignal+Operations.h */, - D03764A219EDA41200A782A9 /* RACSignal+Operations.m */, - D03764A319EDA41200A782A9 /* RACSignalProvider.d */, - D03764A419EDA41200A782A9 /* RACSignalSequence.h */, - D03764A519EDA41200A782A9 /* RACSignalSequence.m */, - D03764A619EDA41200A782A9 /* RACStream.h */, - D03764A719EDA41200A782A9 /* RACStream.m */, - D03764A819EDA41200A782A9 /* RACStream+Private.h */, - D03764A919EDA41200A782A9 /* RACStringSequence.h */, - D03764AA19EDA41200A782A9 /* RACStringSequence.m */, - D03764AB19EDA41200A782A9 /* RACSubject.h */, - D03764AC19EDA41200A782A9 /* RACSubject.m */, - D03764AD19EDA41200A782A9 /* RACSubscriber.h */, - D03764AE19EDA41200A782A9 /* RACSubscriber.m */, - D03764AF19EDA41200A782A9 /* RACSubscriber+Private.h */, - D03764B019EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h */, - D03764B119EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m */, - D03764B219EDA41200A782A9 /* RACSubscriptionScheduler.h */, - D03764B319EDA41200A782A9 /* RACSubscriptionScheduler.m */, - D03764B419EDA41200A782A9 /* RACTargetQueueScheduler.h */, - D03764B519EDA41200A782A9 /* RACTargetQueueScheduler.m */, - D03764B619EDA41200A782A9 /* RACTestScheduler.h */, - D03764B719EDA41200A782A9 /* RACTestScheduler.m */, - D03764B819EDA41200A782A9 /* RACTuple.h */, - D03764B919EDA41200A782A9 /* RACTuple.m */, - D03764BA19EDA41200A782A9 /* RACTupleSequence.h */, - D03764BB19EDA41200A782A9 /* RACTupleSequence.m */, - D03764BC19EDA41200A782A9 /* RACUnarySequence.h */, - D03764BD19EDA41200A782A9 /* RACUnarySequence.m */, - D03764BE19EDA41200A782A9 /* RACUnit.h */, - D03764BF19EDA41200A782A9 /* RACUnit.m */, - D03764C019EDA41200A782A9 /* RACValueTransformer.h */, - D03764C119EDA41200A782A9 /* RACValueTransformer.m */, - D03764C219EDA41200A782A9 /* UIActionSheet+RACSignalSupport.h */, - D03764C319EDA41200A782A9 /* UIActionSheet+RACSignalSupport.m */, - D03764C419EDA41200A782A9 /* UIAlertView+RACSignalSupport.h */, - D03764C519EDA41200A782A9 /* UIAlertView+RACSignalSupport.m */, - D03764C619EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.h */, - D03764C719EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.m */, - D03764C819EDA41200A782A9 /* UIButton+RACCommandSupport.h */, - D03764C919EDA41200A782A9 /* UIButton+RACCommandSupport.m */, - D03764CA19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.h */, - D03764CB19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.m */, - D03764CC19EDA41200A782A9 /* UIControl+RACSignalSupport.h */, - D03764CD19EDA41200A782A9 /* UIControl+RACSignalSupport.m */, - D03764CE19EDA41200A782A9 /* UIControl+RACSignalSupportPrivate.h */, - D03764CF19EDA41200A782A9 /* UIControl+RACSignalSupportPrivate.m */, - D03764D019EDA41200A782A9 /* UIDatePicker+RACSignalSupport.h */, - D03764D119EDA41200A782A9 /* UIDatePicker+RACSignalSupport.m */, - D03764D219EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.h */, - D03764D319EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.m */, - D03764D419EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.h */, - D03764D519EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.m */, - D03764D619EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.h */, - D03764D719EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.m */, - D03764D819EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.h */, - D03764D919EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.m */, - D03764DA19EDA41200A782A9 /* UISlider+RACSignalSupport.h */, - D03764DB19EDA41200A782A9 /* UISlider+RACSignalSupport.m */, - D03764DC19EDA41200A782A9 /* UIStepper+RACSignalSupport.h */, - D03764DD19EDA41200A782A9 /* UIStepper+RACSignalSupport.m */, - D03764DE19EDA41200A782A9 /* UISwitch+RACSignalSupport.h */, - D03764DF19EDA41200A782A9 /* UISwitch+RACSignalSupport.m */, - D03764E019EDA41200A782A9 /* UITableViewCell+RACSignalSupport.h */, - D03764E119EDA41200A782A9 /* UITableViewCell+RACSignalSupport.m */, - D03764E219EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.h */, - D03764E319EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.m */, - D03764E419EDA41200A782A9 /* UITextField+RACSignalSupport.h */, - D03764E519EDA41200A782A9 /* UITextField+RACSignalSupport.m */, - D03764E619EDA41200A782A9 /* UITextView+RACSignalSupport.h */, - D03764E719EDA41200A782A9 /* UITextView+RACSignalSupport.m */, - D04725ED19E49ED7006002AA /* Supporting Files */, - ); - path = ReactiveObjC; - sourceTree = ""; - }; - D04725ED19E49ED7006002AA /* Supporting Files */ = { - isa = PBXGroup; - children = ( - D04725EE19E49ED7006002AA /* Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - D04725F919E49ED7006002AA /* ReactiveObjCTests */ = { - isa = PBXGroup; - children = ( - D037667619EDA60000A782A9 /* NSControllerRACSupportSpec.m */, - D037667819EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m */, - D037667919EDA60000A782A9 /* NSNotificationCenterRACSupportSpec.m */, - D037667A19EDA60000A782A9 /* NSObjectRACAppKitBindingsSpec.m */, - D037667B19EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m */, - D037667C19EDA60000A782A9 /* NSObjectRACLiftingSpec.m */, - D037667D19EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.h */, - D037667E19EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.m */, - D037667F19EDA60000A782A9 /* NSObjectRACPropertySubscribingSpec.m */, - D037668019EDA60000A782A9 /* NSObjectRACSelectorSignalSpec.m */, - D037668119EDA60000A782A9 /* NSStringRACKeyPathUtilitiesSpec.m */, - D037668319EDA60000A782A9 /* NSURLConnectionRACSupportSpec.m */, - D037668419EDA60000A782A9 /* NSUserDefaultsRACSupportSpec.m */, - D037668619EDA60000A782A9 /* RACBlockTrampolineSpec.m */, - D037668719EDA60000A782A9 /* RACChannelExamples.h */, - D037668819EDA60000A782A9 /* RACChannelExamples.m */, - D037668919EDA60000A782A9 /* RACChannelSpec.m */, - D037668A19EDA60000A782A9 /* RACCommandSpec.m */, - D037668B19EDA60000A782A9 /* RACCompoundDisposableSpec.m */, - D037668C19EDA60000A782A9 /* RACControlCommandExamples.h */, - D037668D19EDA60000A782A9 /* RACControlCommandExamples.m */, - D037668E19EDA60000A782A9 /* RACDelegateProxySpec.m */, - D037668F19EDA60000A782A9 /* RACDisposableSpec.m */, - D037669019EDA60000A782A9 /* RACEventSpec.m */, - D037669119EDA60000A782A9 /* RACKVOChannelSpec.m */, - 7A7065831A3F8967001E8354 /* RACKVOProxySpec.m */, - D037669219EDA60000A782A9 /* RACKVOWrapperSpec.m */, - D037669319EDA60000A782A9 /* RACMulticastConnectionSpec.m */, - D037669419EDA60000A782A9 /* RACPropertySignalExamples.h */, - D037669519EDA60000A782A9 /* RACPropertySignalExamples.m */, - D037669619EDA60000A782A9 /* RACSchedulerSpec.m */, - D037669719EDA60000A782A9 /* RACSequenceAdditionsSpec.m */, - D037669819EDA60000A782A9 /* RACSequenceExamples.h */, - D037669919EDA60000A782A9 /* RACSequenceExamples.m */, - D037669A19EDA60000A782A9 /* RACSequenceSpec.m */, - D037669B19EDA60000A782A9 /* RACSerialDisposableSpec.m */, - D037669C19EDA60000A782A9 /* RACSignalSpec.m */, - D037669F19EDA60000A782A9 /* RACStreamExamples.h */, - D03766A019EDA60000A782A9 /* RACStreamExamples.m */, - D03766A119EDA60000A782A9 /* RACSubclassObject.h */, - D03766A219EDA60000A782A9 /* RACSubclassObject.m */, - D03766A319EDA60000A782A9 /* RACSubjectSpec.m */, - D03766A419EDA60000A782A9 /* RACSubscriberExamples.h */, - D03766A519EDA60000A782A9 /* RACSubscriberExamples.m */, - D03766A619EDA60000A782A9 /* RACSubscriberSpec.m */, - D03766A719EDA60000A782A9 /* RACSubscriptingAssignmentTrampolineSpec.m */, - D03766A819EDA60000A782A9 /* RACTargetQueueSchedulerSpec.m */, - D03766B019EDA60000A782A9 /* RACTupleSpec.m */, - D03766B219EDA60000A782A9 /* UIActionSheetRACSupportSpec.m */, - D03766B319EDA60000A782A9 /* UIAlertViewRACSupportSpec.m */, - D03766B419EDA60000A782A9 /* UIBarButtonItemRACSupportSpec.m */, - D03766B519EDA60000A782A9 /* UIButtonRACSupportSpec.m */, - D03766B719EDA60000A782A9 /* UIImagePickerControllerRACSupportSpec.m */, - D0C3131719EF2D9700984962 /* RACTestExampleScheduler.h */, - D0C3131819EF2D9700984962 /* RACTestExampleScheduler.m */, - D0C3131919EF2D9700984962 /* RACTestObject.h */, - D0C3131A19EF2D9700984962 /* RACTestObject.m */, - D0C3131B19EF2D9700984962 /* RACTestSchedulerSpec.m */, - D0C3131C19EF2D9700984962 /* RACTestUIButton.h */, - D0C3131D19EF2D9700984962 /* RACTestUIButton.m */, - D04725FA19E49ED7006002AA /* Supporting Files */, - ); - path = ReactiveObjCTests; - sourceTree = ""; - }; - D04725FA19E49ED7006002AA /* Supporting Files */ = { - isa = PBXGroup; - children = ( - D05E662419EDD82000904ACA /* Nimble.framework */, - D037672B19EDA75D00A782A9 /* Quick.framework */, - D03766B119EDA60000A782A9 /* test-data.json */, - D04725FB19E49ED7006002AA /* Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - D047262519E49FE8006002AA /* Configuration */ = { - isa = PBXGroup; - children = ( - D047262619E49FE8006002AA /* Base */, - D047263119E49FE8006002AA /* iOS */, - D047263619E49FE8006002AA /* Mac OS X */, - A97451321B3A935E00F48E55 /* watchOS */, - 57A4D2431BA13F9700F7D4B1 /* tvOS */, - D047263C19E49FE8006002AA /* README.md */, - ); - name = Configuration; - path = Carthage/Checkouts/xcconfigs; - sourceTree = ""; - }; - D047262619E49FE8006002AA /* Base */ = { - isa = PBXGroup; - children = ( - D047262719E49FE8006002AA /* Common.xcconfig */, - D047262819E49FE8006002AA /* Configurations */, - D047262D19E49FE8006002AA /* Targets */, - ); - path = Base; - sourceTree = ""; - }; - D047262819E49FE8006002AA /* Configurations */ = { - isa = PBXGroup; - children = ( - D047262919E49FE8006002AA /* Debug.xcconfig */, - D047262A19E49FE8006002AA /* Profile.xcconfig */, - D047262B19E49FE8006002AA /* Release.xcconfig */, - D047262C19E49FE8006002AA /* Test.xcconfig */, - ); - path = Configurations; - sourceTree = ""; - }; - D047262D19E49FE8006002AA /* Targets */ = { - isa = PBXGroup; - children = ( - D047262E19E49FE8006002AA /* Application.xcconfig */, - D047262F19E49FE8006002AA /* Framework.xcconfig */, - D047263019E49FE8006002AA /* StaticLibrary.xcconfig */, - ); - path = Targets; - sourceTree = ""; - }; - D047263119E49FE8006002AA /* iOS */ = { - isa = PBXGroup; - children = ( - D047263219E49FE8006002AA /* iOS-Application.xcconfig */, - D047263319E49FE8006002AA /* iOS-Base.xcconfig */, - D047263419E49FE8006002AA /* iOS-Framework.xcconfig */, - D047263519E49FE8006002AA /* iOS-StaticLibrary.xcconfig */, - ); - path = iOS; - sourceTree = ""; - }; - D047263619E49FE8006002AA /* Mac OS X */ = { - isa = PBXGroup; - children = ( - D047263719E49FE8006002AA /* Mac-Application.xcconfig */, - D047263819E49FE8006002AA /* Mac-Base.xcconfig */, - D047263919E49FE8006002AA /* Mac-DynamicLibrary.xcconfig */, - D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */, - D047263B19E49FE8006002AA /* Mac-StaticLibrary.xcconfig */, - ); - path = "Mac OS X"; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - 57A4D2091BA13D7A00F7D4B1 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 57A4D20A1BA13D7A00F7D4B1 /* ReactiveObjC.h in Headers */, - 57A4D20B1BA13D7A00F7D4B1 /* EXTKeyPathCoding.h in Headers */, - A1046B7D1BFF5664004D8045 /* EXTRuntimeExtensions.h in Headers */, - 57A4D20C1BA13D7A00F7D4B1 /* EXTScope.h in Headers */, - 57A4D20D1BA13D7A00F7D4B1 /* metamacros.h in Headers */, - 57A4D20E1BA13D7A00F7D4B1 /* NSArray+RACSequenceAdditions.h in Headers */, - 57A4D20F1BA13D7A00F7D4B1 /* NSData+RACSupport.h in Headers */, - BEBDD6E51CDC292D009A75A9 /* RACDelegateProxy.h in Headers */, - 57A4D2101BA13D7A00F7D4B1 /* NSDictionary+RACSequenceAdditions.h in Headers */, - 57A4D2111BA13D7A00F7D4B1 /* NSEnumerator+RACSequenceAdditions.h in Headers */, - 57A4D2121BA13D7A00F7D4B1 /* NSFileHandle+RACSupport.h in Headers */, - 57A4D2131BA13D7A00F7D4B1 /* NSIndexSet+RACSequenceAdditions.h in Headers */, - 57A4D2141BA13D7A00F7D4B1 /* NSNotificationCenter+RACSupport.h in Headers */, - 57A4D2151BA13D7A00F7D4B1 /* NSObject+RACDeallocating.h in Headers */, - 57A4D2161BA13D7A00F7D4B1 /* NSObject+RACLifting.h in Headers */, - 57A4D2171BA13D7A00F7D4B1 /* NSObject+RACPropertySubscribing.h in Headers */, - 57DC89A01C5066D400E367B7 /* UIGestureRecognizer+RACSignalSupport.h in Headers */, - 57A4D2181BA13D7A00F7D4B1 /* NSObject+RACSelectorSignal.h in Headers */, - 57A4D2191BA13D7A00F7D4B1 /* NSOrderedSet+RACSequenceAdditions.h in Headers */, - 57A4D21A1BA13D7A00F7D4B1 /* NSSet+RACSequenceAdditions.h in Headers */, - 57A4D21B1BA13D7A00F7D4B1 /* NSString+RACSequenceAdditions.h in Headers */, - 57A4D21C1BA13D7A00F7D4B1 /* NSString+RACSupport.h in Headers */, - 57A4D21E1BA13D7A00F7D4B1 /* NSUserDefaults+RACSupport.h in Headers */, - 57A4D21F1BA13D7A00F7D4B1 /* RACBehaviorSubject.h in Headers */, - 57A4D2201BA13D7A00F7D4B1 /* RACChannel.h in Headers */, - 57A4D2211BA13D7A00F7D4B1 /* RACCommand.h in Headers */, - 57A4D2221BA13D7A00F7D4B1 /* RACCompoundDisposable.h in Headers */, - 57A4D2231BA13D7A00F7D4B1 /* RACDisposable.h in Headers */, - 57A4D2241BA13D7A00F7D4B1 /* RACEvent.h in Headers */, - 7DFBED6F1CDB926400EE435B /* UIBarButtonItem+RACCommandSupport.h in Headers */, - 57A4D2251BA13D7A00F7D4B1 /* RACGroupedSignal.h in Headers */, - 57A4D2261BA13D7A00F7D4B1 /* RACKVOChannel.h in Headers */, - 57A4D2271BA13D7A00F7D4B1 /* RACMulticastConnection.h in Headers */, - 57A4D2281BA13D7A00F7D4B1 /* RACQueueScheduler.h in Headers */, - 57DC89A51C50675700E367B7 /* UITextField+RACSignalSupport.h in Headers */, - 57A4D2291BA13D7A00F7D4B1 /* RACQueueScheduler+Subclass.h in Headers */, - 57A4D22A1BA13D7A00F7D4B1 /* RACReplaySubject.h in Headers */, - 57DC89A21C50673C00E367B7 /* UISegmentedControl+RACSignalSupport.h in Headers */, - 57A4D22B1BA13D7A00F7D4B1 /* RACScheduler.h in Headers */, - 57DC89A41C50674D00E367B7 /* UITableViewHeaderFooterView+RACSignalSupport.h in Headers */, - 57A4D22C1BA13D7A00F7D4B1 /* RACScheduler+Subclass.h in Headers */, - 57DC89A11C50672B00E367B7 /* UIControl+RACSignalSupport.h in Headers */, - 57A4D22D1BA13D7A00F7D4B1 /* RACScopedDisposable.h in Headers */, - 57DC89A31C50674300E367B7 /* UITableViewCell+RACSignalSupport.h in Headers */, - 57A4D22E1BA13D7A00F7D4B1 /* RACSequence.h in Headers */, - 57A4D22F1BA13D7A00F7D4B1 /* RACSerialDisposable.h in Headers */, - 57A4D2301BA13D7A00F7D4B1 /* RACSignal.h in Headers */, - 57DC89A81C50679E00E367B7 /* UICollectionReusableView+RACSignalSupport.h in Headers */, - 57DC89A71C50679700E367B7 /* UIButton+RACCommandSupport.h in Headers */, - 57A4D2311BA13D7A00F7D4B1 /* RACSignal+Operations.h in Headers */, - 57A4D2321BA13D7A00F7D4B1 /* RACStream.h in Headers */, - 57A4D2331BA13D7A00F7D4B1 /* RACSubject.h in Headers */, - 57A4D2341BA13D7A00F7D4B1 /* RACSubscriber.h in Headers */, - 57A4D2351BA13D7A00F7D4B1 /* RACSubscriptingAssignmentTrampoline.h in Headers */, - 57A4D2361BA13D7A00F7D4B1 /* RACTargetQueueScheduler.h in Headers */, - 57A4D2371BA13D7A00F7D4B1 /* RACTestScheduler.h in Headers */, - 57A4D2381BA13D7A00F7D4B1 /* RACTuple.h in Headers */, - 57DC89A61C50675F00E367B7 /* UITextView+RACSignalSupport.h in Headers */, - 57A4D2391BA13D7A00F7D4B1 /* RACUnit.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A9B315511B3940610001CB9C /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - A9B315CA1B3940AB0001CB9C /* ReactiveObjC.h in Headers */, - A9B315CB1B3940AB0001CB9C /* EXTKeyPathCoding.h in Headers */, - A1046B7C1BFF5662004D8045 /* EXTRuntimeExtensions.h in Headers */, - A9B315CD1B3940AB0001CB9C /* EXTScope.h in Headers */, - A9B315CE1B3940AB0001CB9C /* metamacros.h in Headers */, - A9B315D01B3940AB0001CB9C /* NSArray+RACSequenceAdditions.h in Headers */, - A9B315D31B3940AB0001CB9C /* NSData+RACSupport.h in Headers */, - A9B315D41B3940AB0001CB9C /* NSDictionary+RACSequenceAdditions.h in Headers */, - A9B315D51B3940AB0001CB9C /* NSEnumerator+RACSequenceAdditions.h in Headers */, - A9B315D61B3940AB0001CB9C /* NSFileHandle+RACSupport.h in Headers */, - A9B315D71B3940AB0001CB9C /* NSIndexSet+RACSequenceAdditions.h in Headers */, - A9B315D91B3940AB0001CB9C /* NSNotificationCenter+RACSupport.h in Headers */, - A9B315DB1B3940AB0001CB9C /* NSObject+RACDeallocating.h in Headers */, - A9B315DE1B3940AB0001CB9C /* NSObject+RACLifting.h in Headers */, - A9B315DF1B3940AB0001CB9C /* NSObject+RACPropertySubscribing.h in Headers */, - A9B315E01B3940AB0001CB9C /* NSObject+RACSelectorSignal.h in Headers */, - A9B315E11B3940AB0001CB9C /* NSOrderedSet+RACSequenceAdditions.h in Headers */, - A9B315E21B3940AB0001CB9C /* NSSet+RACSequenceAdditions.h in Headers */, - A9B315E41B3940AB0001CB9C /* NSString+RACSequenceAdditions.h in Headers */, - A9B315E51B3940AB0001CB9C /* NSString+RACSupport.h in Headers */, - A9B315E81B3940AB0001CB9C /* NSUserDefaults+RACSupport.h in Headers */, - A9B315EA1B3940AB0001CB9C /* RACBehaviorSubject.h in Headers */, - A9B315EC1B3940AB0001CB9C /* RACChannel.h in Headers */, - A9B315ED1B3940AC0001CB9C /* RACCommand.h in Headers */, - A9B315EE1B3940AC0001CB9C /* RACCompoundDisposable.h in Headers */, - A9B315F01B3940AC0001CB9C /* RACDisposable.h in Headers */, - A9B315F71B3940AC0001CB9C /* RACEvent.h in Headers */, - BEBDD6E61CDC292D009A75A9 /* RACDelegateProxy.h in Headers */, - A9B315F81B3940AC0001CB9C /* RACGroupedSignal.h in Headers */, - A9B315FB1B3940AC0001CB9C /* RACKVOChannel.h in Headers */, - A9B315FE1B3940AC0001CB9C /* RACMulticastConnection.h in Headers */, - A9B316021B3940AD0001CB9C /* RACQueueScheduler.h in Headers */, - A9B316031B3940AD0001CB9C /* RACQueueScheduler+Subclass.h in Headers */, - A9B316041B3940AD0001CB9C /* RACReplaySubject.h in Headers */, - A9B316061B3940AD0001CB9C /* RACScheduler.h in Headers */, - A9B316081B3940AD0001CB9C /* RACScheduler+Subclass.h in Headers */, - A9B316091B3940AD0001CB9C /* RACScopedDisposable.h in Headers */, - A9B3160A1B3940AD0001CB9C /* RACSequence.h in Headers */, - A9B3160B1B3940AD0001CB9C /* RACSerialDisposable.h in Headers */, - A9B3160C1B3940AE0001CB9C /* RACSignal.h in Headers */, - A9B3160D1B3940AE0001CB9C /* RACSignal+Operations.h in Headers */, - A9B3160F1B3940AE0001CB9C /* RACStream.h in Headers */, - A9B316121B3940AE0001CB9C /* RACSubject.h in Headers */, - A9B316131B3940AE0001CB9C /* RACSubscriber.h in Headers */, - A9B316151B3940AE0001CB9C /* RACSubscriptingAssignmentTrampoline.h in Headers */, - A9B316171B3940AF0001CB9C /* RACTargetQueueScheduler.h in Headers */, - A9B316181B3940AF0001CB9C /* RACTestScheduler.h in Headers */, - A9B316191B3940AF0001CB9C /* RACTuple.h in Headers */, - A9B3161C1B3940AF0001CB9C /* RACUnit.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D04725E719E49ED7006002AA /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - D04725F019E49ED7006002AA /* ReactiveObjC.h in Headers */, - D037652019EDA41200A782A9 /* NSObject+RACLifting.h in Headers */, - D03764EC19EDA41200A782A9 /* NSControl+RACCommandSupport.h in Headers */, - D037655C19EDA41200A782A9 /* RACChannel.h in Headers */, - D03765EE19EDA41200A782A9 /* RACSubscriber.h in Headers */, - D03765D219EDA41200A782A9 /* RACSignal.h in Headers */, - D037650419EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h in Headers */, - D037659A19EDA41200A782A9 /* RACKVOChannel.h in Headers */, - D037651419EDA41200A782A9 /* NSObject+RACDeallocating.h in Headers */, - D037650C19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h in Headers */, - D037667319EDA57100A782A9 /* metamacros.h in Headers */, - D037666B19EDA57100A782A9 /* EXTKeyPathCoding.h in Headers */, - D03765F419EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h in Headers */, - D03765C419EDA41200A782A9 /* RACScheduler+Subclass.h in Headers */, - D037656E19EDA41200A782A9 /* RACDisposable.h in Headers */, - D03765B019EDA41200A782A9 /* RACQueueScheduler.h in Headers */, - BEBDD6E81CDC292F009A75A9 /* RACDelegateProxy.h in Headers */, - D037652419EDA41200A782A9 /* NSObject+RACPropertySubscribing.h in Headers */, - D037650019EDA41200A782A9 /* NSFileHandle+RACSupport.h in Headers */, - D037653019EDA41200A782A9 /* NSSet+RACSequenceAdditions.h in Headers */, - D037654019EDA41200A782A9 /* NSText+RACSignalSupport.h in Headers */, - D03765E019EDA41200A782A9 /* RACStream.h in Headers */, - D03765FC19EDA41200A782A9 /* RACTargetQueueScheduler.h in Headers */, - D03765B419EDA41200A782A9 /* RACQueueScheduler+Subclass.h in Headers */, - D037661019EDA41200A782A9 /* RACUnit.h in Headers */, - D037656419EDA41200A782A9 /* RACCompoundDisposable.h in Headers */, - D03764F419EDA41200A782A9 /* NSData+RACSupport.h in Headers */, - D03764FC19EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h in Headers */, - D03765CA19EDA41200A782A9 /* RACSequence.h in Headers */, - D037672719EDA63400A782A9 /* RACBehaviorSubject.h in Headers */, - D037653C19EDA41200A782A9 /* NSString+RACSupport.h in Headers */, - D03765CE19EDA41200A782A9 /* RACSerialDisposable.h in Headers */, - D03765D619EDA41200A782A9 /* RACSignal+Operations.h in Headers */, - D03765B619EDA41200A782A9 /* RACReplaySubject.h in Headers */, - D03765A219EDA41200A782A9 /* RACMulticastConnection.h in Headers */, - D037658E19EDA41200A782A9 /* RACGroupedSignal.h in Headers */, - D037654819EDA41200A782A9 /* NSUserDefaults+RACSupport.h in Headers */, - D03765BE19EDA41200A782A9 /* RACScheduler.h in Headers */, - D037656019EDA41200A782A9 /* RACCommand.h in Headers */, - D037660419EDA41200A782A9 /* RACTuple.h in Headers */, - D03765C619EDA41200A782A9 /* RACScopedDisposable.h in Headers */, - D037660019EDA41200A782A9 /* RACTestScheduler.h in Headers */, - D037652C19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h in Headers */, - D03764F019EDA41200A782A9 /* NSControl+RACTextSignalSupport.h in Headers */, - D03765EA19EDA41200A782A9 /* RACSubject.h in Headers */, - A1046B7A1BFF5661004D8045 /* EXTRuntimeExtensions.h in Headers */, - D037652819EDA41200A782A9 /* NSObject+RACSelectorSignal.h in Headers */, - D037654419EDA41200A782A9 /* NSURLConnection+RACSupport.h in Headers */, - D03764E819EDA41200A782A9 /* NSArray+RACSequenceAdditions.h in Headers */, - D037651019EDA41200A782A9 /* NSObject+RACAppKitBindings.h in Headers */, - D037658A19EDA41200A782A9 /* RACEvent.h in Headers */, - D037667119EDA57100A782A9 /* EXTScope.h in Headers */, - D037653819EDA41200A782A9 /* NSString+RACSequenceAdditions.h in Headers */, - D03764F819EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D047260919E49F82006002AA /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - D037664519EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.h in Headers */, - D037666419EDA43C00A782A9 /* ReactiveObjC.h in Headers */, - D037652519EDA41200A782A9 /* NSObject+RACPropertySubscribing.h in Headers */, - D03765B119EDA41200A782A9 /* RACQueueScheduler.h in Headers */, - D037662519EDA41200A782A9 /* UIButton+RACCommandSupport.h in Headers */, - D037672819EDA63500A782A9 /* RACBehaviorSubject.h in Headers */, - D037660119EDA41200A782A9 /* RACTestScheduler.h in Headers */, - D03765A319EDA41200A782A9 /* RACMulticastConnection.h in Headers */, - D03765B719EDA41200A782A9 /* RACReplaySubject.h in Headers */, - D037663D19EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.h in Headers */, - D037656F19EDA41200A782A9 /* RACDisposable.h in Headers */, - 314304171ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.h in Headers */, - D037654519EDA41200A782A9 /* NSURLConnection+RACSupport.h in Headers */, - D037661D19EDA41200A782A9 /* UIAlertView+RACSignalSupport.h in Headers */, - D037650D19EDA41200A782A9 /* NSNotificationCenter+RACSupport.h in Headers */, - D037650119EDA41200A782A9 /* NSFileHandle+RACSupport.h in Headers */, - D037666119EDA41200A782A9 /* UITextView+RACSignalSupport.h in Headers */, - D037659B19EDA41200A782A9 /* RACKVOChannel.h in Headers */, - D037652D19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.h in Headers */, - D03764F919EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.h in Headers */, - D037667219EDA57100A782A9 /* EXTScope.h in Headers */, - D037663519EDA41200A782A9 /* UIDatePicker+RACSignalSupport.h in Headers */, - D037667419EDA57100A782A9 /* metamacros.h in Headers */, - D03764FD19EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.h in Headers */, - D037652119EDA41200A782A9 /* NSObject+RACLifting.h in Headers */, - D037665919EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.h in Headers */, - D037656519EDA41200A782A9 /* RACCompoundDisposable.h in Headers */, - D037653D19EDA41200A782A9 /* NSString+RACSupport.h in Headers */, - D037662919EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.h in Headers */, - D037660519EDA41200A782A9 /* RACTuple.h in Headers */, - D037665519EDA41200A782A9 /* UITableViewCell+RACSignalSupport.h in Headers */, - D03764F519EDA41200A782A9 /* NSData+RACSupport.h in Headers */, - D037653919EDA41200A782A9 /* NSString+RACSequenceAdditions.h in Headers */, - D037651519EDA41200A782A9 /* NSObject+RACDeallocating.h in Headers */, - D037658F19EDA41200A782A9 /* RACGroupedSignal.h in Headers */, - D03765C519EDA41200A782A9 /* RACScheduler+Subclass.h in Headers */, - D03765E119EDA41200A782A9 /* RACStream.h in Headers */, - D03765D719EDA41200A782A9 /* RACSignal+Operations.h in Headers */, - D037665D19EDA41200A782A9 /* UITextField+RACSignalSupport.h in Headers */, - D037664919EDA41200A782A9 /* UISlider+RACSignalSupport.h in Headers */, - D03765BF19EDA41200A782A9 /* RACScheduler.h in Headers */, - D03764E919EDA41200A782A9 /* NSArray+RACSequenceAdditions.h in Headers */, - D037654919EDA41200A782A9 /* NSUserDefaults+RACSupport.h in Headers */, - D037663919EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.h in Headers */, - D037653119EDA41200A782A9 /* NSSet+RACSequenceAdditions.h in Headers */, - D03765CB19EDA41200A782A9 /* RACSequence.h in Headers */, - D037662D19EDA41200A782A9 /* UIControl+RACSignalSupport.h in Headers */, - D037666C19EDA57100A782A9 /* EXTKeyPathCoding.h in Headers */, - D037658B19EDA41200A782A9 /* RACEvent.h in Headers */, - D03765CF19EDA41200A782A9 /* RACSerialDisposable.h in Headers */, - D037650519EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.h in Headers */, - D037655D19EDA41200A782A9 /* RACChannel.h in Headers */, - D03765B519EDA41200A782A9 /* RACQueueScheduler+Subclass.h in Headers */, - D037665119EDA41200A782A9 /* UISwitch+RACSignalSupport.h in Headers */, - D037664119EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.h in Headers */, - D037652919EDA41200A782A9 /* NSObject+RACSelectorSignal.h in Headers */, - D03765D319EDA41200A782A9 /* RACSignal.h in Headers */, - D03765F519EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.h in Headers */, - D03765C719EDA41200A782A9 /* RACScopedDisposable.h in Headers */, - A1046B7B1BFF5662004D8045 /* EXTRuntimeExtensions.h in Headers */, - D037661119EDA41200A782A9 /* RACUnit.h in Headers */, - D03765FD19EDA41200A782A9 /* RACTargetQueueScheduler.h in Headers */, - D037661919EDA41200A782A9 /* UIActionSheet+RACSignalSupport.h in Headers */, - D037664D19EDA41200A782A9 /* UIStepper+RACSignalSupport.h in Headers */, - D037662119EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.h in Headers */, - D03765EB19EDA41200A782A9 /* RACSubject.h in Headers */, - BEBDD6E71CDC292E009A75A9 /* RACDelegateProxy.h in Headers */, - D037656119EDA41200A782A9 /* RACCommand.h in Headers */, - D03765EF19EDA41200A782A9 /* RACSubscriber.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 57A4D1AF1BA13D7A00F7D4B1 /* ReactiveObjC-tvOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = 57A4D23C1BA13D7A00F7D4B1 /* Build configuration list for PBXNativeTarget "ReactiveObjC-tvOS" */; - buildPhases = ( - 57A4D1B01BA13D7A00F7D4B1 /* Sources */, - 57A4D2071BA13D7A00F7D4B1 /* Frameworks */, - 57A4D2091BA13D7A00F7D4B1 /* Headers */, - 57A4D23B1BA13D7A00F7D4B1 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "ReactiveObjC-tvOS"; - productName = ReactiveCocoa; - productReference = 57A4D2411BA13D7A00F7D4B1 /* ReactiveObjC.framework */; - productType = "com.apple.product-type.framework"; - }; - 7DFBED021CDB8C9500EE435B /* ReactiveObjC-tvOSTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 7DFBED0F1CDB8C9500EE435B /* Build configuration list for PBXNativeTarget "ReactiveObjC-tvOSTests" */; - buildPhases = ( - 7DFBECFF1CDB8C9500EE435B /* Sources */, - 7DFBED001CDB8C9500EE435B /* Frameworks */, - 7DFBED011CDB8C9500EE435B /* Resources */, - 7DFBED151CDB8CEC00EE435B /* Copy Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - 7DFBED0A1CDB8C9500EE435B /* PBXTargetDependency */, - ); - name = "ReactiveObjC-tvOSTests"; - productName = "ReactiveCocoa-tvOSTests"; - productReference = 7DFBED031CDB8C9500EE435B /* ReactiveObjCTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - A9B315531B3940610001CB9C /* ReactiveObjC-watchOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = A9B3155D1B3940610001CB9C /* Build configuration list for PBXNativeTarget "ReactiveObjC-watchOS" */; - buildPhases = ( - A9B3154F1B3940610001CB9C /* Sources */, - A9B315501B3940610001CB9C /* Frameworks */, - A9B315511B3940610001CB9C /* Headers */, - A9B315521B3940610001CB9C /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "ReactiveObjC-watchOS"; - productName = ReactiveCocoa; - productReference = A9B315541B3940610001CB9C /* ReactiveObjC.framework */; - productType = "com.apple.product-type.framework"; - }; - D04725E919E49ED7006002AA /* ReactiveObjC-macOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = D047260019E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjC-macOS" */; - buildPhases = ( - D04725E519E49ED7006002AA /* Sources */, - D04725E619E49ED7006002AA /* Frameworks */, - D04725E719E49ED7006002AA /* Headers */, - D04725E819E49ED7006002AA /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "ReactiveObjC-macOS"; - productName = ReactiveCocoa; - productReference = D04725EA19E49ED7006002AA /* ReactiveObjC.framework */; - productType = "com.apple.product-type.framework"; - }; - D04725F419E49ED7006002AA /* ReactiveObjC-macOSTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = D047260319E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjC-macOSTests" */; - buildPhases = ( - D04725F119E49ED7006002AA /* Sources */, - D04725F219E49ED7006002AA /* Frameworks */, - D04725F319E49ED7006002AA /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - D04725F819E49ED7006002AA /* PBXTargetDependency */, - ); - name = "ReactiveObjC-macOSTests"; - productName = ReactiveCocoaTests; - productReference = D04725F519E49ED7006002AA /* ReactiveObjCTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - D047260B19E49F82006002AA /* ReactiveObjC-iOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = D047261F19E49F82006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjC-iOS" */; - buildPhases = ( - D047260719E49F82006002AA /* Sources */, - D047260819E49F82006002AA /* Frameworks */, - D047260919E49F82006002AA /* Headers */, - D047260A19E49F82006002AA /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "ReactiveObjC-iOS"; - productName = ReactiveCocoa; - productReference = D047260C19E49F82006002AA /* ReactiveObjC.framework */; - productType = "com.apple.product-type.framework"; - }; - D047261519E49F82006002AA /* ReactiveObjC-iOSTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = D047262219E49F82006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjC-iOSTests" */; - buildPhases = ( - D047261219E49F82006002AA /* Sources */, - D047261319E49F82006002AA /* Frameworks */, - D047261419E49F82006002AA /* Resources */, - D01B7B6119EDD8F600D26E01 /* Copy Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - D047261919E49F82006002AA /* PBXTargetDependency */, - ); - name = "ReactiveObjC-iOSTests"; - productName = ReactiveCocoaTests; - productReference = D047261619E49F82006002AA /* ReactiveObjCTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - D04725E119E49ED7006002AA /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0800; - ORGANIZATIONNAME = GitHub; - TargetAttributes = { - 57A4D1AF1BA13D7A00F7D4B1 = { - LastSwiftMigration = 0800; - }; - 7DFBED021CDB8C9500EE435B = { - CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 0800; - }; - A9B315531B3940610001CB9C = { - CreatedOnToolsVersion = 7.0; - LastSwiftMigration = 0800; - }; - D04725E919E49ED7006002AA = { - CreatedOnToolsVersion = 6.1; - LastSwiftMigration = 0800; - }; - D04725F419E49ED7006002AA = { - CreatedOnToolsVersion = 6.1; - LastSwiftMigration = 0800; - }; - D047260B19E49F82006002AA = { - CreatedOnToolsVersion = 6.1; - LastSwiftMigration = 0800; - }; - D047261519E49F82006002AA = { - CreatedOnToolsVersion = 6.1; - LastSwiftMigration = 0800; - }; - }; - }; - buildConfigurationList = D04725E419E49ED7006002AA /* Build configuration list for PBXProject "ReactiveObjC" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - ); - mainGroup = D04725E019E49ED7006002AA; - productRefGroup = D04725EB19E49ED7006002AA /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - D04725E919E49ED7006002AA /* ReactiveObjC-macOS */, - D04725F419E49ED7006002AA /* ReactiveObjC-macOSTests */, - D047260B19E49F82006002AA /* ReactiveObjC-iOS */, - D047261519E49F82006002AA /* ReactiveObjC-iOSTests */, - A9B315531B3940610001CB9C /* ReactiveObjC-watchOS */, - 57A4D1AF1BA13D7A00F7D4B1 /* ReactiveObjC-tvOS */, - 7DFBED021CDB8C9500EE435B /* ReactiveObjC-tvOSTests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 57A4D23B1BA13D7A00F7D4B1 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 7DFBED011CDB8C9500EE435B /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 7DFBED141CDB8CE600EE435B /* test-data.json in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A9B315521B3940610001CB9C /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D04725E819E49ED7006002AA /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D04725F319E49ED7006002AA /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D037671719EDA60000A782A9 /* test-data.json in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D047260A19E49F82006002AA /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D047261419E49F82006002AA /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D037671819EDA60000A782A9 /* test-data.json in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 57A4D1B01BA13D7A00F7D4B1 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 57D476951C4206EC00EFE697 /* UITableViewCell+RACSignalSupport.m in Sources */, - 57A4D1B21BA13D7A00F7D4B1 /* RACCompoundDisposableProvider.d in Sources */, - 57D476901C4206D400EFE697 /* UIControl+RACSignalSupportPrivate.m in Sources */, - 57A4D1B31BA13D7A00F7D4B1 /* RACSignalProvider.d in Sources */, - 57A4D1C11BA13D7A00F7D4B1 /* EXTRuntimeExtensions.m in Sources */, - 57A4D1C21BA13D7A00F7D4B1 /* NSArray+RACSequenceAdditions.m in Sources */, - 57A4D1C31BA13D7A00F7D4B1 /* NSData+RACSupport.m in Sources */, - 57A4D1C41BA13D7A00F7D4B1 /* NSDictionary+RACSequenceAdditions.m in Sources */, - 57A4D1C51BA13D7A00F7D4B1 /* NSEnumerator+RACSequenceAdditions.m in Sources */, - 57D476961C4206EC00EFE697 /* UITableViewHeaderFooterView+RACSignalSupport.m in Sources */, - 57A4D1C61BA13D7A00F7D4B1 /* NSFileHandle+RACSupport.m in Sources */, - 57A4D1C71BA13D7A00F7D4B1 /* NSIndexSet+RACSequenceAdditions.m in Sources */, - 57A4D1C81BA13D7A00F7D4B1 /* NSInvocation+RACTypeParsing.m in Sources */, - 57D4769B1C4206F200EFE697 /* UICollectionReusableView+RACSignalSupport.m in Sources */, - 57A4D1C91BA13D7A00F7D4B1 /* NSNotificationCenter+RACSupport.m in Sources */, - 7DFBED6E1CDB918900EE435B /* UIBarButtonItem+RACCommandSupport.m in Sources */, - 57A4D1CA1BA13D7A00F7D4B1 /* NSObject+RACDeallocating.m in Sources */, - 57A4D1CB1BA13D7A00F7D4B1 /* NSObject+RACDescription.m in Sources */, - 57A4D1CC1BA13D7A00F7D4B1 /* NSObject+RACKVOWrapper.m in Sources */, - 57A4D1CD1BA13D7A00F7D4B1 /* NSObject+RACLifting.m in Sources */, - 57A4D1CE1BA13D7A00F7D4B1 /* NSObject+RACPropertySubscribing.m in Sources */, - 57A4D1CF1BA13D7A00F7D4B1 /* NSObject+RACSelectorSignal.m in Sources */, - 57D476981C4206EC00EFE697 /* UITextView+RACSignalSupport.m in Sources */, - 57A4D1D01BA13D7A00F7D4B1 /* NSOrderedSet+RACSequenceAdditions.m in Sources */, - 57A4D1D11BA13D7A00F7D4B1 /* NSSet+RACSequenceAdditions.m in Sources */, - 57D476911C4206DA00EFE697 /* UIGestureRecognizer+RACSignalSupport.m in Sources */, - 57A4D1D21BA13D7A00F7D4B1 /* NSString+RACKeyPathUtilities.m in Sources */, - 57D4769A1C4206F200EFE697 /* UIButton+RACCommandSupport.m in Sources */, - 57A4D1D31BA13D7A00F7D4B1 /* NSString+RACSequenceAdditions.m in Sources */, - 57A4D1D41BA13D7A00F7D4B1 /* NSString+RACSupport.m in Sources */, - 57A4D1D61BA13D7A00F7D4B1 /* NSUserDefaults+RACSupport.m in Sources */, - 57A4D1D71BA13D7A00F7D4B1 /* RACArraySequence.m in Sources */, - 57A4D1D81BA13D7A00F7D4B1 /* RACBehaviorSubject.m in Sources */, - 57A4D1D91BA13D7A00F7D4B1 /* RACBlockTrampoline.m in Sources */, - 57A4D1DA1BA13D7A00F7D4B1 /* RACChannel.m in Sources */, - 57A4D1DB1BA13D7A00F7D4B1 /* RACCommand.m in Sources */, - 57A4D1DC1BA13D7A00F7D4B1 /* RACCompoundDisposable.m in Sources */, - 57A4D1DD1BA13D7A00F7D4B1 /* RACDelegateProxy.m in Sources */, - 57A4D1DE1BA13D7A00F7D4B1 /* RACDisposable.m in Sources */, - 57A4D1DF1BA13D7A00F7D4B1 /* RACDynamicSequence.m in Sources */, - 57A4D1E01BA13D7A00F7D4B1 /* RACDynamicSignal.m in Sources */, - 57A4D1E11BA13D7A00F7D4B1 /* RACEagerSequence.m in Sources */, - 57D4768D1C42063C00EFE697 /* UIControl+RACSignalSupport.m in Sources */, - 57A4D1E21BA13D7A00F7D4B1 /* RACEmptySequence.m in Sources */, - 57A4D1E31BA13D7A00F7D4B1 /* RACEmptySignal.m in Sources */, - 57A4D1E41BA13D7A00F7D4B1 /* RACErrorSignal.m in Sources */, - 57A4D1E51BA13D7A00F7D4B1 /* RACEvent.m in Sources */, - 57A4D1E61BA13D7A00F7D4B1 /* RACGroupedSignal.m in Sources */, - 57A4D1E71BA13D7A00F7D4B1 /* RACImmediateScheduler.m in Sources */, - 57D476971C4206EC00EFE697 /* UITextField+RACSignalSupport.m in Sources */, - 57A4D1E81BA13D7A00F7D4B1 /* RACIndexSetSequence.m in Sources */, - 57A4D1E91BA13D7A00F7D4B1 /* RACKVOChannel.m in Sources */, - 57A4D1EA1BA13D7A00F7D4B1 /* RACKVOProxy.m in Sources */, - 57A4D1EB1BA13D7A00F7D4B1 /* RACKVOTrampoline.m in Sources */, - 57A4D1EC1BA13D7A00F7D4B1 /* RACMulticastConnection.m in Sources */, - 57A4D1EE1BA13D7A00F7D4B1 /* RACPassthroughSubscriber.m in Sources */, - 57A4D1EF1BA13D7A00F7D4B1 /* RACQueueScheduler.m in Sources */, - 57A4D1F01BA13D7A00F7D4B1 /* RACReplaySubject.m in Sources */, - 57A4D1F11BA13D7A00F7D4B1 /* RACReturnSignal.m in Sources */, - 57A4D1F21BA13D7A00F7D4B1 /* RACScheduler.m in Sources */, - 57A4D1F31BA13D7A00F7D4B1 /* RACScopedDisposable.m in Sources */, - 57A4D1F41BA13D7A00F7D4B1 /* RACSequence.m in Sources */, - 57A4D1F51BA13D7A00F7D4B1 /* RACSerialDisposable.m in Sources */, - 57A4D1F61BA13D7A00F7D4B1 /* RACSignal.m in Sources */, - 57D476921C4206DF00EFE697 /* UISegmentedControl+RACSignalSupport.m in Sources */, - 57A4D1F71BA13D7A00F7D4B1 /* RACSignal+Operations.m in Sources */, - 57A4D1F81BA13D7A00F7D4B1 /* RACSignalSequence.m in Sources */, - 57A4D1F91BA13D7A00F7D4B1 /* RACStream.m in Sources */, - 57A4D1FA1BA13D7A00F7D4B1 /* RACStringSequence.m in Sources */, - 57A4D1FB1BA13D7A00F7D4B1 /* RACSubject.m in Sources */, - 57A4D1FC1BA13D7A00F7D4B1 /* RACSubscriber.m in Sources */, - 57A4D1FD1BA13D7A00F7D4B1 /* RACSubscriptingAssignmentTrampoline.m in Sources */, - 57A4D1FE1BA13D7A00F7D4B1 /* RACSubscriptionScheduler.m in Sources */, - 57A4D1FF1BA13D7A00F7D4B1 /* RACTargetQueueScheduler.m in Sources */, - 57A4D2001BA13D7A00F7D4B1 /* RACTestScheduler.m in Sources */, - 57A4D2011BA13D7A00F7D4B1 /* RACTuple.m in Sources */, - 57A4D2021BA13D7A00F7D4B1 /* RACTupleSequence.m in Sources */, - 57A4D2031BA13D7A00F7D4B1 /* RACUnarySequence.m in Sources */, - 57A4D2041BA13D7A00F7D4B1 /* RACUnit.m in Sources */, - 57A4D2051BA13D7A00F7D4B1 /* RACValueTransformer.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 7DFBECFF1CDB8C9500EE435B /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 7DFBED321CDB8DE300EE435B /* NSEnumeratorRACSequenceAdditionsSpec.m in Sources */, - 7DFBED331CDB8DE300EE435B /* NSNotificationCenterRACSupportSpec.m in Sources */, - 7DFBED351CDB8DE300EE435B /* NSObjectRACDeallocatingSpec.m in Sources */, - 7DFBED361CDB8DE300EE435B /* NSObjectRACLiftingSpec.m in Sources */, - 7DFBED381CDB8DE300EE435B /* NSObjectRACPropertySubscribingExamples.m in Sources */, - 7DFBED391CDB8DE300EE435B /* NSObjectRACPropertySubscribingSpec.m in Sources */, - 7DFBED3A1CDB8DE300EE435B /* NSObjectRACSelectorSignalSpec.m in Sources */, - 7DFBED3B1CDB8DE300EE435B /* NSStringRACKeyPathUtilitiesSpec.m in Sources */, - 7DFBED3D1CDB8DE300EE435B /* NSUserDefaultsRACSupportSpec.m in Sources */, - 7DFBED3E1CDB8DE300EE435B /* RACBlockTrampolineSpec.m in Sources */, - 7DFBED401CDB8DE300EE435B /* RACChannelExamples.m in Sources */, - 7DFBED411CDB8DE300EE435B /* RACChannelSpec.m in Sources */, - 7DFBED421CDB8DE300EE435B /* RACCommandSpec.m in Sources */, - 7DFBED431CDB8DE300EE435B /* RACCompoundDisposableSpec.m in Sources */, - 7DFBED451CDB8DE300EE435B /* RACControlCommandExamples.m in Sources */, - 7DFBED461CDB8DE300EE435B /* RACDelegateProxySpec.m in Sources */, - 7DFBED471CDB8DE300EE435B /* RACDisposableSpec.m in Sources */, - 7DFBED481CDB8DE300EE435B /* RACEventSpec.m in Sources */, - 7DFBED491CDB8DE300EE435B /* RACKVOChannelSpec.m in Sources */, - 7DFBED4A1CDB8DE300EE435B /* RACKVOProxySpec.m in Sources */, - 7DFBED4B1CDB8DE300EE435B /* RACKVOWrapperSpec.m in Sources */, - 7DFBED4C1CDB8DE300EE435B /* RACMulticastConnectionSpec.m in Sources */, - 7DFBED4E1CDB8DE300EE435B /* RACPropertySignalExamples.m in Sources */, - 7DFBED4F1CDB8DE300EE435B /* RACSchedulerSpec.m in Sources */, - 7DFBED501CDB8DE300EE435B /* RACSequenceAdditionsSpec.m in Sources */, - 7DFBED521CDB8DE300EE435B /* RACSequenceExamples.m in Sources */, - 7DFBED531CDB8DE300EE435B /* RACSequenceSpec.m in Sources */, - 7DFBED541CDB8DE300EE435B /* RACSerialDisposableSpec.m in Sources */, - 7DFBED551CDB8DE300EE435B /* RACSignalSpec.m in Sources */, - 7DFBED571CDB8DE300EE435B /* RACStreamExamples.m in Sources */, - 7DFBED591CDB8DE300EE435B /* RACSubclassObject.m in Sources */, - 7DFBED5A1CDB8DE300EE435B /* RACSubjectSpec.m in Sources */, - 7DFBED5C1CDB8DE300EE435B /* RACSubscriberExamples.m in Sources */, - 7DFBED5D1CDB8DE300EE435B /* RACSubscriberSpec.m in Sources */, - 7DFBED5E1CDB8DE300EE435B /* RACSubscriptingAssignmentTrampolineSpec.m in Sources */, - 7DFBED5F1CDB8DE300EE435B /* RACTargetQueueSchedulerSpec.m in Sources */, - 7DFBED601CDB8DE300EE435B /* RACTupleSpec.m in Sources */, - 7DFBED631CDB8DE300EE435B /* UIBarButtonItemRACSupportSpec.m in Sources */, - 7DFBED641CDB8DE300EE435B /* UIButtonRACSupportSpec.m in Sources */, - 7DFBED671CDB8DE300EE435B /* RACTestExampleScheduler.m in Sources */, - 7DFBED691CDB8DE300EE435B /* RACTestObject.m in Sources */, - 7DFBED6A1CDB8DE300EE435B /* RACTestSchedulerSpec.m in Sources */, - 7DFBED6C1CDB8DE300EE435B /* RACTestUIButton.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A9B3154F1B3940610001CB9C /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - A9B316341B394C7F0001CB9C /* RACCompoundDisposableProvider.d in Sources */, - A9B316351B394C7F0001CB9C /* RACSignalProvider.d in Sources */, - A9B3155E1B3940750001CB9C /* EXTRuntimeExtensions.m in Sources */, - A9B315601B3940750001CB9C /* NSArray+RACSequenceAdditions.m in Sources */, - A9B315631B3940750001CB9C /* NSData+RACSupport.m in Sources */, - A9B315641B3940750001CB9C /* NSDictionary+RACSequenceAdditions.m in Sources */, - A9B315651B3940750001CB9C /* NSEnumerator+RACSequenceAdditions.m in Sources */, - A9B315661B3940750001CB9C /* NSFileHandle+RACSupport.m in Sources */, - A9B315671B3940750001CB9C /* NSIndexSet+RACSequenceAdditions.m in Sources */, - A9B315681B3940750001CB9C /* NSInvocation+RACTypeParsing.m in Sources */, - A9B315691B3940750001CB9C /* NSNotificationCenter+RACSupport.m in Sources */, - A9B3156B1B3940750001CB9C /* NSObject+RACDeallocating.m in Sources */, - A9B3156C1B3940750001CB9C /* NSObject+RACDescription.m in Sources */, - A9B3156D1B3940750001CB9C /* NSObject+RACKVOWrapper.m in Sources */, - A9B3156E1B3940750001CB9C /* NSObject+RACLifting.m in Sources */, - A9B3156F1B3940750001CB9C /* NSObject+RACPropertySubscribing.m in Sources */, - A9B315701B3940750001CB9C /* NSObject+RACSelectorSignal.m in Sources */, - A9B315711B3940750001CB9C /* NSOrderedSet+RACSequenceAdditions.m in Sources */, - A9B315721B3940750001CB9C /* NSSet+RACSequenceAdditions.m in Sources */, - A9B315731B3940750001CB9C /* NSString+RACKeyPathUtilities.m in Sources */, - A9B315741B3940750001CB9C /* NSString+RACSequenceAdditions.m in Sources */, - A9B315751B3940750001CB9C /* NSString+RACSupport.m in Sources */, - A9B315781B3940750001CB9C /* NSUserDefaults+RACSupport.m in Sources */, - A9B315791B3940750001CB9C /* RACArraySequence.m in Sources */, - A9B3157A1B3940750001CB9C /* RACBehaviorSubject.m in Sources */, - A9B3157B1B3940750001CB9C /* RACBlockTrampoline.m in Sources */, - A9B3157C1B3940750001CB9C /* RACChannel.m in Sources */, - A9B3157D1B3940750001CB9C /* RACCommand.m in Sources */, - A9B3157E1B3940750001CB9C /* RACCompoundDisposable.m in Sources */, - A9B3157F1B3940750001CB9C /* RACDelegateProxy.m in Sources */, - A9B315801B3940750001CB9C /* RACDisposable.m in Sources */, - A9B315811B3940750001CB9C /* RACDynamicSequence.m in Sources */, - A9B315821B3940750001CB9C /* RACDynamicSignal.m in Sources */, - A9B315831B3940750001CB9C /* RACEagerSequence.m in Sources */, - A9B315841B3940750001CB9C /* RACEmptySequence.m in Sources */, - A9B315851B3940750001CB9C /* RACEmptySignal.m in Sources */, - A9B315861B3940750001CB9C /* RACErrorSignal.m in Sources */, - A9B315871B3940750001CB9C /* RACEvent.m in Sources */, - A9B315881B3940750001CB9C /* RACGroupedSignal.m in Sources */, - A9B315891B3940750001CB9C /* RACImmediateScheduler.m in Sources */, - A9B3158A1B3940750001CB9C /* RACIndexSetSequence.m in Sources */, - A9B3158B1B3940750001CB9C /* RACKVOChannel.m in Sources */, - A9B3158C1B3940750001CB9C /* RACKVOProxy.m in Sources */, - A9B3158D1B3940750001CB9C /* RACKVOTrampoline.m in Sources */, - A9B3158E1B3940750001CB9C /* RACMulticastConnection.m in Sources */, - A9B315901B3940750001CB9C /* RACPassthroughSubscriber.m in Sources */, - A9B315911B3940750001CB9C /* RACQueueScheduler.m in Sources */, - A9B315921B3940750001CB9C /* RACReplaySubject.m in Sources */, - A9B315931B3940750001CB9C /* RACReturnSignal.m in Sources */, - A9B315941B3940750001CB9C /* RACScheduler.m in Sources */, - A9B315951B3940750001CB9C /* RACScopedDisposable.m in Sources */, - A9B315961B3940750001CB9C /* RACSequence.m in Sources */, - A9B315971B3940750001CB9C /* RACSerialDisposable.m in Sources */, - A9B315981B3940750001CB9C /* RACSignal.m in Sources */, - A9B315991B3940750001CB9C /* RACSignal+Operations.m in Sources */, - A9B3159A1B3940750001CB9C /* RACSignalSequence.m in Sources */, - A9B3159B1B3940750001CB9C /* RACStream.m in Sources */, - A9B3159C1B3940750001CB9C /* RACStringSequence.m in Sources */, - A9B3159D1B3940750001CB9C /* RACSubject.m in Sources */, - A9B3159E1B3940750001CB9C /* RACSubscriber.m in Sources */, - A9B3159F1B3940750001CB9C /* RACSubscriptingAssignmentTrampoline.m in Sources */, - A9B315A01B3940750001CB9C /* RACSubscriptionScheduler.m in Sources */, - A9B315A11B3940750001CB9C /* RACTargetQueueScheduler.m in Sources */, - A9B315A21B3940750001CB9C /* RACTestScheduler.m in Sources */, - A9B315A31B3940750001CB9C /* RACTuple.m in Sources */, - A9B315A41B3940750001CB9C /* RACTupleSequence.m in Sources */, - A9B315A51B3940750001CB9C /* RACUnarySequence.m in Sources */, - A9B315A61B3940750001CB9C /* RACUnit.m in Sources */, - A9B315A71B3940750001CB9C /* RACValueTransformer.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D04725E519E49ED7006002AA /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D037654219EDA41200A782A9 /* NSText+RACSignalSupport.m in Sources */, - D037659C19EDA41200A782A9 /* RACKVOChannel.m in Sources */, - D03765C819EDA41200A782A9 /* RACScopedDisposable.m in Sources */, - D03764FE19EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m in Sources */, - D03764EA19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m in Sources */, - D03765C019EDA41200A782A9 /* RACScheduler.m in Sources */, - D037659819EDA41200A782A9 /* RACIndexSetSequence.m in Sources */, - D03765D819EDA41200A782A9 /* RACSignal+Operations.m in Sources */, - D03764F219EDA41200A782A9 /* NSControl+RACTextSignalSupport.m in Sources */, - D037650219EDA41200A782A9 /* NSFileHandle+RACSupport.m in Sources */, - D03765E219EDA41200A782A9 /* RACStream.m in Sources */, - D037655619EDA41200A782A9 /* RACBehaviorSubject.m in Sources */, - D037660219EDA41200A782A9 /* RACTestScheduler.m in Sources */, - D03765B819EDA41200A782A9 /* RACReplaySubject.m in Sources */, - D03765EC19EDA41200A782A9 /* RACSubject.m in Sources */, - D03765D019EDA41200A782A9 /* RACSerialDisposable.m in Sources */, - D037666F19EDA57100A782A9 /* EXTRuntimeExtensions.m in Sources */, - D037653E19EDA41200A782A9 /* NSString+RACSupport.m in Sources */, - D037653619EDA41200A782A9 /* NSString+RACKeyPathUtilities.m in Sources */, - D03764FA19EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m in Sources */, - D037656819EDA41200A782A9 /* RACCompoundDisposableProvider.d in Sources */, - D037653A19EDA41200A782A9 /* NSString+RACSequenceAdditions.m in Sources */, - D03765E819EDA41200A782A9 /* RACStringSequence.m in Sources */, - D03764EE19EDA41200A782A9 /* NSControl+RACCommandSupport.m in Sources */, - D037660A19EDA41200A782A9 /* RACTupleSequence.m in Sources */, - D03765D419EDA41200A782A9 /* RACSignal.m in Sources */, - D037651A19EDA41200A782A9 /* NSObject+RACDescription.m in Sources */, - D03765A419EDA41200A782A9 /* RACMulticastConnection.m in Sources */, - D037654E19EDA41200A782A9 /* RACArraySequence.m in Sources */, - D037652219EDA41200A782A9 /* NSObject+RACLifting.m in Sources */, - D037650619EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m in Sources */, - D037650E19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m in Sources */, - D03765FA19EDA41200A782A9 /* RACSubscriptionScheduler.m in Sources */, - D037658019EDA41200A782A9 /* RACEmptySequence.m in Sources */, - D037654A19EDA41200A782A9 /* NSUserDefaults+RACSupport.m in Sources */, - D037660E19EDA41200A782A9 /* RACUnarySequence.m in Sources */, - D03765FE19EDA41200A782A9 /* RACTargetQueueScheduler.m in Sources */, - D03765DE19EDA41200A782A9 /* RACSignalSequence.m in Sources */, - D037656C19EDA41200A782A9 /* RACDelegateProxy.m in Sources */, - D037657419EDA41200A782A9 /* RACDynamicSequence.m in Sources */, - D037657019EDA41200A782A9 /* RACDisposable.m in Sources */, - D03765DA19EDA41200A782A9 /* RACSignalProvider.d in Sources */, - D037653219EDA41200A782A9 /* NSSet+RACSequenceAdditions.m in Sources */, - D037651219EDA41200A782A9 /* NSObject+RACAppKitBindings.m in Sources */, - D037656619EDA41200A782A9 /* RACCompoundDisposable.m in Sources */, - D037655A19EDA41200A782A9 /* RACBlockTrampoline.m in Sources */, - D037659019EDA41200A782A9 /* RACGroupedSignal.m in Sources */, - D037655E19EDA41200A782A9 /* RACChannel.m in Sources */, - D037657C19EDA41200A782A9 /* RACEagerSequence.m in Sources */, - D037657819EDA41200A782A9 /* RACDynamicSignal.m in Sources */, - D037659419EDA41200A782A9 /* RACImmediateScheduler.m in Sources */, - 7A7065811A3F88B8001E8354 /* RACKVOProxy.m in Sources */, - D037651619EDA41200A782A9 /* NSObject+RACDeallocating.m in Sources */, - D037658419EDA41200A782A9 /* RACEmptySignal.m in Sources */, - D037654619EDA41200A782A9 /* NSURLConnection+RACSupport.m in Sources */, - D03765F019EDA41200A782A9 /* RACSubscriber.m in Sources */, - D03764F619EDA41200A782A9 /* NSData+RACSupport.m in Sources */, - D037656219EDA41200A782A9 /* RACCommand.m in Sources */, - D037658819EDA41200A782A9 /* RACErrorSignal.m in Sources */, - D03765F619EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m in Sources */, - D037661219EDA41200A782A9 /* RACUnit.m in Sources */, - D03765A019EDA41200A782A9 /* RACKVOTrampoline.m in Sources */, - D037650A19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m in Sources */, - D037660619EDA41200A782A9 /* RACTuple.m in Sources */, - D037651E19EDA41200A782A9 /* NSObject+RACKVOWrapper.m in Sources */, - D037661619EDA41200A782A9 /* RACValueTransformer.m in Sources */, - D03765CC19EDA41200A782A9 /* RACSequence.m in Sources */, - D037652E19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m in Sources */, - D037652619EDA41200A782A9 /* NSObject+RACPropertySubscribing.m in Sources */, - D037658C19EDA41200A782A9 /* RACEvent.m in Sources */, - D03765B219EDA41200A782A9 /* RACQueueScheduler.m in Sources */, - D037652A19EDA41200A782A9 /* NSObject+RACSelectorSignal.m in Sources */, - D03765AE19EDA41200A782A9 /* RACPassthroughSubscriber.m in Sources */, - D03765BC19EDA41200A782A9 /* RACReturnSignal.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D04725F119E49ED7006002AA /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D03766C719EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.m in Sources */, - D03766E319EDA60000A782A9 /* RACDelegateProxySpec.m in Sources */, - D03766F919EDA60000A782A9 /* RACSerialDisposableSpec.m in Sources */, - D0C3131E19EF2D9700984962 /* RACTestExampleScheduler.m in Sources */, - D037670B19EDA60000A782A9 /* RACTargetQueueSchedulerSpec.m in Sources */, - D03766DD19EDA60000A782A9 /* RACCommandSpec.m in Sources */, - D037670919EDA60000A782A9 /* RACSubscriptingAssignmentTrampolineSpec.m in Sources */, - D03766EB19EDA60000A782A9 /* RACKVOWrapperSpec.m in Sources */, - D03766E719EDA60000A782A9 /* RACEventSpec.m in Sources */, - D03766F719EDA60000A782A9 /* RACSequenceSpec.m in Sources */, - D03766C919EDA60000A782A9 /* NSObjectRACPropertySubscribingSpec.m in Sources */, - D03766C319EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m in Sources */, - D03766BD19EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m in Sources */, - D037670119EDA60000A782A9 /* RACSubclassObject.m in Sources */, - D03766CD19EDA60000A782A9 /* NSStringRACKeyPathUtilitiesSpec.m in Sources */, - D037671519EDA60000A782A9 /* RACTupleSpec.m in Sources */, - D03766C519EDA60000A782A9 /* NSObjectRACLiftingSpec.m in Sources */, - D03766D119EDA60000A782A9 /* NSURLConnectionRACSupportSpec.m in Sources */, - D03766F319EDA60000A782A9 /* RACSequenceAdditionsSpec.m in Sources */, - D03766ED19EDA60000A782A9 /* RACMulticastConnectionSpec.m in Sources */, - D03766E919EDA60000A782A9 /* RACKVOChannelSpec.m in Sources */, - D03766FB19EDA60000A782A9 /* RACSignalSpec.m in Sources */, - 7A7065841A3F8967001E8354 /* RACKVOProxySpec.m in Sources */, - D037670719EDA60000A782A9 /* RACSubscriberSpec.m in Sources */, - D03766EF19EDA60000A782A9 /* RACPropertySignalExamples.m in Sources */, - D037670519EDA60000A782A9 /* RACSubscriberExamples.m in Sources */, - D0C3132219EF2D9700984962 /* RACTestSchedulerSpec.m in Sources */, - D03766D719EDA60000A782A9 /* RACBlockTrampolineSpec.m in Sources */, - D03766FF19EDA60000A782A9 /* RACStreamExamples.m in Sources */, - D03766CB19EDA60000A782A9 /* NSObjectRACSelectorSignalSpec.m in Sources */, - D03766E119EDA60000A782A9 /* RACControlCommandExamples.m in Sources */, - D03766BF19EDA60000A782A9 /* NSNotificationCenterRACSupportSpec.m in Sources */, - D037670319EDA60000A782A9 /* RACSubjectSpec.m in Sources */, - D03766F119EDA60000A782A9 /* RACSchedulerSpec.m in Sources */, - D03766DF19EDA60000A782A9 /* RACCompoundDisposableSpec.m in Sources */, - D03766E519EDA60000A782A9 /* RACDisposableSpec.m in Sources */, - D0C3132019EF2D9700984962 /* RACTestObject.m in Sources */, - D03766D319EDA60000A782A9 /* NSUserDefaultsRACSupportSpec.m in Sources */, - D03766C119EDA60000A782A9 /* NSObjectRACAppKitBindingsSpec.m in Sources */, - D03766DB19EDA60000A782A9 /* RACChannelSpec.m in Sources */, - D03766D919EDA60000A782A9 /* RACChannelExamples.m in Sources */, - D03766F519EDA60000A782A9 /* RACSequenceExamples.m in Sources */, - D03766B919EDA60000A782A9 /* NSControllerRACSupportSpec.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D047260719E49F82006002AA /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D037659D19EDA41200A782A9 /* RACKVOChannel.m in Sources */, - D037666319EDA41200A782A9 /* UITextView+RACSignalSupport.m in Sources */, - D037662F19EDA41200A782A9 /* UIControl+RACSignalSupport.m in Sources */, - D03765C919EDA41200A782A9 /* RACScopedDisposable.m in Sources */, - D03764FF19EDA41200A782A9 /* NSEnumerator+RACSequenceAdditions.m in Sources */, - D037664719EDA41200A782A9 /* UISegmentedControl+RACSignalSupport.m in Sources */, - D03764EB19EDA41200A782A9 /* NSArray+RACSequenceAdditions.m in Sources */, - D03765C119EDA41200A782A9 /* RACScheduler.m in Sources */, - D037662B19EDA41200A782A9 /* UICollectionReusableView+RACSignalSupport.m in Sources */, - D037659919EDA41200A782A9 /* RACIndexSetSequence.m in Sources */, - D03765D919EDA41200A782A9 /* RACSignal+Operations.m in Sources */, - D037661B19EDA41200A782A9 /* UIActionSheet+RACSignalSupport.m in Sources */, - D037650319EDA41200A782A9 /* NSFileHandle+RACSupport.m in Sources */, - D03765E319EDA41200A782A9 /* RACStream.m in Sources */, - D037655719EDA41200A782A9 /* RACBehaviorSubject.m in Sources */, - D037663B19EDA41200A782A9 /* UIGestureRecognizer+RACSignalSupport.m in Sources */, - D037660319EDA41200A782A9 /* RACTestScheduler.m in Sources */, - D03765B919EDA41200A782A9 /* RACReplaySubject.m in Sources */, - D03765ED19EDA41200A782A9 /* RACSubject.m in Sources */, - D037664F19EDA41200A782A9 /* UIStepper+RACSignalSupport.m in Sources */, - D03765D119EDA41200A782A9 /* RACSerialDisposable.m in Sources */, - D037663F19EDA41200A782A9 /* UIImagePickerController+RACSignalSupport.m in Sources */, - D037653F19EDA41200A782A9 /* NSString+RACSupport.m in Sources */, - D037653719EDA41200A782A9 /* NSString+RACKeyPathUtilities.m in Sources */, - D03764FB19EDA41200A782A9 /* NSDictionary+RACSequenceAdditions.m in Sources */, - D037656919EDA41200A782A9 /* RACCompoundDisposableProvider.d in Sources */, - D037653B19EDA41200A782A9 /* NSString+RACSequenceAdditions.m in Sources */, - D037661F19EDA41200A782A9 /* UIAlertView+RACSignalSupport.m in Sources */, - D03765E919EDA41200A782A9 /* RACStringSequence.m in Sources */, - D037660B19EDA41200A782A9 /* RACTupleSequence.m in Sources */, - D03765D519EDA41200A782A9 /* RACSignal.m in Sources */, - D037663319EDA41200A782A9 /* UIControl+RACSignalSupportPrivate.m in Sources */, - D037664319EDA41200A782A9 /* UIRefreshControl+RACCommandSupport.m in Sources */, - D037651B19EDA41200A782A9 /* NSObject+RACDescription.m in Sources */, - D03765A519EDA41200A782A9 /* RACMulticastConnection.m in Sources */, - D037654F19EDA41200A782A9 /* RACArraySequence.m in Sources */, - D037652319EDA41200A782A9 /* NSObject+RACLifting.m in Sources */, - D037650719EDA41200A782A9 /* NSIndexSet+RACSequenceAdditions.m in Sources */, - D037665F19EDA41200A782A9 /* UITextField+RACSignalSupport.m in Sources */, - D037650F19EDA41200A782A9 /* NSNotificationCenter+RACSupport.m in Sources */, - D03765FB19EDA41200A782A9 /* RACSubscriptionScheduler.m in Sources */, - D037658119EDA41200A782A9 /* RACEmptySequence.m in Sources */, - D037654B19EDA41200A782A9 /* NSUserDefaults+RACSupport.m in Sources */, - D037660F19EDA41200A782A9 /* RACUnarySequence.m in Sources */, - D03765FF19EDA41200A782A9 /* RACTargetQueueScheduler.m in Sources */, - D03765DF19EDA41200A782A9 /* RACSignalSequence.m in Sources */, - D037656D19EDA41200A782A9 /* RACDelegateProxy.m in Sources */, - D037657519EDA41200A782A9 /* RACDynamicSequence.m in Sources */, - D037657119EDA41200A782A9 /* RACDisposable.m in Sources */, - D03765DB19EDA41200A782A9 /* RACSignalProvider.d in Sources */, - D037653319EDA41200A782A9 /* NSSet+RACSequenceAdditions.m in Sources */, - D037665319EDA41200A782A9 /* UISwitch+RACSignalSupport.m in Sources */, - D037664B19EDA41200A782A9 /* UISlider+RACSignalSupport.m in Sources */, - D037656719EDA41200A782A9 /* RACCompoundDisposable.m in Sources */, - D037655B19EDA41200A782A9 /* RACBlockTrampoline.m in Sources */, - D037659119EDA41200A782A9 /* RACGroupedSignal.m in Sources */, - D037655F19EDA41200A782A9 /* RACChannel.m in Sources */, - D037657D19EDA41200A782A9 /* RACEagerSequence.m in Sources */, - D037657919EDA41200A782A9 /* RACDynamicSignal.m in Sources */, - D037659519EDA41200A782A9 /* RACImmediateScheduler.m in Sources */, - D037651719EDA41200A782A9 /* NSObject+RACDeallocating.m in Sources */, - D037658519EDA41200A782A9 /* RACEmptySignal.m in Sources */, - D037663719EDA41200A782A9 /* UIDatePicker+RACSignalSupport.m in Sources */, - D037654719EDA41200A782A9 /* NSURLConnection+RACSupport.m in Sources */, - D03765F119EDA41200A782A9 /* RACSubscriber.m in Sources */, - D03764F719EDA41200A782A9 /* NSData+RACSupport.m in Sources */, - D037656319EDA41200A782A9 /* RACCommand.m in Sources */, - D037658919EDA41200A782A9 /* RACErrorSignal.m in Sources */, - D03765F719EDA41200A782A9 /* RACSubscriptingAssignmentTrampoline.m in Sources */, - D037661319EDA41200A782A9 /* RACUnit.m in Sources */, - D037662319EDA41200A782A9 /* UIBarButtonItem+RACCommandSupport.m in Sources */, - D03765A119EDA41200A782A9 /* RACKVOTrampoline.m in Sources */, - D037665B19EDA41200A782A9 /* UITableViewHeaderFooterView+RACSignalSupport.m in Sources */, - D037650B19EDA41200A782A9 /* NSInvocation+RACTypeParsing.m in Sources */, - D037660719EDA41200A782A9 /* RACTuple.m in Sources */, - D037667019EDA57100A782A9 /* EXTRuntimeExtensions.m in Sources */, - D037651F19EDA41200A782A9 /* NSObject+RACKVOWrapper.m in Sources */, - D037661719EDA41200A782A9 /* RACValueTransformer.m in Sources */, - D03765CD19EDA41200A782A9 /* RACSequence.m in Sources */, - 314304181ACA8B1E00595017 /* MKAnnotationView+RACSignalSupport.m in Sources */, - D037652F19EDA41200A782A9 /* NSOrderedSet+RACSequenceAdditions.m in Sources */, - D037662719EDA41200A782A9 /* UIButton+RACCommandSupport.m in Sources */, - D037652719EDA41200A782A9 /* NSObject+RACPropertySubscribing.m in Sources */, - 7A7065821A3F88B8001E8354 /* RACKVOProxy.m in Sources */, - D037658D19EDA41200A782A9 /* RACEvent.m in Sources */, - D03765B319EDA41200A782A9 /* RACQueueScheduler.m in Sources */, - D037665719EDA41200A782A9 /* UITableViewCell+RACSignalSupport.m in Sources */, - D037652B19EDA41200A782A9 /* NSObject+RACSelectorSignal.m in Sources */, - D03765AF19EDA41200A782A9 /* RACPassthroughSubscriber.m in Sources */, - D03765BD19EDA41200A782A9 /* RACReturnSignal.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D047261219E49F82006002AA /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D03766C819EDA60000A782A9 /* NSObjectRACPropertySubscribingExamples.m in Sources */, - D037672419EDA60000A782A9 /* UIImagePickerControllerRACSupportSpec.m in Sources */, - D03766E419EDA60000A782A9 /* RACDelegateProxySpec.m in Sources */, - D03766FA19EDA60000A782A9 /* RACSerialDisposableSpec.m in Sources */, - D037670C19EDA60000A782A9 /* RACTargetQueueSchedulerSpec.m in Sources */, - D03766DE19EDA60000A782A9 /* RACCommandSpec.m in Sources */, - D037670A19EDA60000A782A9 /* RACSubscriptingAssignmentTrampolineSpec.m in Sources */, - D03766EC19EDA60000A782A9 /* RACKVOWrapperSpec.m in Sources */, - D03766E819EDA60000A782A9 /* RACEventSpec.m in Sources */, - D03766F819EDA60000A782A9 /* RACSequenceSpec.m in Sources */, - D037671E19EDA60000A782A9 /* UIBarButtonItemRACSupportSpec.m in Sources */, - D03766CA19EDA60000A782A9 /* NSObjectRACPropertySubscribingSpec.m in Sources */, - D0C3132319EF2D9700984962 /* RACTestSchedulerSpec.m in Sources */, - D03766C419EDA60000A782A9 /* NSObjectRACDeallocatingSpec.m in Sources */, - D03766BE19EDA60000A782A9 /* NSEnumeratorRACSequenceAdditionsSpec.m in Sources */, - D037672019EDA60000A782A9 /* UIButtonRACSupportSpec.m in Sources */, - D0C3132519EF2D9700984962 /* RACTestUIButton.m in Sources */, - D037670219EDA60000A782A9 /* RACSubclassObject.m in Sources */, - D03766CE19EDA60000A782A9 /* NSStringRACKeyPathUtilitiesSpec.m in Sources */, - D037671619EDA60000A782A9 /* RACTupleSpec.m in Sources */, - 7A7065851A3F8967001E8354 /* RACKVOProxySpec.m in Sources */, - D03766C619EDA60000A782A9 /* NSObjectRACLiftingSpec.m in Sources */, - D0C3131F19EF2D9700984962 /* RACTestExampleScheduler.m in Sources */, - D03766D219EDA60000A782A9 /* NSURLConnectionRACSupportSpec.m in Sources */, - D03766F419EDA60000A782A9 /* RACSequenceAdditionsSpec.m in Sources */, - D03766EE19EDA60000A782A9 /* RACMulticastConnectionSpec.m in Sources */, - D03766EA19EDA60000A782A9 /* RACKVOChannelSpec.m in Sources */, - D0C3132119EF2D9700984962 /* RACTestObject.m in Sources */, - D03766FC19EDA60000A782A9 /* RACSignalSpec.m in Sources */, - D037670819EDA60000A782A9 /* RACSubscriberSpec.m in Sources */, - D037671C19EDA60000A782A9 /* UIAlertViewRACSupportSpec.m in Sources */, - D03766F019EDA60000A782A9 /* RACPropertySignalExamples.m in Sources */, - D037670619EDA60000A782A9 /* RACSubscriberExamples.m in Sources */, - D03766D819EDA60000A782A9 /* RACBlockTrampolineSpec.m in Sources */, - D037670019EDA60000A782A9 /* RACStreamExamples.m in Sources */, - D03766CC19EDA60000A782A9 /* NSObjectRACSelectorSignalSpec.m in Sources */, - D03766E219EDA60000A782A9 /* RACControlCommandExamples.m in Sources */, - D03766C019EDA60000A782A9 /* NSNotificationCenterRACSupportSpec.m in Sources */, - D037670419EDA60000A782A9 /* RACSubjectSpec.m in Sources */, - D03766F219EDA60000A782A9 /* RACSchedulerSpec.m in Sources */, - D03766E019EDA60000A782A9 /* RACCompoundDisposableSpec.m in Sources */, - D03766E619EDA60000A782A9 /* RACDisposableSpec.m in Sources */, - D03766D419EDA60000A782A9 /* NSUserDefaultsRACSupportSpec.m in Sources */, - D03766DC19EDA60000A782A9 /* RACChannelSpec.m in Sources */, - D037671A19EDA60000A782A9 /* UIActionSheetRACSupportSpec.m in Sources */, - D03766DA19EDA60000A782A9 /* RACChannelExamples.m in Sources */, - D03766F619EDA60000A782A9 /* RACSequenceExamples.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 7DFBED0A1CDB8C9500EE435B /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 57A4D1AF1BA13D7A00F7D4B1 /* ReactiveObjC-tvOS */; - targetProxy = 7DFBED091CDB8C9500EE435B /* PBXContainerItemProxy */; - }; - D04725F819E49ED7006002AA /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D04725E919E49ED7006002AA /* ReactiveObjC-macOS */; - targetProxy = D04725F719E49ED7006002AA /* PBXContainerItemProxy */; - }; - D047261919E49F82006002AA /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D047260B19E49F82006002AA /* ReactiveObjC-iOS */; - targetProxy = D047261819E49F82006002AA /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 57A4D23D1BA13D7A00F7D4B1 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); - INFOPLIST_FILE = ReactiveObjC/Info.plist; - }; - name = Debug; - }; - 57A4D23E1BA13D7A00F7D4B1 /* Test */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); - INFOPLIST_FILE = ReactiveObjC/Info.plist; - }; - name = Test; - }; - 57A4D23F1BA13D7A00F7D4B1 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); - INFOPLIST_FILE = ReactiveObjC/Info.plist; - }; - name = Release; - }; - 57A4D2401BA13D7A00F7D4B1 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); - INFOPLIST_FILE = ReactiveObjC/Info.plist; - }; - name = Profile; - }; - 7DFBED0B1CDB8C9500EE435B /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CODE_SIGN_IDENTITY = ""; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - INFOPLIST_FILE = ReactiveObjCTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Debug; - }; - 7DFBED0C1CDB8C9500EE435B /* Test */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CODE_SIGN_IDENTITY = ""; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - INFOPLIST_FILE = ReactiveObjCTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Test; - }; - 7DFBED0D1CDB8C9500EE435B /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CODE_SIGN_IDENTITY = ""; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - INFOPLIST_FILE = ReactiveObjCTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Release; - }; - 7DFBED0E1CDB8C9500EE435B /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CODE_SIGN_IDENTITY = ""; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - INFOPLIST_FILE = ReactiveObjCTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Profile; - }; - A9B315591B3940610001CB9C /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); - INFOPLIST_FILE = ReactiveObjC/Info.plist; - }; - name = Debug; - }; - A9B3155A1B3940610001CB9C /* Test */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); - INFOPLIST_FILE = ReactiveObjC/Info.plist; - }; - name = Test; - }; - A9B3155B1B3940610001CB9C /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); - INFOPLIST_FILE = ReactiveObjC/Info.plist; - }; - name = Release; - }; - A9B3155C1B3940610001CB9C /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); - INFOPLIST_FILE = ReactiveObjC/Info.plist; - }; - name = Profile; - }; - D04725FE19E49ED7006002AA /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047262919E49FE8006002AA /* Debug.xcconfig */; - buildSettings = { - BITCODE_GENERATION_MODE = bitcode; - CODE_SIGNING_REQUIRED = NO; - CURRENT_PROJECT_VERSION = 1; - ENABLE_TESTABILITY = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; - PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(PROJECT_NAME)"; - TVOS_DEPLOYMENT_TARGET = 9.0; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 2.0; - }; - name = Debug; - }; - D04725FF19E49ED7006002AA /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047262B19E49FE8006002AA /* Release.xcconfig */; - buildSettings = { - BITCODE_GENERATION_MODE = bitcode; - CODE_SIGNING_REQUIRED = NO; - CURRENT_PROJECT_VERSION = 1; - GCC_OPTIMIZATION_LEVEL = 0; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; - PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(PROJECT_NAME)"; - TVOS_DEPLOYMENT_TARGET = 9.0; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 2.0; - }; - name = Release; - }; - D047260119E49ED7006002AA /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - FRAMEWORK_VERSION = A; - INFOPLIST_FILE = ReactiveObjC/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - USER_HEADER_SEARCH_PATHS = ReactiveObjC/extobjc; - }; - name = Debug; - }; - D047260219E49ED7006002AA /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - FRAMEWORK_VERSION = A; - INFOPLIST_FILE = ReactiveObjC/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - USER_HEADER_SEARCH_PATHS = ReactiveObjC/extobjc; - }; - name = Release; - }; - D047260419E49ED7006002AA /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(DEVELOPER_FRAMEWORKS_DIR)", - "$(inherited)", - ); - INFOPLIST_FILE = ReactiveObjCTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Debug; - }; - D047260519E49ED7006002AA /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(DEVELOPER_FRAMEWORKS_DIR)", - "$(inherited)", - ); - INFOPLIST_FILE = ReactiveObjCTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Release; - }; - D047262019E49F82006002AA /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - INFOPLIST_FILE = ReactiveObjC/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - USER_HEADER_SEARCH_PATHS = ReactiveObjC/extobjc; - }; - name = Debug; - }; - D047262119E49F82006002AA /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - INFOPLIST_FILE = ReactiveObjC/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - USER_HEADER_SEARCH_PATHS = ReactiveObjC/extobjc; - }; - name = Release; - }; - D047262319E49F82006002AA /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - INFOPLIST_FILE = ReactiveObjCTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Debug; - }; - D047262419E49F82006002AA /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - INFOPLIST_FILE = ReactiveObjCTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Release; - }; - D047263D19E4A008006002AA /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047262A19E49FE8006002AA /* Profile.xcconfig */; - buildSettings = { - BITCODE_GENERATION_MODE = bitcode; - CODE_SIGNING_REQUIRED = NO; - CURRENT_PROJECT_VERSION = 1; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; - PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(PROJECT_NAME)"; - TVOS_DEPLOYMENT_TARGET = 9.0; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 2.0; - }; - name = Profile; - }; - D047263E19E4A008006002AA /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - FRAMEWORK_VERSION = A; - INFOPLIST_FILE = ReactiveObjC/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - USER_HEADER_SEARCH_PATHS = ReactiveObjC/extobjc; - }; - name = Profile; - }; - D047263F19E4A008006002AA /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(DEVELOPER_FRAMEWORKS_DIR)", - "$(inherited)", - ); - INFOPLIST_FILE = ReactiveObjCTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Profile; - }; - D047264019E4A008006002AA /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - INFOPLIST_FILE = ReactiveObjC/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - USER_HEADER_SEARCH_PATHS = ReactiveObjC/extobjc; - }; - name = Profile; - }; - D047264119E4A008006002AA /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - INFOPLIST_FILE = ReactiveObjCTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Profile; - }; - D047264219E4A00B006002AA /* Test */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047262C19E49FE8006002AA /* Test.xcconfig */; - buildSettings = { - BITCODE_GENERATION_MODE = bitcode; - CODE_SIGNING_REQUIRED = NO; - CURRENT_PROJECT_VERSION = 1; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; - PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)-Tests"; - PRODUCT_NAME = "$(PROJECT_NAME)"; - TVOS_DEPLOYMENT_TARGET = 9.0; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 2.0; - }; - name = Test; - }; - D047264319E4A00B006002AA /* Test */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - FRAMEWORK_VERSION = A; - INFOPLIST_FILE = ReactiveObjC/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - USER_HEADER_SEARCH_PATHS = ReactiveObjC/extobjc; - }; - name = Test; - }; - D047264419E4A00B006002AA /* Test */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(DEVELOPER_FRAMEWORKS_DIR)", - "$(inherited)", - ); - INFOPLIST_FILE = ReactiveObjCTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Test; - }; - D047264519E4A00B006002AA /* Test */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - INFOPLIST_FILE = ReactiveObjC/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - USER_HEADER_SEARCH_PATHS = ReactiveObjC/extobjc; - }; - name = Test; - }; - D047264619E4A00B006002AA /* Test */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - INFOPLIST_FILE = ReactiveObjCTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Test; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 57A4D23C1BA13D7A00F7D4B1 /* Build configuration list for PBXNativeTarget "ReactiveObjC-tvOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 57A4D23D1BA13D7A00F7D4B1 /* Debug */, - 57A4D23E1BA13D7A00F7D4B1 /* Test */, - 57A4D23F1BA13D7A00F7D4B1 /* Release */, - 57A4D2401BA13D7A00F7D4B1 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 7DFBED0F1CDB8C9500EE435B /* Build configuration list for PBXNativeTarget "ReactiveObjC-tvOSTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 7DFBED0B1CDB8C9500EE435B /* Debug */, - 7DFBED0C1CDB8C9500EE435B /* Test */, - 7DFBED0D1CDB8C9500EE435B /* Release */, - 7DFBED0E1CDB8C9500EE435B /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - A9B3155D1B3940610001CB9C /* Build configuration list for PBXNativeTarget "ReactiveObjC-watchOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - A9B315591B3940610001CB9C /* Debug */, - A9B3155A1B3940610001CB9C /* Test */, - A9B3155B1B3940610001CB9C /* Release */, - A9B3155C1B3940610001CB9C /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D04725E419E49ED7006002AA /* Build configuration list for PBXProject "ReactiveObjC" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D04725FE19E49ED7006002AA /* Debug */, - D047264219E4A00B006002AA /* Test */, - D04725FF19E49ED7006002AA /* Release */, - D047263D19E4A008006002AA /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D047260019E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjC-macOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D047260119E49ED7006002AA /* Debug */, - D047264319E4A00B006002AA /* Test */, - D047260219E49ED7006002AA /* Release */, - D047263E19E4A008006002AA /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D047260319E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjC-macOSTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D047260419E49ED7006002AA /* Debug */, - D047264419E4A00B006002AA /* Test */, - D047260519E49ED7006002AA /* Release */, - D047263F19E4A008006002AA /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D047261F19E49F82006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjC-iOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D047262019E49F82006002AA /* Debug */, - D047264519E4A00B006002AA /* Test */, - D047262119E49F82006002AA /* Release */, - D047264019E4A008006002AA /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D047262219E49F82006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjC-iOSTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D047262319E49F82006002AA /* Debug */, - D047264619E4A00B006002AA /* Test */, - D047262419E49F82006002AA /* Release */, - D047264119E4A008006002AA /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = D04725E119E49ED7006002AA /* Project object */; -} diff --git a/ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-iOS.xcscheme b/ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-iOS.xcscheme deleted file mode 100644 index 52e154e943..0000000000 --- a/ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-iOS.xcscheme +++ /dev/null @@ -1,142 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-macOS.xcscheme b/ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-macOS.xcscheme deleted file mode 100644 index fbf4e6c545..0000000000 --- a/ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-macOS.xcscheme +++ /dev/null @@ -1,142 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-tvOS.xcscheme b/ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-tvOS.xcscheme deleted file mode 100644 index f171d1a28a..0000000000 --- a/ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-tvOS.xcscheme +++ /dev/null @@ -1,142 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-watchOS.xcscheme b/ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-watchOS.xcscheme deleted file mode 100644 index 4193c26877..0000000000 --- a/ReactiveObjC.xcodeproj/xcshareddata/xcschemes/ReactiveObjC-watchOS.xcscheme +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ReactiveObjC.xcworkspace/contents.xcworkspacedata b/ReactiveObjC.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index b62e938513..0000000000 --- a/ReactiveObjC.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - diff --git a/ReactiveObjC/Info.plist b/ReactiveObjC/Info.plist deleted file mode 100644 index 9a11afdc43..0000000000 --- a/ReactiveObjC/Info.plist +++ /dev/null @@ -1,28 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - NSHumanReadableCopyright - Copyright © 2014 GitHub. All rights reserved. - NSPrincipalClass - - - diff --git a/ReactiveObjC/MKAnnotationView+RACSignalSupport.h b/ReactiveObjC/MKAnnotationView+RACSignalSupport.h deleted file mode 100644 index e0a3001bca..0000000000 --- a/ReactiveObjC/MKAnnotationView+RACSignalSupport.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// MKAnnotationView+RACSignalSupport.h -// ReactiveObjC -// -// Created by Zak Remer on 3/31/15. -// Copyright (c) 2015 GitHub. All rights reserved. -// - -#import -#import - -@class RACSignal; - -@interface MKAnnotationView (RACSignalSupport) - -/// A signal which will send a RACUnit whenever -prepareForReuse is invoked upon -/// the receiver. -/// -/// Examples -/// -/// [[[self.cancelButton -/// rac_signalForControlEvents:UIControlEventTouchUpInside] -/// takeUntil:self.rac_prepareForReuseSignal] -/// subscribeNext:^(UIButton *x) { -/// // do other things -/// }]; -@property (nonatomic, strong, readonly) RACSignal *rac_prepareForReuseSignal; - -@end diff --git a/ReactiveObjC/MKAnnotationView+RACSignalSupport.m b/ReactiveObjC/MKAnnotationView+RACSignalSupport.m deleted file mode 100644 index d138a79f48..0000000000 --- a/ReactiveObjC/MKAnnotationView+RACSignalSupport.m +++ /dev/null @@ -1,31 +0,0 @@ -// -// MKAnnotationView+RACSignalSupport.m -// ReactiveObjC -// -// Created by Zak Remer on 3/31/15. -// Copyright (c) 2015 GitHub. All rights reserved. -// - -#import "MKAnnotationView+RACSignalSupport.h" -#import "NSObject+RACDescription.h" -#import "NSObject+RACSelectorSignal.h" -#import "RACSignal+Operations.h" -#import "RACUnit.h" -#import - -@implementation MKAnnotationView (RACSignalSupport) - -- (RACSignal *)rac_prepareForReuseSignal { - RACSignal *signal = objc_getAssociatedObject(self, _cmd); - if (signal != nil) return signal; - - signal = [[[self - rac_signalForSelector:@selector(prepareForReuse)] - mapReplace:RACUnit.defaultUnit] - setNameWithFormat:@"%@ -rac_prepareForReuseSignal", RACDescription(self)]; - - objc_setAssociatedObject(self, _cmd, signal, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - return signal; -} - -@end diff --git a/ReactiveObjC/NSArray+RACSequenceAdditions.h b/ReactiveObjC/NSArray+RACSequenceAdditions.h deleted file mode 100644 index 2dcc596b31..0000000000 --- a/ReactiveObjC/NSArray+RACSequenceAdditions.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// NSArray+RACSequenceAdditions.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-10-29. -// Copyright (c) 2012 GitHub. All rights reserved. -// - -#import - -@class RACSequence; - -@interface NSArray (RACSequenceAdditions) - -/// Creates and returns a sequence corresponding to the receiver. -/// -/// Mutating the receiver will not affect the sequence after it's been created. -@property (nonatomic, copy, readonly) RACSequence *rac_sequence; - -@end diff --git a/ReactiveObjC/NSArray+RACSequenceAdditions.m b/ReactiveObjC/NSArray+RACSequenceAdditions.m deleted file mode 100644 index 4eaebb8a12..0000000000 --- a/ReactiveObjC/NSArray+RACSequenceAdditions.m +++ /dev/null @@ -1,18 +0,0 @@ -// -// NSArray+RACSequenceAdditions.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-10-29. -// Copyright (c) 2012 GitHub. All rights reserved. -// - -#import "NSArray+RACSequenceAdditions.h" -#import "RACArraySequence.h" - -@implementation NSArray (RACSequenceAdditions) - -- (RACSequence *)rac_sequence { - return [RACArraySequence sequenceWithArray:self offset:0]; -} - -@end diff --git a/ReactiveObjC/NSControl+RACCommandSupport.h b/ReactiveObjC/NSControl+RACCommandSupport.h deleted file mode 100644 index cef6126959..0000000000 --- a/ReactiveObjC/NSControl+RACCommandSupport.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// NSControl+RACCommandSupport.h -// ReactiveObjC -// -// Created by Josh Abernathy on 3/3/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import - -@class RACCommand<__contravariant InputType>; - -@interface NSControl (RACCommandSupport) - -/// Sets the control's command. When the control is clicked, the command is -/// executed with the sender of the event. The control's enabledness is bound -/// to the command's `canExecute`. -/// -/// Note: this will reset the control's target and action. -@property (nonatomic, strong) RACCommand<__kindof NSControl *> *rac_command; - -@end diff --git a/ReactiveObjC/NSControl+RACCommandSupport.m b/ReactiveObjC/NSControl+RACCommandSupport.m deleted file mode 100644 index 1d8f6ba250..0000000000 --- a/ReactiveObjC/NSControl+RACCommandSupport.m +++ /dev/null @@ -1,57 +0,0 @@ -// -// NSControl+RACCommandSupport.m -// ReactiveObjC -// -// Created by Josh Abernathy on 3/3/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "NSControl+RACCommandSupport.h" -#import "RACCommand.h" -#import "RACScopedDisposable.h" -#import "RACSignal+Operations.h" -#import - -static void *NSControlRACCommandKey = &NSControlRACCommandKey; -static void *NSControlEnabledDisposableKey = &NSControlEnabledDisposableKey; - -@implementation NSControl (RACCommandSupport) - -- (RACCommand *)rac_command { - return objc_getAssociatedObject(self, NSControlRACCommandKey); -} - -- (void)setRac_command:(RACCommand *)command { - objc_setAssociatedObject(self, NSControlRACCommandKey, command, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - - // Tear down any previous binding before setting up our new one, or else we - // might get assertion failures. - [objc_getAssociatedObject(self, NSControlEnabledDisposableKey) dispose]; - objc_setAssociatedObject(self, NSControlEnabledDisposableKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - - if (command == nil) { - self.enabled = YES; - return; - } - - [self rac_hijackActionAndTargetIfNeeded]; - - RACScopedDisposable *disposable = [[command.enabled setKeyPath:@"enabled" onObject:self] asScopedDisposable]; - objc_setAssociatedObject(self, NSControlEnabledDisposableKey, disposable, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -- (void)rac_hijackActionAndTargetIfNeeded { - SEL hijackSelector = @selector(rac_commandPerformAction:); - if (self.target == self && self.action == hijackSelector) return; - - if (self.target != nil) NSLog(@"WARNING: NSControl.rac_command hijacks the control's existing target and action."); - - self.target = self; - self.action = hijackSelector; -} - -- (void)rac_commandPerformAction:(id)sender { - [self.rac_command execute:sender]; -} - -@end diff --git a/ReactiveObjC/NSControl+RACTextSignalSupport.h b/ReactiveObjC/NSControl+RACTextSignalSupport.h deleted file mode 100644 index b7440f899a..0000000000 --- a/ReactiveObjC/NSControl+RACTextSignalSupport.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// NSControl+RACTextSignalSupport.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-03-08. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -@class RACSignal; - -@interface NSControl (RACTextSignalSupport) - -/// Observes a text-based control for changes. -/// -/// Using this method on a control without editable text is considered undefined -/// behavior. -/// -/// Returns a signal which sends the current string value of the receiver, then -/// the new value any time it changes. -- (RACSignal *)rac_textSignal; - -@end diff --git a/ReactiveObjC/NSControl+RACTextSignalSupport.m b/ReactiveObjC/NSControl+RACTextSignalSupport.m deleted file mode 100644 index 88da38f6e8..0000000000 --- a/ReactiveObjC/NSControl+RACTextSignalSupport.m +++ /dev/null @@ -1,38 +0,0 @@ -// -// NSControl+RACTextSignalSupport.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-03-08. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "NSControl+RACTextSignalSupport.h" -#import -#import "NSObject+RACDescription.h" -#import "RACDisposable.h" -#import "RACSignal.h" -#import "RACSubscriber.h" - -@implementation NSControl (RACTextSignalSupport) - -- (RACSignal *)rac_textSignal { - @weakify(self); - return [[[[RACSignal - createSignal:^(id subscriber) { - @strongify(self); - id observer = [NSNotificationCenter.defaultCenter addObserverForName:NSControlTextDidChangeNotification object:self queue:nil usingBlock:^(NSNotification *note) { - [subscriber sendNext:note.object]; - }]; - - return [RACDisposable disposableWithBlock:^{ - [NSNotificationCenter.defaultCenter removeObserver:observer]; - }]; - }] - map:^(NSControl *control) { - return [control.stringValue copy]; - }] - startWith:[self.stringValue copy]] - setNameWithFormat:@"%@ -rac_textSignal", RACDescription(self)]; -} - -@end diff --git a/ReactiveObjC/NSData+RACSupport.h b/ReactiveObjC/NSData+RACSupport.h deleted file mode 100644 index 55edcd8216..0000000000 --- a/ReactiveObjC/NSData+RACSupport.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// NSData+RACSupport.h -// ReactiveObjC -// -// Created by Josh Abernathy on 5/11/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import - -@class RACScheduler; -@class RACSignal; - -@interface NSData (RACSupport) - -// Read the data at the URL using -[NSData initWithContentsOfURL:options:error:]. -// Sends the data or the error. -// -// scheduler - cannot be nil. -+ (RACSignal *)rac_readContentsOfURL:(NSURL *)URL options:(NSDataReadingOptions)options scheduler:(RACScheduler *)scheduler; - -@end diff --git a/ReactiveObjC/NSData+RACSupport.m b/ReactiveObjC/NSData+RACSupport.m deleted file mode 100644 index 977ba4aea7..0000000000 --- a/ReactiveObjC/NSData+RACSupport.m +++ /dev/null @@ -1,35 +0,0 @@ -// -// NSData+RACSupport.m -// ReactiveObjC -// -// Created by Josh Abernathy on 5/11/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "NSData+RACSupport.h" -#import "RACReplaySubject.h" -#import "RACScheduler.h" - -@implementation NSData (RACSupport) - -+ (RACSignal *)rac_readContentsOfURL:(NSURL *)URL options:(NSDataReadingOptions)options scheduler:(RACScheduler *)scheduler { - NSCParameterAssert(scheduler != nil); - - RACReplaySubject *subject = [RACReplaySubject subject]; - [subject setNameWithFormat:@"+rac_readContentsOfURL: %@ options: %lu scheduler: %@", URL, (unsigned long)options, scheduler]; - - [scheduler schedule:^{ - NSError *error = nil; - NSData *data = [[NSData alloc] initWithContentsOfURL:URL options:options error:&error]; - if (data == nil) { - [subject sendError:error]; - } else { - [subject sendNext:data]; - [subject sendCompleted]; - } - }]; - - return subject; -} - -@end diff --git a/ReactiveObjC/NSDictionary+RACSequenceAdditions.h b/ReactiveObjC/NSDictionary+RACSequenceAdditions.h deleted file mode 100644 index 1eb0534c29..0000000000 --- a/ReactiveObjC/NSDictionary+RACSequenceAdditions.h +++ /dev/null @@ -1,31 +0,0 @@ -// -// NSDictionary+RACSequenceAdditions.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-10-29. -// Copyright (c) 2012 GitHub. All rights reserved. -// - -#import - -@class RACSequence; - -@interface NSDictionary (RACSequenceAdditions) - -/// Creates and returns a sequence of RACTuple key/value pairs. The key will be -/// the first element in the tuple, and the value will be the second. -/// -/// Mutating the receiver will not affect the sequence after it's been created. -@property (nonatomic, copy, readonly) RACSequence *rac_sequence; - -/// Creates and returns a sequence corresponding to the keys in the receiver. -/// -/// Mutating the receiver will not affect the sequence after it's been created. -@property (nonatomic, copy, readonly) RACSequence *rac_keySequence; - -/// Creates and returns a sequence corresponding to the values in the receiver. -/// -/// Mutating the receiver will not affect the sequence after it's been created. -@property (nonatomic, copy, readonly) RACSequence *rac_valueSequence; - -@end diff --git a/ReactiveObjC/NSDictionary+RACSequenceAdditions.m b/ReactiveObjC/NSDictionary+RACSequenceAdditions.m deleted file mode 100644 index 2998b9a154..0000000000 --- a/ReactiveObjC/NSDictionary+RACSequenceAdditions.m +++ /dev/null @@ -1,34 +0,0 @@ -// -// NSDictionary+RACSequenceAdditions.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-10-29. -// Copyright (c) 2012 GitHub. All rights reserved. -// - -#import "NSDictionary+RACSequenceAdditions.h" -#import "NSArray+RACSequenceAdditions.h" -#import "RACSequence.h" -#import "RACTuple.h" - -@implementation NSDictionary (RACSequenceAdditions) - -- (RACSequence *)rac_sequence { - NSDictionary *immutableDict = [self copy]; - - // TODO: First class support for dictionary sequences. - return [immutableDict.allKeys.rac_sequence map:^(id key) { - id value = immutableDict[key]; - return RACTuplePack(key, value); - }]; -} - -- (RACSequence *)rac_keySequence { - return self.allKeys.rac_sequence; -} - -- (RACSequence *)rac_valueSequence { - return self.allValues.rac_sequence; -} - -@end diff --git a/ReactiveObjC/NSEnumerator+RACSequenceAdditions.h b/ReactiveObjC/NSEnumerator+RACSequenceAdditions.h deleted file mode 100644 index 4c172a69f8..0000000000 --- a/ReactiveObjC/NSEnumerator+RACSequenceAdditions.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// NSEnumerator+RACSequenceAdditions.h -// ReactiveObjC -// -// Created by Uri Baghin on 07/01/2013. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -@class RACSequence; - -@interface NSEnumerator (RACSequenceAdditions) - -/// Creates and returns a sequence corresponding to the receiver. -/// -/// The receiver is exhausted lazily as the sequence is enumerated. -@property (nonatomic, copy, readonly) RACSequence *rac_sequence; - -@end diff --git a/ReactiveObjC/NSEnumerator+RACSequenceAdditions.m b/ReactiveObjC/NSEnumerator+RACSequenceAdditions.m deleted file mode 100644 index 59b6380009..0000000000 --- a/ReactiveObjC/NSEnumerator+RACSequenceAdditions.m +++ /dev/null @@ -1,22 +0,0 @@ -// -// NSEnumerator+RACSequenceAdditions.m -// ReactiveObjC -// -// Created by Uri Baghin on 07/01/2013. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "NSEnumerator+RACSequenceAdditions.h" -#import "RACSequence.h" - -@implementation NSEnumerator (RACSequenceAdditions) - -- (RACSequence *)rac_sequence { - return [RACSequence sequenceWithHeadBlock:^{ - return [self nextObject]; - } tailBlock:^{ - return self.rac_sequence; - }]; -} - -@end diff --git a/ReactiveObjC/NSFileHandle+RACSupport.h b/ReactiveObjC/NSFileHandle+RACSupport.h deleted file mode 100644 index 705745909d..0000000000 --- a/ReactiveObjC/NSFileHandle+RACSupport.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// NSFileHandle+RACSupport.h -// ReactiveObjC -// -// Created by Josh Abernathy on 5/10/12. -// Copyright (c) 2012 GitHub. All rights reserved. -// - -#import - -@class RACSignal; - -@interface NSFileHandle (RACSupport) - -// Read any available data in the background and send it. Completes when data -// length is <= 0. -- (RACSignal *)rac_readInBackground; - -@end diff --git a/ReactiveObjC/NSFileHandle+RACSupport.m b/ReactiveObjC/NSFileHandle+RACSupport.m deleted file mode 100644 index 9d1c42f37d..0000000000 --- a/ReactiveObjC/NSFileHandle+RACSupport.m +++ /dev/null @@ -1,40 +0,0 @@ -// -// NSFileHandle+RACSupport.m -// ReactiveObjC -// -// Created by Josh Abernathy on 5/10/12. -// Copyright (c) 2012 GitHub. All rights reserved. -// - -#import "NSFileHandle+RACSupport.h" -#import "NSNotificationCenter+RACSupport.h" -#import "NSObject+RACDescription.h" -#import "RACReplaySubject.h" -#import "RACDisposable.h" - -@implementation NSFileHandle (RACSupport) - -- (RACSignal *)rac_readInBackground { - RACReplaySubject *subject = [RACReplaySubject subject]; - [subject setNameWithFormat:@"%@ -rac_readInBackground", RACDescription(self)]; - - RACSignal *dataNotification = [[[NSNotificationCenter defaultCenter] rac_addObserverForName:NSFileHandleReadCompletionNotification object:self] map:^(NSNotification *note) { - return note.userInfo[NSFileHandleNotificationDataItem]; - }]; - - __block RACDisposable *subscription = [dataNotification subscribeNext:^(NSData *data) { - if (data.length > 0) { - [subject sendNext:data]; - [self readInBackgroundAndNotify]; - } else { - [subject sendCompleted]; - [subscription dispose]; - } - }]; - - [self readInBackgroundAndNotify]; - - return subject; -} - -@end diff --git a/ReactiveObjC/NSIndexSet+RACSequenceAdditions.h b/ReactiveObjC/NSIndexSet+RACSequenceAdditions.h deleted file mode 100644 index e431d68bc7..0000000000 --- a/ReactiveObjC/NSIndexSet+RACSequenceAdditions.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// NSIndexSet+RACSequenceAdditions.h -// ReactiveObjC -// -// Created by Sergey Gavrilyuk on 12/17/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -@class RACSequence; - -@interface NSIndexSet (RACSequenceAdditions) - -/// Creates and returns a sequence of indexes (as `NSNumber`s) corresponding to -/// the receiver. -/// -/// Mutating the receiver will not affect the sequence after it's been created. -@property (nonatomic, copy, readonly) RACSequence *rac_sequence; - -@end diff --git a/ReactiveObjC/NSIndexSet+RACSequenceAdditions.m b/ReactiveObjC/NSIndexSet+RACSequenceAdditions.m deleted file mode 100644 index e2866b823b..0000000000 --- a/ReactiveObjC/NSIndexSet+RACSequenceAdditions.m +++ /dev/null @@ -1,18 +0,0 @@ -// -// NSIndexSet+RACSequenceAdditions.m -// ReactiveObjC -// -// Created by Sergey Gavrilyuk on 12/17/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "NSIndexSet+RACSequenceAdditions.h" -#import "RACIndexSetSequence.h" - -@implementation NSIndexSet (RACSequenceAdditions) - -- (RACSequence *)rac_sequence { - return [RACIndexSetSequence sequenceWithIndexSet:self]; -} - -@end diff --git a/ReactiveObjC/NSInvocation+RACTypeParsing.h b/ReactiveObjC/NSInvocation+RACTypeParsing.h deleted file mode 100644 index a7ac23b731..0000000000 --- a/ReactiveObjC/NSInvocation+RACTypeParsing.h +++ /dev/null @@ -1,56 +0,0 @@ -// -// NSInvocation+RACTypeParsing.h -// ReactiveObjC -// -// Created by Josh Abernathy on 11/17/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import - -@class RACTuple; - -// A private category of methods to handle wrapping and unwrapping of values. -@interface NSInvocation (RACTypeParsing) - -// Sets the argument for the invocation at the given index by unboxing the given -// object based on the type signature of the argument. -// -// This does not support C arrays or unions. -// -// Note that calling this on a char * or const char * argument can cause all -// arguments to be retained. -// -// object - The object to unbox and set as the argument. -// index - The index of the argument to set. -- (void)rac_setArgument:(id)object atIndex:(NSUInteger)index; - -// Gets the argument for the invocation at the given index based on the -// invocation's method signature. The value is then wrapped in the appropriate -// object type. -// -// This does not support C arrays or unions. -// -// index - The index of the argument to get. -// -// Returns the argument of the invocation, wrapped in an object. -- (id)rac_argumentAtIndex:(NSUInteger)index; - -// Arguments tuple for the invocation. -// -// The arguments tuple excludes implicit variables `self` and `_cmd`. -// -// See -rac_argumentAtIndex: and -rac_setArgumentAtIndex: for further -// description of the underlying behavior. -@property (nonatomic, copy) RACTuple *rac_argumentsTuple; - -// Gets the return value from the invocation based on the invocation's method -// signature. The value is then wrapped in the appropriate object type. -// -// This does not support C arrays or unions. -// -// Returns the return value of the invocation, wrapped in an object. Voids are -// returned as `RACUnit.defaultUnit`. -- (id)rac_returnValue; - -@end diff --git a/ReactiveObjC/NSInvocation+RACTypeParsing.m b/ReactiveObjC/NSInvocation+RACTypeParsing.m deleted file mode 100644 index 92b5b7d064..0000000000 --- a/ReactiveObjC/NSInvocation+RACTypeParsing.m +++ /dev/null @@ -1,232 +0,0 @@ -// -// NSInvocation+RACTypeParsing.m -// ReactiveObjC -// -// Created by Josh Abernathy on 11/17/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "NSInvocation+RACTypeParsing.h" -#import "RACTuple.h" -#import "RACUnit.h" -#import - -@implementation NSInvocation (RACTypeParsing) - -- (void)rac_setArgument:(id)object atIndex:(NSUInteger)index { -#define PULL_AND_SET(type, selector) \ - do { \ - type val = [object selector]; \ - [self setArgument:&val atIndex:(NSInteger)index]; \ - } while (0) - - const char *argType = [self.methodSignature getArgumentTypeAtIndex:index]; - // Skip const type qualifier. - if (argType[0] == 'r') { - argType++; - } - - if (strcmp(argType, @encode(id)) == 0 || strcmp(argType, @encode(Class)) == 0) { - [self setArgument:&object atIndex:(NSInteger)index]; - } else if (strcmp(argType, @encode(char)) == 0) { - PULL_AND_SET(char, charValue); - } else if (strcmp(argType, @encode(int)) == 0) { - PULL_AND_SET(int, intValue); - } else if (strcmp(argType, @encode(short)) == 0) { - PULL_AND_SET(short, shortValue); - } else if (strcmp(argType, @encode(long)) == 0) { - PULL_AND_SET(long, longValue); - } else if (strcmp(argType, @encode(long long)) == 0) { - PULL_AND_SET(long long, longLongValue); - } else if (strcmp(argType, @encode(unsigned char)) == 0) { - PULL_AND_SET(unsigned char, unsignedCharValue); - } else if (strcmp(argType, @encode(unsigned int)) == 0) { - PULL_AND_SET(unsigned int, unsignedIntValue); - } else if (strcmp(argType, @encode(unsigned short)) == 0) { - PULL_AND_SET(unsigned short, unsignedShortValue); - } else if (strcmp(argType, @encode(unsigned long)) == 0) { - PULL_AND_SET(unsigned long, unsignedLongValue); - } else if (strcmp(argType, @encode(unsigned long long)) == 0) { - PULL_AND_SET(unsigned long long, unsignedLongLongValue); - } else if (strcmp(argType, @encode(float)) == 0) { - PULL_AND_SET(float, floatValue); - } else if (strcmp(argType, @encode(double)) == 0) { - PULL_AND_SET(double, doubleValue); - } else if (strcmp(argType, @encode(BOOL)) == 0) { - PULL_AND_SET(BOOL, boolValue); - } else if (strcmp(argType, @encode(char *)) == 0) { - const char *cString = [object UTF8String]; - [self setArgument:&cString atIndex:(NSInteger)index]; - [self retainArguments]; - } else if (strcmp(argType, @encode(void (^)(void))) == 0) { - [self setArgument:&object atIndex:(NSInteger)index]; - } else { - NSCParameterAssert([object isKindOfClass:NSValue.class]); - - NSUInteger valueSize = 0; - NSGetSizeAndAlignment([object objCType], &valueSize, NULL); - -#if DEBUG - NSUInteger argSize = 0; - NSGetSizeAndAlignment(argType, &argSize, NULL); - NSCAssert(valueSize == argSize, @"Value size does not match argument size in -rac_setArgument: %@ atIndex: %lu", object, (unsigned long)index); -#endif - - unsigned char valueBytes[valueSize]; - [object getValue:valueBytes]; - - [self setArgument:valueBytes atIndex:(NSInteger)index]; - } - -#undef PULL_AND_SET -} - -- (id)rac_argumentAtIndex:(NSUInteger)index { -#define WRAP_AND_RETURN(type) \ - do { \ - type val = 0; \ - [self getArgument:&val atIndex:(NSInteger)index]; \ - return @(val); \ - } while (0) - - const char *argType = [self.methodSignature getArgumentTypeAtIndex:index]; - // Skip const type qualifier. - if (argType[0] == 'r') { - argType++; - } - - if (strcmp(argType, @encode(id)) == 0 || strcmp(argType, @encode(Class)) == 0) { - __autoreleasing id returnObj; - [self getArgument:&returnObj atIndex:(NSInteger)index]; - return returnObj; - } else if (strcmp(argType, @encode(char)) == 0) { - WRAP_AND_RETURN(char); - } else if (strcmp(argType, @encode(int)) == 0) { - WRAP_AND_RETURN(int); - } else if (strcmp(argType, @encode(short)) == 0) { - WRAP_AND_RETURN(short); - } else if (strcmp(argType, @encode(long)) == 0) { - WRAP_AND_RETURN(long); - } else if (strcmp(argType, @encode(long long)) == 0) { - WRAP_AND_RETURN(long long); - } else if (strcmp(argType, @encode(unsigned char)) == 0) { - WRAP_AND_RETURN(unsigned char); - } else if (strcmp(argType, @encode(unsigned int)) == 0) { - WRAP_AND_RETURN(unsigned int); - } else if (strcmp(argType, @encode(unsigned short)) == 0) { - WRAP_AND_RETURN(unsigned short); - } else if (strcmp(argType, @encode(unsigned long)) == 0) { - WRAP_AND_RETURN(unsigned long); - } else if (strcmp(argType, @encode(unsigned long long)) == 0) { - WRAP_AND_RETURN(unsigned long long); - } else if (strcmp(argType, @encode(float)) == 0) { - WRAP_AND_RETURN(float); - } else if (strcmp(argType, @encode(double)) == 0) { - WRAP_AND_RETURN(double); - } else if (strcmp(argType, @encode(BOOL)) == 0) { - WRAP_AND_RETURN(BOOL); - } else if (strcmp(argType, @encode(char *)) == 0) { - WRAP_AND_RETURN(const char *); - } else if (strcmp(argType, @encode(void (^)(void))) == 0) { - __unsafe_unretained id block = nil; - [self getArgument:&block atIndex:(NSInteger)index]; - return [block copy]; - } else { - NSUInteger valueSize = 0; - NSGetSizeAndAlignment(argType, &valueSize, NULL); - - unsigned char valueBytes[valueSize]; - [self getArgument:valueBytes atIndex:(NSInteger)index]; - - return [NSValue valueWithBytes:valueBytes objCType:argType]; - } - - return nil; - -#undef WRAP_AND_RETURN -} - -- (RACTuple *)rac_argumentsTuple { - NSUInteger numberOfArguments = self.methodSignature.numberOfArguments; - NSMutableArray *argumentsArray = [NSMutableArray arrayWithCapacity:numberOfArguments - 2]; - for (NSUInteger index = 2; index < numberOfArguments; index++) { - [argumentsArray addObject:[self rac_argumentAtIndex:index] ?: RACTupleNil.tupleNil]; - } - - return [RACTuple tupleWithObjectsFromArray:argumentsArray]; -} - -- (void)setRac_argumentsTuple:(RACTuple *)arguments { - NSCAssert(arguments.count == self.methodSignature.numberOfArguments - 2, @"Number of supplied arguments (%lu), does not match the number expected by the signature (%lu)", (unsigned long)arguments.count, (unsigned long)self.methodSignature.numberOfArguments - 2); - - NSUInteger index = 2; - for (id arg in arguments) { - [self rac_setArgument:(arg == RACTupleNil.tupleNil ? nil : arg) atIndex:index]; - index++; - } -} - -- (id)rac_returnValue { -#define WRAP_AND_RETURN(type) \ - do { \ - type val = 0; \ - [self getReturnValue:&val]; \ - return @(val); \ - } while (0) - - const char *returnType = self.methodSignature.methodReturnType; - // Skip const type qualifier. - if (returnType[0] == 'r') { - returnType++; - } - - if (strcmp(returnType, @encode(id)) == 0 || strcmp(returnType, @encode(Class)) == 0 || strcmp(returnType, @encode(void (^)(void))) == 0) { - __autoreleasing id returnObj; - [self getReturnValue:&returnObj]; - return returnObj; - } else if (strcmp(returnType, @encode(char)) == 0) { - WRAP_AND_RETURN(char); - } else if (strcmp(returnType, @encode(int)) == 0) { - WRAP_AND_RETURN(int); - } else if (strcmp(returnType, @encode(short)) == 0) { - WRAP_AND_RETURN(short); - } else if (strcmp(returnType, @encode(long)) == 0) { - WRAP_AND_RETURN(long); - } else if (strcmp(returnType, @encode(long long)) == 0) { - WRAP_AND_RETURN(long long); - } else if (strcmp(returnType, @encode(unsigned char)) == 0) { - WRAP_AND_RETURN(unsigned char); - } else if (strcmp(returnType, @encode(unsigned int)) == 0) { - WRAP_AND_RETURN(unsigned int); - } else if (strcmp(returnType, @encode(unsigned short)) == 0) { - WRAP_AND_RETURN(unsigned short); - } else if (strcmp(returnType, @encode(unsigned long)) == 0) { - WRAP_AND_RETURN(unsigned long); - } else if (strcmp(returnType, @encode(unsigned long long)) == 0) { - WRAP_AND_RETURN(unsigned long long); - } else if (strcmp(returnType, @encode(float)) == 0) { - WRAP_AND_RETURN(float); - } else if (strcmp(returnType, @encode(double)) == 0) { - WRAP_AND_RETURN(double); - } else if (strcmp(returnType, @encode(BOOL)) == 0) { - WRAP_AND_RETURN(BOOL); - } else if (strcmp(returnType, @encode(char *)) == 0) { - WRAP_AND_RETURN(const char *); - } else if (strcmp(returnType, @encode(void)) == 0) { - return RACUnit.defaultUnit; - } else { - NSUInteger valueSize = 0; - NSGetSizeAndAlignment(returnType, &valueSize, NULL); - - unsigned char valueBytes[valueSize]; - [self getReturnValue:valueBytes]; - - return [NSValue valueWithBytes:valueBytes objCType:returnType]; - } - - return nil; - -#undef WRAP_AND_RETURN -} - -@end diff --git a/ReactiveObjC/NSNotificationCenter+RACSupport.h b/ReactiveObjC/NSNotificationCenter+RACSupport.h deleted file mode 100644 index 06b8dc8637..0000000000 --- a/ReactiveObjC/NSNotificationCenter+RACSupport.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// NSNotificationCenter+RACSupport.h -// ReactiveObjC -// -// Created by Josh Abernathy on 5/10/12. -// Copyright (c) 2012 GitHub. All rights reserved. -// - -#import - -@class RACSignal; - -@interface NSNotificationCenter (RACSupport) - -// Sends the NSNotification every time the notification is posted. -- (RACSignal *)rac_addObserverForName:(NSString *)notificationName object:(id)object; - -@end diff --git a/ReactiveObjC/NSNotificationCenter+RACSupport.m b/ReactiveObjC/NSNotificationCenter+RACSupport.m deleted file mode 100644 index 6d6f94384e..0000000000 --- a/ReactiveObjC/NSNotificationCenter+RACSupport.m +++ /dev/null @@ -1,31 +0,0 @@ -// -// NSNotificationCenter+RACSupport.m -// ReactiveObjC -// -// Created by Josh Abernathy on 5/10/12. -// Copyright (c) 2012 GitHub. All rights reserved. -// - -#import "NSNotificationCenter+RACSupport.h" -#import -#import "RACSignal.h" -#import "RACSubscriber.h" -#import "RACDisposable.h" - -@implementation NSNotificationCenter (RACSupport) - -- (RACSignal *)rac_addObserverForName:(NSString *)notificationName object:(id)object { - @unsafeify(object); - return [[RACSignal createSignal:^(id subscriber) { - @strongify(object); - id observer = [self addObserverForName:notificationName object:object queue:nil usingBlock:^(NSNotification *note) { - [subscriber sendNext:note]; - }]; - - return [RACDisposable disposableWithBlock:^{ - [self removeObserver:observer]; - }]; - }] setNameWithFormat:@"-rac_addObserverForName: %@ object: <%@: %p>", notificationName, [object class], object]; -} - -@end diff --git a/ReactiveObjC/NSObject+RACAppKitBindings.h b/ReactiveObjC/NSObject+RACAppKitBindings.h deleted file mode 100644 index 19d45c43c4..0000000000 --- a/ReactiveObjC/NSObject+RACAppKitBindings.h +++ /dev/null @@ -1,40 +0,0 @@ -// -// NSObject+RACAppKitBindings.h -// ReactiveObjC -// -// Created by Josh Abernathy on 4/17/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import - -@class RACChannelTerminal; - -@interface NSObject (RACAppKitBindings) - -/// Invokes -rac_channelToBinding:options: without any options. -- (RACChannelTerminal *)rac_channelToBinding:(NSString *)binding; - -/// Applies a Cocoa binding to the receiver, then exposes a RACChannel-based -/// interface for manipulating it. -/// -/// Creating two of the same bindings on the same object will result in undefined -/// behavior. -/// -/// binding - The name of the binding. This must not be nil. -/// options - Any options to pass to Cocoa Bindings. This may be nil. -/// -/// Returns a RACChannelTerminal which will send future values from the receiver, -/// and update the receiver when values are sent to the terminal. -- (RACChannelTerminal *)rac_channelToBinding:(NSString *)binding options:(NSDictionary *)options; - -@end - -@interface NSObject (RACUnavailableAppKitBindings) - -- (void)rac_bind:(NSString *)binding toObject:(id)object withKeyPath:(NSString *)keyPath __attribute__((unavailable("Use -rac_bind:options: instead"))); -- (void)rac_bind:(NSString *)binding toObject:(id)object withKeyPath:(NSString *)keyPath nilValue:(id)nilValue __attribute__((unavailable("Use -rac_bind:options: instead"))); -- (void)rac_bind:(NSString *)binding toObject:(id)object withKeyPath:(NSString *)keyPath transform:(id (^)(id value))transformBlock __attribute__((unavailable("Use -rac_bind:options: instead"))); -- (void)rac_bind:(NSString *)binding toObject:(id)object withNegatedKeyPath:(NSString *)keyPath __attribute__((unavailable("Use -rac_bind:options: instead"))); - -@end diff --git a/ReactiveObjC/NSObject+RACAppKitBindings.m b/ReactiveObjC/NSObject+RACAppKitBindings.m deleted file mode 100644 index dde3d13101..0000000000 --- a/ReactiveObjC/NSObject+RACAppKitBindings.m +++ /dev/null @@ -1,147 +0,0 @@ -// -// NSObject+RACAppKitBindings.m -// ReactiveObjC -// -// Created by Josh Abernathy on 4/17/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "NSObject+RACAppKitBindings.h" -#import -#import -#import "NSObject+RACDeallocating.h" -#import "RACChannel.h" -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACKVOChannel.h" -#import "RACValueTransformer.h" -#import - -// Used as an object to bind to, so we can hide the object creation and just -// expose a RACChannel instead. -@interface RACChannelProxy : NSObject - -// The RACChannel used for this Cocoa binding. -@property (nonatomic, strong, readonly) RACChannel *channel; - -// The KVC- and KVO-compliant property to be read and written by the Cocoa -// binding. -// -// This should not be set manually. -@property (nonatomic, strong) id value; - -// The target of the Cocoa binding. -// -// This should be set to nil when the target deallocates. -@property (atomic, unsafe_unretained) id target; - -// The name of the Cocoa binding used. -@property (nonatomic, copy, readonly) NSString *bindingName; - -// Improves the performance of KVO on the receiver. -// -// See the documentation for for more information. -@property (atomic, assign) void *observationInfo; - -// Initializes the receiver and binds to the given target. -// -// target - The target of the Cocoa binding. This must not be nil. -// bindingName - The name of the Cocoa binding to use. This must not be nil. -// options - Any options to pass to the binding. This may be nil. -// -// Returns an initialized channel proxy. -- (id)initWithTarget:(id)target bindingName:(NSString *)bindingName options:(NSDictionary *)options; - -@end - -@implementation NSObject (RACAppKitBindings) - -- (RACChannelTerminal *)rac_channelToBinding:(NSString *)binding { - return [self rac_channelToBinding:binding options:nil]; -} - -- (RACChannelTerminal *)rac_channelToBinding:(NSString *)binding options:(NSDictionary *)options { - NSCParameterAssert(binding != nil); - - RACChannelProxy *proxy = [[RACChannelProxy alloc] initWithTarget:self bindingName:binding options:options]; - return proxy.channel.leadingTerminal; -} - -@end - -@implementation RACChannelProxy - -#pragma mark Properties - -- (void)setValue:(id)value { - [self willChangeValueForKey:@keypath(self.value)]; - _value = value; - [self didChangeValueForKey:@keypath(self.value)]; -} - -#pragma mark Lifecycle - -- (id)initWithTarget:(id)target bindingName:(NSString *)bindingName options:(NSDictionary *)options { - NSCParameterAssert(target != nil); - NSCParameterAssert(bindingName != nil); - - self = [super init]; - if (self == nil) return nil; - - _target = target; - _bindingName = [bindingName copy]; - _channel = [[RACChannel alloc] init]; - - @weakify(self); - - void (^cleanUp)() = ^{ - @strongify(self); - - id target = self.target; - if (target == nil) return; - - self.target = nil; - - [target unbind:bindingName]; - objc_setAssociatedObject(target, (__bridge void *)self, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - }; - - // When the channel terminates, tear down this proxy. - [self.channel.followingTerminal subscribeError:^(NSError *error) { - cleanUp(); - } completed:cleanUp]; - - [self.target bind:bindingName toObject:self withKeyPath:@keypath(self.value) options:options]; - - // Keep the proxy alive as long as the target, or until the property subject - // terminates. - objc_setAssociatedObject(self.target, (__bridge void *)self, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - - [[self.target rac_deallocDisposable] addDisposable:[RACDisposable disposableWithBlock:^{ - @strongify(self); - [self.channel.followingTerminal sendCompleted]; - }]]; - - RACChannelTo(self, value, options[NSNullPlaceholderBindingOption]) = self.channel.followingTerminal; - return self; -} - -- (void)dealloc { - [self.channel.followingTerminal sendCompleted]; -} - -#pragma mark NSObject - -- (NSString *)description { - return [NSString stringWithFormat:@"<%@: %p>{ target: %@, binding: %@ }", self.class, self, self.target, self.bindingName]; -} - -#pragma mark NSKeyValueObserving - -+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key { - // Generating manual notifications for `value` is simpler and more - // performant than having KVO swizzle our class and add its own logic. - return NO; -} - -@end diff --git a/ReactiveObjC/NSObject+RACDeallocating.h b/ReactiveObjC/NSObject+RACDeallocating.h deleted file mode 100644 index 5e9a529934..0000000000 --- a/ReactiveObjC/NSObject+RACDeallocating.h +++ /dev/null @@ -1,33 +0,0 @@ -// -// NSObject+RACDeallocating.h -// ReactiveObjC -// -// Created by Kazuo Koga on 2013/03/15. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -@class RACCompoundDisposable; -@class RACDisposable; -@class RACSignal; - -@interface NSObject (RACDeallocating) - -/// The compound disposable which will be disposed of when the receiver is -/// deallocated. -@property (atomic, readonly, strong) RACCompoundDisposable *rac_deallocDisposable; - -/// Returns a signal that will complete immediately before the receiver is fully -/// deallocated. If already deallocated when the signal is subscribed to, -/// a `completed` event will be sent immediately. -- (RACSignal *)rac_willDeallocSignal; - -@end - -@interface NSObject (RACUnavailableDeallocating) - -- (RACSignal *)rac_didDeallocSignal __attribute__((unavailable("Use -rac_willDeallocSignal"))); -- (void)rac_addDeallocDisposable:(RACDisposable *)disposable __attribute__((unavailable("Add disposables to -rac_deallocDisposable instead"))); - -@end diff --git a/ReactiveObjC/NSObject+RACDeallocating.m b/ReactiveObjC/NSObject+RACDeallocating.m deleted file mode 100644 index 49ed176718..0000000000 --- a/ReactiveObjC/NSObject+RACDeallocating.m +++ /dev/null @@ -1,103 +0,0 @@ -// -// NSObject+RACDeallocating.m -// ReactiveObjC -// -// Created by Kazuo Koga on 2013/03/15. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "NSObject+RACDeallocating.h" -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACReplaySubject.h" -#import -#import - -static const void *RACObjectCompoundDisposable = &RACObjectCompoundDisposable; - -static NSMutableSet *swizzledClasses() { - static dispatch_once_t onceToken; - static NSMutableSet *swizzledClasses = nil; - dispatch_once(&onceToken, ^{ - swizzledClasses = [[NSMutableSet alloc] init]; - }); - - return swizzledClasses; -} - -static void swizzleDeallocIfNeeded(Class classToSwizzle) { - @synchronized (swizzledClasses()) { - NSString *className = NSStringFromClass(classToSwizzle); - if ([swizzledClasses() containsObject:className]) return; - - SEL deallocSelector = sel_registerName("dealloc"); - - __block void (*originalDealloc)(__unsafe_unretained id, SEL) = NULL; - - id newDealloc = ^(__unsafe_unretained id self) { - RACCompoundDisposable *compoundDisposable = objc_getAssociatedObject(self, RACObjectCompoundDisposable); - [compoundDisposable dispose]; - - if (originalDealloc == NULL) { - struct objc_super superInfo = { - .receiver = self, - .super_class = class_getSuperclass(classToSwizzle) - }; - - void (*msgSend)(struct objc_super *, SEL) = (__typeof__(msgSend))objc_msgSendSuper; - msgSend(&superInfo, deallocSelector); - } else { - originalDealloc(self, deallocSelector); - } - }; - - IMP newDeallocIMP = imp_implementationWithBlock(newDealloc); - - if (!class_addMethod(classToSwizzle, deallocSelector, newDeallocIMP, "v@:")) { - // The class already contains a method implementation. - Method deallocMethod = class_getInstanceMethod(classToSwizzle, deallocSelector); - - // We need to store original implementation before setting new implementation - // in case method is called at the time of setting. - originalDealloc = (__typeof__(originalDealloc))method_getImplementation(deallocMethod); - - // We need to store original implementation again, in case it just changed. - originalDealloc = (__typeof__(originalDealloc))method_setImplementation(deallocMethod, newDeallocIMP); - } - - [swizzledClasses() addObject:className]; - } -} - -@implementation NSObject (RACDeallocating) - -- (RACSignal *)rac_willDeallocSignal { - RACSignal *signal = objc_getAssociatedObject(self, _cmd); - if (signal != nil) return signal; - - RACReplaySubject *subject = [RACReplaySubject subject]; - - [self.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - [subject sendCompleted]; - }]]; - - objc_setAssociatedObject(self, _cmd, subject, OBJC_ASSOCIATION_RETAIN); - - return subject; -} - -- (RACCompoundDisposable *)rac_deallocDisposable { - @synchronized (self) { - RACCompoundDisposable *compoundDisposable = objc_getAssociatedObject(self, RACObjectCompoundDisposable); - if (compoundDisposable != nil) return compoundDisposable; - - swizzleDeallocIfNeeded(self.class); - - compoundDisposable = [RACCompoundDisposable compoundDisposable]; - objc_setAssociatedObject(self, RACObjectCompoundDisposable, compoundDisposable, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - - return compoundDisposable; - } -} - -@end diff --git a/ReactiveObjC/NSObject+RACDescription.h b/ReactiveObjC/NSObject+RACDescription.h deleted file mode 100644 index 066e102759..0000000000 --- a/ReactiveObjC/NSObject+RACDescription.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// NSObject+RACDescription.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-05-13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -// A simplified description of the object, which does not invoke -description -// (and thus should be much faster in many cases). -// -// This is for debugging purposes only, and will return a constant string -// unless the RAC_DEBUG_SIGNAL_NAMES environment variable is set. -NSString *RACDescription(id object); diff --git a/ReactiveObjC/NSObject+RACDescription.m b/ReactiveObjC/NSObject+RACDescription.m deleted file mode 100644 index 1b214277eb..0000000000 --- a/ReactiveObjC/NSObject+RACDescription.m +++ /dev/null @@ -1,50 +0,0 @@ -// -// NSObject+RACDescription.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-05-13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "NSObject+RACDescription.h" -#import "RACTuple.h" - -@implementation NSValue (RACDescription) - -- (NSString *)rac_description { - return self.description; -} - -@end - -@implementation NSString (RACDescription) - -- (NSString *)rac_description { - return self.description; -} - -@end - -@implementation RACTuple (RACDescription) - -- (NSString *)rac_description { - if (getenv("RAC_DEBUG_SIGNAL_NAMES") != NULL) { - return self.allObjects.description; - } else { - return @"(description skipped)"; - } -} - -@end - -NSString *RACDescription(id object) { - if (getenv("RAC_DEBUG_SIGNAL_NAMES") != NULL) { - if ([object respondsToSelector:@selector(rac_description)]) { - return [object rac_description]; - } else { - return [[NSString alloc] initWithFormat:@"<%@: %p>", [object class], object]; - } - } else { - return @"(description skipped)"; - } -} diff --git a/ReactiveObjC/NSObject+RACKVOWrapper.h b/ReactiveObjC/NSObject+RACKVOWrapper.h deleted file mode 100644 index 48e6a0403f..0000000000 --- a/ReactiveObjC/NSObject+RACKVOWrapper.h +++ /dev/null @@ -1,46 +0,0 @@ -// -// NSObject+RACKVOWrapper.h -// ReactiveObjC -// -// Created by Josh Abernathy on 10/11/11. -// Copyright (c) 2011 GitHub. All rights reserved. -// - -#import - -@class RACDisposable; -@class RACKVOTrampoline; - -// A private category providing a block based interface to KVO. -@interface NSObject (RACKVOWrapper) - -// Adds the given block as the callbacks for when the key path changes. -// -// Unlike direct KVO observation, this handles deallocation of `weak` properties -// by generating an appropriate notification. This will only occur if there is -// an `@property` declaration visible in the observed class, with the `weak` -// memory management attribute. -// -// The observation does not need to be explicitly removed. It will be removed -// when the observer or the receiver deallocate. -// -// keyPath - The key path to observe. Must not be nil. -// options - The KVO observation options. -// observer - The object that requested the observation. May be nil. -// block - The block called when the value at the key path changes. It is -// passed the current value of the key path and the extended KVO -// change dictionary including RAC-specific keys and values. Must not -// be nil. -// -// Returns a disposable that can be used to stop the observation. -- (RACDisposable *)rac_observeKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options observer:(__weak NSObject *)observer block:(void (^)(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent))block; - -@end - -typedef void (^RACKVOBlock)(id target, id observer, NSDictionary *change); - -@interface NSObject (RACUnavailableKVOWrapper) - -- (RACKVOTrampoline *)rac_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(RACKVOBlock)block __attribute((unavailable("Use rac_observeKeyPath:options:observer:block: instead."))); - -@end diff --git a/ReactiveObjC/NSObject+RACKVOWrapper.m b/ReactiveObjC/NSObject+RACKVOWrapper.m deleted file mode 100644 index 32e99e2669..0000000000 --- a/ReactiveObjC/NSObject+RACKVOWrapper.m +++ /dev/null @@ -1,200 +0,0 @@ -// -// NSObject+RACKVOWrapper.m -// ReactiveObjC -// -// Created by Josh Abernathy on 10/11/11. -// Copyright (c) 2011 GitHub. All rights reserved. -// - -#import "NSObject+RACKVOWrapper.h" -#import -#import -#import "NSObject+RACDeallocating.h" -#import "NSString+RACKeyPathUtilities.h" -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACKVOTrampoline.h" -#import "RACSerialDisposable.h" - -@implementation NSObject (RACKVOWrapper) - -- (RACDisposable *)rac_observeKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options observer:(__weak NSObject *)weakObserver block:(void (^)(id, NSDictionary *, BOOL, BOOL))block { - NSCParameterAssert(block != nil); - NSCParameterAssert(keyPath.rac_keyPathComponents.count > 0); - - keyPath = [keyPath copy]; - - NSObject *strongObserver = weakObserver; - - NSArray *keyPathComponents = keyPath.rac_keyPathComponents; - BOOL keyPathHasOneComponent = (keyPathComponents.count == 1); - NSString *keyPathHead = keyPathComponents[0]; - NSString *keyPathTail = keyPath.rac_keyPathByDeletingFirstKeyPathComponent; - - RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable]; - - // The disposable that groups all disposal necessary to clean up the callbacks - // added to the value of the first key path component. - RACSerialDisposable *firstComponentSerialDisposable = [RACSerialDisposable serialDisposableWithDisposable:[RACCompoundDisposable compoundDisposable]]; - RACCompoundDisposable * (^firstComponentDisposable)(void) = ^{ - return (RACCompoundDisposable *)firstComponentSerialDisposable.disposable; - }; - - [disposable addDisposable:firstComponentSerialDisposable]; - - BOOL shouldAddDeallocObserver = NO; - - objc_property_t property = class_getProperty(object_getClass(self), keyPathHead.UTF8String); - if (property != NULL) { - rac_propertyAttributes *attributes = rac_copyPropertyAttributes(property); - if (attributes != NULL) { - @onExit { - free(attributes); - }; - - BOOL isObject = attributes->objectClass != nil || strstr(attributes->type, @encode(id)) == attributes->type; - BOOL isProtocol = attributes->objectClass == NSClassFromString(@"Protocol"); - BOOL isBlock = strcmp(attributes->type, @encode(void(^)())) == 0; - BOOL isWeak = attributes->weak; - - // If this property isn't actually an object (or is a Class object), - // no point in observing the deallocation of the wrapper returned by - // KVC. - // - // If this property is an object, but not declared `weak`, we - // don't need to watch for it spontaneously being set to nil. - // - // Attempting to observe non-weak properties will result in - // broken behavior for dynamic getters, so don't even try. - shouldAddDeallocObserver = isObject && isWeak && !isBlock && !isProtocol; - } - } - - // Adds the callback block to the value's deallocation. Also adds the logic to - // clean up the callback to the firstComponentDisposable. - void (^addDeallocObserverToPropertyValue)(NSObject *) = ^(NSObject *value) { - if (!shouldAddDeallocObserver) return; - - // If a key path value is the observer, commonly when a key path begins - // with "self", we prevent deallocation triggered callbacks for any such key - // path components. Thus, the observer's deallocation is not considered a - // change to the key path. - if (value == weakObserver) return; - - NSDictionary *change = @{ - NSKeyValueChangeKindKey: @(NSKeyValueChangeSetting), - NSKeyValueChangeNewKey: NSNull.null, - }; - - RACCompoundDisposable *valueDisposable = value.rac_deallocDisposable; - RACDisposable *deallocDisposable = [RACDisposable disposableWithBlock:^{ - block(nil, change, YES, keyPathHasOneComponent); - }]; - - [valueDisposable addDisposable:deallocDisposable]; - [firstComponentDisposable() addDisposable:[RACDisposable disposableWithBlock:^{ - [valueDisposable removeDisposable:deallocDisposable]; - }]]; - }; - - // Adds the callback block to the remaining path components on the value. Also - // adds the logic to clean up the callbacks to the firstComponentDisposable. - void (^addObserverToValue)(NSObject *) = ^(NSObject *value) { - RACDisposable *observerDisposable = [value rac_observeKeyPath:keyPathTail options:(options & ~NSKeyValueObservingOptionInitial) observer:weakObserver block:block]; - [firstComponentDisposable() addDisposable:observerDisposable]; - }; - - // Observe only the first key path component, when the value changes clean up - // the callbacks on the old value, add callbacks to the new value and call the - // callback block as needed. - // - // Note this does not use NSKeyValueObservingOptionInitial so this only - // handles changes to the value, callbacks to the initial value must be added - // separately. - NSKeyValueObservingOptions trampolineOptions = (options | NSKeyValueObservingOptionPrior) & ~NSKeyValueObservingOptionInitial; - RACKVOTrampoline *trampoline = [[RACKVOTrampoline alloc] initWithTarget:self observer:strongObserver keyPath:keyPathHead options:trampolineOptions block:^(id trampolineTarget, id trampolineObserver, NSDictionary *change) { - // If this is a prior notification, clean up all the callbacks added to the - // previous value and call the callback block. Everything else is deferred - // until after we get the notification after the change. - if ([change[NSKeyValueChangeNotificationIsPriorKey] boolValue]) { - [firstComponentDisposable() dispose]; - - if ((options & NSKeyValueObservingOptionPrior) != 0) { - block([trampolineTarget valueForKeyPath:keyPath], change, NO, keyPathHasOneComponent); - } - - return; - } - - // From here the notification is not prior. - NSObject *value = [trampolineTarget valueForKey:keyPathHead]; - - // If the value has changed but is nil, there is no need to add callbacks to - // it, just call the callback block. - if (value == nil) { - block(nil, change, NO, keyPathHasOneComponent); - return; - } - - // From here the notification is not prior and the value is not nil. - - // Create a new firstComponentDisposable while getting rid of the old one at - // the same time, in case this is being called concurrently. - RACDisposable *oldFirstComponentDisposable = [firstComponentSerialDisposable swapInDisposable:[RACCompoundDisposable compoundDisposable]]; - [oldFirstComponentDisposable dispose]; - - addDeallocObserverToPropertyValue(value); - - // If there are no further key path components, there is no need to add the - // other callbacks, just call the callback block with the value itself. - if (keyPathHasOneComponent) { - block(value, change, NO, keyPathHasOneComponent); - return; - } - - // The value has changed, is not nil, and there are more key path components - // to consider. Add the callbacks to the value for the remaining key path - // components and call the callback block with the current value of the full - // key path. - addObserverToValue(value); - block([value valueForKeyPath:keyPathTail], change, NO, keyPathHasOneComponent); - }]; - - // Stop the KVO observation when this one is disposed of. - [disposable addDisposable:trampoline]; - - // Add the callbacks to the initial value if needed. - NSObject *value = [self valueForKey:keyPathHead]; - if (value != nil) { - addDeallocObserverToPropertyValue(value); - - if (!keyPathHasOneComponent) { - addObserverToValue(value); - } - } - - // Call the block with the initial value if needed. - if ((options & NSKeyValueObservingOptionInitial) != 0) { - id initialValue = [self valueForKeyPath:keyPath]; - NSDictionary *initialChange = @{ - NSKeyValueChangeKindKey: @(NSKeyValueChangeSetting), - NSKeyValueChangeNewKey: initialValue ?: NSNull.null, - }; - block(initialValue, initialChange, NO, keyPathHasOneComponent); - } - - - RACCompoundDisposable *observerDisposable = strongObserver.rac_deallocDisposable; - RACCompoundDisposable *selfDisposable = self.rac_deallocDisposable; - // Dispose of this observation if the receiver or the observer deallocate. - [observerDisposable addDisposable:disposable]; - [selfDisposable addDisposable:disposable]; - - return [RACDisposable disposableWithBlock:^{ - [disposable dispose]; - [observerDisposable removeDisposable:disposable]; - [selfDisposable removeDisposable:disposable]; - }]; -} - -@end diff --git a/ReactiveObjC/NSObject+RACLifting.h b/ReactiveObjC/NSObject+RACLifting.h deleted file mode 100644 index 8f93563793..0000000000 --- a/ReactiveObjC/NSObject+RACLifting.h +++ /dev/null @@ -1,57 +0,0 @@ -// -// NSObject+RACLifting.h -// ReactiveObjC -// -// Created by Josh Abernathy on 10/13/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import - -@class RACSignal; - -@interface NSObject (RACLifting) - -/// Lifts the selector on the receiver into the reactive world. The selector will -/// be invoked whenever any signal argument sends a value, but only after each -/// signal has sent an initial value. -/// -/// It will replay the most recently sent value to new subscribers. -/// -/// This does not support C arrays or unions. -/// -/// selector - The selector on self to invoke. -/// firstSignal - The signal corresponding to the first method argument. This -/// must not be nil. -/// ... - A list of RACSignals corresponding to the remaining arguments. -/// There must be a non-nil signal for each method argument. -/// -/// Examples -/// -/// [button rac_liftSelector:@selector(setTitleColor:forState:) withSignals:textColorSignal, [RACSignal return:@(UIControlStateNormal)], nil]; -/// -/// Returns a signal which sends the return value from each invocation of the -/// selector. If the selector returns void, it instead sends RACUnit.defaultUnit. -/// It completes only after all the signal arguments complete. -- (RACSignal *)rac_liftSelector:(SEL)selector withSignals:(RACSignal *)firstSignal, ... NS_REQUIRES_NIL_TERMINATION; - -/// Like -rac_liftSelector:withSignals:, but accepts an array instead of -/// a variadic list of arguments. -- (RACSignal *)rac_liftSelector:(SEL)selector withSignalsFromArray:(NSArray *)signals; - -/// Like -rac_liftSelector:withSignals:, but accepts a signal sending tuples of -/// arguments instead of a variadic list of arguments. -- (RACSignal *)rac_liftSelector:(SEL)selector withSignalOfArguments:(RACSignal *)arguments; - -@end - -@interface NSObject (RACUnavailableLifting) - -- (RACSignal *)rac_liftSelector:(SEL)selector withObjects:(id)arg, ... __attribute__((unavailable("Use -rac_liftSelector:withSignals: instead"))); -- (RACSignal *)rac_liftSelector:(SEL)selector withObjectsFromArray:(NSArray *)args __attribute__((unavailable("Use -rac_liftSelector:withSignalsFromArray: instead"))); -- (RACSignal *)rac_liftBlock:(id)block withArguments:(id)arg, ... NS_REQUIRES_NIL_TERMINATION __attribute__((unavailable("Use +combineLatest:reduce: instead"))); -- (RACSignal *)rac_liftBlock:(id)block withArgumentsFromArray:(NSArray *)args __attribute__((unavailable("Use +combineLatest:reduce: instead"))); - -- (instancetype)rac_lift __attribute__((unavailable("Use -rac_liftSelector:withSignals: instead"))); - -@end diff --git a/ReactiveObjC/NSObject+RACLifting.m b/ReactiveObjC/NSObject+RACLifting.m deleted file mode 100644 index a0c09d5c08..0000000000 --- a/ReactiveObjC/NSObject+RACLifting.m +++ /dev/null @@ -1,78 +0,0 @@ -// -// NSObject+RACLifting.m -// ReactiveObjC -// -// Created by Josh Abernathy on 10/13/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "NSObject+RACLifting.h" -#import -#import "NSInvocation+RACTypeParsing.h" -#import "NSObject+RACDeallocating.h" -#import "NSObject+RACDescription.h" -#import "RACSignal+Operations.h" -#import "RACTuple.h" - -@implementation NSObject (RACLifting) - -- (RACSignal *)rac_liftSelector:(SEL)selector withSignalOfArguments:(RACSignal *)arguments { - NSCParameterAssert(selector != NULL); - NSCParameterAssert(arguments != nil); - - @unsafeify(self); - - NSMethodSignature *methodSignature = [self methodSignatureForSelector:selector]; - NSCAssert(methodSignature != nil, @"%@ does not respond to %@", self, NSStringFromSelector(selector)); - - return [[[[arguments - takeUntil:self.rac_willDeallocSignal] - map:^(RACTuple *arguments) { - @strongify(self); - - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; - invocation.selector = selector; - invocation.rac_argumentsTuple = arguments; - [invocation invokeWithTarget:self]; - - return invocation.rac_returnValue; - }] - replayLast] - setNameWithFormat:@"%@ -rac_liftSelector: %s withSignalsOfArguments: %@", RACDescription(self), sel_getName(selector), arguments]; -} - -- (RACSignal *)rac_liftSelector:(SEL)selector withSignalsFromArray:(NSArray *)signals { - NSCParameterAssert(signals != nil); - NSCParameterAssert(signals.count > 0); - - NSMethodSignature *methodSignature = [self methodSignatureForSelector:selector]; - NSCAssert(methodSignature != nil, @"%@ does not respond to %@", self, NSStringFromSelector(selector)); - - NSUInteger numberOfArguments __attribute__((unused)) = methodSignature.numberOfArguments - 2; - NSCAssert(numberOfArguments == signals.count, @"Wrong number of signals for %@ (expected %lu, got %lu)", NSStringFromSelector(selector), (unsigned long)numberOfArguments, (unsigned long)signals.count); - - return [[self - rac_liftSelector:selector withSignalOfArguments:[RACSignal combineLatest:signals]] - setNameWithFormat:@"%@ -rac_liftSelector: %s withSignalsFromArray: %@", RACDescription(self), sel_getName(selector), signals]; -} - -- (RACSignal *)rac_liftSelector:(SEL)selector withSignals:(RACSignal *)firstSignal, ... { - NSCParameterAssert(firstSignal != nil); - - NSMutableArray *signals = [NSMutableArray array]; - - va_list args; - va_start(args, firstSignal); - for (id currentSignal = firstSignal; currentSignal != nil; currentSignal = va_arg(args, id)) { - NSCAssert([currentSignal isKindOfClass:RACSignal.class], @"Argument %@ is not a RACSignal", currentSignal); - - [signals addObject:currentSignal]; - } - va_end(args); - - return [[self - rac_liftSelector:selector withSignalsFromArray:signals] - setNameWithFormat:@"%@ -rac_liftSelector: %s withSignals: %@", RACDescription(self), sel_getName(selector), signals]; -} - -@end diff --git a/ReactiveObjC/NSObject+RACPropertySubscribing.h b/ReactiveObjC/NSObject+RACPropertySubscribing.h deleted file mode 100644 index e2e6acd2f7..0000000000 --- a/ReactiveObjC/NSObject+RACPropertySubscribing.h +++ /dev/null @@ -1,126 +0,0 @@ -// -// NSObject+RACPropertySubscribing.h -// ReactiveObjC -// -// Created by Josh Abernathy on 3/2/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import -#import -#import "metamacros.h" - -/// Creates a signal which observes `KEYPATH` on `TARGET` for changes. -/// -/// In either case, the observation continues until `TARGET` _or self_ is -/// deallocated. If any intermediate object is deallocated instead, it will be -/// assumed to have been set to nil. -/// -/// Make sure to `@strongify(self)` when using this macro within a block! The -/// macro will _always_ reference `self`, which can silently introduce a retain -/// cycle within a block. As a result, you should make sure that `self` is a weak -/// reference (e.g., created by `@weakify` and `@strongify`) before the -/// expression that uses `RACObserve`. -/// -/// Examples -/// -/// // Observes self, and doesn't stop until self is deallocated. -/// RACSignal *selfSignal = RACObserve(self, arrayController.items); -/// -/// // Observes the array controller, and stops when self _or_ the array -/// // controller is deallocated. -/// RACSignal *arrayControllerSignal = RACObserve(self.arrayController, items); -/// -/// // Observes obj.arrayController, and stops when self _or_ the array -/// // controller is deallocated. -/// RACSignal *signal2 = RACObserve(obj.arrayController, items); -/// -/// @weakify(self); -/// RACSignal *signal3 = [anotherSignal flattenMap:^(NSArrayController *arrayController) { -/// // Avoids a retain cycle because of RACObserve implicitly referencing -/// // self. -/// @strongify(self); -/// return RACObserve(arrayController, items); -/// }]; -/// -/// Returns a signal which sends the current value of the key path on -/// subscription, then sends the new value every time it changes, and sends -/// completed if self or observer is deallocated. -#define _RACObserve(TARGET, KEYPATH) \ -({ \ - __weak id target_ = (TARGET); \ - [target_ rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self]; \ -}) - -#if __clang__ && (__clang_major__ >= 8) -#define RACObserve(TARGET, KEYPATH) _RACObserve(TARGET, KEYPATH) -#else -#define RACObserve(TARGET, KEYPATH) \ -({ \ - _Pragma("clang diagnostic push") \ - _Pragma("clang diagnostic ignored \"-Wreceiver-is-weak\"") \ - _RACObserve(TARGET, KEYPATH) \ - _Pragma("clang diagnostic pop") \ -}) -#endif - -@class RACDisposable; -@class RACSignal; - -@interface NSObject (RACPropertySubscribing) - -/// Creates a signal to observe the value at the given key path. -/// -/// The initial value is sent on subscription, the subsequent values are sent -/// from whichever thread the change occured on, even if it doesn't have a valid -/// scheduler. -/// -/// Returns a signal that immediately sends the receiver's current value at the -/// given keypath, then any changes thereafter. -#if OS_OBJECT_HAVE_OBJC_SUPPORT -- (RACSignal *)rac_valuesForKeyPath:(NSString *)keyPath observer:(__weak NSObject *)observer; -#else -// Swift builds with OS_OBJECT_HAVE_OBJC_SUPPORT=0 for Playgrounds and LLDB :( -- (RACSignal *)rac_valuesForKeyPath:(NSString *)keyPath observer:(NSObject *)observer; -#endif - -/// Creates a signal to observe the changes of the given key path. -/// -/// The initial value is sent on subscription if `NSKeyValueObservingOptionInitial` is set. -/// The subsequent values are sent from whichever thread the change occured on, -/// even if it doesn't have a valid scheduler. -/// -/// Returns a signal that sends tuples containing the current value at the key -/// path and the change dictionary for each KVO callback. -#if OS_OBJECT_HAVE_OBJC_SUPPORT -- (RACSignal *)rac_valuesAndChangesForKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options observer:(__weak NSObject *)observer; -#else -- (RACSignal *)rac_valuesAndChangesForKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options observer:(NSObject *)observer; -#endif - -@end - -#define RACAble(...) \ - metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__)) \ - (_RACAbleObject(self, __VA_ARGS__)) \ - (_RACAbleObject(__VA_ARGS__)) - -#define _RACAbleObject(object, property) [object rac_signalForKeyPath:@keypath(object, property) observer:self] - -#define RACAbleWithStart(...) \ - metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__)) \ - (_RACAbleWithStartObject(self, __VA_ARGS__)) \ - (_RACAbleWithStartObject(__VA_ARGS__)) - -#define _RACAbleWithStartObject(object, property) [object rac_signalWithStartingValueForKeyPath:@keypath(object, property) observer:self] - -@interface NSObject (RACUnavailablePropertySubscribing) - -+ (RACSignal *)rac_signalFor:(NSObject *)object keyPath:(NSString *)keyPath observer:(NSObject *)observer __attribute__((unavailable("Use -rac_valuesForKeyPath:observer: or RACObserve() instead."))); -+ (RACSignal *)rac_signalWithStartingValueFor:(NSObject *)object keyPath:(NSString *)keyPath observer:(NSObject *)observer __attribute__((unavailable("Use -rac_valuesForKeyPath:observer: or RACObserve() instead."))); -+ (RACSignal *)rac_signalWithChangesFor:(NSObject *)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options observer:(NSObject *)observer __attribute__((unavailable("Use -rac_valuesAndChangesForKeyPath:options:observer: instead."))); -- (RACSignal *)rac_signalForKeyPath:(NSString *)keyPath observer:(NSObject *)observer __attribute__((unavailable("Use -rac_valuesForKeyPath:observer: or RACObserve() instead."))); -- (RACSignal *)rac_signalWithStartingValueForKeyPath:(NSString *)keyPath observer:(NSObject *)observer __attribute__((unavailable("Use -rac_valuesForKeyPath:observer: or RACObserve() instead."))); -- (RACDisposable *)rac_deriveProperty:(NSString *)keyPath from:(RACSignal *)signal __attribute__((unavailable("Use -[RACSignal setKeyPath:onObject:] instead"))); - -@end diff --git a/ReactiveObjC/NSObject+RACPropertySubscribing.m b/ReactiveObjC/NSObject+RACPropertySubscribing.m deleted file mode 100644 index 29e3df89e4..0000000000 --- a/ReactiveObjC/NSObject+RACPropertySubscribing.m +++ /dev/null @@ -1,84 +0,0 @@ -// -// NSObject+RACPropertySubscribing.m -// ReactiveObjC -// -// Created by Josh Abernathy on 3/2/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "NSObject+RACPropertySubscribing.h" -#import -#import "NSObject+RACDeallocating.h" -#import "NSObject+RACDescription.h" -#import "NSObject+RACKVOWrapper.h" -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACKVOTrampoline.h" -#import "RACSubscriber.h" -#import "RACSignal+Operations.h" -#import "RACTuple.h" -#import - -@implementation NSObject (RACPropertySubscribing) - -- (RACSignal *)rac_valuesForKeyPath:(NSString *)keyPath observer:(__weak NSObject *)observer { - return [[[self - rac_valuesAndChangesForKeyPath:keyPath options:NSKeyValueObservingOptionInitial observer:observer] - map:^(RACTuple *value) { - // -map: because it doesn't require the block trampoline that -reduceEach: uses - return value[0]; - }] - setNameWithFormat:@"RACObserve(%@, %@)", RACDescription(self), keyPath]; -} - -- (RACSignal *)rac_valuesAndChangesForKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options observer:(__weak NSObject *)weakObserver { - NSObject *strongObserver = weakObserver; - keyPath = [keyPath copy]; - - NSRecursiveLock *objectLock = [[NSRecursiveLock alloc] init]; - objectLock.name = @"org.reactivecocoa.ReactiveObjC.NSObjectRACPropertySubscribing"; - - __weak NSObject *weakSelf = self; - - RACSignal *deallocSignal = [[RACSignal - zip:@[ - self.rac_willDeallocSignal, - strongObserver.rac_willDeallocSignal ?: [RACSignal never] - ]] - doCompleted:^{ - // Forces deallocation to wait if the object variables are currently - // being read on another thread. - [objectLock lock]; - @onExit { - [objectLock unlock]; - }; - }]; - - return [[[RACSignal - createSignal:^ RACDisposable * (id subscriber) { - // Hold onto the lock the whole time we're setting up the KVO - // observation, because any resurrection that might be caused by our - // retaining below must be balanced out by the time -dealloc returns - // (if another thread is waiting on the lock above). - [objectLock lock]; - @onExit { - [objectLock unlock]; - }; - - __strong NSObject *observer __attribute__((objc_precise_lifetime)) = weakObserver; - __strong NSObject *self __attribute__((objc_precise_lifetime)) = weakSelf; - - if (self == nil) { - [subscriber sendCompleted]; - return nil; - } - - return [self rac_observeKeyPath:keyPath options:options observer:observer block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) { - [subscriber sendNext:RACTuplePack(value, change)]; - }]; - }] - takeUntil:deallocSignal] - setNameWithFormat:@"%@ -rac_valueAndChangesForKeyPath: %@ options: %lu observer: %@", RACDescription(self), keyPath, (unsigned long)options, RACDescription(strongObserver)]; -} - -@end diff --git a/ReactiveObjC/NSObject+RACSelectorSignal.h b/ReactiveObjC/NSObject+RACSelectorSignal.h deleted file mode 100644 index 0353e105ec..0000000000 --- a/ReactiveObjC/NSObject+RACSelectorSignal.h +++ /dev/null @@ -1,79 +0,0 @@ -// -// NSObject+RACSelectorSignal.h -// ReactiveObjC -// -// Created by Josh Abernathy on 3/18/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -@class RACSignal; - -/// The domain for any errors originating from -rac_signalForSelector:. -extern NSString * const RACSelectorSignalErrorDomain; - -/// -rac_signalForSelector: was going to add a new method implementation for -/// `selector`, but another thread added an implementation before it was able to. -/// -/// This will _not_ occur for cases where a method implementation exists before -/// -rac_signalForSelector: is invoked. -extern const NSInteger RACSelectorSignalErrorMethodSwizzlingRace; - -@interface NSObject (RACSelectorSignal) - -/// Creates a signal associated with the receiver, which will send a tuple of the -/// method's arguments each time the given selector is invoked. -/// -/// If the selector is already implemented on the receiver, the existing -/// implementation will be invoked _before_ the signal fires. -/// -/// If the selector is not yet implemented on the receiver, the injected -/// implementation will have a `void` return type and accept only object -/// arguments. Invoking the added implementation with non-object values, or -/// expecting a return value, will result in undefined behavior. -/// -/// This is useful for changing an event or delegate callback into a signal. For -/// example, on an NSView: -/// -/// [[view rac_signalForSelector:@selector(mouseDown:)] subscribeNext:^(RACTuple *args) { -/// NSEvent *event = args.first; -/// NSLog(@"mouse button pressed: %@", event); -/// }]; -/// -/// selector - The selector for whose invocations are to be observed. If it -/// doesn't exist, it will be implemented to accept object arguments -/// and return void. This cannot have C arrays or unions as arguments -/// or C arrays, unions, structs, complex or vector types as return -/// type. -/// -/// Returns a signal which will send a tuple of arguments upon each invocation of -/// the selector, then completes when the receiver is deallocated. `next` events -/// will be sent synchronously from the thread that invoked the method. If -/// a runtime call fails, the signal will send an error in the -/// RACSelectorSignalErrorDomain. -- (RACSignal *)rac_signalForSelector:(SEL)selector; - -/// Behaves like -rac_signalForSelector:, but if the selector is not yet -/// implemented on the receiver, its method signature is looked up within -/// `protocol`, and may accept non-object arguments. -/// -/// If the selector is not yet implemented and has a return value, the injected -/// method will return all zero bits (equal to `nil`, `NULL`, 0, 0.0f, etc.). -/// -/// selector - The selector for whose invocations are to be observed. If it -/// doesn't exist, it will be implemented using information from -/// `protocol`, and may accept non-object arguments and return -/// a value. This cannot have C arrays or unions as arguments or -/// return type. -/// protocol - The protocol in which `selector` is declared. This will be used -/// for type information if the selector is not already implemented on -/// the receiver. This must not be `NULL`, and `selector` must exist -/// in this protocol. -/// -/// Returns a signal which will send a tuple of arguments on each invocation of -/// the selector, or an error in RACSelectorSignalErrorDomain if a runtime -/// call fails. -- (RACSignal *)rac_signalForSelector:(SEL)selector fromProtocol:(Protocol *)protocol; - -@end diff --git a/ReactiveObjC/NSObject+RACSelectorSignal.m b/ReactiveObjC/NSObject+RACSelectorSignal.m deleted file mode 100644 index 63e2320035..0000000000 --- a/ReactiveObjC/NSObject+RACSelectorSignal.m +++ /dev/null @@ -1,329 +0,0 @@ -// -// NSObject+RACSelectorSignal.m -// ReactiveObjC -// -// Created by Josh Abernathy on 3/18/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "NSObject+RACSelectorSignal.h" -#import -#import "NSInvocation+RACTypeParsing.h" -#import "NSObject+RACDeallocating.h" -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACSubject.h" -#import "RACTuple.h" -#import "NSObject+RACDescription.h" -#import -#import - -NSString * const RACSelectorSignalErrorDomain = @"RACSelectorSignalErrorDomain"; -const NSInteger RACSelectorSignalErrorMethodSwizzlingRace = 1; - -static NSString * const RACSignalForSelectorAliasPrefix = @"rac_alias_"; -static NSString * const RACSubclassSuffix = @"_RACSelectorSignal"; -static void *RACSubclassAssociationKey = &RACSubclassAssociationKey; - -static NSMutableSet *swizzledClasses() { - static NSMutableSet *set; - static dispatch_once_t pred; - - dispatch_once(&pred, ^{ - set = [[NSMutableSet alloc] init]; - }); - - return set; -} - -@implementation NSObject (RACSelectorSignal) - -static BOOL RACForwardInvocation(id self, NSInvocation *invocation) { - SEL aliasSelector = RACAliasForSelector(invocation.selector); - RACSubject *subject = objc_getAssociatedObject(self, aliasSelector); - - Class class = object_getClass(invocation.target); - BOOL respondsToAlias = [class instancesRespondToSelector:aliasSelector]; - if (respondsToAlias) { - invocation.selector = aliasSelector; - [invocation invoke]; - } - - if (subject == nil) return respondsToAlias; - - [subject sendNext:invocation.rac_argumentsTuple]; - return YES; -} - -static void RACSwizzleForwardInvocation(Class class) { - SEL forwardInvocationSEL = @selector(forwardInvocation:); - Method forwardInvocationMethod = class_getInstanceMethod(class, forwardInvocationSEL); - - // Preserve any existing implementation of -forwardInvocation:. - void (*originalForwardInvocation)(id, SEL, NSInvocation *) = NULL; - if (forwardInvocationMethod != NULL) { - originalForwardInvocation = (__typeof__(originalForwardInvocation))method_getImplementation(forwardInvocationMethod); - } - - // Set up a new version of -forwardInvocation:. - // - // If the selector has been passed to -rac_signalForSelector:, invoke - // the aliased method, and forward the arguments to any attached signals. - // - // If the selector has not been passed to -rac_signalForSelector:, - // invoke any existing implementation of -forwardInvocation:. If there - // was no existing implementation, throw an unrecognized selector - // exception. - id newForwardInvocation = ^(id self, NSInvocation *invocation) { - BOOL matched = RACForwardInvocation(self, invocation); - if (matched) return; - - if (originalForwardInvocation == NULL) { - [self doesNotRecognizeSelector:invocation.selector]; - } else { - originalForwardInvocation(self, forwardInvocationSEL, invocation); - } - }; - - class_replaceMethod(class, forwardInvocationSEL, imp_implementationWithBlock(newForwardInvocation), "v@:@"); -} - -static void RACSwizzleRespondsToSelector(Class class) { - SEL respondsToSelectorSEL = @selector(respondsToSelector:); - - // Preserve existing implementation of -respondsToSelector:. - Method respondsToSelectorMethod = class_getInstanceMethod(class, respondsToSelectorSEL); - BOOL (*originalRespondsToSelector)(id, SEL, SEL) = (__typeof__(originalRespondsToSelector))method_getImplementation(respondsToSelectorMethod); - - // Set up a new version of -respondsToSelector: that returns YES for methods - // added by -rac_signalForSelector:. - // - // If the selector has a method defined on the receiver's actual class, and - // if that method's implementation is _objc_msgForward, then returns whether - // the instance has a signal for the selector. - // Otherwise, call the original -respondsToSelector:. - id newRespondsToSelector = ^ BOOL (id self, SEL selector) { - Method method = rac_getImmediateInstanceMethod(class, selector); - - if (method != NULL && method_getImplementation(method) == _objc_msgForward) { - SEL aliasSelector = RACAliasForSelector(selector); - if (objc_getAssociatedObject(self, aliasSelector) != nil) return YES; - } - - return originalRespondsToSelector(self, respondsToSelectorSEL, selector); - }; - - class_replaceMethod(class, respondsToSelectorSEL, imp_implementationWithBlock(newRespondsToSelector), method_getTypeEncoding(respondsToSelectorMethod)); -} - -static void RACSwizzleGetClass(Class class, Class statedClass) { - SEL selector = @selector(class); - Method method = class_getInstanceMethod(class, selector); - IMP newIMP = imp_implementationWithBlock(^(id self) { - return statedClass; - }); - class_replaceMethod(class, selector, newIMP, method_getTypeEncoding(method)); -} - -static void RACSwizzleMethodSignatureForSelector(Class class) { - IMP newIMP = imp_implementationWithBlock(^(id self, SEL selector) { - // Don't send the -class message to the receiver because we've changed - // that to return the original class. - Class actualClass = object_getClass(self); - Method method = class_getInstanceMethod(actualClass, selector); - if (method == NULL) { - // Messages that the original class dynamically implements fall - // here. - // - // Call the original class' -methodSignatureForSelector:. - struct objc_super target = { - .super_class = class_getSuperclass(class), - .receiver = self, - }; - NSMethodSignature * (*messageSend)(struct objc_super *, SEL, SEL) = (__typeof__(messageSend))objc_msgSendSuper; - return messageSend(&target, @selector(methodSignatureForSelector:), selector); - } - - char const *encoding = method_getTypeEncoding(method); - return [NSMethodSignature signatureWithObjCTypes:encoding]; - }); - - SEL selector = @selector(methodSignatureForSelector:); - Method methodSignatureForSelectorMethod = class_getInstanceMethod(class, selector); - class_replaceMethod(class, selector, newIMP, method_getTypeEncoding(methodSignatureForSelectorMethod)); -} - -// It's hard to tell which struct return types use _objc_msgForward, and -// which use _objc_msgForward_stret instead, so just exclude all struct, array, -// union, complex and vector return types. -static void RACCheckTypeEncoding(const char *typeEncoding) { -#if !NS_BLOCK_ASSERTIONS - // Some types, including vector types, are not encoded. In these cases the - // signature starts with the size of the argument frame. - NSCAssert(*typeEncoding < '1' || *typeEncoding > '9', @"unknown method return type not supported in type encoding: %s", typeEncoding); - NSCAssert(strstr(typeEncoding, "(") != typeEncoding, @"union method return type not supported"); - NSCAssert(strstr(typeEncoding, "{") != typeEncoding, @"struct method return type not supported"); - NSCAssert(strstr(typeEncoding, "[") != typeEncoding, @"array method return type not supported"); - NSCAssert(strstr(typeEncoding, @encode(_Complex float)) != typeEncoding, @"complex float method return type not supported"); - NSCAssert(strstr(typeEncoding, @encode(_Complex double)) != typeEncoding, @"complex double method return type not supported"); - NSCAssert(strstr(typeEncoding, @encode(_Complex long double)) != typeEncoding, @"complex long double method return type not supported"); - -#endif // !NS_BLOCK_ASSERTIONS -} - -static RACSignal *NSObjectRACSignalForSelector(NSObject *self, SEL selector, Protocol *protocol) { - SEL aliasSelector = RACAliasForSelector(selector); - - @synchronized (self) { - RACSubject *subject = objc_getAssociatedObject(self, aliasSelector); - if (subject != nil) return subject; - - Class class = RACSwizzleClass(self); - NSCAssert(class != nil, @"Could not swizzle class of %@", self); - - subject = [[RACSubject subject] setNameWithFormat:@"%@ -rac_signalForSelector: %s", RACDescription(self), sel_getName(selector)]; - objc_setAssociatedObject(self, aliasSelector, subject, OBJC_ASSOCIATION_RETAIN); - - [self.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - [subject sendCompleted]; - }]]; - - Method targetMethod = class_getInstanceMethod(class, selector); - if (targetMethod == NULL) { - const char *typeEncoding; - if (protocol == NULL) { - typeEncoding = RACSignatureForUndefinedSelector(selector); - } else { - // Look for the selector as an optional instance method. - struct objc_method_description methodDescription = protocol_getMethodDescription(protocol, selector, NO, YES); - - if (methodDescription.name == NULL) { - // Then fall back to looking for a required instance - // method. - methodDescription = protocol_getMethodDescription(protocol, selector, YES, YES); - NSCAssert(methodDescription.name != NULL, @"Selector %@ does not exist in <%s>", NSStringFromSelector(selector), protocol_getName(protocol)); - } - - typeEncoding = methodDescription.types; - } - - RACCheckTypeEncoding(typeEncoding); - - // Define the selector to call -forwardInvocation:. - if (!class_addMethod(class, selector, _objc_msgForward, typeEncoding)) { - NSDictionary *userInfo = @{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedString(@"A race condition occurred implementing %@ on class %@", nil), NSStringFromSelector(selector), class], - NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Invoke -rac_signalForSelector: again to override the implementation.", nil) - }; - - return [RACSignal error:[NSError errorWithDomain:RACSelectorSignalErrorDomain code:RACSelectorSignalErrorMethodSwizzlingRace userInfo:userInfo]]; - } - } else if (method_getImplementation(targetMethod) != _objc_msgForward) { - // Make a method alias for the existing method implementation. - const char *typeEncoding = method_getTypeEncoding(targetMethod); - - RACCheckTypeEncoding(typeEncoding); - - BOOL addedAlias __attribute__((unused)) = class_addMethod(class, aliasSelector, method_getImplementation(targetMethod), typeEncoding); - NSCAssert(addedAlias, @"Original implementation for %@ is already copied to %@ on %@", NSStringFromSelector(selector), NSStringFromSelector(aliasSelector), class); - - // Redefine the selector to call -forwardInvocation:. - class_replaceMethod(class, selector, _objc_msgForward, method_getTypeEncoding(targetMethod)); - } - - return subject; - } -} - -static SEL RACAliasForSelector(SEL originalSelector) { - NSString *selectorName = NSStringFromSelector(originalSelector); - return NSSelectorFromString([RACSignalForSelectorAliasPrefix stringByAppendingString:selectorName]); -} - -static const char *RACSignatureForUndefinedSelector(SEL selector) { - const char *name = sel_getName(selector); - NSMutableString *signature = [NSMutableString stringWithString:@"v@:"]; - - while ((name = strchr(name, ':')) != NULL) { - [signature appendString:@"@"]; - name++; - } - - return signature.UTF8String; -} - -static Class RACSwizzleClass(NSObject *self) { - Class statedClass = self.class; - Class baseClass = object_getClass(self); - - // The "known dynamic subclass" is the subclass generated by RAC. - // It's stored as an associated object on every instance that's already - // been swizzled, so that even if something else swizzles the class of - // this instance, we can still access the RAC generated subclass. - Class knownDynamicSubclass = objc_getAssociatedObject(self, RACSubclassAssociationKey); - if (knownDynamicSubclass != Nil) return knownDynamicSubclass; - - NSString *className = NSStringFromClass(baseClass); - - if (statedClass != baseClass) { - // If the class is already lying about what it is, it's probably a KVO - // dynamic subclass or something else that we shouldn't subclass - // ourselves. - // - // Just swizzle -forwardInvocation: in-place. Since the object's class - // was almost certainly dynamically changed, we shouldn't see another of - // these classes in the hierarchy. - // - // Additionally, swizzle -respondsToSelector: because the default - // implementation may be ignorant of methods added to this class. - @synchronized (swizzledClasses()) { - if (![swizzledClasses() containsObject:className]) { - RACSwizzleForwardInvocation(baseClass); - RACSwizzleRespondsToSelector(baseClass); - RACSwizzleGetClass(baseClass, statedClass); - RACSwizzleGetClass(object_getClass(baseClass), statedClass); - RACSwizzleMethodSignatureForSelector(baseClass); - [swizzledClasses() addObject:className]; - } - } - - return baseClass; - } - - const char *subclassName = [className stringByAppendingString:RACSubclassSuffix].UTF8String; - Class subclass = objc_getClass(subclassName); - - if (subclass == nil) { - subclass = objc_allocateClassPair(baseClass, subclassName, 0); - if (subclass == nil) return nil; - - RACSwizzleForwardInvocation(subclass); - RACSwizzleRespondsToSelector(subclass); - - RACSwizzleGetClass(subclass, statedClass); - RACSwizzleGetClass(object_getClass(subclass), statedClass); - - RACSwizzleMethodSignatureForSelector(subclass); - - objc_registerClassPair(subclass); - } - - object_setClass(self, subclass); - objc_setAssociatedObject(self, RACSubclassAssociationKey, subclass, OBJC_ASSOCIATION_ASSIGN); - return subclass; -} - -- (RACSignal *)rac_signalForSelector:(SEL)selector { - NSCParameterAssert(selector != NULL); - - return NSObjectRACSignalForSelector(self, selector, NULL); -} - -- (RACSignal *)rac_signalForSelector:(SEL)selector fromProtocol:(Protocol *)protocol { - NSCParameterAssert(selector != NULL); - NSCParameterAssert(protocol != NULL); - - return NSObjectRACSignalForSelector(self, selector, protocol); -} - -@end diff --git a/ReactiveObjC/NSOrderedSet+RACSequenceAdditions.h b/ReactiveObjC/NSOrderedSet+RACSequenceAdditions.h deleted file mode 100644 index 15a487d087..0000000000 --- a/ReactiveObjC/NSOrderedSet+RACSequenceAdditions.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// NSOrderedSet+RACSequenceAdditions.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-10-29. -// Copyright (c) 2012 GitHub. All rights reserved. -// - -#import - -@class RACSequence; - -@interface NSOrderedSet (RACSequenceAdditions) - -/// Creates and returns a sequence corresponding to the receiver. -/// -/// Mutating the receiver will not affect the sequence after it's been created. -@property (nonatomic, copy, readonly) RACSequence *rac_sequence; - -@end diff --git a/ReactiveObjC/NSOrderedSet+RACSequenceAdditions.m b/ReactiveObjC/NSOrderedSet+RACSequenceAdditions.m deleted file mode 100644 index 31792d4573..0000000000 --- a/ReactiveObjC/NSOrderedSet+RACSequenceAdditions.m +++ /dev/null @@ -1,19 +0,0 @@ -// -// NSOrderedSet+RACSequenceAdditions.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-10-29. -// Copyright (c) 2012 GitHub. All rights reserved. -// - -#import "NSOrderedSet+RACSequenceAdditions.h" -#import "NSArray+RACSequenceAdditions.h" - -@implementation NSOrderedSet (RACSequenceAdditions) - -- (RACSequence *)rac_sequence { - // TODO: First class support for ordered set sequences. - return self.array.rac_sequence; -} - -@end diff --git a/ReactiveObjC/NSSet+RACSequenceAdditions.h b/ReactiveObjC/NSSet+RACSequenceAdditions.h deleted file mode 100644 index a6de6dc343..0000000000 --- a/ReactiveObjC/NSSet+RACSequenceAdditions.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// NSSet+RACSequenceAdditions.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-10-29. -// Copyright (c) 2012 GitHub. All rights reserved. -// - -#import - -@class RACSequence; - -@interface NSSet (RACSequenceAdditions) - -/// Creates and returns a sequence corresponding to the receiver. -/// -/// Mutating the receiver will not affect the sequence after it's been created. -@property (nonatomic, copy, readonly) RACSequence *rac_sequence; - -@end diff --git a/ReactiveObjC/NSSet+RACSequenceAdditions.m b/ReactiveObjC/NSSet+RACSequenceAdditions.m deleted file mode 100644 index 8c7355fac4..0000000000 --- a/ReactiveObjC/NSSet+RACSequenceAdditions.m +++ /dev/null @@ -1,19 +0,0 @@ -// -// NSSet+RACSequenceAdditions.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-10-29. -// Copyright (c) 2012 GitHub. All rights reserved. -// - -#import "NSSet+RACSequenceAdditions.h" -#import "NSArray+RACSequenceAdditions.h" - -@implementation NSSet (RACSequenceAdditions) - -- (RACSequence *)rac_sequence { - // TODO: First class support for set sequences. - return self.allObjects.rac_sequence; -} - -@end diff --git a/ReactiveObjC/NSString+RACKeyPathUtilities.h b/ReactiveObjC/NSString+RACKeyPathUtilities.h deleted file mode 100644 index d9377042d7..0000000000 --- a/ReactiveObjC/NSString+RACKeyPathUtilities.h +++ /dev/null @@ -1,34 +0,0 @@ -// -// NSString+RACKeyPathUtilities.h -// ReactiveObjC -// -// Created by Uri Baghin on 05/05/2013. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -// A private category of methods to extract parts of a key path. -@interface NSString (RACKeyPathUtilities) - -// Returns an array of the components of the receiver. -// -// Calling this method on a string that isn't a key path is considered undefined -// behavior. -- (NSArray *)rac_keyPathComponents; - -// Returns a key path with all the components of the receiver except for the -// last one. -// -// Calling this method on a string that isn't a key path is considered undefined -// behavior. -- (NSString *)rac_keyPathByDeletingLastKeyPathComponent; - -// Returns a key path with all the components of the receiver expect for the -// first one. -// -// Calling this method on a string that isn't a key path is considered undefined -// behavior. -- (NSString *)rac_keyPathByDeletingFirstKeyPathComponent; - -@end diff --git a/ReactiveObjC/NSString+RACKeyPathUtilities.m b/ReactiveObjC/NSString+RACKeyPathUtilities.m deleted file mode 100644 index d62c0521b8..0000000000 --- a/ReactiveObjC/NSString+RACKeyPathUtilities.m +++ /dev/null @@ -1,36 +0,0 @@ -// -// NSString+RACKeyPathUtilities.m -// ReactiveObjC -// -// Created by Uri Baghin on 05/05/2013. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "NSString+RACKeyPathUtilities.h" - -@implementation NSString (RACKeyPathUtilities) - -- (NSArray *)rac_keyPathComponents { - if (self.length == 0) { - return nil; - } - return [self componentsSeparatedByString:@"."]; -} - -- (NSString *)rac_keyPathByDeletingLastKeyPathComponent { - NSUInteger lastDotIndex = [self rangeOfString:@"." options:NSBackwardsSearch].location; - if (lastDotIndex == NSNotFound) { - return nil; - } - return [self substringToIndex:lastDotIndex]; -} - -- (NSString *)rac_keyPathByDeletingFirstKeyPathComponent { - NSUInteger firstDotIndex = [self rangeOfString:@"."].location; - if (firstDotIndex == NSNotFound) { - return nil; - } - return [self substringFromIndex:firstDotIndex + 1]; -} - -@end diff --git a/ReactiveObjC/NSString+RACSequenceAdditions.h b/ReactiveObjC/NSString+RACSequenceAdditions.h deleted file mode 100644 index e2f9e8bd00..0000000000 --- a/ReactiveObjC/NSString+RACSequenceAdditions.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// NSString+RACSequenceAdditions.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-10-29. -// Copyright (c) 2012 GitHub. All rights reserved. -// - -#import - -@class RACSequence; - -@interface NSString (RACSequenceAdditions) - -/// Creates and returns a sequence containing strings corresponding to each -/// composed character sequence in the receiver. -/// -/// Mutating the receiver will not affect the sequence after it's been created. -@property (nonatomic, copy, readonly) RACSequence *rac_sequence; - -@end diff --git a/ReactiveObjC/NSString+RACSequenceAdditions.m b/ReactiveObjC/NSString+RACSequenceAdditions.m deleted file mode 100644 index cbcc31b07e..0000000000 --- a/ReactiveObjC/NSString+RACSequenceAdditions.m +++ /dev/null @@ -1,18 +0,0 @@ -// -// NSString+RACSequenceAdditions.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-10-29. -// Copyright (c) 2012 GitHub. All rights reserved. -// - -#import "NSString+RACSequenceAdditions.h" -#import "RACStringSequence.h" - -@implementation NSString (RACSequenceAdditions) - -- (RACSequence *)rac_sequence { - return [RACStringSequence sequenceWithString:self offset:0]; -} - -@end diff --git a/ReactiveObjC/NSString+RACSupport.h b/ReactiveObjC/NSString+RACSupport.h deleted file mode 100644 index a54c0e0823..0000000000 --- a/ReactiveObjC/NSString+RACSupport.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// NSString+RACSupport.h -// ReactiveObjC -// -// Created by Josh Abernathy on 5/11/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import - -@class RACScheduler; -@class RACSignal; - -@interface NSString (RACSupport) - -// Reads in the contents of the file using +[NSString stringWithContentsOfURL:usedEncoding:error:]. -// Note that encoding won't be valid until the signal completes successfully. -// -// scheduler - cannot be nil. -+ (RACSignal *)rac_readContentsOfURL:(NSURL *)URL usedEncoding:(NSStringEncoding *)encoding scheduler:(RACScheduler *)scheduler; - -@end diff --git a/ReactiveObjC/NSString+RACSupport.m b/ReactiveObjC/NSString+RACSupport.m deleted file mode 100644 index 5e98062c13..0000000000 --- a/ReactiveObjC/NSString+RACSupport.m +++ /dev/null @@ -1,35 +0,0 @@ -// -// NSString+RACSupport.m -// ReactiveObjC -// -// Created by Josh Abernathy on 5/11/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "NSString+RACSupport.h" -#import "RACReplaySubject.h" -#import "RACScheduler.h" - -@implementation NSString (RACSupport) - -+ (RACSignal *)rac_readContentsOfURL:(NSURL *)URL usedEncoding:(NSStringEncoding *)encoding scheduler:(RACScheduler *)scheduler { - NSCParameterAssert(scheduler != nil); - - RACReplaySubject *subject = [RACReplaySubject subject]; - [subject setNameWithFormat:@"+rac_readContentsOfURL: %@ usedEncoding:scheduler: %@", URL, scheduler]; - - [scheduler schedule:^{ - NSError *error = nil; - NSString *string = [NSString stringWithContentsOfURL:URL usedEncoding:encoding error:&error]; - if (string == nil) { - [subject sendError:error]; - } else { - [subject sendNext:string]; - [subject sendCompleted]; - } - }]; - - return subject; -} - -@end diff --git a/ReactiveObjC/NSText+RACSignalSupport.h b/ReactiveObjC/NSText+RACSignalSupport.h deleted file mode 100644 index fd9afd7781..0000000000 --- a/ReactiveObjC/NSText+RACSignalSupport.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// NSText+RACSignalSupport.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-03-08. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -@class RACSignal; - -@interface NSText (RACSignalSupport) - -/// Returns a signal which sends the current `string` of the receiver, then the -/// new value any time it changes. -- (RACSignal *)rac_textSignal; - -@end diff --git a/ReactiveObjC/NSText+RACSignalSupport.m b/ReactiveObjC/NSText+RACSignalSupport.m deleted file mode 100644 index dfeff1ee23..0000000000 --- a/ReactiveObjC/NSText+RACSignalSupport.m +++ /dev/null @@ -1,38 +0,0 @@ -// -// NSText+RACSignalSupport.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-03-08. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "NSText+RACSignalSupport.h" -#import -#import "NSObject+RACDescription.h" -#import "RACDisposable.h" -#import "RACSignal.h" -#import "RACSubscriber.h" - -@implementation NSText (RACSignalSupport) - -- (RACSignal *)rac_textSignal { - @unsafeify(self); - return [[[[RACSignal - createSignal:^(id subscriber) { - @strongify(self); - id observer = [NSNotificationCenter.defaultCenter addObserverForName:NSTextDidChangeNotification object:self queue:nil usingBlock:^(NSNotification *note) { - [subscriber sendNext:note.object]; - }]; - - return [RACDisposable disposableWithBlock:^{ - [NSNotificationCenter.defaultCenter removeObserver:observer]; - }]; - }] - map:^(NSText *text) { - return [text.string copy]; - }] - startWith:[self.string copy]] - setNameWithFormat:@"%@ -rac_textSignal", RACDescription(self)]; -} - -@end diff --git a/ReactiveObjC/NSURLConnection+RACSupport.h b/ReactiveObjC/NSURLConnection+RACSupport.h deleted file mode 100644 index 20bb61e276..0000000000 --- a/ReactiveObjC/NSURLConnection+RACSupport.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// NSURLConnection+RACSupport.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-10-01. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -@class RACSignal; - -@interface NSURLConnection (RACSupport) - -// Lazily loads data for the given request in the background. -// -// request - The URL request to load. This must not be nil. -// -// Returns a signal which will begin loading the request upon each subscription, -// then send a `RACTuple` of the received `NSURLResponse` and downloaded -// `NSData`, and complete on a background thread. If any errors occur, the -// returned signal will error out. -+ (RACSignal *)rac_sendAsynchronousRequest:(NSURLRequest *)request; - -@end diff --git a/ReactiveObjC/NSURLConnection+RACSupport.m b/ReactiveObjC/NSURLConnection+RACSupport.m deleted file mode 100644 index 7acd322aea..0000000000 --- a/ReactiveObjC/NSURLConnection+RACSupport.m +++ /dev/null @@ -1,54 +0,0 @@ -// -// NSURLConnection+RACSupport.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-10-01. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "NSURLConnection+RACSupport.h" -#import "RACDisposable.h" -#import "RACSignal.h" -#import "RACSubscriber.h" -#import "RACTuple.h" - -@implementation NSURLConnection (RACSupport) - -+ (RACSignal *)rac_sendAsynchronousRequest:(NSURLRequest *)request { - NSCParameterAssert(request != nil); - - return [[RACSignal - createSignal:^(id subscriber) { - NSOperationQueue *queue = [[NSOperationQueue alloc] init]; - queue.name = @"org.reactivecocoa.ReactiveObjC.NSURLConnectionRACSupport"; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) { - // The docs say that `nil` data means an error occurred, but - // `nil` responses can also occur in practice (circumstances - // unknown). Consider either to be an error. - // - // Note that _empty_ data is not necessarily erroneous, as there - // may be headers but no HTTP body. - if (response == nil || data == nil) { - [subscriber sendError:error]; - } else { - [subscriber sendNext:RACTuplePack(response, data)]; - [subscriber sendCompleted]; - } - }]; -#pragma clang diagnostic pop - - return [RACDisposable disposableWithBlock:^{ - // It's not clear if this will actually cancel the connection, - // but we can at least prevent _some_ unnecessary work -- - // without writing all the code for a proper delegate, which - // doesn't really belong in RAC. - queue.suspended = YES; - [queue cancelAllOperations]; - }]; - }] - setNameWithFormat:@"+rac_sendAsynchronousRequest: %@", request]; -} - -@end diff --git a/ReactiveObjC/NSUserDefaults+RACSupport.h b/ReactiveObjC/NSUserDefaults+RACSupport.h deleted file mode 100644 index c13aa9c1d3..0000000000 --- a/ReactiveObjC/NSUserDefaults+RACSupport.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// NSUserDefaults+RACSupport.h -// ReactiveObjC -// -// Created by Matt Diephouse on 12/19/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -@class RACChannelTerminal; - -@interface NSUserDefaults (RACSupport) - -/// Creates and returns a terminal for binding the user defaults key. -/// -/// **Note:** The value in the user defaults is *asynchronously* updated with -/// values sent to the channel. -/// -/// key - The user defaults key to create the channel terminal for. -/// -/// Returns a channel terminal that sends the value of the user defaults key -/// upon subscription, sends an updated value whenever the default changes, and -/// updates the default asynchronously with values it receives. -- (RACChannelTerminal *)rac_channelTerminalForKey:(NSString *)key; - -@end diff --git a/ReactiveObjC/NSUserDefaults+RACSupport.m b/ReactiveObjC/NSUserDefaults+RACSupport.m deleted file mode 100644 index ebb7533674..0000000000 --- a/ReactiveObjC/NSUserDefaults+RACSupport.m +++ /dev/null @@ -1,56 +0,0 @@ -// -// NSUserDefaults+RACSupport.m -// ReactiveObjC -// -// Created by Matt Diephouse on 12/19/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "NSUserDefaults+RACSupport.h" -#import -#import "NSNotificationCenter+RACSupport.h" -#import "NSObject+RACDeallocating.h" -#import "RACChannel.h" -#import "RACScheduler.h" -#import "RACSignal+Operations.h" - -@implementation NSUserDefaults (RACSupport) - -- (RACChannelTerminal *)rac_channelTerminalForKey:(NSString *)key { - RACChannel *channel = [RACChannel new]; - - RACScheduler *scheduler = [RACScheduler scheduler]; - __block BOOL ignoreNextValue = NO; - - @weakify(self); - [[[[[[[NSNotificationCenter.defaultCenter - rac_addObserverForName:NSUserDefaultsDidChangeNotification object:self] - map:^(id _) { - @strongify(self); - return [self objectForKey:key]; - }] - startWith:[self objectForKey:key]] - // Don't send values that were set on the other side of the terminal. - filter:^ BOOL (id _) { - if (RACScheduler.currentScheduler == scheduler && ignoreNextValue) { - ignoreNextValue = NO; - return NO; - } - return YES; - }] - distinctUntilChanged] - takeUntil:self.rac_willDeallocSignal] - subscribe:channel.leadingTerminal]; - - [[channel.leadingTerminal - deliverOn:scheduler] - subscribeNext:^(id value) { - @strongify(self); - ignoreNextValue = YES; - [self setObject:value forKey:key]; - }]; - - return channel.followingTerminal; -} - -@end diff --git a/ReactiveObjC/RACArraySequence.h b/ReactiveObjC/RACArraySequence.h deleted file mode 100644 index a77959622c..0000000000 --- a/ReactiveObjC/RACArraySequence.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// RACArraySequence.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-10-29. -// Copyright (c) 2012 GitHub. All rights reserved. -// - -#import "RACSequence.h" - -// Private class that adapts an array to the RACSequence interface. -@interface RACArraySequence : RACSequence - -// Returns a sequence for enumerating over the given array, starting from the -// given offset. The array will be copied to prevent mutation. -+ (instancetype)sequenceWithArray:(NSArray *)array offset:(NSUInteger)offset; - -@end diff --git a/ReactiveObjC/RACArraySequence.m b/ReactiveObjC/RACArraySequence.m deleted file mode 100644 index cc9a63b122..0000000000 --- a/ReactiveObjC/RACArraySequence.m +++ /dev/null @@ -1,125 +0,0 @@ -// -// RACArraySequence.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-10-29. -// Copyright (c) 2012 GitHub. All rights reserved. -// - -#import "RACArraySequence.h" - -@interface RACArraySequence () - -// Redeclared from the superclass and marked deprecated to prevent using `array` -// where `backingArray` is intended. -@property (nonatomic, copy, readonly) NSArray *array __attribute__((deprecated)); - -// The array being sequenced. -@property (nonatomic, copy, readonly) NSArray *backingArray; - -// The index in the array from which the sequence starts. -@property (nonatomic, assign, readonly) NSUInteger offset; - -@end - -@implementation RACArraySequence - -#pragma mark Lifecycle - -+ (instancetype)sequenceWithArray:(NSArray *)array offset:(NSUInteger)offset { - NSCParameterAssert(offset <= array.count); - - if (offset == array.count) return self.empty; - - RACArraySequence *seq = [[self alloc] init]; - seq->_backingArray = [array copy]; - seq->_offset = offset; - return seq; -} - -#pragma mark RACSequence - -- (id)head { - return self.backingArray[self.offset]; -} - -- (RACSequence *)tail { - RACSequence *sequence = [self.class sequenceWithArray:self.backingArray offset:self.offset + 1]; - sequence.name = self.name; - return sequence; -} - -#pragma mark NSFastEnumeration - -- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(__unsafe_unretained id[])stackbuf count:(NSUInteger)len { - NSCParameterAssert(len > 0); - - if (state->state >= self.backingArray.count) { - // Enumeration has completed. - return 0; - } - - if (state->state == 0) { - state->state = self.offset; - - // Since a sequence doesn't mutate, this just needs to be set to - // something non-NULL. - state->mutationsPtr = state->extra; - } - - state->itemsPtr = stackbuf; - - NSUInteger startIndex = state->state; - NSUInteger index = 0; - - for (id value in self.backingArray) { - // Constructing an index set for -enumerateObjectsAtIndexes: can actually be - // slower than just skipping the items we don't care about. - if (index < startIndex) { - ++index; - continue; - } - - stackbuf[index - startIndex] = value; - - ++index; - if (index - startIndex >= len) break; - } - - NSCAssert(index > startIndex, @"Final index (%lu) should be greater than start index (%lu)", (unsigned long)index, (unsigned long)startIndex); - - state->state = index; - return index - startIndex; -} - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-implementations" -- (NSArray *)array { - return [self.backingArray subarrayWithRange:NSMakeRange(self.offset, self.backingArray.count - self.offset)]; -} -#pragma clang diagnostic pop - -#pragma mark NSCoding - -- (id)initWithCoder:(NSCoder *)coder { - self = [super initWithCoder:coder]; - if (self == nil) return nil; - - _backingArray = [coder decodeObjectForKey:@"array"]; - _offset = 0; - - return self; -} - -- (void)encodeWithCoder:(NSCoder *)coder { - // Encoding is handled in RACSequence. - [super encodeWithCoder:coder]; -} - -#pragma mark NSObject - -- (NSString *)description { - return [NSString stringWithFormat:@"<%@: %p>{ name = %@, array = %@ }", self.class, self, self.name, self.backingArray]; -} - -@end diff --git a/ReactiveObjC/RACBehaviorSubject.h b/ReactiveObjC/RACBehaviorSubject.h deleted file mode 100644 index 9d273643c6..0000000000 --- a/ReactiveObjC/RACBehaviorSubject.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// RACBehaviorSubject.h -// ReactiveObjC -// -// Created by Josh Abernathy on 3/16/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACSubject.h" - -/// A behavior subject sends the last value it received when it is subscribed to. -@interface RACBehaviorSubject : RACSubject - -/// Creates a new behavior subject with a default value. If it hasn't received -/// any values when it gets subscribed to, it sends the default value. -+ (instancetype)behaviorSubjectWithDefaultValue:(id)value; - -@end diff --git a/ReactiveObjC/RACBehaviorSubject.m b/ReactiveObjC/RACBehaviorSubject.m deleted file mode 100644 index be0a60091a..0000000000 --- a/ReactiveObjC/RACBehaviorSubject.m +++ /dev/null @@ -1,56 +0,0 @@ -// -// RACBehaviorSubject.m -// ReactiveObjC -// -// Created by Josh Abernathy on 3/16/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACBehaviorSubject.h" -#import "RACDisposable.h" -#import "RACScheduler+Private.h" - -@interface RACBehaviorSubject () - -// This property should only be used while synchronized on self. -@property (nonatomic, strong) id currentValue; - -@end - -@implementation RACBehaviorSubject - -#pragma mark Lifecycle - -+ (instancetype)behaviorSubjectWithDefaultValue:(id)value { - RACBehaviorSubject *subject = [self subject]; - subject.currentValue = value; - return subject; -} - -#pragma mark RACSignal - -- (RACDisposable *)subscribe:(id)subscriber { - RACDisposable *subscriptionDisposable = [super subscribe:subscriber]; - - RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{ - @synchronized (self) { - [subscriber sendNext:self.currentValue]; - } - }]; - - return [RACDisposable disposableWithBlock:^{ - [subscriptionDisposable dispose]; - [schedulingDisposable dispose]; - }]; -} - -#pragma mark RACSubscriber - -- (void)sendNext:(id)value { - @synchronized (self) { - self.currentValue = value; - [super sendNext:value]; - } -} - -@end diff --git a/ReactiveObjC/RACBlockTrampoline.h b/ReactiveObjC/RACBlockTrampoline.h deleted file mode 100644 index c0e89bca4a..0000000000 --- a/ReactiveObjC/RACBlockTrampoline.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// RACBlockTrampoline.h -// ReactiveObjC -// -// Created by Josh Abernathy on 10/21/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import - -@class RACTuple; - -// A private class that allows a limited type of dynamic block invocation. -@interface RACBlockTrampoline : NSObject - -// Invokes the given block with the given arguments. All of the block's -// argument types must be objects and it must be typed to return an object. -// -// At this time, it only supports blocks that take up to 15 arguments. Any more -// is just cray. -// -// block - The block to invoke. Must accept as many arguments as are given in -// the arguments array. Cannot be nil. -// arguments - The arguments with which to invoke the block. `RACTupleNil`s will -// be passed as nils. -// -// Returns the return value of invoking the block. -+ (id)invokeBlock:(id)block withArguments:(RACTuple *)arguments; - -@end diff --git a/ReactiveObjC/RACBlockTrampoline.m b/ReactiveObjC/RACBlockTrampoline.m deleted file mode 100644 index 483a89b772..0000000000 --- a/ReactiveObjC/RACBlockTrampoline.m +++ /dev/null @@ -1,156 +0,0 @@ -// -// RACBlockTrampoline.m -// ReactiveObjC -// -// Created by Josh Abernathy on 10/21/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACBlockTrampoline.h" -#import "RACTuple.h" - -@interface RACBlockTrampoline () -@property (nonatomic, readonly, copy) id block; -@end - -@implementation RACBlockTrampoline - -#pragma mark API - -- (id)initWithBlock:(id)block { - self = [super init]; - if (self == nil) return nil; - - _block = [block copy]; - - return self; -} - -+ (id)invokeBlock:(id)block withArguments:(RACTuple *)arguments { - NSCParameterAssert(block != NULL); - - RACBlockTrampoline *trampoline = [[self alloc] initWithBlock:block]; - return [trampoline invokeWithArguments:arguments]; -} - -- (id)invokeWithArguments:(RACTuple *)arguments { - SEL selector = [self selectorForArgumentCount:arguments.count]; - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:selector]]; - invocation.selector = selector; - invocation.target = self; - - for (NSUInteger i = 0; i < arguments.count; i++) { - id arg = arguments[i]; - NSInteger argIndex = (NSInteger)(i + 2); - [invocation setArgument:&arg atIndex:argIndex]; - } - - [invocation invoke]; - - __unsafe_unretained id returnVal; - [invocation getReturnValue:&returnVal]; - return returnVal; -} - -- (SEL)selectorForArgumentCount:(NSUInteger)count { - NSCParameterAssert(count > 0); - - switch (count) { - case 0: return NULL; - case 1: return @selector(performWith:); - case 2: return @selector(performWith::); - case 3: return @selector(performWith:::); - case 4: return @selector(performWith::::); - case 5: return @selector(performWith:::::); - case 6: return @selector(performWith::::::); - case 7: return @selector(performWith:::::::); - case 8: return @selector(performWith::::::::); - case 9: return @selector(performWith:::::::::); - case 10: return @selector(performWith::::::::::); - case 11: return @selector(performWith:::::::::::); - case 12: return @selector(performWith::::::::::::); - case 13: return @selector(performWith:::::::::::::); - case 14: return @selector(performWith::::::::::::::); - case 15: return @selector(performWith:::::::::::::::); - } - - NSCAssert(NO, @"The argument count is too damn high! Only blocks of up to 15 arguments are currently supported."); - return NULL; -} - -- (id)performWith:(id)obj1 { - id (^block)(id) = self.block; - return block(obj1); -} - -- (id)performWith:(id)obj1 :(id)obj2 { - id (^block)(id, id) = self.block; - return block(obj1, obj2); -} - -- (id)performWith:(id)obj1 :(id)obj2 :(id)obj3 { - id (^block)(id, id, id) = self.block; - return block(obj1, obj2, obj3); -} - -- (id)performWith:(id)obj1 :(id)obj2 :(id)obj3 :(id)obj4 { - id (^block)(id, id, id, id) = self.block; - return block(obj1, obj2, obj3, obj4); -} - -- (id)performWith:(id)obj1 :(id)obj2 :(id)obj3 :(id)obj4 :(id)obj5 { - id (^block)(id, id, id, id, id) = self.block; - return block(obj1, obj2, obj3, obj4, obj5); -} - -- (id)performWith:(id)obj1 :(id)obj2 :(id)obj3 :(id)obj4 :(id)obj5 :(id)obj6 { - id (^block)(id, id, id, id, id, id) = self.block; - return block(obj1, obj2, obj3, obj4, obj5, obj6); -} - -- (id)performWith:(id)obj1 :(id)obj2 :(id)obj3 :(id)obj4 :(id)obj5 :(id)obj6 :(id)obj7 { - id (^block)(id, id, id, id, id, id, id) = self.block; - return block(obj1, obj2, obj3, obj4, obj5, obj6, obj7); -} - -- (id)performWith:(id)obj1 :(id)obj2 :(id)obj3 :(id)obj4 :(id)obj5 :(id)obj6 :(id)obj7 :(id)obj8 { - id (^block)(id, id, id, id, id, id, id, id) = self.block; - return block(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8); -} - -- (id)performWith:(id)obj1 :(id)obj2 :(id)obj3 :(id)obj4 :(id)obj5 :(id)obj6 :(id)obj7 :(id)obj8 :(id)obj9 { - id (^block)(id, id, id, id, id, id, id, id, id) = self.block; - return block(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8, obj9); -} - -- (id)performWith:(id)obj1 :(id)obj2 :(id)obj3 :(id)obj4 :(id)obj5 :(id)obj6 :(id)obj7 :(id)obj8 :(id)obj9 :(id)obj10 { - id (^block)(id, id, id, id, id, id, id, id, id, id) = self.block; - return block(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8, obj9, obj10); -} - -- (id)performWith:(id)obj1 :(id)obj2 :(id)obj3 :(id)obj4 :(id)obj5 :(id)obj6 :(id)obj7 :(id)obj8 :(id)obj9 :(id)obj10 :(id)obj11 { - id (^block)(id, id, id, id, id, id, id, id, id, id, id) = self.block; - return block(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8, obj9, obj10, obj11); -} - -- (id)performWith:(id)obj1 :(id)obj2 :(id)obj3 :(id)obj4 :(id)obj5 :(id)obj6 :(id)obj7 :(id)obj8 :(id)obj9 :(id)obj10 :(id)obj11 :(id)obj12 { - id (^block)(id, id, id, id, id, id, id, id, id, id, id, id) = self.block; - return block(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8, obj9, obj10, obj11, obj12); -} - -- (id)performWith:(id)obj1 :(id)obj2 :(id)obj3 :(id)obj4 :(id)obj5 :(id)obj6 :(id)obj7 :(id)obj8 :(id)obj9 :(id)obj10 :(id)obj11 :(id)obj12 :(id)obj13 { - id (^block)(id, id, id, id, id, id, id, id, id, id, id, id, id) = self.block; - return block(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8, obj9, obj10, obj11, obj12, obj13); -} - -- (id)performWith:(id)obj1 :(id)obj2 :(id)obj3 :(id)obj4 :(id)obj5 :(id)obj6 :(id)obj7 :(id)obj8 :(id)obj9 :(id)obj10 :(id)obj11 :(id)obj12 :(id)obj13 :(id)obj14 { - id (^block)(id, id, id, id, id, id, id, id, id, id, id, id, id, id) = self.block; - return block(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8, obj9, obj10, obj11, obj12, obj13, obj14); -} - -- (id)performWith:(id)obj1 :(id)obj2 :(id)obj3 :(id)obj4 :(id)obj5 :(id)obj6 :(id)obj7 :(id)obj8 :(id)obj9 :(id)obj10 :(id)obj11 :(id)obj12 :(id)obj13 :(id)obj14 :(id)obj15 { - id (^block)(id, id, id, id, id, id, id, id, id, id, id, id, id, id, id) = self.block; - return block(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8, obj9, obj10, obj11, obj12, obj13, obj14, obj15); -} - -@end diff --git a/ReactiveObjC/RACChannel.h b/ReactiveObjC/RACChannel.h deleted file mode 100644 index 67a7306afd..0000000000 --- a/ReactiveObjC/RACChannel.h +++ /dev/null @@ -1,70 +0,0 @@ -// -// RACChannel.h -// ReactiveObjC -// -// Created by Uri Baghin on 01/01/2013. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACSignal.h" -#import "RACSubscriber.h" - -@class RACChannelTerminal; - -/// A two-way channel. -/// -/// Conceptually, RACChannel can be thought of as a bidirectional connection, -/// composed of two controllable signals that work in parallel. -/// -/// For example, when connecting between a view and a model: -/// -/// Model View -/// `leadingTerminal` ------> `followingTerminal` -/// `leadingTerminal` <------ `followingTerminal` -/// -/// The initial value of the model and all future changes to it are _sent on_ the -/// `leadingTerminal`, and _received by_ subscribers of the `followingTerminal`. -/// -/// Likewise, whenever the user changes the value of the view, that value is sent -/// on the `followingTerminal`, and received in the model from the -/// `leadingTerminal`. However, the initial value of the view is not received -/// from the `leadingTerminal` (only future changes). -@interface RACChannel : NSObject - -/// The terminal which "leads" the channel, by sending its latest value -/// immediately to new subscribers of the `followingTerminal`. -/// -/// New subscribers to this terminal will not receive a starting value, but will -/// receive all future values that are sent to the `followingTerminal`. -@property (nonatomic, strong, readonly) RACChannelTerminal *leadingTerminal; - -/// The terminal which "follows" the lead of the other terminal, only sending -/// _future_ values to the subscribers of the `leadingTerminal`. -/// -/// The latest value sent to the `leadingTerminal` (if any) will be sent -/// immediately to new subscribers of this terminal, and then all future values -/// as well. -@property (nonatomic, strong, readonly) RACChannelTerminal *followingTerminal; - -@end - -/// Represents one end of a RACChannel. -/// -/// An terminal is similar to a socket or pipe -- it represents one end of -/// a connection (the RACChannel, in this case). Values sent to this terminal -/// will _not_ be received by its subscribers. Instead, the values will be sent -/// to the subscribers of the RACChannel's _other_ terminal. -/// -/// For example, when using the `followingTerminal`, _sent_ values can only be -/// _received_ from the `leadingTerminal`, and vice versa. -/// -/// To make it easy to terminate a RACChannel, `error` and `completed` events -/// sent to either terminal will be received by the subscribers of _both_ -/// terminals. -/// -/// Do not instantiate this class directly. Create a RACChannel instead. -@interface RACChannelTerminal : RACSignal - -- (id)init __attribute__((unavailable("Instantiate a RACChannel instead"))); - -@end diff --git a/ReactiveObjC/RACChannel.m b/ReactiveObjC/RACChannel.m deleted file mode 100644 index 10138eeac0..0000000000 --- a/ReactiveObjC/RACChannel.m +++ /dev/null @@ -1,90 +0,0 @@ -// -// RACChannel.m -// ReactiveObjC -// -// Created by Uri Baghin on 01/01/2013. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACChannel.h" -#import "RACDisposable.h" -#import "RACReplaySubject.h" -#import "RACSignal+Operations.h" - -@interface RACChannelTerminal () - -// The values for this terminal. -@property (nonatomic, strong, readonly) RACSignal *values; - -// A subscriber will will send values to the other terminal. -@property (nonatomic, strong, readonly) id otherTerminal; - -- (id)initWithValues:(RACSignal *)values otherTerminal:(id)otherTerminal; - -@end - -@implementation RACChannel - -- (id)init { - self = [super init]; - if (self == nil) return nil; - - // We don't want any starting value from the leadingSubject, but we do want - // error and completion to be replayed. - RACReplaySubject *leadingSubject = [[RACReplaySubject replaySubjectWithCapacity:0] setNameWithFormat:@"leadingSubject"]; - RACReplaySubject *followingSubject = [[RACReplaySubject replaySubjectWithCapacity:1] setNameWithFormat:@"followingSubject"]; - - // Propagate errors and completion to everything. - [[leadingSubject ignoreValues] subscribe:followingSubject]; - [[followingSubject ignoreValues] subscribe:leadingSubject]; - - _leadingTerminal = [[[RACChannelTerminal alloc] initWithValues:leadingSubject otherTerminal:followingSubject] setNameWithFormat:@"leadingTerminal"]; - _followingTerminal = [[[RACChannelTerminal alloc] initWithValues:followingSubject otherTerminal:leadingSubject] setNameWithFormat:@"followingTerminal"]; - - return self; -} - -@end - -@implementation RACChannelTerminal - -#pragma mark Lifecycle - -- (id)initWithValues:(RACSignal *)values otherTerminal:(id)otherTerminal { - NSCParameterAssert(values != nil); - NSCParameterAssert(otherTerminal != nil); - - self = [super init]; - if (self == nil) return nil; - - _values = values; - _otherTerminal = otherTerminal; - - return self; -} - -#pragma mark RACSignal - -- (RACDisposable *)subscribe:(id)subscriber { - return [self.values subscribe:subscriber]; -} - -#pragma mark - -- (void)sendNext:(id)value { - [self.otherTerminal sendNext:value]; -} - -- (void)sendError:(NSError *)error { - [self.otherTerminal sendError:error]; -} - -- (void)sendCompleted { - [self.otherTerminal sendCompleted]; -} - -- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable { - [self.otherTerminal didSubscribeWithDisposable:disposable]; -} - -@end diff --git a/ReactiveObjC/RACCommand.h b/ReactiveObjC/RACCommand.h deleted file mode 100644 index 23e20dc618..0000000000 --- a/ReactiveObjC/RACCommand.h +++ /dev/null @@ -1,127 +0,0 @@ -// -// RACCommand.h -// ReactiveObjC -// -// Created by Josh Abernathy on 3/3/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import - -@class RACSignal; -NS_ASSUME_NONNULL_BEGIN - -/// The domain for errors originating within `RACCommand`. -extern NSString * const RACCommandErrorDomain; - -/// -execute: was invoked while the command was disabled. -extern const NSInteger RACCommandErrorNotEnabled; - -/// A `userInfo` key for an error, associated with the `RACCommand` that the -/// error originated from. -/// -/// This is included only when the error code is `RACCommandErrorNotEnabled`. -extern NSString * const RACUnderlyingCommandErrorKey; - -/// A command is a signal triggered in response to some action, typically -/// UI-related. -@interface RACCommand<__contravariant InputType> : NSObject - -/// A signal of the signals returned by successful invocations of -execute: -/// (i.e., while the receiver is `enabled`). -/// -/// Errors will be automatically caught upon the inner signals, and sent upon -/// `errors` instead. If you _want_ to receive inner errors, use -execute: or -/// -[RACSignal materialize]. -/// -/// Only executions that begin _after_ subscription will be sent upon this -/// signal. All inner signals will arrive upon the main thread. -@property (nonatomic, strong, readonly) RACSignal *executionSignals; - -/// A signal of whether this command is currently executing. -/// -/// This will send YES whenever -execute: is invoked and the created signal has -/// not yet terminated. Once all executions have terminated, `executing` will -/// send NO. -/// -/// This signal will send its current value upon subscription, and then all -/// future values on the main thread. -@property (nonatomic, strong, readonly) RACSignal *executing; - -/// A signal of whether this command is able to execute. -/// -/// This will send NO if: -/// -/// - The command was created with an `enabledSignal`, and NO is sent upon that -/// signal, or -/// - `allowsConcurrentExecution` is NO and the command has started executing. -/// -/// Once the above conditions are no longer met, the signal will send YES. -/// -/// This signal will send its current value upon subscription, and then all -/// future values on the main thread. -@property (nonatomic, strong, readonly) RACSignal *enabled; - -/// Forwards any errors that occur within signals returned by -execute:. -/// -/// When an error occurs on a signal returned from -execute:, this signal will -/// send the associated NSError value as a `next` event (since an `error` event -/// would terminate the stream). -/// -/// After subscription, this signal will send all future errors on the main -/// thread. -@property (nonatomic, strong, readonly) RACSignal *errors; - -/// Whether the command allows multiple executions to proceed concurrently. -/// -/// The default value for this property is NO. -@property (atomic, assign) BOOL allowsConcurrentExecution; - -/// Invokes -initWithEnabled:signalBlock: with a nil `enabledSignal`. -- (id)initWithSignalBlock:(RACSignal * (^)(InputType _Nullable input))signalBlock; - -/// Initializes a command that is conditionally enabled. -/// -/// This is the designated initializer for this class. -/// -/// enabledSignal - A signal of BOOLs which indicate whether the command should -/// be enabled. `enabled` will be based on the latest value sent -/// from this signal. Before any values are sent, `enabled` will -/// default to YES. This argument may be nil. -/// signalBlock - A block which will map each input value (passed to -execute:) -/// to a signal of work. The returned signal will be multicasted -/// to a replay subject, sent on `executionSignals`, then -/// subscribed to synchronously. Neither the block nor the -/// returned signal may be nil. -- (id)initWithEnabled:(nullable RACSignal *)enabledSignal signalBlock:(RACSignal * (^)(InputType _Nullable input))signalBlock; - -/// If the receiver is enabled, this method will: -/// -/// 1. Invoke the `signalBlock` given at the time of initialization. -/// 2. Multicast the returned signal to a RACReplaySubject. -/// 3. Send the multicasted signal on `executionSignals`. -/// 4. Subscribe (connect) to the original signal on the main thread. -/// -/// input - The input value to pass to the receiver's `signalBlock`. This may be -/// nil. -/// -/// Returns the multicasted signal, after subscription. If the receiver is not -/// enabled, returns a signal that will send an error with code -/// RACCommandErrorNotEnabled. -- (RACSignal *)execute:(nullable InputType)input; - -NS_ASSUME_NONNULL_END -@end - -@interface RACCommand (Unavailable) -NS_ASSUME_NONNULL_BEGIN - -@property (atomic, readonly) BOOL canExecute __attribute__((unavailable("Use the 'enabled' signal instead"))); - -+ (instancetype)command __attribute__((unavailable("Use -initWithSignalBlock: instead"))); -+ (instancetype)commandWithCanExecuteSignal:(RACSignal *)canExecuteSignal __attribute__((unavailable("Use -initWithEnabled:signalBlock: instead"))); -- (id)initWithCanExecuteSignal:(RACSignal *)canExecuteSignal __attribute__((unavailable("Use -initWithEnabled:signalBlock: instead"))); -- (RACSignal *)addSignalBlock:(RACSignal * (^)(id value))signalBlock __attribute__((unavailable("Pass the signalBlock to -initWithSignalBlock: instead"))); - -NS_ASSUME_NONNULL_END -@end diff --git a/ReactiveObjC/RACCommand.m b/ReactiveObjC/RACCommand.m deleted file mode 100644 index b7f77d0fd9..0000000000 --- a/ReactiveObjC/RACCommand.m +++ /dev/null @@ -1,203 +0,0 @@ -// -// RACCommand.m -// ReactiveObjC -// -// Created by Josh Abernathy on 3/3/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACCommand.h" -#import -#import "NSArray+RACSequenceAdditions.h" -#import "NSObject+RACDeallocating.h" -#import "NSObject+RACDescription.h" -#import "NSObject+RACPropertySubscribing.h" -#import "RACMulticastConnection.h" -#import "RACReplaySubject.h" -#import "RACScheduler.h" -#import "RACSequence.h" -#import "RACSignal+Operations.h" -#import - -NSString * const RACCommandErrorDomain = @"RACCommandErrorDomain"; -NSString * const RACUnderlyingCommandErrorKey = @"RACUnderlyingCommandErrorKey"; - -const NSInteger RACCommandErrorNotEnabled = 1; - -@interface RACCommand () { - // Atomic backing variable for `allowsConcurrentExecution`. - volatile uint32_t _allowsConcurrentExecution; -} - -/// A subject that sends added execution signals. -@property (nonatomic, strong, readonly) RACSubject *addedExecutionSignalsSubject; - -/// A subject that sends the new value of `allowsConcurrentExecution` whenever it changes. -@property (nonatomic, strong, readonly) RACSubject *allowsConcurrentExecutionSubject; - -// `enabled`, but without a hop to the main thread. -// -// Values from this signal may arrive on any thread. -@property (nonatomic, strong, readonly) RACSignal *immediateEnabled; - -// The signal block that the receiver was initialized with. -@property (nonatomic, copy, readonly) RACSignal * (^signalBlock)(id input); - -@end - -@implementation RACCommand - -#pragma mark Properties - -- (BOOL)allowsConcurrentExecution { - return _allowsConcurrentExecution != 0; -} - -- (void)setAllowsConcurrentExecution:(BOOL)allowed { - if (allowed) { - OSAtomicOr32Barrier(1, &_allowsConcurrentExecution); - } else { - OSAtomicAnd32Barrier(0, &_allowsConcurrentExecution); - } - - [self.allowsConcurrentExecutionSubject sendNext:@(_allowsConcurrentExecution)]; -} - -#pragma mark Lifecycle - -- (id)init { - NSCAssert(NO, @"Use -initWithSignalBlock: instead"); - return nil; -} - -- (id)initWithSignalBlock:(RACSignal * (^)(id input))signalBlock { - return [self initWithEnabled:nil signalBlock:signalBlock]; -} - -- (void)dealloc { - [_addedExecutionSignalsSubject sendCompleted]; - [_allowsConcurrentExecutionSubject sendCompleted]; -} - -- (id)initWithEnabled:(RACSignal *)enabledSignal signalBlock:(RACSignal * (^)(id input))signalBlock { - NSCParameterAssert(signalBlock != nil); - - self = [super init]; - if (self == nil) return nil; - - _addedExecutionSignalsSubject = [RACSubject new]; - _allowsConcurrentExecutionSubject = [RACSubject new]; - _signalBlock = [signalBlock copy]; - - _executionSignals = [[[self.addedExecutionSignalsSubject - map:^(RACSignal *signal) { - return [signal catchTo:[RACSignal empty]]; - }] - deliverOn:RACScheduler.mainThreadScheduler] - setNameWithFormat:@"%@ -executionSignals", self]; - - // `errors` needs to be multicasted so that it picks up all - // `activeExecutionSignals` that are added. - // - // In other words, if someone subscribes to `errors` _after_ an execution - // has started, it should still receive any error from that execution. - RACMulticastConnection *errorsConnection = [[[self.addedExecutionSignalsSubject - flattenMap:^(RACSignal *signal) { - return [[signal - ignoreValues] - catch:^(NSError *error) { - return [RACSignal return:error]; - }]; - }] - deliverOn:RACScheduler.mainThreadScheduler] - publish]; - - _errors = [errorsConnection.signal setNameWithFormat:@"%@ -errors", self]; - [errorsConnection connect]; - - RACSignal *immediateExecuting = [[[[self.addedExecutionSignalsSubject - flattenMap:^(RACSignal *signal) { - return [[[signal - catchTo:[RACSignal empty]] - then:^{ - return [RACSignal return:@-1]; - }] - startWith:@1]; - }] - scanWithStart:@0 reduce:^(NSNumber *running, NSNumber *next) { - return @(running.integerValue + next.integerValue); - }] - map:^(NSNumber *count) { - return @(count.integerValue > 0); - }] - startWith:@NO]; - - _executing = [[[[[immediateExecuting - deliverOn:RACScheduler.mainThreadScheduler] - // This is useful before the first value arrives on the main thread. - startWith:@NO] - distinctUntilChanged] - replayLast] - setNameWithFormat:@"%@ -executing", self]; - - RACSignal *moreExecutionsAllowed = [RACSignal - if:[self.allowsConcurrentExecutionSubject startWith:@NO] - then:[RACSignal return:@YES] - else:[immediateExecuting not]]; - - if (enabledSignal == nil) { - enabledSignal = [RACSignal return:@YES]; - } else { - enabledSignal = [enabledSignal startWith:@YES]; - } - - _immediateEnabled = [[[[RACSignal - combineLatest:@[ enabledSignal, moreExecutionsAllowed ]] - and] - takeUntil:self.rac_willDeallocSignal] - replayLast]; - - _enabled = [[[[[self.immediateEnabled - take:1] - concat:[[self.immediateEnabled skip:1] deliverOn:RACScheduler.mainThreadScheduler]] - distinctUntilChanged] - replayLast] - setNameWithFormat:@"%@ -enabled", self]; - - return self; -} - -#pragma mark Execution - -- (RACSignal *)execute:(id)input { - // `immediateEnabled` is guaranteed to send a value upon subscription, so - // -first is acceptable here. - BOOL enabled = [[self.immediateEnabled first] boolValue]; - if (!enabled) { - NSError *error = [NSError errorWithDomain:RACCommandErrorDomain code:RACCommandErrorNotEnabled userInfo:@{ - NSLocalizedDescriptionKey: NSLocalizedString(@"The command is disabled and cannot be executed", nil), - RACUnderlyingCommandErrorKey: self - }]; - - return [RACSignal error:error]; - } - - RACSignal *signal = self.signalBlock(input); - NSCAssert(signal != nil, @"nil signal returned from signal block for value: %@", input); - - // We subscribe to the signal on the main thread so that it occurs _after_ - // -addActiveExecutionSignal: completes below. - // - // This means that `executing` and `enabled` will send updated values before - // the signal actually starts performing work. - RACMulticastConnection *connection = [[signal - subscribeOn:RACScheduler.mainThreadScheduler] - multicast:[RACReplaySubject subject]]; - - [self.addedExecutionSignalsSubject sendNext:connection.signal]; - - [connection connect]; - return [connection.signal setNameWithFormat:@"%@ -execute: %@", self, RACDescription(input)]; -} - -@end diff --git a/ReactiveObjC/RACCompoundDisposable.h b/ReactiveObjC/RACCompoundDisposable.h deleted file mode 100644 index ca3fc202c1..0000000000 --- a/ReactiveObjC/RACCompoundDisposable.h +++ /dev/null @@ -1,48 +0,0 @@ -// -// RACCompoundDisposable.h -// ReactiveObjC -// -// Created by Josh Abernathy on 11/30/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACDisposable.h" - -/// A disposable of disposables. When it is disposed, it disposes of all its -/// contained disposables. -/// -/// If -addDisposable: is called after the compound disposable has been disposed -/// of, the given disposable is immediately disposed. This allows a compound -/// disposable to act as a stand-in for a disposable that will be delivered -/// asynchronously. -@interface RACCompoundDisposable : RACDisposable - -/// Creates and returns a new compound disposable. -+ (instancetype)compoundDisposable; - -/// Creates and returns a new compound disposable containing the given -/// disposables. -+ (instancetype)compoundDisposableWithDisposables:(NSArray *)disposables; - -/// Adds the given disposable. If the receiving disposable has already been -/// disposed of, the given disposable is disposed immediately. -/// -/// This method is thread-safe. -/// -/// disposable - The disposable to add. This may be nil, in which case nothing -/// happens. -- (void)addDisposable:(RACDisposable *)disposable; - -/// Removes the specified disposable from the compound disposable (regardless of -/// its disposed status), or does nothing if it's not in the compound disposable. -/// -/// This is mainly useful for limiting the memory usage of the compound -/// disposable for long-running operations. -/// -/// This method is thread-safe. -/// -/// disposable - The disposable to remove. This argument may be nil (to make the -/// use of weak references easier). -- (void)removeDisposable:(RACDisposable *)disposable; - -@end diff --git a/ReactiveObjC/RACCompoundDisposable.m b/ReactiveObjC/RACCompoundDisposable.m deleted file mode 100644 index c081938d03..0000000000 --- a/ReactiveObjC/RACCompoundDisposable.m +++ /dev/null @@ -1,252 +0,0 @@ -// -// RACCompoundDisposable.m -// ReactiveObjC -// -// Created by Josh Abernathy on 11/30/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACCompoundDisposable.h" -#import "RACCompoundDisposableProvider.h" -#import - -// The number of child disposables for which space will be reserved directly in -// `RACCompoundDisposable`. -// -// This number has been empirically determined to provide a good tradeoff -// between performance, memory usage, and `RACCompoundDisposable` instance size -// in a moderately complex GUI application. -// -// Profile any change! -#define RACCompoundDisposableInlineCount 2 - -static CFMutableArrayRef RACCreateDisposablesArray(void) { - // Compare values using only pointer equality. - CFArrayCallBacks callbacks = kCFTypeArrayCallBacks; - callbacks.equal = NULL; - - return CFArrayCreateMutable(NULL, 0, &callbacks); -} - -@interface RACCompoundDisposable () { - // Used for synchronization. - pthread_mutex_t _mutex; - - #if RACCompoundDisposableInlineCount - // A fast array to the first N of the receiver's disposables. - // - // Once this is full, `_disposables` will be created and used for additional - // disposables. - // - // This array should only be manipulated while _mutex is held. - RACDisposable *_inlineDisposables[RACCompoundDisposableInlineCount]; - #endif - - // Contains the receiver's disposables. - // - // This array should only be manipulated while _mutex is held. If - // `_disposed` is YES, this may be NULL. - CFMutableArrayRef _disposables; - - // Whether the receiver has already been disposed. - // - // This ivar should only be accessed while _mutex is held. - BOOL _disposed; -} - -@end - -@implementation RACCompoundDisposable - -#pragma mark Properties - -- (BOOL)isDisposed { - pthread_mutex_lock(&_mutex); - BOOL disposed = _disposed; - pthread_mutex_unlock(&_mutex); - - return disposed; -} - -#pragma mark Lifecycle - -+ (instancetype)compoundDisposable { - return [[self alloc] initWithDisposables:nil]; -} - -+ (instancetype)compoundDisposableWithDisposables:(NSArray *)disposables { - return [[self alloc] initWithDisposables:disposables]; -} - -- (instancetype)init { - self = [super init]; - if (self == nil) return nil; - - const int result = pthread_mutex_init(&_mutex, NULL); - NSCAssert(0 == result, @"Failed to initialize mutex with error %d.", result); - - return self; -} - -- (instancetype)initWithDisposables:(NSArray *)otherDisposables { - self = [self init]; - if (self == nil) return nil; - - #if RACCompoundDisposableInlineCount - [otherDisposables enumerateObjectsUsingBlock:^(RACDisposable *disposable, NSUInteger index, BOOL *stop) { - self->_inlineDisposables[index] = disposable; - - // Stop after this iteration if we've reached the end of the inlined - // array. - if (index == RACCompoundDisposableInlineCount - 1) *stop = YES; - }]; - #endif - - if (otherDisposables.count > RACCompoundDisposableInlineCount) { - _disposables = RACCreateDisposablesArray(); - - CFRange range = CFRangeMake(RACCompoundDisposableInlineCount, (CFIndex)otherDisposables.count - RACCompoundDisposableInlineCount); - CFArrayAppendArray(_disposables, (__bridge CFArrayRef)otherDisposables, range); - } - - return self; -} - -- (instancetype)initWithBlock:(void (^)(void))block { - RACDisposable *disposable = [RACDisposable disposableWithBlock:block]; - return [self initWithDisposables:@[ disposable ]]; -} - -- (void)dealloc { - #if RACCompoundDisposableInlineCount - for (unsigned i = 0; i < RACCompoundDisposableInlineCount; i++) { - _inlineDisposables[i] = nil; - } - #endif - - if (_disposables != NULL) { - CFRelease(_disposables); - _disposables = NULL; - } - - const int result = pthread_mutex_destroy(&_mutex); - NSCAssert(0 == result, @"Failed to destroy mutex with error %d.", result); -} - -#pragma mark Addition and Removal - -- (void)addDisposable:(RACDisposable *)disposable { - NSCParameterAssert(disposable != self); - if (disposable == nil || disposable.disposed) return; - - BOOL shouldDispose = NO; - - pthread_mutex_lock(&_mutex); - { - if (_disposed) { - shouldDispose = YES; - } else { - #if RACCompoundDisposableInlineCount - for (unsigned i = 0; i < RACCompoundDisposableInlineCount; i++) { - if (_inlineDisposables[i] == nil) { - _inlineDisposables[i] = disposable; - goto foundSlot; - } - } - #endif - - if (_disposables == NULL) _disposables = RACCreateDisposablesArray(); - CFArrayAppendValue(_disposables, (__bridge void *)disposable); - - if (RACCOMPOUNDDISPOSABLE_ADDED_ENABLED()) { - RACCOMPOUNDDISPOSABLE_ADDED(self.description.UTF8String, disposable.description.UTF8String, CFArrayGetCount(_disposables) + RACCompoundDisposableInlineCount); - } - - #if RACCompoundDisposableInlineCount - foundSlot:; - #endif - } - } - pthread_mutex_unlock(&_mutex); - - // Performed outside of the lock in case the compound disposable is used - // recursively. - if (shouldDispose) [disposable dispose]; -} - -- (void)removeDisposable:(RACDisposable *)disposable { - if (disposable == nil) return; - - pthread_mutex_lock(&_mutex); - { - if (!_disposed) { - #if RACCompoundDisposableInlineCount - for (unsigned i = 0; i < RACCompoundDisposableInlineCount; i++) { - if (_inlineDisposables[i] == disposable) _inlineDisposables[i] = nil; - } - #endif - - if (_disposables != NULL) { - CFIndex count = CFArrayGetCount(_disposables); - for (CFIndex i = count - 1; i >= 0; i--) { - const void *item = CFArrayGetValueAtIndex(_disposables, i); - if (item == (__bridge void *)disposable) { - CFArrayRemoveValueAtIndex(_disposables, i); - } - } - - if (RACCOMPOUNDDISPOSABLE_REMOVED_ENABLED()) { - RACCOMPOUNDDISPOSABLE_REMOVED(self.description.UTF8String, disposable.description.UTF8String, CFArrayGetCount(_disposables) + RACCompoundDisposableInlineCount); - } - } - } - } - pthread_mutex_unlock(&_mutex); -} - -#pragma mark RACDisposable - -static void disposeEach(const void *value, void *context) { - RACDisposable *disposable = (__bridge id)value; - [disposable dispose]; -} - -- (void)dispose { - #if RACCompoundDisposableInlineCount - RACDisposable *inlineCopy[RACCompoundDisposableInlineCount]; - #endif - - CFArrayRef remainingDisposables = NULL; - - pthread_mutex_lock(&_mutex); - { - _disposed = YES; - - #if RACCompoundDisposableInlineCount - for (unsigned i = 0; i < RACCompoundDisposableInlineCount; i++) { - inlineCopy[i] = _inlineDisposables[i]; - _inlineDisposables[i] = nil; - } - #endif - - remainingDisposables = _disposables; - _disposables = NULL; - } - pthread_mutex_unlock(&_mutex); - - #if RACCompoundDisposableInlineCount - // Dispose outside of the lock in case the compound disposable is used - // recursively. - for (unsigned i = 0; i < RACCompoundDisposableInlineCount; i++) { - [inlineCopy[i] dispose]; - } - #endif - - if (remainingDisposables == NULL) return; - - CFIndex count = CFArrayGetCount(remainingDisposables); - CFArrayApplyFunction(remainingDisposables, CFRangeMake(0, count), &disposeEach, NULL); - CFRelease(remainingDisposables); -} - -@end diff --git a/ReactiveObjC/RACCompoundDisposableProvider.d b/ReactiveObjC/RACCompoundDisposableProvider.d deleted file mode 100644 index 847db19b17..0000000000 --- a/ReactiveObjC/RACCompoundDisposableProvider.d +++ /dev/null @@ -1,4 +0,0 @@ -provider RACCompoundDisposable { - probe added(char *compoundDisposable, char *disposable, long newTotal); - probe removed(char *compoundDisposable, char *disposable, long newTotal); -}; diff --git a/ReactiveObjC/RACDelegateProxy.h b/ReactiveObjC/RACDelegateProxy.h deleted file mode 100644 index 60b94b9ff7..0000000000 --- a/ReactiveObjC/RACDelegateProxy.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// RACDelegateProxy.h -// ReactiveObjC -// -// Created by Cody Krieger on 5/19/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import - -@class RACSignal; - -// A private delegate object suitable for using -// -rac_signalForSelector:fromProtocol: upon. -@interface RACDelegateProxy : NSObject - -// The delegate to which messages should be forwarded if not handled by -// any -signalForSelector: applications. -@property (nonatomic, unsafe_unretained) id rac_proxiedDelegate; - -// Creates a delegate proxy capable of responding to selectors from `protocol`. -- (instancetype)initWithProtocol:(Protocol *)protocol; - -// Calls -rac_signalForSelector:fromProtocol: using the `protocol` specified -// during initialization. -- (RACSignal *)signalForSelector:(SEL)selector; - -@end diff --git a/ReactiveObjC/RACDelegateProxy.m b/ReactiveObjC/RACDelegateProxy.m deleted file mode 100644 index 12a13a6fb5..0000000000 --- a/ReactiveObjC/RACDelegateProxy.m +++ /dev/null @@ -1,76 +0,0 @@ -// -// RACDelegateProxy.m -// ReactiveObjC -// -// Created by Cody Krieger on 5/19/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACDelegateProxy.h" -#import "NSObject+RACSelectorSignal.h" -#import - -@interface RACDelegateProxy () { - // Declared as an ivar to avoid method naming conflicts. - Protocol *_protocol; -} - -@end - -@implementation RACDelegateProxy - -#pragma mark Lifecycle - -- (instancetype)initWithProtocol:(Protocol *)protocol { - NSCParameterAssert(protocol != NULL); - - self = [super init]; - if (self == nil) return nil; - - class_addProtocol(self.class, protocol); - - _protocol = protocol; - - return self; -} - -#pragma mark API - -- (RACSignal *)signalForSelector:(SEL)selector { - return [self rac_signalForSelector:selector fromProtocol:_protocol]; -} - -#pragma mark NSObject - -- (BOOL)isProxy { - return YES; -} - -- (void)forwardInvocation:(NSInvocation *)invocation { - [invocation invokeWithTarget:self.rac_proxiedDelegate]; -} - -- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector { - // Look for the selector as an optional instance method. - struct objc_method_description methodDescription = protocol_getMethodDescription(_protocol, selector, NO, YES); - - if (methodDescription.name == NULL) { - // Then fall back to looking for a required instance - // method. - methodDescription = protocol_getMethodDescription(_protocol, selector, YES, YES); - if (methodDescription.name == NULL) return [super methodSignatureForSelector:selector]; - } - - return [NSMethodSignature signatureWithObjCTypes:methodDescription.types]; -} - -- (BOOL)respondsToSelector:(SEL)selector { - // Add the delegate to the autorelease pool, so it doesn't get deallocated - // between this method call and -forwardInvocation:. - __autoreleasing id delegate = self.rac_proxiedDelegate; - if ([delegate respondsToSelector:selector]) return YES; - - return [super respondsToSelector:selector]; -} - -@end diff --git a/ReactiveObjC/RACDisposable.h b/ReactiveObjC/RACDisposable.h deleted file mode 100644 index 19ba0b6fd8..0000000000 --- a/ReactiveObjC/RACDisposable.h +++ /dev/null @@ -1,35 +0,0 @@ -// -// RACDisposable.h -// ReactiveObjC -// -// Created by Josh Abernathy on 3/16/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import - -@class RACScopedDisposable; - -/// A disposable encapsulates the work necessary to tear down and cleanup a -/// subscription. -@interface RACDisposable : NSObject - -/// Whether the receiver has been disposed. -/// -/// Use of this property is discouraged, since it may be set to `YES` -/// concurrently at any time. -/// -/// This property is not KVO-compliant. -@property (atomic, assign, getter = isDisposed, readonly) BOOL disposed; - -+ (instancetype)disposableWithBlock:(void (^)(void))block; - -/// Performs the disposal work. Can be called multiple times, though subsequent -/// calls won't do anything. -- (void)dispose; - -/// Returns a new disposable which will dispose of this disposable when it gets -/// dealloc'd. -- (RACScopedDisposable *)asScopedDisposable; - -@end diff --git a/ReactiveObjC/RACDisposable.m b/ReactiveObjC/RACDisposable.m deleted file mode 100644 index 6bd569826c..0000000000 --- a/ReactiveObjC/RACDisposable.m +++ /dev/null @@ -1,92 +0,0 @@ -// -// RACDisposable.m -// ReactiveObjC -// -// Created by Josh Abernathy on 3/16/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACDisposable.h" -#import "RACScopedDisposable.h" -#import - -@interface RACDisposable () { - // A copied block of type void (^)(void) containing the logic for disposal, - // a pointer to `self` if no logic should be performed upon disposal, or - // NULL if the receiver is already disposed. - // - // This should only be used atomically. - void * volatile _disposeBlock; -} - -@end - -@implementation RACDisposable - -#pragma mark Properties - -- (BOOL)isDisposed { - return _disposeBlock == NULL; -} - -#pragma mark Lifecycle - -- (id)init { - self = [super init]; - if (self == nil) return nil; - - _disposeBlock = (__bridge void *)self; - OSMemoryBarrier(); - - return self; -} - -- (id)initWithBlock:(void (^)(void))block { - NSCParameterAssert(block != nil); - - self = [super init]; - if (self == nil) return nil; - - _disposeBlock = (void *)CFBridgingRetain([block copy]); - OSMemoryBarrier(); - - return self; -} - -+ (instancetype)disposableWithBlock:(void (^)(void))block { - return [[self alloc] initWithBlock:block]; -} - -- (void)dealloc { - if (_disposeBlock == NULL || _disposeBlock == (__bridge void *)self) return; - - CFRelease(_disposeBlock); - _disposeBlock = NULL; -} - -#pragma mark Disposal - -- (void)dispose { - void (^disposeBlock)(void) = NULL; - - while (YES) { - void *blockPtr = _disposeBlock; - if (OSAtomicCompareAndSwapPtrBarrier(blockPtr, NULL, &_disposeBlock)) { - if (blockPtr != (__bridge void *)self) { - disposeBlock = CFBridgingRelease(blockPtr); - } - - break; - } - } - - if (disposeBlock != nil) disposeBlock(); -} - -#pragma mark Scoped Disposables - -- (RACScopedDisposable *)asScopedDisposable { - return [RACScopedDisposable scopedDisposableWithDisposable:self]; -} - -@end diff --git a/ReactiveObjC/RACDynamicSequence.h b/ReactiveObjC/RACDynamicSequence.h deleted file mode 100644 index 545ea18afe..0000000000 --- a/ReactiveObjC/RACDynamicSequence.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// RACDynamicSequence.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-10-29. -// Copyright (c) 2012 GitHub. All rights reserved. -// - -#import "RACSequence.h" - -// Private class that implements a sequence dynamically using blocks. -@interface RACDynamicSequence : RACSequence - -// Returns a sequence which evaluates `dependencyBlock` only once, the first -// time either `headBlock` or `tailBlock` is evaluated. The result of -// `dependencyBlock` will be passed into `headBlock` and `tailBlock` when -// invoked. -+ (RACSequence *)sequenceWithLazyDependency:(id (^)(void))dependencyBlock headBlock:(id (^)(id dependency))headBlock tailBlock:(RACSequence *(^)(id dependency))tailBlock; - -@end diff --git a/ReactiveObjC/RACDynamicSequence.m b/ReactiveObjC/RACDynamicSequence.m deleted file mode 100644 index 86d8be9c2c..0000000000 --- a/ReactiveObjC/RACDynamicSequence.m +++ /dev/null @@ -1,197 +0,0 @@ -// -// RACDynamicSequence.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-10-29. -// Copyright (c) 2012 GitHub. All rights reserved. -// - -#import "RACDynamicSequence.h" -#import - -// Determines how RACDynamicSequences will be deallocated before the next one is -// shifted onto the autorelease pool. -// -// This avoids stack overflows when deallocating long chains of dynamic -// sequences. -#define DEALLOC_OVERFLOW_GUARD 100 - -@interface RACDynamicSequence () { - // The value for the "head" property, if it's been evaluated already. - // - // Because it's legal for head to be nil, this ivar is valid any time - // headBlock is nil. - // - // This ivar should only be accessed while synchronized on self. - id _head; - - // The value for the "tail" property, if it's been evaluated already. - // - // Because it's legal for tail to be nil, this ivar is valid any time - // tailBlock is nil. - // - // This ivar should only be accessed while synchronized on self. - RACSequence *_tail; - - // The result of an evaluated `dependencyBlock`. - // - // This ivar is valid any time `hasDependency` is YES and `dependencyBlock` - // is nil. - // - // This ivar should only be accessed while synchronized on self. - id _dependency; -} - -// A block used to evaluate head. This should be set to nil after `_head` has been -// initialized. -// -// This is marked `strong` instead of `copy` because of some bizarre block -// copying bug. See https://github.com/ReactiveCocoa/ReactiveCocoa/pull/506. -// -// The signature of this block varies based on the value of `hasDependency`: -// -// - If YES, this block is of type `id (^)(id)`. -// - If NO, this block is of type `id (^)(void)`. -// -// This property should only be accessed while synchronized on self. -@property (nonatomic, strong) id headBlock; - -// A block used to evaluate tail. This should be set to nil after `_tail` has been -// initialized. -// -// This is marked `strong` instead of `copy` because of some bizarre block -// copying bug. See https://github.com/ReactiveCocoa/ReactiveCocoa/pull/506. -// -// The signature of this block varies based on the value of `hasDependency`: -// -// - If YES, this block is of type `RACSequence * (^)(id)`. -// - If NO, this block is of type `RACSequence * (^)(void)`. -// -// This property should only be accessed while synchronized on self. -@property (nonatomic, strong) id tailBlock; - -// Whether the receiver was initialized with a `dependencyBlock`. -// -// This property should only be accessed while synchronized on self. -@property (nonatomic, assign) BOOL hasDependency; - -// A dependency which must be evaluated before `headBlock` and `tailBlock`. This -// should be set to nil after `_dependency` and `dependencyBlockExecuted` have -// been set. -// -// This is marked `strong` instead of `copy` because of some bizarre block -// copying bug. See https://github.com/ReactiveCocoa/ReactiveCocoa/pull/506. -// -// This property should only be accessed while synchronized on self. -@property (nonatomic, strong) id (^dependencyBlock)(void); - -@end - -@implementation RACDynamicSequence - -#pragma mark Lifecycle - -+ (RACSequence *)sequenceWithHeadBlock:(id (^)(void))headBlock tailBlock:(RACSequence *(^)(void))tailBlock { - NSCParameterAssert(headBlock != nil); - - RACDynamicSequence *seq = [[RACDynamicSequence alloc] init]; - seq.headBlock = [headBlock copy]; - seq.tailBlock = [tailBlock copy]; - seq.hasDependency = NO; - return seq; -} - -+ (RACSequence *)sequenceWithLazyDependency:(id (^)(void))dependencyBlock headBlock:(id (^)(id dependency))headBlock tailBlock:(RACSequence *(^)(id dependency))tailBlock { - NSCParameterAssert(dependencyBlock != nil); - NSCParameterAssert(headBlock != nil); - - RACDynamicSequence *seq = [[RACDynamicSequence alloc] init]; - seq.headBlock = [headBlock copy]; - seq.tailBlock = [tailBlock copy]; - seq.dependencyBlock = [dependencyBlock copy]; - seq.hasDependency = YES; - return seq; -} - -- (void)dealloc { - static volatile int32_t directDeallocCount = 0; - - if (OSAtomicIncrement32(&directDeallocCount) >= DEALLOC_OVERFLOW_GUARD) { - OSAtomicAdd32(-DEALLOC_OVERFLOW_GUARD, &directDeallocCount); - - // Put this sequence's tail onto the autorelease pool so we stop - // recursing. - __autoreleasing RACSequence *tail __attribute__((unused)) = _tail; - } - - _tail = nil; -} - -#pragma mark RACSequence - -- (id)head { - @synchronized (self) { - id untypedHeadBlock = self.headBlock; - if (untypedHeadBlock == nil) return _head; - - if (self.hasDependency) { - if (self.dependencyBlock != nil) { - _dependency = self.dependencyBlock(); - self.dependencyBlock = nil; - } - - id (^headBlock)(id) = untypedHeadBlock; - _head = headBlock(_dependency); - } else { - id (^headBlock)(void) = untypedHeadBlock; - _head = headBlock(); - } - - self.headBlock = nil; - return _head; - } -} - -- (RACSequence *)tail { - @synchronized (self) { - id untypedTailBlock = self.tailBlock; - if (untypedTailBlock == nil) return _tail; - - if (self.hasDependency) { - if (self.dependencyBlock != nil) { - _dependency = self.dependencyBlock(); - self.dependencyBlock = nil; - } - - RACSequence * (^tailBlock)(id) = untypedTailBlock; - _tail = tailBlock(_dependency); - } else { - RACSequence * (^tailBlock)(void) = untypedTailBlock; - _tail = tailBlock(); - } - - if (_tail.name == nil) _tail.name = self.name; - - self.tailBlock = nil; - return _tail; - } -} - -#pragma mark NSObject - -- (NSString *)description { - id head = @"(unresolved)"; - id tail = @"(unresolved)"; - - @synchronized (self) { - if (self.headBlock == nil) head = _head; - if (self.tailBlock == nil) { - tail = _tail; - if (tail == self) tail = @"(self)"; - } - } - - return [NSString stringWithFormat:@"<%@: %p>{ name = %@, head = %@, tail = %@ }", self.class, self, self.name, head, tail]; -} - -@end diff --git a/ReactiveObjC/RACDynamicSignal.h b/ReactiveObjC/RACDynamicSignal.h deleted file mode 100644 index cadd7bc02b..0000000000 --- a/ReactiveObjC/RACDynamicSignal.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// RACDynamicSignal.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-10-10. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACSignal.h" - -// A private `RACSignal` subclasses that implements its subscription behavior -// using a block. -@interface RACDynamicSignal : RACSignal - -+ (RACSignal *)createSignal:(RACDisposable * (^)(id subscriber))didSubscribe; - -@end diff --git a/ReactiveObjC/RACDynamicSignal.m b/ReactiveObjC/RACDynamicSignal.m deleted file mode 100644 index 3bee2f7c2b..0000000000 --- a/ReactiveObjC/RACDynamicSignal.m +++ /dev/null @@ -1,54 +0,0 @@ -// -// RACDynamicSignal.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-10-10. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACDynamicSignal.h" -#import -#import "RACCompoundDisposable.h" -#import "RACPassthroughSubscriber.h" -#import "RACScheduler+Private.h" -#import "RACSubscriber.h" -#import - -@interface RACDynamicSignal () - -// The block to invoke for each subscriber. -@property (nonatomic, copy, readonly) RACDisposable * (^didSubscribe)(id subscriber); - -@end - -@implementation RACDynamicSignal - -#pragma mark Lifecycle - -+ (RACSignal *)createSignal:(RACDisposable * (^)(id subscriber))didSubscribe { - RACDynamicSignal *signal = [[self alloc] init]; - signal->_didSubscribe = [didSubscribe copy]; - return [signal setNameWithFormat:@"+createSignal:"]; -} - -#pragma mark Managing Subscribers - -- (RACDisposable *)subscribe:(id)subscriber { - NSCParameterAssert(subscriber != nil); - - RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable]; - subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable]; - - if (self.didSubscribe != NULL) { - RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{ - RACDisposable *innerDisposable = self.didSubscribe(subscriber); - [disposable addDisposable:innerDisposable]; - }]; - - [disposable addDisposable:schedulingDisposable]; - } - - return disposable; -} - -@end diff --git a/ReactiveObjC/RACEagerSequence.h b/ReactiveObjC/RACEagerSequence.h deleted file mode 100644 index 04ddf7e34b..0000000000 --- a/ReactiveObjC/RACEagerSequence.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// RACEagerSequence.h -// ReactiveObjC -// -// Created by Uri Baghin on 02/01/2013. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACArraySequence.h" - -// Private class that implements an eager sequence. -@interface RACEagerSequence : RACArraySequence - -@end diff --git a/ReactiveObjC/RACEagerSequence.m b/ReactiveObjC/RACEagerSequence.m deleted file mode 100644 index 56815ec993..0000000000 --- a/ReactiveObjC/RACEagerSequence.m +++ /dev/null @@ -1,66 +0,0 @@ -// -// RACEagerSequence.m -// ReactiveObjC -// -// Created by Uri Baghin on 02/01/2013. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACEagerSequence.h" -#import "NSObject+RACDescription.h" -#import "RACArraySequence.h" - -@implementation RACEagerSequence - -#pragma mark RACStream - -+ (instancetype)return:(id)value { - return [[self sequenceWithArray:@[ value ] offset:0] setNameWithFormat:@"+return: %@", RACDescription(value)]; -} - -- (instancetype)bind:(RACStreamBindBlock (^)(void))block { - NSCParameterAssert(block != nil); - RACStreamBindBlock bindBlock = block(); - NSArray *currentArray = self.array; - NSMutableArray *resultArray = [NSMutableArray arrayWithCapacity:currentArray.count]; - - for (id value in currentArray) { - BOOL stop = NO; - RACSequence *boundValue = (id)bindBlock(value, &stop); - if (boundValue == nil) break; - - for (id x in boundValue) { - [resultArray addObject:x]; - } - - if (stop) break; - } - - return [[self.class sequenceWithArray:resultArray offset:0] setNameWithFormat:@"[%@] -bind:", self.name]; -} - -- (instancetype)concat:(RACSequence *)sequence { - NSCParameterAssert(sequence != nil); - NSCParameterAssert([sequence isKindOfClass:RACSequence.class]); - - NSArray *array = [self.array arrayByAddingObjectsFromArray:sequence.array]; - return [[self.class sequenceWithArray:array offset:0] setNameWithFormat:@"[%@] -concat: %@", self.name, sequence]; -} - -#pragma mark Extended methods - -- (RACSequence *)eagerSequence { - return self; -} - -- (RACSequence *)lazySequence { - return [RACArraySequence sequenceWithArray:self.array offset:0]; -} - -- (id)foldRightWithStart:(id)start reduce:(id (^)(id, RACSequence *rest))reduce { - return [super foldRightWithStart:start reduce:^(id first, RACSequence *rest) { - return reduce(first, rest.eagerSequence); - }]; -} - -@end diff --git a/ReactiveObjC/RACEmptySequence.h b/ReactiveObjC/RACEmptySequence.h deleted file mode 100644 index ea91e41c82..0000000000 --- a/ReactiveObjC/RACEmptySequence.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// RACEmptySequence.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-10-29. -// Copyright (c) 2012 GitHub. All rights reserved. -// - -#import "RACSequence.h" - -// Private class representing an empty sequence. -@interface RACEmptySequence : RACSequence - -@end diff --git a/ReactiveObjC/RACEmptySequence.m b/ReactiveObjC/RACEmptySequence.m deleted file mode 100644 index 7413ac24e0..0000000000 --- a/ReactiveObjC/RACEmptySequence.m +++ /dev/null @@ -1,71 +0,0 @@ -// -// RACEmptySequence.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-10-29. -// Copyright (c) 2012 GitHub. All rights reserved. -// - -#import "RACEmptySequence.h" - -@implementation RACEmptySequence - -#pragma mark Lifecycle - -+ (instancetype)empty { - static id singleton; - static dispatch_once_t pred; - - dispatch_once(&pred, ^{ - singleton = [[self alloc] init]; - }); - - return singleton; -} - -#pragma mark RACSequence - -- (id)head { - return nil; -} - -- (RACSequence *)tail { - return nil; -} - -- (RACSequence *)bind:(RACStreamBindBlock)bindBlock passingThroughValuesFromSequence:(RACSequence *)passthroughSequence { - return passthroughSequence ?: self; -} - -#pragma mark NSCoding - -- (Class)classForCoder { - // Empty sequences should be encoded as themselves, not array sequences. - return self.class; -} - -- (id)initWithCoder:(NSCoder *)coder { - // Return the singleton. - return self.class.empty; -} - -- (void)encodeWithCoder:(NSCoder *)coder { -} - -#pragma mark NSObject - -- (NSString *)description { - return [NSString stringWithFormat:@"<%@: %p>{ name = %@ }", self.class, self, self.name]; -} - -- (NSUInteger)hash { - // This hash isn't ideal, but it's better than -[RACSequence hash], which - // would just be zero because we have no head. - return (NSUInteger)(__bridge void *)self; -} - -- (BOOL)isEqual:(RACSequence *)seq { - return (self == seq); -} - -@end diff --git a/ReactiveObjC/RACEmptySignal.h b/ReactiveObjC/RACEmptySignal.h deleted file mode 100644 index 854749f00b..0000000000 --- a/ReactiveObjC/RACEmptySignal.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// RACEmptySignal.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-10-10. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACSignal.h" - -// A private `RACSignal` subclasses that synchronously sends completed to any -// subscribers. -@interface RACEmptySignal : RACSignal - -+ (RACSignal *)empty; - -@end diff --git a/ReactiveObjC/RACEmptySignal.m b/ReactiveObjC/RACEmptySignal.m deleted file mode 100644 index d47ac27336..0000000000 --- a/ReactiveObjC/RACEmptySignal.m +++ /dev/null @@ -1,62 +0,0 @@ -// -// RACEmptySignal.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-10-10. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACEmptySignal.h" -#import "RACScheduler+Private.h" -#import "RACSubscriber.h" - -@implementation RACEmptySignal - -#pragma mark Properties - -// Only allow this signal's name to be customized in DEBUG, since it's -// a singleton in release builds (see +empty). -- (void)setName:(NSString *)name { -#ifdef DEBUG - [super setName:name]; -#endif -} - -- (NSString *)name { -#ifdef DEBUG - return super.name; -#else - return @"+empty"; -#endif -} - -#pragma mark Lifecycle - -+ (RACSignal *)empty { -#ifdef DEBUG - // Create multiple instances of this class in DEBUG so users can set custom - // names on each. - return [[[self alloc] init] setNameWithFormat:@"+empty"]; -#else - static id singleton; - static dispatch_once_t pred; - - dispatch_once(&pred, ^{ - singleton = [[self alloc] init]; - }); - - return singleton; -#endif -} - -#pragma mark Subscription - -- (RACDisposable *)subscribe:(id)subscriber { - NSCParameterAssert(subscriber != nil); - - return [RACScheduler.subscriptionScheduler schedule:^{ - [subscriber sendCompleted]; - }]; -} - -@end diff --git a/ReactiveObjC/RACErrorSignal.h b/ReactiveObjC/RACErrorSignal.h deleted file mode 100644 index fdb600ecf0..0000000000 --- a/ReactiveObjC/RACErrorSignal.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// RACErrorSignal.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-10-10. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACSignal.h" - -// A private `RACSignal` subclasses that synchronously sends an error to any -// subscribers. -@interface RACErrorSignal : RACSignal - -+ (RACSignal *)error:(NSError *)error; - -@end diff --git a/ReactiveObjC/RACErrorSignal.m b/ReactiveObjC/RACErrorSignal.m deleted file mode 100644 index 1d08a2f930..0000000000 --- a/ReactiveObjC/RACErrorSignal.m +++ /dev/null @@ -1,47 +0,0 @@ -// -// RACErrorSignal.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-10-10. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACErrorSignal.h" -#import "RACScheduler+Private.h" -#import "RACSubscriber.h" - -@interface RACErrorSignal () - -// The error to send upon subscription. -@property (nonatomic, strong, readonly) NSError *error; - -@end - -@implementation RACErrorSignal - -#pragma mark Lifecycle - -+ (RACSignal *)error:(NSError *)error { - RACErrorSignal *signal = [[self alloc] init]; - signal->_error = error; - -#ifdef DEBUG - [signal setNameWithFormat:@"+error: %@", error]; -#else - signal.name = @"+error:"; -#endif - - return signal; -} - -#pragma mark Subscription - -- (RACDisposable *)subscribe:(id)subscriber { - NSCParameterAssert(subscriber != nil); - - return [RACScheduler.subscriptionScheduler schedule:^{ - [subscriber sendError:self.error]; - }]; -} - -@end diff --git a/ReactiveObjC/RACEvent.h b/ReactiveObjC/RACEvent.h deleted file mode 100644 index 3b2ce96016..0000000000 --- a/ReactiveObjC/RACEvent.h +++ /dev/null @@ -1,51 +0,0 @@ -// -// RACEvent.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-01-07. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -/// Describes the type of a RACEvent. -/// -/// RACEventTypeCompleted - A `completed` event. -/// RACEventTypeError - An `error` event. -/// RACEventTypeNext - A `next` event. -typedef NS_ENUM(NSUInteger, RACEventType) { - RACEventTypeCompleted, - RACEventTypeError, - RACEventTypeNext -}; - -/// Represents an event sent by a RACSignal. -/// -/// This corresponds to the `Notification` class in Rx. -@interface RACEvent : NSObject - -/// Returns a singleton RACEvent representing the `completed` event. -+ (instancetype)completedEvent; - -/// Returns a new event of type RACEventTypeError, containing the given error. -+ (instancetype)eventWithError:(NSError *)error; - -/// Returns a new event of type RACEventTypeNext, containing the given value. -+ (instancetype)eventWithValue:(id)value; - -/// The type of event represented by the receiver. -@property (nonatomic, assign, readonly) RACEventType eventType; - -/// Returns whether the receiver is of type RACEventTypeCompleted or -/// RACEventTypeError. -@property (nonatomic, getter = isFinished, assign, readonly) BOOL finished; - -/// The error associated with an event of type RACEventTypeError. This will be -/// nil for all other event types. -@property (nonatomic, strong, readonly) NSError *error; - -/// The value associated with an event of type RACEventTypeNext. This will be -/// nil for all other event types. -@property (nonatomic, strong, readonly) id value; - -@end diff --git a/ReactiveObjC/RACEvent.m b/ReactiveObjC/RACEvent.m deleted file mode 100644 index da1e185434..0000000000 --- a/ReactiveObjC/RACEvent.m +++ /dev/null @@ -1,113 +0,0 @@ -// -// RACEvent.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-01-07. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACEvent.h" - -@interface RACEvent () - -// An object associated with this event. This will be used for the error and -// value properties. -@property (nonatomic, strong, readonly) id object; - -// Initializes the receiver with the given type and object. -- (id)initWithEventType:(RACEventType)type object:(id)object; - -@end - -@implementation RACEvent - -#pragma mark Properties - -- (BOOL)isFinished { - return self.eventType == RACEventTypeCompleted || self.eventType == RACEventTypeError; -} - -- (NSError *)error { - return (self.eventType == RACEventTypeError ? self.object : nil); -} - -- (id)value { - return (self.eventType == RACEventTypeNext ? self.object : nil); -} - -#pragma mark Lifecycle - -+ (instancetype)completedEvent { - static dispatch_once_t pred; - static id singleton; - - dispatch_once(&pred, ^{ - singleton = [[self alloc] initWithEventType:RACEventTypeCompleted object:nil]; - }); - - return singleton; -} - -+ (instancetype)eventWithError:(NSError *)error { - return [[self alloc] initWithEventType:RACEventTypeError object:error]; -} - -+ (instancetype)eventWithValue:(id)value { - return [[self alloc] initWithEventType:RACEventTypeNext object:value]; -} - -- (id)initWithEventType:(RACEventType)type object:(id)object { - self = [super init]; - if (self == nil) return nil; - - _eventType = type; - _object = object; - - return self; -} - -#pragma mark NSCopying - -- (id)copyWithZone:(NSZone *)zone { - return self; -} - -#pragma mark NSObject - -- (NSString *)description { - NSString *eventDescription = nil; - - switch (self.eventType) { - case RACEventTypeCompleted: - eventDescription = @"completed"; - break; - - case RACEventTypeError: - eventDescription = [NSString stringWithFormat:@"error = %@", self.object]; - break; - - case RACEventTypeNext: - eventDescription = [NSString stringWithFormat:@"next = %@", self.object]; - break; - - default: - NSCAssert(NO, @"Unrecognized event type: %i", (int)self.eventType); - } - - return [NSString stringWithFormat:@"<%@: %p>{ %@ }", self.class, self, eventDescription]; -} - -- (NSUInteger)hash { - return self.eventType ^ [self.object hash]; -} - -- (BOOL)isEqual:(id)event { - if (event == self) return YES; - if (![event isKindOfClass:RACEvent.class]) return NO; - if (self.eventType != [event eventType]) return NO; - - // Catches the nil case too. - return self.object == [event object] || [self.object isEqual:[event object]]; -} - -@end diff --git a/ReactiveObjC/RACGroupedSignal.h b/ReactiveObjC/RACGroupedSignal.h deleted file mode 100644 index ef342874c2..0000000000 --- a/ReactiveObjC/RACGroupedSignal.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// RACGroupedSignal.h -// ReactiveObjC -// -// Created by Josh Abernathy on 5/2/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACSubject.h" - -/// A grouped signal is used by -[RACSignal groupBy:transform:]. -@interface RACGroupedSignal : RACSubject - -/// The key shared by the group. -@property (nonatomic, readonly, copy) id key; - -+ (instancetype)signalWithKey:(id)key; - -@end diff --git a/ReactiveObjC/RACGroupedSignal.m b/ReactiveObjC/RACGroupedSignal.m deleted file mode 100644 index a391ef25bd..0000000000 --- a/ReactiveObjC/RACGroupedSignal.m +++ /dev/null @@ -1,25 +0,0 @@ -// -// RACGroupedSignal.m -// ReactiveObjC -// -// Created by Josh Abernathy on 5/2/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACGroupedSignal.h" - -@interface RACGroupedSignal () -@property (nonatomic, copy) id key; -@end - -@implementation RACGroupedSignal - -#pragma mark API - -+ (instancetype)signalWithKey:(id)key { - RACGroupedSignal *subject = [self subject]; - subject.key = key; - return subject; -} - -@end diff --git a/ReactiveObjC/RACImmediateScheduler.h b/ReactiveObjC/RACImmediateScheduler.h deleted file mode 100644 index edafe062b0..0000000000 --- a/ReactiveObjC/RACImmediateScheduler.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// RACImmediateScheduler.h -// ReactiveObjC -// -// Created by Josh Abernathy on 11/30/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACScheduler.h" - -// A private scheduler which immediately executes its scheduled blocks. -@interface RACImmediateScheduler : RACScheduler - -@end diff --git a/ReactiveObjC/RACImmediateScheduler.m b/ReactiveObjC/RACImmediateScheduler.m deleted file mode 100644 index 920a29fdf7..0000000000 --- a/ReactiveObjC/RACImmediateScheduler.m +++ /dev/null @@ -1,54 +0,0 @@ -// -// RACImmediateScheduler.m -// ReactiveObjC -// -// Created by Josh Abernathy on 11/30/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACImmediateScheduler.h" -#import "RACScheduler+Private.h" - -@implementation RACImmediateScheduler - -#pragma mark Lifecycle - -- (id)init { - return [super initWithName:@"org.reactivecocoa.ReactiveObjC.RACScheduler.immediateScheduler"]; -} - -#pragma mark RACScheduler - -- (RACDisposable *)schedule:(void (^)(void))block { - NSCParameterAssert(block != NULL); - - block(); - return nil; -} - -- (RACDisposable *)after:(NSDate *)date schedule:(void (^)(void))block { - NSCParameterAssert(date != nil); - NSCParameterAssert(block != NULL); - - [NSThread sleepUntilDate:date]; - block(); - - return nil; -} - -- (RACDisposable *)after:(NSDate *)date repeatingEvery:(NSTimeInterval)interval withLeeway:(NSTimeInterval)leeway schedule:(void (^)(void))block { - NSCAssert(NO, @"+[RACScheduler immediateScheduler] does not support %@.", NSStringFromSelector(_cmd)); - return nil; -} - -- (RACDisposable *)scheduleRecursiveBlock:(RACSchedulerRecursiveBlock)recursiveBlock { - for (__block NSUInteger remaining = 1; remaining > 0; remaining--) { - recursiveBlock(^{ - remaining++; - }); - } - - return nil; -} - -@end diff --git a/ReactiveObjC/RACIndexSetSequence.h b/ReactiveObjC/RACIndexSetSequence.h deleted file mode 100644 index 020dbb27f3..0000000000 --- a/ReactiveObjC/RACIndexSetSequence.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// RACIndexSetSequence.h -// ReactiveObjC -// -// Created by Sergey Gavrilyuk on 12/18/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACSequence.h" - -// Private class that adapts an array to the RACSequence interface. -@interface RACIndexSetSequence : RACSequence - -+ (instancetype)sequenceWithIndexSet:(NSIndexSet *)indexSet; - -@end diff --git a/ReactiveObjC/RACIndexSetSequence.m b/ReactiveObjC/RACIndexSetSequence.m deleted file mode 100644 index 2fe98a6eae..0000000000 --- a/ReactiveObjC/RACIndexSetSequence.m +++ /dev/null @@ -1,111 +0,0 @@ -// -// RACIndexSetSequence.m -// ReactiveObjC -// -// Created by Sergey Gavrilyuk on 12/18/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACIndexSetSequence.h" - -@interface RACIndexSetSequence () - -// A buffer holding the `NSUInteger` values to enumerate over. -// -// This is mostly used for memory management. Most access should go through -// `indexes` instead. -@property (nonatomic, strong, readonly) NSData *data; - -// The indexes that this sequence should enumerate. -@property (nonatomic, readonly) const NSUInteger *indexes; - -// The number of indexes to enumerate. -@property (nonatomic, readonly) NSUInteger count; - -@end - -@implementation RACIndexSetSequence - -#pragma mark Lifecycle - -+ (instancetype)sequenceWithIndexSet:(NSIndexSet *)indexSet { - NSUInteger count = indexSet.count; - - if (count == 0) return self.empty; - - NSUInteger sizeInBytes = sizeof(NSUInteger) * count; - - NSMutableData *data = [[NSMutableData alloc] initWithCapacity:sizeInBytes]; - [indexSet getIndexes:data.mutableBytes maxCount:count inIndexRange:NULL]; - - RACIndexSetSequence *seq = [[self alloc] init]; - seq->_data = data; - seq->_indexes = data.bytes; - seq->_count = count; - return seq; -} - -+ (instancetype)sequenceWithIndexSetSequence:(RACIndexSetSequence *)indexSetSequence offset:(NSUInteger)offset { - NSCParameterAssert(offset < indexSetSequence.count); - - RACIndexSetSequence *seq = [[self alloc] init]; - seq->_data = indexSetSequence.data; - seq->_indexes = indexSetSequence.indexes + offset; - seq->_count = indexSetSequence.count - offset; - return seq; -} - -#pragma mark RACSequence - -- (id)head { - return @(self.indexes[0]); -} - -- (RACSequence *)tail { - if (self.count <= 1) return [RACSequence empty]; - - return [self.class sequenceWithIndexSetSequence:self offset:1]; -} - -#pragma mark NSFastEnumeration - -- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(__unsafe_unretained id[])stackbuf count:(NSUInteger)len { - NSCParameterAssert(len > 0); - - if (state->state >= self.count) { - // Enumeration has completed. - return 0; - } - - if (state->state == 0) { - // Enumeration begun, mark the mutation flag. - state->mutationsPtr = state->extra; - } - - state->itemsPtr = stackbuf; - - unsigned long index = 0; - while (index < MIN(self.count - state->state, len)) { - stackbuf[index] = @(self.indexes[index + state->state]); - ++index; - } - - state->state += index; - return index; -} - -#pragma mark NSObject - -- (NSString *)description { - NSMutableString *indexesStr = [NSMutableString string]; - - for (unsigned int i = 0; i < self.count; ++i) { - if (i > 0) [indexesStr appendString:@", "]; - - [indexesStr appendFormat:@"%lu", (unsigned long)self.indexes[i]]; - } - - return [NSString stringWithFormat:@"<%@: %p>{ name = %@, indexes = %@ }", self.class, self, self.name, indexesStr]; -} - -@end diff --git a/ReactiveObjC/RACKVOChannel.h b/ReactiveObjC/RACKVOChannel.h deleted file mode 100644 index 0cc6efc8db..0000000000 --- a/ReactiveObjC/RACKVOChannel.h +++ /dev/null @@ -1,102 +0,0 @@ -// -// RACKVOChannel.h -// ReactiveObjC -// -// Created by Uri Baghin on 27/12/2012. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACChannel.h" -#import -#import "metamacros.h" - -/// Creates a RACKVOChannel to the given key path. When the targeted object -/// deallocates, the channel will complete. -/// -/// If RACChannelTo() is used as an expression, it returns a RACChannelTerminal that -/// can be used to watch the specified property for changes, and set new values -/// for it. The terminal will start with the property's current value upon -/// subscription. -/// -/// If RACChannelTo() is used on the left-hand side of an assignment, there must a -/// RACChannelTerminal on the right-hand side of the assignment. The two will be -/// subscribed to one another: the property's value is immediately set to the -/// value of the channel terminal on the right-hand side, and subsequent changes -/// to either terminal will be reflected on the other. -/// -/// There are two different versions of this macro: -/// -/// - RACChannelTo(TARGET, KEYPATH, NILVALUE) will create a channel to the `KEYPATH` -/// of `TARGET`. If the terminal is ever sent a `nil` value, the property will -/// be set to `NILVALUE` instead. `NILVALUE` may itself be `nil` for object -/// properties, but an NSValue should be used for primitive properties, to -/// avoid an exception if `nil` is sent (which might occur if an intermediate -/// object is set to `nil`). -/// - RACChannelTo(TARGET, KEYPATH) is the same as the above, but `NILVALUE` defaults to -/// `nil`. -/// -/// Examples -/// -/// RACChannelTerminal *integerChannel = RACChannelTo(self, integerProperty, @42); -/// -/// // Sets self.integerProperty to 5. -/// [integerChannel sendNext:@5]; -/// -/// // Logs the current value of self.integerProperty, and all future changes. -/// [integerChannel subscribeNext:^(id value) { -/// NSLog(@"value: %@", value); -/// }]; -/// -/// // Binds properties to each other, taking the initial value from the right -/// side. -/// RACChannelTo(view, objectProperty) = RACChannelTo(model, objectProperty); -/// RACChannelTo(view, integerProperty, @2) = RACChannelTo(model, integerProperty, @10); -#define RACChannelTo(TARGET, ...) \ - metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__)) \ - (RACChannelTo_(TARGET, __VA_ARGS__, nil)) \ - (RACChannelTo_(TARGET, __VA_ARGS__)) - -/// Do not use this directly. Use the RACChannelTo macro above. -#define RACChannelTo_(TARGET, KEYPATH, NILVALUE) \ - [[RACKVOChannel alloc] initWithTarget:(TARGET) keyPath:@keypath(TARGET, KEYPATH) nilValue:(NILVALUE)][@keypath(RACKVOChannel.new, followingTerminal)] - -/// A RACChannel that observes a KVO-compliant key path for changes. -@interface RACKVOChannel : RACChannel - -/// Initializes a channel that will observe the given object and key path. -/// -/// The current value of the key path, and future KVO notifications for the given -/// key path, will be sent to subscribers of the channel's `followingTerminal`. -/// Values sent to the `followingTerminal` will be set at the given key path using -/// key-value coding. -/// -/// When the target object deallocates, the channel will complete. Signal errors -/// are considered undefined behavior. -/// -/// This is the designated initializer for this class. -/// -/// target - The object to bind to. -/// keyPath - The key path to observe and set the value of. -/// nilValue - The value to set at the key path whenever a `nil` value is -/// received. This may be nil when connecting to object properties, but -/// an NSValue should be used for primitive properties, to avoid an -/// exception if `nil` is received (which might occur if an intermediate -/// object is set to `nil`). -#if OS_OBJECT_HAVE_OBJC_SUPPORT -- (id)initWithTarget:(__weak NSObject *)target keyPath:(NSString *)keyPath nilValue:(id)nilValue; -#else -// Swift builds with OS_OBJECT_HAVE_OBJC_SUPPORT=0 for Playgrounds and LLDB :( -- (id)initWithTarget:(NSObject *)target keyPath:(NSString *)keyPath nilValue:(id)nilValue; -#endif - -- (id)init __attribute__((unavailable("Use -initWithTarget:keyPath:nilValue: instead"))); - -@end - -/// Methods needed for the convenience macro. Do not call explicitly. -@interface RACKVOChannel (RACChannelTo) - -- (RACChannelTerminal *)objectForKeyedSubscript:(NSString *)key; -- (void)setObject:(RACChannelTerminal *)otherTerminal forKeyedSubscript:(NSString *)key; - -@end diff --git a/ReactiveObjC/RACKVOChannel.m b/ReactiveObjC/RACKVOChannel.m deleted file mode 100644 index 0b3ad89731..0000000000 --- a/ReactiveObjC/RACKVOChannel.m +++ /dev/null @@ -1,208 +0,0 @@ -// -// RACKVOChannel.m -// ReactiveObjC -// -// Created by Uri Baghin on 27/12/2012. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACKVOChannel.h" -#import -#import "NSObject+RACDeallocating.h" -#import "NSObject+RACKVOWrapper.h" -#import "NSString+RACKeyPathUtilities.h" -#import "RACChannel.h" -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACSignal+Operations.h" - -// Key for the array of RACKVOChannel's additional thread local -// data in the thread dictionary. -static NSString * const RACKVOChannelDataDictionaryKey = @"RACKVOChannelKey"; - -// Wrapper class for additional thread local data. -@interface RACKVOChannelData : NSObject - -// The flag used to ignore updates the channel itself has triggered. -@property (nonatomic, assign) BOOL ignoreNextUpdate; - -// A pointer to the owner of the data. Only use this for pointer comparison, -// never as an object reference. -@property (nonatomic, assign) void *owner; - -+ (instancetype)dataForChannel:(RACKVOChannel *)channel; - -@end - -@interface RACKVOChannel () - -// The object whose key path the channel is wrapping. -@property (atomic, weak) NSObject *target; - -// The key path the channel is wrapping. -@property (nonatomic, copy, readonly) NSString *keyPath; - -// Returns the existing thread local data container or nil if none exists. -@property (nonatomic, strong, readonly) RACKVOChannelData *currentThreadData; - -// Creates the thread local data container for the channel. -- (void)createCurrentThreadData; - -// Destroy the thread local data container for the channel. -- (void)destroyCurrentThreadData; - -@end - -@implementation RACKVOChannel - -#pragma mark Properties - -- (RACKVOChannelData *)currentThreadData { - NSMutableArray *dataArray = NSThread.currentThread.threadDictionary[RACKVOChannelDataDictionaryKey]; - - for (RACKVOChannelData *data in dataArray) { - if (data.owner == (__bridge void *)self) return data; - } - - return nil; -} - -#pragma mark Lifecycle - -- (id)initWithTarget:(__weak NSObject *)target keyPath:(NSString *)keyPath nilValue:(id)nilValue { - NSCParameterAssert(keyPath.rac_keyPathComponents.count > 0); - - NSObject *strongTarget = target; - - self = [super init]; - if (self == nil) return nil; - - _target = target; - _keyPath = [keyPath copy]; - - [self.leadingTerminal setNameWithFormat:@"[-initWithTarget: %@ keyPath: %@ nilValue: %@] -leadingTerminal", target, keyPath, nilValue]; - [self.followingTerminal setNameWithFormat:@"[-initWithTarget: %@ keyPath: %@ nilValue: %@] -followingTerminal", target, keyPath, nilValue]; - - if (strongTarget == nil) { - [self.leadingTerminal sendCompleted]; - return self; - } - - // Observe the key path on target for changes and forward the changes to the - // terminal. - // - // Intentionally capturing `self` strongly in the blocks below, so the - // channel object stays alive while observing. - RACDisposable *observationDisposable = [strongTarget rac_observeKeyPath:keyPath options:NSKeyValueObservingOptionInitial observer:nil block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) { - // If the change wasn't triggered by deallocation, only affects the last - // path component, and ignoreNextUpdate is set, then it was triggered by - // this channel and should not be forwarded. - if (!causedByDealloc && affectedOnlyLastComponent && self.currentThreadData.ignoreNextUpdate) { - [self destroyCurrentThreadData]; - return; - } - - [self.leadingTerminal sendNext:value]; - }]; - - NSString *keyPathByDeletingLastKeyPathComponent = keyPath.rac_keyPathByDeletingLastKeyPathComponent; - NSArray *keyPathComponents = keyPath.rac_keyPathComponents; - NSUInteger keyPathComponentsCount = keyPathComponents.count; - NSString *lastKeyPathComponent = keyPathComponents.lastObject; - - // Update the value of the property with the values received. - [[self.leadingTerminal - finally:^{ - [observationDisposable dispose]; - }] - subscribeNext:^(id x) { - // Check the value of the second to last key path component. Since the - // channel can only update the value of a property on an object, and not - // update intermediate objects, it can only update the value of the whole - // key path if this object is not nil. - NSObject *object = (keyPathComponentsCount > 1 ? [self.target valueForKeyPath:keyPathByDeletingLastKeyPathComponent] : self.target); - if (object == nil) return; - - // Set the ignoreNextUpdate flag before setting the value so this channel - // ignores the value in the subsequent -didChangeValueForKey: callback. - [self createCurrentThreadData]; - self.currentThreadData.ignoreNextUpdate = YES; - - [object setValue:x ?: nilValue forKey:lastKeyPathComponent]; - } error:^(NSError *error) { - NSCAssert(NO, @"Received error in %@: %@", self, error); - - // Log the error if we're running with assertions disabled. - NSLog(@"Received error in %@: %@", self, error); - }]; - - // Capture `self` weakly for the target's deallocation disposable, so we can - // freely deallocate if we complete before then. - @weakify(self); - - [strongTarget.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - @strongify(self); - [self.leadingTerminal sendCompleted]; - self.target = nil; - }]]; - - return self; -} - -- (void)createCurrentThreadData { - NSMutableArray *dataArray = NSThread.currentThread.threadDictionary[RACKVOChannelDataDictionaryKey]; - if (dataArray == nil) { - dataArray = [NSMutableArray array]; - NSThread.currentThread.threadDictionary[RACKVOChannelDataDictionaryKey] = dataArray; - [dataArray addObject:[RACKVOChannelData dataForChannel:self]]; - return; - } - - for (RACKVOChannelData *data in dataArray) { - if (data.owner == (__bridge void *)self) return; - } - - [dataArray addObject:[RACKVOChannelData dataForChannel:self]]; -} - -- (void)destroyCurrentThreadData { - NSMutableArray *dataArray = NSThread.currentThread.threadDictionary[RACKVOChannelDataDictionaryKey]; - NSUInteger index = [dataArray indexOfObjectPassingTest:^ BOOL (RACKVOChannelData *data, NSUInteger idx, BOOL *stop) { - return data.owner == (__bridge void *)self; - }]; - - if (index != NSNotFound) [dataArray removeObjectAtIndex:index]; -} - -@end - -@implementation RACKVOChannel (RACChannelTo) - -- (RACChannelTerminal *)objectForKeyedSubscript:(NSString *)key { - NSCParameterAssert(key != nil); - - RACChannelTerminal *terminal = [self valueForKey:key]; - NSCAssert([terminal isKindOfClass:RACChannelTerminal.class], @"Key \"%@\" does not identify a channel terminal", key); - - return terminal; -} - -- (void)setObject:(RACChannelTerminal *)otherTerminal forKeyedSubscript:(NSString *)key { - NSCParameterAssert(otherTerminal != nil); - - RACChannelTerminal *selfTerminal = [self objectForKeyedSubscript:key]; - [otherTerminal subscribe:selfTerminal]; - [[selfTerminal skip:1] subscribe:otherTerminal]; -} - -@end - -@implementation RACKVOChannelData - -+ (instancetype)dataForChannel:(RACKVOChannel *)channel { - RACKVOChannelData *data = [[self alloc] init]; - data->_owner = (__bridge void *)channel; - return data; -} - -@end diff --git a/ReactiveObjC/RACKVOProxy.h b/ReactiveObjC/RACKVOProxy.h deleted file mode 100644 index 7e49793600..0000000000 --- a/ReactiveObjC/RACKVOProxy.h +++ /dev/null @@ -1,34 +0,0 @@ -// -// RACKVOProxy.h -// ReactiveObjC -// -// Created by Richard Speyer on 4/10/14. -// Copyright (c) 2014 GitHub, Inc. All rights reserved. -// - -#import - -/// A singleton that can act as a proxy between a KVO observation and a RAC -/// subscriber, in order to protect against KVO lifetime issues. -@interface RACKVOProxy : NSObject - -/// Returns the singleton KVO proxy object. -+ (instancetype)sharedProxy; - -/// Registers an observer with the proxy, such that when the proxy receives a -/// KVO change with the given context, it forwards it to the observer. -/// -/// observer - True observer of the KVO change. Must not be nil. -/// context - Arbitrary context object used to differentiate multiple -/// observations of the same keypath. Must be unique, cannot be nil. -- (void)addObserver:(__weak NSObject *)observer forContext:(void *)context; - -/// Removes an observer from the proxy. Parameters must match those passed to -/// addObserver:forContext:. -/// -/// observer - True observer of the KVO change. Must not be nil. -/// context - Arbitrary context object used to differentiate multiple -/// observations of the same keypath. Must be unique, cannot be nil. -- (void)removeObserver:(NSObject *)observer forContext:(void *)context; - -@end diff --git a/ReactiveObjC/RACKVOProxy.m b/ReactiveObjC/RACKVOProxy.m deleted file mode 100644 index c0b7debecb..0000000000 --- a/ReactiveObjC/RACKVOProxy.m +++ /dev/null @@ -1,70 +0,0 @@ -// -// RACKVOProxy.m -// ReactiveObjC -// -// Created by Richard Speyer on 4/10/14. -// Copyright (c) 2014 GitHub, Inc. All rights reserved. -// - -#import "RACKVOProxy.h" - -@interface RACKVOProxy() - -@property (strong, nonatomic, readonly) NSMapTable *trampolines; -@property (strong, nonatomic, readonly) dispatch_queue_t queue; - -@end - -@implementation RACKVOProxy - -+ (instancetype)sharedProxy { - static RACKVOProxy *proxy; - static dispatch_once_t onceToken; - - dispatch_once(&onceToken, ^{ - proxy = [[self alloc] init]; - }); - - return proxy; -} - -- (instancetype)init { - self = [super init]; - if (self == nil) return nil; - - _queue = dispatch_queue_create("org.reactivecocoa.ReactiveObjC.RACKVOProxy", DISPATCH_QUEUE_SERIAL); - _trampolines = [NSMapTable strongToWeakObjectsMapTable]; - - return self; -} - -- (void)addObserver:(__weak NSObject *)observer forContext:(void *)context { - NSValue *valueContext = [NSValue valueWithPointer:context]; - - dispatch_sync(self.queue, ^{ - [self.trampolines setObject:observer forKey:valueContext]; - }); -} - -- (void)removeObserver:(NSObject *)observer forContext:(void *)context { - NSValue *valueContext = [NSValue valueWithPointer:context]; - - dispatch_sync(self.queue, ^{ - [self.trampolines removeObjectForKey:valueContext]; - }); -} - -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - NSValue *valueContext = [NSValue valueWithPointer:context]; - __block NSObject *trueObserver; - - dispatch_sync(self.queue, ^{ - trueObserver = [self.trampolines objectForKey:valueContext]; - }); - - if (trueObserver != nil) { - [trueObserver observeValueForKeyPath:keyPath ofObject:object change:change context:context]; - } -} - -@end diff --git a/ReactiveObjC/RACKVOTrampoline.h b/ReactiveObjC/RACKVOTrampoline.h deleted file mode 100644 index dd79fb97c8..0000000000 --- a/ReactiveObjC/RACKVOTrampoline.h +++ /dev/null @@ -1,31 +0,0 @@ -// -// RACKVOTrampoline.h -// ReactiveObjC -// -// Created by Josh Abernathy on 1/15/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import -#import "NSObject+RACKVOWrapper.h" -#import "RACDisposable.h" - -// A private trampoline object that represents a KVO observation. -// -// Disposing of the trampoline will stop observation. -@interface RACKVOTrampoline : RACDisposable - -// Initializes the receiver with the given parameters. -// -// target - The object whose key path should be observed. Cannot be nil. -// observer - The object that gets notified when the value at the key path -// changes. Can be nil. -// keyPath - The key path on `target` to observe. Cannot be nil. -// options - Any key value observing options to use in the observation. -// block - The block to call when the value at the observed key path changes. -// Cannot be nil. -// -// Returns the initialized object. -- (id)initWithTarget:(__weak NSObject *)target observer:(__weak NSObject *)observer keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(RACKVOBlock)block; - -@end diff --git a/ReactiveObjC/RACKVOTrampoline.m b/ReactiveObjC/RACKVOTrampoline.m deleted file mode 100644 index 1f3a1fe726..0000000000 --- a/ReactiveObjC/RACKVOTrampoline.m +++ /dev/null @@ -1,110 +0,0 @@ -// -// RACKVOTrampoline.m -// ReactiveObjC -// -// Created by Josh Abernathy on 1/15/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACKVOTrampoline.h" -#import "NSObject+RACDeallocating.h" -#import "RACCompoundDisposable.h" -#import "RACKVOProxy.h" - -@interface RACKVOTrampoline () - -// The keypath which the trampoline is observing. -@property (nonatomic, readonly, copy) NSString *keyPath; - -// These properties should only be manipulated while synchronized on the -// receiver. -@property (nonatomic, readonly, copy) RACKVOBlock block; -@property (nonatomic, readonly, unsafe_unretained) NSObject *unsafeTarget; -@property (nonatomic, readonly, weak) NSObject *weakTarget; -@property (nonatomic, readonly, weak) NSObject *observer; - -@end - -@implementation RACKVOTrampoline - -#pragma mark Lifecycle - -- (id)initWithTarget:(__weak NSObject *)target observer:(__weak NSObject *)observer keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(RACKVOBlock)block { - NSCParameterAssert(keyPath != nil); - NSCParameterAssert(block != nil); - - NSObject *strongTarget = target; - if (strongTarget == nil) return nil; - - self = [super init]; - if (self == nil) return nil; - - _keyPath = [keyPath copy]; - - _block = [block copy]; - _weakTarget = target; - _unsafeTarget = strongTarget; - _observer = observer; - - [RACKVOProxy.sharedProxy addObserver:self forContext:(__bridge void *)self]; - [strongTarget addObserver:RACKVOProxy.sharedProxy forKeyPath:self.keyPath options:options context:(__bridge void *)self]; - - [strongTarget.rac_deallocDisposable addDisposable:self]; - [self.observer.rac_deallocDisposable addDisposable:self]; - - return self; -} - -- (void)dealloc { - [self dispose]; -} - -#pragma mark Observation - -- (void)dispose { - NSObject *target; - NSObject *observer; - - @synchronized (self) { - _block = nil; - - // The target should still exist at this point, because we still need to - // tear down its KVO observation. Therefore, we can use the unsafe - // reference (and need to, because the weak one will have been zeroed by - // now). - target = self.unsafeTarget; - observer = self.observer; - - _unsafeTarget = nil; - _observer = nil; - } - - [target.rac_deallocDisposable removeDisposable:self]; - [observer.rac_deallocDisposable removeDisposable:self]; - - [target removeObserver:RACKVOProxy.sharedProxy forKeyPath:self.keyPath context:(__bridge void *)self]; - [RACKVOProxy.sharedProxy removeObserver:self forContext:(__bridge void *)self]; -} - -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - if (context != (__bridge void *)self) { - [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; - return; - } - - RACKVOBlock block; - id observer; - id target; - - @synchronized (self) { - block = self.block; - observer = self.observer; - target = self.weakTarget; - } - - if (block == nil || target == nil) return; - - block(target, observer, change); -} - -@end diff --git a/ReactiveObjC/RACMulticastConnection+Private.h b/ReactiveObjC/RACMulticastConnection+Private.h deleted file mode 100644 index 29ee5a42fa..0000000000 --- a/ReactiveObjC/RACMulticastConnection+Private.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// RACMulticastConnection+Private.h -// ReactiveObjC -// -// Created by Josh Abernathy on 4/11/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACMulticastConnection.h" - -@class RACSubject; - -@interface RACMulticastConnection () - -- (id)initWithSourceSignal:(RACSignal *)source subject:(RACSubject *)subject; - -@end diff --git a/ReactiveObjC/RACMulticastConnection.h b/ReactiveObjC/RACMulticastConnection.h deleted file mode 100644 index 9577fe812f..0000000000 --- a/ReactiveObjC/RACMulticastConnection.h +++ /dev/null @@ -1,48 +0,0 @@ -// -// RACMulticastConnection.h -// ReactiveObjC -// -// Created by Josh Abernathy on 4/11/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import - -@class RACDisposable; -@class RACSignal; - -/// A multicast connection encapsulates the idea of sharing one subscription to a -/// signal to many subscribers. This is most often needed if the subscription to -/// the underlying signal involves side-effects or shouldn't be called more than -/// once. -/// -/// The multicasted signal is only subscribed to when -/// -[RACMulticastConnection connect] is called. Until that happens, no values -/// will be sent on `signal`. See -[RACMulticastConnection autoconnect] for how -/// -[RACMulticastConnection connect] can be called automatically. -/// -/// Note that you shouldn't create RACMulticastConnection manually. Instead use -/// -[RACSignal publish] or -[RACSignal multicast:]. -@interface RACMulticastConnection : NSObject - -/// The multicasted signal. -@property (nonatomic, strong, readonly) RACSignal *signal; - -/// Connect to the underlying signal by subscribing to it. Calling this multiple -/// times does nothing but return the existing connection's disposable. -/// -/// Returns the disposable for the subscription to the multicasted signal. -- (RACDisposable *)connect; - -/// Connects to the underlying signal when the returned signal is first -/// subscribed to, and disposes of the subscription to the multicasted signal -/// when the returned signal has no subscribers. -/// -/// If new subscribers show up after being disposed, they'll subscribe and then -/// be immediately disposed of. The returned signal will never re-connect to the -/// multicasted signal. -/// -/// Returns the autoconnecting signal. -- (RACSignal *)autoconnect; - -@end diff --git a/ReactiveObjC/RACMulticastConnection.m b/ReactiveObjC/RACMulticastConnection.m deleted file mode 100644 index e0da72a6e4..0000000000 --- a/ReactiveObjC/RACMulticastConnection.m +++ /dev/null @@ -1,85 +0,0 @@ -// -// RACMulticastConnection.m -// ReactiveObjC -// -// Created by Josh Abernathy on 4/11/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACMulticastConnection.h" -#import "RACMulticastConnection+Private.h" -#import "RACDisposable.h" -#import "RACSerialDisposable.h" -#import "RACSubject.h" -#import - -@interface RACMulticastConnection () { - RACSubject *_signal; - - // When connecting, a caller should attempt to atomically swap the value of this - // from `0` to `1`. - // - // If the swap is successful the caller is resposible for subscribing `_signal` - // to `sourceSignal` and storing the returned disposable in `serialDisposable`. - // - // If the swap is unsuccessful it means that `_sourceSignal` has already been - // connected and the caller has no action to take. - int32_t volatile _hasConnected; -} - -@property (nonatomic, readonly, strong) RACSignal *sourceSignal; -@property (strong) RACSerialDisposable *serialDisposable; -@end - -@implementation RACMulticastConnection - -#pragma mark Lifecycle - -- (id)initWithSourceSignal:(RACSignal *)source subject:(RACSubject *)subject { - NSCParameterAssert(source != nil); - NSCParameterAssert(subject != nil); - - self = [super init]; - if (self == nil) return nil; - - _sourceSignal = source; - _serialDisposable = [[RACSerialDisposable alloc] init]; - _signal = subject; - - return self; -} - -#pragma mark Connecting - -- (RACDisposable *)connect { - BOOL shouldConnect = OSAtomicCompareAndSwap32Barrier(0, 1, &_hasConnected); - - if (shouldConnect) { - self.serialDisposable.disposable = [self.sourceSignal subscribe:_signal]; - } - - return self.serialDisposable; -} - -- (RACSignal *)autoconnect { - __block volatile int32_t subscriberCount = 0; - - return [[RACSignal - createSignal:^(id subscriber) { - OSAtomicIncrement32Barrier(&subscriberCount); - - RACDisposable *subscriptionDisposable = [self.signal subscribe:subscriber]; - RACDisposable *connectionDisposable = [self connect]; - - return [RACDisposable disposableWithBlock:^{ - [subscriptionDisposable dispose]; - - if (OSAtomicDecrement32Barrier(&subscriberCount) == 0) { - [connectionDisposable dispose]; - } - }]; - }] - setNameWithFormat:@"[%@] -autoconnect", self.signal.name]; -} - -@end diff --git a/ReactiveObjC/RACPassthroughSubscriber.h b/ReactiveObjC/RACPassthroughSubscriber.h deleted file mode 100644 index 3f9eddbe0d..0000000000 --- a/ReactiveObjC/RACPassthroughSubscriber.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// RACPassthroughSubscriber.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-06-13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import -#import "RACSubscriber.h" - -@class RACCompoundDisposable; -@class RACSignal; - -// A private subscriber that passes through all events to another subscriber -// while not disposed. -@interface RACPassthroughSubscriber : NSObject - -// Initializes the receiver to pass through events until disposed. -// -// subscriber - The subscriber to forward events to. This must not be nil. -// signal - The signal that will be sending events to the receiver. -// disposable - When this disposable is disposed, no more events will be -// forwarded. This must not be nil. -// -// Returns an initialized passthrough subscriber. -- (instancetype)initWithSubscriber:(id)subscriber signal:(RACSignal *)signal disposable:(RACCompoundDisposable *)disposable; - -@end diff --git a/ReactiveObjC/RACPassthroughSubscriber.m b/ReactiveObjC/RACPassthroughSubscriber.m deleted file mode 100644 index 85018f5b11..0000000000 --- a/ReactiveObjC/RACPassthroughSubscriber.m +++ /dev/null @@ -1,107 +0,0 @@ -// -// RACPassthroughSubscriber.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-06-13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACPassthroughSubscriber.h" -#import "RACCompoundDisposable.h" -#import "RACSignal.h" -#import "RACSignalProvider.h" - -#if !defined(DTRACE_PROBES_DISABLED) || !DTRACE_PROBES_DISABLED - -static const char *cleanedDTraceString(NSString *original) { - return [original stringByReplacingOccurrencesOfString:@"\\s+" withString:@" " options:NSRegularExpressionSearch range:NSMakeRange(0, original.length)].UTF8String; -} - -static const char *cleanedSignalDescription(RACSignal *signal) { - NSString *desc = signal.description; - - NSRange range = [desc rangeOfString:@" name:"]; - if (range.location != NSNotFound) { - desc = [desc stringByReplacingCharactersInRange:range withString:@""]; - } - - return cleanedDTraceString(desc); -} - -#endif - -@interface RACPassthroughSubscriber () - -// The subscriber to which events should be forwarded. -@property (nonatomic, strong, readonly) id innerSubscriber; - -// The signal sending events to this subscriber. -// -// This property isn't `weak` because it's only used for DTrace probes, so -// a zeroing weak reference would incur an unnecessary performance penalty in -// normal usage. -@property (nonatomic, unsafe_unretained, readonly) RACSignal *signal; - -// A disposable representing the subscription. When disposed, no further events -// should be sent to the `innerSubscriber`. -@property (nonatomic, strong, readonly) RACCompoundDisposable *disposable; - -@end - -@implementation RACPassthroughSubscriber - -#pragma mark Lifecycle - -- (instancetype)initWithSubscriber:(id)subscriber signal:(RACSignal *)signal disposable:(RACCompoundDisposable *)disposable { - NSCParameterAssert(subscriber != nil); - - self = [super init]; - if (self == nil) return nil; - - _innerSubscriber = subscriber; - _signal = signal; - _disposable = disposable; - - [self.innerSubscriber didSubscribeWithDisposable:self.disposable]; - return self; -} - -#pragma mark RACSubscriber - -- (void)sendNext:(id)value { - if (self.disposable.disposed) return; - - if (RACSIGNAL_NEXT_ENABLED()) { - RACSIGNAL_NEXT(cleanedSignalDescription(self.signal), cleanedDTraceString(self.innerSubscriber.description), cleanedDTraceString([value description])); - } - - [self.innerSubscriber sendNext:value]; -} - -- (void)sendError:(NSError *)error { - if (self.disposable.disposed) return; - - if (RACSIGNAL_ERROR_ENABLED()) { - RACSIGNAL_ERROR(cleanedSignalDescription(self.signal), cleanedDTraceString(self.innerSubscriber.description), cleanedDTraceString(error.description)); - } - - [self.innerSubscriber sendError:error]; -} - -- (void)sendCompleted { - if (self.disposable.disposed) return; - - if (RACSIGNAL_COMPLETED_ENABLED()) { - RACSIGNAL_COMPLETED(cleanedSignalDescription(self.signal), cleanedDTraceString(self.innerSubscriber.description)); - } - - [self.innerSubscriber sendCompleted]; -} - -- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable { - if (disposable != self.disposable) { - [self.disposable addDisposable:disposable]; - } -} - -@end diff --git a/ReactiveObjC/RACQueueScheduler+Subclass.h b/ReactiveObjC/RACQueueScheduler+Subclass.h deleted file mode 100644 index 8e9e88136f..0000000000 --- a/ReactiveObjC/RACQueueScheduler+Subclass.h +++ /dev/null @@ -1,40 +0,0 @@ -// -// RACQueueScheduler+Subclass.h -// ReactiveObjC -// -// Created by Josh Abernathy on 6/6/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACQueueScheduler.h" -#import "RACScheduler+Subclass.h" - -/// An interface for use by GCD queue-based subclasses. -/// -/// See RACScheduler+Subclass.h for subclassing notes. -@interface RACQueueScheduler () - -/// The queue on which blocks are enqueued. -#if OS_OBJECT_USE_OBJC -@property (nonatomic, strong, readonly) dispatch_queue_t queue; -#else -// Swift builds with OS_OBJECT_HAVE_OBJC_SUPPORT=0 for Playgrounds and LLDB :( -@property (nonatomic, assign, readonly) dispatch_queue_t queue; -#endif - -/// Initializes the receiver with the name of the scheduler and the queue which -/// the scheduler should use. -/// -/// name - The name of the scheduler. If nil, a default name will be used. -/// queue - The queue upon which the receiver should enqueue scheduled blocks. -/// This argument must not be NULL. -/// -/// Returns the initialized object. -- (id)initWithName:(NSString *)name queue:(dispatch_queue_t)queue; - -/// Converts a date into a GCD time using dispatch_walltime(). -/// -/// date - The date to convert. This must not be nil. -+ (dispatch_time_t)wallTimeWithDate:(NSDate *)date; - -@end diff --git a/ReactiveObjC/RACQueueScheduler.h b/ReactiveObjC/RACQueueScheduler.h deleted file mode 100644 index cf0de462f5..0000000000 --- a/ReactiveObjC/RACQueueScheduler.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// RACQueueScheduler.h -// ReactiveObjC -// -// Created by Josh Abernathy on 11/30/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACScheduler.h" - -/// An abstract scheduler which asynchronously enqueues all its work to a Grand -/// Central Dispatch queue. -/// -/// Because RACQueueScheduler is abstract, it should not be instantiated -/// directly. Create a subclass using the `RACQueueScheduler+Subclass.h` -/// interface and use that instead. -@interface RACQueueScheduler : RACScheduler -@end diff --git a/ReactiveObjC/RACQueueScheduler.m b/ReactiveObjC/RACQueueScheduler.m deleted file mode 100644 index 44f5e698c0..0000000000 --- a/ReactiveObjC/RACQueueScheduler.m +++ /dev/null @@ -1,107 +0,0 @@ -// -// RACQueueScheduler.m -// ReactiveObjC -// -// Created by Josh Abernathy on 11/30/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACQueueScheduler.h" -#import "RACDisposable.h" -#import "RACQueueScheduler+Subclass.h" -#import "RACScheduler+Private.h" - -@implementation RACQueueScheduler - -#pragma mark Lifecycle - -- (id)initWithName:(NSString *)name queue:(dispatch_queue_t)queue { - NSCParameterAssert(queue != NULL); - - self = [super initWithName:name]; - if (self == nil) return nil; - - _queue = queue; -#if !OS_OBJECT_USE_OBJC - dispatch_retain(_queue); -#endif - - return self; -} - -#if !OS_OBJECT_USE_OBJC - -- (void)dealloc { - if (_queue != NULL) { - dispatch_release(_queue); - _queue = NULL; - } -} - -#endif - -#pragma mark Date Conversions - -+ (dispatch_time_t)wallTimeWithDate:(NSDate *)date { - NSCParameterAssert(date != nil); - - double seconds = 0; - double frac = modf(date.timeIntervalSince1970, &seconds); - - struct timespec walltime = { - .tv_sec = (time_t)fmin(fmax(seconds, LONG_MIN), LONG_MAX), - .tv_nsec = (long)fmin(fmax(frac * NSEC_PER_SEC, LONG_MIN), LONG_MAX) - }; - - return dispatch_walltime(&walltime, 0); -} - -#pragma mark RACScheduler - -- (RACDisposable *)schedule:(void (^)(void))block { - NSCParameterAssert(block != NULL); - - RACDisposable *disposable = [[RACDisposable alloc] init]; - - dispatch_async(self.queue, ^{ - if (disposable.disposed) return; - [self performAsCurrentScheduler:block]; - }); - - return disposable; -} - -- (RACDisposable *)after:(NSDate *)date schedule:(void (^)(void))block { - NSCParameterAssert(date != nil); - NSCParameterAssert(block != NULL); - - RACDisposable *disposable = [[RACDisposable alloc] init]; - - dispatch_after([self.class wallTimeWithDate:date], self.queue, ^{ - if (disposable.disposed) return; - [self performAsCurrentScheduler:block]; - }); - - return disposable; -} - -- (RACDisposable *)after:(NSDate *)date repeatingEvery:(NSTimeInterval)interval withLeeway:(NSTimeInterval)leeway schedule:(void (^)(void))block { - NSCParameterAssert(date != nil); - NSCParameterAssert(interval > 0.0 && interval < INT64_MAX / NSEC_PER_SEC); - NSCParameterAssert(leeway >= 0.0 && leeway < INT64_MAX / NSEC_PER_SEC); - NSCParameterAssert(block != NULL); - - uint64_t intervalInNanoSecs = (uint64_t)(interval * NSEC_PER_SEC); - uint64_t leewayInNanoSecs = (uint64_t)(leeway * NSEC_PER_SEC); - - dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.queue); - dispatch_source_set_timer(timer, [self.class wallTimeWithDate:date], intervalInNanoSecs, leewayInNanoSecs); - dispatch_source_set_event_handler(timer, block); - dispatch_resume(timer); - - return [RACDisposable disposableWithBlock:^{ - dispatch_source_cancel(timer); - }]; -} - -@end diff --git a/ReactiveObjC/RACReplaySubject.h b/ReactiveObjC/RACReplaySubject.h deleted file mode 100644 index 62753f8fb6..0000000000 --- a/ReactiveObjC/RACReplaySubject.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// RACReplaySubject.h -// ReactiveObjC -// -// Created by Josh Abernathy on 3/14/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACSubject.h" - -extern const NSUInteger RACReplaySubjectUnlimitedCapacity; - -/// A replay subject saves the values it is sent (up to its defined capacity) -/// and resends those to new subscribers. It will also replay an error or -/// completion. -@interface RACReplaySubject : RACSubject - -/// Creates a new replay subject with the given capacity. A capacity of -/// RACReplaySubjectUnlimitedCapacity means values are never trimmed. -+ (instancetype)replaySubjectWithCapacity:(NSUInteger)capacity; - -@end diff --git a/ReactiveObjC/RACReplaySubject.m b/ReactiveObjC/RACReplaySubject.m deleted file mode 100644 index 6916ff8b1f..0000000000 --- a/ReactiveObjC/RACReplaySubject.m +++ /dev/null @@ -1,112 +0,0 @@ -// -// RACReplaySubject.m -// ReactiveObjC -// -// Created by Josh Abernathy on 3/14/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACReplaySubject.h" -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACScheduler+Private.h" -#import "RACSubscriber.h" -#import "RACTuple.h" - -const NSUInteger RACReplaySubjectUnlimitedCapacity = NSUIntegerMax; - -@interface RACReplaySubject () - -@property (nonatomic, assign, readonly) NSUInteger capacity; - -// These properties should only be modified while synchronized on self. -@property (nonatomic, strong, readonly) NSMutableArray *valuesReceived; -@property (nonatomic, assign) BOOL hasCompleted; -@property (nonatomic, assign) BOOL hasError; -@property (nonatomic, strong) NSError *error; - -@end - - -@implementation RACReplaySubject - -#pragma mark Lifecycle - -+ (instancetype)replaySubjectWithCapacity:(NSUInteger)capacity { - return [(RACReplaySubject *)[self alloc] initWithCapacity:capacity]; -} - -- (instancetype)init { - return [self initWithCapacity:RACReplaySubjectUnlimitedCapacity]; -} - -- (instancetype)initWithCapacity:(NSUInteger)capacity { - self = [super init]; - if (self == nil) return nil; - - _capacity = capacity; - _valuesReceived = (capacity == RACReplaySubjectUnlimitedCapacity ? [NSMutableArray array] : [NSMutableArray arrayWithCapacity:capacity]); - - return self; -} - -#pragma mark RACSignal - -- (RACDisposable *)subscribe:(id)subscriber { - RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable]; - - RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{ - @synchronized (self) { - for (id value in self.valuesReceived) { - if (compoundDisposable.disposed) return; - - [subscriber sendNext:(value == RACTupleNil.tupleNil ? nil : value)]; - } - - if (compoundDisposable.disposed) return; - - if (self.hasCompleted) { - [subscriber sendCompleted]; - } else if (self.hasError) { - [subscriber sendError:self.error]; - } else { - RACDisposable *subscriptionDisposable = [super subscribe:subscriber]; - [compoundDisposable addDisposable:subscriptionDisposable]; - } - } - }]; - - [compoundDisposable addDisposable:schedulingDisposable]; - - return compoundDisposable; -} - -#pragma mark RACSubscriber - -- (void)sendNext:(id)value { - @synchronized (self) { - [self.valuesReceived addObject:value ?: RACTupleNil.tupleNil]; - [super sendNext:value]; - - if (self.capacity != RACReplaySubjectUnlimitedCapacity && self.valuesReceived.count > self.capacity) { - [self.valuesReceived removeObjectsInRange:NSMakeRange(0, self.valuesReceived.count - self.capacity)]; - } - } -} - -- (void)sendCompleted { - @synchronized (self) { - self.hasCompleted = YES; - [super sendCompleted]; - } -} - -- (void)sendError:(NSError *)e { - @synchronized (self) { - self.hasError = YES; - self.error = e; - [super sendError:e]; - } -} - -@end diff --git a/ReactiveObjC/RACReturnSignal.h b/ReactiveObjC/RACReturnSignal.h deleted file mode 100644 index 1952847444..0000000000 --- a/ReactiveObjC/RACReturnSignal.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// RACReturnSignal.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-10-10. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACSignal.h" - -// A private `RACSignal` subclasses that synchronously sends a value to any -// subscribers, then completes. -@interface RACReturnSignal : RACSignal - -+ (RACSignal *)return:(id)value; - -@end diff --git a/ReactiveObjC/RACReturnSignal.m b/ReactiveObjC/RACReturnSignal.m deleted file mode 100644 index dca319d080..0000000000 --- a/ReactiveObjC/RACReturnSignal.m +++ /dev/null @@ -1,90 +0,0 @@ -// -// RACReturnSignal.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-10-10. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACReturnSignal.h" -#import "RACScheduler+Private.h" -#import "RACSubscriber.h" -#import "RACUnit.h" - -@interface RACReturnSignal () - -// The value to send upon subscription. -@property (nonatomic, strong, readonly) id value; - -@end - -@implementation RACReturnSignal - -#pragma mark Properties - -// Only allow this signal's name to be customized in DEBUG, since it's -// potentially a singleton in release builds (see +return:). -- (void)setName:(NSString *)name { -#ifdef DEBUG - [super setName:name]; -#endif -} - -- (NSString *)name { -#ifdef DEBUG - return super.name; -#else - return @"+return:"; -#endif -} - -#pragma mark Lifecycle - -+ (RACSignal *)return:(id)value { -#ifndef DEBUG - // In release builds, use singletons for two very common cases. - if (value == RACUnit.defaultUnit) { - static RACReturnSignal *unitSingleton; - static dispatch_once_t unitPred; - - dispatch_once(&unitPred, ^{ - unitSingleton = [[self alloc] init]; - unitSingleton->_value = RACUnit.defaultUnit; - }); - - return unitSingleton; - } else if (value == nil) { - static RACReturnSignal *nilSingleton; - static dispatch_once_t nilPred; - - dispatch_once(&nilPred, ^{ - nilSingleton = [[self alloc] init]; - nilSingleton->_value = nil; - }); - - return nilSingleton; - } -#endif - - RACReturnSignal *signal = [[self alloc] init]; - signal->_value = value; - -#ifdef DEBUG - [signal setNameWithFormat:@"+return: %@", value]; -#endif - - return signal; -} - -#pragma mark Subscription - -- (RACDisposable *)subscribe:(id)subscriber { - NSCParameterAssert(subscriber != nil); - - return [RACScheduler.subscriptionScheduler schedule:^{ - [subscriber sendNext:self.value]; - [subscriber sendCompleted]; - }]; -} - -@end diff --git a/ReactiveObjC/RACScheduler+Private.h b/ReactiveObjC/RACScheduler+Private.h deleted file mode 100644 index d14956c584..0000000000 --- a/ReactiveObjC/RACScheduler+Private.h +++ /dev/null @@ -1,34 +0,0 @@ -// -// RACScheduler+Private.h -// ReactiveObjC -// -// Created by Josh Abernathy on 11/29/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACScheduler.h" - -// The thread-specific current scheduler key. -extern NSString * const RACSchedulerCurrentSchedulerKey; - -// A private interface for internal RAC use only. -@interface RACScheduler () - -// A dedicated scheduler that fills two requirements: -// -// 1. By the time subscription happens, we need a valid +currentScheduler. -// 2. Subscription should happen as soon as possible. -// -// To fulfill those two, if we already have a valid +currentScheduler, it -// immediately executes scheduled blocks. If we don't, it will execute scheduled -// blocks with a private background scheduler. -+ (instancetype)subscriptionScheduler; - -// Initializes the receiver with the given name. -// -// name - The name of the scheduler. If nil, a default name will be used. -// -// Returns the initialized object. -- (id)initWithName:(NSString *)name; - -@end diff --git a/ReactiveObjC/RACScheduler+Subclass.h b/ReactiveObjC/RACScheduler+Subclass.h deleted file mode 100644 index 8694c6fac5..0000000000 --- a/ReactiveObjC/RACScheduler+Subclass.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// RACScheduler.m -// ReactiveObjC -// -// Created by Miķelis Vindavs on 5/27/14. -// Copyright (c) 2014 GitHub, Inc. All rights reserved. -// - -#import -#import "RACScheduler.h" - -/// An interface for use by subclasses. -/// -/// Subclasses should use `-performAsCurrentScheduler:` to do the actual block -/// invocation so that +[RACScheduler currentScheduler] behaves as expected. -/// -/// **Note that RACSchedulers are expected to be serial**. Subclasses must honor -/// that contract. See `RACTargetQueueScheduler` for a queue-based scheduler -/// which will enforce the serialization guarantee. -@interface RACScheduler () - -/// Performs the given block with the receiver as the current scheduler for -/// its thread. This should only be called by subclasses to perform their -/// scheduled blocks. -/// -/// block - The block to execute. Cannot be NULL. -- (void)performAsCurrentScheduler:(void (^)(void))block; - -@end diff --git a/ReactiveObjC/RACScheduler.h b/ReactiveObjC/RACScheduler.h deleted file mode 100644 index 4dcc22966f..0000000000 --- a/ReactiveObjC/RACScheduler.h +++ /dev/null @@ -1,148 +0,0 @@ -// -// RACScheduler.h -// ReactiveObjC -// -// Created by Josh Abernathy on 4/16/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import - -/// The priority for the scheduler. -/// -/// RACSchedulerPriorityHigh - High priority. -/// RACSchedulerPriorityDefault - Default priority. -/// RACSchedulerPriorityLow - Low priority. -/// RACSchedulerPriorityBackground - Background priority. -typedef enum : long { - RACSchedulerPriorityHigh = DISPATCH_QUEUE_PRIORITY_HIGH, - RACSchedulerPriorityDefault = DISPATCH_QUEUE_PRIORITY_DEFAULT, - RACSchedulerPriorityLow = DISPATCH_QUEUE_PRIORITY_LOW, - RACSchedulerPriorityBackground = DISPATCH_QUEUE_PRIORITY_BACKGROUND, -} RACSchedulerPriority; - -/// Scheduled with -scheduleRecursiveBlock:, this type of block is passed a block -/// with which it can call itself recursively. -typedef void (^RACSchedulerRecursiveBlock)(void (^reschedule)(void)); - -@class RACDisposable; - -/// Schedulers are used to control when and where work is performed. -@interface RACScheduler : NSObject - -/// A singleton scheduler that immediately executes the blocks it is given. -/// -/// **Note:** Unlike most other schedulers, this does not set the current -/// scheduler. There may still be a valid +currentScheduler if this is used -/// within a block scheduled on a different scheduler. -+ (RACScheduler *)immediateScheduler; - -/// A singleton scheduler that executes blocks in the main thread. -+ (RACScheduler *)mainThreadScheduler; - -/// Creates and returns a new background scheduler with the given priority and -/// name. The name is for debug and instrumentation purposes only. -/// -/// Scheduler creation is cheap. It's unnecessary to save the result of this -/// method call unless you want to serialize some actions on the same background -/// scheduler. -+ (RACScheduler *)schedulerWithPriority:(RACSchedulerPriority)priority name:(NSString *)name; - -/// Invokes +schedulerWithPriority:name: with a default name. -+ (RACScheduler *)schedulerWithPriority:(RACSchedulerPriority)priority; - -/// Invokes +schedulerWithPriority: with RACSchedulerPriorityDefault. -+ (RACScheduler *)scheduler; - -/// The current scheduler. This will only be valid when used from within a -/// -[RACScheduler schedule:] block or when on the main thread. -+ (RACScheduler *)currentScheduler; - -/// Schedule the given block for execution on the scheduler. -/// -/// Scheduled blocks will be executed in the order in which they were scheduled. -/// -/// block - The block to schedule for execution. Cannot be nil. -/// -/// Returns a disposable which can be used to cancel the scheduled block before -/// it begins executing, or nil if cancellation is not supported. -- (RACDisposable *)schedule:(void (^)(void))block; - -/// Schedule the given block for execution on the scheduler at or after -/// a specific time. -/// -/// Note that blocks scheduled for a certain time will not preempt any other -/// scheduled work that is executing at the time. -/// -/// When invoked on the +immediateScheduler, the calling thread **will block** -/// until the specified time. -/// -/// date - The earliest time at which `block` should begin executing. The block -/// may not execute immediately at this time, whether due to system load -/// or another block on the scheduler currently being run. Cannot be nil. -/// block - The block to schedule for execution. Cannot be nil. -/// -/// Returns a disposable which can be used to cancel the scheduled block before -/// it begins executing, or nil if cancellation is not supported. -- (RACDisposable *)after:(NSDate *)date schedule:(void (^)(void))block; - -/// Schedule the given block for execution on the scheduler after the delay. -/// -/// Converts the delay into an NSDate, then invokes `-after:schedule:`. -- (RACDisposable *)afterDelay:(NSTimeInterval)delay schedule:(void (^)(void))block; - -/// Reschedule the given block at a particular interval, starting at a specific -/// time, and with a given leeway for deferral. -/// -/// Note that blocks scheduled for a certain time will not preempt any other -/// scheduled work that is executing at the time. -/// -/// Regardless of the value of `leeway`, the given block may not execute exactly -/// at `when` or exactly on successive intervals, whether due to system load or -/// because another block is currently being run on the scheduler. -/// -/// It is considered undefined behavior to invoke this method on the -/// +immediateScheduler. -/// -/// date - The earliest time at which `block` should begin executing. The -/// block may not execute immediately at this time, whether due to -/// system load or another block on the scheduler currently being -/// run. Cannot be nil. -/// interval - The interval at which the block should be rescheduled, starting -/// from `date`. This will use the system wall clock, to avoid -/// skew when the computer goes to sleep. -/// leeway - A hint to the system indicating the number of seconds that each -/// scheduling can be deferred. Note that this is just a hint, and -/// there may be some additional latency no matter what. -/// block - The block to repeatedly schedule for execution. Cannot be nil. -/// -/// Returns a disposable which can be used to cancel the automatic scheduling and -/// rescheduling, or nil if cancellation is not supported. -- (RACDisposable *)after:(NSDate *)date repeatingEvery:(NSTimeInterval)interval withLeeway:(NSTimeInterval)leeway schedule:(void (^)(void))block; - -/// Schedule the given recursive block for execution on the scheduler. The -/// scheduler will automatically flatten any recursive scheduling into iteration -/// instead, so this can be used without issue for blocks that may keep invoking -/// themselves forever. -/// -/// Scheduled blocks will be executed in the order in which they were scheduled. -/// -/// recursiveBlock - The block to schedule for execution. When invoked, the -/// recursive block will be passed a `void (^)(void)` block -/// which will reschedule the recursive block at the end of the -/// receiver's queue. This passed-in block will automatically -/// skip scheduling if the scheduling of the `recursiveBlock` -/// was disposed in the meantime. -/// -/// Returns a disposable which can be used to cancel the scheduled block before -/// it begins executing, or to stop it from rescheduling if it's already begun -/// execution. -- (RACDisposable *)scheduleRecursiveBlock:(RACSchedulerRecursiveBlock)recursiveBlock; - -@end - -@interface RACScheduler (Unavailable) - -+ (RACScheduler *)schedulerWithQueue:(dispatch_queue_t)queue name:(NSString *)name __attribute__((unavailable("Use -[RACTargetQueueScheduler initWithName:targetQueue:] instead."))); - -@end diff --git a/ReactiveObjC/RACScheduler.m b/ReactiveObjC/RACScheduler.m deleted file mode 100644 index 3d90578993..0000000000 --- a/ReactiveObjC/RACScheduler.m +++ /dev/null @@ -1,212 +0,0 @@ -// -// RACScheduler.m -// ReactiveObjC -// -// Created by Josh Abernathy on 4/16/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACScheduler.h" -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACImmediateScheduler.h" -#import "RACScheduler+Private.h" -#import "RACSubscriptionScheduler.h" -#import "RACTargetQueueScheduler.h" - -// The key for the thread-specific current scheduler. -NSString * const RACSchedulerCurrentSchedulerKey = @"RACSchedulerCurrentSchedulerKey"; - -@interface RACScheduler () -@property (nonatomic, readonly, copy) NSString *name; -@end - -@implementation RACScheduler - -#pragma mark NSObject - -- (NSString *)description { - return [NSString stringWithFormat:@"<%@: %p> %@", self.class, self, self.name]; -} - -#pragma mark Initializers - -- (id)initWithName:(NSString *)name { - self = [super init]; - if (self == nil) return nil; - - if (name == nil) { - _name = [NSString stringWithFormat:@"org.reactivecocoa.ReactiveObjC.%@.anonymousScheduler", self.class]; - } else { - _name = [name copy]; - } - - return self; -} - -#pragma mark Schedulers - -+ (instancetype)immediateScheduler { - static dispatch_once_t onceToken; - static RACScheduler *immediateScheduler; - dispatch_once(&onceToken, ^{ - immediateScheduler = [[RACImmediateScheduler alloc] init]; - }); - - return immediateScheduler; -} - -+ (instancetype)mainThreadScheduler { - static dispatch_once_t onceToken; - static RACScheduler *mainThreadScheduler; - dispatch_once(&onceToken, ^{ - mainThreadScheduler = [[RACTargetQueueScheduler alloc] initWithName:@"org.reactivecocoa.ReactiveObjC.RACScheduler.mainThreadScheduler" targetQueue:dispatch_get_main_queue()]; - }); - - return mainThreadScheduler; -} - -+ (instancetype)schedulerWithPriority:(RACSchedulerPriority)priority name:(NSString *)name { - return [[RACTargetQueueScheduler alloc] initWithName:name targetQueue:dispatch_get_global_queue(priority, 0)]; -} - -+ (instancetype)schedulerWithPriority:(RACSchedulerPriority)priority { - return [self schedulerWithPriority:priority name:@"org.reactivecocoa.ReactiveObjC.RACScheduler.backgroundScheduler"]; -} - -+ (instancetype)scheduler { - return [self schedulerWithPriority:RACSchedulerPriorityDefault]; -} - -+ (instancetype)subscriptionScheduler { - static dispatch_once_t onceToken; - static RACScheduler *subscriptionScheduler; - dispatch_once(&onceToken, ^{ - subscriptionScheduler = [[RACSubscriptionScheduler alloc] init]; - }); - - return subscriptionScheduler; -} - -+ (BOOL)isOnMainThread { - return [NSOperationQueue.currentQueue isEqual:NSOperationQueue.mainQueue] || [NSThread isMainThread]; -} - -+ (instancetype)currentScheduler { - RACScheduler *scheduler = NSThread.currentThread.threadDictionary[RACSchedulerCurrentSchedulerKey]; - if (scheduler != nil) return scheduler; - if ([self.class isOnMainThread]) return RACScheduler.mainThreadScheduler; - - return nil; -} - -#pragma mark Scheduling - -- (RACDisposable *)schedule:(void (^)(void))block { - NSCAssert(NO, @"%@ must be implemented by subclasses.", NSStringFromSelector(_cmd)); - return nil; -} - -- (RACDisposable *)after:(NSDate *)date schedule:(void (^)(void))block { - NSCAssert(NO, @"%@ must be implemented by subclasses.", NSStringFromSelector(_cmd)); - return nil; -} - -- (RACDisposable *)afterDelay:(NSTimeInterval)delay schedule:(void (^)(void))block { - return [self after:[NSDate dateWithTimeIntervalSinceNow:delay] schedule:block]; -} - -- (RACDisposable *)after:(NSDate *)date repeatingEvery:(NSTimeInterval)interval withLeeway:(NSTimeInterval)leeway schedule:(void (^)(void))block { - NSCAssert(NO, @"%@ must be implemented by subclasses.", NSStringFromSelector(_cmd)); - return nil; -} - -- (RACDisposable *)scheduleRecursiveBlock:(RACSchedulerRecursiveBlock)recursiveBlock { - RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable]; - - [self scheduleRecursiveBlock:[recursiveBlock copy] addingToDisposable:disposable]; - return disposable; -} - -- (void)scheduleRecursiveBlock:(RACSchedulerRecursiveBlock)recursiveBlock addingToDisposable:(RACCompoundDisposable *)disposable { - @autoreleasepool { - RACCompoundDisposable *selfDisposable = [RACCompoundDisposable compoundDisposable]; - [disposable addDisposable:selfDisposable]; - - __weak RACDisposable *weakSelfDisposable = selfDisposable; - - RACDisposable *schedulingDisposable = [self schedule:^{ - @autoreleasepool { - // At this point, we've been invoked, so our disposable is now useless. - [disposable removeDisposable:weakSelfDisposable]; - } - - if (disposable.disposed) return; - - void (^reallyReschedule)(void) = ^{ - if (disposable.disposed) return; - [self scheduleRecursiveBlock:recursiveBlock addingToDisposable:disposable]; - }; - - // Protects the variables below. - // - // This doesn't actually need to be __block qualified, but Clang - // complains otherwise. :C - __block NSLock *lock = [[NSLock alloc] init]; - lock.name = [NSString stringWithFormat:@"%@ %s", self, sel_getName(_cmd)]; - - __block NSUInteger rescheduleCount = 0; - - // Set to YES once synchronous execution has finished. Further - // rescheduling should occur immediately (rather than being - // flattened). - __block BOOL rescheduleImmediately = NO; - - @autoreleasepool { - recursiveBlock(^{ - [lock lock]; - BOOL immediate = rescheduleImmediately; - if (!immediate) ++rescheduleCount; - [lock unlock]; - - if (immediate) reallyReschedule(); - }); - } - - [lock lock]; - NSUInteger synchronousCount = rescheduleCount; - rescheduleImmediately = YES; - [lock unlock]; - - for (NSUInteger i = 0; i < synchronousCount; i++) { - reallyReschedule(); - } - }]; - - [selfDisposable addDisposable:schedulingDisposable]; - } -} - -- (void)performAsCurrentScheduler:(void (^)(void))block { - NSCParameterAssert(block != NULL); - - // If we're using a concurrent queue, we could end up in here concurrently, - // in which case we *don't* want to clear the current scheduler immediately - // after our block is done executing, but only *after* all our concurrent - // invocations are done. - - RACScheduler *previousScheduler = RACScheduler.currentScheduler; - NSThread.currentThread.threadDictionary[RACSchedulerCurrentSchedulerKey] = self; - - @autoreleasepool { - block(); - } - - if (previousScheduler != nil) { - NSThread.currentThread.threadDictionary[RACSchedulerCurrentSchedulerKey] = previousScheduler; - } else { - [NSThread.currentThread.threadDictionary removeObjectForKey:RACSchedulerCurrentSchedulerKey]; - } -} - -@end diff --git a/ReactiveObjC/RACScopedDisposable.h b/ReactiveObjC/RACScopedDisposable.h deleted file mode 100644 index a845849710..0000000000 --- a/ReactiveObjC/RACScopedDisposable.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// RACScopedDisposable.h -// ReactiveObjC -// -// Created by Josh Abernathy on 3/28/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACDisposable.h" - -/// A disposable that calls its own -dispose when it is dealloc'd. -@interface RACScopedDisposable : RACDisposable - -/// Creates a new scoped disposable that will also dispose of the given -/// disposable when it is dealloc'd. -+ (instancetype)scopedDisposableWithDisposable:(RACDisposable *)disposable; - -@end diff --git a/ReactiveObjC/RACScopedDisposable.m b/ReactiveObjC/RACScopedDisposable.m deleted file mode 100644 index 2da50cb91d..0000000000 --- a/ReactiveObjC/RACScopedDisposable.m +++ /dev/null @@ -1,32 +0,0 @@ -// -// RACScopedDisposable.m -// ReactiveObjC -// -// Created by Josh Abernathy on 3/28/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACScopedDisposable.h" - -@implementation RACScopedDisposable - -#pragma mark Lifecycle - -+ (instancetype)scopedDisposableWithDisposable:(RACDisposable *)disposable { - return [self disposableWithBlock:^{ - [disposable dispose]; - }]; -} - -- (void)dealloc { - [self dispose]; -} - -#pragma mark RACDisposable - -- (RACScopedDisposable *)asScopedDisposable { - // totally already are - return self; -} - -@end diff --git a/ReactiveObjC/RACSequence.h b/ReactiveObjC/RACSequence.h deleted file mode 100644 index 80d4765c2d..0000000000 --- a/ReactiveObjC/RACSequence.h +++ /dev/null @@ -1,155 +0,0 @@ -// -// RACSequence.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-10-29. -// Copyright (c) 2012 GitHub. All rights reserved. -// - -#import -#import "RACStream.h" - -@class RACScheduler; -@class RACSignal; - -/// Represents an immutable sequence of values. Unless otherwise specified, the -/// sequences' values are evaluated lazily on demand. Like Cocoa collections, -/// sequences cannot contain nil. -/// -/// Most inherited RACStream methods that accept a block will execute the block -/// _at most_ once for each value that is evaluated in the returned sequence. -/// Side effects are subject to the behavior described in -/// +sequenceWithHeadBlock:tailBlock:. -/// -/// Implemented as a class cluster. A minimal implementation for a subclass -/// consists simply of -head and -tail. -@interface RACSequence : RACStream - -/// The first object in the sequence, or nil if the sequence is empty. -/// -/// Subclasses must provide an implementation of this method. -@property (nonatomic, strong, readonly) id head; - -/// All but the first object in the sequence, or nil if there are no other -/// objects. -/// -/// Subclasses must provide an implementation of this method. -@property (nonatomic, strong, readonly) RACSequence *tail; - -/// Evaluates the full sequence to produce an equivalently-sized array. -@property (nonatomic, copy, readonly) NSArray *array; - -/// Returns an enumerator of all objects in the sequence. -@property (nonatomic, copy, readonly) NSEnumerator *objectEnumerator; - -/// Converts a sequence into an eager sequence. -/// -/// An eager sequence fully evaluates all of its values immediately. Sequences -/// derived from an eager sequence will also be eager. -/// -/// Returns a new eager sequence, or the receiver if the sequence is already -/// eager. -@property (nonatomic, copy, readonly) RACSequence *eagerSequence; - -/// Converts a sequence into a lazy sequence. -/// -/// A lazy sequence evaluates its values on demand, as they are accessed. -/// Sequences derived from a lazy sequence will also be lazy. -/// -/// Returns a new lazy sequence, or the receiver if the sequence is already lazy. -@property (nonatomic, copy, readonly) RACSequence *lazySequence; - -/// Invokes -signalWithScheduler: with a new RACScheduler. -- (RACSignal *)signal; - -/// Evaluates the full sequence on the given scheduler. -/// -/// Each item is evaluated in its own scheduled block, such that control of the -/// scheduler is yielded between each value. -/// -/// Returns a signal which sends the receiver's values on the given scheduler as -/// they're evaluated. -- (RACSignal *)signalWithScheduler:(RACScheduler *)scheduler; - -/// Applies a left fold to the sequence. -/// -/// This is the same as iterating the sequence along with a provided start value. -/// This uses a constant amount of memory. A left fold is left-associative so in -/// the sequence [1,2,3] the block would applied in the following order: -/// reduce(reduce(reduce(start, 1), 2), 3) -/// -/// start - The starting value for the fold. Used as `accumulator` for the -/// first fold. -/// reduce - The block used to combine the accumulated value and the next value. -/// Cannot be nil. -/// -/// Returns a reduced value. -- (id)foldLeftWithStart:(id)start reduce:(id (^)(id accumulator, id value))reduce; - -/// Applies a right fold to the sequence. -/// -/// A right fold is equivalent to recursion on the list. The block is evaluated -/// from the right to the left in list. It is right associative so it's applied -/// to the rightmost elements first. For example, in the sequence [1,2,3] the -/// block is applied in the order: -/// reduce(1, reduce(2, reduce(3, start))) -/// -/// start - The starting value for the fold. -/// reduce - The block used to combine the accumulated value and the next head. -/// The block is given the accumulated value and the value of the rest -/// of the computation (result of the recursion). This is computed when -/// you retrieve its value using `rest.head`. This allows you to -/// prevent unnecessary computation by not accessing `rest.head` if you -/// don't need to. -/// -/// Returns a reduced value. -- (id)foldRightWithStart:(id)start reduce:(id (^)(id first, RACSequence *rest))reduce; - -/// Check if any value in sequence passes the block. -/// -/// block - The block predicate used to check each item. Cannot be nil. -/// -/// Returns a boolean indiciating if any value in the sequence passed. -- (BOOL)any:(BOOL (^)(id value))block; - -/// Check if all values in the sequence pass the block. -/// -/// block - The block predicate used to check each item. Cannot be nil. -/// -/// Returns a boolean indicating if all values in the sequence passed. -- (BOOL)all:(BOOL (^)(id value))block; - -/// Returns the first object that passes the block. -/// -/// block - The block predicate used to check each item. Cannot be nil. -/// -/// Returns an object that passes the block or nil if no objects passed. -- (id)objectPassingTest:(BOOL (^)(id value))block; - -/// Creates a sequence that dynamically generates its values. -/// -/// headBlock - Invoked the first time -head is accessed. -/// tailBlock - Invoked the first time -tail is accessed. -/// -/// The results from each block are memoized, so each block will be invoked at -/// most once, no matter how many times the head and tail properties of the -/// sequence are accessed. -/// -/// Any side effects in `headBlock` or `tailBlock` should be thread-safe, since -/// the sequence may be evaluated at any time from any thread. Not only that, but -/// -tail may be accessed before -head, or both may be accessed simultaneously. -/// As noted above, side effects will only be triggered the _first_ time -head or -/// -tail is invoked. -/// -/// Returns a sequence that lazily invokes the given blocks to provide head and -/// tail. `headBlock` must not be nil. -+ (RACSequence *)sequenceWithHeadBlock:(id (^)(void))headBlock tailBlock:(RACSequence *(^)(void))tailBlock; - -@end - -@interface RACSequence (Unavailable) - -- (id)foldLeftWithStart:(id)start combine:(id (^)(id accumulator, id value))combine __attribute__((unavailable("Renamed to -foldLeftWithStart:reduce:"))); -- (id)foldRightWithStart:(id)start combine:(id (^)(id first, RACSequence *rest))combine __attribute__((unavailable("Renamed to -foldRightWithStart:reduce:"))); - -@end diff --git a/ReactiveObjC/RACSequence.m b/ReactiveObjC/RACSequence.m deleted file mode 100644 index b9b1ee011a..0000000000 --- a/ReactiveObjC/RACSequence.m +++ /dev/null @@ -1,371 +0,0 @@ -// -// RACSequence.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-10-29. -// Copyright (c) 2012 GitHub. All rights reserved. -// - -#import "RACSequence.h" -#import "RACArraySequence.h" -#import "RACDynamicSequence.h" -#import "RACEagerSequence.h" -#import "RACEmptySequence.h" -#import "RACScheduler.h" -#import "RACSignal.h" -#import "RACSubscriber.h" -#import "RACTuple.h" -#import "RACUnarySequence.h" - -// An enumerator over sequences. -@interface RACSequenceEnumerator : NSEnumerator - -// The sequence the enumerator is enumerating. -// -// This will change as the enumerator is exhausted. This property should only be -// accessed while synchronized on self. -@property (nonatomic, strong) RACSequence *sequence; - -@end - -@interface RACSequence () - -// Performs one iteration of lazy binding, passing through values from `current` -// until the sequence is exhausted, then recursively binding the remaining -// values in the receiver. -// -// Returns a new sequence which contains `current`, followed by the combined -// result of all applications of `block` to the remaining values in the receiver. -- (instancetype)bind:(RACStreamBindBlock)block passingThroughValuesFromSequence:(RACSequence *)current; - -@end - -@implementation RACSequenceEnumerator - -- (id)nextObject { - id object = nil; - - @synchronized (self) { - object = self.sequence.head; - self.sequence = self.sequence.tail; - } - - return object; -} - -@end - -@implementation RACSequence - -#pragma mark Lifecycle - -+ (RACSequence *)sequenceWithHeadBlock:(id (^)(void))headBlock tailBlock:(RACSequence *(^)(void))tailBlock { - return [[RACDynamicSequence sequenceWithHeadBlock:headBlock tailBlock:tailBlock] setNameWithFormat:@"+sequenceWithHeadBlock:tailBlock:"]; -} - -#pragma mark Class cluster primitives - -- (id)head { - NSCAssert(NO, @"%s must be overridden by subclasses", __func__); - return nil; -} - -- (RACSequence *)tail { - NSCAssert(NO, @"%s must be overridden by subclasses", __func__); - return nil; -} - -#pragma mark RACStream - -+ (instancetype)empty { - return RACEmptySequence.empty; -} - -+ (instancetype)return:(id)value { - return [RACUnarySequence return:value]; -} - -- (instancetype)bind:(RACStreamBindBlock (^)(void))block { - RACStreamBindBlock bindBlock = block(); - return [[self bind:bindBlock passingThroughValuesFromSequence:nil] setNameWithFormat:@"[%@] -bind:", self.name]; -} - -- (instancetype)bind:(RACStreamBindBlock)bindBlock passingThroughValuesFromSequence:(RACSequence *)passthroughSequence { - // Store values calculated in the dependency here instead, avoiding any kind - // of temporary collection and boxing. - // - // This relies on the implementation of RACDynamicSequence synchronizing - // access to its head, tail, and dependency, and we're only doing it because - // we really need the performance. - __block RACSequence *valuesSeq = self; - __block RACSequence *current = passthroughSequence; - __block BOOL stop = NO; - - RACSequence *sequence = [RACDynamicSequence sequenceWithLazyDependency:^ id { - while (current.head == nil) { - if (stop) return nil; - - // We've exhausted the current sequence, create a sequence from the - // next value. - id value = valuesSeq.head; - - if (value == nil) { - // We've exhausted all the sequences. - stop = YES; - return nil; - } - - current = (id)bindBlock(value, &stop); - if (current == nil) { - stop = YES; - return nil; - } - - valuesSeq = valuesSeq.tail; - } - - NSCAssert([current isKindOfClass:RACSequence.class], @"-bind: block returned an object that is not a sequence: %@", current); - return nil; - } headBlock:^(id _) { - return current.head; - } tailBlock:^ id (id _) { - if (stop) return nil; - - return [valuesSeq bind:bindBlock passingThroughValuesFromSequence:current.tail]; - }]; - - sequence.name = self.name; - return sequence; -} - -- (instancetype)concat:(RACStream *)stream { - NSCParameterAssert(stream != nil); - - return [[[RACArraySequence sequenceWithArray:@[ self, stream ] offset:0] - flatten] - setNameWithFormat:@"[%@] -concat: %@", self.name, stream]; -} - -- (instancetype)zipWith:(RACSequence *)sequence { - NSCParameterAssert(sequence != nil); - - return [[RACSequence - sequenceWithHeadBlock:^ id { - if (self.head == nil || sequence.head == nil) return nil; - return RACTuplePack(self.head, sequence.head); - } tailBlock:^ id { - if (self.tail == nil || [[RACSequence empty] isEqual:self.tail]) return nil; - if (sequence.tail == nil || [[RACSequence empty] isEqual:sequence.tail]) return nil; - - return [self.tail zipWith:sequence.tail]; - }] - setNameWithFormat:@"[%@] -zipWith: %@", self.name, sequence]; -} - -#pragma mark Extended methods - -- (NSArray *)array { - NSMutableArray *array = [NSMutableArray array]; - for (id obj in self) { - [array addObject:obj]; - } - - return [array copy]; -} - -- (NSEnumerator *)objectEnumerator { - RACSequenceEnumerator *enumerator = [[RACSequenceEnumerator alloc] init]; - enumerator.sequence = self; - return enumerator; -} - -- (RACSignal *)signal { - return [[self signalWithScheduler:[RACScheduler scheduler]] setNameWithFormat:@"[%@] -signal", self.name]; -} - -- (RACSignal *)signalWithScheduler:(RACScheduler *)scheduler { - return [[RACSignal createSignal:^(id subscriber) { - __block RACSequence *sequence = self; - - return [scheduler scheduleRecursiveBlock:^(void (^reschedule)(void)) { - if (sequence.head == nil) { - [subscriber sendCompleted]; - return; - } - - [subscriber sendNext:sequence.head]; - - sequence = sequence.tail; - reschedule(); - }]; - }] setNameWithFormat:@"[%@] -signalWithScheduler: %@", self.name, scheduler]; -} - -- (id)foldLeftWithStart:(id)start reduce:(id (^)(id, id))reduce { - NSCParameterAssert(reduce != NULL); - - if (self.head == nil) return start; - - for (id value in self) { - start = reduce(start, value); - } - - return start; -} - -- (id)foldRightWithStart:(id)start reduce:(id (^)(id, RACSequence *))reduce { - NSCParameterAssert(reduce != NULL); - - if (self.head == nil) return start; - - RACSequence *rest = [RACSequence sequenceWithHeadBlock:^{ - if (self.tail) { - return [self.tail foldRightWithStart:start reduce:reduce]; - } else { - return start; - } - } tailBlock:nil]; - - return reduce(self.head, rest); -} - -- (BOOL)any:(BOOL (^)(id))block { - NSCParameterAssert(block != NULL); - - return [self objectPassingTest:block] != nil; -} - -- (BOOL)all:(BOOL (^)(id))block { - NSCParameterAssert(block != NULL); - - NSNumber *result = [self foldLeftWithStart:@YES reduce:^(NSNumber *accumulator, id value) { - return @(accumulator.boolValue && block(value)); - }]; - - return result.boolValue; -} - -- (id)objectPassingTest:(BOOL (^)(id))block { - NSCParameterAssert(block != NULL); - - return [self filter:block].head; -} - -- (RACSequence *)eagerSequence { - return [RACEagerSequence sequenceWithArray:self.array offset:0]; -} - -- (RACSequence *)lazySequence { - return self; -} - -#pragma mark NSCopying - -- (id)copyWithZone:(NSZone *)zone { - return self; -} - -#pragma mark NSCoding - -- (Class)classForCoder { - // Most sequences should be archived as RACArraySequences. - return RACArraySequence.class; -} - -- (id)initWithCoder:(NSCoder *)coder { - if (![self isKindOfClass:RACArraySequence.class]) return [[RACArraySequence alloc] initWithCoder:coder]; - - // Decoding is handled in RACArraySequence. - return [super init]; -} - -- (void)encodeWithCoder:(NSCoder *)coder { - [coder encodeObject:self.array forKey:@"array"]; -} - -#pragma mark NSFastEnumeration - -- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(__unsafe_unretained id *)stackbuf count:(NSUInteger)len { - if (state->state == ULONG_MAX) { - // Enumeration has completed. - return 0; - } - - // We need to traverse the sequence itself on repeated calls to this - // method, so use the 'state' field to track the current head. - RACSequence *(^getSequence)(void) = ^{ - return (__bridge RACSequence *)(void *)state->state; - }; - - void (^setSequence)(RACSequence *) = ^(RACSequence *sequence) { - // Release the old sequence and retain the new one. - CFBridgingRelease((void *)state->state); - - state->state = (unsigned long)CFBridgingRetain(sequence); - }; - - void (^complete)(void) = ^{ - // Release any stored sequence. - setSequence(nil); - state->state = ULONG_MAX; - }; - - if (state->state == 0) { - // Since a sequence doesn't mutate, this just needs to be set to - // something non-NULL. - state->mutationsPtr = state->extra; - - setSequence(self); - } - - state->itemsPtr = stackbuf; - - NSUInteger enumeratedCount = 0; - while (enumeratedCount < len) { - RACSequence *seq = getSequence(); - - // Because the objects in a sequence may be generated lazily, we want to - // prevent them from being released until the enumerator's used them. - __autoreleasing id obj = seq.head; - if (obj == nil) { - complete(); - break; - } - - stackbuf[enumeratedCount++] = obj; - - if (seq.tail == nil) { - complete(); - break; - } - - setSequence(seq.tail); - } - - return enumeratedCount; -} - -#pragma mark NSObject - -- (NSUInteger)hash { - return [self.head hash]; -} - -- (BOOL)isEqual:(RACSequence *)seq { - if (self == seq) return YES; - if (![seq isKindOfClass:RACSequence.class]) return NO; - - for (id selfObj in self) { - id seqObj = seq.head; - - // Handles the nil case too. - if (![seqObj isEqual:selfObj]) return NO; - - seq = seq.tail; - } - - // self is now depleted -- the argument should be too. - return (seq.head == nil); -} - -@end diff --git a/ReactiveObjC/RACSerialDisposable.h b/ReactiveObjC/RACSerialDisposable.h deleted file mode 100644 index 992196e73f..0000000000 --- a/ReactiveObjC/RACSerialDisposable.h +++ /dev/null @@ -1,43 +0,0 @@ -// -// RACSerialDisposable.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-07-22. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACDisposable.h" - -/// A disposable that contains exactly one other disposable and allows it to be -/// swapped out atomically. -@interface RACSerialDisposable : RACDisposable - -/// The inner disposable managed by the serial disposable. -/// -/// This property is thread-safe for reading and writing. However, if you want to -/// read the current value _and_ write a new one atomically, use -/// -swapInDisposable: instead. -/// -/// Disposing of the receiver will also dispose of the current disposable set for -/// this property, then set the property to nil. If any new disposable is set -/// after the receiver is disposed, it will be disposed immediately and this -/// property will remain set to nil. -@property (atomic, strong) RACDisposable *disposable; - -/// Creates a serial disposable which will wrap the given disposable. -/// -/// disposable - The value to set for `disposable`. This may be nil. -/// -/// Returns a RACSerialDisposable, or nil if an error occurs. -+ (instancetype)serialDisposableWithDisposable:(RACDisposable *)disposable; - -/// Atomically swaps the receiver's `disposable` for `newDisposable`. -/// -/// newDisposable - The new value for `disposable`. If the receiver has already -/// been disposed, this disposable will be too, and `disposable` -/// will remain set to nil. This argument may be nil. -/// -/// Returns the previous value for the `disposable` property. -- (RACDisposable *)swapInDisposable:(RACDisposable *)newDisposable; - -@end diff --git a/ReactiveObjC/RACSerialDisposable.m b/ReactiveObjC/RACSerialDisposable.m deleted file mode 100644 index a91c053c73..0000000000 --- a/ReactiveObjC/RACSerialDisposable.m +++ /dev/null @@ -1,121 +0,0 @@ -// -// RACSerialDisposable.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-07-22. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACSerialDisposable.h" -#import - -@interface RACSerialDisposable () { - // The receiver's `disposable`. This variable must only be referenced while - // _mutex is held. - RACDisposable * _disposable; - - // YES if the receiver has been disposed. This variable must only be accessed - // while _mutex is held. - BOOL _disposed; - - // A mutex to protect access to _disposable and _disposed. - pthread_mutex_t _mutex; -} - -@end - -@implementation RACSerialDisposable - -#pragma mark Properties - -- (BOOL)isDisposed { - pthread_mutex_lock(&_mutex); - const BOOL disposed = _disposed; - pthread_mutex_unlock(&_mutex); - - return disposed; -} - -- (RACDisposable *)disposable { - pthread_mutex_lock(&_mutex); - RACDisposable * const result = _disposable; - pthread_mutex_unlock(&_mutex); - - return result; -} - -- (void)setDisposable:(RACDisposable *)disposable { - [self swapInDisposable:disposable]; -} - -#pragma mark Lifecycle - -+ (instancetype)serialDisposableWithDisposable:(RACDisposable *)disposable { - RACSerialDisposable *serialDisposable = [[self alloc] init]; - serialDisposable.disposable = disposable; - return serialDisposable; -} - -- (instancetype)init { - self = [super init]; - if (self == nil) return nil; - - const int result = pthread_mutex_init(&_mutex, NULL); - NSCAssert(0 == result, @"Failed to initialize mutex with error %d", result); - - return self; -} - -- (instancetype)initWithBlock:(void (^)(void))block { - self = [self init]; - if (self == nil) return nil; - - self.disposable = [RACDisposable disposableWithBlock:block]; - - return self; -} - -- (void)dealloc { - const int result = pthread_mutex_destroy(&_mutex); - NSCAssert(0 == result, @"Failed to destroy mutex with error %d", result); -} - -#pragma mark Inner Disposable - -- (RACDisposable *)swapInDisposable:(RACDisposable *)newDisposable { - RACDisposable *existingDisposable; - BOOL alreadyDisposed; - - pthread_mutex_lock(&_mutex); - alreadyDisposed = _disposed; - if (!alreadyDisposed) { - existingDisposable = _disposable; - _disposable = newDisposable; - } - pthread_mutex_unlock(&_mutex); - - if (alreadyDisposed) { - [newDisposable dispose]; - return nil; - } - - return existingDisposable; -} - -#pragma mark Disposal - -- (void)dispose { - RACDisposable *existingDisposable; - - pthread_mutex_lock(&_mutex); - if (!_disposed) { - existingDisposable = _disposable; - _disposed = YES; - _disposable = nil; - } - pthread_mutex_unlock(&_mutex); - - [existingDisposable dispose]; -} - -@end diff --git a/ReactiveObjC/RACSignal+Operations.h b/ReactiveObjC/RACSignal+Operations.h deleted file mode 100644 index 1ae92986bf..0000000000 --- a/ReactiveObjC/RACSignal+Operations.h +++ /dev/null @@ -1,728 +0,0 @@ -// -// RACSignal+Operations.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-09-06. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import -#import "RACSignal.h" - -/// The domain for errors originating in RACSignal operations. -extern NSString * _Nonnull const RACSignalErrorDomain; - -/// The error code used with -timeout:. -extern const NSInteger RACSignalErrorTimedOut; - -/// The error code used when a value passed into +switch:cases:default: does not -/// match any of the cases, and no default was given. -extern const NSInteger RACSignalErrorNoMatchingCase; - -@class RACCommand; -@class RACDisposable; -@class RACMulticastConnection; -@class RACScheduler; -@class RACSequence; -@class RACSubject; -@class RACTuple; -@protocol RACSubscriber; - -@interface RACSignal (Operations) -NS_ASSUME_NONNULL_BEGIN - -/// Do the given block on `next`. This should be used to inject side effects into -/// the signal. -- (RACSignal *)doNext:(void (^)(id _Nullable x))block; - -/// Do the given block on `error`. This should be used to inject side effects -/// into the signal. -- (RACSignal *)doError:(void (^)(NSError * _Nonnull error))block; - -/// Do the given block on `completed`. This should be used to inject side effects -/// into the signal. -- (RACSignal *)doCompleted:(void (^)(void))block; - -/// Sends `next`s only if we don't receive another `next` in `interval` seconds. -/// -/// If a `next` is received, and then another `next` is received before -/// `interval` seconds have passed, the first value is discarded. -/// -/// After `interval` seconds have passed since the most recent `next` was sent, -/// the most recent `next` is forwarded on the scheduler that the value was -/// originally received on. If +[RACScheduler currentScheduler] was nil at the -/// time, a private background scheduler is used. -/// -/// Returns a signal which sends throttled and delayed `next` events. Completion -/// and errors are always forwarded immediately. -- (RACSignal *)throttle:(NSTimeInterval)interval; - -/// Throttles `next`s for which `predicate` returns YES. -/// -/// When `predicate` returns YES for a `next`: -/// -/// 1. If another `next` is received before `interval` seconds have passed, the -/// prior value is discarded. This happens regardless of whether the new -/// value will be throttled. -/// 2. After `interval` seconds have passed since the value was originally -/// received, it will be forwarded on the scheduler that it was received -/// upon. If +[RACScheduler currentScheduler] was nil at the time, a private -/// background scheduler is used. -/// -/// When `predicate` returns NO for a `next`, it is forwarded immediately, -/// without any throttling. -/// -/// interval - The number of seconds for which to buffer the latest value that -/// passes `predicate`. -/// predicate - Passed each `next` from the receiver, this block returns -/// whether the given value should be throttled. This argument must -/// not be nil. -/// -/// Returns a signal which sends `next` events, throttled when `predicate` -/// returns YES. Completion and errors are always forwarded immediately. -- (RACSignal *)throttle:(NSTimeInterval)interval valuesPassingTest:(BOOL (^)(id _Nullable next))predicate; - -/// Forwards `next` and `completed` events after delaying for `interval` seconds -/// on the current scheduler (on which the events were delivered). -/// -/// If +[RACScheduler currentScheduler] is nil when `next` or `completed` is -/// received, a private background scheduler is used. -/// -/// Returns a signal which sends delayed `next` and `completed` events. Errors -/// are always forwarded immediately. -- (RACSignal *)delay:(NSTimeInterval)interval; - -/// Resubscribes when the signal completes. -- (RACSignal *)repeat; - -/// Executes the given block each time a subscription is created. -/// -/// block - A block which defines the subscription side effects. Cannot be `nil`. -/// -/// Example: -/// -/// // Write new file, with backup. -/// [[[[fileManager -/// rac_createFileAtPath:path contents:data] -/// initially:^{ -/// // 2. Second, backup current file -/// [fileManager moveItemAtPath:path toPath:backupPath error:nil]; -/// }] -/// initially:^{ -/// // 1. First, acquire write lock. -/// [writeLock lock]; -/// }] -/// finally:^{ -/// [writeLock unlock]; -/// }]; -/// -/// Returns a signal that passes through all events of the receiver, plus -/// introduces side effects which occur prior to any subscription side effects -/// of the receiver. -- (RACSignal *)initially:(void (^)(void))block; - -/// Executes the given block when the signal completes or errors. -- (RACSignal *)finally:(void (^)(void))block; - -/// Divides the receiver's `next`s into buffers which deliver every `interval` -/// seconds. -/// -/// interval - The interval in which values are grouped into one buffer. -/// scheduler - The scheduler upon which the returned signal will deliver its -/// values. This must not be nil or +[RACScheduler -/// immediateScheduler]. -/// -/// Returns a signal which sends RACTuples of the buffered values at each -/// interval on `scheduler`. When the receiver completes, any currently-buffered -/// values will be sent immediately. -- (RACSignal *)bufferWithTime:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler; - -/// Collects all receiver's `next`s into a NSArray. Nil values will be converted -/// to NSNull. -/// -/// This corresponds to the `ToArray` method in Rx. -/// -/// Returns a signal which sends a single NSArray when the receiver completes -/// successfully. -- (RACSignal *)collect; - -/// Takes the last `count` `next`s after the receiving signal completes. -- (RACSignal *)takeLast:(NSUInteger)count; - -/// Combines the latest values from the receiver and the given signal into -/// RACTuples, once both have sent at least one `next`. -/// -/// Any additional `next`s will result in a new RACTuple with the latest values -/// from both signals. -/// -/// signal - The signal to combine with. This argument must not be nil. -/// -/// Returns a signal which sends RACTuples of the combined values, forwards any -/// `error` events, and completes when both input signals complete. -- (RACSignal *)combineLatestWith:(RACSignal *)signal; - -/// Combines the latest values from the given signals into RACTuples, once all -/// the signals have sent at least one `next`. -/// -/// Any additional `next`s will result in a new RACTuple with the latest values -/// from all signals. -/// -/// signals - The signals to combine. If this collection is empty, the returned -/// signal will immediately complete upon subscription. -/// -/// Returns a signal which sends RACTuples of the combined values, forwards any -/// `error` events, and completes when all input signals complete. -+ (RACSignal *)combineLatest:(id)signals; - -/// Combines signals using +combineLatest:, then reduces the resulting tuples -/// into a single value using -reduceEach:. -/// -/// signals - The signals to combine. If this collection is empty, the -/// returned signal will immediately complete upon subscription. -/// reduceBlock - The block which reduces the latest values from all the -/// signals into one value. It must take as many arguments as the -/// number of signals given. Each argument will be an object -/// argument. The return value must be an object. This argument -/// must not be nil. -/// -/// Example: -/// -/// [RACSignal combineLatest:@[ stringSignal, intSignal ] reduce:^(NSString *string, NSNumber *number) { -/// return [NSString stringWithFormat:@"%@: %@", string, number]; -/// }]; -/// -/// Returns a signal which sends the results from each invocation of -/// `reduceBlock`. -+ (RACSignal *)combineLatest:(id)signals reduce:(id (^)())reduceBlock; - -/// Merges the receiver and the given signal with `+merge:` and returns the -/// resulting signal. -- (RACSignal *)merge:(RACSignal *)signal; - -/// Sends the latest `next` from any of the signals. -/// -/// Returns a signal that passes through values from each of the given signals, -/// and sends `completed` when all of them complete. If any signal sends an error, -/// the returned signal sends `error` immediately. -+ (RACSignal *)merge:(id)signals; - -/// Merges the signals sent by the receiver into a flattened signal, but only -/// subscribes to `maxConcurrent` number of signals at a time. New signals are -/// queued and subscribed to as other signals complete. -/// -/// If an error occurs on any of the signals, it is sent on the returned signal. -/// It completes only after the receiver and all sent signals have completed. -/// -/// This corresponds to `Merge(IObservable>, Int32)` -/// in Rx. -/// -/// maxConcurrent - the maximum number of signals to subscribe to at a -/// time. If 0, it subscribes to an unlimited number of -/// signals. -- (RACSignal *)flatten:(NSUInteger)maxConcurrent; - -/// Ignores all `next`s from the receiver, waits for the receiver to complete, -/// then subscribes to a new signal. -/// -/// block - A block which will create or obtain a new signal to subscribe to, -/// executed only after the receiver completes. This block must not be -/// nil, and it must not return a nil signal. -/// -/// Returns a signal which will pass through the events of the signal created in -/// `block`. If the receiver errors out, the returned signal will error as well. -- (RACSignal *)then:(RACSignal * (^)(void))block; - -/// Concats the inner signals of a signal of signals. -- (RACSignal *)concat; - -/// Aggregates the `next` values of the receiver into a single combined value. -/// -/// The algorithm proceeds as follows: -/// -/// 1. `start` is passed into the block as the `running` value, and the first -/// element of the receiver is passed into the block as the `next` value. -/// 2. The result of the invocation (`running`) and the next element of the -/// receiver (`next`) is passed into `reduceBlock`. -/// 3. Steps 2 and 3 are repeated until all values have been processed. -/// 4. The last result of `reduceBlock` is sent on the returned signal. -/// -/// This method is similar to -scanWithStart:reduce:, except that only the -/// final result is sent on the returned signal. -/// -/// start - The value to be combined with the first element of the -/// receiver. This value may be `nil`. -/// reduceBlock - The block that describes how to combine values of the -/// receiver. If the receiver is empty, this block will never be -/// invoked. Cannot be nil. -/// -/// Returns a signal that will send the aggregated value when the receiver -/// completes, then itself complete. If the receiver never sends any values, -/// `start` will be sent instead. -- (RACSignal *)aggregateWithStart:(id)start reduce:(id (^)(id running, id next))reduceBlock; - -/// Aggregates the `next` values of the receiver into a single combined value. -/// This is indexed version of -aggregateWithStart:reduce:. -/// -/// start - The value to be combined with the first element of the -/// receiver. This value may be `nil`. -/// reduceBlock - The block that describes how to combine values of the -/// receiver. This block takes zero-based index value as the last -/// parameter. If the receiver is empty, this block will never be -/// invoked. Cannot be nil. -/// -/// Returns a signal that will send the aggregated value when the receiver -/// completes, then itself complete. If the receiver never sends any values, -/// `start` will be sent instead. -- (RACSignal *)aggregateWithStart:(id)start reduceWithIndex:(id (^)(id running, id next, NSUInteger index))reduceBlock; - -/// Aggregates the `next` values of the receiver into a single combined value. -/// -/// This invokes `startFactory` block on each subscription, then calls -/// -aggregateWithStart:reduce: with the return value of the block as start value. -/// -/// startFactory - The block that returns start value which will be combined -/// with the first element of the receiver. Cannot be nil. -/// reduceBlock - The block that describes how to combine values of the -/// receiver. If the receiver is empty, this block will never be -/// invoked. Cannot be nil. -/// -/// Returns a signal that will send the aggregated value when the receiver -/// completes, then itself complete. If the receiver never sends any values, -/// the return value of `startFactory` will be sent instead. -- (RACSignal *)aggregateWithStartFactory:(id (^)(void))startFactory reduce:(id (^)(id running, id next))reduceBlock; - -/// Invokes -setKeyPath:onObject:nilValue: with `nil` for the nil value. -/// -/// WARNING: Under certain conditions, this method is known to be thread-unsafe. -/// See the description in -setKeyPath:onObject:nilValue:. -- (RACDisposable *)setKeyPath:(NSString *)keyPath onObject:(NSObject *)object; - -/// Binds the receiver to an object, automatically setting the given key path on -/// every `next`. When the signal completes, the binding is automatically -/// disposed of. -/// -/// WARNING: Under certain conditions, this method is known to be thread-unsafe. -/// A crash can result if `object` is deallocated concurrently on -/// another thread within a window of time between a value being sent -/// on this signal and immediately prior to the invocation of -/// -setValue:forKeyPath:, which sets the property. To prevent this, -/// ensure `object` is deallocated on the same thread the receiver -/// sends on, or ensure that the returned disposable is disposed of -/// before `object` deallocates. -/// See https://github.com/ReactiveCocoa/ReactiveCocoa/pull/1184 -/// -/// Sending an error on the signal is considered undefined behavior, and will -/// generate an assertion failure in Debug builds. -/// -/// A given key on an object should only have one active signal bound to it at any -/// given time. Binding more than one signal to the same property is considered -/// undefined behavior. -/// -/// keyPath - The key path to update with `next`s from the receiver. -/// object - The object that `keyPath` is relative to. -/// nilValue - The value to set at the key path whenever `nil` is sent by the -/// receiver. This may be nil when binding to object properties, but -/// an NSValue should be used for primitive properties, to avoid an -/// exception if `nil` is sent (which might occur if an intermediate -/// object is set to `nil`). -/// -/// Returns a disposable which can be used to terminate the binding. -- (RACDisposable *)setKeyPath:(NSString *)keyPath onObject:(NSObject *)object nilValue:(nullable id)nilValue; - -/// Sends NSDate.date every `interval` seconds. -/// -/// interval - The time interval in seconds at which the current time is sent. -/// scheduler - The scheduler upon which the current NSDate should be sent. This -/// must not be nil or +[RACScheduler immediateScheduler]. -/// -/// Returns a signal that sends the current date/time every `interval` on -/// `scheduler`. -+ (RACSignal *)interval:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler; - -/// Sends NSDate.date at intervals of at least `interval` seconds, up to -/// approximately `interval` + `leeway` seconds. -/// -/// The created signal will defer sending each `next` for at least `interval` -/// seconds, and for an additional amount of time up to `leeway` seconds in the -/// interest of performance or power consumption. Note that some additional -/// latency is to be expected, even when specifying a `leeway` of 0. -/// -/// interval - The base interval between `next`s. -/// scheduler - The scheduler upon which the current NSDate should be sent. This -/// must not be nil or +[RACScheduler immediateScheduler]. -/// leeway - The maximum amount of additional time the `next` can be deferred. -/// -/// Returns a signal that sends the current date/time at intervals of at least -/// `interval seconds` up to approximately `interval` + `leeway` seconds on -/// `scheduler`. -+ (RACSignal *)interval:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler withLeeway:(NSTimeInterval)leeway; - -/// Takes `next`s until the `signalTrigger` sends `next` or `completed`. -/// -/// Returns a signal which passes through all events from the receiver until -/// `signalTrigger` sends `next` or `completed`, at which point the returned signal -/// will send `completed`. -- (RACSignal *)takeUntil:(RACSignal *)signalTrigger; - -/// Takes `next`s until the `replacement` sends an event. -/// -/// replacement - The signal which replaces the receiver as soon as it sends an -/// event. -/// -/// Returns a signal which passes through `next`s and `error` from the receiver -/// until `replacement` sends an event, at which point the returned signal will -/// send that event and switch to passing through events from `replacement` -/// instead, regardless of whether the receiver has sent events already. -- (RACSignal *)takeUntilReplacement:(RACSignal *)replacement; - -/// Subscribes to the returned signal when an error occurs. -- (RACSignal *)catch:(RACSignal * (^)(NSError * _Nonnull error))catchBlock; - -/// Subscribes to the given signal when an error occurs. -- (RACSignal *)catchTo:(RACSignal *)signal; - -/// Returns a signal that will either immediately send the return value of -/// `tryBlock` and complete, or error using the `NSError` passed out from the -/// block. -/// -/// tryBlock - An action that performs some computation that could fail. If the -/// block returns nil, the block must return an error via the -/// `errorPtr` parameter. -/// -/// Example: -/// -/// [RACSignal try:^(NSError **error) { -/// return [NSJSONSerialization JSONObjectWithData:someJSONData options:0 error:error]; -/// }]; -+ (RACSignal *)try:(id (^)(NSError **errorPtr))tryBlock; - -/// Runs `tryBlock` against each of the receiver's values, passing values -/// until `tryBlock` returns NO, or the receiver completes. -/// -/// tryBlock - An action to run against each of the receiver's values. -/// The block should return YES to indicate that the action was -/// successful. This block must not be nil. -/// -/// Example: -/// -/// // The returned signal will send an error if data values cannot be -/// // written to `someFileURL`. -/// [signal try:^(NSData *data, NSError **errorPtr) { -/// return [data writeToURL:someFileURL options:NSDataWritingAtomic error:errorPtr]; -/// }]; -/// -/// Returns a signal which passes through all the values of the receiver. If -/// `tryBlock` fails for any value, the returned signal will error using the -/// `NSError` passed out from the block. -- (RACSignal *)try:(BOOL (^)(id _Nullable value, NSError **errorPtr))tryBlock; - -/// Runs `mapBlock` against each of the receiver's values, mapping values until -/// `mapBlock` returns nil, or the receiver completes. -/// -/// mapBlock - An action to map each of the receiver's values. The block should -/// return a non-nil value to indicate that the action was successful. -/// This block must not be nil. -/// -/// Example: -/// -/// // The returned signal will send an error if data cannot be read from -/// // `fileURL`. -/// [signal tryMap:^(NSURL *fileURL, NSError **errorPtr) { -/// return [NSData dataWithContentsOfURL:fileURL options:0 error:errorPtr]; -/// }]; -/// -/// Returns a signal which transforms all the values of the receiver. If -/// `mapBlock` returns nil for any value, the returned signal will error using -/// the `NSError` passed out from the block. -- (RACSignal *)tryMap:(id (^)(id _Nullable value, NSError **errorPtr))mapBlock; - -/// Returns the first `next`. Note that this is a blocking call. -- (nullable id)first; - -/// Returns the first `next` or `defaultValue` if the signal completes or errors -/// without sending a `next`. Note that this is a blocking call. -- (nullable id)firstOrDefault:(nullable id)defaultValue; - -/// Returns the first `next` or `defaultValue` if the signal completes or errors -/// without sending a `next`. If an error occurs success will be NO and error -/// will be populated. Note that this is a blocking call. -/// -/// Both success and error may be NULL. -- (nullable id)firstOrDefault:(nullable id)defaultValue success:(nullable BOOL *)success error:(NSError * _Nullable * _Nullable)error; - -/// Blocks the caller and waits for the signal to complete. -/// -/// error - If not NULL, set to any error that occurs. -/// -/// Returns whether the signal completed successfully. If NO, `error` will be set -/// to the error that occurred. -- (BOOL)waitUntilCompleted:(NSError * _Nullable * _Nullable)error; - -/// Defers creation of a signal until the signal's actually subscribed to. -/// -/// This can be used to effectively turn a hot signal into a cold signal. -+ (RACSignal *)defer:(RACSignal * (^)(void))block; - -/// Every time the receiver sends a new RACSignal, subscribes and sends `next`s and -/// `error`s only for that signal. -/// -/// The receiver must be a signal of signals. -/// -/// Returns a signal which passes through `next`s and `error`s from the latest -/// signal sent by the receiver, and sends `completed` when both the receiver and -/// the last sent signal complete. -- (RACSignal *)switchToLatest; - -/// Switches between the signals in `cases` as well as `defaultSignal` based on -/// the latest value sent by `signal`. -/// -/// signal - A signal of objects used as keys in the `cases` dictionary. -/// This argument must not be nil. -/// cases - A dictionary that has signals as values. This argument must -/// not be nil. A RACTupleNil key in this dictionary will match -/// nil `next` events that are received on `signal`. -/// defaultSignal - The signal to pass through after `signal` sends a value for -/// which `cases` does not contain a signal. If nil, any -/// unmatched values will result in -/// a RACSignalErrorNoMatchingCase error. -/// -/// Returns a signal which passes through `next`s and `error`s from one of the -/// the signals in `cases` or `defaultSignal`, and sends `completed` when both -/// `signal` and the last used signal complete. If no `defaultSignal` is given, -/// an unmatched `next` will result in an error on the returned signal. -+ (RACSignal *)switch:(RACSignal *)signal cases:(NSDictionary *)cases default:(nullable RACSignal *)defaultSignal; - -/// Switches between `trueSignal` and `falseSignal` based on the latest value -/// sent by `boolSignal`. -/// -/// boolSignal - A signal of BOOLs determining whether `trueSignal` or -/// `falseSignal` should be active. This argument must not be nil. -/// trueSignal - The signal to pass through after `boolSignal` has sent YES. -/// This argument must not be nil. -/// falseSignal - The signal to pass through after `boolSignal` has sent NO. This -/// argument must not be nil. -/// -/// Returns a signal which passes through `next`s and `error`s from `trueSignal` -/// and/or `falseSignal`, and sends `completed` when both `boolSignal` and the -/// last switched signal complete. -+ (RACSignal *)if:(RACSignal *)boolSignal then:(RACSignal *)trueSignal else:(RACSignal *)falseSignal; - -/// Adds every `next` to an array. Nils are represented by NSNulls. Note that -/// this is a blocking call. -/// -/// **This is not the same as the `ToArray` method in Rx.** See -collect for -/// that behavior instead. -/// -/// Returns the array of `next` values, or nil if an error occurs. -- (nullable NSArray *)toArray; - -/// Adds every `next` to a sequence. Nils are represented by NSNulls. -/// -/// This corresponds to the `ToEnumerable` method in Rx. -/// -/// Returns a sequence which provides values from the signal as they're sent. -/// Trying to retrieve a value from the sequence which has not yet been sent will -/// block. -@property (nonatomic, strong, readonly) RACSequence *sequence; - -/// Creates and returns a multicast connection. This allows you to share a single -/// subscription to the underlying signal. -- (RACMulticastConnection *)publish; - -/// Creates and returns a multicast connection that pushes values into the given -/// subject. This allows you to share a single subscription to the underlying -/// signal. -- (RACMulticastConnection *)multicast:(RACSubject *)subject; - -/// Multicasts the signal to a RACReplaySubject of unlimited capacity, and -/// immediately connects to the resulting RACMulticastConnection. -/// -/// Returns the connected, multicasted signal. -- (RACSignal *)replay; - -/// Multicasts the signal to a RACReplaySubject of capacity 1, and immediately -/// connects to the resulting RACMulticastConnection. -/// -/// Returns the connected, multicasted signal. -- (RACSignal *)replayLast; - -/// Multicasts the signal to a RACReplaySubject of unlimited capacity, and -/// lazily connects to the resulting RACMulticastConnection. -/// -/// This means the returned signal will subscribe to the multicasted signal only -/// when the former receives its first subscription. -/// -/// Returns the lazily connected, multicasted signal. -- (RACSignal *)replayLazily; - -/// Sends an error after `interval` seconds if the source doesn't complete -/// before then. -/// -/// The error will be in the RACSignalErrorDomain and have a code of -/// RACSignalErrorTimedOut. -/// -/// interval - The number of seconds after which the signal should error out. -/// scheduler - The scheduler upon which any timeout error should be sent. This -/// must not be nil or +[RACScheduler immediateScheduler]. -/// -/// Returns a signal that passes through the receiver's events, until the stream -/// finishes or times out, at which point an error will be sent on `scheduler`. -- (RACSignal *)timeout:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler; - -/// Creates and returns a signal that delivers its events on the given scheduler. -/// Any side effects of the receiver will still be performed on the original -/// thread. -/// -/// This is ideal when the signal already performs its work on the desired -/// thread, but you want to handle its events elsewhere. -/// -/// This corresponds to the `ObserveOn` method in Rx. -- (RACSignal *)deliverOn:(RACScheduler *)scheduler; - -/// Creates and returns a signal that executes its side effects and delivers its -/// events on the given scheduler. -/// -/// Use of this operator should be avoided whenever possible, because the -/// receiver's side effects may not be safe to run on another thread. If you just -/// want to receive the signal's events on `scheduler`, use -deliverOn: instead. -- (RACSignal *)subscribeOn:(RACScheduler *)scheduler; - -/// Creates and returns a signal that delivers its events on the main thread. -/// If events are already being sent on the main thread, they may be passed on -/// without delay. An event will instead be queued for later delivery on the main -/// thread if sent on another thread, or if a previous event is already being -/// processed, or has been queued. -/// -/// Any side effects of the receiver will still be performed on the original -/// thread. -/// -/// This can be used when a signal will cause UI updates, to avoid potential -/// flicker caused by delayed delivery of events, such as the first event from -/// a RACObserve at view instantiation. -- (RACSignal *)deliverOnMainThread; - -/// Groups each received object into a group, as determined by calling `keyBlock` -/// with that object. The object sent is transformed by calling `transformBlock` -/// with the object. If `transformBlock` is nil, it sends the original object. -/// -/// The returned signal is a signal of RACGroupedSignal. -- (RACSignal *)groupBy:(id _Nullable (^)(id _Nullable object))keyBlock transform:(nullable id _Nullable (^)(id _Nullable object))transformBlock; - -/// Calls -[RACSignal groupBy:keyBlock transform:nil]. -- (RACSignal *)groupBy:(id _Nullable (^)(id _Nullable object))keyBlock; - -/// Sends an [NSNumber numberWithBool:YES] if the receiving signal sends any -/// objects. -- (RACSignal *)any; - -/// Sends an [NSNumber numberWithBool:YES] if the receiving signal sends any -/// objects that pass `predicateBlock`. -/// -/// predicateBlock - cannot be nil. -- (RACSignal *)any:(BOOL (^)(id _Nullable object))predicateBlock; - -/// Sends an [NSNumber numberWithBool:YES] if all the objects the receiving -/// signal sends pass `predicateBlock`. -/// -/// predicateBlock - cannot be nil. -- (RACSignal *)all:(BOOL (^)(id _Nullable object))predicateBlock; - -/// Resubscribes to the receiving signal if an error occurs, up until it has -/// retried the given number of times. -/// -/// retryCount - if 0, it keeps retrying until it completes. -- (RACSignal *)retry:(NSInteger)retryCount; - -/// Resubscribes to the receiving signal if an error occurs. -- (RACSignal *)retry; - -/// Sends the latest value from the receiver only when `sampler` sends a value. -/// The returned signal could repeat values if `sampler` fires more often than -/// the receiver. Values from `sampler` are ignored before the receiver sends -/// its first value. -/// -/// sampler - The signal that controls when the latest value from the receiver -/// is sent. Cannot be nil. -- (RACSignal *)sample:(RACSignal *)sampler; - -/// Ignores all `next`s from the receiver. -/// -/// Returns a signal which only passes through `error` or `completed` events from -/// the receiver. -- (RACSignal *)ignoreValues; - -/// Converts each of the receiver's events into a RACEvent object. -/// -/// Returns a signal which sends the receiver's events as RACEvents, and -/// completes after the receiver sends `completed` or `error`. -- (RACSignal *)materialize; - -/// Converts each RACEvent in the receiver back into "real" RACSignal events. -/// -/// Returns a signal which sends `next` for each value RACEvent, `error` for each -/// error RACEvent, and `completed` for each completed RACEvent. -- (RACSignal *)dematerialize; - -/// Inverts each NSNumber-wrapped BOOL sent by the receiver. It will assert if -/// the receiver sends anything other than NSNumbers. -/// -/// Returns a signal of inverted NSNumber-wrapped BOOLs. -- (RACSignal *)not; - -/// Performs a boolean AND on all of the RACTuple of NSNumbers in sent by the receiver. -/// -/// Asserts if the receiver sends anything other than a RACTuple of one or more NSNumbers. -/// -/// Returns a signal that applies AND to each NSNumber in the tuple. -- (RACSignal *)and; - -/// Performs a boolean OR on all of the RACTuple of NSNumbers in sent by the receiver. -/// -/// Asserts if the receiver sends anything other than a RACTuple of one or more NSNumbers. -/// -/// Returns a signal that applies OR to each NSNumber in the tuple. -- (RACSignal *)or; - -/// Sends the result of calling the block with arguments as packed in each RACTuple -/// sent by the receiver. -/// -/// The receiver must send tuple values, where the first element of the tuple is -/// a block, taking a number of parameters equal to the count of the remaining -/// elements of the tuple, and returning an object. Each block must take at least -/// one argument, so each tuple must contain at least 2 elements. -/// -/// Example: -/// -/// RACSignal *adder = [RACSignal return:^(NSNumber *a, NSNumber *b) { -/// return @(a.intValue + b.intValue); -/// }]; -/// RACSignal *sums = [[RACSignal -/// combineLatest:@[ adder, as, bs ]] -/// reduceApply]; -/// -/// Returns a signal of the result of applying the first element of each tuple -/// to the remaining elements. -- (RACSignal *)reduceApply; - -NS_ASSUME_NONNULL_END -@end - -@interface RACSignal (UnavailableOperations) -NS_ASSUME_NONNULL_BEGIN - -- (RACSignal *)windowWithStart:(RACSignal *)openSignal close:(RACSignal * (^)(RACSignal *start))closeBlock __attribute__((unavailable("See https://github.com/ReactiveCocoa/ReactiveCocoa/issues/587"))); -- (RACSignal *)buffer:(NSUInteger)bufferCount __attribute__((unavailable("See https://github.com/ReactiveCocoa/ReactiveCocoa/issues/587"))); -- (RACSignal *)let:(RACSignal * (^)(RACSignal *sharedSignal))letBlock __attribute__((unavailable("Use -publish instead"))); -+ (RACSignal *)interval:(NSTimeInterval)interval __attribute__((unavailable("Use +interval:onScheduler: instead"))); -+ (RACSignal *)interval:(NSTimeInterval)interval withLeeway:(NSTimeInterval)leeway __attribute__((unavailable("Use +interval:onScheduler:withLeeway: instead"))); -- (RACSignal *)bufferWithTime:(NSTimeInterval)interval __attribute__((unavailable("Use -bufferWithTime:onScheduler: instead"))); -- (RACSignal *)timeout:(NSTimeInterval)interval __attribute__((unavailable("Use -timeout:onScheduler: instead"))); -- (RACDisposable *)toProperty:(NSString *)keyPath onObject:(NSObject *)object __attribute__((unavailable("Renamed to -setKeyPath:onObject:"))); -- (RACSignal *)ignoreElements __attribute__((unavailable("Renamed to -ignoreValues"))); -- (RACSignal *)sequenceNext:(RACSignal * (^)(void))block __attribute__((unavailable("Renamed to -then:"))); -- (RACSignal *)aggregateWithStart:(nullable id)start combine:(id _Nullable (^)(id _Nullable running, id _Nullable next))combineBlock __attribute__((unavailable("Renamed to -aggregateWithStart:reduce:"))); -- (RACSignal *)aggregateWithStartFactory:(id _Nullable (^)(void))startFactory combine:(id _Nullable (^)(id _Nullable running, id _Nullable next))combineBlock __attribute__((unavailable("Renamed to -aggregateWithStartFactory:reduce:"))); -- (RACDisposable *)executeCommand:(RACCommand *)command __attribute__((unavailable("Use -flattenMap: or -subscribeNext: instead"))); - -NS_ASSUME_NONNULL_END -@end diff --git a/ReactiveObjC/RACSignal+Operations.m b/ReactiveObjC/RACSignal+Operations.m deleted file mode 100644 index 23a9fa6f5f..0000000000 --- a/ReactiveObjC/RACSignal+Operations.m +++ /dev/null @@ -1,1333 +0,0 @@ -// -// RACSignal+Operations.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-09-06. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACSignal+Operations.h" -#import "NSObject+RACDeallocating.h" -#import "NSObject+RACDescription.h" -#import "RACBlockTrampoline.h" -#import "RACCommand.h" -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACEvent.h" -#import "RACGroupedSignal.h" -#import "RACMulticastConnection+Private.h" -#import "RACReplaySubject.h" -#import "RACScheduler.h" -#import "RACSerialDisposable.h" -#import "RACSignalSequence.h" -#import "RACStream+Private.h" -#import "RACSubject.h" -#import "RACSubscriber+Private.h" -#import "RACSubscriber.h" -#import "RACTuple.h" -#import "RACUnit.h" -#import -#import - -NSString * const RACSignalErrorDomain = @"RACSignalErrorDomain"; - -const NSInteger RACSignalErrorTimedOut = 1; -const NSInteger RACSignalErrorNoMatchingCase = 2; - -// Subscribes to the given signal with the given blocks. -// -// If the signal errors or completes, the corresponding block is invoked. If the -// disposable passed to the block is _not_ disposed, then the signal is -// subscribed to again. -static RACDisposable *subscribeForever (RACSignal *signal, void (^next)(id), void (^error)(NSError *, RACDisposable *), void (^completed)(RACDisposable *)) { - next = [next copy]; - error = [error copy]; - completed = [completed copy]; - - RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable]; - - RACSchedulerRecursiveBlock recursiveBlock = ^(void (^recurse)(void)) { - RACCompoundDisposable *selfDisposable = [RACCompoundDisposable compoundDisposable]; - [compoundDisposable addDisposable:selfDisposable]; - - __weak RACDisposable *weakSelfDisposable = selfDisposable; - - RACDisposable *subscriptionDisposable = [signal subscribeNext:next error:^(NSError *e) { - @autoreleasepool { - error(e, compoundDisposable); - [compoundDisposable removeDisposable:weakSelfDisposable]; - } - - recurse(); - } completed:^{ - @autoreleasepool { - completed(compoundDisposable); - [compoundDisposable removeDisposable:weakSelfDisposable]; - } - - recurse(); - }]; - - [selfDisposable addDisposable:subscriptionDisposable]; - }; - - // Subscribe once immediately, and then use recursive scheduling for any - // further resubscriptions. - recursiveBlock(^{ - RACScheduler *recursiveScheduler = RACScheduler.currentScheduler ?: [RACScheduler scheduler]; - - RACDisposable *schedulingDisposable = [recursiveScheduler scheduleRecursiveBlock:recursiveBlock]; - [compoundDisposable addDisposable:schedulingDisposable]; - }); - - return compoundDisposable; -} - -@implementation RACSignal (Operations) - -- (RACSignal *)doNext:(void (^)(id x))block { - NSCParameterAssert(block != NULL); - - return [[RACSignal createSignal:^(id subscriber) { - return [self subscribeNext:^(id x) { - block(x); - [subscriber sendNext:x]; - } error:^(NSError *error) { - [subscriber sendError:error]; - } completed:^{ - [subscriber sendCompleted]; - }]; - }] setNameWithFormat:@"[%@] -doNext:", self.name]; -} - -- (RACSignal *)doError:(void (^)(NSError *error))block { - NSCParameterAssert(block != NULL); - - return [[RACSignal createSignal:^(id subscriber) { - return [self subscribeNext:^(id x) { - [subscriber sendNext:x]; - } error:^(NSError *error) { - block(error); - [subscriber sendError:error]; - } completed:^{ - [subscriber sendCompleted]; - }]; - }] setNameWithFormat:@"[%@] -doError:", self.name]; -} - -- (RACSignal *)doCompleted:(void (^)(void))block { - NSCParameterAssert(block != NULL); - - return [[RACSignal createSignal:^(id subscriber) { - return [self subscribeNext:^(id x) { - [subscriber sendNext:x]; - } error:^(NSError *error) { - [subscriber sendError:error]; - } completed:^{ - block(); - [subscriber sendCompleted]; - }]; - }] setNameWithFormat:@"[%@] -doCompleted:", self.name]; -} - -- (RACSignal *)throttle:(NSTimeInterval)interval { - return [[self throttle:interval valuesPassingTest:^(id _) { - return YES; - }] setNameWithFormat:@"[%@] -throttle: %f", self.name, (double)interval]; -} - -- (RACSignal *)throttle:(NSTimeInterval)interval valuesPassingTest:(BOOL (^)(id next))predicate { - NSCParameterAssert(interval >= 0); - NSCParameterAssert(predicate != nil); - - return [[RACSignal createSignal:^(id subscriber) { - RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable]; - - // We may never use this scheduler, but we need to set it up ahead of - // time so that our scheduled blocks are run serially if we do. - RACScheduler *scheduler = [RACScheduler scheduler]; - - // Information about any currently-buffered `next` event. - __block id nextValue = nil; - __block BOOL hasNextValue = NO; - RACSerialDisposable *nextDisposable = [[RACSerialDisposable alloc] init]; - - void (^flushNext)(BOOL send) = ^(BOOL send) { - @synchronized (compoundDisposable) { - [nextDisposable.disposable dispose]; - - if (!hasNextValue) return; - if (send) [subscriber sendNext:nextValue]; - - nextValue = nil; - hasNextValue = NO; - } - }; - - RACDisposable *subscriptionDisposable = [self subscribeNext:^(id x) { - RACScheduler *delayScheduler = RACScheduler.currentScheduler ?: scheduler; - BOOL shouldThrottle = predicate(x); - - @synchronized (compoundDisposable) { - flushNext(NO); - if (!shouldThrottle) { - [subscriber sendNext:x]; - return; - } - - nextValue = x; - hasNextValue = YES; - nextDisposable.disposable = [delayScheduler afterDelay:interval schedule:^{ - flushNext(YES); - }]; - } - } error:^(NSError *error) { - [compoundDisposable dispose]; - [subscriber sendError:error]; - } completed:^{ - flushNext(YES); - [subscriber sendCompleted]; - }]; - - [compoundDisposable addDisposable:subscriptionDisposable]; - return compoundDisposable; - }] setNameWithFormat:@"[%@] -throttle: %f valuesPassingTest:", self.name, (double)interval]; -} - -- (RACSignal *)delay:(NSTimeInterval)interval { - return [[RACSignal createSignal:^(id subscriber) { - RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable]; - - // We may never use this scheduler, but we need to set it up ahead of - // time so that our scheduled blocks are run serially if we do. - RACScheduler *scheduler = [RACScheduler scheduler]; - - void (^schedule)(dispatch_block_t) = ^(dispatch_block_t block) { - RACScheduler *delayScheduler = RACScheduler.currentScheduler ?: scheduler; - RACDisposable *schedulerDisposable = [delayScheduler afterDelay:interval schedule:block]; - [disposable addDisposable:schedulerDisposable]; - }; - - RACDisposable *subscriptionDisposable = [self subscribeNext:^(id x) { - schedule(^{ - [subscriber sendNext:x]; - }); - } error:^(NSError *error) { - [subscriber sendError:error]; - } completed:^{ - schedule(^{ - [subscriber sendCompleted]; - }); - }]; - - [disposable addDisposable:subscriptionDisposable]; - return disposable; - }] setNameWithFormat:@"[%@] -delay: %f", self.name, (double)interval]; -} - -- (RACSignal *)repeat { - return [[RACSignal createSignal:^(id subscriber) { - return subscribeForever(self, - ^(id x) { - [subscriber sendNext:x]; - }, - ^(NSError *error, RACDisposable *disposable) { - [disposable dispose]; - [subscriber sendError:error]; - }, - ^(RACDisposable *disposable) { - // Resubscribe. - }); - }] setNameWithFormat:@"[%@] -repeat", self.name]; -} - -- (RACSignal *)catch:(RACSignal * (^)(NSError *error))catchBlock { - NSCParameterAssert(catchBlock != NULL); - - return [[RACSignal createSignal:^(id subscriber) { - RACSerialDisposable *catchDisposable = [[RACSerialDisposable alloc] init]; - - RACDisposable *subscriptionDisposable = [self subscribeNext:^(id x) { - [subscriber sendNext:x]; - } error:^(NSError *error) { - RACSignal *signal = catchBlock(error); - NSCAssert(signal != nil, @"Expected non-nil signal from catch block on %@", self); - catchDisposable.disposable = [signal subscribe:subscriber]; - } completed:^{ - [subscriber sendCompleted]; - }]; - - return [RACDisposable disposableWithBlock:^{ - [catchDisposable dispose]; - [subscriptionDisposable dispose]; - }]; - }] setNameWithFormat:@"[%@] -catch:", self.name]; -} - -- (RACSignal *)catchTo:(RACSignal *)signal { - return [[self catch:^(NSError *error) { - return signal; - }] setNameWithFormat:@"[%@] -catchTo: %@", self.name, signal]; -} - -+ (RACSignal *)try:(id (^)(NSError **errorPtr))tryBlock { - NSCParameterAssert(tryBlock != NULL); - - return [[RACSignal createSignal:^(id subscriber) { - NSError *error; - id value = tryBlock(&error); - RACSignal *signal = (value == nil ? [RACSignal error:error] : [RACSignal return:value]); - return [signal subscribe:subscriber]; - }] setNameWithFormat:@"+try:"]; -} - -- (RACSignal *)try:(BOOL (^)(id value, NSError **errorPtr))tryBlock { - NSCParameterAssert(tryBlock != NULL); - - return [[self flattenMap:^(id value) { - NSError *error = nil; - BOOL passed = tryBlock(value, &error); - return (passed ? [RACSignal return:value] : [RACSignal error:error]); - }] setNameWithFormat:@"[%@] -try:", self.name]; -} - -- (RACSignal *)tryMap:(id (^)(id value, NSError **errorPtr))mapBlock { - NSCParameterAssert(mapBlock != NULL); - - return [[self flattenMap:^(id value) { - NSError *error = nil; - id mappedValue = mapBlock(value, &error); - return (mappedValue == nil ? [RACSignal error:error] : [RACSignal return:mappedValue]); - }] setNameWithFormat:@"[%@] -tryMap:", self.name]; -} - -- (RACSignal *)initially:(void (^)(void))block { - NSCParameterAssert(block != NULL); - - return [[RACSignal defer:^{ - block(); - return self; - }] setNameWithFormat:@"[%@] -initially:", self.name]; -} - -- (RACSignal *)finally:(void (^)(void))block { - NSCParameterAssert(block != NULL); - - return [[[self - doError:^(NSError *error) { - block(); - }] - doCompleted:^{ - block(); - }] - setNameWithFormat:@"[%@] -finally:", self.name]; -} - -- (RACSignal *)bufferWithTime:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler { - NSCParameterAssert(scheduler != nil); - NSCParameterAssert(scheduler != RACScheduler.immediateScheduler); - - return [[RACSignal createSignal:^(id subscriber) { - RACSerialDisposable *timerDisposable = [[RACSerialDisposable alloc] init]; - NSMutableArray *values = [NSMutableArray array]; - - void (^flushValues)() = ^{ - @synchronized (values) { - [timerDisposable.disposable dispose]; - - if (values.count == 0) return; - - RACTuple *tuple = [RACTuple tupleWithObjectsFromArray:values]; - [values removeAllObjects]; - [subscriber sendNext:tuple]; - } - }; - - RACDisposable *selfDisposable = [self subscribeNext:^(id x) { - @synchronized (values) { - if (values.count == 0) { - timerDisposable.disposable = [scheduler afterDelay:interval schedule:flushValues]; - } - - [values addObject:x ?: RACTupleNil.tupleNil]; - } - } error:^(NSError *error) { - [subscriber sendError:error]; - } completed:^{ - flushValues(); - [subscriber sendCompleted]; - }]; - - return [RACDisposable disposableWithBlock:^{ - [selfDisposable dispose]; - [timerDisposable dispose]; - }]; - }] setNameWithFormat:@"[%@] -bufferWithTime: %f onScheduler: %@", self.name, (double)interval, scheduler]; -} - -- (RACSignal *)collect { - return [[self aggregateWithStartFactory:^{ - return [[NSMutableArray alloc] init]; - } reduce:^(NSMutableArray *collectedValues, id x) { - [collectedValues addObject:(x ?: NSNull.null)]; - return collectedValues; - }] setNameWithFormat:@"[%@] -collect", self.name]; -} - -- (RACSignal *)takeLast:(NSUInteger)count { - return [[RACSignal createSignal:^(id subscriber) { - NSMutableArray *valuesTaken = [NSMutableArray arrayWithCapacity:count]; - return [self subscribeNext:^(id x) { - [valuesTaken addObject:x ? : RACTupleNil.tupleNil]; - - while (valuesTaken.count > count) { - [valuesTaken removeObjectAtIndex:0]; - } - } error:^(NSError *error) { - [subscriber sendError:error]; - } completed:^{ - for (id value in valuesTaken) { - [subscriber sendNext:value == RACTupleNil.tupleNil ? nil : value]; - } - - [subscriber sendCompleted]; - }]; - }] setNameWithFormat:@"[%@] -takeLast: %lu", self.name, (unsigned long)count]; -} - -- (RACSignal *)combineLatestWith:(RACSignal *)signal { - NSCParameterAssert(signal != nil); - - return [[RACSignal createSignal:^(id subscriber) { - RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable]; - - __block id lastSelfValue = nil; - __block BOOL selfCompleted = NO; - - __block id lastOtherValue = nil; - __block BOOL otherCompleted = NO; - - void (^sendNext)(void) = ^{ - @synchronized (disposable) { - if (lastSelfValue == nil || lastOtherValue == nil) return; - [subscriber sendNext:RACTuplePack(lastSelfValue, lastOtherValue)]; - } - }; - - RACDisposable *selfDisposable = [self subscribeNext:^(id x) { - @synchronized (disposable) { - lastSelfValue = x ?: RACTupleNil.tupleNil; - sendNext(); - } - } error:^(NSError *error) { - [subscriber sendError:error]; - } completed:^{ - @synchronized (disposable) { - selfCompleted = YES; - if (otherCompleted) [subscriber sendCompleted]; - } - }]; - - [disposable addDisposable:selfDisposable]; - - RACDisposable *otherDisposable = [signal subscribeNext:^(id x) { - @synchronized (disposable) { - lastOtherValue = x ?: RACTupleNil.tupleNil; - sendNext(); - } - } error:^(NSError *error) { - [subscriber sendError:error]; - } completed:^{ - @synchronized (disposable) { - otherCompleted = YES; - if (selfCompleted) [subscriber sendCompleted]; - } - }]; - - [disposable addDisposable:otherDisposable]; - - return disposable; - }] setNameWithFormat:@"[%@] -combineLatestWith: %@", self.name, signal]; -} - -+ (RACSignal *)combineLatest:(id)signals { - return [[self join:signals block:^(RACSignal *left, RACSignal *right) { - return [left combineLatestWith:right]; - }] setNameWithFormat:@"+combineLatest: %@", signals]; -} - -+ (RACSignal *)combineLatest:(id)signals reduce:(id (^)())reduceBlock { - NSCParameterAssert(reduceBlock != nil); - - RACSignal *result = [self combineLatest:signals]; - - // Although we assert this condition above, older versions of this method - // supported this argument being nil. Avoid crashing Release builds of - // apps that depended on that. - if (reduceBlock != nil) result = [result reduceEach:reduceBlock]; - - return [result setNameWithFormat:@"+combineLatest: %@ reduce:", signals]; -} - -- (RACSignal *)merge:(RACSignal *)signal { - return [[RACSignal - merge:@[ self, signal ]] - setNameWithFormat:@"[%@] -merge: %@", self.name, signal]; -} - -+ (RACSignal *)merge:(id)signals { - NSMutableArray *copiedSignals = [[NSMutableArray alloc] init]; - for (RACSignal *signal in signals) { - [copiedSignals addObject:signal]; - } - - return [[[RACSignal - createSignal:^ RACDisposable * (id subscriber) { - for (RACSignal *signal in copiedSignals) { - [subscriber sendNext:signal]; - } - - [subscriber sendCompleted]; - return nil; - }] - flatten] - setNameWithFormat:@"+merge: %@", copiedSignals]; -} - -- (RACSignal *)flatten:(NSUInteger)maxConcurrent { - return [[RACSignal createSignal:^(id subscriber) { - RACCompoundDisposable *compoundDisposable = [[RACCompoundDisposable alloc] init]; - - // Contains disposables for the currently active subscriptions. - // - // This should only be used while synchronized on `subscriber`. - NSMutableArray *activeDisposables = [[NSMutableArray alloc] initWithCapacity:maxConcurrent]; - - // Whether the signal-of-signals has completed yet. - // - // This should only be used while synchronized on `subscriber`. - __block BOOL selfCompleted = NO; - - // Subscribes to the given signal. - __block void (^subscribeToSignal)(RACSignal *); - - // Weak reference to the above, to avoid a leak. - __weak __block void (^recur)(RACSignal *); - - // Sends completed to the subscriber if all signals are finished. - // - // This should only be used while synchronized on `subscriber`. - void (^completeIfAllowed)(void) = ^{ - if (selfCompleted && activeDisposables.count == 0) { - [subscriber sendCompleted]; - } - }; - - // The signals waiting to be started. - // - // This array should only be used while synchronized on `subscriber`. - NSMutableArray *queuedSignals = [NSMutableArray array]; - - recur = subscribeToSignal = ^(RACSignal *signal) { - RACSerialDisposable *serialDisposable = [[RACSerialDisposable alloc] init]; - - @synchronized (subscriber) { - [compoundDisposable addDisposable:serialDisposable]; - [activeDisposables addObject:serialDisposable]; - } - - serialDisposable.disposable = [signal subscribeNext:^(id x) { - [subscriber sendNext:x]; - } error:^(NSError *error) { - [subscriber sendError:error]; - } completed:^{ - __strong void (^subscribeToSignal)(RACSignal *) = recur; - RACSignal *nextSignal; - - @synchronized (subscriber) { - [compoundDisposable removeDisposable:serialDisposable]; - [activeDisposables removeObjectIdenticalTo:serialDisposable]; - - if (queuedSignals.count == 0) { - completeIfAllowed(); - return; - } - - nextSignal = queuedSignals[0]; - [queuedSignals removeObjectAtIndex:0]; - } - - subscribeToSignal(nextSignal); - }]; - }; - - [compoundDisposable addDisposable:[self subscribeNext:^(RACSignal *signal) { - if (signal == nil) return; - - NSCAssert([signal isKindOfClass:RACSignal.class], @"Expected a RACSignal, got %@", signal); - - @synchronized (subscriber) { - if (maxConcurrent > 0 && activeDisposables.count >= maxConcurrent) { - [queuedSignals addObject:signal]; - - // If we need to wait, skip subscribing to this - // signal. - return; - } - } - - subscribeToSignal(signal); - } error:^(NSError *error) { - [subscriber sendError:error]; - } completed:^{ - @synchronized (subscriber) { - selfCompleted = YES; - completeIfAllowed(); - } - }]]; - - [compoundDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - // A strong reference is held to `subscribeToSignal` until we're - // done, preventing it from deallocating early. - subscribeToSignal = nil; - }]]; - - return compoundDisposable; - }] setNameWithFormat:@"[%@] -flatten: %lu", self.name, (unsigned long)maxConcurrent]; -} - -- (RACSignal *)then:(RACSignal * (^)(void))block { - NSCParameterAssert(block != nil); - - return [[[self - ignoreValues] - concat:[RACSignal defer:block]] - setNameWithFormat:@"[%@] -then:", self.name]; -} - -- (RACSignal *)concat { - return [[self flatten:1] setNameWithFormat:@"[%@] -concat", self.name]; -} - -- (RACSignal *)aggregateWithStartFactory:(id (^)(void))startFactory reduce:(id (^)(id running, id next))reduceBlock { - NSCParameterAssert(startFactory != NULL); - NSCParameterAssert(reduceBlock != NULL); - - return [[RACSignal defer:^{ - return [self aggregateWithStart:startFactory() reduce:reduceBlock]; - }] setNameWithFormat:@"[%@] -aggregateWithStartFactory:reduce:", self.name]; -} - -- (RACSignal *)aggregateWithStart:(id)start reduce:(id (^)(id running, id next))reduceBlock { - return [[self - aggregateWithStart:start - reduceWithIndex:^(id running, id next, NSUInteger index) { - return reduceBlock(running, next); - }] - setNameWithFormat:@"[%@] -aggregateWithStart: %@ reduce:", self.name, RACDescription(start)]; -} - -- (RACSignal *)aggregateWithStart:(id)start reduceWithIndex:(id (^)(id, id, NSUInteger))reduceBlock { - return [[[[self - scanWithStart:start reduceWithIndex:reduceBlock] - startWith:start] - takeLast:1] - setNameWithFormat:@"[%@] -aggregateWithStart: %@ reduceWithIndex:", self.name, RACDescription(start)]; -} - -- (RACDisposable *)setKeyPath:(NSString *)keyPath onObject:(NSObject *)object { - return [self setKeyPath:keyPath onObject:object nilValue:nil]; -} - -- (RACDisposable *)setKeyPath:(NSString *)keyPath onObject:(NSObject *)object nilValue:(id)nilValue { - NSCParameterAssert(keyPath != nil); - NSCParameterAssert(object != nil); - - keyPath = [keyPath copy]; - - RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable]; - - // Purposely not retaining 'object', since we want to tear down the binding - // when it deallocates normally. - __block void * volatile objectPtr = (__bridge void *)object; - - RACDisposable *subscriptionDisposable = [self subscribeNext:^(id x) { - // Possibly spec, possibly compiler bug, but this __bridge cast does not - // result in a retain here, effectively an invisible __unsafe_unretained - // qualifier. Using objc_precise_lifetime gives the __strong reference - // desired. The explicit use of __strong is strictly defensive. - __strong NSObject *object __attribute__((objc_precise_lifetime)) = (__bridge __strong id)objectPtr; - [object setValue:x ?: nilValue forKeyPath:keyPath]; - } error:^(NSError *error) { - __strong NSObject *object __attribute__((objc_precise_lifetime)) = (__bridge __strong id)objectPtr; - - NSCAssert(NO, @"Received error from %@ in binding for key path \"%@\" on %@: %@", self, keyPath, object, error); - - // Log the error if we're running with assertions disabled. - NSLog(@"Received error from %@ in binding for key path \"%@\" on %@: %@", self, keyPath, object, error); - - [disposable dispose]; - } completed:^{ - [disposable dispose]; - }]; - - [disposable addDisposable:subscriptionDisposable]; - - #if DEBUG - static void *bindingsKey = &bindingsKey; - NSMutableDictionary *bindings; - - @synchronized (object) { - bindings = objc_getAssociatedObject(object, bindingsKey); - if (bindings == nil) { - bindings = [NSMutableDictionary dictionary]; - objc_setAssociatedObject(object, bindingsKey, bindings, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - } - } - - @synchronized (bindings) { - NSCAssert(bindings[keyPath] == nil, @"Signal %@ is already bound to key path \"%@\" on object %@, adding signal %@ is undefined behavior", [bindings[keyPath] nonretainedObjectValue], keyPath, object, self); - - bindings[keyPath] = [NSValue valueWithNonretainedObject:self]; - } - #endif - - RACDisposable *clearPointerDisposable = [RACDisposable disposableWithBlock:^{ - #if DEBUG - @synchronized (bindings) { - [bindings removeObjectForKey:keyPath]; - } - #endif - - while (YES) { - void *ptr = objectPtr; - if (OSAtomicCompareAndSwapPtrBarrier(ptr, NULL, &objectPtr)) { - break; - } - } - }]; - - [disposable addDisposable:clearPointerDisposable]; - - [object.rac_deallocDisposable addDisposable:disposable]; - - RACCompoundDisposable *objectDisposable = object.rac_deallocDisposable; - return [RACDisposable disposableWithBlock:^{ - [objectDisposable removeDisposable:disposable]; - [disposable dispose]; - }]; -} - -+ (RACSignal *)interval:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler { - return [[RACSignal interval:interval onScheduler:scheduler withLeeway:0.0] setNameWithFormat:@"+interval: %f onScheduler: %@", (double)interval, scheduler]; -} - -+ (RACSignal *)interval:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler withLeeway:(NSTimeInterval)leeway { - NSCParameterAssert(scheduler != nil); - NSCParameterAssert(scheduler != RACScheduler.immediateScheduler); - - return [[RACSignal createSignal:^(id subscriber) { - return [scheduler after:[NSDate dateWithTimeIntervalSinceNow:interval] repeatingEvery:interval withLeeway:leeway schedule:^{ - [subscriber sendNext:[NSDate date]]; - }]; - }] setNameWithFormat:@"+interval: %f onScheduler: %@ withLeeway: %f", (double)interval, scheduler, (double)leeway]; -} - -- (RACSignal *)takeUntil:(RACSignal *)signalTrigger { - return [[RACSignal createSignal:^(id subscriber) { - RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable]; - void (^triggerCompletion)(void) = ^{ - [disposable dispose]; - [subscriber sendCompleted]; - }; - - RACDisposable *triggerDisposable = [signalTrigger subscribeNext:^(id _) { - triggerCompletion(); - } completed:^{ - triggerCompletion(); - }]; - - [disposable addDisposable:triggerDisposable]; - - if (!disposable.disposed) { - RACDisposable *selfDisposable = [self subscribeNext:^(id x) { - [subscriber sendNext:x]; - } error:^(NSError *error) { - [subscriber sendError:error]; - } completed:^{ - [disposable dispose]; - [subscriber sendCompleted]; - }]; - - [disposable addDisposable:selfDisposable]; - } - - return disposable; - }] setNameWithFormat:@"[%@] -takeUntil: %@", self.name, signalTrigger]; -} - -- (RACSignal *)takeUntilReplacement:(RACSignal *)replacement { - return [RACSignal createSignal:^(id subscriber) { - RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init]; - - RACDisposable *replacementDisposable = [replacement subscribeNext:^(id x) { - [selfDisposable dispose]; - [subscriber sendNext:x]; - } error:^(NSError *error) { - [selfDisposable dispose]; - [subscriber sendError:error]; - } completed:^{ - [selfDisposable dispose]; - [subscriber sendCompleted]; - }]; - - if (!selfDisposable.disposed) { - selfDisposable.disposable = [[self - concat:[RACSignal never]] - subscribe:subscriber]; - } - - return [RACDisposable disposableWithBlock:^{ - [selfDisposable dispose]; - [replacementDisposable dispose]; - }]; - }]; -} - -- (RACSignal *)switchToLatest { - return [[RACSignal createSignal:^(id subscriber) { - RACMulticastConnection *connection = [self publish]; - - RACDisposable *subscriptionDisposable = [[connection.signal - flattenMap:^(RACSignal *x) { - NSCAssert(x == nil || [x isKindOfClass:RACSignal.class], @"-switchToLatest requires that the source signal (%@) send signals. Instead we got: %@", self, x); - - // -concat:[RACSignal never] prevents completion of the receiver from - // prematurely terminating the inner signal. - return [x takeUntil:[connection.signal concat:[RACSignal never]]]; - }] - subscribe:subscriber]; - - RACDisposable *connectionDisposable = [connection connect]; - return [RACDisposable disposableWithBlock:^{ - [subscriptionDisposable dispose]; - [connectionDisposable dispose]; - }]; - }] setNameWithFormat:@"[%@] -switchToLatest", self.name]; -} - -+ (RACSignal *)switch:(RACSignal *)signal cases:(NSDictionary *)cases default:(RACSignal *)defaultSignal { - NSCParameterAssert(signal != nil); - NSCParameterAssert(cases != nil); - - for (id key in cases) { - id value __attribute__((unused)) = cases[key]; - NSCAssert([value isKindOfClass:RACSignal.class], @"Expected all cases to be RACSignals, %@ isn't", value); - } - - NSDictionary *copy = [cases copy]; - - return [[[signal - map:^(id key) { - if (key == nil) key = RACTupleNil.tupleNil; - - RACSignal *signal = copy[key] ?: defaultSignal; - if (signal == nil) { - NSString *description = [NSString stringWithFormat:NSLocalizedString(@"No matching signal found for value %@", @""), key]; - return [RACSignal error:[NSError errorWithDomain:RACSignalErrorDomain code:RACSignalErrorNoMatchingCase userInfo:@{ NSLocalizedDescriptionKey: description }]]; - } - - return signal; - }] - switchToLatest] - setNameWithFormat:@"+switch: %@ cases: %@ default: %@", signal, cases, defaultSignal]; -} - -+ (RACSignal *)if:(RACSignal *)boolSignal then:(RACSignal *)trueSignal else:(RACSignal *)falseSignal { - NSCParameterAssert(boolSignal != nil); - NSCParameterAssert(trueSignal != nil); - NSCParameterAssert(falseSignal != nil); - - return [[[boolSignal - map:^(NSNumber *value) { - NSCAssert([value isKindOfClass:NSNumber.class], @"Expected %@ to send BOOLs, not %@", boolSignal, value); - - return (value.boolValue ? trueSignal : falseSignal); - }] - switchToLatest] - setNameWithFormat:@"+if: %@ then: %@ else: %@", boolSignal, trueSignal, falseSignal]; -} - -- (id)first { - return [self firstOrDefault:nil]; -} - -- (id)firstOrDefault:(id)defaultValue { - return [self firstOrDefault:defaultValue success:NULL error:NULL]; -} - -- (id)firstOrDefault:(id)defaultValue success:(BOOL *)success error:(NSError **)error { - NSCondition *condition = [[NSCondition alloc] init]; - condition.name = [NSString stringWithFormat:@"[%@] -firstOrDefault: %@ success:error:", self.name, defaultValue]; - - __block id value = defaultValue; - __block BOOL done = NO; - - // Ensures that we don't pass values across thread boundaries by reference. - __block NSError *localError; - __block BOOL localSuccess; - - [[self take:1] subscribeNext:^(id x) { - [condition lock]; - - value = x; - localSuccess = YES; - - done = YES; - [condition broadcast]; - [condition unlock]; - } error:^(NSError *e) { - [condition lock]; - - if (!done) { - localSuccess = NO; - localError = e; - - done = YES; - [condition broadcast]; - } - - [condition unlock]; - } completed:^{ - [condition lock]; - - localSuccess = YES; - - done = YES; - [condition broadcast]; - [condition unlock]; - }]; - - [condition lock]; - while (!done) { - [condition wait]; - } - - if (success != NULL) *success = localSuccess; - if (error != NULL) *error = localError; - - [condition unlock]; - return value; -} - -- (BOOL)waitUntilCompleted:(NSError **)error { - BOOL success = NO; - - [[[self - ignoreValues] - setNameWithFormat:@"[%@] -waitUntilCompleted:", self.name] - firstOrDefault:nil success:&success error:error]; - - return success; -} - -+ (RACSignal *)defer:(RACSignal * (^)(void))block { - NSCParameterAssert(block != NULL); - - return [[RACSignal createSignal:^(id subscriber) { - return [block() subscribe:subscriber]; - }] setNameWithFormat:@"+defer:"]; -} - -- (NSArray *)toArray { - return [[[self collect] first] copy]; -} - -- (RACSequence *)sequence { - return [[RACSignalSequence sequenceWithSignal:self] setNameWithFormat:@"[%@] -sequence", self.name]; -} - -- (RACMulticastConnection *)publish { - RACSubject *subject = [[RACSubject subject] setNameWithFormat:@"[%@] -publish", self.name]; - RACMulticastConnection *connection = [self multicast:subject]; - return connection; -} - -- (RACMulticastConnection *)multicast:(RACSubject *)subject { - [subject setNameWithFormat:@"[%@] -multicast: %@", self.name, subject.name]; - RACMulticastConnection *connection = [[RACMulticastConnection alloc] initWithSourceSignal:self subject:subject]; - return connection; -} - -- (RACSignal *)replay { - RACReplaySubject *subject = [[RACReplaySubject subject] setNameWithFormat:@"[%@] -replay", self.name]; - - RACMulticastConnection *connection = [self multicast:subject]; - [connection connect]; - - return connection.signal; -} - -- (RACSignal *)replayLast { - RACReplaySubject *subject = [[RACReplaySubject replaySubjectWithCapacity:1] setNameWithFormat:@"[%@] -replayLast", self.name]; - - RACMulticastConnection *connection = [self multicast:subject]; - [connection connect]; - - return connection.signal; -} - -- (RACSignal *)replayLazily { - RACMulticastConnection *connection = [self multicast:[RACReplaySubject subject]]; - return [[RACSignal - defer:^{ - [connection connect]; - return connection.signal; - }] - setNameWithFormat:@"[%@] -replayLazily", self.name]; -} - -- (RACSignal *)timeout:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler { - NSCParameterAssert(scheduler != nil); - NSCParameterAssert(scheduler != RACScheduler.immediateScheduler); - - return [[RACSignal createSignal:^(id subscriber) { - RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable]; - - RACDisposable *timeoutDisposable = [scheduler afterDelay:interval schedule:^{ - [disposable dispose]; - [subscriber sendError:[NSError errorWithDomain:RACSignalErrorDomain code:RACSignalErrorTimedOut userInfo:nil]]; - }]; - - [disposable addDisposable:timeoutDisposable]; - - RACDisposable *subscriptionDisposable = [self subscribeNext:^(id x) { - [subscriber sendNext:x]; - } error:^(NSError *error) { - [disposable dispose]; - [subscriber sendError:error]; - } completed:^{ - [disposable dispose]; - [subscriber sendCompleted]; - }]; - - [disposable addDisposable:subscriptionDisposable]; - return disposable; - }] setNameWithFormat:@"[%@] -timeout: %f onScheduler: %@", self.name, (double)interval, scheduler]; -} - -- (RACSignal *)deliverOn:(RACScheduler *)scheduler { - return [[RACSignal createSignal:^(id subscriber) { - return [self subscribeNext:^(id x) { - [scheduler schedule:^{ - [subscriber sendNext:x]; - }]; - } error:^(NSError *error) { - [scheduler schedule:^{ - [subscriber sendError:error]; - }]; - } completed:^{ - [scheduler schedule:^{ - [subscriber sendCompleted]; - }]; - }]; - }] setNameWithFormat:@"[%@] -deliverOn: %@", self.name, scheduler]; -} - -- (RACSignal *)subscribeOn:(RACScheduler *)scheduler { - return [[RACSignal createSignal:^(id subscriber) { - RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable]; - - RACDisposable *schedulingDisposable = [scheduler schedule:^{ - RACDisposable *subscriptionDisposable = [self subscribe:subscriber]; - - [disposable addDisposable:subscriptionDisposable]; - }]; - - [disposable addDisposable:schedulingDisposable]; - return disposable; - }] setNameWithFormat:@"[%@] -subscribeOn: %@", self.name, scheduler]; -} - -- (RACSignal *)deliverOnMainThread { - return [[RACSignal createSignal:^(id subscriber) { - __block volatile int32_t queueLength = 0; - - void (^performOnMainThread)(dispatch_block_t) = ^(dispatch_block_t block) { - int32_t queued = OSAtomicIncrement32(&queueLength); - if (NSThread.isMainThread && queued == 1) { - block(); - OSAtomicDecrement32(&queueLength); - } else { - dispatch_async(dispatch_get_main_queue(), ^{ - block(); - OSAtomicDecrement32(&queueLength); - }); - } - }; - - return [self subscribeNext:^(id x) { - performOnMainThread(^{ - [subscriber sendNext:x]; - }); - } error:^(NSError *error) { - performOnMainThread(^{ - [subscriber sendError:error]; - }); - } completed:^{ - performOnMainThread(^{ - [subscriber sendCompleted]; - }); - }]; - }] setNameWithFormat:@"[%@] -deliverOnMainThread", self.name]; -} - -- (RACSignal *)groupBy:(id (^)(id object))keyBlock transform:(id (^)(id object))transformBlock { - NSCParameterAssert(keyBlock != NULL); - - return [[RACSignal createSignal:^(id subscriber) { - NSMutableDictionary *groups = [NSMutableDictionary dictionary]; - NSMutableArray *orderedGroups = [NSMutableArray array]; - - return [self subscribeNext:^(id x) { - id key = keyBlock(x); - RACGroupedSignal *groupSubject = nil; - @synchronized(groups) { - groupSubject = groups[key]; - if (groupSubject == nil) { - groupSubject = [RACGroupedSignal signalWithKey:key]; - groups[key] = groupSubject; - [orderedGroups addObject:groupSubject]; - [subscriber sendNext:groupSubject]; - } - } - - [groupSubject sendNext:transformBlock != NULL ? transformBlock(x) : x]; - } error:^(NSError *error) { - [subscriber sendError:error]; - - [orderedGroups makeObjectsPerformSelector:@selector(sendError:) withObject:error]; - } completed:^{ - [subscriber sendCompleted]; - - [orderedGroups makeObjectsPerformSelector:@selector(sendCompleted)]; - }]; - }] setNameWithFormat:@"[%@] -groupBy:transform:", self.name]; -} - -- (RACSignal *)groupBy:(id (^)(id object))keyBlock { - return [[self groupBy:keyBlock transform:nil] setNameWithFormat:@"[%@] -groupBy:", self.name]; -} - -- (RACSignal *)any { - return [[self any:^(id x) { - return YES; - }] setNameWithFormat:@"[%@] -any", self.name]; -} - -- (RACSignal *)any:(BOOL (^)(id object))predicateBlock { - NSCParameterAssert(predicateBlock != NULL); - - return [[[self materialize] bind:^{ - return ^(RACEvent *event, BOOL *stop) { - if (event.finished) { - *stop = YES; - return [RACSignal return:@NO]; - } - - if (predicateBlock(event.value)) { - *stop = YES; - return [RACSignal return:@YES]; - } - - return [RACSignal empty]; - }; - }] setNameWithFormat:@"[%@] -any:", self.name]; -} - -- (RACSignal *)all:(BOOL (^)(id object))predicateBlock { - NSCParameterAssert(predicateBlock != NULL); - - return [[[self materialize] bind:^{ - return ^(RACEvent *event, BOOL *stop) { - if (event.eventType == RACEventTypeCompleted) { - *stop = YES; - return [RACSignal return:@YES]; - } - - if (event.eventType == RACEventTypeError || !predicateBlock(event.value)) { - *stop = YES; - return [RACSignal return:@NO]; - } - - return [RACSignal empty]; - }; - }] setNameWithFormat:@"[%@] -all:", self.name]; -} - -- (RACSignal *)retry:(NSInteger)retryCount { - return [[RACSignal createSignal:^(id subscriber) { - __block NSInteger currentRetryCount = 0; - return subscribeForever(self, - ^(id x) { - [subscriber sendNext:x]; - }, - ^(NSError *error, RACDisposable *disposable) { - if (retryCount == 0 || currentRetryCount < retryCount) { - // Resubscribe. - currentRetryCount++; - return; - } - - [disposable dispose]; - [subscriber sendError:error]; - }, - ^(RACDisposable *disposable) { - [disposable dispose]; - [subscriber sendCompleted]; - }); - }] setNameWithFormat:@"[%@] -retry: %lu", self.name, (unsigned long)retryCount]; -} - -- (RACSignal *)retry { - return [[self retry:0] setNameWithFormat:@"[%@] -retry", self.name]; -} - -- (RACSignal *)sample:(RACSignal *)sampler { - NSCParameterAssert(sampler != nil); - - return [[RACSignal createSignal:^(id subscriber) { - NSLock *lock = [[NSLock alloc] init]; - __block id lastValue; - __block BOOL hasValue = NO; - - RACSerialDisposable *samplerDisposable = [[RACSerialDisposable alloc] init]; - RACDisposable *sourceDisposable = [self subscribeNext:^(id x) { - [lock lock]; - hasValue = YES; - lastValue = x; - [lock unlock]; - } error:^(NSError *error) { - [samplerDisposable dispose]; - [subscriber sendError:error]; - } completed:^{ - [samplerDisposable dispose]; - [subscriber sendCompleted]; - }]; - - samplerDisposable.disposable = [sampler subscribeNext:^(id _) { - BOOL shouldSend = NO; - id value; - [lock lock]; - shouldSend = hasValue; - value = lastValue; - [lock unlock]; - - if (shouldSend) { - [subscriber sendNext:value]; - } - } error:^(NSError *error) { - [sourceDisposable dispose]; - [subscriber sendError:error]; - } completed:^{ - [sourceDisposable dispose]; - [subscriber sendCompleted]; - }]; - - return [RACDisposable disposableWithBlock:^{ - [samplerDisposable dispose]; - [sourceDisposable dispose]; - }]; - }] setNameWithFormat:@"[%@] -sample: %@", self.name, sampler]; -} - -- (RACSignal *)ignoreValues { - return [[self filter:^(id _) { - return NO; - }] setNameWithFormat:@"[%@] -ignoreValues", self.name]; -} - -- (RACSignal *)materialize { - return [[RACSignal createSignal:^(id subscriber) { - return [self subscribeNext:^(id x) { - [subscriber sendNext:[RACEvent eventWithValue:x]]; - } error:^(NSError *error) { - [subscriber sendNext:[RACEvent eventWithError:error]]; - [subscriber sendCompleted]; - } completed:^{ - [subscriber sendNext:RACEvent.completedEvent]; - [subscriber sendCompleted]; - }]; - }] setNameWithFormat:@"[%@] -materialize", self.name]; -} - -- (RACSignal *)dematerialize { - return [[self bind:^{ - return ^(RACEvent *event, BOOL *stop) { - switch (event.eventType) { - case RACEventTypeCompleted: - *stop = YES; - return [RACSignal empty]; - - case RACEventTypeError: - *stop = YES; - return [RACSignal error:event.error]; - - case RACEventTypeNext: - return [RACSignal return:event.value]; - } - }; - }] setNameWithFormat:@"[%@] -dematerialize", self.name]; -} - -- (RACSignal *)not { - return [[self map:^(NSNumber *value) { - NSCAssert([value isKindOfClass:NSNumber.class], @"-not must only be used on a signal of NSNumbers. Instead, got: %@", value); - - return @(!value.boolValue); - }] setNameWithFormat:@"[%@] -not", self.name]; -} - -- (RACSignal *)and { - return [[self map:^(RACTuple *tuple) { - NSCAssert([tuple isKindOfClass:RACTuple.class], @"-and must only be used on a signal of RACTuples of NSNumbers. Instead, received: %@", tuple); - NSCAssert(tuple.count > 0, @"-and must only be used on a signal of RACTuples of NSNumbers, with at least 1 value in the tuple"); - - return @([tuple.rac_sequence all:^(NSNumber *number) { - NSCAssert([number isKindOfClass:NSNumber.class], @"-and must only be used on a signal of RACTuples of NSNumbers. Instead, tuple contains a non-NSNumber value: %@", tuple); - - return number.boolValue; - }]); - }] setNameWithFormat:@"[%@] -and", self.name]; -} - -- (RACSignal *)or { - return [[self map:^(RACTuple *tuple) { - NSCAssert([tuple isKindOfClass:RACTuple.class], @"-or must only be used on a signal of RACTuples of NSNumbers. Instead, received: %@", tuple); - NSCAssert(tuple.count > 0, @"-or must only be used on a signal of RACTuples of NSNumbers, with at least 1 value in the tuple"); - - return @([tuple.rac_sequence any:^(NSNumber *number) { - NSCAssert([number isKindOfClass:NSNumber.class], @"-or must only be used on a signal of RACTuples of NSNumbers. Instead, tuple contains a non-NSNumber value: %@", tuple); - - return number.boolValue; - }]); - }] setNameWithFormat:@"[%@] -or", self.name]; -} - -- (RACSignal *)reduceApply { - return [[self map:^(RACTuple *tuple) { - NSCAssert([tuple isKindOfClass:RACTuple.class], @"-reduceApply must only be used on a signal of RACTuples. Instead, received: %@", tuple); - NSCAssert(tuple.count > 1, @"-reduceApply must only be used on a signal of RACTuples, with at least a block in tuple[0] and its first argument in tuple[1]"); - - // We can't use -array, because we need to preserve RACTupleNil - NSMutableArray *tupleArray = [NSMutableArray arrayWithCapacity:tuple.count]; - for (id val in tuple) { - [tupleArray addObject:val]; - } - RACTuple *arguments = [RACTuple tupleWithObjectsFromArray:[tupleArray subarrayWithRange:NSMakeRange(1, tupleArray.count - 1)]]; - - return [RACBlockTrampoline invokeBlock:tuple[0] withArguments:arguments]; - }] setNameWithFormat:@"[%@] -reduceApply", self.name]; -} - -@end diff --git a/ReactiveObjC/RACSignal.h b/ReactiveObjC/RACSignal.h deleted file mode 100644 index 9461b64f9c..0000000000 --- a/ReactiveObjC/RACSignal.h +++ /dev/null @@ -1,223 +0,0 @@ -// -// RACSignal.h -// ReactiveObjC -// -// Created by Josh Abernathy on 3/1/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import -#import "RACStream.h" - -@class RACDisposable; -@class RACScheduler; -@class RACSubject; -@protocol RACSubscriber; - -@interface RACSignal : RACStream -NS_ASSUME_NONNULL_BEGIN - -/// Creates a new signal. This is the preferred way to create a new signal -/// operation or behavior. -/// -/// Events can be sent to new subscribers immediately in the `didSubscribe` -/// block, but the subscriber will not be able to dispose of the signal until -/// a RACDisposable is returned from `didSubscribe`. In the case of infinite -/// signals, this won't _ever_ happen if events are sent immediately. -/// -/// To ensure that the signal is disposable, events can be scheduled on the -/// +[RACScheduler currentScheduler] (so that they're deferred, not sent -/// immediately), or they can be sent in the background. The RACDisposable -/// returned by the `didSubscribe` block should cancel any such scheduling or -/// asynchronous work. -/// -/// didSubscribe - Called when the signal is subscribed to. The new subscriber is -/// passed in. You can then manually control the by -/// sending it -sendNext:, -sendError:, and -sendCompleted, -/// as defined by the operation you're implementing. This block -/// should return a RACDisposable which cancels any ongoing work -/// triggered by the subscription, and cleans up any resources or -/// disposables created as part of it. When the disposable is -/// disposed of, the signal must not send any more events to the -/// `subscriber`. If no cleanup is necessary, return nil. -/// -/// **Note:** The `didSubscribe` block is called every time a new subscriber -/// subscribes. Any side effects within the block will thus execute once for each -/// subscription, not necessarily on one thread, and possibly even -/// simultaneously! -+ (RACSignal *)createSignal:(RACDisposable * _Nullable (^)(id subscriber))didSubscribe; - -/// Returns a signal that immediately sends the given error. -+ (RACSignal *)error:(nullable NSError *)error; - -/// Returns a signal that never completes. -+ (RACSignal *)never; - -/// Immediately schedules the given block on the given scheduler. The block is -/// given a subscriber to which it can send events. -/// -/// scheduler - The scheduler on which `block` will be scheduled and results -/// delivered. Cannot be nil. -/// block - The block to invoke. Cannot be NULL. -/// -/// Returns a signal which will send all events sent on the subscriber given to -/// `block`. All events will be sent on `scheduler` and it will replay any missed -/// events to new subscribers. -+ (RACSignal *)startEagerlyWithScheduler:(RACScheduler *)scheduler block:(void (^)(id subscriber))block; - -/// Invokes the given block only on the first subscription. The block is given a -/// subscriber to which it can send events. -/// -/// Note that disposing of the subscription to the returned signal will *not* -/// dispose of the underlying subscription. If you need that behavior, see -/// -[RACMulticastConnection autoconnect]. The underlying subscription will never -/// be disposed of. Because of this, `block` should never return an infinite -/// signal since there would be no way of ending it. -/// -/// scheduler - The scheduler on which the block should be scheduled. Note that -/// if given +[RACScheduler immediateScheduler], the block will be -/// invoked synchronously on the first subscription. Cannot be nil. -/// block - The block to invoke on the first subscription. Cannot be NULL. -/// -/// Returns a signal which will pass through the events sent to the subscriber -/// given to `block` and replay any missed events to new subscribers. -+ (RACSignal *)startLazilyWithScheduler:(RACScheduler *)scheduler block:(void (^)(id subscriber))block; - -@end - -@interface RACSignal (RACStream) - -/// Returns a signal that immediately sends the given value and then completes. -+ (RACSignal *)return:(nullable id)value; - -/// Returns a signal that immediately completes. -+ (RACSignal *)empty; - -/// Subscribes to `signal` when the source signal completes. -- (RACSignal *)concat:(RACSignal *)signal; - -/// Zips the values in the receiver with those of the given signal to create -/// RACTuples. -/// -/// The first `next` of each stream will be combined, then the second `next`, and -/// so forth, until either signal completes or errors. -/// -/// signal - The signal to zip with. This must not be `nil`. -/// -/// Returns a new signal of RACTuples, representing the combined values of the -/// two signals. Any error from one of the original signals will be forwarded on -/// the returned signal. -- (RACSignal *)zipWith:(RACSignal *)signal; - -@end - -@interface RACSignal (Subscription) - -/// Subscribes `subscriber` to changes on the receiver. The receiver defines which -/// events it actually sends and in what situations the events are sent. -/// -/// Subscription will always happen on a valid RACScheduler. If the -/// +[RACScheduler currentScheduler] cannot be determined at the time of -/// subscription (e.g., because the calling code is running on a GCD queue or -/// NSOperationQueue), subscription will occur on a private background scheduler. -/// On the main thread, subscriptions will always occur immediately, with a -/// +[RACScheduler currentScheduler] of +[RACScheduler mainThreadScheduler]. -/// -/// This method must be overridden by any subclasses. -/// -/// Returns nil or a disposable. You can call -[RACDisposable dispose] if you -/// need to end your subscription before it would "naturally" end, either by -/// completing or erroring. Once the disposable has been disposed, the subscriber -/// won't receive any more events from the subscription. -- (RACDisposable *)subscribe:(id)subscriber; - -/// Convenience method to subscribe to the `next` event. -/// -/// This corresponds to `IObserver.OnNext` in Rx. -- (RACDisposable *)subscribeNext:(void (^)(id _Nullable x))nextBlock; - -/// Convenience method to subscribe to the `next` and `completed` events. -- (RACDisposable *)subscribeNext:(void (^)(id _Nullable x))nextBlock completed:(void (^)(void))completedBlock; - -/// Convenience method to subscribe to the `next`, `completed`, and `error` events. -- (RACDisposable *)subscribeNext:(void (^)(id _Nullable x))nextBlock error:(void (^)(NSError * _Nullable error))errorBlock completed:(void (^)(void))completedBlock; - -/// Convenience method to subscribe to `error` events. -/// -/// This corresponds to the `IObserver.OnError` in Rx. -- (RACDisposable *)subscribeError:(void (^)(NSError * _Nullable error))errorBlock; - -/// Convenience method to subscribe to `completed` events. -/// -/// This corresponds to the `IObserver.OnCompleted` in Rx. -- (RACDisposable *)subscribeCompleted:(void (^)(void))completedBlock; - -/// Convenience method to subscribe to `next` and `error` events. -- (RACDisposable *)subscribeNext:(void (^)(id _Nullable x))nextBlock error:(void (^)(NSError * _Nullable error))errorBlock; - -/// Convenience method to subscribe to `error` and `completed` events. -- (RACDisposable *)subscribeError:(void (^)(NSError * _Nullable error))errorBlock completed:(void (^)(void))completedBlock; - -@end - -/// Additional methods to assist with debugging. -@interface RACSignal (Debugging) - -/// Logs all events that the receiver sends. -- (RACSignal *)logAll; - -/// Logs each `next` that the receiver sends. -- (RACSignal *)logNext; - -/// Logs any error that the receiver sends. -- (RACSignal *)logError; - -/// Logs any `completed` event that the receiver sends. -- (RACSignal *)logCompleted; - -@end - -/// Additional methods to assist with unit testing. -/// -/// **These methods should never ship in production code.** -@interface RACSignal (Testing) - -/// Spins the main run loop for a short while, waiting for the receiver to send a `next`. -/// -/// **Because this method executes the run loop recursively, it should only be used -/// on the main thread, and only from a unit test.** -/// -/// defaultValue - Returned if the receiver completes or errors before sending -/// a `next`, or if the method times out. This argument may be -/// nil. -/// success - If not NULL, set to whether the receiver completed -/// successfully. -/// error - If not NULL, set to any error that occurred. -/// -/// Returns the first value received, or `defaultValue` if no value is received -/// before the signal finishes or the method times out. -- (nullable id)asynchronousFirstOrDefault:(nullable id)defaultValue success:(nullable BOOL *)success error:(NSError * _Nullable * _Nullable)error; - -/// Spins the main run loop for a short while, waiting for the receiver to complete. -/// -/// **Because this method executes the run loop recursively, it should only be used -/// on the main thread, and only from a unit test.** -/// -/// error - If not NULL, set to any error that occurs. -/// -/// Returns whether the signal completed successfully before timing out. If NO, -/// `error` will be set to any error that occurred. -- (BOOL)asynchronouslyWaitUntilCompleted:(NSError * _Nullable * _Nullable)error; - -NS_ASSUME_NONNULL_END -@end - -@interface RACSignal (Unavailable) -NS_ASSUME_NONNULL_BEGIN - -+ (RACSignal *)start:(id (^)(BOOL *success, NSError * _Nullable * _Nullable error))block __attribute__((unavailable("Use +startEagerlyWithScheduler:block: instead"))); -+ (RACSignal *)startWithScheduler:(RACScheduler *)scheduler subjectBlock:(void (^)(RACSubject *subject))block __attribute__((unavailable("Use +startEagerlyWithScheduler:block: instead"))); -+ (RACSignal *)startWithScheduler:(RACScheduler *)scheduler block:(id (^)(BOOL *success, NSError * _Nullable * _Nullable error))block __attribute__((unavailable("Use +startEagerlyWithScheduler:block: instead"))); - -NS_ASSUME_NONNULL_END -@end diff --git a/ReactiveObjC/RACSignal.m b/ReactiveObjC/RACSignal.m deleted file mode 100644 index 7df3a276b7..0000000000 --- a/ReactiveObjC/RACSignal.m +++ /dev/null @@ -1,401 +0,0 @@ -// -// RACSignal.m -// ReactiveObjC -// -// Created by Josh Abernathy on 3/15/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACSignal.h" -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACDynamicSignal.h" -#import "RACEmptySignal.h" -#import "RACErrorSignal.h" -#import "RACMulticastConnection.h" -#import "RACReplaySubject.h" -#import "RACReturnSignal.h" -#import "RACScheduler.h" -#import "RACSerialDisposable.h" -#import "RACSignal+Operations.h" -#import "RACSubject.h" -#import "RACSubscriber+Private.h" -#import "RACTuple.h" -#import - -@implementation RACSignal - -#pragma mark Lifecycle - -+ (RACSignal *)createSignal:(RACDisposable * (^)(id subscriber))didSubscribe { - return [RACDynamicSignal createSignal:didSubscribe]; -} - -+ (RACSignal *)error:(NSError *)error { - return [RACErrorSignal error:error]; -} - -+ (RACSignal *)never { - return [[self createSignal:^ RACDisposable * (id subscriber) { - return nil; - }] setNameWithFormat:@"+never"]; -} - -+ (RACSignal *)startEagerlyWithScheduler:(RACScheduler *)scheduler block:(void (^)(id subscriber))block { - NSCParameterAssert(scheduler != nil); - NSCParameterAssert(block != NULL); - - RACSignal *signal = [self startLazilyWithScheduler:scheduler block:block]; - // Subscribe to force the lazy signal to call its block. - [[signal publish] connect]; - return [signal setNameWithFormat:@"+startEagerlyWithScheduler: %@ block:", scheduler]; -} - -+ (RACSignal *)startLazilyWithScheduler:(RACScheduler *)scheduler block:(void (^)(id subscriber))block { - NSCParameterAssert(scheduler != nil); - NSCParameterAssert(block != NULL); - - RACMulticastConnection *connection = [[RACSignal - createSignal:^ id (id subscriber) { - block(subscriber); - return nil; - }] - multicast:[RACReplaySubject subject]]; - - return [[[RACSignal - createSignal:^ id (id subscriber) { - [connection.signal subscribe:subscriber]; - [connection connect]; - return nil; - }] - subscribeOn:scheduler] - setNameWithFormat:@"+startLazilyWithScheduler: %@ block:", scheduler]; -} - -#pragma mark NSObject - -- (NSString *)description { - return [NSString stringWithFormat:@"<%@: %p> name: %@", self.class, self, self.name]; -} - -@end - -@implementation RACSignal (RACStream) - -+ (RACSignal *)empty { - return [RACEmptySignal empty]; -} - -+ (RACSignal *)return:(id)value { - return [RACReturnSignal return:value]; -} - -- (RACSignal *)bind:(RACStreamBindBlock (^)(void))block { - NSCParameterAssert(block != NULL); - - /* - * -bind: should: - * - * 1. Subscribe to the original signal of values. - * 2. Any time the original signal sends a value, transform it using the binding block. - * 3. If the binding block returns a signal, subscribe to it, and pass all of its values through to the subscriber as they're received. - * 4. If the binding block asks the bind to terminate, complete the _original_ signal. - * 5. When _all_ signals complete, send completed to the subscriber. - * - * If any signal sends an error at any point, send that to the subscriber. - */ - - return [[RACSignal createSignal:^(id subscriber) { - RACStreamBindBlock bindingBlock = block(); - - __block volatile int32_t signalCount = 1; // indicates self - - RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable]; - - void (^completeSignal)(RACDisposable *) = ^(RACDisposable *finishedDisposable) { - if (OSAtomicDecrement32Barrier(&signalCount) == 0) { - [subscriber sendCompleted]; - [compoundDisposable dispose]; - } else { - [compoundDisposable removeDisposable:finishedDisposable]; - } - }; - - void (^addSignal)(RACSignal *) = ^(RACSignal *signal) { - OSAtomicIncrement32Barrier(&signalCount); - - RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init]; - [compoundDisposable addDisposable:selfDisposable]; - - RACDisposable *disposable = [signal subscribeNext:^(id x) { - [subscriber sendNext:x]; - } error:^(NSError *error) { - [compoundDisposable dispose]; - [subscriber sendError:error]; - } completed:^{ - @autoreleasepool { - completeSignal(selfDisposable); - } - }]; - - selfDisposable.disposable = disposable; - }; - - @autoreleasepool { - RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init]; - [compoundDisposable addDisposable:selfDisposable]; - - RACDisposable *bindingDisposable = [self subscribeNext:^(id x) { - // Manually check disposal to handle synchronous errors. - if (compoundDisposable.disposed) return; - - BOOL stop = NO; - id signal = bindingBlock(x, &stop); - - @autoreleasepool { - if (signal != nil) addSignal(signal); - if (signal == nil || stop) { - [selfDisposable dispose]; - completeSignal(selfDisposable); - } - } - } error:^(NSError *error) { - [compoundDisposable dispose]; - [subscriber sendError:error]; - } completed:^{ - @autoreleasepool { - completeSignal(selfDisposable); - } - }]; - - selfDisposable.disposable = bindingDisposable; - } - - return compoundDisposable; - }] setNameWithFormat:@"[%@] -bind:", self.name]; -} - -- (RACSignal *)concat:(RACSignal *)signal { - return [[RACSignal createSignal:^(id subscriber) { - RACCompoundDisposable *compoundDisposable = [[RACCompoundDisposable alloc] init]; - - RACDisposable *sourceDisposable = [self subscribeNext:^(id x) { - [subscriber sendNext:x]; - } error:^(NSError *error) { - [subscriber sendError:error]; - } completed:^{ - RACDisposable *concattedDisposable = [signal subscribe:subscriber]; - [compoundDisposable addDisposable:concattedDisposable]; - }]; - - [compoundDisposable addDisposable:sourceDisposable]; - return compoundDisposable; - }] setNameWithFormat:@"[%@] -concat: %@", self.name, signal]; -} - -- (RACSignal *)zipWith:(RACSignal *)signal { - NSCParameterAssert(signal != nil); - - return [[RACSignal createSignal:^(id subscriber) { - __block BOOL selfCompleted = NO; - NSMutableArray *selfValues = [NSMutableArray array]; - - __block BOOL otherCompleted = NO; - NSMutableArray *otherValues = [NSMutableArray array]; - - void (^sendCompletedIfNecessary)(void) = ^{ - @synchronized (selfValues) { - BOOL selfEmpty = (selfCompleted && selfValues.count == 0); - BOOL otherEmpty = (otherCompleted && otherValues.count == 0); - if (selfEmpty || otherEmpty) [subscriber sendCompleted]; - } - }; - - void (^sendNext)(void) = ^{ - @synchronized (selfValues) { - if (selfValues.count == 0) return; - if (otherValues.count == 0) return; - - RACTuple *tuple = RACTuplePack(selfValues[0], otherValues[0]); - [selfValues removeObjectAtIndex:0]; - [otherValues removeObjectAtIndex:0]; - - [subscriber sendNext:tuple]; - sendCompletedIfNecessary(); - } - }; - - RACDisposable *selfDisposable = [self subscribeNext:^(id x) { - @synchronized (selfValues) { - [selfValues addObject:x ?: RACTupleNil.tupleNil]; - sendNext(); - } - } error:^(NSError *error) { - [subscriber sendError:error]; - } completed:^{ - @synchronized (selfValues) { - selfCompleted = YES; - sendCompletedIfNecessary(); - } - }]; - - RACDisposable *otherDisposable = [signal subscribeNext:^(id x) { - @synchronized (selfValues) { - [otherValues addObject:x ?: RACTupleNil.tupleNil]; - sendNext(); - } - } error:^(NSError *error) { - [subscriber sendError:error]; - } completed:^{ - @synchronized (selfValues) { - otherCompleted = YES; - sendCompletedIfNecessary(); - } - }]; - - return [RACDisposable disposableWithBlock:^{ - [selfDisposable dispose]; - [otherDisposable dispose]; - }]; - }] setNameWithFormat:@"[%@] -zipWith: %@", self.name, signal]; -} - -@end - -@implementation RACSignal (Subscription) - -- (RACDisposable *)subscribe:(id)subscriber { - NSCAssert(NO, @"This method must be overridden by subclasses"); - return nil; -} - -- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock { - NSCParameterAssert(nextBlock != NULL); - - RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL]; - return [self subscribe:o]; -} - -- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock completed:(void (^)(void))completedBlock { - NSCParameterAssert(nextBlock != NULL); - NSCParameterAssert(completedBlock != NULL); - - RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:completedBlock]; - return [self subscribe:o]; -} - -- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock completed:(void (^)(void))completedBlock { - NSCParameterAssert(nextBlock != NULL); - NSCParameterAssert(errorBlock != NULL); - NSCParameterAssert(completedBlock != NULL); - - RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:errorBlock completed:completedBlock]; - return [self subscribe:o]; -} - -- (RACDisposable *)subscribeError:(void (^)(NSError *error))errorBlock { - NSCParameterAssert(errorBlock != NULL); - - RACSubscriber *o = [RACSubscriber subscriberWithNext:NULL error:errorBlock completed:NULL]; - return [self subscribe:o]; -} - -- (RACDisposable *)subscribeCompleted:(void (^)(void))completedBlock { - NSCParameterAssert(completedBlock != NULL); - - RACSubscriber *o = [RACSubscriber subscriberWithNext:NULL error:NULL completed:completedBlock]; - return [self subscribe:o]; -} - -- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock { - NSCParameterAssert(nextBlock != NULL); - NSCParameterAssert(errorBlock != NULL); - - RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:errorBlock completed:NULL]; - return [self subscribe:o]; -} - -- (RACDisposable *)subscribeError:(void (^)(NSError *))errorBlock completed:(void (^)(void))completedBlock { - NSCParameterAssert(completedBlock != NULL); - NSCParameterAssert(errorBlock != NULL); - - RACSubscriber *o = [RACSubscriber subscriberWithNext:NULL error:errorBlock completed:completedBlock]; - return [self subscribe:o]; -} - -@end - -@implementation RACSignal (Debugging) - -- (RACSignal *)logAll { - return [[[self logNext] logError] logCompleted]; -} - -- (RACSignal *)logNext { - return [[self doNext:^(id x) { - NSLog(@"%@ next: %@", self, x); - }] setNameWithFormat:@"%@", self.name]; -} - -- (RACSignal *)logError { - return [[self doError:^(NSError *error) { - NSLog(@"%@ error: %@", self, error); - }] setNameWithFormat:@"%@", self.name]; -} - -- (RACSignal *)logCompleted { - return [[self doCompleted:^{ - NSLog(@"%@ completed", self); - }] setNameWithFormat:@"%@", self.name]; -} - -@end - -@implementation RACSignal (Testing) - -static const NSTimeInterval RACSignalAsynchronousWaitTimeout = 10; - -- (id)asynchronousFirstOrDefault:(id)defaultValue success:(BOOL *)success error:(NSError **)error { - NSCAssert([NSThread isMainThread], @"%s should only be used from the main thread", __func__); - - __block id result = defaultValue; - __block BOOL done = NO; - - // Ensures that we don't pass values across thread boundaries by reference. - __block NSError *localError; - __block BOOL localSuccess = YES; - - [[[[self - take:1] - timeout:RACSignalAsynchronousWaitTimeout onScheduler:[RACScheduler scheduler]] - deliverOn:RACScheduler.mainThreadScheduler] - subscribeNext:^(id x) { - result = x; - done = YES; - } error:^(NSError *e) { - if (!done) { - localSuccess = NO; - localError = e; - done = YES; - } - } completed:^{ - done = YES; - }]; - - do { - [NSRunLoop.mainRunLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; - } while (!done); - - if (success != NULL) *success = localSuccess; - if (error != NULL) *error = localError; - - return result; -} - -- (BOOL)asynchronouslyWaitUntilCompleted:(NSError **)error { - BOOL success = NO; - [[self ignoreValues] asynchronousFirstOrDefault:nil success:&success error:error]; - return success; -} - -@end diff --git a/ReactiveObjC/RACSignalProvider.d b/ReactiveObjC/RACSignalProvider.d deleted file mode 100644 index 8add9a105d..0000000000 --- a/ReactiveObjC/RACSignalProvider.d +++ /dev/null @@ -1,5 +0,0 @@ -provider RACSignal { - probe next(char *signal, char *subscriber, char *valueDescription); - probe completed(char *signal, char *subscriber); - probe error(char *signal, char *subscriber, char *errorDescription); -}; diff --git a/ReactiveObjC/RACSignalSequence.h b/ReactiveObjC/RACSignalSequence.h deleted file mode 100644 index fd0c487e8a..0000000000 --- a/ReactiveObjC/RACSignalSequence.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// RACSignalSequence.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-11-09. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACSequence.h" - -@class RACSignal; - -// Private class that adapts a RACSignal to the RACSequence interface. -@interface RACSignalSequence : RACSequence - -// Returns a sequence for enumerating over the given signal. -+ (RACSequence *)sequenceWithSignal:(RACSignal *)signal; - -@end diff --git a/ReactiveObjC/RACSignalSequence.m b/ReactiveObjC/RACSignalSequence.m deleted file mode 100644 index 14cea6b14e..0000000000 --- a/ReactiveObjC/RACSignalSequence.m +++ /dev/null @@ -1,79 +0,0 @@ -// -// RACSignalSequence.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-11-09. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACSignalSequence.h" -#import "RACDisposable.h" -#import "RACReplaySubject.h" -#import "RACSignal+Operations.h" - -@interface RACSignalSequence () - -// Replays the signal given on initialization. -@property (nonatomic, strong, readonly) RACReplaySubject *subject; - -@end - -@implementation RACSignalSequence - -#pragma mark Lifecycle - -+ (RACSequence *)sequenceWithSignal:(RACSignal *)signal { - RACSignalSequence *seq = [[self alloc] init]; - - RACReplaySubject *subject = [RACReplaySubject subject]; - [signal subscribeNext:^(id value) { - [subject sendNext:value]; - } error:^(NSError *error) { - [subject sendError:error]; - } completed:^{ - [subject sendCompleted]; - }]; - - seq->_subject = subject; - return seq; -} - -#pragma mark RACSequence - -- (id)head { - id value = [self.subject firstOrDefault:self]; - - if (value == self) { - return nil; - } else { - return value ?: NSNull.null; - } -} - -- (RACSequence *)tail { - RACSequence *sequence = [self.class sequenceWithSignal:[self.subject skip:1]]; - sequence.name = self.name; - return sequence; -} - -- (NSArray *)array { - return self.subject.toArray; -} - -#pragma mark NSObject - -- (NSString *)description { - // Synchronously accumulate the values that have been sent so far. - NSMutableArray *values = [NSMutableArray array]; - RACDisposable *disposable = [self.subject subscribeNext:^(id value) { - @synchronized (values) { - [values addObject:value ?: NSNull.null]; - } - }]; - - [disposable dispose]; - - return [NSString stringWithFormat:@"<%@: %p>{ name = %@, values = %@ … }", self.class, self, self.name, values]; -} - -@end diff --git a/ReactiveObjC/RACStream+Private.h b/ReactiveObjC/RACStream+Private.h deleted file mode 100644 index 385baf19cb..0000000000 --- a/ReactiveObjC/RACStream+Private.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// RACStream+Private.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-07-22. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACStream.h" - -@interface RACStream () - -// Combines a list of streams using the logic of the given block. -// -// streams - The streams to combine. -// block - An operator that combines two streams and returns a new one. The -// returned stream should contain 2-tuples of the streams' combined -// values. -// -// Returns a combined stream. -+ (instancetype)join:(id)streams block:(RACStream * (^)(id, id))block; - -@end diff --git a/ReactiveObjC/RACStream.h b/ReactiveObjC/RACStream.h deleted file mode 100644 index 7ae3ce9f7a..0000000000 --- a/ReactiveObjC/RACStream.h +++ /dev/null @@ -1,335 +0,0 @@ -// -// RACStream.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-10-31. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import - -@class RACStream; - -/// A block which accepts a value from a RACStream and returns a new instance -/// of the same stream class. -/// -/// Setting `stop` to `YES` will cause the bind to terminate after the returned -/// value. Returning `nil` will result in immediate termination. -typedef RACStream * (^RACStreamBindBlock)(id value, BOOL *stop); - -/// An abstract class representing any stream of values. -/// -/// This class represents a monad, upon which many stream-based operations can -/// be built. -/// -/// When subclassing RACStream, only the methods in the main @interface body need -/// to be overridden. -@interface RACStream : NSObject - -/// Returns an empty stream. -+ (instancetype)empty; - -/// Lifts `value` into the stream monad. -/// -/// Returns a stream containing only the given value. -+ (instancetype)return:(id)value; - -/// Lazily binds a block to the values in the receiver. -/// -/// This should only be used if you need to terminate the bind early, or close -/// over some state. -flattenMap: is more appropriate for all other cases. -/// -/// block - A block returning a RACStreamBindBlock. This block will be invoked -/// each time the bound stream is re-evaluated. This block must not be -/// nil or return nil. -/// -/// Returns a new stream which represents the combined result of all lazy -/// applications of `block`. -- (instancetype)bind:(RACStreamBindBlock (^)(void))block; - -/// Appends the values of `stream` to the values in the receiver. -/// -/// stream - A stream to concatenate. This must be an instance of the same -/// concrete class as the receiver, and should not be `nil`. -/// -/// Returns a new stream representing the receiver followed by `stream`. -- (instancetype)concat:(RACStream *)stream; - -/// Zips the values in the receiver with those of the given stream to create -/// RACTuples. -/// -/// The first value of each stream will be combined, then the second value, and -/// so forth, until at least one of the streams is exhausted. -/// -/// stream - The stream to zip with. This must be an instance of the same -/// concrete class as the receiver, and should not be `nil`. -/// -/// Returns a new stream of RACTuples, representing the zipped values of the -/// two streams. -- (instancetype)zipWith:(RACStream *)stream; - -@end - -/// This extension contains functionality to support naming streams for -/// debugging. -/// -/// Subclasses do not need to override the methods here. -@interface RACStream () - -/// The name of the stream. This is for debugging/human purposes only. -@property (copy) NSString *name; - -/// Sets the name of the receiver to the given format string. -/// -/// This is for debugging purposes only, and won't do anything unless the -/// RAC_DEBUG_SIGNAL_NAMES environment variable is set. -/// -/// Returns the receiver, for easy method chaining. -- (instancetype)setNameWithFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1, 2); - -@end - -/// Operations built on the RACStream primitives. -/// -/// These methods do not need to be overridden, although subclasses may -/// occasionally gain better performance from doing so. -@interface RACStream (Operations) - -/// Maps `block` across the values in the receiver and flattens the result. -/// -/// Note that operators applied _after_ -flattenMap: behave differently from -/// operators _within_ -flattenMap:. See the Examples section below. -/// -/// This corresponds to the `SelectMany` method in Rx. -/// -/// block - A block which accepts the values in the receiver and returns a new -/// instance of the receiver's class. Returning `nil` from this block is -/// equivalent to returning an empty signal. -/// -/// Examples -/// -/// [signal flattenMap:^(id x) { -/// // Logs each time a returned signal completes. -/// return [[RACSignal return:x] logCompleted]; -/// }]; -/// -/// [[signal -/// flattenMap:^(id x) { -/// return [RACSignal return:x]; -/// }] -/// // Logs only once, when all of the signals complete. -/// logCompleted]; -/// -/// Returns a new stream which represents the combined streams resulting from -/// mapping `block`. -- (instancetype)flattenMap:(RACStream * (^)(id value))block; - -/// Flattens a stream of streams. -/// -/// This corresponds to the `Merge` method in Rx. -/// -/// Returns a stream consisting of the combined streams obtained from the -/// receiver. -- (instancetype)flatten; - -/// Maps `block` across the values in the receiver. -/// -/// This corresponds to the `Select` method in Rx. -/// -/// Returns a new stream with the mapped values. -- (instancetype)map:(id (^)(id value))block; - -/// Replaces each value in the receiver with the given object. -/// -/// Returns a new stream which includes the given object once for each value in -/// the receiver. -- (instancetype)mapReplace:(id)object; - -/// Filters out values in the receiver that don't pass the given test. -/// -/// This corresponds to the `Where` method in Rx. -/// -/// Returns a new stream with only those values that passed. -- (instancetype)filter:(BOOL (^)(id value))block; - -/// Filters out values in the receiver that equal (via -isEqual:) the provided value. -/// -/// value - The value can be `nil`, in which case it ignores `nil` values. -/// -/// Returns a new stream containing only the values which did not compare equal -/// to `value`. -- (instancetype)ignore:(id)value; - -/// Unpacks each RACTuple in the receiver and maps the values to a new value. -/// -/// reduceBlock - The block which reduces each RACTuple's values into one value. -/// It must take as many arguments as the number of tuple elements -/// to process. Each argument will be an object argument. The -/// return value must be an object. This argument cannot be nil. -/// -/// Returns a new stream of reduced tuple values. -- (instancetype)reduceEach:(id (^)())reduceBlock; - -/// Returns a stream consisting of `value`, followed by the values in the -/// receiver. -- (instancetype)startWith:(id)value; - -/// Skips the first `skipCount` values in the receiver. -/// -/// Returns the receiver after skipping the first `skipCount` values. If -/// `skipCount` is greater than the number of values in the stream, an empty -/// stream is returned. -- (instancetype)skip:(NSUInteger)skipCount; - -/// Returns a stream of the first `count` values in the receiver. If `count` is -/// greater than or equal to the number of values in the stream, a stream -/// equivalent to the receiver is returned. -- (instancetype)take:(NSUInteger)count; - -/// Zips the values in the given streams to create RACTuples. -/// -/// The first value of each stream will be combined, then the second value, and -/// so forth, until at least one of the streams is exhausted. -/// -/// streams - The streams to combine. These must all be instances of the same -/// concrete class implementing the protocol. If this collection is -/// empty, the returned stream will be empty. -/// -/// Returns a new stream containing RACTuples of the zipped values from the -/// streams. -+ (instancetype)zip:(id)streams; - -/// Zips streams using +zip:, then reduces the resulting tuples into a single -/// value using -reduceEach: -/// -/// streams - The streams to combine. These must all be instances of the -/// same concrete class implementing the protocol. If this -/// collection is empty, the returned stream will be empty. -/// reduceBlock - The block which reduces the values from all the streams -/// into one value. It must take as many arguments as the -/// number of streams given. Each argument will be an object -/// argument. The return value must be an object. This argument -/// must not be nil. -/// -/// Example: -/// -/// [RACStream zip:@[ stringSignal, intSignal ] reduce:^(NSString *string, NSNumber *number) { -/// return [NSString stringWithFormat:@"%@: %@", string, number]; -/// }]; -/// -/// Returns a new stream containing the results from each invocation of -/// `reduceBlock`. -+ (instancetype)zip:(id)streams reduce:(id (^)())reduceBlock; - -/// Returns a stream obtained by concatenating `streams` in order. -+ (instancetype)concat:(id)streams; - -/// Combines values in the receiver from left to right using the given block. -/// -/// The algorithm proceeds as follows: -/// -/// 1. `startingValue` is passed into the block as the `running` value, and the -/// first element of the receiver is passed into the block as the `next` value. -/// 2. The result of the invocation is added to the returned stream. -/// 3. The result of the invocation (`running`) and the next element of the -/// receiver (`next`) is passed into `block`. -/// 4. Steps 2 and 3 are repeated until all values have been processed. -/// -/// startingValue - The value to be combined with the first element of the -/// receiver. This value may be `nil`. -/// reduceBlock - The block that describes how to combine values of the -/// receiver. If the receiver is empty, this block will never be -/// invoked. Cannot be nil. -/// -/// Examples -/// -/// RACSequence *numbers = @[ @1, @2, @3, @4 ].rac_sequence; -/// -/// // Contains 1, 3, 6, 10 -/// RACSequence *sums = [numbers scanWithStart:@0 reduce:^(NSNumber *sum, NSNumber *next) { -/// return @(sum.integerValue + next.integerValue); -/// }]; -/// -/// Returns a new stream that consists of each application of `reduceBlock`. If the -/// receiver is empty, an empty stream is returned. -- (instancetype)scanWithStart:(id)startingValue reduce:(id (^)(id running, id next))reduceBlock; - -/// Combines values in the receiver from left to right using the given block -/// which also takes zero-based index of the values. -/// -/// startingValue - The value to be combined with the first element of the -/// receiver. This value may be `nil`. -/// reduceBlock - The block that describes how to combine values of the -/// receiver. This block takes zero-based index value as the last -/// parameter. If the receiver is empty, this block will never -/// be invoked. Cannot be nil. -/// -/// Returns a new stream that consists of each application of `reduceBlock`. If the -/// receiver is empty, an empty stream is returned. -- (instancetype)scanWithStart:(id)startingValue reduceWithIndex:(id (^)(id running, id next, NSUInteger index))reduceBlock; - -/// Combines each previous and current value into one object. -/// -/// This method is similar to -scanWithStart:reduce:, but only ever operates on -/// the previous and current values (instead of the whole stream), and does not -/// pass the return value of `reduceBlock` into the next invocation of it. -/// -/// start - The value passed into `reduceBlock` as `previous` for the -/// first value. -/// reduceBlock - The block that combines the previous value and the current -/// value to create the reduced value. Cannot be nil. -/// -/// Examples -/// -/// RACSequence *numbers = @[ @1, @2, @3, @4 ].rac_sequence; -/// -/// // Contains 1, 3, 5, 7 -/// RACSequence *sums = [numbers combinePreviousWithStart:@0 reduce:^(NSNumber *previous, NSNumber *next) { -/// return @(previous.integerValue + next.integerValue); -/// }]; -/// -/// Returns a new stream consisting of the return values from each application of -/// `reduceBlock`. -- (instancetype)combinePreviousWithStart:(id)start reduce:(id (^)(id previous, id current))reduceBlock; - -/// Takes values until the given block returns `YES`. -/// -/// Returns a stream of the initial values in the receiver that fail `predicate`. -/// If `predicate` never returns `YES`, a stream equivalent to the receiver is -/// returned. -- (instancetype)takeUntilBlock:(BOOL (^)(id x))predicate; - -/// Takes values until the given block returns `NO`. -/// -/// Returns a stream of the initial values in the receiver that pass `predicate`. -/// If `predicate` never returns `NO`, a stream equivalent to the receiver is -/// returned. -- (instancetype)takeWhileBlock:(BOOL (^)(id x))predicate; - -/// Skips values until the given block returns `YES`. -/// -/// Returns a stream containing the values of the receiver that follow any -/// initial values failing `predicate`. If `predicate` never returns `YES`, -/// an empty stream is returned. -- (instancetype)skipUntilBlock:(BOOL (^)(id x))predicate; - -/// Skips values until the given block returns `NO`. -/// -/// Returns a stream containing the values of the receiver that follow any -/// initial values passing `predicate`. If `predicate` never returns `NO`, an -/// empty stream is returned. -- (instancetype)skipWhileBlock:(BOOL (^)(id x))predicate; - -/// Returns a stream of values for which -isEqual: returns NO when compared to the -/// previous value. -- (instancetype)distinctUntilChanged; - -@end - -@interface RACStream (Unavailable) - -- (instancetype)sequenceMany:(RACStream * (^)(void))block __attribute__((unavailable("Use -flattenMap: instead"))); -- (instancetype)scanWithStart:(id)startingValue combine:(id (^)(id running, id next))block __attribute__((unavailable("Renamed to -scanWithStart:reduce:"))); -- (instancetype)mapPreviousWithStart:(id)start reduce:(id (^)(id previous, id current))combineBlock __attribute__((unavailable("Renamed to -combinePreviousWithStart:reduce:"))); - -@end diff --git a/ReactiveObjC/RACStream.m b/ReactiveObjC/RACStream.m deleted file mode 100644 index 0c896178ee..0000000000 --- a/ReactiveObjC/RACStream.m +++ /dev/null @@ -1,351 +0,0 @@ -// -// RACStream.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-10-31. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACStream.h" -#import "NSObject+RACDescription.h" -#import "RACBlockTrampoline.h" -#import "RACTuple.h" - -@implementation RACStream - -#pragma mark Lifecycle - -- (id)init { - self = [super init]; - if (self == nil) return nil; - - self.name = @""; - return self; -} - -#pragma mark Abstract methods - -+ (instancetype)empty { - return nil; -} - -- (instancetype)bind:(RACStreamBindBlock (^)(void))block { - return nil; -} - -+ (instancetype)return:(id)value { - return nil; -} - -- (instancetype)concat:(RACStream *)stream { - return nil; -} - -- (instancetype)zipWith:(RACStream *)stream { - return nil; -} - -#pragma mark Naming - -- (instancetype)setNameWithFormat:(NSString *)format, ... { - if (getenv("RAC_DEBUG_SIGNAL_NAMES") == NULL) return self; - - NSCParameterAssert(format != nil); - - va_list args; - va_start(args, format); - - NSString *str = [[NSString alloc] initWithFormat:format arguments:args]; - va_end(args); - - self.name = str; - return self; -} - -@end - -@implementation RACStream (Operations) - -- (instancetype)flattenMap:(RACStream * (^)(id value))block { - Class class = self.class; - - return [[self bind:^{ - return ^(id value, BOOL *stop) { - id stream = block(value) ?: [class empty]; - NSCAssert([stream isKindOfClass:RACStream.class], @"Value returned from -flattenMap: is not a stream: %@", stream); - - return stream; - }; - }] setNameWithFormat:@"[%@] -flattenMap:", self.name]; -} - -- (instancetype)flatten { - return [[self flattenMap:^(id value) { - return value; - }] setNameWithFormat:@"[%@] -flatten", self.name]; -} - -- (instancetype)map:(id (^)(id value))block { - NSCParameterAssert(block != nil); - - Class class = self.class; - - return [[self flattenMap:^(id value) { - return [class return:block(value)]; - }] setNameWithFormat:@"[%@] -map:", self.name]; -} - -- (instancetype)mapReplace:(id)object { - return [[self map:^(id _) { - return object; - }] setNameWithFormat:@"[%@] -mapReplace: %@", self.name, RACDescription(object)]; -} - -- (instancetype)combinePreviousWithStart:(id)start reduce:(id (^)(id previous, id next))reduceBlock { - NSCParameterAssert(reduceBlock != NULL); - return [[[self - scanWithStart:RACTuplePack(start) - reduce:^(RACTuple *previousTuple, id next) { - id value = reduceBlock(previousTuple[0], next); - return RACTuplePack(next, value); - }] - map:^(RACTuple *tuple) { - return tuple[1]; - }] - setNameWithFormat:@"[%@] -combinePreviousWithStart: %@ reduce:", self.name, RACDescription(start)]; -} - -- (instancetype)filter:(BOOL (^)(id value))block { - NSCParameterAssert(block != nil); - - Class class = self.class; - - return [[self flattenMap:^ id (id value) { - if (block(value)) { - return [class return:value]; - } else { - return class.empty; - } - }] setNameWithFormat:@"[%@] -filter:", self.name]; -} - -- (instancetype)ignore:(id)value { - return [[self filter:^ BOOL (id innerValue) { - return innerValue != value && ![innerValue isEqual:value]; - }] setNameWithFormat:@"[%@] -ignore: %@", self.name, RACDescription(value)]; -} - -- (instancetype)reduceEach:(id (^)())reduceBlock { - NSCParameterAssert(reduceBlock != nil); - - __weak RACStream *stream __attribute__((unused)) = self; - return [[self map:^(RACTuple *t) { - NSCAssert([t isKindOfClass:RACTuple.class], @"Value from stream %@ is not a tuple: %@", stream, t); - return [RACBlockTrampoline invokeBlock:reduceBlock withArguments:t]; - }] setNameWithFormat:@"[%@] -reduceEach:", self.name]; -} - -- (instancetype)startWith:(id)value { - return [[[self.class return:value] - concat:self] - setNameWithFormat:@"[%@] -startWith: %@", self.name, RACDescription(value)]; -} - -- (instancetype)skip:(NSUInteger)skipCount { - Class class = self.class; - - return [[self bind:^{ - __block NSUInteger skipped = 0; - - return ^(id value, BOOL *stop) { - if (skipped >= skipCount) return [class return:value]; - - skipped++; - return class.empty; - }; - }] setNameWithFormat:@"[%@] -skip: %lu", self.name, (unsigned long)skipCount]; -} - -- (instancetype)take:(NSUInteger)count { - Class class = self.class; - - if (count == 0) return class.empty; - - return [[self bind:^{ - __block NSUInteger taken = 0; - - return ^ id (id value, BOOL *stop) { - if (taken < count) { - ++taken; - if (taken == count) *stop = YES; - return [class return:value]; - } else { - return nil; - } - }; - }] setNameWithFormat:@"[%@] -take: %lu", self.name, (unsigned long)count]; -} - -+ (instancetype)join:(id)streams block:(RACStream * (^)(id, id))block { - RACStream *current = nil; - - // Creates streams of successively larger tuples by combining the input - // streams one-by-one. - for (RACStream *stream in streams) { - // For the first stream, just wrap its values in a RACTuple. That way, - // if only one stream is given, the result is still a stream of tuples. - if (current == nil) { - current = [stream map:^(id x) { - return RACTuplePack(x); - }]; - - continue; - } - - current = block(current, stream); - } - - if (current == nil) return [self empty]; - - return [current map:^(RACTuple *xs) { - // Right now, each value is contained in its own tuple, sorta like: - // - // (((1), 2), 3) - // - // We need to unwrap all the layers and create a tuple out of the result. - NSMutableArray *values = [[NSMutableArray alloc] init]; - - while (xs != nil) { - [values insertObject:xs.last ?: RACTupleNil.tupleNil atIndex:0]; - xs = (xs.count > 1 ? xs.first : nil); - } - - return [RACTuple tupleWithObjectsFromArray:values]; - }]; -} - -+ (instancetype)zip:(id)streams { - return [[self join:streams block:^(RACStream *left, RACStream *right) { - return [left zipWith:right]; - }] setNameWithFormat:@"+zip: %@", streams]; -} - -+ (instancetype)zip:(id)streams reduce:(id (^)())reduceBlock { - NSCParameterAssert(reduceBlock != nil); - - RACStream *result = [self zip:streams]; - - // Although we assert this condition above, older versions of this method - // supported this argument being nil. Avoid crashing Release builds of - // apps that depended on that. - if (reduceBlock != nil) result = [result reduceEach:reduceBlock]; - - return [result setNameWithFormat:@"+zip: %@ reduce:", streams]; -} - -+ (instancetype)concat:(id)streams { - RACStream *result = self.empty; - for (RACStream *stream in streams) { - result = [result concat:stream]; - } - - return [result setNameWithFormat:@"+concat: %@", streams]; -} - -- (instancetype)scanWithStart:(id)startingValue reduce:(id (^)(id running, id next))reduceBlock { - NSCParameterAssert(reduceBlock != nil); - - return [[self - scanWithStart:startingValue - reduceWithIndex:^(id running, id next, NSUInteger index) { - return reduceBlock(running, next); - }] - setNameWithFormat:@"[%@] -scanWithStart: %@ reduce:", self.name, RACDescription(startingValue)]; -} - -- (instancetype)scanWithStart:(id)startingValue reduceWithIndex:(id (^)(id, id, NSUInteger))reduceBlock { - NSCParameterAssert(reduceBlock != nil); - - Class class = self.class; - - return [[self bind:^{ - __block id running = startingValue; - __block NSUInteger index = 0; - - return ^(id value, BOOL *stop) { - running = reduceBlock(running, value, index++); - return [class return:running]; - }; - }] setNameWithFormat:@"[%@] -scanWithStart: %@ reduceWithIndex:", self.name, RACDescription(startingValue)]; -} - -- (instancetype)takeUntilBlock:(BOOL (^)(id x))predicate { - NSCParameterAssert(predicate != nil); - - Class class = self.class; - - return [[self bind:^{ - return ^ id (id value, BOOL *stop) { - if (predicate(value)) return nil; - - return [class return:value]; - }; - }] setNameWithFormat:@"[%@] -takeUntilBlock:", self.name]; -} - -- (instancetype)takeWhileBlock:(BOOL (^)(id x))predicate { - NSCParameterAssert(predicate != nil); - - return [[self takeUntilBlock:^ BOOL (id x) { - return !predicate(x); - }] setNameWithFormat:@"[%@] -takeWhileBlock:", self.name]; -} - -- (instancetype)skipUntilBlock:(BOOL (^)(id x))predicate { - NSCParameterAssert(predicate != nil); - - Class class = self.class; - - return [[self bind:^{ - __block BOOL skipping = YES; - - return ^ id (id value, BOOL *stop) { - if (skipping) { - if (predicate(value)) { - skipping = NO; - } else { - return class.empty; - } - } - - return [class return:value]; - }; - }] setNameWithFormat:@"[%@] -skipUntilBlock:", self.name]; -} - -- (instancetype)skipWhileBlock:(BOOL (^)(id x))predicate { - NSCParameterAssert(predicate != nil); - - return [[self skipUntilBlock:^ BOOL (id x) { - return !predicate(x); - }] setNameWithFormat:@"[%@] -skipWhileBlock:", self.name]; -} - -- (instancetype)distinctUntilChanged { - Class class = self.class; - - return [[self bind:^{ - __block id lastValue = nil; - __block BOOL initial = YES; - - return ^(id x, BOOL *stop) { - if (!initial && (lastValue == x || [x isEqual:lastValue])) return [class empty]; - - initial = NO; - lastValue = x; - return [class return:x]; - }; - }] setNameWithFormat:@"[%@] -distinctUntilChanged", self.name]; -} - -@end diff --git a/ReactiveObjC/RACStringSequence.h b/ReactiveObjC/RACStringSequence.h deleted file mode 100644 index 6597fc84ec..0000000000 --- a/ReactiveObjC/RACStringSequence.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// RACStringSequence.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-10-29. -// Copyright (c) 2012 GitHub. All rights reserved. -// - -#import "RACSequence.h" - -// Private class that adapts a string to the RACSequence interface. -@interface RACStringSequence : RACSequence - -// Returns a sequence for enumerating over the given string, starting from the -// given character offset. The string will be copied to prevent mutation. -+ (RACSequence *)sequenceWithString:(NSString *)string offset:(NSUInteger)offset; - -@end diff --git a/ReactiveObjC/RACStringSequence.m b/ReactiveObjC/RACStringSequence.m deleted file mode 100644 index 809db5d30e..0000000000 --- a/ReactiveObjC/RACStringSequence.m +++ /dev/null @@ -1,65 +0,0 @@ -// -// RACStringSequence.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-10-29. -// Copyright (c) 2012 GitHub. All rights reserved. -// - -#import "RACStringSequence.h" - -@interface RACStringSequence () - -// The string being sequenced. -@property (nonatomic, copy, readonly) NSString *string; - -// The index in the string from which the sequence starts. -@property (nonatomic, assign, readonly) NSUInteger offset; - -@end - -@implementation RACStringSequence - -#pragma mark Lifecycle - -+ (RACSequence *)sequenceWithString:(NSString *)string offset:(NSUInteger)offset { - NSCParameterAssert(offset <= string.length); - - if (offset == string.length) return self.empty; - - RACStringSequence *seq = [[self alloc] init]; - seq->_string = [string copy]; - seq->_offset = offset; - return seq; -} - -#pragma mark RACSequence - -- (id)head { - return [self.string substringWithRange:NSMakeRange(self.offset, 1)]; -} - -- (RACSequence *)tail { - RACSequence *sequence = [self.class sequenceWithString:self.string offset:self.offset + 1]; - sequence.name = self.name; - return sequence; -} - -- (NSArray *)array { - NSUInteger substringLength = self.string.length - self.offset; - NSMutableArray *array = [NSMutableArray arrayWithCapacity:substringLength]; - - [self.string enumerateSubstringsInRange:NSMakeRange(self.offset, substringLength) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) { - [array addObject:substring]; - }]; - - return [array copy]; -} - -#pragma mark NSObject - -- (NSString *)description { - return [NSString stringWithFormat:@"<%@: %p>{ name = %@, string = %@ }", self.class, self, self.name, [self.string substringFromIndex:self.offset]]; -} - -@end diff --git a/ReactiveObjC/RACSubject.h b/ReactiveObjC/RACSubject.h deleted file mode 100644 index a76cf15014..0000000000 --- a/ReactiveObjC/RACSubject.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// RACSubject.h -// ReactiveObjC -// -// Created by Josh Abernathy on 3/9/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACSignal.h" -#import "RACSubscriber.h" - -/// A subject can be thought of as a signal that you can manually control by -/// sending next, completed, and error. -/// -/// They're most helpful in bridging the non-RAC world to RAC, since they let you -/// manually control the sending of events. -@interface RACSubject : RACSignal - -/// Returns a new subject. -+ (instancetype)subject; - -@end diff --git a/ReactiveObjC/RACSubject.m b/ReactiveObjC/RACSubject.m deleted file mode 100644 index 3833dfcb63..0000000000 --- a/ReactiveObjC/RACSubject.m +++ /dev/null @@ -1,126 +0,0 @@ -// -// RACSubject.m -// ReactiveObjC -// -// Created by Josh Abernathy on 3/9/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACSubject.h" -#import -#import "RACCompoundDisposable.h" -#import "RACPassthroughSubscriber.h" - -@interface RACSubject () - -// Contains all current subscribers to the receiver. -// -// This should only be used while synchronized on `self`. -@property (nonatomic, strong, readonly) NSMutableArray *subscribers; - -// Contains all of the receiver's subscriptions to other signals. -@property (nonatomic, strong, readonly) RACCompoundDisposable *disposable; - -// Enumerates over each of the receiver's `subscribers` and invokes `block` for -// each. -- (void)enumerateSubscribersUsingBlock:(void (^)(id subscriber))block; - -@end - -@implementation RACSubject - -#pragma mark Lifecycle - -+ (instancetype)subject { - return [[self alloc] init]; -} - -- (id)init { - self = [super init]; - if (self == nil) return nil; - - _disposable = [RACCompoundDisposable compoundDisposable]; - _subscribers = [[NSMutableArray alloc] initWithCapacity:1]; - - return self; -} - -- (void)dealloc { - [self.disposable dispose]; -} - -#pragma mark Subscription - -- (RACDisposable *)subscribe:(id)subscriber { - NSCParameterAssert(subscriber != nil); - - RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable]; - subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable]; - - NSMutableArray *subscribers = self.subscribers; - @synchronized (subscribers) { - [subscribers addObject:subscriber]; - } - - [disposable addDisposable:[RACDisposable disposableWithBlock:^{ - @synchronized (subscribers) { - // Since newer subscribers are generally shorter-lived, search - // starting from the end of the list. - NSUInteger index = [subscribers indexOfObjectWithOptions:NSEnumerationReverse passingTest:^ BOOL (id obj, NSUInteger index, BOOL *stop) { - return obj == subscriber; - }]; - - if (index != NSNotFound) [subscribers removeObjectAtIndex:index]; - } - }]]; - - return disposable; -} - -- (void)enumerateSubscribersUsingBlock:(void (^)(id subscriber))block { - NSArray *subscribers; - @synchronized (self.subscribers) { - subscribers = [self.subscribers copy]; - } - - for (id subscriber in subscribers) { - block(subscriber); - } -} - -#pragma mark RACSubscriber - -- (void)sendNext:(id)value { - [self enumerateSubscribersUsingBlock:^(id subscriber) { - [subscriber sendNext:value]; - }]; -} - -- (void)sendError:(NSError *)error { - [self.disposable dispose]; - - [self enumerateSubscribersUsingBlock:^(id subscriber) { - [subscriber sendError:error]; - }]; -} - -- (void)sendCompleted { - [self.disposable dispose]; - - [self enumerateSubscribersUsingBlock:^(id subscriber) { - [subscriber sendCompleted]; - }]; -} - -- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)d { - if (d.disposed) return; - [self.disposable addDisposable:d]; - - @weakify(self, d); - [d addDisposable:[RACDisposable disposableWithBlock:^{ - @strongify(self, d); - [self.disposable removeDisposable:d]; - }]]; -} - -@end diff --git a/ReactiveObjC/RACSubscriber+Private.h b/ReactiveObjC/RACSubscriber+Private.h deleted file mode 100644 index f898d1c2ee..0000000000 --- a/ReactiveObjC/RACSubscriber+Private.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// RACSubscriber+Private.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-06-13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACSubscriber.h" - -// A simple block-based subscriber. -@interface RACSubscriber : NSObject - -// Creates a new subscriber with the given blocks. -+ (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed; - -@end diff --git a/ReactiveObjC/RACSubscriber.h b/ReactiveObjC/RACSubscriber.h deleted file mode 100644 index a4950674b5..0000000000 --- a/ReactiveObjC/RACSubscriber.h +++ /dev/null @@ -1,51 +0,0 @@ -// -// RACSubscriber.h -// ReactiveObjC -// -// Created by Josh Abernathy on 3/1/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import - -@class RACCompoundDisposable; - -/// Represents any object which can directly receive values from a RACSignal. -/// -/// You generally shouldn't need to implement this protocol. +[RACSignal -/// createSignal:], RACSignal's subscription methods, or RACSubject should work -/// for most uses. -/// -/// Implementors of this protocol may receive messages and values from multiple -/// threads simultaneously, and so should be thread-safe. Subscribers will also -/// be weakly referenced so implementations must allow that. -@protocol RACSubscriber -@required - -/// Sends the next value to subscribers. -/// -/// value - The value to send. This can be `nil`. -- (void)sendNext:(id)value; - -/// Sends the error to subscribers. -/// -/// error - The error to send. This can be `nil`. -/// -/// This terminates the subscription, and invalidates the subscriber (such that -/// it cannot subscribe to anything else in the future). -- (void)sendError:(NSError *)error; - -/// Sends completed to subscribers. -/// -/// This terminates the subscription, and invalidates the subscriber (such that -/// it cannot subscribe to anything else in the future). -- (void)sendCompleted; - -/// Sends the subscriber a disposable that represents one of its subscriptions. -/// -/// A subscriber may receive multiple disposables if it gets subscribed to -/// multiple signals; however, any error or completed events must terminate _all_ -/// subscriptions. -- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable; - -@end diff --git a/ReactiveObjC/RACSubscriber.m b/ReactiveObjC/RACSubscriber.m deleted file mode 100644 index 46a9a0c24e..0000000000 --- a/ReactiveObjC/RACSubscriber.m +++ /dev/null @@ -1,112 +0,0 @@ -// -// RACSubscriber.m -// ReactiveObjC -// -// Created by Josh Abernathy on 3/1/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACSubscriber.h" -#import "RACSubscriber+Private.h" -#import -#import "RACCompoundDisposable.h" - -@interface RACSubscriber () - -// These callbacks should only be accessed while synchronized on self. -@property (nonatomic, copy) void (^next)(id value); -@property (nonatomic, copy) void (^error)(NSError *error); -@property (nonatomic, copy) void (^completed)(void); - -@property (nonatomic, strong, readonly) RACCompoundDisposable *disposable; - -@end - -@implementation RACSubscriber - -#pragma mark Lifecycle - -+ (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed { - RACSubscriber *subscriber = [[self alloc] init]; - - subscriber->_next = [next copy]; - subscriber->_error = [error copy]; - subscriber->_completed = [completed copy]; - - return subscriber; -} - -- (id)init { - self = [super init]; - if (self == nil) return nil; - - @unsafeify(self); - - RACDisposable *selfDisposable = [RACDisposable disposableWithBlock:^{ - @strongify(self); - - @synchronized (self) { - self.next = nil; - self.error = nil; - self.completed = nil; - } - }]; - - _disposable = [RACCompoundDisposable compoundDisposable]; - [_disposable addDisposable:selfDisposable]; - - return self; -} - -- (void)dealloc { - [self.disposable dispose]; -} - -#pragma mark RACSubscriber - -- (void)sendNext:(id)value { - @synchronized (self) { - void (^nextBlock)(id) = [self.next copy]; - if (nextBlock == nil) return; - - nextBlock(value); - } -} - -- (void)sendError:(NSError *)e { - @synchronized (self) { - void (^errorBlock)(NSError *) = [self.error copy]; - [self.disposable dispose]; - - if (errorBlock == nil) return; - errorBlock(e); - } -} - -- (void)sendCompleted { - @synchronized (self) { - void (^completedBlock)(void) = [self.completed copy]; - [self.disposable dispose]; - - if (completedBlock == nil) return; - completedBlock(); - } -} - -- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)otherDisposable { - if (otherDisposable.disposed) return; - - RACCompoundDisposable *selfDisposable = self.disposable; - [selfDisposable addDisposable:otherDisposable]; - - @unsafeify(otherDisposable); - - // If this subscription terminates, purge its disposable to avoid unbounded - // memory growth. - [otherDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - @strongify(otherDisposable); - [selfDisposable removeDisposable:otherDisposable]; - }]]; -} - -@end diff --git a/ReactiveObjC/RACSubscriptingAssignmentTrampoline.h b/ReactiveObjC/RACSubscriptingAssignmentTrampoline.h deleted file mode 100644 index 7a0f585151..0000000000 --- a/ReactiveObjC/RACSubscriptingAssignmentTrampoline.h +++ /dev/null @@ -1,54 +0,0 @@ -// -// RACSubscriptingAssignmentTrampoline.h -// ReactiveObjC -// -// Created by Josh Abernathy on 9/24/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import -#import - -@class RACSignal; - -/// Assigns a signal to an object property, automatically setting the given key -/// path on every `next`. When the signal completes, the binding is automatically -/// disposed of. -/// -/// There are two different versions of this macro: -/// -/// - RAC(TARGET, KEYPATH, NILVALUE) will bind the `KEYPATH` of `TARGET` to the -/// given signal. If the signal ever sends a `nil` value, the property will be -/// set to `NILVALUE` instead. `NILVALUE` may itself be `nil` for object -/// properties, but an NSValue should be used for primitive properties, to -/// avoid an exception if `nil` is sent (which might occur if an intermediate -/// object is set to `nil`). -/// - RAC(TARGET, KEYPATH) is the same as the above, but `NILVALUE` defaults to -/// `nil`. -/// -/// See -[RACSignal setKeyPath:onObject:nilValue:] for more information about the -/// binding's semantics. -/// -/// Examples -/// -/// RAC(self, objectProperty) = objectSignal; -/// RAC(self, stringProperty, @"foobar") = stringSignal; -/// RAC(self, integerProperty, @42) = integerSignal; -/// -/// WARNING: Under certain conditions, use of this macro can be thread-unsafe. -/// See the documentation of -setKeyPath:onObject:nilValue:. -#define RAC(TARGET, ...) \ - metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__)) \ - (RAC_(TARGET, __VA_ARGS__, nil)) \ - (RAC_(TARGET, __VA_ARGS__)) - -/// Do not use this directly. Use the RAC macro above. -#define RAC_(TARGET, KEYPATH, NILVALUE) \ - [[RACSubscriptingAssignmentTrampoline alloc] initWithTarget:(TARGET) nilValue:(NILVALUE)][@keypath(TARGET, KEYPATH)] - -@interface RACSubscriptingAssignmentTrampoline : NSObject - -- (id)initWithTarget:(id)target nilValue:(id)nilValue; -- (void)setObject:(RACSignal *)signal forKeyedSubscript:(NSString *)keyPath; - -@end diff --git a/ReactiveObjC/RACSubscriptingAssignmentTrampoline.m b/ReactiveObjC/RACSubscriptingAssignmentTrampoline.m deleted file mode 100644 index f9bc5a0b8c..0000000000 --- a/ReactiveObjC/RACSubscriptingAssignmentTrampoline.m +++ /dev/null @@ -1,42 +0,0 @@ -// -// RACSubscriptingAssignmentTrampoline.m -// ReactiveObjC -// -// Created by Josh Abernathy on 9/24/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACSubscriptingAssignmentTrampoline.h" -#import "RACSignal+Operations.h" - -@interface RACSubscriptingAssignmentTrampoline () - -// The object to bind to. -@property (nonatomic, strong, readonly) id target; - -// A value to use when `nil` is sent on the bound signal. -@property (nonatomic, strong, readonly) id nilValue; - -@end - -@implementation RACSubscriptingAssignmentTrampoline - -- (id)initWithTarget:(id)target nilValue:(id)nilValue { - // This is often a programmer error, but this prevents crashes if the target - // object has unexpectedly deallocated. - if (target == nil) return nil; - - self = [super init]; - if (self == nil) return nil; - - _target = target; - _nilValue = nilValue; - - return self; -} - -- (void)setObject:(RACSignal *)signal forKeyedSubscript:(NSString *)keyPath { - [signal setKeyPath:keyPath onObject:self.target nilValue:self.nilValue]; -} - -@end diff --git a/ReactiveObjC/RACSubscriptionScheduler.h b/ReactiveObjC/RACSubscriptionScheduler.h deleted file mode 100644 index 8398e727bb..0000000000 --- a/ReactiveObjC/RACSubscriptionScheduler.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// RACSubscriptionScheduler.h -// ReactiveObjC -// -// Created by Josh Abernathy on 11/30/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACScheduler.h" - -// A private scheduler used only for subscriptions. See the private -// +[RACScheduler subscriptionScheduler] method for more information. -@interface RACSubscriptionScheduler : RACScheduler - -@end diff --git a/ReactiveObjC/RACSubscriptionScheduler.m b/ReactiveObjC/RACSubscriptionScheduler.m deleted file mode 100644 index 58ddca8b0c..0000000000 --- a/ReactiveObjC/RACSubscriptionScheduler.m +++ /dev/null @@ -1,54 +0,0 @@ -// -// RACSubscriptionScheduler.m -// ReactiveObjC -// -// Created by Josh Abernathy on 11/30/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACSubscriptionScheduler.h" -#import "RACScheduler+Private.h" - -@interface RACSubscriptionScheduler () - -// A private background scheduler on which to subscribe if the +currentScheduler -// is unknown. -@property (nonatomic, strong, readonly) RACScheduler *backgroundScheduler; - -@end - -@implementation RACSubscriptionScheduler - -#pragma mark Lifecycle - -- (id)init { - self = [super initWithName:@"org.reactivecocoa.ReactiveObjC.RACScheduler.subscriptionScheduler"]; - if (self == nil) return nil; - - _backgroundScheduler = [RACScheduler scheduler]; - - return self; -} - -#pragma mark RACScheduler - -- (RACDisposable *)schedule:(void (^)(void))block { - NSCParameterAssert(block != NULL); - - if (RACScheduler.currentScheduler == nil) return [self.backgroundScheduler schedule:block]; - - block(); - return nil; -} - -- (RACDisposable *)after:(NSDate *)date schedule:(void (^)(void))block { - RACScheduler *scheduler = RACScheduler.currentScheduler ?: self.backgroundScheduler; - return [scheduler after:date schedule:block]; -} - -- (RACDisposable *)after:(NSDate *)date repeatingEvery:(NSTimeInterval)interval withLeeway:(NSTimeInterval)leeway schedule:(void (^)(void))block { - RACScheduler *scheduler = RACScheduler.currentScheduler ?: self.backgroundScheduler; - return [scheduler after:date repeatingEvery:interval withLeeway:leeway schedule:block]; -} - -@end diff --git a/ReactiveObjC/RACTargetQueueScheduler.h b/ReactiveObjC/RACTargetQueueScheduler.h deleted file mode 100644 index 0fe8a6024c..0000000000 --- a/ReactiveObjC/RACTargetQueueScheduler.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// RACTargetQueueScheduler.h -// ReactiveObjC -// -// Created by Josh Abernathy on 6/6/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACQueueScheduler.h" - -/// A scheduler that enqueues blocks on a private serial queue, targeting an -/// arbitrary GCD queue. -@interface RACTargetQueueScheduler : RACQueueScheduler - -/// Initializes the receiver with a serial queue that will target the given -/// `targetQueue`. -/// -/// name - The name of the scheduler. If nil, a default name will be used. -/// targetQueue - The queue to target. Cannot be NULL. -/// -/// Returns the initialized object. -- (id)initWithName:(NSString *)name targetQueue:(dispatch_queue_t)targetQueue; - -@end diff --git a/ReactiveObjC/RACTargetQueueScheduler.m b/ReactiveObjC/RACTargetQueueScheduler.m deleted file mode 100644 index fde6b1d26b..0000000000 --- a/ReactiveObjC/RACTargetQueueScheduler.m +++ /dev/null @@ -1,31 +0,0 @@ -// -// RACTargetQueueScheduler.m -// ReactiveObjC -// -// Created by Josh Abernathy on 6/6/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACTargetQueueScheduler.h" -#import "RACQueueScheduler+Subclass.h" - -@implementation RACTargetQueueScheduler - -#pragma mark Lifecycle - -- (id)initWithName:(NSString *)name targetQueue:(dispatch_queue_t)targetQueue { - NSCParameterAssert(targetQueue != NULL); - - if (name == nil) { - name = [NSString stringWithFormat:@"org.reactivecocoa.ReactiveObjC.RACTargetQueueScheduler(%s)", dispatch_queue_get_label(targetQueue)]; - } - - dispatch_queue_t queue = dispatch_queue_create(name.UTF8String, DISPATCH_QUEUE_SERIAL); - if (queue == NULL) return nil; - - dispatch_set_target_queue(queue, targetQueue); - - return [super initWithName:name queue:queue]; -} - -@end diff --git a/ReactiveObjC/RACTestScheduler.h b/ReactiveObjC/RACTestScheduler.h deleted file mode 100644 index 2975f126df..0000000000 --- a/ReactiveObjC/RACTestScheduler.h +++ /dev/null @@ -1,42 +0,0 @@ -// -// RACTestScheduler.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-07-06. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACScheduler.h" - -/// A special kind of scheduler that steps through virtualized time. -/// -/// This scheduler class can be used in unit tests to verify asynchronous -/// behaviors without spending significant time waiting. -/// -/// This class can be used from multiple threads, but only one thread can `step` -/// through the enqueued actions at a time. Other threads will wait while the -/// scheduled blocks are being executed. -@interface RACTestScheduler : RACScheduler - -/// Initializes a new test scheduler. -- (instancetype)init; - -/// Executes the next scheduled block, if any. -/// -/// This method will block until the scheduled action has completed. -- (void)step; - -/// Executes up to the next `ticks` scheduled blocks. -/// -/// This method will block until the scheduled actions have completed. -/// -/// ticks - The number of scheduled blocks to execute. If there aren't this many -/// blocks enqueued, all scheduled blocks are executed. -- (void)step:(NSUInteger)ticks; - -/// Executes all of the scheduled blocks on the receiver. -/// -/// This method will block until the scheduled actions have completed. -- (void)stepAll; - -@end diff --git a/ReactiveObjC/RACTestScheduler.m b/ReactiveObjC/RACTestScheduler.m deleted file mode 100644 index cc248e8a90..0000000000 --- a/ReactiveObjC/RACTestScheduler.m +++ /dev/null @@ -1,223 +0,0 @@ -// -// RACTestScheduler.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-07-06. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACTestScheduler.h" -#import -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACScheduler+Private.h" - -@interface RACTestSchedulerAction : NSObject - -// The date at which the action should be executed. -// -// This absolute time will not actually be honored. This date is only used for -// comparison, to determine which block should be run _next_. -@property (nonatomic, copy, readonly) NSDate *date; - -// The scheduled block. -@property (nonatomic, copy, readonly) void (^block)(void); - -// A disposable for this action. -// -// When disposed, the action should not start executing if it hasn't already. -@property (nonatomic, strong, readonly) RACDisposable *disposable; - -// Initializes a new scheduler action. -- (id)initWithDate:(NSDate *)date block:(void (^)(void))block; - -@end - -static CFComparisonResult RACCompareScheduledActions(const void *ptr1, const void *ptr2, void *info) { - RACTestSchedulerAction *action1 = (__bridge id)ptr1; - RACTestSchedulerAction *action2 = (__bridge id)ptr2; - return CFDateCompare((__bridge CFDateRef)action1.date, (__bridge CFDateRef)action2.date, NULL); -} - -static const void *RACRetainScheduledAction(CFAllocatorRef allocator, const void *ptr) { - return CFRetain(ptr); -} - -static void RACReleaseScheduledAction(CFAllocatorRef allocator, const void *ptr) { - CFRelease(ptr); -} - -@interface RACTestScheduler () - -// All of the RACTestSchedulerActions that have been enqueued and not yet -// executed. -// -// The minimum value in the heap represents the action to execute next. -// -// This property should only be used while synchronized on self. -@property (nonatomic, assign, readonly) CFBinaryHeapRef scheduledActions; - -// The number of blocks that have been directly enqueued with -schedule: so -// far. -// -// This is used to ensure unique dates when two blocks are enqueued -// simultaneously. -// -// This property should only be used while synchronized on self. -@property (nonatomic, assign) NSUInteger numberOfDirectlyScheduledBlocks; - -@end - -@implementation RACTestScheduler - -#pragma mark Lifecycle - -- (instancetype)init { - self = [super initWithName:@"org.reactivecocoa.ReactiveObjC.RACTestScheduler"]; - if (self == nil) return nil; - - CFBinaryHeapCallBacks callbacks = (CFBinaryHeapCallBacks){ - .version = 0, - .retain = &RACRetainScheduledAction, - .release = &RACReleaseScheduledAction, - .copyDescription = &CFCopyDescription, - .compare = &RACCompareScheduledActions - }; - - _scheduledActions = CFBinaryHeapCreate(NULL, 0, &callbacks, NULL); - return self; -} - -- (void)dealloc { - [self stepAll]; - - if (_scheduledActions != NULL) { - CFBridgingRelease(_scheduledActions); - _scheduledActions = NULL; - } -} - -#pragma mark Execution - -- (void)step { - [self step:1]; -} - -- (void)step:(NSUInteger)ticks { - @synchronized (self) { - for (NSUInteger i = 0; i < ticks; i++) { - const void *actionPtr = NULL; - if (!CFBinaryHeapGetMinimumIfPresent(self.scheduledActions, &actionPtr)) break; - - RACTestSchedulerAction *action = (__bridge id)actionPtr; - CFBinaryHeapRemoveMinimumValue(self.scheduledActions); - - if (action.disposable.disposed) continue; - - RACScheduler *previousScheduler = RACScheduler.currentScheduler; - NSThread.currentThread.threadDictionary[RACSchedulerCurrentSchedulerKey] = self; - - action.block(); - - if (previousScheduler != nil) { - NSThread.currentThread.threadDictionary[RACSchedulerCurrentSchedulerKey] = previousScheduler; - } else { - [NSThread.currentThread.threadDictionary removeObjectForKey:RACSchedulerCurrentSchedulerKey]; - } - } - } -} - -- (void)stepAll { - [self step:NSUIntegerMax]; -} - -#pragma mark RACScheduler - -- (RACDisposable *)schedule:(void (^)(void))block { - NSCParameterAssert(block != nil); - - @synchronized (self) { - NSDate *uniqueDate = [NSDate dateWithTimeIntervalSinceReferenceDate:self.numberOfDirectlyScheduledBlocks]; - self.numberOfDirectlyScheduledBlocks++; - - RACTestSchedulerAction *action = [[RACTestSchedulerAction alloc] initWithDate:uniqueDate block:block]; - CFBinaryHeapAddValue(self.scheduledActions, (__bridge void *)action); - - return action.disposable; - } -} - -- (RACDisposable *)after:(NSDate *)date schedule:(void (^)(void))block { - NSCParameterAssert(date != nil); - NSCParameterAssert(block != nil); - - @synchronized (self) { - RACTestSchedulerAction *action = [[RACTestSchedulerAction alloc] initWithDate:date block:block]; - CFBinaryHeapAddValue(self.scheduledActions, (__bridge void *)action); - - return action.disposable; - } -} - -- (RACDisposable *)after:(NSDate *)date repeatingEvery:(NSTimeInterval)interval withLeeway:(NSTimeInterval)leeway schedule:(void (^)(void))block { - NSCParameterAssert(date != nil); - NSCParameterAssert(block != nil); - NSCParameterAssert(interval >= 0); - NSCParameterAssert(leeway >= 0); - - RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable]; - - @weakify(self); - @synchronized (self) { - __block RACDisposable *thisDisposable = nil; - - void (^reschedulingBlock)(void) = ^{ - @strongify(self); - - [compoundDisposable removeDisposable:thisDisposable]; - - // Schedule the next interval. - RACDisposable *schedulingDisposable = [self after:[date dateByAddingTimeInterval:interval] repeatingEvery:interval withLeeway:leeway schedule:block]; - [compoundDisposable addDisposable:schedulingDisposable]; - - block(); - }; - - RACTestSchedulerAction *action = [[RACTestSchedulerAction alloc] initWithDate:date block:reschedulingBlock]; - CFBinaryHeapAddValue(self.scheduledActions, (__bridge void *)action); - - thisDisposable = action.disposable; - [compoundDisposable addDisposable:thisDisposable]; - } - - return compoundDisposable; -} - -@end - -@implementation RACTestSchedulerAction - -#pragma mark Lifecycle - -- (id)initWithDate:(NSDate *)date block:(void (^)(void))block { - NSCParameterAssert(date != nil); - NSCParameterAssert(block != nil); - - self = [super init]; - if (self == nil) return nil; - - _date = [date copy]; - _block = [block copy]; - _disposable = [[RACDisposable alloc] init]; - - return self; -} - -#pragma mark NSObject - -- (NSString *)description { - return [NSString stringWithFormat:@"<%@: %p>{ date: %@ }", self.class, self, self.date]; -} - -@end diff --git a/ReactiveObjC/RACTuple.h b/ReactiveObjC/RACTuple.h deleted file mode 100644 index 50383325bc..0000000000 --- a/ReactiveObjC/RACTuple.h +++ /dev/null @@ -1,159 +0,0 @@ -// -// RACTuple.h -// ReactiveObjC -// -// Created by Josh Abernathy on 4/12/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import -#import "metamacros.h" - -@class RACSequence; - -/// Creates a new tuple with the given values. At least one value must be given. -/// Values can be nil. -#define RACTuplePack(...) \ - RACTuplePack_(__VA_ARGS__) - -/// Declares new object variables and unpacks a RACTuple into them. -/// -/// This macro should be used on the left side of an assignment, with the -/// tuple on the right side. Nothing else should appear on the same line, and the -/// macro should not be the only statement in a conditional or loop body. -/// -/// If the tuple has more values than there are variables listed, the excess -/// values are ignored. -/// -/// If the tuple has fewer values than there are variables listed, the excess -/// variables are initialized to nil. -/// -/// Examples -/// -/// RACTupleUnpack(NSString *string, NSNumber *num) = [RACTuple tupleWithObjects:@"foo", @5, nil]; -/// NSLog(@"string: %@", string); -/// NSLog(@"num: %@", num); -/// -/// /* The above is equivalent to: */ -/// RACTuple *t = [RACTuple tupleWithObjects:@"foo", @5, nil]; -/// NSString *string = t[0]; -/// NSNumber *num = t[1]; -/// NSLog(@"string: %@", string); -/// NSLog(@"num: %@", num); -#define RACTupleUnpack(...) \ - RACTupleUnpack_(__VA_ARGS__) - -/// A sentinel object that represents nils in the tuple. -/// -/// It should never be necessary to create a tuple nil yourself. Just use -/// +tupleNil. -@interface RACTupleNil : NSObject -/// A singleton instance. -+ (RACTupleNil *)tupleNil; -@end - - -/// A tuple is an ordered collection of objects. It may contain nils, represented -/// by RACTupleNil. -@interface RACTuple : NSObject - -@property (nonatomic, readonly) NSUInteger count; - -/// These properties all return the object at that index or nil if the number of -/// objects is less than the index. -@property (nonatomic, readonly) id first; -@property (nonatomic, readonly) id second; -@property (nonatomic, readonly) id third; -@property (nonatomic, readonly) id fourth; -@property (nonatomic, readonly) id fifth; -@property (nonatomic, readonly) id last; - -/// Creates a new tuple out of the array. Does not convert nulls to nils. -+ (instancetype)tupleWithObjectsFromArray:(NSArray *)array; - -/// Creates a new tuple out of the array. If `convert` is YES, it also converts -/// every NSNull to RACTupleNil. -+ (instancetype)tupleWithObjectsFromArray:(NSArray *)array convertNullsToNils:(BOOL)convert; - -/// Creates a new tuple with the given objects. Use RACTupleNil to represent -/// nils. -+ (instancetype)tupleWithObjects:(id)object, ... NS_REQUIRES_NIL_TERMINATION; - -/// Returns the object at `index` or nil if the object is a RACTupleNil. Unlike -/// NSArray and friends, it's perfectly fine to ask for the object at an index -/// past the tuple's count - 1. It will simply return nil. -- (id)objectAtIndex:(NSUInteger)index; - -/// Returns an array of all the objects. RACTupleNils are converted to NSNulls. -- (NSArray *)allObjects; - -/// Appends `obj` to the receiver. -/// -/// obj - The object to add to the tuple. This argument may be nil. -/// -/// Returns a new tuple. -- (instancetype)tupleByAddingObject:(id)obj; - -@end - -@interface RACTuple (RACSequenceAdditions) - -/// Returns a sequence of all the objects. RACTupleNils are converted to NSNulls. -@property (nonatomic, copy, readonly) RACSequence *rac_sequence; - -@end - -@interface RACTuple (ObjectSubscripting) -/// Returns the object at that index or nil if the number of objects is less -/// than the index. -- (id)objectAtIndexedSubscript:(NSUInteger)idx; -@end - -/// This and everything below is for internal use only. -/// -/// See RACTuplePack() and RACTupleUnpack() instead. -#define RACTuplePack_(...) \ - ([RACTuple tupleWithObjectsFromArray:@[ metamacro_foreach(RACTuplePack_object_or_ractuplenil,, __VA_ARGS__) ]]) - -#define RACTuplePack_object_or_ractuplenil(INDEX, ARG) \ - (ARG) ?: RACTupleNil.tupleNil, - -#define RACTupleUnpack_(...) \ - metamacro_foreach(RACTupleUnpack_decl,, __VA_ARGS__) \ - \ - int RACTupleUnpack_state = 0; \ - \ - RACTupleUnpack_after: \ - ; \ - metamacro_foreach(RACTupleUnpack_assign,, __VA_ARGS__) \ - if (RACTupleUnpack_state != 0) RACTupleUnpack_state = 2; \ - \ - while (RACTupleUnpack_state != 2) \ - if (RACTupleUnpack_state == 1) { \ - goto RACTupleUnpack_after; \ - } else \ - for (; RACTupleUnpack_state != 1; RACTupleUnpack_state = 1) \ - [RACTupleUnpackingTrampoline trampoline][ @[ metamacro_foreach(RACTupleUnpack_value,, __VA_ARGS__) ] ] - -#define RACTupleUnpack_state metamacro_concat(RACTupleUnpack_state, __LINE__) -#define RACTupleUnpack_after metamacro_concat(RACTupleUnpack_after, __LINE__) -#define RACTupleUnpack_loop metamacro_concat(RACTupleUnpack_loop, __LINE__) - -#define RACTupleUnpack_decl_name(INDEX) \ - metamacro_concat(metamacro_concat(RACTupleUnpack, __LINE__), metamacro_concat(_var, INDEX)) - -#define RACTupleUnpack_decl(INDEX, ARG) \ - __strong id RACTupleUnpack_decl_name(INDEX); - -#define RACTupleUnpack_assign(INDEX, ARG) \ - __strong ARG = RACTupleUnpack_decl_name(INDEX); - -#define RACTupleUnpack_value(INDEX, ARG) \ - [NSValue valueWithPointer:&RACTupleUnpack_decl_name(INDEX)], - -@interface RACTupleUnpackingTrampoline : NSObject - -+ (instancetype)trampoline; -- (void)setObject:(RACTuple *)tuple forKeyedSubscript:(NSArray *)variables; - -@end diff --git a/ReactiveObjC/RACTuple.m b/ReactiveObjC/RACTuple.m deleted file mode 100644 index 2848a2223b..0000000000 --- a/ReactiveObjC/RACTuple.m +++ /dev/null @@ -1,252 +0,0 @@ -// -// RACTuple.m -// ReactiveObjC -// -// Created by Josh Abernathy on 4/12/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACTuple.h" -#import -#import "RACTupleSequence.h" - -@implementation RACTupleNil - -+ (RACTupleNil *)tupleNil { - static dispatch_once_t onceToken; - static RACTupleNil *tupleNil = nil; - dispatch_once(&onceToken, ^{ - tupleNil = [[self alloc] init]; - }); - - return tupleNil; -} - -#pragma mark NSCopying - -- (id)copyWithZone:(NSZone *)zone { - return self; -} - -#pragma mark NSCoding - -- (id)initWithCoder:(NSCoder *)coder { - // Always return the singleton. - return self.class.tupleNil; -} - -- (void)encodeWithCoder:(NSCoder *)coder { -} - -@end - - -@interface RACTuple () -@property (nonatomic, strong) NSArray *backingArray; -@end - - -@implementation RACTuple - -- (instancetype)init { - self = [super init]; - if (self == nil) return nil; - - self.backingArray = [NSArray array]; - - return self; -} - -- (NSString *)description { - return [NSString stringWithFormat:@"<%@: %p> %@", self.class, self, self.allObjects]; -} - -- (BOOL)isEqual:(RACTuple *)object { - if (object == self) return YES; - if (![object isKindOfClass:self.class]) return NO; - - return [self.backingArray isEqual:object.backingArray]; -} - -- (NSUInteger)hash { - return self.backingArray.hash; -} - - -#pragma mark NSFastEnumeration - -- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)len { - return [self.backingArray countByEnumeratingWithState:state objects:buffer count:len]; -} - - -#pragma mark NSCopying - -- (instancetype)copyWithZone:(NSZone *)zone { - // we're immutable, bitches! - return self; -} - - -#pragma mark NSCoding - -- (id)initWithCoder:(NSCoder *)coder { - self = [self init]; - if (self == nil) return nil; - - self.backingArray = [coder decodeObjectForKey:@keypath(self.backingArray)]; - return self; -} - -- (void)encodeWithCoder:(NSCoder *)coder { - if (self.backingArray != nil) [coder encodeObject:self.backingArray forKey:@keypath(self.backingArray)]; -} - - -#pragma mark API - -+ (instancetype)tupleWithObjectsFromArray:(NSArray *)array { - return [self tupleWithObjectsFromArray:array convertNullsToNils:NO]; -} - -+ (instancetype)tupleWithObjectsFromArray:(NSArray *)array convertNullsToNils:(BOOL)convert { - RACTuple *tuple = [[self alloc] init]; - - if (convert) { - NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:array.count]; - for (id object in array) { - [newArray addObject:(object == NSNull.null ? RACTupleNil.tupleNil : object)]; - } - - tuple.backingArray = newArray; - } else { - tuple.backingArray = [array copy]; - } - - return tuple; -} - -+ (instancetype)tupleWithObjects:(id)object, ... { - RACTuple *tuple = [[self alloc] init]; - - va_list args; - va_start(args, object); - - NSUInteger count = 0; - for (id currentObject = object; currentObject != nil; currentObject = va_arg(args, id)) { - ++count; - } - - va_end(args); - - if (count == 0) { - tuple.backingArray = @[]; - return tuple; - } - - NSMutableArray *objects = [[NSMutableArray alloc] initWithCapacity:count]; - - va_start(args, object); - for (id currentObject = object; currentObject != nil; currentObject = va_arg(args, id)) { - [objects addObject:currentObject]; - } - - va_end(args); - - tuple.backingArray = objects; - return tuple; -} - -- (id)objectAtIndex:(NSUInteger)index { - if (index >= self.count) return nil; - - id object = self.backingArray[index]; - return (object == RACTupleNil.tupleNil ? nil : object); -} - -- (NSArray *)allObjects { - NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:self.backingArray.count]; - for (id object in self.backingArray) { - [newArray addObject:(object == RACTupleNil.tupleNil ? NSNull.null : object)]; - } - - return newArray; -} - -- (instancetype)tupleByAddingObject:(id)obj { - NSArray *newArray = [self.backingArray arrayByAddingObject:obj ?: RACTupleNil.tupleNil]; - return [self.class tupleWithObjectsFromArray:newArray]; -} - -- (NSUInteger)count { - return self.backingArray.count; -} - -- (id)first { - return self[0]; -} - -- (id)second { - return self[1]; -} - -- (id)third { - return self[2]; -} - -- (id)fourth { - return self[3]; -} - -- (id)fifth { - return self[4]; -} - -- (id)last { - return self[self.count - 1]; -} - -@end - - -@implementation RACTuple (RACSequenceAdditions) - -- (RACSequence *)rac_sequence { - return [RACTupleSequence sequenceWithTupleBackingArray:self.backingArray offset:0]; -} - -@end - -@implementation RACTuple (ObjectSubscripting) - -- (id)objectAtIndexedSubscript:(NSUInteger)idx { - return [self objectAtIndex:idx]; -} - -@end - - -@implementation RACTupleUnpackingTrampoline - -#pragma mark Lifecycle - -+ (instancetype)trampoline { - static dispatch_once_t onceToken; - static id trampoline = nil; - dispatch_once(&onceToken, ^{ - trampoline = [[self alloc] init]; - }); - - return trampoline; -} - -- (void)setObject:(RACTuple *)tuple forKeyedSubscript:(NSArray *)variables { - NSCParameterAssert(variables != nil); - - [variables enumerateObjectsUsingBlock:^(NSValue *value, NSUInteger index, BOOL *stop) { - __strong id *ptr = (__strong id *)value.pointerValue; - *ptr = tuple[index]; - }]; -} - -@end diff --git a/ReactiveObjC/RACTupleSequence.h b/ReactiveObjC/RACTupleSequence.h deleted file mode 100644 index c933684672..0000000000 --- a/ReactiveObjC/RACTupleSequence.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// RACTupleSequence.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-05-01. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACSequence.h" - -// Private class that adapts a RACTuple to the RACSequence interface. -@interface RACTupleSequence : RACSequence - -// Returns a sequence for enumerating over the given backing array (from a -// RACTuple), starting from the given offset. -+ (instancetype)sequenceWithTupleBackingArray:(NSArray *)backingArray offset:(NSUInteger)offset; - -@end diff --git a/ReactiveObjC/RACTupleSequence.m b/ReactiveObjC/RACTupleSequence.m deleted file mode 100644 index e83b749617..0000000000 --- a/ReactiveObjC/RACTupleSequence.m +++ /dev/null @@ -1,68 +0,0 @@ -// -// RACTupleSequence.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-05-01. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACTupleSequence.h" -#import "RACTuple.h" - -@interface RACTupleSequence () - -// The array being sequenced, as taken from RACTuple.backingArray. -@property (nonatomic, strong, readonly) NSArray *tupleBackingArray; - -// The index in the array from which the sequence starts. -@property (nonatomic, assign, readonly) NSUInteger offset; - -@end - -@implementation RACTupleSequence - -#pragma mark Lifecycle - -+ (instancetype)sequenceWithTupleBackingArray:(NSArray *)backingArray offset:(NSUInteger)offset { - NSCParameterAssert(offset <= backingArray.count); - - if (offset == backingArray.count) return self.empty; - - RACTupleSequence *seq = [[self alloc] init]; - seq->_tupleBackingArray = backingArray; - seq->_offset = offset; - return seq; -} - -#pragma mark RACSequence - -- (id)head { - id object = self.tupleBackingArray[self.offset]; - return (object == RACTupleNil.tupleNil ? NSNull.null : object); -} - -- (RACSequence *)tail { - RACSequence *sequence = [self.class sequenceWithTupleBackingArray:self.tupleBackingArray offset:self.offset + 1]; - sequence.name = self.name; - return sequence; -} - -- (NSArray *)array { - NSRange range = NSMakeRange(self.offset, self.tupleBackingArray.count - self.offset); - NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:range.length]; - - [self.tupleBackingArray enumerateObjectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:range] options:0 usingBlock:^(id object, NSUInteger index, BOOL *stop) { - id mappedObject = (object == RACTupleNil.tupleNil ? NSNull.null : object); - [array addObject:mappedObject]; - }]; - - return array; -} - -#pragma mark NSObject - -- (NSString *)description { - return [NSString stringWithFormat:@"<%@: %p>{ name = %@, tuple = %@ }", self.class, self, self.name, self.tupleBackingArray]; -} - -@end diff --git a/ReactiveObjC/RACUnarySequence.h b/ReactiveObjC/RACUnarySequence.h deleted file mode 100644 index 1606425e1c..0000000000 --- a/ReactiveObjC/RACUnarySequence.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// RACUnarySequence.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-05-01. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACSequence.h" - -// Private class representing a sequence of exactly one value. -@interface RACUnarySequence : RACSequence - -@end diff --git a/ReactiveObjC/RACUnarySequence.m b/ReactiveObjC/RACUnarySequence.m deleted file mode 100644 index ff5d333bba..0000000000 --- a/ReactiveObjC/RACUnarySequence.m +++ /dev/null @@ -1,81 +0,0 @@ -// -// RACUnarySequence.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-05-01. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACUnarySequence.h" -#import -#import "NSObject+RACDescription.h" - -@interface RACUnarySequence () - -// The single value stored in this sequence. -@property (nonatomic, strong, readwrite) id head; - -@end - -@implementation RACUnarySequence - -#pragma mark Properties - -@synthesize head = _head; - -#pragma mark Lifecycle - -+ (instancetype)return:(id)value { - RACUnarySequence *sequence = [[self alloc] init]; - sequence.head = value; - return [sequence setNameWithFormat:@"+return: %@", RACDescription(value)]; -} - -#pragma mark RACSequence - -- (RACSequence *)tail { - return nil; -} - -- (instancetype)bind:(RACStreamBindBlock (^)(void))block { - RACStreamBindBlock bindBlock = block(); - BOOL stop = NO; - - RACSequence *result = (id)[bindBlock(self.head, &stop) setNameWithFormat:@"[%@] -bind:", self.name]; - return result ?: self.class.empty; -} - -#pragma mark NSCoding - -- (Class)classForCoder { - // Unary sequences should be encoded as themselves, not array sequences. - return self.class; -} - -- (id)initWithCoder:(NSCoder *)coder { - id value = [coder decodeObjectForKey:@keypath(self.head)]; - return [self.class return:value]; -} - -- (void)encodeWithCoder:(NSCoder *)coder { - if (self.head != nil) [coder encodeObject:self.head forKey:@keypath(self.head)]; -} - -#pragma mark NSObject - -- (NSString *)description { - return [NSString stringWithFormat:@"<%@: %p>{ name = %@, head = %@ }", self.class, self, self.name, self.head]; -} - -- (NSUInteger)hash { - return [self.head hash]; -} - -- (BOOL)isEqual:(RACUnarySequence *)seq { - if (self == seq) return YES; - if (![seq isKindOfClass:RACUnarySequence.class]) return NO; - - return self.head == seq.head || [(NSObject *)self.head isEqual:seq.head]; -} - -@end diff --git a/ReactiveObjC/RACUnit.h b/ReactiveObjC/RACUnit.h deleted file mode 100644 index aae3defde2..0000000000 --- a/ReactiveObjC/RACUnit.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// RACUnit.h -// ReactiveObjC -// -// Created by Josh Abernathy on 3/27/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import - -/// A unit represents an empty value. -/// -/// It should never be necessary to create a unit yourself. Just use +defaultUnit. -@interface RACUnit : NSObject - -/// A singleton instance. -+ (RACUnit *)defaultUnit; - -@end diff --git a/ReactiveObjC/RACUnit.m b/ReactiveObjC/RACUnit.m deleted file mode 100644 index 691e5da427..0000000000 --- a/ReactiveObjC/RACUnit.m +++ /dev/null @@ -1,25 +0,0 @@ -// -// RACUnit.m -// ReactiveObjC -// -// Created by Josh Abernathy on 3/27/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACUnit.h" - -@implementation RACUnit - -#pragma mark API - -+ (RACUnit *)defaultUnit { - static dispatch_once_t onceToken; - static RACUnit *defaultUnit = nil; - dispatch_once(&onceToken, ^{ - defaultUnit = [[self alloc] init]; - }); - - return defaultUnit; -} - -@end diff --git a/ReactiveObjC/RACValueTransformer.h b/ReactiveObjC/RACValueTransformer.h deleted file mode 100644 index c827e6cd80..0000000000 --- a/ReactiveObjC/RACValueTransformer.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// RACValueTransformer.h -// ReactiveObjC -// -// Created by Josh Abernathy on 3/6/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import - -// A private block based transformer. -@interface RACValueTransformer : NSValueTransformer - -+ (instancetype)transformerWithBlock:(id (^)(id value))block; - -@end diff --git a/ReactiveObjC/RACValueTransformer.m b/ReactiveObjC/RACValueTransformer.m deleted file mode 100644 index 6f44e0f2b9..0000000000 --- a/ReactiveObjC/RACValueTransformer.m +++ /dev/null @@ -1,42 +0,0 @@ -// -// RACValueTransformer.m -// ReactiveObjC -// -// Created by Josh Abernathy on 3/6/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACValueTransformer.h" - -@interface RACValueTransformer () -@property (nonatomic, copy) id (^transformBlock)(id value); -@end - - -@implementation RACValueTransformer - - -#pragma mark NSValueTransformer - -+ (BOOL)allowsReverseTransformation { - return NO; -} - -- (id)transformedValue:(id)value { - return self.transformBlock(value); -} - - -#pragma mark API - -@synthesize transformBlock; - -+ (instancetype)transformerWithBlock:(id (^)(id value))block { - NSCParameterAssert(block != NULL); - - RACValueTransformer *transformer = [[self alloc] init]; - transformer.transformBlock = block; - return transformer; -} - -@end diff --git a/ReactiveObjC/ReactiveObjC.h b/ReactiveObjC/ReactiveObjC.h deleted file mode 100644 index 6defe8aa87..0000000000 --- a/ReactiveObjC/ReactiveObjC.h +++ /dev/null @@ -1,95 +0,0 @@ -// -// ReactiveObjC.h -// ReactiveObjC -// -// Created by Josh Abernathy on 3/5/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import - -//! Project version number for ReactiveObjC. -FOUNDATION_EXPORT double ReactiveObjCVersionNumber; - -//! Project version string for ReactiveObjC. -FOUNDATION_EXPORT const unsigned char ReactiveObjCVersionString[]; - -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import - -#if TARGET_OS_WATCH -#elif TARGET_OS_IOS || TARGET_OS_TV - #import - #import - #import - #import - #import - #import - #import - #import - #import - #import - - #if TARGET_OS_IOS - #import - #import - #import - #import - #import - #import - #import - #import - #import - #import - #endif -#elif TARGET_OS_MAC - #import - #import - #import - #import - #import -#endif diff --git a/ReactiveObjC/UIActionSheet+RACSignalSupport.h b/ReactiveObjC/UIActionSheet+RACSignalSupport.h deleted file mode 100644 index 8975f42ae9..0000000000 --- a/ReactiveObjC/UIActionSheet+RACSignalSupport.h +++ /dev/null @@ -1,32 +0,0 @@ -// -// UIActionSheet+RACSignalSupport.h -// ReactiveObjC -// -// Created by Dave Lee on 2013-06-22. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -@class RACDelegateProxy; -@class RACSignal; - -@interface UIActionSheet (RACSignalSupport) - -/// A delegate proxy which will be set as the receiver's delegate when any of the -/// methods in this category are used. -@property (nonatomic, strong, readonly) RACDelegateProxy *rac_delegateProxy; - -/// Creates a signal for button clicks on the receiver. -/// -/// When this method is invoked, the `rac_delegateProxy` will become the -/// receiver's delegate. Any previous delegate will become the -[RACDelegateProxy -/// rac_proxiedDelegate], so that it receives any messages that the proxy doesn't -/// know how to handle. Setting the receiver's `delegate` afterward is -/// considered undefined behavior. -/// -/// Returns a signal which will send the index of the specific button clicked. -/// The signal will complete when the receiver is deallocated. -- (RACSignal *)rac_buttonClickedSignal; - -@end diff --git a/ReactiveObjC/UIActionSheet+RACSignalSupport.m b/ReactiveObjC/UIActionSheet+RACSignalSupport.m deleted file mode 100644 index 6a1a4102d7..0000000000 --- a/ReactiveObjC/UIActionSheet+RACSignalSupport.m +++ /dev/null @@ -1,49 +0,0 @@ -// -// UIActionSheet+RACSignalSupport.m -// ReactiveObjC -// -// Created by Dave Lee on 2013-06-22. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "UIActionSheet+RACSignalSupport.h" -#import "RACDelegateProxy.h" -#import "RACSignal+Operations.h" -#import "NSObject+RACDeallocating.h" -#import "NSObject+RACDescription.h" -#import - -@implementation UIActionSheet (RACSignalSupport) - -static void RACUseDelegateProxy(UIActionSheet *self) { - if (self.delegate == self.rac_delegateProxy) return; - - self.rac_delegateProxy.rac_proxiedDelegate = self.delegate; - self.delegate = (id)self.rac_delegateProxy; -} - -- (RACDelegateProxy *)rac_delegateProxy { - RACDelegateProxy *proxy = objc_getAssociatedObject(self, _cmd); - if (proxy == nil) { - proxy = [[RACDelegateProxy alloc] initWithProtocol:@protocol(UIActionSheetDelegate)]; - objc_setAssociatedObject(self, _cmd, proxy, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - } - - return proxy; -} - -- (RACSignal *)rac_buttonClickedSignal { - RACSignal *signal = [[[[self.rac_delegateProxy - signalForSelector:@selector(actionSheet:clickedButtonAtIndex:)] - reduceEach:^(UIActionSheet *actionSheet, NSNumber *buttonIndex) { - return buttonIndex; - }] - takeUntil:self.rac_willDeallocSignal] - setNameWithFormat:@"%@ -rac_buttonClickedSignal", RACDescription(self)]; - - RACUseDelegateProxy(self); - - return signal; -} - -@end diff --git a/ReactiveObjC/UIAlertView+RACSignalSupport.h b/ReactiveObjC/UIAlertView+RACSignalSupport.h deleted file mode 100644 index 848db46a89..0000000000 --- a/ReactiveObjC/UIAlertView+RACSignalSupport.h +++ /dev/null @@ -1,47 +0,0 @@ -// -// UIAlertView+RACSignalSupport.h -// ReactiveObjC -// -// Created by Henrik Hodne on 6/16/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -@class RACDelegateProxy; -@class RACSignal; - -@interface UIAlertView (RACSignalSupport) - -/// A delegate proxy which will be set as the receiver's delegate when any of the -/// methods in this category are used. -@property (nonatomic, strong, readonly) RACDelegateProxy *rac_delegateProxy; - -/// Creates a signal for button clicks on the receiver. -/// -/// When this method is invoked, the `rac_delegateProxy` will become the -/// receiver's delegate. Any previous delegate will become the -[RACDelegateProxy -/// rac_proxiedDelegate], so that it receives any messages that the proxy doesn't -/// know how to handle. Setting the receiver's `delegate` afterward is considered -/// undefined behavior. -/// -/// Note that this signal will not send a value when the alert is dismissed -/// programatically. -/// -/// Returns a signal which will send the index of the specific button clicked. -/// The signal will complete itself when the receiver is deallocated. -- (RACSignal *)rac_buttonClickedSignal; - -/// Creates a signal for dismissal of the receiver. -/// -/// When this method is invoked, the `rac_delegateProxy` will become the -/// receiver's delegate. Any previous delegate will become the -[RACDelegateProxy -/// rac_proxiedDelegate], so that it receives any messages that the proxy doesn't -/// know how to handle. Setting the receiver's `delegate` afterward is considered -/// undefined behavior. -/// -/// Returns a signal which will send the index of the button associated with the -/// dismissal. The signal will complete itself when the receiver is deallocated. -- (RACSignal *)rac_willDismissSignal; - -@end diff --git a/ReactiveObjC/UIAlertView+RACSignalSupport.m b/ReactiveObjC/UIAlertView+RACSignalSupport.m deleted file mode 100644 index 0afc94a8d6..0000000000 --- a/ReactiveObjC/UIAlertView+RACSignalSupport.m +++ /dev/null @@ -1,63 +0,0 @@ -// -// UIAlertView+RACSignalSupport.m -// ReactiveObjC -// -// Created by Henrik Hodne on 6/16/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "UIAlertView+RACSignalSupport.h" -#import "RACDelegateProxy.h" -#import "RACSignal+Operations.h" -#import "NSObject+RACDeallocating.h" -#import "NSObject+RACDescription.h" -#import - -@implementation UIAlertView (RACSignalSupport) - -static void RACUseDelegateProxy(UIAlertView *self) { - if (self.delegate == self.rac_delegateProxy) return; - - self.rac_delegateProxy.rac_proxiedDelegate = self.delegate; - self.delegate = (id)self.rac_delegateProxy; -} - -- (RACDelegateProxy *)rac_delegateProxy { - RACDelegateProxy *proxy = objc_getAssociatedObject(self, _cmd); - if (proxy == nil) { - proxy = [[RACDelegateProxy alloc] initWithProtocol:@protocol(UIAlertViewDelegate)]; - objc_setAssociatedObject(self, _cmd, proxy, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - } - - return proxy; -} - -- (RACSignal *)rac_buttonClickedSignal { - RACSignal *signal = [[[[self.rac_delegateProxy - signalForSelector:@selector(alertView:clickedButtonAtIndex:)] - reduceEach:^(UIAlertView *alertView, NSNumber *buttonIndex) { - return buttonIndex; - }] - takeUntil:self.rac_willDeallocSignal] - setNameWithFormat:@"%@ -rac_buttonClickedSignal", RACDescription(self)]; - - RACUseDelegateProxy(self); - - return signal; -} - -- (RACSignal *)rac_willDismissSignal { - RACSignal *signal = [[[[self.rac_delegateProxy - signalForSelector:@selector(alertView:willDismissWithButtonIndex:)] - reduceEach:^(UIAlertView *alertView, NSNumber *buttonIndex) { - return buttonIndex; - }] - takeUntil:self.rac_willDeallocSignal] - setNameWithFormat:@"%@ -rac_willDismissSignal", RACDescription(self)]; - - RACUseDelegateProxy(self); - - return signal; -} - -@end diff --git a/ReactiveObjC/UIBarButtonItem+RACCommandSupport.h b/ReactiveObjC/UIBarButtonItem+RACCommandSupport.h deleted file mode 100644 index 0061934245..0000000000 --- a/ReactiveObjC/UIBarButtonItem+RACCommandSupport.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// UIBarButtonItem+RACCommandSupport.h -// ReactiveObjC -// -// Created by Kyle LeNeau on 3/27/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -@class RACCommand<__contravariant InputType>; - -@interface UIBarButtonItem (RACCommandSupport) - -/// Sets the control's command. When the control is clicked, the command is -/// executed with the sender of the event. The control's enabledness is bound -/// to the command's `canExecute`. -/// -/// Note: this will reset the control's target and action. -@property (nonatomic, strong) RACCommand<__kindof UIBarButtonItem *> *rac_command; - -@end diff --git a/ReactiveObjC/UIBarButtonItem+RACCommandSupport.m b/ReactiveObjC/UIBarButtonItem+RACCommandSupport.m deleted file mode 100644 index 225e8b751a..0000000000 --- a/ReactiveObjC/UIBarButtonItem+RACCommandSupport.m +++ /dev/null @@ -1,54 +0,0 @@ -// -// UIBarButtonItem+RACCommandSupport.m -// ReactiveObjC -// -// Created by Kyle LeNeau on 3/27/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "UIBarButtonItem+RACCommandSupport.h" -#import -#import "RACCommand.h" -#import "RACDisposable.h" -#import "RACSignal+Operations.h" -#import - -static void *UIControlRACCommandKey = &UIControlRACCommandKey; -static void *UIControlEnabledDisposableKey = &UIControlEnabledDisposableKey; - -@implementation UIBarButtonItem (RACCommandSupport) - -- (RACCommand *)rac_command { - return objc_getAssociatedObject(self, UIControlRACCommandKey); -} - -- (void)setRac_command:(RACCommand *)command { - objc_setAssociatedObject(self, UIControlRACCommandKey, command, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - - // Check for stored signal in order to remove it and add a new one - RACDisposable *disposable = objc_getAssociatedObject(self, UIControlEnabledDisposableKey); - [disposable dispose]; - - if (command == nil) return; - - disposable = [command.enabled setKeyPath:@keypath(self.enabled) onObject:self]; - objc_setAssociatedObject(self, UIControlEnabledDisposableKey, disposable, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - - [self rac_hijackActionAndTargetIfNeeded]; -} - -- (void)rac_hijackActionAndTargetIfNeeded { - SEL hijackSelector = @selector(rac_commandPerformAction:); - if (self.target == self && self.action == hijackSelector) return; - - if (self.target != nil) NSLog(@"WARNING: UIBarButtonItem.rac_command hijacks the control's existing target and action."); - - self.target = self; - self.action = hijackSelector; -} - -- (void)rac_commandPerformAction:(id)sender { - [self.rac_command execute:sender]; -} - -@end diff --git a/ReactiveObjC/UIButton+RACCommandSupport.h b/ReactiveObjC/UIButton+RACCommandSupport.h deleted file mode 100644 index 69af7c7577..0000000000 --- a/ReactiveObjC/UIButton+RACCommandSupport.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// UIButton+RACCommandSupport.h -// ReactiveObjC -// -// Created by Ash Furrow on 2013-06-06. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -@class RACCommand<__contravariant InputType>; - -@interface UIButton (RACCommandSupport) - -/// Sets the button's command. When the button is clicked, the command is -/// executed with the sender of the event. The button's enabledness is bound -/// to the command's `canExecute`. -@property (nonatomic, strong) RACCommand<__kindof UIButton *> *rac_command; - -@end diff --git a/ReactiveObjC/UIButton+RACCommandSupport.m b/ReactiveObjC/UIButton+RACCommandSupport.m deleted file mode 100644 index 6b11dad276..0000000000 --- a/ReactiveObjC/UIButton+RACCommandSupport.m +++ /dev/null @@ -1,56 +0,0 @@ -// -// UIButton+RACCommandSupport.m -// ReactiveObjC -// -// Created by Ash Furrow on 2013-06-06. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "UIButton+RACCommandSupport.h" -#import -#import "RACCommand.h" -#import "RACDisposable.h" -#import "RACSignal+Operations.h" -#import - -static void *UIButtonRACCommandKey = &UIButtonRACCommandKey; -static void *UIButtonEnabledDisposableKey = &UIButtonEnabledDisposableKey; - -@implementation UIButton (RACCommandSupport) - -- (RACCommand *)rac_command { - return objc_getAssociatedObject(self, UIButtonRACCommandKey); -} - -- (void)setRac_command:(RACCommand *)command { - objc_setAssociatedObject(self, UIButtonRACCommandKey, command, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - - // Check for stored signal in order to remove it and add a new one - RACDisposable *disposable = objc_getAssociatedObject(self, UIButtonEnabledDisposableKey); - [disposable dispose]; - - if (command == nil) return; - - disposable = [command.enabled setKeyPath:@keypath(self.enabled) onObject:self]; - objc_setAssociatedObject(self, UIButtonEnabledDisposableKey, disposable, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - - [self rac_hijackActionAndTargetIfNeeded]; -} - -- (void)rac_hijackActionAndTargetIfNeeded { - SEL hijackSelector = @selector(rac_commandPerformAction:); - - for (NSString *selector in [self actionsForTarget:self forControlEvent:UIControlEventTouchUpInside]) { - if (hijackSelector == NSSelectorFromString(selector)) { - return; - } - } - - [self addTarget:self action:hijackSelector forControlEvents:UIControlEventTouchUpInside]; -} - -- (void)rac_commandPerformAction:(id)sender { - [self.rac_command execute:sender]; -} - -@end diff --git a/ReactiveObjC/UICollectionReusableView+RACSignalSupport.h b/ReactiveObjC/UICollectionReusableView+RACSignalSupport.h deleted file mode 100644 index 94f4890504..0000000000 --- a/ReactiveObjC/UICollectionReusableView+RACSignalSupport.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// UICollectionReusableView+RACSignalSupport.h -// ReactiveObjC -// -// Created by Kent Wong on 2013-10-04. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -@class RACSignal; - -// This category is only applicable to iOS >= 6.0. -@interface UICollectionReusableView (RACSignalSupport) - -/// A signal which will send a RACUnit whenever -prepareForReuse is invoked upon -/// the receiver. -/// -/// Examples -/// -/// [[[self.cancelButton -/// rac_signalForControlEvents:UIControlEventTouchUpInside] -/// takeUntil:self.rac_prepareForReuseSignal] -/// subscribeNext:^(UIButton *x) { -/// // do other things -/// }]; -@property (nonatomic, strong, readonly) RACSignal *rac_prepareForReuseSignal; - -@end diff --git a/ReactiveObjC/UICollectionReusableView+RACSignalSupport.m b/ReactiveObjC/UICollectionReusableView+RACSignalSupport.m deleted file mode 100644 index 9215c162ba..0000000000 --- a/ReactiveObjC/UICollectionReusableView+RACSignalSupport.m +++ /dev/null @@ -1,31 +0,0 @@ -// -// UICollectionReusableView+RACSignalSupport.m -// ReactiveObjC -// -// Created by Kent Wong on 2013-10-04. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "UICollectionReusableView+RACSignalSupport.h" -#import "NSObject+RACDescription.h" -#import "NSObject+RACSelectorSignal.h" -#import "RACSignal+Operations.h" -#import "RACUnit.h" -#import - -@implementation UICollectionReusableView (RACSignalSupport) - -- (RACSignal *)rac_prepareForReuseSignal { - RACSignal *signal = objc_getAssociatedObject(self, _cmd); - if (signal != nil) return signal; - - signal = [[[self - rac_signalForSelector:@selector(prepareForReuse)] - mapReplace:RACUnit.defaultUnit] - setNameWithFormat:@"%@ -rac_prepareForReuseSignal", RACDescription(self)]; - - objc_setAssociatedObject(self, _cmd, signal, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - return signal; -} - -@end diff --git a/ReactiveObjC/UIControl+RACSignalSupport.h b/ReactiveObjC/UIControl+RACSignalSupport.h deleted file mode 100644 index 524ba049b4..0000000000 --- a/ReactiveObjC/UIControl+RACSignalSupport.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// UIControl+RACSignalSupport.h -// ReactiveObjC -// -// Created by Josh Abernathy on 4/17/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import - -@class RACSignal; - -@interface UIControl (RACSignalSupport) - -/// Creates and returns a signal that sends the sender of the control event -/// whenever one of the control events is triggered. -- (RACSignal *)rac_signalForControlEvents:(UIControlEvents)controlEvents; - -@end diff --git a/ReactiveObjC/UIControl+RACSignalSupport.m b/ReactiveObjC/UIControl+RACSignalSupport.m deleted file mode 100644 index df3108eb71..0000000000 --- a/ReactiveObjC/UIControl+RACSignalSupport.m +++ /dev/null @@ -1,43 +0,0 @@ -// -// UIControl+RACSignalSupport.m -// ReactiveObjC -// -// Created by Josh Abernathy on 4/17/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "UIControl+RACSignalSupport.h" -#import -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACSignal.h" -#import "RACSubscriber.h" -#import "NSObject+RACDeallocating.h" -#import "NSObject+RACDescription.h" - -@implementation UIControl (RACSignalSupport) - -- (RACSignal *)rac_signalForControlEvents:(UIControlEvents)controlEvents { - @weakify(self); - - return [[RACSignal - createSignal:^(id subscriber) { - @strongify(self); - - [self addTarget:subscriber action:@selector(sendNext:) forControlEvents:controlEvents]; - - RACDisposable *disposable = [RACDisposable disposableWithBlock:^{ - [subscriber sendCompleted]; - }]; - [self.rac_deallocDisposable addDisposable:disposable]; - - return [RACDisposable disposableWithBlock:^{ - @strongify(self); - [self.rac_deallocDisposable removeDisposable:disposable]; - [self removeTarget:subscriber action:@selector(sendNext:) forControlEvents:controlEvents]; - }]; - }] - setNameWithFormat:@"%@ -rac_signalForControlEvents: %lx", RACDescription(self), (unsigned long)controlEvents]; -} - -@end diff --git a/ReactiveObjC/UIControl+RACSignalSupportPrivate.h b/ReactiveObjC/UIControl+RACSignalSupportPrivate.h deleted file mode 100644 index 3f0ac348c9..0000000000 --- a/ReactiveObjC/UIControl+RACSignalSupportPrivate.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// UIControl+RACSignalSupportPrivate.h -// ReactiveObjC -// -// Created by Uri Baghin on 06/08/2013. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -@class RACChannelTerminal; - -@interface UIControl (RACSignalSupportPrivate) - -// Adds a RACChannel-based interface to the receiver for the given -// UIControlEvents and exposes it. -// -// controlEvents - A mask of UIControlEvents on which to send new values. -// key - The key whose value should be read and set when a control -// event fires and when a value is sent to the -// RACChannelTerminal respectively. -// nilValue - The value to be assigned to the key when `nil` is sent to the -// RACChannelTerminal. -// -// Returns a RACChannelTerminal which will send future values from the receiver, -// and update the receiver when values are sent to the terminal. -- (RACChannelTerminal *)rac_channelForControlEvents:(UIControlEvents)controlEvents key:(NSString *)key nilValue:(id)nilValue; - -@end diff --git a/ReactiveObjC/UIControl+RACSignalSupportPrivate.m b/ReactiveObjC/UIControl+RACSignalSupportPrivate.m deleted file mode 100644 index 867f57e74d..0000000000 --- a/ReactiveObjC/UIControl+RACSignalSupportPrivate.m +++ /dev/null @@ -1,48 +0,0 @@ -// -// UIControl+RACSignalSupportPrivate.m -// ReactiveObjC -// -// Created by Uri Baghin on 06/08/2013. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "UIControl+RACSignalSupportPrivate.h" -#import "NSObject+RACDeallocating.h" -#import "NSObject+RACLifting.h" -#import "RACChannel.h" -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACSignal+Operations.h" -#import "UIControl+RACSignalSupport.h" - -@implementation UIControl (RACSignalSupportPrivate) - -- (RACChannelTerminal *)rac_channelForControlEvents:(UIControlEvents)controlEvents key:(NSString *)key nilValue:(id)nilValue { - NSCParameterAssert(key.length > 0); - key = [key copy]; - RACChannel *channel = [[RACChannel alloc] init]; - - [self.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - [channel.followingTerminal sendCompleted]; - }]]; - - RACSignal *eventSignal = [[[self - rac_signalForControlEvents:controlEvents] - mapReplace:key] - takeUntil:[[channel.followingTerminal - ignoreValues] - catchTo:RACSignal.empty]]; - [[self - rac_liftSelector:@selector(valueForKey:) withSignals:eventSignal, nil] - subscribe:channel.followingTerminal]; - - RACSignal *valuesSignal = [channel.followingTerminal - map:^(id value) { - return value ?: nilValue; - }]; - [self rac_liftSelector:@selector(setValue:forKey:) withSignals:valuesSignal, [RACSignal return:key], nil]; - - return channel.leadingTerminal; -} - -@end diff --git a/ReactiveObjC/UIDatePicker+RACSignalSupport.h b/ReactiveObjC/UIDatePicker+RACSignalSupport.h deleted file mode 100644 index cc03460b96..0000000000 --- a/ReactiveObjC/UIDatePicker+RACSignalSupport.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// UIDatePicker+RACSignalSupport.h -// ReactiveObjC -// -// Created by Uri Baghin on 20/07/2013. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -@class RACChannelTerminal; - -@interface UIDatePicker (RACSignalSupport) - -/// Creates a new RACChannel-based binding to the receiver. -/// -/// nilValue - The date to set when the terminal receives `nil`. -/// -/// Returns a RACChannelTerminal that sends the receiver's date whenever the -/// UIControlEventValueChanged control event is fired, and sets the date to the -/// values it receives. -- (RACChannelTerminal *)rac_newDateChannelWithNilValue:(NSDate *)nilValue; - -@end diff --git a/ReactiveObjC/UIDatePicker+RACSignalSupport.m b/ReactiveObjC/UIDatePicker+RACSignalSupport.m deleted file mode 100644 index 5c815c2f24..0000000000 --- a/ReactiveObjC/UIDatePicker+RACSignalSupport.m +++ /dev/null @@ -1,19 +0,0 @@ -// -// UIDatePicker+RACSignalSupport.m -// ReactiveObjC -// -// Created by Uri Baghin on 20/07/2013. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "UIDatePicker+RACSignalSupport.h" -#import -#import "UIControl+RACSignalSupportPrivate.h" - -@implementation UIDatePicker (RACSignalSupport) - -- (RACChannelTerminal *)rac_newDateChannelWithNilValue:(NSDate *)nilValue { - return [self rac_channelForControlEvents:UIControlEventValueChanged key:@keypath(self.date) nilValue:nilValue]; -} - -@end diff --git a/ReactiveObjC/UIGestureRecognizer+RACSignalSupport.h b/ReactiveObjC/UIGestureRecognizer+RACSignalSupport.h deleted file mode 100644 index ecafdb0b9e..0000000000 --- a/ReactiveObjC/UIGestureRecognizer+RACSignalSupport.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// UIGestureRecognizer+RACSignalSupport.h -// ReactiveObjC -// -// Created by Josh Vera on 5/5/13. -// Copyright (c) 2013 GitHub. All rights reserved. -// - -#import - -@class RACSignal; - -@interface UIGestureRecognizer (RACSignalSupport) - -/// Returns a signal that sends the receiver when its gesture occurs. -- (RACSignal *)rac_gestureSignal; - -@end diff --git a/ReactiveObjC/UIGestureRecognizer+RACSignalSupport.m b/ReactiveObjC/UIGestureRecognizer+RACSignalSupport.m deleted file mode 100644 index 4f816eb8e6..0000000000 --- a/ReactiveObjC/UIGestureRecognizer+RACSignalSupport.m +++ /dev/null @@ -1,40 +0,0 @@ -// -// UIGestureRecognizer+RACSignalSupport.m -// ReactiveObjC -// -// Created by Josh Vera on 5/5/13. -// Copyright (c) 2013 GitHub. All rights reserved. -// - -#import "UIGestureRecognizer+RACSignalSupport.h" -#import -#import "NSObject+RACDeallocating.h" -#import "NSObject+RACDescription.h" -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACSignal.h" -#import "RACSubscriber.h" - -@implementation UIGestureRecognizer (RACSignalSupport) - -- (RACSignal *)rac_gestureSignal { - @weakify(self); - - return [[RACSignal - createSignal:^(id subscriber) { - @strongify(self); - - [self addTarget:subscriber action:@selector(sendNext:)]; - [self.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - [subscriber sendCompleted]; - }]]; - - return [RACDisposable disposableWithBlock:^{ - @strongify(self); - [self removeTarget:subscriber action:@selector(sendNext:)]; - }]; - }] - setNameWithFormat:@"%@ -rac_gestureSignal", RACDescription(self)]; -} - -@end diff --git a/ReactiveObjC/UIImagePickerController+RACSignalSupport.h b/ReactiveObjC/UIImagePickerController+RACSignalSupport.h deleted file mode 100644 index 84d90476f3..0000000000 --- a/ReactiveObjC/UIImagePickerController+RACSignalSupport.h +++ /dev/null @@ -1,33 +0,0 @@ -// -// UIImagePickerController+RACSignalSupport.h -// ReactiveObjC -// -// Created by Timur Kuchkarov on 28.03.14. -// Copyright (c) 2014 GitHub. All rights reserved. -// - -#import - -@class RACDelegateProxy; -@class RACSignal; - -@interface UIImagePickerController (RACSignalSupport) - -/// A delegate proxy which will be set as the receiver's delegate when any of the -/// methods in this category are used. -@property (nonatomic, strong, readonly) RACDelegateProxy *rac_delegateProxy; - -/// Creates a signal for every new selected image. -/// -/// When this method is invoked, the `rac_delegateProxy` will become the -/// receiver's delegate. Any previous delegate will become the -[RACDelegateProxy -/// rac_proxiedDelegate], so that it receives any messages that the proxy doesn't -/// know how to handle. Setting the receiver's `delegate` afterward is considered -/// undefined behavior. -/// -/// Returns a signal which will send the dictionary with info for the selected image. -/// Caller is responsible for picker controller dismissal. The signal will complete -/// itself when the receiver is deallocated or when user cancels selection. -- (RACSignal *)rac_imageSelectedSignal; - -@end diff --git a/ReactiveObjC/UIImagePickerController+RACSignalSupport.m b/ReactiveObjC/UIImagePickerController+RACSignalSupport.m deleted file mode 100644 index a5df4eec76..0000000000 --- a/ReactiveObjC/UIImagePickerController+RACSignalSupport.m +++ /dev/null @@ -1,53 +0,0 @@ -// -// UIImagePickerController+RACSignalSupport.m -// ReactiveObjC -// -// Created by Timur Kuchkarov on 28.03.14. -// Copyright (c) 2014 GitHub. All rights reserved. -// - -#import "UIImagePickerController+RACSignalSupport.h" -#import "RACDelegateProxy.h" -#import "RACSignal+Operations.h" -#import "NSObject+RACDeallocating.h" -#import "NSObject+RACDescription.h" -#import - -@implementation UIImagePickerController (RACSignalSupport) - -static void RACUseDelegateProxy(UIImagePickerController *self) { - if (self.delegate == self.rac_delegateProxy) return; - - self.rac_delegateProxy.rac_proxiedDelegate = self.delegate; - self.delegate = (id)self.rac_delegateProxy; -} - -- (RACDelegateProxy *)rac_delegateProxy { - RACDelegateProxy *proxy = objc_getAssociatedObject(self, _cmd); - if (proxy == nil) { - proxy = [[RACDelegateProxy alloc] initWithProtocol:@protocol(UIImagePickerControllerDelegate)]; - objc_setAssociatedObject(self, _cmd, proxy, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - } - - return proxy; -} - -- (RACSignal *)rac_imageSelectedSignal { - RACSignal *pickerCancelledSignal = [[self.rac_delegateProxy - signalForSelector:@selector(imagePickerControllerDidCancel:)] - merge:self.rac_willDeallocSignal]; - - RACSignal *imagePickerSignal = [[[[self.rac_delegateProxy - signalForSelector:@selector(imagePickerController:didFinishPickingMediaWithInfo:)] - reduceEach:^(UIImagePickerController *pickerController, NSDictionary *userInfo) { - return userInfo; - }] - takeUntil:pickerCancelledSignal] - setNameWithFormat:@"%@ -rac_imageSelectedSignal", RACDescription(self)]; - - RACUseDelegateProxy(self); - - return imagePickerSignal; -} - -@end diff --git a/ReactiveObjC/UIRefreshControl+RACCommandSupport.h b/ReactiveObjC/UIRefreshControl+RACCommandSupport.h deleted file mode 100644 index 09b3413449..0000000000 --- a/ReactiveObjC/UIRefreshControl+RACCommandSupport.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// UIRefreshControl+RACCommandSupport.h -// ReactiveObjC -// -// Created by Dave Lee on 2013-10-17. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -@class RACCommand<__contravariant InputType>; - -@interface UIRefreshControl (RACCommandSupport) - -/// Manipulate the RACCommand property associated with this refresh control. -/// -/// When this refresh control is activated by the user, the command will be -/// executed. Upon completion or error of the execution signal, -endRefreshing -/// will be invoked. -@property (nonatomic, strong) RACCommand<__kindof UIRefreshControl *> *rac_command; - -@end diff --git a/ReactiveObjC/UIRefreshControl+RACCommandSupport.m b/ReactiveObjC/UIRefreshControl+RACCommandSupport.m deleted file mode 100644 index 3301ca194b..0000000000 --- a/ReactiveObjC/UIRefreshControl+RACCommandSupport.m +++ /dev/null @@ -1,59 +0,0 @@ -// -// UIRefreshControl+RACCommandSupport.m -// ReactiveObjC -// -// Created by Dave Lee on 2013-10-17. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "UIRefreshControl+RACCommandSupport.h" -#import -#import "RACCommand.h" -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACSignal.h" -#import "RACSignal+Operations.h" -#import "UIControl+RACSignalSupport.h" -#import - -static void *UIRefreshControlRACCommandKey = &UIRefreshControlRACCommandKey; -static void *UIRefreshControlDisposableKey = &UIRefreshControlDisposableKey; - -@implementation UIRefreshControl (RACCommandSupport) - -- (RACCommand *)rac_command { - return objc_getAssociatedObject(self, UIRefreshControlRACCommandKey); -} - -- (void)setRac_command:(RACCommand *)command { - objc_setAssociatedObject(self, UIRefreshControlRACCommandKey, command, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - - // Dispose of any active command associations. - [objc_getAssociatedObject(self, UIRefreshControlDisposableKey) dispose]; - - if (command == nil) return; - - // Like RAC(self, enabled) = command.enabled; but with access to disposable. - RACDisposable *enabledDisposable = [command.enabled setKeyPath:@keypath(self.enabled) onObject:self]; - - RACDisposable *executionDisposable = [[[[[self - rac_signalForControlEvents:UIControlEventValueChanged] - map:^(UIRefreshControl *x) { - return [[[command - execute:x] - catchTo:[RACSignal empty]] - then:^{ - return [RACSignal return:x]; - }]; - }] - concat] - deliverOnMainThread] - subscribeNext:^(UIRefreshControl *x) { - [x endRefreshing]; - }]; - - RACDisposable *commandDisposable = [RACCompoundDisposable compoundDisposableWithDisposables:@[ enabledDisposable, executionDisposable ]]; - objc_setAssociatedObject(self, UIRefreshControlDisposableKey, commandDisposable, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -@end diff --git a/ReactiveObjC/UISegmentedControl+RACSignalSupport.h b/ReactiveObjC/UISegmentedControl+RACSignalSupport.h deleted file mode 100644 index c4c70627ae..0000000000 --- a/ReactiveObjC/UISegmentedControl+RACSignalSupport.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// UISegmentedControl+RACSignalSupport.h -// ReactiveObjC -// -// Created by Uri Baghin on 20/07/2013. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -@class RACChannelTerminal; - -@interface UISegmentedControl (RACSignalSupport) - -/// Creates a new RACChannel-based binding to the receiver. -/// -/// nilValue - The segment to select when the terminal receives `nil`. -/// -/// Returns a RACChannelTerminal that sends the receiver's currently selected -/// segment's index whenever the UIControlEventValueChanged control event is -/// fired, and sets the selected segment index to the values it receives. -- (RACChannelTerminal *)rac_newSelectedSegmentIndexChannelWithNilValue:(NSNumber *)nilValue; - -@end diff --git a/ReactiveObjC/UISegmentedControl+RACSignalSupport.m b/ReactiveObjC/UISegmentedControl+RACSignalSupport.m deleted file mode 100644 index 782cabca08..0000000000 --- a/ReactiveObjC/UISegmentedControl+RACSignalSupport.m +++ /dev/null @@ -1,19 +0,0 @@ -// -// UISegmentedControl+RACSignalSupport.m -// ReactiveObjC -// -// Created by Uri Baghin on 20/07/2013. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "UISegmentedControl+RACSignalSupport.h" -#import -#import "UIControl+RACSignalSupportPrivate.h" - -@implementation UISegmentedControl (RACSignalSupport) - -- (RACChannelTerminal *)rac_newSelectedSegmentIndexChannelWithNilValue:(NSNumber *)nilValue { - return [self rac_channelForControlEvents:UIControlEventValueChanged key:@keypath(self.selectedSegmentIndex) nilValue:nilValue]; -} - -@end diff --git a/ReactiveObjC/UISlider+RACSignalSupport.h b/ReactiveObjC/UISlider+RACSignalSupport.h deleted file mode 100644 index 841bbad9d2..0000000000 --- a/ReactiveObjC/UISlider+RACSignalSupport.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// UISlider+RACSignalSupport.h -// ReactiveObjC -// -// Created by Uri Baghin on 20/07/2013. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -@class RACChannelTerminal; - -@interface UISlider (RACSignalSupport) - -/// Creates a new RACChannel-based binding to the receiver. -/// -/// nilValue - The value to set when the terminal receives `nil`. -/// -/// Returns a RACChannelTerminal that sends the receiver's value whenever the -/// UIControlEventValueChanged control event is fired, and sets the value to the -/// values it receives. -- (RACChannelTerminal *)rac_newValueChannelWithNilValue:(NSNumber *)nilValue; - -@end diff --git a/ReactiveObjC/UISlider+RACSignalSupport.m b/ReactiveObjC/UISlider+RACSignalSupport.m deleted file mode 100644 index 3583d843c5..0000000000 --- a/ReactiveObjC/UISlider+RACSignalSupport.m +++ /dev/null @@ -1,19 +0,0 @@ -// -// UISlider+RACSignalSupport.m -// ReactiveObjC -// -// Created by Uri Baghin on 20/07/2013. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "UISlider+RACSignalSupport.h" -#import -#import "UIControl+RACSignalSupportPrivate.h" - -@implementation UISlider (RACSignalSupport) - -- (RACChannelTerminal *)rac_newValueChannelWithNilValue:(NSNumber *)nilValue { - return [self rac_channelForControlEvents:UIControlEventValueChanged key:@keypath(self.value) nilValue:nilValue]; -} - -@end diff --git a/ReactiveObjC/UIStepper+RACSignalSupport.h b/ReactiveObjC/UIStepper+RACSignalSupport.h deleted file mode 100644 index b0b49416ec..0000000000 --- a/ReactiveObjC/UIStepper+RACSignalSupport.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// UIStepper+RACSignalSupport.h -// ReactiveObjC -// -// Created by Uri Baghin on 20/07/2013. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -@class RACChannelTerminal; - -@interface UIStepper (RACSignalSupport) - -/// Creates a new RACChannel-based binding to the receiver. -/// -/// nilValue - The value to set when the terminal receives `nil`. -/// -/// Returns a RACChannelTerminal that sends the receiver's value whenever the -/// UIControlEventValueChanged control event is fired, and sets the value to the -/// values it receives. -- (RACChannelTerminal *)rac_newValueChannelWithNilValue:(NSNumber *)nilValue; - -@end diff --git a/ReactiveObjC/UIStepper+RACSignalSupport.m b/ReactiveObjC/UIStepper+RACSignalSupport.m deleted file mode 100644 index 5eee8f8534..0000000000 --- a/ReactiveObjC/UIStepper+RACSignalSupport.m +++ /dev/null @@ -1,19 +0,0 @@ -// -// UIStepper+RACSignalSupport.m -// ReactiveObjC -// -// Created by Uri Baghin on 20/07/2013. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "UIStepper+RACSignalSupport.h" -#import -#import "UIControl+RACSignalSupportPrivate.h" - -@implementation UIStepper (RACSignalSupport) - -- (RACChannelTerminal *)rac_newValueChannelWithNilValue:(NSNumber *)nilValue { - return [self rac_channelForControlEvents:UIControlEventValueChanged key:@keypath(self.value) nilValue:nilValue]; -} - -@end diff --git a/ReactiveObjC/UISwitch+RACSignalSupport.h b/ReactiveObjC/UISwitch+RACSignalSupport.h deleted file mode 100644 index 53b7b6765f..0000000000 --- a/ReactiveObjC/UISwitch+RACSignalSupport.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// UISwitch+RACSignalSupport.h -// ReactiveObjC -// -// Created by Uri Baghin on 20/07/2013. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -@class RACChannelTerminal; - -@interface UISwitch (RACSignalSupport) - -/// Creates a new RACChannel-based binding to the receiver. -/// -/// Returns a RACChannelTerminal that sends whether the receiver is on whenever -/// the UIControlEventValueChanged control event is fired, and sets it on or off -/// when it receives @YES or @NO respectively. -- (RACChannelTerminal *)rac_newOnChannel; - -@end diff --git a/ReactiveObjC/UISwitch+RACSignalSupport.m b/ReactiveObjC/UISwitch+RACSignalSupport.m deleted file mode 100644 index a5f847d805..0000000000 --- a/ReactiveObjC/UISwitch+RACSignalSupport.m +++ /dev/null @@ -1,19 +0,0 @@ -// -// UISwitch+RACSignalSupport.m -// ReactiveObjC -// -// Created by Uri Baghin on 20/07/2013. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "UISwitch+RACSignalSupport.h" -#import -#import "UIControl+RACSignalSupportPrivate.h" - -@implementation UISwitch (RACSignalSupport) - -- (RACChannelTerminal *)rac_newOnChannel { - return [self rac_channelForControlEvents:UIControlEventValueChanged key:@keypath(self.on) nilValue:@NO]; -} - -@end diff --git a/ReactiveObjC/UITableViewCell+RACSignalSupport.h b/ReactiveObjC/UITableViewCell+RACSignalSupport.h deleted file mode 100644 index 144cbbaab0..0000000000 --- a/ReactiveObjC/UITableViewCell+RACSignalSupport.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// UITableViewCell+RACSignalSupport.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-07-22. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -@class RACSignal; - -@interface UITableViewCell (RACSignalSupport) - -/// A signal which will send a RACUnit whenever -prepareForReuse is invoked upon -/// the receiver. -/// -/// Examples -/// -/// [[[self.cancelButton -/// rac_signalForControlEvents:UIControlEventTouchUpInside] -/// takeUntil:self.rac_prepareForReuseSignal] -/// subscribeNext:^(UIButton *x) { -/// // do other things -/// }]; -@property (nonatomic, strong, readonly) RACSignal *rac_prepareForReuseSignal; - -@end diff --git a/ReactiveObjC/UITableViewCell+RACSignalSupport.m b/ReactiveObjC/UITableViewCell+RACSignalSupport.m deleted file mode 100644 index 5cf9dfa443..0000000000 --- a/ReactiveObjC/UITableViewCell+RACSignalSupport.m +++ /dev/null @@ -1,31 +0,0 @@ -// -// UITableViewCell+RACSignalSupport.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-07-22. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "UITableViewCell+RACSignalSupport.h" -#import "NSObject+RACDescription.h" -#import "NSObject+RACSelectorSignal.h" -#import "RACSignal+Operations.h" -#import "RACUnit.h" -#import - -@implementation UITableViewCell (RACSignalSupport) - -- (RACSignal *)rac_prepareForReuseSignal { - RACSignal *signal = objc_getAssociatedObject(self, _cmd); - if (signal != nil) return signal; - - signal = [[[self - rac_signalForSelector:@selector(prepareForReuse)] - mapReplace:RACUnit.defaultUnit] - setNameWithFormat:@"%@ -rac_prepareForReuseSignal", RACDescription(self)]; - - objc_setAssociatedObject(self, _cmd, signal, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - return signal; -} - -@end diff --git a/ReactiveObjC/UITableViewHeaderFooterView+RACSignalSupport.h b/ReactiveObjC/UITableViewHeaderFooterView+RACSignalSupport.h deleted file mode 100644 index db15b26f0e..0000000000 --- a/ReactiveObjC/UITableViewHeaderFooterView+RACSignalSupport.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// UITableViewHeaderFooterView+RACSignalSupport.h -// ReactiveObjC -// -// Created by Syo Ikeda on 12/30/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -@class RACSignal; - -// This category is only applicable to iOS >= 6.0. -@interface UITableViewHeaderFooterView (RACSignalSupport) - -/// A signal which will send a RACUnit whenever -prepareForReuse is invoked upon -/// the receiver. -/// -/// Examples -/// -/// [[[self.cancelButton -/// rac_signalForControlEvents:UIControlEventTouchUpInside] -/// takeUntil:self.rac_prepareForReuseSignal] -/// subscribeNext:^(UIButton *x) { -/// // do other things -/// }]; -@property (nonatomic, strong, readonly) RACSignal *rac_prepareForReuseSignal; - -@end diff --git a/ReactiveObjC/UITableViewHeaderFooterView+RACSignalSupport.m b/ReactiveObjC/UITableViewHeaderFooterView+RACSignalSupport.m deleted file mode 100644 index a0ffb436a4..0000000000 --- a/ReactiveObjC/UITableViewHeaderFooterView+RACSignalSupport.m +++ /dev/null @@ -1,31 +0,0 @@ -// -// UITableViewHeaderFooterView+RACSignalSupport.m -// ReactiveObjC -// -// Created by Syo Ikeda on 12/30/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "UITableViewHeaderFooterView+RACSignalSupport.h" -#import "NSObject+RACDescription.h" -#import "NSObject+RACSelectorSignal.h" -#import "RACSignal+Operations.h" -#import "RACUnit.h" -#import - -@implementation UITableViewHeaderFooterView (RACSignalSupport) - -- (RACSignal *)rac_prepareForReuseSignal { - RACSignal *signal = objc_getAssociatedObject(self, _cmd); - if (signal != nil) return signal; - - signal = [[[self - rac_signalForSelector:@selector(prepareForReuse)] - mapReplace:RACUnit.defaultUnit] - setNameWithFormat:@"%@ -rac_prepareForReuseSignal", RACDescription(self)]; - - objc_setAssociatedObject(self, _cmd, signal, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - return signal; -} - -@end diff --git a/ReactiveObjC/UITextField+RACSignalSupport.h b/ReactiveObjC/UITextField+RACSignalSupport.h deleted file mode 100644 index dd7b50ea18..0000000000 --- a/ReactiveObjC/UITextField+RACSignalSupport.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// UITextField+RACSignalSupport.h -// ReactiveObjC -// -// Created by Josh Abernathy on 4/17/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import - -@class RACChannelTerminal; -@class RACSignal; - -@interface UITextField (RACSignalSupport) - -/// Creates and returns a signal for the text of the field. It always starts with -/// the current text. The signal sends next when the UIControlEventAllEditingEvents -/// control event is fired on the control. -- (RACSignal *)rac_textSignal; - -/// Creates a new RACChannel-based binding to the receiver. -/// -/// Returns a RACChannelTerminal that sends the receiver's text whenever the -/// UIControlEventAllEditingEvents control event is fired, and sets the text -/// to the values it receives. -- (RACChannelTerminal *)rac_newTextChannel; - -@end diff --git a/ReactiveObjC/UITextField+RACSignalSupport.m b/ReactiveObjC/UITextField+RACSignalSupport.m deleted file mode 100644 index b02444858f..0000000000 --- a/ReactiveObjC/UITextField+RACSignalSupport.m +++ /dev/null @@ -1,39 +0,0 @@ -// -// UITextField+RACSignalSupport.m -// ReactiveObjC -// -// Created by Josh Abernathy on 4/17/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "UITextField+RACSignalSupport.h" -#import -#import -#import "NSObject+RACDeallocating.h" -#import "NSObject+RACDescription.h" -#import "RACSignal+Operations.h" -#import "UIControl+RACSignalSupport.h" -#import "UIControl+RACSignalSupportPrivate.h" - -@implementation UITextField (RACSignalSupport) - -- (RACSignal *)rac_textSignal { - @weakify(self); - return [[[[[RACSignal - defer:^{ - @strongify(self); - return [RACSignal return:self]; - }] - concat:[self rac_signalForControlEvents:UIControlEventAllEditingEvents]] - map:^(UITextField *x) { - return x.text; - }] - takeUntil:self.rac_willDeallocSignal] - setNameWithFormat:@"%@ -rac_textSignal", RACDescription(self)]; -} - -- (RACChannelTerminal *)rac_newTextChannel { - return [self rac_channelForControlEvents:UIControlEventAllEditingEvents key:@keypath(self.text) nilValue:@""]; -} - -@end diff --git a/ReactiveObjC/UITextView+RACSignalSupport.h b/ReactiveObjC/UITextView+RACSignalSupport.h deleted file mode 100644 index c2f7e8230b..0000000000 --- a/ReactiveObjC/UITextView+RACSignalSupport.h +++ /dev/null @@ -1,39 +0,0 @@ -// -// UITextView+RACSignalSupport.h -// ReactiveObjC -// -// Created by Cody Krieger on 5/18/12. -// Copyright (c) 2012 Cody Krieger. All rights reserved. -// - -#import - -@class RACDelegateProxy; -@class RACSignal; - -@interface UITextView (RACSignalSupport) - -/// A delegate proxy which will be set as the receiver's delegate when any of the -/// methods in this category are used. -@property (nonatomic, strong, readonly) RACDelegateProxy *rac_delegateProxy; - -/// Creates a signal for the text of the receiver. -/// -/// When this method is invoked, the `rac_delegateProxy` will become the -/// receiver's delegate. Any previous delegate will become the -[RACDelegateProxy -/// rac_proxiedDelegate], so that it receives any messages that the proxy doesn't -/// know how to handle. Setting the receiver's `delegate` afterward is -/// considered undefined behavior. -/// -/// Returns a signal which will send the current text upon subscription, then -/// again whenever the receiver's text is changed. The signal will complete when -/// the receiver is deallocated. -- (RACSignal *)rac_textSignal; - -@end - -@interface UITextView (RACSignalSupportUnavailable) - -- (RACSignal *)rac_signalForDelegateMethod:(SEL)method __attribute__((unavailable("Use -rac_signalForSelector:fromProtocol: instead"))); - -@end diff --git a/ReactiveObjC/UITextView+RACSignalSupport.m b/ReactiveObjC/UITextView+RACSignalSupport.m deleted file mode 100644 index d47f6e59c6..0000000000 --- a/ReactiveObjC/UITextView+RACSignalSupport.m +++ /dev/null @@ -1,56 +0,0 @@ -// -// UITextView+RACSignalSupport.m -// ReactiveObjC -// -// Created by Cody Krieger on 5/18/12. -// Copyright (c) 2012 Cody Krieger. All rights reserved. -// - -#import "UITextView+RACSignalSupport.h" -#import -#import "NSObject+RACDeallocating.h" -#import "NSObject+RACDescription.h" -#import "RACDelegateProxy.h" -#import "RACSignal+Operations.h" -#import "RACTuple.h" -#import - -@implementation UITextView (RACSignalSupport) - -static void RACUseDelegateProxy(UITextView *self) { - if (self.delegate == self.rac_delegateProxy) return; - - self.rac_delegateProxy.rac_proxiedDelegate = self.delegate; - self.delegate = (id)self.rac_delegateProxy; -} - -- (RACDelegateProxy *)rac_delegateProxy { - RACDelegateProxy *proxy = objc_getAssociatedObject(self, _cmd); - if (proxy == nil) { - proxy = [[RACDelegateProxy alloc] initWithProtocol:@protocol(UITextViewDelegate)]; - objc_setAssociatedObject(self, _cmd, proxy, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - } - - return proxy; -} - -- (RACSignal *)rac_textSignal { - @weakify(self); - RACSignal *signal = [[[[[RACSignal - defer:^{ - @strongify(self); - return [RACSignal return:RACTuplePack(self)]; - }] - concat:[self.rac_delegateProxy signalForSelector:@selector(textViewDidChange:)]] - reduceEach:^(UITextView *x) { - return x.text; - }] - takeUntil:self.rac_willDeallocSignal] - setNameWithFormat:@"%@ -rac_textSignal", RACDescription(self)]; - - RACUseDelegateProxy(self); - - return signal; -} - -@end diff --git a/ReactiveObjC/extobjc/EXTKeyPathCoding.h b/ReactiveObjC/extobjc/EXTKeyPathCoding.h deleted file mode 100644 index f34dc4a440..0000000000 --- a/ReactiveObjC/extobjc/EXTKeyPathCoding.h +++ /dev/null @@ -1,68 +0,0 @@ -// -// EXTKeyPathCoding.h -// extobjc -// -// Created by Justin Spahr-Summers on 19.06.12. -// Copyright (C) 2012 Justin Spahr-Summers. -// Released under the MIT license. -// - -#import -#import "metamacros.h" - -/** - * \@keypath allows compile-time verification of key paths. Given a real object - * receiver and key path: - * - * @code - -NSString *UTF8StringPath = @keypath(str.lowercaseString.UTF8String); -// => @"lowercaseString.UTF8String" - -NSString *versionPath = @keypath(NSObject, version); -// => @"version" - -NSString *lowercaseStringPath = @keypath(NSString.new, lowercaseString); -// => @"lowercaseString" - - * @endcode - * - * ... the macro returns an \c NSString containing all but the first path - * component or argument (e.g., @"lowercaseString.UTF8String", @"version"). - * - * In addition to simply creating a key path, this macro ensures that the key - * path is valid at compile-time (causing a syntax error if not), and supports - * refactoring, such that changing the name of the property will also update any - * uses of \@keypath. - */ -#define keypath(...) \ - metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__))(keypath1(__VA_ARGS__))(keypath2(__VA_ARGS__)) - -#define keypath1(PATH) \ - (((void)(NO && ((void)PATH, NO)), strchr(# PATH, '.') + 1)) - -#define keypath2(OBJ, PATH) \ - (((void)(NO && ((void)OBJ.PATH, NO)), # PATH)) - -/** - * \@collectionKeypath allows compile-time verification of key paths across collections NSArray/NSSet etc. Given a real object - * receiver, collection object receiver and related keypaths: - * - * @code - - NSString *employessFirstNamePath = @collectionKeypath(department.employees, Employee.new, firstName) - // => @"employees.firstName" - - NSString *employessFirstNamePath = @collectionKeypath(Department.new, employees, Employee.new, firstName) - // => @"employees.firstName" - - * @endcode - * - */ -#define collectionKeypath(...) \ - metamacro_if_eq(3, metamacro_argcount(__VA_ARGS__))(collectionKeypath3(__VA_ARGS__))(collectionKeypath4(__VA_ARGS__)) - -#define collectionKeypath3(PATH, COLLECTION_OBJECT, COLLECTION_PATH) ([[NSString stringWithFormat:@"%s.%s",keypath(PATH), keypath(COLLECTION_OBJECT, COLLECTION_PATH)] UTF8String]) - -#define collectionKeypath4(OBJ, PATH, COLLECTION_OBJECT, COLLECTION_PATH) ([[NSString stringWithFormat:@"%s.%s",keypath(OBJ, PATH), keypath(COLLECTION_OBJECT, COLLECTION_PATH)] UTF8String]) - diff --git a/ReactiveObjC/extobjc/EXTRuntimeExtensions.h b/ReactiveObjC/extobjc/EXTRuntimeExtensions.h deleted file mode 100644 index ab4e11d0c7..0000000000 --- a/ReactiveObjC/extobjc/EXTRuntimeExtensions.h +++ /dev/null @@ -1,122 +0,0 @@ -// -// EXTRuntimeExtensions.h -// extobjc -// -// Created by Justin Spahr-Summers on 2011-03-05. -// Copyright (C) 2012 Justin Spahr-Summers. -// Released under the MIT license. -// - -#import - -/** - * Describes the memory management policy of a property. - */ -typedef enum { - /** - * The value is assigned. - */ - rac_propertyMemoryManagementPolicyAssign = 0, - - /** - * The value is retained. - */ - rac_propertyMemoryManagementPolicyRetain, - - /** - * The value is copied. - */ - rac_propertyMemoryManagementPolicyCopy -} rac_propertyMemoryManagementPolicy; - -/** - * Describes the attributes and type information of a property. - */ -typedef struct { - /** - * Whether this property was declared with the \c readonly attribute. - */ - BOOL readonly; - - /** - * Whether this property was declared with the \c nonatomic attribute. - */ - BOOL nonatomic; - - /** - * Whether the property is a weak reference. - */ - BOOL weak; - - /** - * Whether the property is eligible for garbage collection. - */ - BOOL canBeCollected; - - /** - * Whether this property is defined with \c \@dynamic. - */ - BOOL dynamic; - - /** - * The memory management policy for this property. This will always be - * #rac_propertyMemoryManagementPolicyAssign if #readonly is \c YES. - */ - rac_propertyMemoryManagementPolicy memoryManagementPolicy; - - /** - * The selector for the getter of this property. This will reflect any - * custom \c getter= attribute provided in the property declaration, or the - * inferred getter name otherwise. - */ - SEL getter; - - /** - * The selector for the setter of this property. This will reflect any - * custom \c setter= attribute provided in the property declaration, or the - * inferred setter name otherwise. - * - * @note If #readonly is \c YES, this value will represent what the setter - * \e would be, if the property were writable. - */ - SEL setter; - - /** - * The backing instance variable for this property, or \c NULL if \c - * \c @synthesize was not used, and therefore no instance variable exists. This - * would also be the case if the property is implemented dynamically. - */ - const char *ivar; - - /** - * If this property is defined as being an instance of a specific class, - * this will be the class object representing it. - * - * This will be \c nil if the property was defined as type \c id, if the - * property is not of an object type, or if the class could not be found at - * runtime. - */ - Class objectClass; - - /** - * The type encoding for the value of this property. This is the type as it - * would be returned by the \c \@encode() directive. - */ - char type[]; -} rac_propertyAttributes; - -/** - * Finds the instance method named \a aSelector on \a aClass and returns it, or - * returns \c NULL if no such instance method exists. Unlike \c - * class_getInstanceMethod(), this does not search superclasses. - * - * @note To get class methods in this manner, use a metaclass for \a aClass. - */ -Method rac_getImmediateInstanceMethod (Class aClass, SEL aSelector); - -/** - * Returns a pointer to a structure containing information about \a property. - * You must \c free() the returned pointer. Returns \c NULL if there is an error - * obtaining information from \a property. - */ -rac_propertyAttributes *rac_copyPropertyAttributes (objc_property_t property); diff --git a/ReactiveObjC/extobjc/EXTRuntimeExtensions.m b/ReactiveObjC/extobjc/EXTRuntimeExtensions.m deleted file mode 100644 index 0e1a3f937e..0000000000 --- a/ReactiveObjC/extobjc/EXTRuntimeExtensions.m +++ /dev/null @@ -1,234 +0,0 @@ -// -// EXTRuntimeExtensions.m -// extobjc -// -// Created by Justin Spahr-Summers on 2011-03-05. -// Copyright (C) 2012 Justin Spahr-Summers. -// Released under the MIT license. -// - -#import - -#import -#import -#import -#import -#import -#import -#import -#import - -rac_propertyAttributes *rac_copyPropertyAttributes (objc_property_t property) { - const char * const attrString = property_getAttributes(property); - if (!attrString) { - fprintf(stderr, "ERROR: Could not get attribute string from property %s\n", property_getName(property)); - return NULL; - } - - if (attrString[0] != 'T') { - fprintf(stderr, "ERROR: Expected attribute string \"%s\" for property %s to start with 'T'\n", attrString, property_getName(property)); - return NULL; - } - - const char *typeString = attrString + 1; - const char *next = NSGetSizeAndAlignment(typeString, NULL, NULL); - if (!next) { - fprintf(stderr, "ERROR: Could not read past type in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); - return NULL; - } - - size_t typeLength = (size_t)(next - typeString); - if (!typeLength) { - fprintf(stderr, "ERROR: Invalid type in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); - return NULL; - } - - // allocate enough space for the structure and the type string (plus a NUL) - rac_propertyAttributes *attributes = calloc(1, sizeof(rac_propertyAttributes) + typeLength + 1); - if (!attributes) { - fprintf(stderr, "ERROR: Could not allocate rac_propertyAttributes structure for attribute string \"%s\" for property %s\n", attrString, property_getName(property)); - return NULL; - } - - // copy the type string - strncpy(attributes->type, typeString, typeLength); - attributes->type[typeLength] = '\0'; - - // if this is an object type, and immediately followed by a quoted string... - if (typeString[0] == *(@encode(id)) && typeString[1] == '"') { - // we should be able to extract a class name - const char *className = typeString + 2; - next = strchr(className, '"'); - - if (!next) { - fprintf(stderr, "ERROR: Could not read class name in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); - return NULL; - } - - if (className != next) { - size_t classNameLength = (size_t)(next - className); - char trimmedName[classNameLength + 1]; - - strncpy(trimmedName, className, classNameLength); - trimmedName[classNameLength] = '\0'; - - // attempt to look up the class in the runtime - attributes->objectClass = objc_getClass(trimmedName); - } - } - - if (*next != '\0') { - // skip past any junk before the first flag - next = strchr(next, ','); - } - - while (next && *next == ',') { - char flag = next[1]; - next += 2; - - switch (flag) { - case '\0': - break; - - case 'R': - attributes->readonly = YES; - break; - - case 'C': - attributes->memoryManagementPolicy = rac_propertyMemoryManagementPolicyCopy; - break; - - case '&': - attributes->memoryManagementPolicy = rac_propertyMemoryManagementPolicyRetain; - break; - - case 'N': - attributes->nonatomic = YES; - break; - - case 'G': - case 'S': - { - const char *nextFlag = strchr(next, ','); - SEL name = NULL; - - if (!nextFlag) { - // assume that the rest of the string is the selector - const char *selectorString = next; - next = ""; - - name = sel_registerName(selectorString); - } else { - size_t selectorLength = (size_t)(nextFlag - next); - if (!selectorLength) { - fprintf(stderr, "ERROR: Found zero length selector name in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); - goto errorOut; - } - - char selectorString[selectorLength + 1]; - - strncpy(selectorString, next, selectorLength); - selectorString[selectorLength] = '\0'; - - name = sel_registerName(selectorString); - next = nextFlag; - } - - if (flag == 'G') - attributes->getter = name; - else - attributes->setter = name; - } - - break; - - case 'D': - attributes->dynamic = YES; - attributes->ivar = NULL; - break; - - case 'V': - // assume that the rest of the string (if present) is the ivar name - if (*next == '\0') { - // if there's nothing there, let's assume this is dynamic - attributes->ivar = NULL; - } else { - attributes->ivar = next; - next = ""; - } - - break; - - case 'W': - attributes->weak = YES; - break; - - case 'P': - attributes->canBeCollected = YES; - break; - - case 't': - fprintf(stderr, "ERROR: Old-style type encoding is unsupported in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); - - // skip over this type encoding - while (*next != ',' && *next != '\0') - ++next; - - break; - - default: - fprintf(stderr, "ERROR: Unrecognized attribute string flag '%c' in attribute string \"%s\" for property %s\n", flag, attrString, property_getName(property)); - } - } - - if (next && *next != '\0') { - fprintf(stderr, "Warning: Unparsed data \"%s\" in attribute string \"%s\" for property %s\n", next, attrString, property_getName(property)); - } - - if (!attributes->getter) { - // use the property name as the getter by default - attributes->getter = sel_registerName(property_getName(property)); - } - - if (!attributes->setter) { - const char *propertyName = property_getName(property); - size_t propertyNameLength = strlen(propertyName); - - // we want to transform the name to setProperty: style - size_t setterLength = propertyNameLength + 4; - - char setterName[setterLength + 1]; - strncpy(setterName, "set", 3); - strncpy(setterName + 3, propertyName, propertyNameLength); - - // capitalize property name for the setter - setterName[3] = (char)toupper(setterName[3]); - - setterName[setterLength - 1] = ':'; - setterName[setterLength] = '\0'; - - attributes->setter = sel_registerName(setterName); - } - - return attributes; - -errorOut: - free(attributes); - return NULL; -} - -Method rac_getImmediateInstanceMethod (Class aClass, SEL aSelector) { - unsigned methodCount = 0; - Method *methods = class_copyMethodList(aClass, &methodCount); - Method foundMethod = NULL; - - for (unsigned methodIndex = 0;methodIndex < methodCount;++methodIndex) { - if (method_getName(methods[methodIndex]) == aSelector) { - foundMethod = methods[methodIndex]; - break; - } - } - - free(methods); - return foundMethod; -} diff --git a/ReactiveObjC/extobjc/EXTScope.h b/ReactiveObjC/extobjc/EXTScope.h deleted file mode 100644 index 4a16f4ae9b..0000000000 --- a/ReactiveObjC/extobjc/EXTScope.h +++ /dev/null @@ -1,118 +0,0 @@ -// -// EXTScope.h -// extobjc -// -// Created by Justin Spahr-Summers on 2011-05-04. -// Copyright (C) 2012 Justin Spahr-Summers. -// Released under the MIT license. -// - -#import "metamacros.h" - -/** - * \@onExit defines some code to be executed when the current scope exits. The - * code must be enclosed in braces and terminated with a semicolon, and will be - * executed regardless of how the scope is exited, including from exceptions, - * \c goto, \c return, \c break, and \c continue. - * - * Provided code will go into a block to be executed later. Keep this in mind as - * it pertains to memory management, restrictions on assignment, etc. Because - * the code is used within a block, \c return is a legal (though perhaps - * confusing) way to exit the cleanup block early. - * - * Multiple \@onExit statements in the same scope are executed in reverse - * lexical order. This helps when pairing resource acquisition with \@onExit - * statements, as it guarantees teardown in the opposite order of acquisition. - * - * @note This statement cannot be used within scopes defined without braces - * (like a one line \c if). In practice, this is not an issue, since \@onExit is - * a useless construct in such a case anyways. - */ -#define onExit \ - rac_keywordify \ - __strong rac_cleanupBlock_t metamacro_concat(rac_exitBlock_, __LINE__) __attribute__((cleanup(rac_executeCleanupBlock), unused)) = ^ - -/** - * Creates \c __weak shadow variables for each of the variables provided as - * arguments, which can later be made strong again with #strongify. - * - * This is typically used to weakly reference variables in a block, but then - * ensure that the variables stay alive during the actual execution of the block - * (if they were live upon entry). - * - * See #strongify for an example of usage. - */ -#define weakify(...) \ - rac_keywordify \ - metamacro_foreach_cxt(rac_weakify_,, __weak, __VA_ARGS__) - -/** - * Like #weakify, but uses \c __unsafe_unretained instead, for targets or - * classes that do not support weak references. - */ -#define unsafeify(...) \ - rac_keywordify \ - metamacro_foreach_cxt(rac_weakify_,, __unsafe_unretained, __VA_ARGS__) - -/** - * Strongly references each of the variables provided as arguments, which must - * have previously been passed to #weakify. - * - * The strong references created will shadow the original variable names, such - * that the original names can be used without issue (and a significantly - * reduced risk of retain cycles) in the current scope. - * - * @code - - id foo = [[NSObject alloc] init]; - id bar = [[NSObject alloc] init]; - - @weakify(foo, bar); - - // this block will not keep 'foo' or 'bar' alive - BOOL (^matchesFooOrBar)(id) = ^ BOOL (id obj){ - // but now, upon entry, 'foo' and 'bar' will stay alive until the block has - // finished executing - @strongify(foo, bar); - - return [foo isEqual:obj] || [bar isEqual:obj]; - }; - - * @endcode - */ -#define strongify(...) \ - rac_keywordify \ - _Pragma("clang diagnostic push") \ - _Pragma("clang diagnostic ignored \"-Wshadow\"") \ - metamacro_foreach(rac_strongify_,, __VA_ARGS__) \ - _Pragma("clang diagnostic pop") - -/*** implementation details follow ***/ -typedef void (^rac_cleanupBlock_t)(); - -static inline void rac_executeCleanupBlock (__strong rac_cleanupBlock_t *block) { - (*block)(); -} - -#define rac_weakify_(INDEX, CONTEXT, VAR) \ - CONTEXT __typeof__(VAR) metamacro_concat(VAR, _weak_) = (VAR); - -#define rac_strongify_(INDEX, VAR) \ - __strong __typeof__(VAR) VAR = metamacro_concat(VAR, _weak_); - -// Details about the choice of backing keyword: -// -// The use of @try/@catch/@finally can cause the compiler to suppress -// return-type warnings. -// The use of @autoreleasepool {} is not optimized away by the compiler, -// resulting in superfluous creation of autorelease pools. -// -// Since neither option is perfect, and with no other alternatives, the -// compromise is to use @autorelease in DEBUG builds to maintain compiler -// analysis, and to use @try/@catch otherwise to avoid insertion of unnecessary -// autorelease pools. -#if DEBUG -#define rac_keywordify autoreleasepool {} -#else -#define rac_keywordify try {} @catch (...) {} -#endif diff --git a/ReactiveObjC/extobjc/metamacros.h b/ReactiveObjC/extobjc/metamacros.h deleted file mode 100644 index 77a77b5f6e..0000000000 --- a/ReactiveObjC/extobjc/metamacros.h +++ /dev/null @@ -1,666 +0,0 @@ -/** - * Macros for metaprogramming - * ExtendedC - * - * Copyright (C) 2012 Justin Spahr-Summers - * Released under the MIT license - */ - -#ifndef EXTC_METAMACROS_H -#define EXTC_METAMACROS_H - -/** - * Executes one or more expressions (which may have a void type, such as a call - * to a function that returns no value) and always returns true. - */ -#define metamacro_exprify(...) \ - ((__VA_ARGS__), true) - -/** - * Returns a string representation of VALUE after full macro expansion. - */ -#define metamacro_stringify(VALUE) \ - metamacro_stringify_(VALUE) - -/** - * Returns A and B concatenated after full macro expansion. - */ -#define metamacro_concat(A, B) \ - metamacro_concat_(A, B) - -/** - * Returns the Nth variadic argument (starting from zero). At least - * N + 1 variadic arguments must be given. N must be between zero and twenty, - * inclusive. - */ -#define metamacro_at(N, ...) \ - metamacro_concat(metamacro_at, N)(__VA_ARGS__) - -/** - * Returns the number of arguments (up to twenty) provided to the macro. At - * least one argument must be provided. - * - * Inspired by P99: http://p99.gforge.inria.fr - */ -#define metamacro_argcount(...) \ - metamacro_at(20, __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1) - -/** - * Identical to #metamacro_foreach_cxt, except that no CONTEXT argument is - * given. Only the index and current argument will thus be passed to MACRO. - */ -#define metamacro_foreach(MACRO, SEP, ...) \ - metamacro_foreach_cxt(metamacro_foreach_iter, SEP, MACRO, __VA_ARGS__) - -/** - * For each consecutive variadic argument (up to twenty), MACRO is passed the - * zero-based index of the current argument, CONTEXT, and then the argument - * itself. The results of adjoining invocations of MACRO are then separated by - * SEP. - * - * Inspired by P99: http://p99.gforge.inria.fr - */ -#define metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...) \ - metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__) - -/** - * Identical to #metamacro_foreach_cxt. This can be used when the former would - * fail due to recursive macro expansion. - */ -#define metamacro_foreach_cxt_recursive(MACRO, SEP, CONTEXT, ...) \ - metamacro_concat(metamacro_foreach_cxt_recursive, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__) - -/** - * In consecutive order, appends each variadic argument (up to twenty) onto - * BASE. The resulting concatenations are then separated by SEP. - * - * This is primarily useful to manipulate a list of macro invocations into instead - * invoking a different, possibly related macro. - */ -#define metamacro_foreach_concat(BASE, SEP, ...) \ - metamacro_foreach_cxt(metamacro_foreach_concat_iter, SEP, BASE, __VA_ARGS__) - -/** - * Iterates COUNT times, each time invoking MACRO with the current index - * (starting at zero) and CONTEXT. The results of adjoining invocations of MACRO - * are then separated by SEP. - * - * COUNT must be an integer between zero and twenty, inclusive. - */ -#define metamacro_for_cxt(COUNT, MACRO, SEP, CONTEXT) \ - metamacro_concat(metamacro_for_cxt, COUNT)(MACRO, SEP, CONTEXT) - -/** - * Returns the first argument given. At least one argument must be provided. - * - * This is useful when implementing a variadic macro, where you may have only - * one variadic argument, but no way to retrieve it (for example, because \c ... - * always needs to match at least one argument). - * - * @code - -#define varmacro(...) \ - metamacro_head(__VA_ARGS__) - - * @endcode - */ -#define metamacro_head(...) \ - metamacro_head_(__VA_ARGS__, 0) - -/** - * Returns every argument except the first. At least two arguments must be - * provided. - */ -#define metamacro_tail(...) \ - metamacro_tail_(__VA_ARGS__) - -/** - * Returns the first N (up to twenty) variadic arguments as a new argument list. - * At least N variadic arguments must be provided. - */ -#define metamacro_take(N, ...) \ - metamacro_concat(metamacro_take, N)(__VA_ARGS__) - -/** - * Removes the first N (up to twenty) variadic arguments from the given argument - * list. At least N variadic arguments must be provided. - */ -#define metamacro_drop(N, ...) \ - metamacro_concat(metamacro_drop, N)(__VA_ARGS__) - -/** - * Decrements VAL, which must be a number between zero and twenty, inclusive. - * - * This is primarily useful when dealing with indexes and counts in - * metaprogramming. - */ -#define metamacro_dec(VAL) \ - metamacro_at(VAL, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19) - -/** - * Increments VAL, which must be a number between zero and twenty, inclusive. - * - * This is primarily useful when dealing with indexes and counts in - * metaprogramming. - */ -#define metamacro_inc(VAL) \ - metamacro_at(VAL, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21) - -/** - * If A is equal to B, the next argument list is expanded; otherwise, the - * argument list after that is expanded. A and B must be numbers between zero - * and twenty, inclusive. Additionally, B must be greater than or equal to A. - * - * @code - -// expands to true -metamacro_if_eq(0, 0)(true)(false) - -// expands to false -metamacro_if_eq(0, 1)(true)(false) - - * @endcode - * - * This is primarily useful when dealing with indexes and counts in - * metaprogramming. - */ -#define metamacro_if_eq(A, B) \ - metamacro_concat(metamacro_if_eq, A)(B) - -/** - * Identical to #metamacro_if_eq. This can be used when the former would fail - * due to recursive macro expansion. - */ -#define metamacro_if_eq_recursive(A, B) \ - metamacro_concat(metamacro_if_eq_recursive, A)(B) - -/** - * Returns 1 if N is an even number, or 0 otherwise. N must be between zero and - * twenty, inclusive. - * - * For the purposes of this test, zero is considered even. - */ -#define metamacro_is_even(N) \ - metamacro_at(N, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1) - -/** - * Returns the logical NOT of B, which must be the number zero or one. - */ -#define metamacro_not(B) \ - metamacro_at(B, 1, 0) - -// IMPLEMENTATION DETAILS FOLLOW! -// Do not write code that depends on anything below this line. -#define metamacro_stringify_(VALUE) # VALUE -#define metamacro_concat_(A, B) A ## B -#define metamacro_foreach_iter(INDEX, MACRO, ARG) MACRO(INDEX, ARG) -#define metamacro_head_(FIRST, ...) FIRST -#define metamacro_tail_(FIRST, ...) __VA_ARGS__ -#define metamacro_consume_(...) -#define metamacro_expand_(...) __VA_ARGS__ - -// implemented from scratch so that metamacro_concat() doesn't end up nesting -#define metamacro_foreach_concat_iter(INDEX, BASE, ARG) metamacro_foreach_concat_iter_(BASE, ARG) -#define metamacro_foreach_concat_iter_(BASE, ARG) BASE ## ARG - -// metamacro_at expansions -#define metamacro_at0(...) metamacro_head(__VA_ARGS__) -#define metamacro_at1(_0, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at2(_0, _1, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at3(_0, _1, _2, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at4(_0, _1, _2, _3, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at5(_0, _1, _2, _3, _4, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at6(_0, _1, _2, _3, _4, _5, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at7(_0, _1, _2, _3, _4, _5, _6, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at8(_0, _1, _2, _3, _4, _5, _6, _7, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at9(_0, _1, _2, _3, _4, _5, _6, _7, _8, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at10(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at11(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at12(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at13(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at14(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at15(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at17(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at18(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at19(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, ...) metamacro_head(__VA_ARGS__) -#define metamacro_at20(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, ...) metamacro_head(__VA_ARGS__) - -// metamacro_foreach_cxt expansions -#define metamacro_foreach_cxt0(MACRO, SEP, CONTEXT) -#define metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0) - -#define metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \ - metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) \ - SEP \ - MACRO(1, CONTEXT, _1) - -#define metamacro_foreach_cxt3(MACRO, SEP, CONTEXT, _0, _1, _2) \ - metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \ - SEP \ - MACRO(2, CONTEXT, _2) - -#define metamacro_foreach_cxt4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \ - metamacro_foreach_cxt3(MACRO, SEP, CONTEXT, _0, _1, _2) \ - SEP \ - MACRO(3, CONTEXT, _3) - -#define metamacro_foreach_cxt5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \ - metamacro_foreach_cxt4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \ - SEP \ - MACRO(4, CONTEXT, _4) - -#define metamacro_foreach_cxt6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \ - metamacro_foreach_cxt5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \ - SEP \ - MACRO(5, CONTEXT, _5) - -#define metamacro_foreach_cxt7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \ - metamacro_foreach_cxt6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \ - SEP \ - MACRO(6, CONTEXT, _6) - -#define metamacro_foreach_cxt8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \ - metamacro_foreach_cxt7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \ - SEP \ - MACRO(7, CONTEXT, _7) - -#define metamacro_foreach_cxt9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \ - metamacro_foreach_cxt8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \ - SEP \ - MACRO(8, CONTEXT, _8) - -#define metamacro_foreach_cxt10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ - metamacro_foreach_cxt9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \ - SEP \ - MACRO(9, CONTEXT, _9) - -#define metamacro_foreach_cxt11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ - metamacro_foreach_cxt10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ - SEP \ - MACRO(10, CONTEXT, _10) - -#define metamacro_foreach_cxt12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ - metamacro_foreach_cxt11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ - SEP \ - MACRO(11, CONTEXT, _11) - -#define metamacro_foreach_cxt13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ - metamacro_foreach_cxt12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ - SEP \ - MACRO(12, CONTEXT, _12) - -#define metamacro_foreach_cxt14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ - metamacro_foreach_cxt13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ - SEP \ - MACRO(13, CONTEXT, _13) - -#define metamacro_foreach_cxt15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ - metamacro_foreach_cxt14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ - SEP \ - MACRO(14, CONTEXT, _14) - -#define metamacro_foreach_cxt16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ - metamacro_foreach_cxt15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ - SEP \ - MACRO(15, CONTEXT, _15) - -#define metamacro_foreach_cxt17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ - metamacro_foreach_cxt16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ - SEP \ - MACRO(16, CONTEXT, _16) - -#define metamacro_foreach_cxt18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \ - metamacro_foreach_cxt17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ - SEP \ - MACRO(17, CONTEXT, _17) - -#define metamacro_foreach_cxt19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \ - metamacro_foreach_cxt18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \ - SEP \ - MACRO(18, CONTEXT, _18) - -#define metamacro_foreach_cxt20(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) \ - metamacro_foreach_cxt19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \ - SEP \ - MACRO(19, CONTEXT, _19) - -// metamacro_foreach_cxt_recursive expansions -#define metamacro_foreach_cxt_recursive0(MACRO, SEP, CONTEXT) -#define metamacro_foreach_cxt_recursive1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0) - -#define metamacro_foreach_cxt_recursive2(MACRO, SEP, CONTEXT, _0, _1) \ - metamacro_foreach_cxt_recursive1(MACRO, SEP, CONTEXT, _0) \ - SEP \ - MACRO(1, CONTEXT, _1) - -#define metamacro_foreach_cxt_recursive3(MACRO, SEP, CONTEXT, _0, _1, _2) \ - metamacro_foreach_cxt_recursive2(MACRO, SEP, CONTEXT, _0, _1) \ - SEP \ - MACRO(2, CONTEXT, _2) - -#define metamacro_foreach_cxt_recursive4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \ - metamacro_foreach_cxt_recursive3(MACRO, SEP, CONTEXT, _0, _1, _2) \ - SEP \ - MACRO(3, CONTEXT, _3) - -#define metamacro_foreach_cxt_recursive5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \ - metamacro_foreach_cxt_recursive4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \ - SEP \ - MACRO(4, CONTEXT, _4) - -#define metamacro_foreach_cxt_recursive6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \ - metamacro_foreach_cxt_recursive5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \ - SEP \ - MACRO(5, CONTEXT, _5) - -#define metamacro_foreach_cxt_recursive7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \ - metamacro_foreach_cxt_recursive6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \ - SEP \ - MACRO(6, CONTEXT, _6) - -#define metamacro_foreach_cxt_recursive8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \ - metamacro_foreach_cxt_recursive7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \ - SEP \ - MACRO(7, CONTEXT, _7) - -#define metamacro_foreach_cxt_recursive9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \ - metamacro_foreach_cxt_recursive8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \ - SEP \ - MACRO(8, CONTEXT, _8) - -#define metamacro_foreach_cxt_recursive10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ - metamacro_foreach_cxt_recursive9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \ - SEP \ - MACRO(9, CONTEXT, _9) - -#define metamacro_foreach_cxt_recursive11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ - metamacro_foreach_cxt_recursive10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ - SEP \ - MACRO(10, CONTEXT, _10) - -#define metamacro_foreach_cxt_recursive12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ - metamacro_foreach_cxt_recursive11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ - SEP \ - MACRO(11, CONTEXT, _11) - -#define metamacro_foreach_cxt_recursive13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ - metamacro_foreach_cxt_recursive12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ - SEP \ - MACRO(12, CONTEXT, _12) - -#define metamacro_foreach_cxt_recursive14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ - metamacro_foreach_cxt_recursive13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ - SEP \ - MACRO(13, CONTEXT, _13) - -#define metamacro_foreach_cxt_recursive15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ - metamacro_foreach_cxt_recursive14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ - SEP \ - MACRO(14, CONTEXT, _14) - -#define metamacro_foreach_cxt_recursive16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ - metamacro_foreach_cxt_recursive15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ - SEP \ - MACRO(15, CONTEXT, _15) - -#define metamacro_foreach_cxt_recursive17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ - metamacro_foreach_cxt_recursive16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ - SEP \ - MACRO(16, CONTEXT, _16) - -#define metamacro_foreach_cxt_recursive18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \ - metamacro_foreach_cxt_recursive17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ - SEP \ - MACRO(17, CONTEXT, _17) - -#define metamacro_foreach_cxt_recursive19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \ - metamacro_foreach_cxt_recursive18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \ - SEP \ - MACRO(18, CONTEXT, _18) - -#define metamacro_foreach_cxt_recursive20(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) \ - metamacro_foreach_cxt_recursive19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \ - SEP \ - MACRO(19, CONTEXT, _19) - -// metamacro_for_cxt expansions -#define metamacro_for_cxt0(MACRO, SEP, CONTEXT) -#define metamacro_for_cxt1(MACRO, SEP, CONTEXT) MACRO(0, CONTEXT) - -#define metamacro_for_cxt2(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt1(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(1, CONTEXT) - -#define metamacro_for_cxt3(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt2(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(2, CONTEXT) - -#define metamacro_for_cxt4(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt3(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(3, CONTEXT) - -#define metamacro_for_cxt5(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt4(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(4, CONTEXT) - -#define metamacro_for_cxt6(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt5(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(5, CONTEXT) - -#define metamacro_for_cxt7(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt6(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(6, CONTEXT) - -#define metamacro_for_cxt8(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt7(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(7, CONTEXT) - -#define metamacro_for_cxt9(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt8(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(8, CONTEXT) - -#define metamacro_for_cxt10(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt9(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(9, CONTEXT) - -#define metamacro_for_cxt11(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt10(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(10, CONTEXT) - -#define metamacro_for_cxt12(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt11(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(11, CONTEXT) - -#define metamacro_for_cxt13(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt12(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(12, CONTEXT) - -#define metamacro_for_cxt14(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt13(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(13, CONTEXT) - -#define metamacro_for_cxt15(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt14(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(14, CONTEXT) - -#define metamacro_for_cxt16(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt15(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(15, CONTEXT) - -#define metamacro_for_cxt17(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt16(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(16, CONTEXT) - -#define metamacro_for_cxt18(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt17(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(17, CONTEXT) - -#define metamacro_for_cxt19(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt18(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(18, CONTEXT) - -#define metamacro_for_cxt20(MACRO, SEP, CONTEXT) \ - metamacro_for_cxt19(MACRO, SEP, CONTEXT) \ - SEP \ - MACRO(19, CONTEXT) - -// metamacro_if_eq expansions -#define metamacro_if_eq0(VALUE) \ - metamacro_concat(metamacro_if_eq0_, VALUE) - -#define metamacro_if_eq0_0(...) __VA_ARGS__ metamacro_consume_ -#define metamacro_if_eq0_1(...) metamacro_expand_ -#define metamacro_if_eq0_2(...) metamacro_expand_ -#define metamacro_if_eq0_3(...) metamacro_expand_ -#define metamacro_if_eq0_4(...) metamacro_expand_ -#define metamacro_if_eq0_5(...) metamacro_expand_ -#define metamacro_if_eq0_6(...) metamacro_expand_ -#define metamacro_if_eq0_7(...) metamacro_expand_ -#define metamacro_if_eq0_8(...) metamacro_expand_ -#define metamacro_if_eq0_9(...) metamacro_expand_ -#define metamacro_if_eq0_10(...) metamacro_expand_ -#define metamacro_if_eq0_11(...) metamacro_expand_ -#define metamacro_if_eq0_12(...) metamacro_expand_ -#define metamacro_if_eq0_13(...) metamacro_expand_ -#define metamacro_if_eq0_14(...) metamacro_expand_ -#define metamacro_if_eq0_15(...) metamacro_expand_ -#define metamacro_if_eq0_16(...) metamacro_expand_ -#define metamacro_if_eq0_17(...) metamacro_expand_ -#define metamacro_if_eq0_18(...) metamacro_expand_ -#define metamacro_if_eq0_19(...) metamacro_expand_ -#define metamacro_if_eq0_20(...) metamacro_expand_ - -#define metamacro_if_eq1(VALUE) metamacro_if_eq0(metamacro_dec(VALUE)) -#define metamacro_if_eq2(VALUE) metamacro_if_eq1(metamacro_dec(VALUE)) -#define metamacro_if_eq3(VALUE) metamacro_if_eq2(metamacro_dec(VALUE)) -#define metamacro_if_eq4(VALUE) metamacro_if_eq3(metamacro_dec(VALUE)) -#define metamacro_if_eq5(VALUE) metamacro_if_eq4(metamacro_dec(VALUE)) -#define metamacro_if_eq6(VALUE) metamacro_if_eq5(metamacro_dec(VALUE)) -#define metamacro_if_eq7(VALUE) metamacro_if_eq6(metamacro_dec(VALUE)) -#define metamacro_if_eq8(VALUE) metamacro_if_eq7(metamacro_dec(VALUE)) -#define metamacro_if_eq9(VALUE) metamacro_if_eq8(metamacro_dec(VALUE)) -#define metamacro_if_eq10(VALUE) metamacro_if_eq9(metamacro_dec(VALUE)) -#define metamacro_if_eq11(VALUE) metamacro_if_eq10(metamacro_dec(VALUE)) -#define metamacro_if_eq12(VALUE) metamacro_if_eq11(metamacro_dec(VALUE)) -#define metamacro_if_eq13(VALUE) metamacro_if_eq12(metamacro_dec(VALUE)) -#define metamacro_if_eq14(VALUE) metamacro_if_eq13(metamacro_dec(VALUE)) -#define metamacro_if_eq15(VALUE) metamacro_if_eq14(metamacro_dec(VALUE)) -#define metamacro_if_eq16(VALUE) metamacro_if_eq15(metamacro_dec(VALUE)) -#define metamacro_if_eq17(VALUE) metamacro_if_eq16(metamacro_dec(VALUE)) -#define metamacro_if_eq18(VALUE) metamacro_if_eq17(metamacro_dec(VALUE)) -#define metamacro_if_eq19(VALUE) metamacro_if_eq18(metamacro_dec(VALUE)) -#define metamacro_if_eq20(VALUE) metamacro_if_eq19(metamacro_dec(VALUE)) - -// metamacro_if_eq_recursive expansions -#define metamacro_if_eq_recursive0(VALUE) \ - metamacro_concat(metamacro_if_eq_recursive0_, VALUE) - -#define metamacro_if_eq_recursive0_0(...) __VA_ARGS__ metamacro_consume_ -#define metamacro_if_eq_recursive0_1(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_2(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_3(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_4(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_5(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_6(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_7(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_8(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_9(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_10(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_11(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_12(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_13(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_14(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_15(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_16(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_17(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_18(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_19(...) metamacro_expand_ -#define metamacro_if_eq_recursive0_20(...) metamacro_expand_ - -#define metamacro_if_eq_recursive1(VALUE) metamacro_if_eq_recursive0(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive2(VALUE) metamacro_if_eq_recursive1(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive3(VALUE) metamacro_if_eq_recursive2(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive4(VALUE) metamacro_if_eq_recursive3(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive5(VALUE) metamacro_if_eq_recursive4(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive6(VALUE) metamacro_if_eq_recursive5(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive7(VALUE) metamacro_if_eq_recursive6(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive8(VALUE) metamacro_if_eq_recursive7(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive9(VALUE) metamacro_if_eq_recursive8(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive10(VALUE) metamacro_if_eq_recursive9(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive11(VALUE) metamacro_if_eq_recursive10(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive12(VALUE) metamacro_if_eq_recursive11(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive13(VALUE) metamacro_if_eq_recursive12(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive14(VALUE) metamacro_if_eq_recursive13(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive15(VALUE) metamacro_if_eq_recursive14(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive16(VALUE) metamacro_if_eq_recursive15(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive17(VALUE) metamacro_if_eq_recursive16(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive18(VALUE) metamacro_if_eq_recursive17(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive19(VALUE) metamacro_if_eq_recursive18(metamacro_dec(VALUE)) -#define metamacro_if_eq_recursive20(VALUE) metamacro_if_eq_recursive19(metamacro_dec(VALUE)) - -// metamacro_take expansions -#define metamacro_take0(...) -#define metamacro_take1(...) metamacro_head(__VA_ARGS__) -#define metamacro_take2(...) metamacro_head(__VA_ARGS__), metamacro_take1(metamacro_tail(__VA_ARGS__)) -#define metamacro_take3(...) metamacro_head(__VA_ARGS__), metamacro_take2(metamacro_tail(__VA_ARGS__)) -#define metamacro_take4(...) metamacro_head(__VA_ARGS__), metamacro_take3(metamacro_tail(__VA_ARGS__)) -#define metamacro_take5(...) metamacro_head(__VA_ARGS__), metamacro_take4(metamacro_tail(__VA_ARGS__)) -#define metamacro_take6(...) metamacro_head(__VA_ARGS__), metamacro_take5(metamacro_tail(__VA_ARGS__)) -#define metamacro_take7(...) metamacro_head(__VA_ARGS__), metamacro_take6(metamacro_tail(__VA_ARGS__)) -#define metamacro_take8(...) metamacro_head(__VA_ARGS__), metamacro_take7(metamacro_tail(__VA_ARGS__)) -#define metamacro_take9(...) metamacro_head(__VA_ARGS__), metamacro_take8(metamacro_tail(__VA_ARGS__)) -#define metamacro_take10(...) metamacro_head(__VA_ARGS__), metamacro_take9(metamacro_tail(__VA_ARGS__)) -#define metamacro_take11(...) metamacro_head(__VA_ARGS__), metamacro_take10(metamacro_tail(__VA_ARGS__)) -#define metamacro_take12(...) metamacro_head(__VA_ARGS__), metamacro_take11(metamacro_tail(__VA_ARGS__)) -#define metamacro_take13(...) metamacro_head(__VA_ARGS__), metamacro_take12(metamacro_tail(__VA_ARGS__)) -#define metamacro_take14(...) metamacro_head(__VA_ARGS__), metamacro_take13(metamacro_tail(__VA_ARGS__)) -#define metamacro_take15(...) metamacro_head(__VA_ARGS__), metamacro_take14(metamacro_tail(__VA_ARGS__)) -#define metamacro_take16(...) metamacro_head(__VA_ARGS__), metamacro_take15(metamacro_tail(__VA_ARGS__)) -#define metamacro_take17(...) metamacro_head(__VA_ARGS__), metamacro_take16(metamacro_tail(__VA_ARGS__)) -#define metamacro_take18(...) metamacro_head(__VA_ARGS__), metamacro_take17(metamacro_tail(__VA_ARGS__)) -#define metamacro_take19(...) metamacro_head(__VA_ARGS__), metamacro_take18(metamacro_tail(__VA_ARGS__)) -#define metamacro_take20(...) metamacro_head(__VA_ARGS__), metamacro_take19(metamacro_tail(__VA_ARGS__)) - -// metamacro_drop expansions -#define metamacro_drop0(...) __VA_ARGS__ -#define metamacro_drop1(...) metamacro_tail(__VA_ARGS__) -#define metamacro_drop2(...) metamacro_drop1(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop3(...) metamacro_drop2(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop4(...) metamacro_drop3(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop5(...) metamacro_drop4(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop6(...) metamacro_drop5(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop7(...) metamacro_drop6(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop8(...) metamacro_drop7(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop9(...) metamacro_drop8(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop10(...) metamacro_drop9(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop11(...) metamacro_drop10(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop12(...) metamacro_drop11(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop13(...) metamacro_drop12(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop14(...) metamacro_drop13(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop15(...) metamacro_drop14(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop16(...) metamacro_drop15(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop17(...) metamacro_drop16(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop18(...) metamacro_drop17(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop19(...) metamacro_drop18(metamacro_tail(__VA_ARGS__)) -#define metamacro_drop20(...) metamacro_drop19(metamacro_tail(__VA_ARGS__)) - -#endif diff --git a/ReactiveObjCBridge.xcodeproj/project.pbxproj b/ReactiveObjCBridge.xcodeproj/project.pbxproj deleted file mode 100644 index f5e5d60697..0000000000 --- a/ReactiveObjCBridge.xcodeproj/project.pbxproj +++ /dev/null @@ -1,1430 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 57A4D1B71BA13D7A00F7D4B1 /* ObjectiveCBridging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312C419EF2A5800984962 /* ObjectiveCBridging.swift */; }; - 57A4D2081BA13D7A00F7D4B1 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; - 57A4D20A1BA13D7A00F7D4B1 /* ReactiveObjCBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveObjCBridge.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 7DFBED081CDB8C9500EE435B /* ReactiveObjCBridge.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57A4D2411BA13D7A00F7D4B1 /* ReactiveObjCBridge.framework */; }; - 7DFBED1E1CDB8D7000EE435B /* ReactiveObjCBridge.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 57A4D2411BA13D7A00F7D4B1 /* ReactiveObjCBridge.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 7DFBED1F1CDB8D7800EE435B /* Quick.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 7DFBED201CDB8D7D00EE435B /* Nimble.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 7DFBED211CDB8D8300EE435B /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 7DFBED271CDB8DE300EE435B /* ObjectiveCBridgingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A226101A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift */; }; - A9B315BF1B3940810001CB9C /* ObjectiveCBridging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312C419EF2A5800984962 /* ObjectiveCBridging.swift */; }; - A9B315C91B3940980001CB9C /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; - A9B315CA1B3940AB0001CB9C /* ReactiveObjCBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveObjCBridge.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BE330A0F1D634F1E00806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A0E1D634F1E00806963 /* ReactiveSwift.framework */; }; - BE330A111D634F2900806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A101D634F2900806963 /* ReactiveSwift.framework */; }; - BE330A131D634F2E00806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A121D634F2E00806963 /* ReactiveSwift.framework */; }; - BE330A151D634F4000806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A141D634F4000806963 /* ReactiveSwift.framework */; }; - BE330A171D634F4E00806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A161D634F4E00806963 /* ReactiveSwift.framework */; }; - BE330A191D634F5900806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A181D634F5900806963 /* ReactiveSwift.framework */; }; - BE330A1B1D634F5F00806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A1A1D634F5F00806963 /* ReactiveSwift.framework */; }; - BEA5E5D91D864DC900A1F0F2 /* ReactiveObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BEA5E5D81D864DC900A1F0F2 /* ReactiveObjC.framework */; }; - BEA5E5DB1D864DD100A1F0F2 /* ReactiveObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BEA5E5DA1D864DD100A1F0F2 /* ReactiveObjC.framework */; }; - BEA5E5DD1D864DD800A1F0F2 /* ReactiveObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BEA5E5DC1D864DD800A1F0F2 /* ReactiveObjC.framework */; }; - BEA5E5DF1D864DDE00A1F0F2 /* ReactiveObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BEA5E5DE1D864DDE00A1F0F2 /* ReactiveObjC.framework */; }; - BEA5E5E11D864DE800A1F0F2 /* ReactiveObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BEA5E5E01D864DE800A1F0F2 /* ReactiveObjC.framework */; }; - BEA5E5E31D864DED00A1F0F2 /* ReactiveObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BEA5E5E21D864DED00A1F0F2 /* ReactiveObjC.framework */; }; - BEA5E5E51D864DF400A1F0F2 /* ReactiveObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BEA5E5E41D864DF400A1F0F2 /* ReactiveObjC.framework */; }; - BEA5E5E81D864E7600A1F0F2 /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEA5E5E61D864E7200A1F0F2 /* TestError.swift */; }; - BEA5E5E91D864E7700A1F0F2 /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEA5E5E61D864E7200A1F0F2 /* TestError.swift */; }; - BEA5E5EA1D864E7900A1F0F2 /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEA5E5E61D864E7200A1F0F2 /* TestError.swift */; }; - CDC42E2F1AE7AB8B00965373 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; - CDC42E301AE7AB8B00965373 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; - CDC42E311AE7AB8B00965373 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; - CDC42E331AE7AC6D00965373 /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - CDF066CA1CDC1CA200199626 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; }; - CDF066CB1CDC1CA200199626 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; }; - D01B7B6219EDD8FE00D26E01 /* Nimble.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - D01B7B6319EDD8FE00D26E01 /* Quick.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - D01B7B6419EDD94B00D26E01 /* ReactiveObjCBridge.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D047260C19E49F82006002AA /* ReactiveObjCBridge.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - D037666419EDA43C00A782A9 /* ReactiveObjCBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveObjCBridge.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D037672D19EDA75D00A782A9 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; }; - D037672F19EDA78B00A782A9 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; }; - D04725F019E49ED7006002AA /* ReactiveObjCBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveObjCBridge.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D04725F619E49ED7006002AA /* ReactiveObjCBridge.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D04725EA19E49ED7006002AA /* ReactiveObjCBridge.framework */; }; - D047261719E49F82006002AA /* ReactiveObjCBridge.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D047260C19E49F82006002AA /* ReactiveObjCBridge.framework */; }; - D05E662519EDD82000904ACA /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; }; - D05E662619EDD83000904ACA /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; }; - D0A226111A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A226101A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift */; }; - D0A226121A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A226101A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift */; }; - D0C312DF19EF2A5800984962 /* ObjectiveCBridging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312C419EF2A5800984962 /* ObjectiveCBridging.swift */; }; - D0C312E019EF2A5800984962 /* ObjectiveCBridging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C312C419EF2A5800984962 /* ObjectiveCBridging.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 7DFBED091CDB8C9500EE435B /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D04725E119E49ED7006002AA /* Project object */; - proxyType = 1; - remoteGlobalIDString = 57A4D1AF1BA13D7A00F7D4B1; - remoteInfo = "ReactiveCocoa-tvOS"; - }; - D04725F719E49ED7006002AA /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D04725E119E49ED7006002AA /* Project object */; - proxyType = 1; - remoteGlobalIDString = D04725E919E49ED7006002AA; - remoteInfo = ReactiveCocoa; - }; - D047261819E49F82006002AA /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D04725E119E49ED7006002AA /* Project object */; - proxyType = 1; - remoteGlobalIDString = D047260B19E49F82006002AA; - remoteInfo = ReactiveCocoa; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 7DFBED151CDB8CEC00EE435B /* Copy Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 7DFBED211CDB8D8300EE435B /* Result.framework in Copy Frameworks */, - 7DFBED201CDB8D7D00EE435B /* Nimble.framework in Copy Frameworks */, - 7DFBED1F1CDB8D7800EE435B /* Quick.framework in Copy Frameworks */, - 7DFBED1E1CDB8D7000EE435B /* ReactiveObjCBridge.framework in Copy Frameworks */, - ); - name = "Copy Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; - D01B7B6119EDD8F600D26E01 /* Copy Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - CDC42E331AE7AC6D00965373 /* Result.framework in Copy Frameworks */, - D01B7B6219EDD8FE00D26E01 /* Nimble.framework in Copy Frameworks */, - D01B7B6319EDD8FE00D26E01 /* Quick.framework in Copy Frameworks */, - D01B7B6419EDD94B00D26E01 /* ReactiveObjCBridge.framework in Copy Frameworks */, - ); - name = "Copy Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 57A4D2411BA13D7A00F7D4B1 /* ReactiveObjCBridge.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveObjCBridge.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Application.xcconfig"; sourceTree = ""; }; - 57A4D2451BA13F9700F7D4B1 /* tvOS-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Base.xcconfig"; sourceTree = ""; }; - 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Framework.xcconfig"; sourceTree = ""; }; - 57A4D2471BA13F9700F7D4B1 /* tvOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-StaticLibrary.xcconfig"; sourceTree = ""; }; - 7DFBED031CDB8C9500EE435B /* ReactiveObjCBridgeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveObjCBridgeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - A97451331B3A935E00F48E55 /* watchOS-Application.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Application.xcconfig"; sourceTree = ""; }; - A97451341B3A935E00F48E55 /* watchOS-Base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Base.xcconfig"; sourceTree = ""; }; - A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Framework.xcconfig"; sourceTree = ""; }; - A97451361B3A935E00F48E55 /* watchOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-StaticLibrary.xcconfig"; sourceTree = ""; }; - A9B315541B3940610001CB9C /* ReactiveObjCBridge.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveObjCBridge.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - BE330A0E1D634F1E00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = build/Debug/ReactiveSwift.framework; sourceTree = ""; }; - BE330A101D634F2900806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = build/Debug/ReactiveSwift.framework; sourceTree = ""; }; - BE330A121D634F2E00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-iphoneos/ReactiveSwift.framework"; sourceTree = ""; }; - BE330A141D634F4000806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-iphoneos/ReactiveSwift.framework"; sourceTree = ""; }; - BE330A161D634F4E00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-watchos/ReactiveSwift.framework"; sourceTree = ""; }; - BE330A181D634F5900806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-appletvos/ReactiveSwift.framework"; sourceTree = ""; }; - BE330A1A1D634F5F00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-appletvos/ReactiveSwift.framework"; sourceTree = ""; }; - BEA5E5D81D864DC900A1F0F2 /* ReactiveObjC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveObjC.framework; path = build/Debug/ReactiveObjC.framework; sourceTree = ""; }; - BEA5E5DA1D864DD100A1F0F2 /* ReactiveObjC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveObjC.framework; path = build/Debug/ReactiveObjC.framework; sourceTree = ""; }; - BEA5E5DC1D864DD800A1F0F2 /* ReactiveObjC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveObjC.framework; path = "build/Debug-iphoneos/ReactiveObjC.framework"; sourceTree = ""; }; - BEA5E5DE1D864DDE00A1F0F2 /* ReactiveObjC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveObjC.framework; path = "build/Debug-iphoneos/ReactiveObjC.framework"; sourceTree = ""; }; - BEA5E5E01D864DE800A1F0F2 /* ReactiveObjC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveObjC.framework; path = "build/Debug-watchos/ReactiveObjC.framework"; sourceTree = ""; }; - BEA5E5E21D864DED00A1F0F2 /* ReactiveObjC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveObjC.framework; path = "build/Debug-appletvos/ReactiveObjC.framework"; sourceTree = ""; }; - BEA5E5E41D864DF400A1F0F2 /* ReactiveObjC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveObjC.framework; path = "build/Debug-appletvos/ReactiveObjC.framework"; sourceTree = ""; }; - BEA5E5E61D864E7200A1F0F2 /* TestError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestError.swift; sourceTree = ""; }; - CDC42E2E1AE7AB8B00965373 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D037672B19EDA75D00A782A9 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Quick.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D04725EA19E49ED7006002AA /* ReactiveObjCBridge.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveObjCBridge.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D04725EE19E49ED7006002AA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - D04725EF19E49ED7006002AA /* ReactiveObjCBridge.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ReactiveObjCBridge.h; sourceTree = ""; }; - D04725F519E49ED7006002AA /* ReactiveObjCBridgeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveObjCBridgeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - D04725FB19E49ED7006002AA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - D047260C19E49F82006002AA /* ReactiveObjCBridge.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveObjCBridge.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D047261619E49F82006002AA /* ReactiveObjCBridgeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveObjCBridgeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - D047262719E49FE8006002AA /* Common.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Common.xcconfig; sourceTree = ""; }; - D047262919E49FE8006002AA /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; - D047262A19E49FE8006002AA /* Profile.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Profile.xcconfig; sourceTree = ""; }; - D047262B19E49FE8006002AA /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; - D047262C19E49FE8006002AA /* Test.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Test.xcconfig; sourceTree = ""; }; - D047262E19E49FE8006002AA /* Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Application.xcconfig; sourceTree = ""; }; - D047262F19E49FE8006002AA /* Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Framework.xcconfig; sourceTree = ""; }; - D047263019E49FE8006002AA /* StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = StaticLibrary.xcconfig; sourceTree = ""; }; - D047263219E49FE8006002AA /* iOS-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-Application.xcconfig"; sourceTree = ""; }; - D047263319E49FE8006002AA /* iOS-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-Base.xcconfig"; sourceTree = ""; }; - D047263419E49FE8006002AA /* iOS-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-Framework.xcconfig"; sourceTree = ""; }; - D047263519E49FE8006002AA /* iOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-StaticLibrary.xcconfig"; sourceTree = ""; }; - D047263719E49FE8006002AA /* Mac-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Application.xcconfig"; sourceTree = ""; }; - D047263819E49FE8006002AA /* Mac-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Base.xcconfig"; sourceTree = ""; }; - D047263919E49FE8006002AA /* Mac-DynamicLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-DynamicLibrary.xcconfig"; sourceTree = ""; }; - D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Framework.xcconfig"; sourceTree = ""; }; - D047263B19E49FE8006002AA /* Mac-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-StaticLibrary.xcconfig"; sourceTree = ""; }; - D047263C19E49FE8006002AA /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - D05E662419EDD82000904ACA /* Nimble.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Nimble.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D0A226101A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ObjectiveCBridgingSpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - D0C312C419EF2A5800984962 /* ObjectiveCBridging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjectiveCBridging.swift; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 57A4D2071BA13D7A00F7D4B1 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - BEA5E5E31D864DED00A1F0F2 /* ReactiveObjC.framework in Frameworks */, - BE330A191D634F5900806963 /* ReactiveSwift.framework in Frameworks */, - 57A4D2081BA13D7A00F7D4B1 /* Result.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 7DFBED001CDB8C9500EE435B /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - BEA5E5E51D864DF400A1F0F2 /* ReactiveObjC.framework in Frameworks */, - BE330A1B1D634F5F00806963 /* ReactiveSwift.framework in Frameworks */, - CDF066CA1CDC1CA200199626 /* Nimble.framework in Frameworks */, - CDF066CB1CDC1CA200199626 /* Quick.framework in Frameworks */, - 7DFBED081CDB8C9500EE435B /* ReactiveObjCBridge.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A9B315501B3940610001CB9C /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - BEA5E5E11D864DE800A1F0F2 /* ReactiveObjC.framework in Frameworks */, - BE330A171D634F4E00806963 /* ReactiveSwift.framework in Frameworks */, - A9B315C91B3940980001CB9C /* Result.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D04725E619E49ED7006002AA /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - BEA5E5D91D864DC900A1F0F2 /* ReactiveObjC.framework in Frameworks */, - BE330A0F1D634F1E00806963 /* ReactiveSwift.framework in Frameworks */, - CDC42E2F1AE7AB8B00965373 /* Result.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D04725F219E49ED7006002AA /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - BEA5E5DB1D864DD100A1F0F2 /* ReactiveObjC.framework in Frameworks */, - BE330A111D634F2900806963 /* ReactiveSwift.framework in Frameworks */, - CDC42E301AE7AB8B00965373 /* Result.framework in Frameworks */, - D05E662519EDD82000904ACA /* Nimble.framework in Frameworks */, - D037672D19EDA75D00A782A9 /* Quick.framework in Frameworks */, - D04725F619E49ED7006002AA /* ReactiveObjCBridge.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D047260819E49F82006002AA /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - BEA5E5DD1D864DD800A1F0F2 /* ReactiveObjC.framework in Frameworks */, - BE330A131D634F2E00806963 /* ReactiveSwift.framework in Frameworks */, - CDC42E311AE7AB8B00965373 /* Result.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D047261319E49F82006002AA /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - BEA5E5DF1D864DDE00A1F0F2 /* ReactiveObjC.framework in Frameworks */, - BE330A151D634F4000806963 /* ReactiveSwift.framework in Frameworks */, - D05E662619EDD83000904ACA /* Nimble.framework in Frameworks */, - D037672F19EDA78B00A782A9 /* Quick.framework in Frameworks */, - D047261719E49F82006002AA /* ReactiveObjCBridge.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 57A4D2431BA13F9700F7D4B1 /* tvOS */ = { - isa = PBXGroup; - children = ( - 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */, - 57A4D2451BA13F9700F7D4B1 /* tvOS-Base.xcconfig */, - 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */, - 57A4D2471BA13F9700F7D4B1 /* tvOS-StaticLibrary.xcconfig */, - ); - path = tvOS; - sourceTree = ""; - }; - A97451321B3A935E00F48E55 /* watchOS */ = { - isa = PBXGroup; - children = ( - A97451331B3A935E00F48E55 /* watchOS-Application.xcconfig */, - A97451341B3A935E00F48E55 /* watchOS-Base.xcconfig */, - A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */, - A97451361B3A935E00F48E55 /* watchOS-StaticLibrary.xcconfig */, - ); - path = watchOS; - sourceTree = ""; - }; - BE330A0D1D634F1E00806963 /* Frameworks */ = { - isa = PBXGroup; - children = ( - BEA5E5E41D864DF400A1F0F2 /* ReactiveObjC.framework */, - BEA5E5E21D864DED00A1F0F2 /* ReactiveObjC.framework */, - BEA5E5E01D864DE800A1F0F2 /* ReactiveObjC.framework */, - BEA5E5DE1D864DDE00A1F0F2 /* ReactiveObjC.framework */, - BEA5E5DC1D864DD800A1F0F2 /* ReactiveObjC.framework */, - BEA5E5DA1D864DD100A1F0F2 /* ReactiveObjC.framework */, - BEA5E5D81D864DC900A1F0F2 /* ReactiveObjC.framework */, - BE330A1A1D634F5F00806963 /* ReactiveSwift.framework */, - BE330A181D634F5900806963 /* ReactiveSwift.framework */, - BE330A161D634F4E00806963 /* ReactiveSwift.framework */, - BE330A141D634F4000806963 /* ReactiveSwift.framework */, - BE330A121D634F2E00806963 /* ReactiveSwift.framework */, - BE330A101D634F2900806963 /* ReactiveSwift.framework */, - BE330A0E1D634F1E00806963 /* ReactiveSwift.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - D04725E019E49ED7006002AA = { - isa = PBXGroup; - children = ( - D04725EC19E49ED7006002AA /* ReactiveObjCBridge */, - D04725F919E49ED7006002AA /* ReactiveObjCBridgeTests */, - D047262519E49FE8006002AA /* Configuration */, - D04725EB19E49ED7006002AA /* Products */, - BE330A0D1D634F1E00806963 /* Frameworks */, - ); - sourceTree = ""; - usesTabs = 1; - }; - D04725EB19E49ED7006002AA /* Products */ = { - isa = PBXGroup; - children = ( - D04725EA19E49ED7006002AA /* ReactiveObjCBridge.framework */, - D04725F519E49ED7006002AA /* ReactiveObjCBridgeTests.xctest */, - D047260C19E49F82006002AA /* ReactiveObjCBridge.framework */, - D047261619E49F82006002AA /* ReactiveObjCBridgeTests.xctest */, - A9B315541B3940610001CB9C /* ReactiveObjCBridge.framework */, - 57A4D2411BA13D7A00F7D4B1 /* ReactiveObjCBridge.framework */, - 7DFBED031CDB8C9500EE435B /* ReactiveObjCBridgeTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - D04725EC19E49ED7006002AA /* ReactiveObjCBridge */ = { - isa = PBXGroup; - children = ( - D04725EF19E49ED7006002AA /* ReactiveObjCBridge.h */, - D0C312C419EF2A5800984962 /* ObjectiveCBridging.swift */, - D04725ED19E49ED7006002AA /* Supporting Files */, - ); - path = ReactiveObjCBridge; - sourceTree = ""; - }; - D04725ED19E49ED7006002AA /* Supporting Files */ = { - isa = PBXGroup; - children = ( - CDC42E2E1AE7AB8B00965373 /* Result.framework */, - D04725EE19E49ED7006002AA /* Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - D04725F919E49ED7006002AA /* ReactiveObjCBridgeTests */ = { - isa = PBXGroup; - children = ( - D0A226101A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift */, - BEA5E5E61D864E7200A1F0F2 /* TestError.swift */, - D04725FA19E49ED7006002AA /* Supporting Files */, - ); - path = ReactiveObjCBridgeTests; - sourceTree = ""; - }; - D04725FA19E49ED7006002AA /* Supporting Files */ = { - isa = PBXGroup; - children = ( - D05E662419EDD82000904ACA /* Nimble.framework */, - D037672B19EDA75D00A782A9 /* Quick.framework */, - D04725FB19E49ED7006002AA /* Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - D047262519E49FE8006002AA /* Configuration */ = { - isa = PBXGroup; - children = ( - D047262619E49FE8006002AA /* Base */, - D047263119E49FE8006002AA /* iOS */, - D047263619E49FE8006002AA /* Mac OS X */, - A97451321B3A935E00F48E55 /* watchOS */, - 57A4D2431BA13F9700F7D4B1 /* tvOS */, - D047263C19E49FE8006002AA /* README.md */, - ); - name = Configuration; - path = Carthage/Checkouts/xcconfigs; - sourceTree = ""; - }; - D047262619E49FE8006002AA /* Base */ = { - isa = PBXGroup; - children = ( - D047262719E49FE8006002AA /* Common.xcconfig */, - D047262819E49FE8006002AA /* Configurations */, - D047262D19E49FE8006002AA /* Targets */, - ); - path = Base; - sourceTree = ""; - }; - D047262819E49FE8006002AA /* Configurations */ = { - isa = PBXGroup; - children = ( - D047262919E49FE8006002AA /* Debug.xcconfig */, - D047262A19E49FE8006002AA /* Profile.xcconfig */, - D047262B19E49FE8006002AA /* Release.xcconfig */, - D047262C19E49FE8006002AA /* Test.xcconfig */, - ); - path = Configurations; - sourceTree = ""; - }; - D047262D19E49FE8006002AA /* Targets */ = { - isa = PBXGroup; - children = ( - D047262E19E49FE8006002AA /* Application.xcconfig */, - D047262F19E49FE8006002AA /* Framework.xcconfig */, - D047263019E49FE8006002AA /* StaticLibrary.xcconfig */, - ); - path = Targets; - sourceTree = ""; - }; - D047263119E49FE8006002AA /* iOS */ = { - isa = PBXGroup; - children = ( - D047263219E49FE8006002AA /* iOS-Application.xcconfig */, - D047263319E49FE8006002AA /* iOS-Base.xcconfig */, - D047263419E49FE8006002AA /* iOS-Framework.xcconfig */, - D047263519E49FE8006002AA /* iOS-StaticLibrary.xcconfig */, - ); - path = iOS; - sourceTree = ""; - }; - D047263619E49FE8006002AA /* Mac OS X */ = { - isa = PBXGroup; - children = ( - D047263719E49FE8006002AA /* Mac-Application.xcconfig */, - D047263819E49FE8006002AA /* Mac-Base.xcconfig */, - D047263919E49FE8006002AA /* Mac-DynamicLibrary.xcconfig */, - D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */, - D047263B19E49FE8006002AA /* Mac-StaticLibrary.xcconfig */, - ); - path = "Mac OS X"; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - 57A4D2091BA13D7A00F7D4B1 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 57A4D20A1BA13D7A00F7D4B1 /* ReactiveObjCBridge.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A9B315511B3940610001CB9C /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - A9B315CA1B3940AB0001CB9C /* ReactiveObjCBridge.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D04725E719E49ED7006002AA /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - D04725F019E49ED7006002AA /* ReactiveObjCBridge.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D047260919E49F82006002AA /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - D037666419EDA43C00A782A9 /* ReactiveObjCBridge.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 57A4D1AF1BA13D7A00F7D4B1 /* ReactiveObjCBridge-tvOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = 57A4D23C1BA13D7A00F7D4B1 /* Build configuration list for PBXNativeTarget "ReactiveObjCBridge-tvOS" */; - buildPhases = ( - 57A4D1B01BA13D7A00F7D4B1 /* Sources */, - 57A4D2071BA13D7A00F7D4B1 /* Frameworks */, - 57A4D2091BA13D7A00F7D4B1 /* Headers */, - 57A4D23B1BA13D7A00F7D4B1 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "ReactiveObjCBridge-tvOS"; - productName = ReactiveCocoa; - productReference = 57A4D2411BA13D7A00F7D4B1 /* ReactiveObjCBridge.framework */; - productType = "com.apple.product-type.framework"; - }; - 7DFBED021CDB8C9500EE435B /* ReactiveObjCBridge-tvOSTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 7DFBED0F1CDB8C9500EE435B /* Build configuration list for PBXNativeTarget "ReactiveObjCBridge-tvOSTests" */; - buildPhases = ( - 7DFBECFF1CDB8C9500EE435B /* Sources */, - 7DFBED001CDB8C9500EE435B /* Frameworks */, - 7DFBED011CDB8C9500EE435B /* Resources */, - 7DFBED151CDB8CEC00EE435B /* Copy Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - 7DFBED0A1CDB8C9500EE435B /* PBXTargetDependency */, - ); - name = "ReactiveObjCBridge-tvOSTests"; - productName = "ReactiveCocoa-tvOSTests"; - productReference = 7DFBED031CDB8C9500EE435B /* ReactiveObjCBridgeTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - A9B315531B3940610001CB9C /* ReactiveObjCBridge-watchOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = A9B3155D1B3940610001CB9C /* Build configuration list for PBXNativeTarget "ReactiveObjCBridge-watchOS" */; - buildPhases = ( - A9B3154F1B3940610001CB9C /* Sources */, - A9B315501B3940610001CB9C /* Frameworks */, - A9B315511B3940610001CB9C /* Headers */, - A9B315521B3940610001CB9C /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "ReactiveObjCBridge-watchOS"; - productName = ReactiveCocoa; - productReference = A9B315541B3940610001CB9C /* ReactiveObjCBridge.framework */; - productType = "com.apple.product-type.framework"; - }; - D04725E919E49ED7006002AA /* ReactiveObjCBridge-macOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = D047260019E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjCBridge-macOS" */; - buildPhases = ( - D04725E519E49ED7006002AA /* Sources */, - D04725E619E49ED7006002AA /* Frameworks */, - D04725E719E49ED7006002AA /* Headers */, - D04725E819E49ED7006002AA /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "ReactiveObjCBridge-macOS"; - productName = ReactiveCocoa; - productReference = D04725EA19E49ED7006002AA /* ReactiveObjCBridge.framework */; - productType = "com.apple.product-type.framework"; - }; - D04725F419E49ED7006002AA /* ReactiveObjCBridge-macOSTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = D047260319E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjCBridge-macOSTests" */; - buildPhases = ( - D04725F119E49ED7006002AA /* Sources */, - D04725F219E49ED7006002AA /* Frameworks */, - D04725F319E49ED7006002AA /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - D04725F819E49ED7006002AA /* PBXTargetDependency */, - ); - name = "ReactiveObjCBridge-macOSTests"; - productName = ReactiveCocoaTests; - productReference = D04725F519E49ED7006002AA /* ReactiveObjCBridgeTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - D047260B19E49F82006002AA /* ReactiveObjCBridge-iOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = D047261F19E49F82006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjCBridge-iOS" */; - buildPhases = ( - D047260719E49F82006002AA /* Sources */, - D047260819E49F82006002AA /* Frameworks */, - D047260919E49F82006002AA /* Headers */, - D047260A19E49F82006002AA /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "ReactiveObjCBridge-iOS"; - productName = ReactiveCocoa; - productReference = D047260C19E49F82006002AA /* ReactiveObjCBridge.framework */; - productType = "com.apple.product-type.framework"; - }; - D047261519E49F82006002AA /* ReactiveObjCBridge-iOSTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = D047262219E49F82006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjCBridge-iOSTests" */; - buildPhases = ( - D047261219E49F82006002AA /* Sources */, - D047261319E49F82006002AA /* Frameworks */, - D047261419E49F82006002AA /* Resources */, - D01B7B6119EDD8F600D26E01 /* Copy Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - D047261919E49F82006002AA /* PBXTargetDependency */, - ); - name = "ReactiveObjCBridge-iOSTests"; - productName = ReactiveCocoaTests; - productReference = D047261619E49F82006002AA /* ReactiveObjCBridgeTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - D04725E119E49ED7006002AA /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0800; - ORGANIZATIONNAME = GitHub; - TargetAttributes = { - 57A4D1AF1BA13D7A00F7D4B1 = { - LastSwiftMigration = 0800; - }; - 7DFBED021CDB8C9500EE435B = { - CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 0800; - }; - A9B315531B3940610001CB9C = { - CreatedOnToolsVersion = 7.0; - LastSwiftMigration = 0800; - }; - D04725E919E49ED7006002AA = { - CreatedOnToolsVersion = 6.1; - LastSwiftMigration = 0800; - }; - D04725F419E49ED7006002AA = { - CreatedOnToolsVersion = 6.1; - LastSwiftMigration = 0800; - }; - D047260B19E49F82006002AA = { - CreatedOnToolsVersion = 6.1; - LastSwiftMigration = 0800; - }; - D047261519E49F82006002AA = { - CreatedOnToolsVersion = 6.1; - LastSwiftMigration = 0800; - }; - }; - }; - buildConfigurationList = D04725E419E49ED7006002AA /* Build configuration list for PBXProject "ReactiveObjCBridge" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - ); - mainGroup = D04725E019E49ED7006002AA; - productRefGroup = D04725EB19E49ED7006002AA /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - D04725E919E49ED7006002AA /* ReactiveObjCBridge-macOS */, - D04725F419E49ED7006002AA /* ReactiveObjCBridge-macOSTests */, - D047260B19E49F82006002AA /* ReactiveObjCBridge-iOS */, - D047261519E49F82006002AA /* ReactiveObjCBridge-iOSTests */, - A9B315531B3940610001CB9C /* ReactiveObjCBridge-watchOS */, - 57A4D1AF1BA13D7A00F7D4B1 /* ReactiveObjCBridge-tvOS */, - 7DFBED021CDB8C9500EE435B /* ReactiveObjCBridge-tvOSTests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 57A4D23B1BA13D7A00F7D4B1 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 7DFBED011CDB8C9500EE435B /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A9B315521B3940610001CB9C /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D04725E819E49ED7006002AA /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D04725F319E49ED7006002AA /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D047260A19E49F82006002AA /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D047261419E49F82006002AA /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 57A4D1B01BA13D7A00F7D4B1 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 57A4D1B71BA13D7A00F7D4B1 /* ObjectiveCBridging.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 7DFBECFF1CDB8C9500EE435B /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - BEA5E5EA1D864E7900A1F0F2 /* TestError.swift in Sources */, - 7DFBED271CDB8DE300EE435B /* ObjectiveCBridgingSpec.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A9B3154F1B3940610001CB9C /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - A9B315BF1B3940810001CB9C /* ObjectiveCBridging.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D04725E519E49ED7006002AA /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D0C312DF19EF2A5800984962 /* ObjectiveCBridging.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D04725F119E49ED7006002AA /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - BEA5E5E81D864E7600A1F0F2 /* TestError.swift in Sources */, - D0A226111A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D047260719E49F82006002AA /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D0C312E019EF2A5800984962 /* ObjectiveCBridging.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D047261219E49F82006002AA /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - BEA5E5E91D864E7700A1F0F2 /* TestError.swift in Sources */, - D0A226121A72F30B00D33B74 /* ObjectiveCBridgingSpec.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 7DFBED0A1CDB8C9500EE435B /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 57A4D1AF1BA13D7A00F7D4B1 /* ReactiveObjCBridge-tvOS */; - targetProxy = 7DFBED091CDB8C9500EE435B /* PBXContainerItemProxy */; - }; - D04725F819E49ED7006002AA /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D04725E919E49ED7006002AA /* ReactiveObjCBridge-macOS */; - targetProxy = D04725F719E49ED7006002AA /* PBXContainerItemProxy */; - }; - D047261919E49F82006002AA /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D047260B19E49F82006002AA /* ReactiveObjCBridge-iOS */; - targetProxy = D047261819E49F82006002AA /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 57A4D23D1BA13D7A00F7D4B1 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/build/Debug-appletvos", - ); - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); - INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; - }; - name = Debug; - }; - 57A4D23E1BA13D7A00F7D4B1 /* Test */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/build/Debug-appletvos", - ); - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); - INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; - }; - name = Test; - }; - 57A4D23F1BA13D7A00F7D4B1 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/build/Debug-appletvos", - ); - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); - INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; - }; - name = Release; - }; - 57A4D2401BA13D7A00F7D4B1 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/build/Debug-appletvos", - ); - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); - INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; - }; - name = Profile; - }; - 7DFBED0B1CDB8C9500EE435B /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; - buildSettings = { - CODE_SIGN_IDENTITY = ""; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - "$(PROJECT_DIR)/build/Debug-appletvos", - ); - INFOPLIST_FILE = ReactiveObjCBridgeTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Debug; - }; - 7DFBED0C1CDB8C9500EE435B /* Test */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; - buildSettings = { - CODE_SIGN_IDENTITY = ""; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - "$(PROJECT_DIR)/build/Debug-appletvos", - ); - INFOPLIST_FILE = ReactiveObjCBridgeTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Test; - }; - 7DFBED0D1CDB8C9500EE435B /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; - buildSettings = { - CODE_SIGN_IDENTITY = ""; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - "$(PROJECT_DIR)/build/Debug-appletvos", - ); - INFOPLIST_FILE = ReactiveObjCBridgeTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Release; - }; - 7DFBED0E1CDB8C9500EE435B /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; - buildSettings = { - CODE_SIGN_IDENTITY = ""; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - "$(PROJECT_DIR)/build/Debug-appletvos", - ); - INFOPLIST_FILE = ReactiveObjCBridgeTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Profile; - }; - A9B315591B3940610001CB9C /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/build/Debug-watchos", - ); - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); - INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; - }; - name = Debug; - }; - A9B3155A1B3940610001CB9C /* Test */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/build/Debug-watchos", - ); - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); - INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; - }; - name = Test; - }; - A9B3155B1B3940610001CB9C /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/build/Debug-watchos", - ); - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); - INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; - }; - name = Release; - }; - A9B3155C1B3940610001CB9C /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/build/Debug-watchos", - ); - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); - INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; - }; - name = Profile; - }; - D04725FE19E49ED7006002AA /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047262919E49FE8006002AA /* Debug.xcconfig */; - buildSettings = { - BITCODE_GENERATION_MODE = bitcode; - CODE_SIGNING_REQUIRED = NO; - CURRENT_PROJECT_VERSION = 1; - ENABLE_TESTABILITY = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; - PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(PROJECT_NAME)"; - SWIFT_VERSION = 3.0; - TVOS_DEPLOYMENT_TARGET = 9.0; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 2.0; - }; - name = Debug; - }; - D04725FF19E49ED7006002AA /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047262B19E49FE8006002AA /* Release.xcconfig */; - buildSettings = { - BITCODE_GENERATION_MODE = bitcode; - CODE_SIGNING_REQUIRED = NO; - CURRENT_PROJECT_VERSION = 1; - GCC_OPTIMIZATION_LEVEL = 0; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; - PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(PROJECT_NAME)"; - SWIFT_VERSION = 3.0; - TVOS_DEPLOYMENT_TARGET = 9.0; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 2.0; - }; - name = Release; - }; - D047260119E49ED7006002AA /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/build/Debug", - ); - FRAMEWORK_VERSION = A; - INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - }; - name = Debug; - }; - D047260219E49ED7006002AA /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/build/Debug", - ); - FRAMEWORK_VERSION = A; - INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - }; - name = Release; - }; - D047260419E49ED7006002AA /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; - buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(DEVELOPER_FRAMEWORKS_DIR)", - "$(inherited)", - "$(PROJECT_DIR)/build/Debug", - ); - INFOPLIST_FILE = ReactiveObjCBridgeTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Debug; - }; - D047260519E49ED7006002AA /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; - buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(DEVELOPER_FRAMEWORKS_DIR)", - "$(inherited)", - "$(PROJECT_DIR)/build/Debug", - ); - INFOPLIST_FILE = ReactiveObjCBridgeTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Release; - }; - D047262019E49F82006002AA /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/build/Debug-iphoneos", - ); - INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - }; - name = Debug; - }; - D047262119E49F82006002AA /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/build/Debug-iphoneos", - ); - INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - }; - name = Release; - }; - D047262319E49F82006002AA /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; - buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - "$(PROJECT_DIR)/build/Debug-iphoneos", - ); - INFOPLIST_FILE = ReactiveObjCBridgeTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Debug; - }; - D047262419E49F82006002AA /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; - buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - "$(PROJECT_DIR)/build/Debug-iphoneos", - ); - INFOPLIST_FILE = ReactiveObjCBridgeTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Release; - }; - D047263D19E4A008006002AA /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047262A19E49FE8006002AA /* Profile.xcconfig */; - buildSettings = { - BITCODE_GENERATION_MODE = bitcode; - CODE_SIGNING_REQUIRED = NO; - CURRENT_PROJECT_VERSION = 1; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; - PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(PROJECT_NAME)"; - SWIFT_VERSION = 3.0; - TVOS_DEPLOYMENT_TARGET = 9.0; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 2.0; - }; - name = Profile; - }; - D047263E19E4A008006002AA /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/build/Debug", - ); - FRAMEWORK_VERSION = A; - INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - }; - name = Profile; - }; - D047263F19E4A008006002AA /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; - buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(DEVELOPER_FRAMEWORKS_DIR)", - "$(inherited)", - "$(PROJECT_DIR)/build/Debug", - ); - INFOPLIST_FILE = ReactiveObjCBridgeTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Profile; - }; - D047264019E4A008006002AA /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/build/Debug-iphoneos", - ); - INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - }; - name = Profile; - }; - D047264119E4A008006002AA /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; - buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - "$(PROJECT_DIR)/build/Debug-iphoneos", - ); - INFOPLIST_FILE = ReactiveObjCBridgeTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Profile; - }; - D047264219E4A00B006002AA /* Test */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047262C19E49FE8006002AA /* Test.xcconfig */; - buildSettings = { - BITCODE_GENERATION_MODE = bitcode; - CODE_SIGNING_REQUIRED = NO; - CURRENT_PROJECT_VERSION = 1; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; - PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)-Tests"; - PRODUCT_NAME = "$(PROJECT_NAME)"; - SWIFT_VERSION = 3.0; - TVOS_DEPLOYMENT_TARGET = 9.0; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 2.0; - }; - name = Test; - }; - D047264319E4A00B006002AA /* Test */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/build/Debug", - ); - FRAMEWORK_VERSION = A; - INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - }; - name = Test; - }; - D047264419E4A00B006002AA /* Test */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; - buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(DEVELOPER_FRAMEWORKS_DIR)", - "$(inherited)", - "$(PROJECT_DIR)/build/Debug", - ); - INFOPLIST_FILE = ReactiveObjCBridgeTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Test; - }; - D047264519E4A00B006002AA /* Test */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/build/Debug-iphoneos", - ); - INFOPLIST_FILE = ReactiveObjCBridge/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - }; - name = Test; - }; - D047264619E4A00B006002AA /* Test */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; - buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - "$(PROJECT_DIR)/build/Debug-iphoneos", - ); - INFOPLIST_FILE = ReactiveObjCBridgeTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(PROJECT_NAME)Tests"; - }; - name = Test; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 57A4D23C1BA13D7A00F7D4B1 /* Build configuration list for PBXNativeTarget "ReactiveObjCBridge-tvOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 57A4D23D1BA13D7A00F7D4B1 /* Debug */, - 57A4D23E1BA13D7A00F7D4B1 /* Test */, - 57A4D23F1BA13D7A00F7D4B1 /* Release */, - 57A4D2401BA13D7A00F7D4B1 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 7DFBED0F1CDB8C9500EE435B /* Build configuration list for PBXNativeTarget "ReactiveObjCBridge-tvOSTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 7DFBED0B1CDB8C9500EE435B /* Debug */, - 7DFBED0C1CDB8C9500EE435B /* Test */, - 7DFBED0D1CDB8C9500EE435B /* Release */, - 7DFBED0E1CDB8C9500EE435B /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - A9B3155D1B3940610001CB9C /* Build configuration list for PBXNativeTarget "ReactiveObjCBridge-watchOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - A9B315591B3940610001CB9C /* Debug */, - A9B3155A1B3940610001CB9C /* Test */, - A9B3155B1B3940610001CB9C /* Release */, - A9B3155C1B3940610001CB9C /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D04725E419E49ED7006002AA /* Build configuration list for PBXProject "ReactiveObjCBridge" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D04725FE19E49ED7006002AA /* Debug */, - D047264219E4A00B006002AA /* Test */, - D04725FF19E49ED7006002AA /* Release */, - D047263D19E4A008006002AA /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D047260019E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjCBridge-macOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D047260119E49ED7006002AA /* Debug */, - D047264319E4A00B006002AA /* Test */, - D047260219E49ED7006002AA /* Release */, - D047263E19E4A008006002AA /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D047260319E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjCBridge-macOSTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D047260419E49ED7006002AA /* Debug */, - D047264419E4A00B006002AA /* Test */, - D047260519E49ED7006002AA /* Release */, - D047263F19E4A008006002AA /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D047261F19E49F82006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjCBridge-iOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D047262019E49F82006002AA /* Debug */, - D047264519E4A00B006002AA /* Test */, - D047262119E49F82006002AA /* Release */, - D047264019E4A008006002AA /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D047262219E49F82006002AA /* Build configuration list for PBXNativeTarget "ReactiveObjCBridge-iOSTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D047262319E49F82006002AA /* Debug */, - D047264619E4A00B006002AA /* Test */, - D047262419E49F82006002AA /* Release */, - D047264119E4A008006002AA /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = D04725E119E49ED7006002AA /* Project object */; -} diff --git a/ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-iOS.xcscheme b/ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-iOS.xcscheme deleted file mode 100644 index 37c8f6be2b..0000000000 --- a/ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-iOS.xcscheme +++ /dev/null @@ -1,184 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-macOS.xcscheme b/ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-macOS.xcscheme deleted file mode 100644 index ad34ab2e88..0000000000 --- a/ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-macOS.xcscheme +++ /dev/null @@ -1,184 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-tvOS.xcscheme b/ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-tvOS.xcscheme deleted file mode 100644 index ffed7e2b63..0000000000 --- a/ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-tvOS.xcscheme +++ /dev/null @@ -1,184 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-watchOS.xcscheme b/ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-watchOS.xcscheme deleted file mode 100644 index 3086c245e7..0000000000 --- a/ReactiveObjCBridge.xcodeproj/xcshareddata/xcschemes/ReactiveObjCBridge-watchOS.xcscheme +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ReactiveObjCBridge.xcworkspace/contents.xcworkspacedata b/ReactiveObjCBridge.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 7eda06116a..0000000000 --- a/ReactiveObjCBridge.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/ReactiveObjCBridge/Info.plist b/ReactiveObjCBridge/Info.plist deleted file mode 100644 index 9a11afdc43..0000000000 --- a/ReactiveObjCBridge/Info.plist +++ /dev/null @@ -1,28 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - NSHumanReadableCopyright - Copyright © 2014 GitHub. All rights reserved. - NSPrincipalClass - - - diff --git a/ReactiveObjCBridge/ObjectiveCBridging.swift b/ReactiveObjCBridge/ObjectiveCBridging.swift deleted file mode 100644 index 646b519580..0000000000 --- a/ReactiveObjCBridge/ObjectiveCBridging.swift +++ /dev/null @@ -1,298 +0,0 @@ -// -// ObjectiveCBridging.swift -// ReactiveObjCBridge -// -// Created by Justin Spahr-Summers on 2014-07-02. -// Copyright (c) 2014 GitHub, Inc. All rights reserved. -// - -import Foundation -import ReactiveObjC -import ReactiveSwift -import Result - -extension SignalProtocol { - /// Turns each value into an Optional. - fileprivate func optionalize() -> Signal { - return map(Optional.init) - } -} - -extension SignalProducerProtocol { - /// Turns each value into an Optional. - fileprivate func optionalize() -> SignalProducer { - return lift { $0.optionalize() } - } -} - -extension RACDisposable: Disposable {} -extension RACScheduler: DateSchedulerProtocol { - /// The current date, as determined by this scheduler. - public var currentDate: Date { - return Date() - } - - /// Schedule an action for immediate execution. - /// - /// - note: This method calls the Objective-C implementation of `schedule:` - /// method. - /// - /// - parameters: - /// - action: Closure to perform. - /// - /// - returns: Disposable that can be used to cancel the work before it - /// begins. - @discardableResult - public func schedule(_ action: @escaping () -> Void) -> Disposable? { - let disposable: RACDisposable = self.schedule(action) // Call the Objective-C implementation - return disposable as Disposable? - } - - /// Schedule an action for execution at or after the given date. - /// - /// - parameters: - /// - date: Starting date. - /// - action: Closure to perform. - /// - /// - returns: Optional disposable that can be used to cancel the work - /// before it begins. - @discardableResult - public func schedule(after date: Date, action: @escaping () -> Void) -> Disposable? { - return self.after(date, schedule: action) - } - - /// Schedule a recurring action at the given interval, beginning at the - /// given start time. - /// - /// - parameters: - /// - date: Starting date. - /// - repeatingEvery: Repetition interval. - /// - withLeeway: Some delta for repetition. - /// - action: Closure of the action to perform. - /// - /// - returns: Optional `Disposable` that can be used to cancel the work - /// before it begins. - @discardableResult - public func schedule(after date: Date, interval: TimeInterval, leeway: TimeInterval, action: @escaping () -> Void) -> Disposable? { - return self.after(date, repeatingEvery: interval, withLeeway: leeway, schedule: action) - } -} - -extension ImmediateScheduler { - /// Create `RACScheduler` that performs actions instantly. - /// - /// - returns: `RACScheduler` that instantly performs actions. - public func toRACScheduler() -> RACScheduler { - return RACScheduler.immediate() - } -} - -extension UIScheduler { - /// Create `RACScheduler` for `UIScheduler` - /// - /// - returns: `RACScheduler` instance that queues events on main thread. - public func toRACScheduler() -> RACScheduler { - return RACScheduler.mainThread() - } -} - -extension QueueScheduler { - /// Create `RACScheduler` backed with own queue - /// - /// - returns: Instance `RACScheduler` that queues events on - /// `QueueScheduler`'s queue. - public func toRACScheduler() -> RACScheduler { - return RACTargetQueueScheduler(name: "org.reactivecocoa.ReactiveObjCBridge.QueueScheduler.toRACScheduler()", targetQueue: queue) - } -} - -private func defaultNSError(_ message: String, file: String, line: Int) -> NSError { - return Result<(), NSError>.error(message, file: file, line: line) -} - -extension RACSignal { - /// Create a `SignalProducer` which will subscribe to the receiver once for - /// each invocation of `start()`. - /// - /// - parameters: - /// - file: Current file name. - /// - line: Current line in file. - /// - /// - returns: Signal producer created from `self`. - public func toSignalProducer(file: String = #file, line: Int = #line) -> SignalProducer { - return SignalProducer { observer, disposable in - let next = { obj in - observer.sendNext(obj) - } - - let failed: (_ nsError: Swift.Error?) -> () = { - observer.sendFailed(($0 as? NSError) ?? defaultNSError("Nil RACSignal error", file: file, line: line)) - } - - let completed = { - observer.sendCompleted() - } - - disposable += self.subscribeNext(next, error: failed, completed: completed) - } - } -} - -extension SignalProducerProtocol { - /// Create a `RACSignal` that will `start()` the producer once for each - /// subscription. - /// - /// - note: Any `interrupted` events will be silently discarded. - /// - /// - returns: `RACSignal` instantiated from `self`. - public func toRACSignal() -> RACSignal { - return RACSignal.createSignal { subscriber in - let selfDisposable = self.start { event in - switch event { - case let .next(value): - subscriber.sendNext(value) - case let .failed(error): - subscriber.sendError(error) - case .completed: - subscriber.sendCompleted() - case .interrupted: - break - } - } - - return RACDisposable { - selfDisposable.dispose() - } - } - } -} - -extension SignalProtocol { - /// Create a `RACSignal` that will observe the given signal. - /// - /// - note: Any `interrupted` events will be silently discarded. - /// - /// - returns: `RACSignal` instantiated from `self`. - public func toRACSignal() -> RACSignal { - return RACSignal.createSignal { subscriber in - let selfDisposable = self.observe { event in - switch event { - case let .next(value): - subscriber.sendNext(value) - case let .failed(error): - subscriber.sendError(error) - case .completed: - subscriber.sendCompleted() - case .interrupted: - break - } - } - - return RACDisposable { - selfDisposable?.dispose() - } - } - } -} - -// MARK: - - -// FIXME: Reintroduce `RACCommand.toAction` when compiler no longer segfault -// on extensions to parameterized ObjC classes. -/** -extension RACCommand { - /// Creates an Action that will execute the receiver. - /// - /// - note: The returned Action will not necessarily be marked as executing - /// when the command is. However, the reverse is always true: the - /// RACCommand will always be marked as executing when the action - /// is. - /// - /// - parameters: - /// - file: Current file name. - /// - line: Current line in file. - /// - /// - returns: Action created from `self`. - public func toAction(file: String = #file, line: Int = #line) -> Action { - let enabledProperty = MutableProperty(true) - - enabledProperty <~ self.enabled.toSignalProducer() - .map { $0 as! Bool } - .flatMapError { _ in SignalProducer(value: false) } - - return Action(enabledIf: enabledProperty) { input -> SignalProducer in - let executionSignal = RACSignal.`defer` { - return self.execute(input) - } -**/ - -extension ActionProtocol { - fileprivate var isCommandEnabled: RACSignal { - return self.isEnabled.producer - .map { $0 as NSNumber } - .toRACSignal() - } -} - -/// Creates an Action that will execute the receiver. -/// -/// - note: The returned Action will not necessarily be marked as executing -/// when the command is. However, the reverse is always true: the -/// RACCommand will always be marked as executing when the action -/// is. -/// -/// - parameters: -/// - file: Current file name. -/// - line: Current line in file. -/// -/// - returns: Action created from `self`. -public func bridgedAction(from command: RACCommand, file: String = #file, line: Int = #line) -> Action { - let command = command as! RACCommand - let enabledProperty = MutableProperty(true) - - enabledProperty <~ command.enabled.toSignalProducer() - .map { $0 as! Bool } - .flatMapError { _ in SignalProducer(value: false) } - - return Action(enabledIf: enabledProperty) { input -> SignalProducer in - let executionSignal = RACSignal.`defer` { - return command.execute(input as AnyObject?) - } - - return executionSignal.toSignalProducer(file: file, line: line) - } -} - -extension ActionProtocol where Input: AnyObject { - /// Creates a RACCommand that will execute the action. - /// - /// - note: The returned command will not necessarily be marked as executing - /// when the action is. However, the reverse is always true: the Action - /// will always be marked as executing when the RACCommand is. - /// - /// - returns: `RACCommand` with bound action. - public func toRACCommand() -> RACCommand { - return RACCommand(enabled: action.isCommandEnabled) { input -> RACSignal in - return self - .apply(input!) - .toRACSignal() - } - } -} - -extension ActionProtocol where Input: OptionalProtocol, Input.Wrapped: AnyObject { - /// Creates a RACCommand that will execute the action. - /// - /// - note: The returned command will not necessarily be marked as executing - /// when the action is. However, the reverse is always true: the Action - /// will always be marked as executing when the RACCommand is. - /// - /// - returns: `RACCommand` with bound action. - public func toRACCommand() -> RACCommand { - return RACCommand(enabled: action.isCommandEnabled) { input -> RACSignal in - return self - .apply(Input(reconstructing: input)) - .toRACSignal() - } - } -} diff --git a/ReactiveObjCBridge/ReactiveObjCBridge.h b/ReactiveObjCBridge/ReactiveObjCBridge.h deleted file mode 100644 index eae278b2c3..0000000000 --- a/ReactiveObjCBridge/ReactiveObjCBridge.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// ReactiveObjCBridge.h -// ReactiveObjCBridge -// -// Created by Josh Abernathy on 3/5/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import - -//! Project version number for ReactiveObjCBridge. -FOUNDATION_EXPORT double ReactiveObjCBridgeVersionNumber; - -//! Project version string for ReactiveObjCBridge. -FOUNDATION_EXPORT const unsigned char ReactiveObjCBridgeVersionString[]; diff --git a/ReactiveObjCBridgeTests/Info.plist b/ReactiveObjCBridgeTests/Info.plist deleted file mode 100644 index ba72822e87..0000000000 --- a/ReactiveObjCBridgeTests/Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - - diff --git a/ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift b/ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift deleted file mode 100644 index 1f87acbc5c..0000000000 --- a/ReactiveObjCBridgeTests/ObjectiveCBridgingSpec.swift +++ /dev/null @@ -1,304 +0,0 @@ -// -// ObjectiveCBridgingSpec.swift -// ReactiveObjCBridge -// -// Created by Justin Spahr-Summers on 2015-01-23. -// Copyright (c) 2015 GitHub. All rights reserved. -// - -import ReactiveObjC -import ReactiveObjCBridge -import ReactiveSwift -import Result -import Nimble -import Quick -import XCTest - -class ObjectiveCBridgingSpec: QuickSpec { - override func spec() { - describe("RACScheduler") { - var originalScheduler: RACTestScheduler! - var scheduler: DateSchedulerProtocol! - - beforeEach { - originalScheduler = RACTestScheduler() - scheduler = originalScheduler as DateSchedulerProtocol - } - - it("gives current date") { - let expected = Date() - let actual = scheduler.currentDate - expect(actual.timeIntervalSinceReferenceDate).to(beCloseTo(expected.timeIntervalSinceReferenceDate, within: 0.0003)) - } - - it("schedules actions") { - var actionRan: Bool = false - - scheduler.schedule { - actionRan = true - } - - expect(actionRan) == false - originalScheduler.step() - expect(actionRan) == true - } - - it("does not invoke action if disposed") { - var actionRan: Bool = false - - let disposable: Disposable? = scheduler.schedule { - actionRan = true - } - - expect(actionRan) == false - disposable!.dispose() - originalScheduler.step() - expect(actionRan) == false - } - } - - describe("RACSignal.toSignalProducer") { - it("should subscribe once per start()") { - var subscriptions = 0 - - let racSignal = RACSignal.createSignal { subscriber in - subscriber.sendNext(subscriptions) - subscriber.sendCompleted() - - subscriptions += 1 - - return nil - } - - let producer = racSignal.toSignalProducer().map { $0 as! Int } - - expect((producer.single())?.value) == 0 - expect((producer.single())?.value) == 1 - expect((producer.single())?.value) == 2 - } - - it("should forward errors") { - let error = TestError.default as NSError - - let racSignal = RACSignal.error(error) - let producer = racSignal.toSignalProducer() - let result = producer.last() - - expect(result?.error) == error - } - } - - describe("toRACSignal") { - let key = NSLocalizedDescriptionKey - let userInfo: [String: String] = [key: "TestValue"] - let testNSError = NSError(domain: "TestDomain", code: 1, userInfo: userInfo) - describe("on a Signal") { - it("should forward events") { - let (signal, observer) = Signal.pipe() - let racSignal = signal.toRACSignal() - - var lastValue: NSNumber? - var didComplete = false - - racSignal.subscribeNext({ number in - lastValue = number as? NSNumber - }, completed: { - didComplete = true - }) - - expect(lastValue).to(beNil()) - - for number in [1, 2, 3] { - observer.sendNext(number as NSNumber) - expect(lastValue) == number as NSNumber - } - - expect(didComplete) == false - observer.sendCompleted() - expect(didComplete) == true - } - - it("should convert errors to NSError") { - let (signal, observer) = Signal.pipe() - let racSignal = signal.toRACSignal() - - let expectedError = TestError.error2 - var error: TestError? - - racSignal.subscribeError { - error = $0 as? TestError - return - } - - observer.sendFailed(expectedError) - expect(error) == expectedError - } - - it("should maintain userInfo on NSError") { - let (signal, observer) = Signal.pipe() - let racSignal = signal.toRACSignal() - - var error: String? - - racSignal.subscribeError { - error = $0?.localizedDescription - return - } - - observer.sendFailed(testNSError) - - expect(error) == userInfo[key] - } - } - - describe("on a SignalProducer") { - it("should start once per subscription") { - var subscriptions = 0 - - let producer = SignalProducer.attempt { - defer { - subscriptions += 1 - } - - return .success(subscriptions as NSNumber) - } - let racSignal = producer.toRACSignal() - - expect(racSignal.first() as? NSNumber) == 0 - expect(racSignal.first() as? NSNumber) == 1 - expect(racSignal.first() as? NSNumber) == 2 - } - - it("should convert errors to NSError") { - let producer = SignalProducer(error: .error1) - let racSignal = producer.toRACSignal().materialize() - - let event = racSignal.first() as? RACEvent - expect(event?.error as? NSError) == TestError.error1 as NSError - } - - it("should maintain userInfo on NSError") { - let producer = SignalProducer(error: testNSError) - let racSignal = producer.toRACSignal().materialize() - - let event = racSignal.first() as? RACEvent - let userInfoValue = event?.error?.localizedDescription - expect(userInfoValue) == userInfo[key] - } - } - } - - describe("toAction") { - var command: RACCommand! - var results: [Int] = [] - - var enabledSubject: RACSubject! - var enabled = false - - var action: Action! - - beforeEach { - enabledSubject = RACSubject() - results = [] - - command = RACCommand(enabled: enabledSubject) { (input: AnyObject?) -> RACSignal in - let inputNumber = input as! Int - return RACSignal.`return`(inputNumber + 1) - } - - expect(command).notTo(beNil()) - - command.enabled.subscribeNext { enabled = $0 as! Bool } - expect(enabled) == true - - command.executionSignals.flatten().subscribeNext { results.append($0 as! Int) } - expect(results) == [] - - action = bridgedAction(from: command) - } - - it("should reflect the enabledness of the command") { - expect(action.isEnabled.value) == true - - enabledSubject.sendNext(false) - expect(enabled).toEventually(beFalsy()) - expect(action.isEnabled.value) == false - } - - it("should execute the command once per start()") { - let producer = action.apply(0 as NSNumber) - expect(results) == [] - - producer.start() - expect(results).toEventually(equal([ 1 ])) - - producer.start() - expect(results).toEventually(equal([ 1, 1 ])) - - let otherProducer = action.apply(2 as NSNumber) - expect(results) == [ 1, 1 ] - - otherProducer.start() - expect(results).toEventually(equal([ 1, 1, 3 ])) - - producer.start() - expect(results).toEventually(equal([ 1, 1, 3, 1 ])) - } - } - - describe("toRACCommand") { - var action: Action! - var results: [NSString] = [] - - var enabledProperty: MutableProperty! - - var command: RACCommand! - var enabled = false - - beforeEach { - results = [] - enabledProperty = MutableProperty(true) - - action = Action(enabledIf: enabledProperty) { input in - let inputNumber = input as! Int - return SignalProducer(value: "\(inputNumber + 1)" as NSString) - } - - expect(action.isEnabled.value) == true - - action.values.observeNext { results.append($0) } - - command = action.toRACCommand() - expect(command).notTo(beNil()) - - command.enabled.subscribeNext { enabled = $0 as! Bool } - expect(enabled) == true - } - - it("should reflect the enabledness of the action") { - enabledProperty.value = false - expect(enabled).toEventually(beFalsy()) - - enabledProperty.value = true - expect(enabled).toEventually(beTruthy()) - } - - it("should apply and start a signal once per execution") { - let signal = command.execute(0 as NSNumber) - - do { - try signal.asynchronouslyWaitUntilCompleted() - expect(results) == [ "1" ] - - try signal.asynchronouslyWaitUntilCompleted() - expect(results) == [ "1" ] - - try command.execute(2 as NSNumber).asynchronouslyWaitUntilCompleted() - expect(results) == [ "1", "3" ] - } catch { - XCTFail("Failed to wait for completion") - } - } - } - } -} diff --git a/ReactiveObjCBridgeTests/TestError.swift b/ReactiveObjCBridgeTests/TestError.swift deleted file mode 100644 index b9d59df8ab..0000000000 --- a/ReactiveObjCBridgeTests/TestError.swift +++ /dev/null @@ -1,43 +0,0 @@ -// -// TestError.swift -// ReactiveSwift -// -// Created by Almas Sapargali on 1/26/15. -// Copyright (c) 2015 GitHub. All rights reserved. -// - -import ReactiveSwift -import Result - -internal enum TestError: Int { - case `default` = 0 - case error1 = 1 - case error2 = 2 -} - -extension TestError: Error { -} - - -internal extension SignalProducerProtocol { - /// Halts if an error is emitted in the receiver signal. - /// This is useful in tests to be able to just use `startWithNext` - /// in cases where we know that an error won't be emitted. - func assumeNoErrors() -> SignalProducer { - return self.lift { $0.assumeNoErrors() } - } -} - -internal extension SignalProtocol { - /// Halts if an error is emitted in the receiver signal. - /// This is useful in tests to be able to just use `startWithNext` - /// in cases where we know that an error won't be emitted. - func assumeNoErrors() -> Signal { - return self.mapError { error in - fatalError("Unexpected error: \(error)") - - () - } - } -} - diff --git a/ReactiveObjCTests/Info.plist b/ReactiveObjCTests/Info.plist deleted file mode 100644 index ba72822e87..0000000000 --- a/ReactiveObjCTests/Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - - diff --git a/ReactiveObjCTests/NSControllerRACSupportSpec.m b/ReactiveObjCTests/NSControllerRACSupportSpec.m deleted file mode 100644 index 4a6cfa18ec..0000000000 --- a/ReactiveObjCTests/NSControllerRACSupportSpec.m +++ /dev/null @@ -1,47 +0,0 @@ -// -// NSControllerRACSupportSpec.m -// ReactiveObjC -// -// Created by Uri Baghin on 26/10/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import -#import "RACKVOChannel.h" - -@interface RACTestController : NSController - -@property (nonatomic, strong) id object; - -@end - -@implementation RACTestController - -@end - -QuickSpecBegin(NSControllerRACSupportSpec) - -qck_it(@"RACKVOChannel should support NSController", ^{ - RACTestController *a = [[RACTestController alloc] init]; - RACTestController *b = [[RACTestController alloc] init]; - RACChannelTo(a, object) = RACChannelTo(b, object); - expect(a.object).to(beNil()); - expect(b.object).to(beNil()); - - a.object = a; - expect(a.object).to(equal(a)); - expect(b.object).to(equal(a)); - - b.object = b; - expect(a.object).to(equal(b)); - expect(b.object).to(equal(b)); - - a.object = nil; - expect(a.object).to(beNil()); - expect(b.object).to(beNil()); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/NSEnumeratorRACSequenceAdditionsSpec.m b/ReactiveObjCTests/NSEnumeratorRACSequenceAdditionsSpec.m deleted file mode 100644 index c1cf616a5e..0000000000 --- a/ReactiveObjCTests/NSEnumeratorRACSequenceAdditionsSpec.m +++ /dev/null @@ -1,28 +0,0 @@ -// -// NSEnumeratorRACSequenceAdditionsSpec.m -// ReactiveObjC -// -// Created by Uri Baghin on 07/01/2013. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACSequenceExamples.h" - -#import "NSEnumerator+RACSequenceAdditions.h" - -QuickSpecBegin(NSEnumeratorRACSequenceAdditionsSpec) - -qck_describe(@"-rac_sequence", ^{ - NSArray *values = @[ @0, @1, @2, @3, @4 ]; - qck_itBehavesLike(RACSequenceExamples, ^{ - return @{ - RACSequenceExampleSequence: values.objectEnumerator.rac_sequence, - RACSequenceExampleExpectedValues: values - }; - }); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/NSNotificationCenterRACSupportSpec.m b/ReactiveObjCTests/NSNotificationCenterRACSupportSpec.m deleted file mode 100644 index b3126eeea0..0000000000 --- a/ReactiveObjCTests/NSNotificationCenterRACSupportSpec.m +++ /dev/null @@ -1,87 +0,0 @@ -// -// NSNotificationCenterRACSupportSpec.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-12-07. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "NSNotificationCenter+RACSupport.h" -#import "RACSignal.h" -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "NSObject+RACDeallocating.h" - -static NSString * const TestNotification = @"TestNotification"; - -QuickSpecBegin(NSNotificationCenterRACSupportSpec) - -__block NSNotificationCenter *notificationCenter; - -qck_beforeEach(^{ - // The compiler gets confused and thinks you might be messaging - // NSDistributedNotificationCenter otherwise. Wtf? - notificationCenter = NSNotificationCenter.defaultCenter; -}); - -qck_it(@"should send the notification when posted by any object", ^{ - RACSignal *signal = [notificationCenter rac_addObserverForName:TestNotification object:nil]; - - __block NSUInteger count = 0; - [signal subscribeNext:^(NSNotification *notification) { - ++count; - - expect(notification).to(beAKindOf(NSNotification.class)); - expect(notification.name).to(equal(TestNotification)); - }]; - - expect(@(count)).to(equal(@0)); - - [notificationCenter postNotificationName:TestNotification object:nil]; - expect(@(count)).to(equal(@1)); - - [notificationCenter postNotificationName:TestNotification object:self]; - expect(@(count)).to(equal(@2)); -}); - -qck_it(@"should send the notification when posted by a specific object", ^{ - RACSignal *signal = [notificationCenter rac_addObserverForName:TestNotification object:self]; - - __block NSUInteger count = 0; - [signal subscribeNext:^(NSNotification *notification) { - ++count; - - expect(notification).to(beAKindOf(NSNotification.class)); - expect(notification.name).to(equal(TestNotification)); - expect(notification.object).to(beIdenticalTo(self)); - }]; - - expect(@(count)).to(equal(@0)); - - [notificationCenter postNotificationName:TestNotification object:nil]; - expect(@(count)).to(equal(@0)); - - [notificationCenter postNotificationName:TestNotification object:self]; - expect(@(count)).to(equal(@1)); -}); - -qck_it(@"shouldn't strongly capture the notification object", ^{ - RACSignal *signal __attribute__((objc_precise_lifetime, unused)); - - __block BOOL dealloced = NO; - @autoreleasepool { - NSObject *notificationObject = [[NSObject alloc] init]; - [notificationObject.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - dealloced = YES; - }]]; - - signal = [notificationCenter rac_addObserverForName:TestNotification object:notificationObject]; - } - - expect(@(dealloced)).to(beTruthy()); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/NSObjectRACAppKitBindingsSpec.m b/ReactiveObjCTests/NSObjectRACAppKitBindingsSpec.m deleted file mode 100644 index 7ca2f391f5..0000000000 --- a/ReactiveObjCTests/NSObjectRACAppKitBindingsSpec.m +++ /dev/null @@ -1,39 +0,0 @@ -// -// NSObjectRACAppKitBindingsSpec.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-07-01. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACChannelExamples.h" - -#import -#import "NSObject+RACAppKitBindings.h" - -QuickSpecBegin(NSObjectRACAppKitBindingsSpec) - -qck_itBehavesLike(RACViewChannelExamples, ^{ - return @{ - RACViewChannelExampleCreateViewBlock: ^{ - return [[NSSlider alloc] initWithFrame:NSZeroRect]; - }, - RACViewChannelExampleCreateTerminalBlock: ^(NSSlider *view) { - return [view rac_channelToBinding:NSValueBinding]; - }, - RACViewChannelExampleKeyPath: @keypath(NSSlider.new, objectValue), - RACViewChannelExampleSetViewValueBlock: ^(NSSlider *view, NSNumber *value) { - view.objectValue = value; - - // Bindings don't actually trigger from programmatic modification. Do it - // manually. - NSDictionary *bindingInfo = [view infoForBinding:NSValueBinding]; - [bindingInfo[NSObservedObjectKey] setValue:value forKeyPath:bindingInfo[NSObservedKeyPathKey]]; - } - }; -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/NSObjectRACDeallocatingSpec.m b/ReactiveObjCTests/NSObjectRACDeallocatingSpec.m deleted file mode 100644 index 8f017f7ff2..0000000000 --- a/ReactiveObjCTests/NSObjectRACDeallocatingSpec.m +++ /dev/null @@ -1,198 +0,0 @@ -// -// NSObject+RACDeallocating.m -// ReactiveObjC -// -// Created by Kazuo Koga on 2013/03/15. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACTestObject.h" - -#import "NSObject+RACDeallocating.h" -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACSignal+Operations.h" -#import - -@interface RACDeallocSwizzlingTestClass : NSObject -@end - -@implementation RACDeallocSwizzlingTestClass - -- (void)dealloc { - // Provide an empty implementation just so we can swizzle it. -} - -@end - -@interface RACDeallocSwizzlingTestSubclass : RACDeallocSwizzlingTestClass -@end - -@implementation RACDeallocSwizzlingTestSubclass -@end - -QuickSpecBegin(NSObjectRACDeallocatingSpec) - -qck_describe(@"-dealloc swizzling", ^{ - SEL selector = NSSelectorFromString(@"dealloc"); - - qck_it(@"should not invoke superclass -dealloc method twice", ^{ - __block NSUInteger superclassDeallocatedCount = 0; - __block BOOL subclassDeallocated = NO; - - @autoreleasepool { - RACDeallocSwizzlingTestSubclass *object __attribute__((objc_precise_lifetime)) = [[RACDeallocSwizzlingTestSubclass alloc] init]; - - Method oldDeallocMethod = class_getInstanceMethod(RACDeallocSwizzlingTestClass.class, selector); - void (*oldDealloc)(id, SEL) = (__typeof__(oldDealloc))method_getImplementation(oldDeallocMethod); - - id newDealloc = ^(__unsafe_unretained id self) { - superclassDeallocatedCount++; - oldDealloc(self, selector); - }; - - class_replaceMethod(RACDeallocSwizzlingTestClass.class, selector, imp_implementationWithBlock(newDealloc), method_getTypeEncoding(oldDeallocMethod)); - - [object.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - subclassDeallocated = YES; - }]]; - - expect(@(subclassDeallocated)).to(beFalsy()); - expect(@(superclassDeallocatedCount)).to(equal(@0)); - } - - expect(@(subclassDeallocated)).to(beTruthy()); - expect(@(superclassDeallocatedCount)).to(equal(@1)); - }); - - qck_it(@"should invoke superclass -dealloc method swizzled in after the subclass", ^{ - __block BOOL superclassDeallocated = NO; - __block BOOL subclassDeallocated = NO; - - @autoreleasepool { - RACDeallocSwizzlingTestSubclass *object __attribute__((objc_precise_lifetime)) = [[RACDeallocSwizzlingTestSubclass alloc] init]; - [object.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - subclassDeallocated = YES; - }]]; - - Method oldDeallocMethod = class_getInstanceMethod(RACDeallocSwizzlingTestClass.class, selector); - void (*oldDealloc)(id, SEL) = (__typeof__(oldDealloc))method_getImplementation(oldDeallocMethod); - - id newDealloc = ^(__unsafe_unretained id self) { - superclassDeallocated = YES; - oldDealloc(self, selector); - }; - - class_replaceMethod(RACDeallocSwizzlingTestClass.class, selector, imp_implementationWithBlock(newDealloc), method_getTypeEncoding(oldDeallocMethod)); - - expect(@(subclassDeallocated)).to(beFalsy()); - expect(@(superclassDeallocated)).to(beFalsy()); - } - - expect(@(subclassDeallocated)).to(beTruthy()); - expect(@(superclassDeallocated)).to(beTruthy()); - }); -}); - -qck_describe(@"-rac_deallocDisposable", ^{ - qck_it(@"should dispose of the disposable when it is dealloc'd", ^{ - __block BOOL wasDisposed = NO; - @autoreleasepool { - NSObject *object __attribute__((objc_precise_lifetime)) = [[NSObject alloc] init]; - [object.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - wasDisposed = YES; - }]]; - - expect(@(wasDisposed)).to(beFalsy()); - } - - expect(@(wasDisposed)).to(beTruthy()); - }); - - qck_it(@"should be able to use the object during disposal", ^{ - @autoreleasepool { - RACTestObject *object __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init]; - - @autoreleasepool { - object.objectValue = [@"foo" mutableCopy]; - } - - __unsafe_unretained RACTestObject *weakObject = object; - - [object.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - expect(weakObject.objectValue).to(equal(@"foo")); - }]]; - } - }); -}); - -qck_describe(@"-rac_willDeallocSignal", ^{ - qck_it(@"should complete on dealloc", ^{ - __block BOOL completed = NO; - @autoreleasepool { - [[[[RACTestObject alloc] init] rac_willDeallocSignal] subscribeCompleted:^{ - completed = YES; - }]; - } - - expect(@(completed)).to(beTruthy()); - }); - - qck_it(@"should not send anything", ^{ - __block BOOL valueReceived = NO; - __block BOOL completed = NO; - @autoreleasepool { - [[[[RACTestObject alloc] init] rac_willDeallocSignal] subscribeNext:^(id x) { - valueReceived = YES; - } completed:^{ - completed = YES; - }]; - } - - expect(@(valueReceived)).to(beFalsy()); - expect(@(completed)).to(beTruthy()); - }); - - qck_it(@"should complete upon subscription if already deallocated", ^{ - __block BOOL deallocated = NO; - - RACSignal *signal; - - @autoreleasepool { - RACTestObject *object = [[RACTestObject alloc] init]; - - signal = [object rac_willDeallocSignal]; - [signal subscribeCompleted:^{ - deallocated = YES; - }]; - } - - expect(@(deallocated)).to(beTruthy()); - expect(@([signal waitUntilCompleted:NULL])).to(beTruthy()); - }); - - qck_it(@"should complete before the object is invalid", ^{ - __block NSString *objectValue; - - @autoreleasepool { - RACTestObject *object __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init]; - - @autoreleasepool { - object.objectValue = [@"foo" mutableCopy]; - } - - __unsafe_unretained RACTestObject *weakObject = object; - - [[object rac_willDeallocSignal] subscribeCompleted:^{ - objectValue = [weakObject.objectValue copy]; - }]; - } - - expect(objectValue).to(equal(@"foo")); - }); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/NSObjectRACLiftingSpec.m b/ReactiveObjCTests/NSObjectRACLiftingSpec.m deleted file mode 100644 index 071f8b421b..0000000000 --- a/ReactiveObjCTests/NSObjectRACLiftingSpec.m +++ /dev/null @@ -1,410 +0,0 @@ -// -// NSObjectRACLifting.m -// ReactiveObjC -// -// Created by Josh Abernathy on 10/2/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACTestObject.h" - -#import "NSObject+RACLifting.h" -#import "NSObject+RACDeallocating.h" -#import "NSObject+RACPropertySubscribing.h" -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACSubject.h" -#import "RACTuple.h" -#import "RACUnit.h" - -QuickSpecBegin(NSObjectRACLiftingSpec) - -qck_describe(@"-rac_liftSelector:withSignals:", ^{ - __block RACTestObject *object; - - qck_beforeEach(^{ - object = [[RACTestObject alloc] init]; - }); - - qck_it(@"should call the selector with the value of the signal", ^{ - RACSubject *subject = [RACSubject subject]; - [object rac_liftSelector:@selector(setObjectValue:) withSignals:subject, nil]; - - expect(object.objectValue).to(beNil()); - - [subject sendNext:@1]; - expect(object.objectValue).to(equal(@1)); - - [subject sendNext:@42]; - expect(object.objectValue).to(equal(@42)); - }); -}); - -qck_describe(@"-rac_liftSelector:withSignalsFromArray:", ^{ - __block RACTestObject *object; - - qck_beforeEach(^{ - object = [[RACTestObject alloc] init]; - }); - - qck_it(@"should call the selector with the value of the signal", ^{ - RACSubject *subject = [RACSubject subject]; - [object rac_liftSelector:@selector(setObjectValue:) withSignalsFromArray:@[ subject ]]; - - expect(object.objectValue).to(beNil()); - - [subject sendNext:@1]; - expect(object.objectValue).to(equal(@1)); - - [subject sendNext:@42]; - expect(object.objectValue).to(equal(@42)); - }); - - qck_it(@"should call the selector with the value of the signal unboxed", ^{ - RACSubject *subject = [RACSubject subject]; - [object rac_liftSelector:@selector(setIntegerValue:) withSignalsFromArray:@[ subject ]]; - - expect(@(object.integerValue)).to(equal(@0)); - - [subject sendNext:@1]; - expect(@(object.integerValue)).to(equal(@1)); - - [subject sendNext:@42]; - expect(@(object.integerValue)).to(equal(@42)); - }); - - qck_it(@"should work with multiple arguments", ^{ - RACSubject *objectValueSubject = [RACSubject subject]; - RACSubject *integerValueSubject = [RACSubject subject]; - [object rac_liftSelector:@selector(setObjectValue:andIntegerValue:) withSignalsFromArray:@[ objectValueSubject, integerValueSubject ]]; - - expect(@(object.hasInvokedSetObjectValueAndIntegerValue)).to(beFalsy()); - expect(object.objectValue).to(beNil()); - expect(@(object.integerValue)).to(equal(@0)); - - [objectValueSubject sendNext:@1]; - expect(@(object.hasInvokedSetObjectValueAndIntegerValue)).to(beFalsy()); - expect(object.objectValue).to(beNil()); - expect(@(object.integerValue)).to(equal(@0)); - - [integerValueSubject sendNext:@42]; - expect(@(object.hasInvokedSetObjectValueAndIntegerValue)).to(beTruthy()); - expect(object.objectValue).to(equal(@1)); - expect(@(object.integerValue)).to(equal(@42)); - }); - - qck_it(@"should work with signals that immediately start with a value", ^{ - RACSubject *subject = [RACSubject subject]; - [object rac_liftSelector:@selector(setObjectValue:) withSignalsFromArray:@[ [subject startWith:@42] ]]; - - expect(object.objectValue).to(equal(@42)); - - [subject sendNext:@1]; - expect(object.objectValue).to(equal(@1)); - }); - - qck_it(@"should work with signals that send nil", ^{ - RACSubject *subject = [RACSubject subject]; - [object rac_liftSelector:@selector(setObjectValue:) withSignalsFromArray:@[ subject ]]; - - [subject sendNext:nil]; - expect(object.objectValue).to(beNil()); - - [subject sendNext:RACTupleNil.tupleNil]; - expect(object.objectValue).to(beNil()); - }); - - qck_it(@"should work with integers", ^{ - RACSubject *subject = [RACSubject subject]; - [object rac_liftSelector:@selector(setIntegerValue:) withSignalsFromArray:@[ subject ]]; - - expect(@(object.integerValue)).to(equal(@0)); - - [subject sendNext:@1]; - expect(@(object.integerValue)).to(equal(@1)); - }); - - qck_it(@"should convert between numeric types", ^{ - RACSubject *subject = [RACSubject subject]; - [object rac_liftSelector:@selector(setIntegerValue:) withSignalsFromArray:@[ subject ]]; - - expect(@(object.integerValue)).to(equal(@0)); - - [subject sendNext:@1.0]; - expect(@(object.integerValue)).to(equal(@1)); - }); - - qck_it(@"should work with class objects", ^{ - RACSubject *subject = [RACSubject subject]; - [object rac_liftSelector:@selector(setObjectValue:) withSignalsFromArray:@[ subject ]]; - - expect(object.objectValue).to(beNil()); - - [subject sendNext:self.class]; - expect(object.objectValue).to(equal(self.class)); - }); - - qck_it(@"should work for char pointer", ^{ - RACSubject *subject = [RACSubject subject]; - [object rac_liftSelector:@selector(setCharPointerValue:) withSignalsFromArray:@[ subject ]]; - - expect(@((size_t)object.charPointerValue)).to(equal(@0)); - - NSString *string = @"blah blah blah"; - [subject sendNext:string]; - expect(@(object.charPointerValue)).to(equal(string)); - }); - - qck_it(@"should work for const char pointer", ^{ - RACSubject *subject = [RACSubject subject]; - [object rac_liftSelector:@selector(setConstCharPointerValue:) withSignalsFromArray:@[ subject ]]; - - expect(@((size_t)object.constCharPointerValue)).to(equal(@0)); - - NSString *string = @"blah blah blah"; - [subject sendNext:string]; - expect(@(object.constCharPointerValue)).to(equal(string)); - }); - - qck_it(@"should work for CGRect", ^{ - RACSubject *subject = [RACSubject subject]; - [object rac_liftSelector:@selector(setRectValue:) withSignalsFromArray:@[ subject ]]; - - expect(@(CGRectEqualToRect(object.rectValue, CGRectZero))).to(beTruthy()); - - CGRect value = CGRectMake(10, 20, 30, 40); - [subject sendNext:[NSValue valueWithBytes:&value objCType:@encode(CGRect)]]; - expect(@(CGRectEqualToRect(object.rectValue, value))).to(beTruthy()); - }); - - qck_it(@"should work for CGSize", ^{ - RACSubject *subject = [RACSubject subject]; - [object rac_liftSelector:@selector(setSizeValue:) withSignalsFromArray:@[ subject ]]; - - expect(@(CGSizeEqualToSize(object.sizeValue, CGSizeZero))).to(beTruthy()); - - CGSize value = CGSizeMake(10, 20); - [subject sendNext:[NSValue valueWithBytes:&value objCType:@encode(CGSize)]]; - expect(@(CGSizeEqualToSize(object.sizeValue, value))).to(beTruthy()); - }); - - qck_it(@"should work for CGPoint", ^{ - RACSubject *subject = [RACSubject subject]; - [object rac_liftSelector:@selector(setPointValue:) withSignalsFromArray:@[ subject ]]; - - expect(@(CGPointEqualToPoint(object.pointValue, CGPointZero))).to(beTruthy()); - - CGPoint value = CGPointMake(10, 20); - [subject sendNext:[NSValue valueWithBytes:&value objCType:@encode(CGPoint)]]; - expect(@(CGPointEqualToPoint(object.pointValue, value))).to(beTruthy()); - }); - - qck_it(@"should work for NSRange", ^{ - RACSubject *subject = [RACSubject subject]; - [object rac_liftSelector:@selector(setRangeValue:) withSignalsFromArray:@[ subject ]]; - - expect(@(NSEqualRanges(object.rangeValue, NSMakeRange(0, 0)))).to(beTruthy()); - - NSRange value = NSMakeRange(10, 20); - [subject sendNext:[NSValue valueWithRange:value]]; - expect(@(NSEqualRanges(object.rangeValue, value))).to(beTruthy()); - }); - - qck_it(@"should work for _Bool", ^{ - RACSubject *subject = [RACSubject subject]; - [object rac_liftSelector:@selector(setC99BoolValue:) withSignalsFromArray:@[ subject ]]; - - expect(@(object.c99BoolValue)).to(beFalsy()); - - _Bool value = true; - [subject sendNext:@(value)]; - expect(@(object.c99BoolValue)).to(beTruthy()); - }); - - qck_it(@"should work for primitive pointers", ^{ - RACSubject *subject = [RACSubject subject]; - [object rac_liftSelector:@selector(write5ToIntPointer:) withSignalsFromArray:@[ subject ]]; - - int value = 0; - int *valuePointer = &value; - expect(@(value)).to(equal(@0)); - - [subject sendNext:[NSValue valueWithPointer:valuePointer]]; - expect(@(value)).to(equal(@5)); - }); - - qck_it(@"should work for custom structs", ^{ - RACSubject *subject = [RACSubject subject]; - [object rac_liftSelector:@selector(setStructValue:) withSignalsFromArray:@[ subject ]]; - - expect(@(object.structValue.integerField)).to(equal(@0)); - expect(@(object.structValue.doubleField)).to(equal(@0.0)); - - RACTestStruct value = (RACTestStruct){7, 1.23}; - [subject sendNext:[NSValue valueWithBytes:&value objCType:@encode(typeof(value))]]; - expect(@(object.structValue.integerField)).to(equal(@(value.integerField))); - expect(@(object.structValue.doubleField)).to(equal(@(value.doubleField))); - }); - - qck_it(@"should send the latest value of the signal as the right argument", ^{ - RACSubject *subject = [RACSubject subject]; - [object rac_liftSelector:@selector(setObjectValue:andIntegerValue:) withSignalsFromArray:@[ [RACSignal return:@"object"], subject ]]; - [subject sendNext:@1]; - - expect(object.objectValue).to(equal(@"object")); - expect(@(object.integerValue)).to(equal(@1)); - }); - - qck_describe(@"the returned signal", ^{ - qck_it(@"should send the return value of the method invocation", ^{ - RACSubject *objectSubject = [RACSubject subject]; - RACSubject *integerSubject = [RACSubject subject]; - RACSignal *signal = [object rac_liftSelector:@selector(combineObjectValue:andIntegerValue:) withSignalsFromArray:@[ objectSubject, integerSubject ]]; - - __block NSString *result; - [signal subscribeNext:^(id x) { - result = x; - }]; - - [objectSubject sendNext:@"Magic number"]; - expect(result).to(beNil()); - - [integerSubject sendNext:@42]; - expect(result).to(equal(@"Magic number: 42")); - }); - - qck_it(@"should send RACUnit.defaultUnit for void-returning methods", ^{ - RACSubject *subject = [RACSubject subject]; - RACSignal *signal = [object rac_liftSelector:@selector(setObjectValue:) withSignalsFromArray:@[ subject ]]; - - __block id result; - [signal subscribeNext:^(id x) { - result = x; - }]; - - [subject sendNext:@1]; - - expect(result).to(equal(RACUnit.defaultUnit)); - }); - - qck_it(@"should support integer returning methods", ^{ - RACSubject *subject = [RACSubject subject]; - RACSignal *signal = [object rac_liftSelector:@selector(doubleInteger:) withSignalsFromArray:@[ subject ]]; - - __block id result; - [signal subscribeNext:^(id x) { - result = x; - }]; - - [subject sendNext:@1]; - - expect(result).to(equal(@2)); - }); - - qck_it(@"should support char * returning methods", ^{ - RACSubject *subject = [RACSubject subject]; - RACSignal *signal = [object rac_liftSelector:@selector(doubleString:) withSignalsFromArray:@[ subject ]]; - - __block id result; - [signal subscribeNext:^(id x) { - result = x; - }]; - - [subject sendNext:@"test"]; - - expect(result).to(equal(@"testtest")); - }); - - qck_it(@"should support const char * returning methods", ^{ - RACSubject *subject = [RACSubject subject]; - RACSignal *signal = [object rac_liftSelector:@selector(doubleConstString:) withSignalsFromArray:@[ subject ]]; - - __block id result; - [signal subscribeNext:^(id x) { - result = x; - }]; - - [subject sendNext:@"test"]; - - expect(result).to(equal(@"testtest")); - }); - - qck_it(@"should support struct returning methods", ^{ - RACSubject *subject = [RACSubject subject]; - RACSignal *signal = [object rac_liftSelector:@selector(doubleStruct:) withSignalsFromArray:@[ subject ]]; - - __block NSValue *boxedResult; - [signal subscribeNext:^(id x) { - boxedResult = x; - }]; - - RACTestStruct value = {4, 12.3}; - NSValue *boxedValue = [NSValue valueWithBytes:&value objCType:@encode(typeof(value))]; - [subject sendNext:boxedValue]; - - RACTestStruct result = {0, 0.0}; - [boxedResult getValue:&result]; - expect(@(result.integerField)).to(equal(@8)); - expect(@(result.doubleField)).to(equal(@24.6)); - }); - - qck_it(@"should support block arguments and returns", ^{ - RACSubject *subject = [RACSubject subject]; - RACSignal *signal = [object rac_liftSelector:@selector(wrapBlock:) withSignalsFromArray:@[ subject ]]; - - __block BOOL blockInvoked = NO; - dispatch_block_t testBlock = ^{ - blockInvoked = YES; - }; - - __block dispatch_block_t result; - [signal subscribeNext:^(id x) { - result = x; - }]; - - [subject sendNext:testBlock]; - expect(result).notTo(beNil()); - - result(); - expect(@(blockInvoked)).to(beTruthy()); - }); - - qck_it(@"should replay the last value", ^{ - RACSubject *objectSubject = [RACSubject subject]; - RACSubject *integerSubject = [RACSubject subject]; - RACSignal *signal = [object rac_liftSelector:@selector(combineObjectValue:andIntegerValue:) withSignalsFromArray:@[ objectSubject, integerSubject ]]; - - [objectSubject sendNext:@"Magic number"]; - [integerSubject sendNext:@42]; - [integerSubject sendNext:@43]; - - __block NSString *result; - [signal subscribeNext:^(id x) { - result = x; - }]; - - expect(result).to(equal(@"Magic number: 43")); - }); - }); - - qck_it(@"shouldn't strongly capture the receiver", ^{ - __block BOOL dealloced = NO; - @autoreleasepool { - RACTestObject *testObject __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init]; - [testObject.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - dealloced = YES; - }]]; - - RACSubject *subject = [RACSubject subject]; - [testObject rac_liftSelector:@selector(setObjectValue:) withSignalsFromArray:@[ subject ]]; - [subject sendNext:@1]; - } - - expect(@(dealloced)).to(beTruthy()); - }); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/NSObjectRACPropertySubscribingExamples.h b/ReactiveObjCTests/NSObjectRACPropertySubscribingExamples.h deleted file mode 100644 index 6f0fa8809b..0000000000 --- a/ReactiveObjCTests/NSObjectRACPropertySubscribingExamples.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// NSObjectRACPropertySubscribingExamples.h -// ReactiveObjC -// -// Created by Josh Vera on 4/10/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -// The name of the shared examples for a signal-driven observation. -extern NSString * const RACPropertySubscribingExamples; - -// The block should have the signature: -// RACSignal * (^)(RACTestObject *testObject, NSString *keyPath, id observer) -// and should observe the value of the key path on testObject with observer. The value -// for this key should not be nil. -extern NSString * const RACPropertySubscribingExamplesSetupBlock; diff --git a/ReactiveObjCTests/NSObjectRACPropertySubscribingExamples.m b/ReactiveObjCTests/NSObjectRACPropertySubscribingExamples.m deleted file mode 100644 index bc19a22ce0..0000000000 --- a/ReactiveObjCTests/NSObjectRACPropertySubscribingExamples.m +++ /dev/null @@ -1,224 +0,0 @@ -// -// NSObjectRACPropertySubscribingExamples.m -// ReactiveObjC -// -// Created by Josh Vera on 4/10/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACTestObject.h" -#import "NSObjectRACPropertySubscribingExamples.h" - -#import -#import "NSObject+RACDeallocating.h" -#import "NSObject+RACPropertySubscribing.h" -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACSignal.h" - -NSString * const RACPropertySubscribingExamples = @"RACPropertySubscribingExamples"; -NSString * const RACPropertySubscribingExamplesSetupBlock = @"RACPropertySubscribingExamplesSetupBlock"; - -QuickConfigurationBegin(NSObjectRACPropertySubscribingExamples) - -+ (void)configure:(Configuration *)configuration { - sharedExamples(RACPropertySubscribingExamples, ^(QCKDSLSharedExampleContext exampleContext) { - __block RACSignal *(^signalBlock)(RACTestObject *object, NSString *keyPath, id observer); - - qck_beforeEach(^{ - signalBlock = exampleContext()[RACPropertySubscribingExamplesSetupBlock]; - }); - - qck_it(@"should send the current value once on subscription", ^{ - RACTestObject *object = [[RACTestObject alloc] init]; - RACSignal *signal = signalBlock(object, @keypath(object, objectValue), self); - NSMutableArray *values = [NSMutableArray array]; - - object.objectValue = @0; - [signal subscribeNext:^(id x) { - [values addObject:x]; - }]; - - expect(values).to(equal((@[ @0 ]))); - }); - - qck_it(@"should send the new value when it changes", ^{ - RACTestObject *object = [[RACTestObject alloc] init]; - RACSignal *signal = signalBlock(object, @keypath(object, objectValue), self); - NSMutableArray *values = [NSMutableArray array]; - - object.objectValue = @0; - [signal subscribeNext:^(id x) { - [values addObject:x]; - }]; - - expect(values).to(equal((@[ @0 ]))); - - object.objectValue = @1; - expect(values).to(equal((@[ @0, @1 ]))); - - }); - - qck_it(@"should stop observing when disposed", ^{ - RACTestObject *object = [[RACTestObject alloc] init]; - RACSignal *signal = signalBlock(object, @keypath(object, objectValue), self); - NSMutableArray *values = [NSMutableArray array]; - - object.objectValue = @0; - RACDisposable *disposable = [signal subscribeNext:^(id x) { - [values addObject:x]; - }]; - - object.objectValue = @1; - NSArray *expected = @[ @0, @1 ]; - expect(values).to(equal(expected)); - - [disposable dispose]; - object.objectValue = @2; - expect(values).to(equal(expected)); - }); - - qck_it(@"shouldn't send any more values after the observer is gone", ^{ - __block BOOL observerDealloced = NO; - RACTestObject *object = [[RACTestObject alloc] init]; - NSMutableArray *values = [NSMutableArray array]; - @autoreleasepool { - RACTestObject *observer __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init]; - [observer.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - observerDealloced = YES; - }]]; - - RACSignal *signal = signalBlock(object, @keypath(object, objectValue), observer); - object.objectValue = @1; - [signal subscribeNext:^(id x) { - [values addObject:x]; - }]; - } - - expect(@(observerDealloced)).to(beTruthy()); - - NSArray *expected = @[ @1 ]; - expect(values).to(equal(expected)); - - object.objectValue = @2; - expect(values).to(equal(expected)); - }); - - qck_it(@"shouldn't keep either object alive unnaturally long", ^{ - __block BOOL objectDealloced = NO; - __block BOOL scopeObjectDealloced = NO; - @autoreleasepool { - RACTestObject *object __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init]; - [object.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - objectDealloced = YES; - }]]; - RACTestObject *scopeObject __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init]; - [scopeObject.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - scopeObjectDealloced = YES; - }]]; - - RACSignal *signal = signalBlock(object, @keypath(object, objectValue), scopeObject); - - [signal subscribeNext:^(id _) { - - }]; - } - - expect(@(objectDealloced)).to(beTruthy()); - expect(@(scopeObjectDealloced)).to(beTruthy()); - }); - - qck_it(@"shouldn't keep the signal alive past the lifetime of the object", ^{ - __block BOOL objectDealloced = NO; - __block BOOL signalDealloced = NO; - @autoreleasepool { - RACTestObject *object __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init]; - [object.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - objectDealloced = YES; - }]]; - - RACSignal *signal = [signalBlock(object, @keypath(object, objectValue), self) map:^(id value) { - return value; - }]; - - [signal.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - signalDealloced = YES; - }]]; - - [signal subscribeNext:^(id _) { - - }]; - } - - expect(@(signalDealloced)).toEventually(beTruthy()); - expect(@(objectDealloced)).to(beTruthy()); - }); - - qck_it(@"shouldn't crash when the value is changed on a different queue", ^{ - __block id value; - @autoreleasepool { - RACTestObject *object __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init]; - - RACSignal *signal = signalBlock(object, @keypath(object, objectValue), self); - - [signal subscribeNext:^(id x) { - value = x; - }]; - - NSOperationQueue *queue = [[NSOperationQueue alloc] init]; - [queue addOperationWithBlock:^{ - object.objectValue = @1; - }]; - - [queue waitUntilAllOperationsAreFinished]; - } - - expect(value).toEventually(equal(@1)); - }); - - qck_describe(@"mutating collections", ^{ - __block RACTestObject *object; - __block NSMutableOrderedSet *lastValue; - __block NSMutableOrderedSet *proxySet; - - qck_beforeEach(^{ - object = [[RACTestObject alloc] init]; - object.objectValue = [NSMutableOrderedSet orderedSetWithObject:@1]; - - NSString *keyPath = @keypath(object, objectValue); - - [signalBlock(object, keyPath, self) subscribeNext:^(NSMutableOrderedSet *x) { - lastValue = x; - }]; - - proxySet = [object mutableOrderedSetValueForKey:keyPath]; - }); - - qck_it(@"sends the newest object when inserting values into an observed object", ^{ - NSMutableOrderedSet *expected = [NSMutableOrderedSet orderedSetWithObjects: @1, @2, nil]; - - [proxySet addObject:@2]; - expect(lastValue).to(equal(expected)); - }); - - qck_it(@"sends the newest object when removing values in an observed object", ^{ - NSMutableOrderedSet *expected = [NSMutableOrderedSet orderedSet]; - - [proxySet removeAllObjects]; - expect(lastValue).to(equal(expected)); - }); - - qck_it(@"sends the newest object when replacing values in an observed object", ^{ - NSMutableOrderedSet *expected = [NSMutableOrderedSet orderedSetWithObjects: @2, nil]; - - [proxySet replaceObjectAtIndex:0 withObject:@2]; - expect(lastValue).to(equal(expected)); - }); - }); - }); -} - -QuickConfigurationEnd diff --git a/ReactiveObjCTests/NSObjectRACPropertySubscribingSpec.m b/ReactiveObjCTests/NSObjectRACPropertySubscribingSpec.m deleted file mode 100644 index 09af494271..0000000000 --- a/ReactiveObjCTests/NSObjectRACPropertySubscribingSpec.m +++ /dev/null @@ -1,158 +0,0 @@ -// -// NSObjectRACPropertySubscribingSpec.m -// ReactiveObjC -// -// Created by Josh Abernathy on 9/28/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "NSObjectRACPropertySubscribingExamples.h" -#import "RACTestObject.h" - -#import "NSObject+RACPropertySubscribing.h" -#import "RACDisposable.h" -#import "RACSignal.h" - -QuickSpecBegin(NSObjectRACPropertySubscribingSpec) - -qck_describe(@"-rac_valuesForKeyPath:observer:", ^{ - id (^setupBlock)(id, id, id) = ^(RACTestObject *object, NSString *keyPath, id observer) { - return [object rac_valuesForKeyPath:keyPath observer:observer]; - }; - - qck_itBehavesLike(RACPropertySubscribingExamples, ^{ - return @{ RACPropertySubscribingExamplesSetupBlock: setupBlock }; - }); - -}); - -qck_describe(@"+rac_signalWithChangesFor:keyPath:options:observer:", ^{ - qck_describe(@"KVO options argument", ^{ - __block RACTestObject *object; - __block id actual; - __block RACSignal *(^objectValueSignal)(NSKeyValueObservingOptions); - - qck_beforeEach(^{ - object = [[RACTestObject alloc] init]; - - objectValueSignal = ^(NSKeyValueObservingOptions options) { - return [[object rac_valuesAndChangesForKeyPath:@keypath(object, objectValue) options:options observer:self] reduceEach:^(id value, NSDictionary *change) { - return change; - }]; - }; - }); - - qck_it(@"sends a KVO dictionary", ^{ - [objectValueSignal(0) subscribeNext:^(NSDictionary *x) { - actual = x; - }]; - - object.objectValue = @1; - - expect(actual).to(beAKindOf(NSDictionary.class)); - }); - - qck_it(@"sends a kind key by default", ^{ - [objectValueSignal(0) subscribeNext:^(NSDictionary *x) { - actual = x[NSKeyValueChangeKindKey]; - }]; - - object.objectValue = @1; - - expect(actual).notTo(beNil()); - }); - - qck_it(@"sends the newest changes with NSKeyValueObservingOptionNew", ^{ - [objectValueSignal(NSKeyValueObservingOptionNew) subscribeNext:^(NSDictionary *x) { - actual = x[NSKeyValueChangeNewKey]; - }]; - - object.objectValue = @1; - expect(actual).to(equal(@1)); - - object.objectValue = @2; - expect(actual).to(equal(@2)); - }); - - qck_it(@"sends an additional change value with NSKeyValueObservingOptionPrior", ^{ - NSMutableArray *values = [NSMutableArray new]; - NSArray *expected = @[ @(YES), @(NO) ]; - - [objectValueSignal(NSKeyValueObservingOptionPrior) subscribeNext:^(NSDictionary *x) { - BOOL isPrior = [x[NSKeyValueChangeNotificationIsPriorKey] boolValue]; - [values addObject:@(isPrior)]; - }]; - - object.objectValue = @[ @1 ]; - - expect(values).to(equal(expected)); - }); - - qck_it(@"sends index changes when adding, inserting or removing a value from an observed object", ^{ - __block NSUInteger hasIndexesCount = 0; - - [objectValueSignal(0) subscribeNext:^(NSDictionary *x) { - if (x[NSKeyValueChangeIndexesKey] != nil) { - hasIndexesCount += 1; - } - }]; - - object.objectValue = [NSMutableOrderedSet orderedSet]; - expect(@(hasIndexesCount)).to(equal(@0)); - - NSMutableOrderedSet *objectValue = [object mutableOrderedSetValueForKey:@"objectValue"]; - - [objectValue addObject:@1]; - expect(@(hasIndexesCount)).to(equal(@1)); - - [objectValue replaceObjectAtIndex:0 withObject:@2]; - expect(@(hasIndexesCount)).to(equal(@2)); - - [objectValue removeObject:@2]; - expect(@(hasIndexesCount)).to(equal(@3)); - }); - - qck_it(@"sends the previous value with NSKeyValueObservingOptionOld", ^{ - [objectValueSignal(NSKeyValueObservingOptionOld) subscribeNext:^(NSDictionary *x) { - actual = x[NSKeyValueChangeOldKey]; - }]; - - object.objectValue = @1; - expect(actual).to(equal(NSNull.null)); - - object.objectValue = @2; - expect(actual).to(equal(@1)); - }); - - qck_it(@"sends the initial value with NSKeyValueObservingOptionInitial", ^{ - [objectValueSignal(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) subscribeNext:^(NSDictionary *x) { - actual = x[NSKeyValueChangeNewKey]; - }]; - - expect(actual).to(equal(NSNull.null)); - }); - }); -}); - -qck_describe(@"-rac_valuesAndChangesForKeyPath:options:observer:", ^{ - qck_it(@"should complete immediately if the receiver or observer have deallocated", ^{ - RACSignal *signal; - @autoreleasepool { - RACTestObject *object __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init]; - RACTestObject *observer __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init]; - signal = [object rac_valuesAndChangesForKeyPath:@keypath(object, stringValue) options:0 observer:observer]; - } - - __block BOOL completed = NO; - [signal subscribeCompleted:^{ - completed = YES; - }]; - - expect(@(completed)).to(beTruthy()); - }); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/NSObjectRACSelectorSignalSpec.m b/ReactiveObjCTests/NSObjectRACSelectorSignalSpec.m deleted file mode 100644 index 2ca24aed76..0000000000 --- a/ReactiveObjCTests/NSObjectRACSelectorSignalSpec.m +++ /dev/null @@ -1,501 +0,0 @@ -// -// NSObjectRACSelectorSignalSpec.m -// ReactiveObjC -// -// Created by Josh Abernathy on 3/18/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACTestObject.h" -#import "RACSubclassObject.h" - -#import "NSObject+RACDeallocating.h" -#import "NSObject+RACPropertySubscribing.h" -#import "NSObject+RACSelectorSignal.h" -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACMulticastConnection.h" -#import "RACSignal+Operations.h" -#import "RACSignal.h" -#import "RACTuple.h" - -@protocol TestProtocol - -@required -- (BOOL)requiredMethod:(NSUInteger)number; -- (void)lifeIsGood:(id)sender; - -@optional -- (NSUInteger)optionalMethodWithObject:(id)object flag:(BOOL)flag; -- (id)objectValue; - -@end - -QuickSpecBegin(NSObjectRACSelectorSignalSpec) - -qck_describe(@"RACTestObject", ^{ - qck_it(@"should send the argument for each invocation", ^{ - RACTestObject *object = [[RACTestObject alloc] init]; - __block id value; - [[object rac_signalForSelector:@selector(lifeIsGood:)] subscribeNext:^(RACTuple *x) { - value = x.first; - }]; - - [object lifeIsGood:@42]; - - expect(value).to(equal(@42)); - }); - - qck_it(@"should send completed on deallocation", ^{ - __block BOOL completed = NO; - __block BOOL deallocated = NO; - - @autoreleasepool { - RACTestObject *object __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init]; - - [object.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - deallocated = YES; - }]]; - - [[object rac_signalForSelector:@selector(lifeIsGood:)] subscribeCompleted:^{ - completed = YES; - }]; - - expect(@(deallocated)).to(beFalsy()); - expect(@(completed)).to(beFalsy()); - } - - expect(@(deallocated)).to(beTruthy()); - expect(@(completed)).to(beTruthy()); - }); - - qck_it(@"should send for a zero-argument method", ^{ - RACTestObject *object = [[RACTestObject alloc] init]; - - __block RACTuple *value; - [[object rac_signalForSelector:@selector(objectValue)] subscribeNext:^(RACTuple *x) { - value = x; - }]; - - (void)[object objectValue]; - expect(value).to(equal([RACTuple tupleWithObjectsFromArray:@[]])); - }); - - qck_it(@"should send the argument for each invocation to the instance's own signal", ^{ - RACTestObject *object1 = [[RACTestObject alloc] init]; - __block id value1; - [[object1 rac_signalForSelector:@selector(lifeIsGood:)] subscribeNext:^(RACTuple *x) { - value1 = x.first; - }]; - - RACTestObject *object2 = [[RACTestObject alloc] init]; - __block id value2; - [[object2 rac_signalForSelector:@selector(lifeIsGood:)] subscribeNext:^(RACTuple *x) { - value2 = x.first; - }]; - - [object1 lifeIsGood:@42]; - [object2 lifeIsGood:@"Carpe diem"]; - - expect(value1).to(equal(@42)); - expect(value2).to(equal(@"Carpe diem")); - }); - - qck_it(@"should send multiple arguments for each invocation", ^{ - RACTestObject *object = [[RACTestObject alloc] init]; - - __block id value1; - __block id value2; - [[object rac_signalForSelector:@selector(combineObjectValue:andSecondObjectValue:)] subscribeNext:^(RACTuple *x) { - value1 = x.first; - value2 = x.second; - }]; - - expect([object combineObjectValue:@42 andSecondObjectValue:@"foo"]).to(equal(@"42: foo")); - expect(value1).to(equal(@42)); - expect(value2).to(equal(@"foo")); - }); - - qck_it(@"should send arguments for invocation of non-existant methods", ^{ - RACTestObject *object = [[RACTestObject alloc] init]; - __block id key; - __block id value; - [[object rac_signalForSelector:@selector(setObject:forKey:)] subscribeNext:^(RACTuple *x) { - value = x.first; - key = x.second; - }]; - - [object performSelector:@selector(setObject:forKey:) withObject:@YES withObject:@"Winner"]; - - expect(value).to(equal(@YES)); - expect(key).to(equal(@"Winner")); - }); - - qck_it(@"should send arguments for invocation and invoke the original method on previously KVO'd receiver", ^{ - RACTestObject *object = [[RACTestObject alloc] init]; - - [[RACObserve(object, objectValue) publish] connect]; - - __block id key; - __block id value; - [[object rac_signalForSelector:@selector(setObjectValue:andSecondObjectValue:)] subscribeNext:^(RACTuple *x) { - value = x.first; - key = x.second; - }]; - - [object setObjectValue:@YES andSecondObjectValue:@"Winner"]; - - expect(@(object.hasInvokedSetObjectValueAndSecondObjectValue)).to(beTruthy()); - expect(object.objectValue).to(equal(@YES)); - expect(object.secondObjectValue).to(equal(@"Winner")); - - expect(value).to(equal(@YES)); - expect(key).to(equal(@"Winner")); - }); - - qck_it(@"should send arguments for invocation and invoke the original method when receiver is subsequently KVO'd", ^{ - RACTestObject *object = [[RACTestObject alloc] init]; - - __block id key; - __block id value; - [[object rac_signalForSelector:@selector(setObjectValue:andSecondObjectValue:)] subscribeNext:^(RACTuple *x) { - value = x.first; - key = x.second; - }]; - - [[RACObserve(object, objectValue) publish] connect]; - - [object setObjectValue:@YES andSecondObjectValue:@"Winner"]; - - expect(@(object.hasInvokedSetObjectValueAndSecondObjectValue)).to(beTruthy()); - expect(object.objectValue).to(equal(@YES)); - expect(object.secondObjectValue).to(equal(@"Winner")); - - expect(value).to(equal(@YES)); - expect(key).to(equal(@"Winner")); - }); - - qck_it(@"should properly implement -respondsToSelector: when called on KVO'd receiver", ^{ - RACTestObject *object = [[RACTestObject alloc] init]; - - // First, setup KVO on `object`, which gives us the desired side-effect - // of `object` taking on a KVO-custom subclass. - [[RACObserve(object, objectValue) publish] connect]; - - SEL selector = NSSelectorFromString(@"anyOldSelector:"); - - // With the KVO subclass in place, call -rac_signalForSelector: to - // implement -anyOldSelector: directly on the KVO subclass. - [object rac_signalForSelector:selector]; - - expect(@([object respondsToSelector:selector])).to(beTruthy()); - }); - - qck_it(@"should properly implement -respondsToSelector: when called on signalForSelector'd receiver that has subsequently been KVO'd", ^{ - RACTestObject *object = [[RACTestObject alloc] init]; - - SEL selector = NSSelectorFromString(@"anyOldSelector:"); - - // Implement -anyOldSelector: on the object first - [object rac_signalForSelector:selector]; - - expect(@([object respondsToSelector:selector])).to(beTruthy()); - - // Then KVO the object - [[RACObserve(object, objectValue) publish] connect]; - - expect(@([object respondsToSelector:selector])).to(beTruthy()); - }); - - qck_it(@"should properly implement -respondsToSelector: when called on signalForSelector'd receiver that has subsequently been KVO'd, then signalForSelector'd again", ^{ - RACTestObject *object = [[RACTestObject alloc] init]; - - SEL selector = NSSelectorFromString(@"anyOldSelector:"); - - // Implement -anyOldSelector: on the object first - [object rac_signalForSelector:selector]; - - expect(@([object respondsToSelector:selector])).to(beTruthy()); - - // Then KVO the object - [[RACObserve(object, objectValue) publish] connect]; - - expect(@([object respondsToSelector:selector])).to(beTruthy()); - - SEL selector2 = NSSelectorFromString(@"anotherSelector:"); - - // Then implement -anotherSelector: on the object - [object rac_signalForSelector:selector2]; - - expect(@([object respondsToSelector:selector2])).to(beTruthy()); - }); - - qck_it(@"should call the right signal for two instances of the same class, adding signals for the same selector", ^{ - RACTestObject *object1 = [[RACTestObject alloc] init]; - RACTestObject *object2 = [[RACTestObject alloc] init]; - - SEL selector = NSSelectorFromString(@"lifeIsGood:"); - - __block id value1 = nil; - [[object1 rac_signalForSelector:selector] subscribeNext:^(RACTuple *x) { - value1 = x.first; - }]; - - __block id value2 = nil; - [[object2 rac_signalForSelector:selector] subscribeNext:^(RACTuple *x) { - value2 = x.first; - }]; - - [object1 lifeIsGood:@42]; - expect(value1).to(equal(@42)); - expect(value2).to(beNil()); - - [object2 lifeIsGood:@420]; - expect(value1).to(equal(@42)); - expect(value2).to(equal(@420)); - }); - - qck_it(@"should properly implement -respondsToSelector: for optional method from a protocol", ^{ - // Selector for the targeted optional method from a protocol. - SEL selector = @selector(optionalProtocolMethodWithObjectValue:); - - RACTestObject *object1 = [[RACTestObject alloc] init]; - - // Method implementation of the selector is added to its swizzled class. - [object1 rac_signalForSelector:selector fromProtocol:@protocol(RACTestProtocol)]; - - expect(@([object1 respondsToSelector:selector])).to(beTruthy()); - - RACTestObject *object2 = [[RACTestObject alloc] init]; - - // Call -rac_signalForSelector: to swizzle this instance's class, - // method implementations of -respondsToSelector: and - // -forwardInvocation:. - [object2 rac_signalForSelector:@selector(lifeIsGood:)]; - - // This instance should not respond to the selector because of not - // calling -rac_signalForSelector: with the selector. - expect(@([object2 respondsToSelector:selector])).to(beFalsy()); - }); - - qck_it(@"should send non-object arguments", ^{ - RACTestObject *object = [[RACTestObject alloc] init]; - - __block id value; - [[object rac_signalForSelector:@selector(setIntegerValue:)] subscribeNext:^(RACTuple *x) { - value = x.first; - }]; - - object.integerValue = 42; - expect(value).to(equal(@42)); - }); - - qck_it(@"should send on signal after the original method is invoked", ^{ - RACTestObject *object = [[RACTestObject alloc] init]; - - __block BOOL invokedMethodBefore = NO; - [[object rac_signalForSelector:@selector(setObjectValue:andSecondObjectValue:)] subscribeNext:^(RACTuple *x) { - invokedMethodBefore = object.hasInvokedSetObjectValueAndSecondObjectValue; - }]; - - [object setObjectValue:@YES andSecondObjectValue:@"Winner"]; - expect(@(invokedMethodBefore)).to(beTruthy()); - }); -}); - -qck_it(@"should swizzle an NSObject method", ^{ - NSObject *object = [[NSObject alloc] init]; - - __block RACTuple *value; - [[object rac_signalForSelector:@selector(description)] subscribeNext:^(RACTuple *x) { - value = x; - }]; - - expect([object description]).notTo(beNil()); - expect(value).to(equal([RACTuple tupleWithObjectsFromArray:@[]])); -}); - -qck_describe(@"a class that already overrides -forwardInvocation:", ^{ - qck_it(@"should invoke the superclass' implementation", ^{ - RACSubclassObject *object = [[RACSubclassObject alloc] init]; - - __block id value; - [[object rac_signalForSelector:@selector(lifeIsGood:)] subscribeNext:^(RACTuple *x) { - value = x.first; - }]; - - [object lifeIsGood:@42]; - expect(value).to(equal(@42)); - - expect(@((size_t)(void*)object.forwardedSelector)).to(equal(@0)); - - [object performSelector:@selector(allObjects)]; - - expect(value).to(equal(@42)); - expect(NSStringFromSelector(object.forwardedSelector)).to(equal(@"allObjects")); - }); - - qck_it(@"should not infinite recurse when KVO'd after RAC swizzled", ^{ - RACSubclassObject *object = [[RACSubclassObject alloc] init]; - - __block id value; - [[object rac_signalForSelector:@selector(lifeIsGood:)] subscribeNext:^(RACTuple *x) { - value = x.first; - }]; - - [[RACObserve(object, objectValue) publish] connect]; - - [object lifeIsGood:@42]; - expect(value).to(equal(@42)); - - expect(@((size_t)(void*)object.forwardedSelector)).to(equal(@0)); - [object performSelector:@selector(allObjects)]; - expect(NSStringFromSelector(object.forwardedSelector)).to(equal(@"allObjects")); - }); -}); - -qck_describe(@"two classes in the same hierarchy", ^{ - __block RACTestObject *superclassObj; - __block RACTuple *superclassTuple; - - __block RACSubclassObject *subclassObj; - __block RACTuple *subclassTuple; - - qck_beforeEach(^{ - superclassObj = [[RACTestObject alloc] init]; - expect(superclassObj).notTo(beNil()); - - subclassObj = [[RACSubclassObject alloc] init]; - expect(subclassObj).notTo(beNil()); - }); - - qck_it(@"should not collide", ^{ - [[superclassObj rac_signalForSelector:@selector(combineObjectValue:andIntegerValue:)] subscribeNext:^(RACTuple *t) { - superclassTuple = t; - }]; - - [[subclassObj rac_signalForSelector:@selector(combineObjectValue:andIntegerValue:)] subscribeNext:^(RACTuple *t) { - subclassTuple = t; - }]; - - expect([superclassObj combineObjectValue:@"foo" andIntegerValue:42]).to(equal(@"foo: 42")); - - NSArray *expectedValues = @[ @"foo", @42 ]; - expect(superclassTuple.allObjects).to(equal(expectedValues)); - - expect([subclassObj combineObjectValue:@"foo" andIntegerValue:42]).to(equal(@"fooSUBCLASS: 42")); - - expectedValues = @[ @"foo", @42 ]; - expect(subclassTuple.allObjects).to(equal(expectedValues)); - }); - - qck_it(@"should not collide when the superclass is invoked asynchronously", ^{ - [[superclassObj rac_signalForSelector:@selector(setObjectValue:andSecondObjectValue:)] subscribeNext:^(RACTuple *t) { - superclassTuple = t; - }]; - - [[subclassObj rac_signalForSelector:@selector(setObjectValue:andSecondObjectValue:)] subscribeNext:^(RACTuple *t) { - subclassTuple = t; - }]; - - [superclassObj setObjectValue:@"foo" andSecondObjectValue:@"42"]; - expect(@(superclassObj.hasInvokedSetObjectValueAndSecondObjectValue)).to(beTruthy()); - - NSArray *expectedValues = @[ @"foo", @"42" ]; - expect(superclassTuple.allObjects).to(equal(expectedValues)); - - [subclassObj setObjectValue:@"foo" andSecondObjectValue:@"42"]; - expect(@(subclassObj.hasInvokedSetObjectValueAndSecondObjectValue)).to(beFalsy()); - expect(@(subclassObj.hasInvokedSetObjectValueAndSecondObjectValue)).toEventually(beTruthy()); - - expectedValues = @[ @"foo", @"42" ]; - expect(subclassTuple.allObjects).to(equal(expectedValues)); - }); -}); - -qck_describe(@"-rac_signalForSelector:fromProtocol", ^{ - __block RACTestObject *object; - __block Protocol *protocol; - - qck_beforeEach(^{ - object = (id)[[RACTestObject alloc] init]; - expect(object).notTo(beNil()); - - protocol = @protocol(TestProtocol); - expect(protocol).notTo(beNil()); - }); - - qck_it(@"should not clobber a required method already implemented", ^{ - __block id value; - [[object rac_signalForSelector:@selector(lifeIsGood:) fromProtocol:protocol] subscribeNext:^(RACTuple *x) { - value = x.first; - }]; - - [object lifeIsGood:@42]; - expect(value).to(equal(@42)); - }); - - qck_it(@"should not clobber an optional method already implemented", ^{ - object.objectValue = @"foo"; - - __block id value; - [[object rac_signalForSelector:@selector(objectValue) fromProtocol:protocol] subscribeNext:^(RACTuple *x) { - value = x; - }]; - - expect([object objectValue]).to(equal(@"foo")); - expect(value).to(equal([RACTuple tupleWithObjectsFromArray:@[]])); - }); - - qck_it(@"should inject a required method", ^{ - __block id value; - [[object rac_signalForSelector:@selector(requiredMethod:) fromProtocol:protocol] subscribeNext:^(RACTuple *x) { - value = x.first; - }]; - - expect(@([object requiredMethod:42])).to(beFalsy()); - expect(value).to(equal(@42)); - }); - - qck_it(@"should inject an optional method", ^{ - __block id value; - [[object rac_signalForSelector:@selector(optionalMethodWithObject:flag:) fromProtocol:protocol] subscribeNext:^(RACTuple *x) { - value = x; - }]; - - expect(@([object optionalMethodWithObject:@"foo" flag:YES])).to(equal(@0)); - expect(value).to(equal(RACTuplePack(@"foo", @YES))); - }); -}); - -qck_describe(@"class reporting", ^{ - __block RACTestObject *object; - __block Class originalClass; - - qck_beforeEach(^{ - object = [[RACTestObject alloc] init]; - originalClass = object.class; - }); - - qck_it(@"should report the original class", ^{ - [object rac_signalForSelector:@selector(lifeIsGood:)]; - expect(object.class).to(beIdenticalTo(originalClass)); - }); - - qck_it(@"should report the original class when it's KVO'd after dynamically subclassing", ^{ - [object rac_signalForSelector:@selector(lifeIsGood:)]; - [[RACObserve(object, objectValue) publish] connect]; - expect(object.class).to(beIdenticalTo(originalClass)); - }); - - qck_it(@"should report the original class when it's KVO'd before dynamically subclassing", ^{ - [[RACObserve(object, objectValue) publish] connect]; - [object rac_signalForSelector:@selector(lifeIsGood:)]; - expect(object.class).to(beIdenticalTo(originalClass)); - }); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/NSStringRACKeyPathUtilitiesSpec.m b/ReactiveObjCTests/NSStringRACKeyPathUtilitiesSpec.m deleted file mode 100644 index 1062436668..0000000000 --- a/ReactiveObjCTests/NSStringRACKeyPathUtilitiesSpec.m +++ /dev/null @@ -1,54 +0,0 @@ -// -// NSStringRACKeyPathUtilitiesSpec.m -// ReactiveObjC -// -// Created by Uri Baghin on 05/05/2013. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "NSString+RACKeyPathUtilities.h" - -QuickSpecBegin(NSStringRACKeyPathUtilitiesSpec) - -qck_describe(@"-keyPathComponents", ^{ - qck_it(@"should return components in the key path", ^{ - expect(@"self.test.key.path".rac_keyPathComponents).to(equal((@[@"self", @"test", @"key", @"path"]))); - }); - - qck_it(@"should return nil if given an empty string", ^{ - expect(@"".rac_keyPathComponents).to(beNil()); - }); -}); - -qck_describe(@"-keyPathByDeletingLastKeyPathComponent", ^{ - qck_it(@"should return the parent key path", ^{ - expect(@"grandparent.parent.child".rac_keyPathByDeletingLastKeyPathComponent).to(equal(@"grandparent.parent")); - }); - - qck_it(@"should return nil if given an empty string", ^{ - expect(@"".rac_keyPathByDeletingLastKeyPathComponent).to(beNil()); - }); - - qck_it(@"should return nil if given a key path with only one component", ^{ - expect(@"self".rac_keyPathByDeletingLastKeyPathComponent).to(beNil()); - }); -}); - -qck_describe(@"-keyPathByDeletingFirstKeyPathComponent", ^{ - qck_it(@"should return the remaining key path", ^{ - expect(@"first.second.third".rac_keyPathByDeletingFirstKeyPathComponent).to(equal(@"second.third")); - }); - - qck_it(@"should return nil if given an empty string", ^{ - expect(@"".rac_keyPathByDeletingFirstKeyPathComponent).to(beNil()); - }); - - qck_it(@"should return nil if given a key path with only one component", ^{ - expect(@"self".rac_keyPathByDeletingFirstKeyPathComponent).to(beNil()); - }); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/NSURLConnectionRACSupportSpec.m b/ReactiveObjCTests/NSURLConnectionRACSupportSpec.m deleted file mode 100644 index a5e47e1440..0000000000 --- a/ReactiveObjCTests/NSURLConnectionRACSupportSpec.m +++ /dev/null @@ -1,39 +0,0 @@ -// -// NSURLConnectionRACSupportSpec.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-10-01. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "NSURLConnection+RACSupport.h" -#import "RACSignal+Operations.h" -#import "RACTuple.h" - -QuickSpecBegin(NSURLConnectionRACSupportSpec) - -qck_it(@"should fetch a JSON file", ^{ - NSURL *fileURL = [[NSBundle bundleForClass:self.class] URLForResource:@"test-data" withExtension:@"json"]; - expect(fileURL).notTo(beNil()); - - NSURLRequest *request = [NSURLRequest requestWithURL:fileURL]; - - BOOL success = NO; - NSError *error = nil; - RACTuple *result = [[NSURLConnection rac_sendAsynchronousRequest:request] firstOrDefault:nil success:&success error:&error]; - expect(@(success)).to(beTruthy()); - expect(error).to(beNil()); - expect(result).to(beAKindOf(RACTuple.class)); - - NSURLResponse *response = result.first; - expect(response).to(beAKindOf(NSURLResponse.class)); - - NSData *data = result.second; - expect(data).to(beAKindOf(NSData.class)); - expect(data).to(equal([NSData dataWithContentsOfURL:fileURL])); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/NSUserDefaultsRACSupportSpec.m b/ReactiveObjCTests/NSUserDefaultsRACSupportSpec.m deleted file mode 100644 index a2a0a36120..0000000000 --- a/ReactiveObjCTests/NSUserDefaultsRACSupportSpec.m +++ /dev/null @@ -1,137 +0,0 @@ -// -// NSUserDefaultsRACSupportSpec.m -// ReactiveObjC -// -// Created by Matt Diephouse on 12/19/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "NSUserDefaults+RACSupport.h" - -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACKVOChannel.h" -#import "NSObject+RACDeallocating.h" -#import "RACSignal+Operations.h" - -static NSString * const NSUserDefaultsRACSupportSpecStringDefault = @"NSUserDefaultsRACSupportSpecStringDefault"; -static NSString * const NSUserDefaultsRACSupportSpecBoolDefault = @"NSUserDefaultsRACSupportSpecBoolDefault"; - -@interface TestObserver : NSObject - -@property (copy, atomic) NSString *string1; -@property (copy, atomic) NSString *string2; - -@property (assign, atomic) BOOL bool1; - -@end - -@implementation TestObserver - -@end - -QuickSpecBegin(NSUserDefaultsRACSupportSpec) - -__block NSUserDefaults *defaults = nil; -__block TestObserver *observer = nil; - -qck_beforeEach(^{ - defaults = NSUserDefaults.standardUserDefaults; - - observer = [TestObserver new]; -}); - -qck_afterEach(^{ - [defaults removeObjectForKey:NSUserDefaultsRACSupportSpecStringDefault]; - [defaults removeObjectForKey:NSUserDefaultsRACSupportSpecBoolDefault]; - - observer = nil; -}); - -qck_it(@"should set defaults", ^{ - RACChannelTo(observer, string1) = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecStringDefault]; - RACChannelTo(observer, bool1, @NO) = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecBoolDefault]; - - observer.string1 = @"A string"; - observer.bool1 = YES; - - expect([defaults objectForKey:NSUserDefaultsRACSupportSpecStringDefault]).toEventually(equal(@"A string")); - expect([defaults objectForKey:NSUserDefaultsRACSupportSpecBoolDefault]).toEventually(equal(@YES)); -}); - -qck_it(@"should read defaults", ^{ - RACChannelTo(observer, string1) = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecStringDefault]; - RACChannelTo(observer, bool1, @NO) = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecBoolDefault]; - - expect(observer.string1).to(beNil()); - expect(@(observer.bool1)).to(equal(@NO)); - - [defaults setObject:@"Another string" forKey:NSUserDefaultsRACSupportSpecStringDefault]; - [defaults setBool:YES forKey:NSUserDefaultsRACSupportSpecBoolDefault]; - - expect(observer.string1).to(equal(@"Another string")); - expect(@(observer.bool1)).to(equal(@YES)); -}); - -qck_it(@"should be okay to create 2 terminals", ^{ - RACChannelTo(observer, string1) = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecStringDefault]; - RACChannelTo(observer, string2) = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecStringDefault]; - - [defaults setObject:@"String 3" forKey:NSUserDefaultsRACSupportSpecStringDefault]; - - expect(observer.string1).to(equal(@"String 3")); - expect(observer.string2).to(equal(@"String 3")); -}); - -qck_it(@"should handle removed defaults", ^{ - observer.string1 = @"Some string"; - observer.bool1 = YES; - - RACChannelTo(observer, string1) = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecStringDefault]; - RACChannelTo(observer, bool1, @NO) = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecBoolDefault]; - - [defaults removeObjectForKey:NSUserDefaultsRACSupportSpecStringDefault]; - [defaults removeObjectForKey:NSUserDefaultsRACSupportSpecBoolDefault]; - - expect(observer.string1).to(beNil()); - expect(@(observer.bool1)).to(equal(@NO)); -}); - -qck_it(@"shouldn't resend values", ^{ - RACChannelTerminal *terminal = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecStringDefault]; - - RACChannelTo(observer, string1) = terminal; - - RACSignal *sentValue = [terminal replayLast]; - observer.string1 = @"Test value"; - id value = [sentValue asynchronousFirstOrDefault:nil success:NULL error:NULL]; - expect(value).to(beNil()); -}); - -qck_it(@"should complete when the NSUserDefaults deallocates", ^{ - __block RACChannelTerminal *terminal; - __block BOOL deallocated = NO; - - @autoreleasepool { - NSUserDefaults *customDefaults __attribute__((objc_precise_lifetime)) = [NSUserDefaults new]; - [customDefaults.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - deallocated = YES; - }]]; - - terminal = [customDefaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecStringDefault]; - } - - expect(@(deallocated)).to(beTruthy()); - expect(@([terminal asynchronouslyWaitUntilCompleted:NULL])).to(beTruthy()); -}); - -qck_it(@"should send an initial value", ^{ - [defaults setObject:@"Initial" forKey:NSUserDefaultsRACSupportSpecStringDefault]; - RACChannelTerminal *terminal = [defaults rac_channelTerminalForKey:NSUserDefaultsRACSupportSpecStringDefault]; - expect([terminal asynchronousFirstOrDefault:nil success:NULL error:NULL]).to(equal(@"Initial")); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/RACBlockTrampolineSpec.m b/ReactiveObjCTests/RACBlockTrampolineSpec.m deleted file mode 100644 index ffe4def6c2..0000000000 --- a/ReactiveObjCTests/RACBlockTrampolineSpec.m +++ /dev/null @@ -1,51 +0,0 @@ -// -// RACBlockTrampolineSpec.m -// ReactiveObjC -// -// Created by Josh Abernathy on 10/28/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACBlockTrampoline.h" -#import "RACTuple.h" - -QuickSpecBegin(RACBlockTrampolineSpec) - -qck_it(@"should invoke the block with the given arguments", ^{ - __block NSString *stringArg; - __block NSNumber *numberArg; - id (^block)(NSString *, NSNumber *) = ^ id (NSString *string, NSNumber *number) { - stringArg = string; - numberArg = number; - return nil; - }; - - [RACBlockTrampoline invokeBlock:block withArguments:RACTuplePack(@"hi", @1)]; - expect(stringArg).to(equal(@"hi")); - expect(numberArg).to(equal(@1)); -}); - -qck_it(@"should return the result of the block invocation", ^{ - NSString * (^block)(NSString *) = ^(NSString *string) { - return string.uppercaseString; - }; - - NSString *result = [RACBlockTrampoline invokeBlock:block withArguments:RACTuplePack(@"hi")]; - expect(result).to(equal(@"HI")); -}); - -qck_it(@"should pass RACTupleNils as nil", ^{ - __block id arg; - id (^block)(id) = ^ id (id obj) { - arg = obj; - return nil; - }; - - [RACBlockTrampoline invokeBlock:block withArguments:RACTuplePack(nil)]; - expect(arg).to(beNil()); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/RACChannelExamples.h b/ReactiveObjCTests/RACChannelExamples.h deleted file mode 100644 index 16a44a6c20..0000000000 --- a/ReactiveObjCTests/RACChannelExamples.h +++ /dev/null @@ -1,34 +0,0 @@ -// -// RACChannelExamples.h -// ReactiveObjC -// -// Created by Uri Baghin on 30/12/2012. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -// The name of the shared examples for RACChannel and its subclasses. -extern NSString * const RACChannelExamples; - -// A block of type `RACChannel * (^)(void)`, which should return a new -// RACChannel. -extern NSString * const RACChannelExampleCreateBlock; - -// The name of the shared examples for any RACChannel class that gets and sets -// a property. -extern NSString * const RACViewChannelExamples; - -// A block of type `NSObject * (^)(void)`, which should create a new test view -// and return it. -extern NSString * const RACViewChannelExampleCreateViewBlock; - -// A block of type `RACChannelTerminal * (^)(NSObject *view)`, which should -// create a new RACChannel to the given test view and return an terminal. -extern NSString * const RACViewChannelExampleCreateTerminalBlock; - -// The key path that will be read/written in RACViewChannelExamples. This -// must lead to an NSNumber or numeric primitive property. -extern NSString * const RACViewChannelExampleKeyPath; - -// A block of type `void (^)(NSObject *view, NSNumber *value)`, which should -// change the given test view's value to the given one. -extern NSString * const RACViewChannelExampleSetViewValueBlock; diff --git a/ReactiveObjCTests/RACChannelExamples.m b/ReactiveObjCTests/RACChannelExamples.m deleted file mode 100644 index ac672cef59..0000000000 --- a/ReactiveObjCTests/RACChannelExamples.m +++ /dev/null @@ -1,299 +0,0 @@ -// -// RACChannelExamples.m -// ReactiveObjC -// -// Created by Uri Baghin on 30/12/2012. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACChannelExamples.h" - -#import "NSObject+RACDeallocating.h" -#import "NSObject+RACPropertySubscribing.h" -#import "RACChannel.h" -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACSignal+Operations.h" - -NSString * const RACChannelExamples = @"RACChannelExamples"; -NSString * const RACChannelExampleCreateBlock = @"RACChannelExampleCreateBlock"; - -NSString * const RACViewChannelExamples = @"RACViewChannelExamples"; -NSString * const RACViewChannelExampleCreateViewBlock = @"RACViewChannelExampleCreateViewBlock"; -NSString * const RACViewChannelExampleCreateTerminalBlock = @"RACViewChannelExampleCreateTerminalBlock"; -NSString * const RACViewChannelExampleKeyPath = @"RACViewChannelExampleKeyPath"; -NSString * const RACViewChannelExampleSetViewValueBlock = @"RACViewChannelExampleSetViewValueBlock"; - -QuickConfigurationBegin(RACChannelExampleGroups) - -+ (void)configure:(Configuration *)configuration { - sharedExamples(RACChannelExamples, ^(QCKDSLSharedExampleContext exampleContext) { - __block RACChannel * (^getChannel)(void); - __block RACChannel *channel; - - id value1 = @"test value 1"; - id value2 = @"test value 2"; - id value3 = @"test value 3"; - NSArray *values = @[ value1, value2, value3 ]; - - qck_beforeEach(^{ - getChannel = exampleContext()[RACChannelExampleCreateBlock]; - channel = getChannel(); - }); - - qck_it(@"should not send any leadingTerminal value on subscription", ^{ - __block id receivedValue = nil; - - [channel.followingTerminal sendNext:value1]; - [channel.leadingTerminal subscribeNext:^(id x) { - receivedValue = x; - }]; - - expect(receivedValue).to(beNil()); - - [channel.followingTerminal sendNext:value2]; - expect(receivedValue).to(equal(value2)); - }); - - qck_it(@"should send the latest followingTerminal value on subscription", ^{ - __block id receivedValue = nil; - - [channel.leadingTerminal sendNext:value1]; - [[channel.followingTerminal take:1] subscribeNext:^(id x) { - receivedValue = x; - }]; - - expect(receivedValue).to(equal(value1)); - - [channel.leadingTerminal sendNext:value2]; - [[channel.followingTerminal take:1] subscribeNext:^(id x) { - receivedValue = x; - }]; - - expect(receivedValue).to(equal(value2)); - }); - - qck_it(@"should send leadingTerminal values as they change", ^{ - NSMutableArray *receivedValues = [NSMutableArray array]; - [channel.leadingTerminal subscribeNext:^(id x) { - [receivedValues addObject:x]; - }]; - - [channel.followingTerminal sendNext:value1]; - [channel.followingTerminal sendNext:value2]; - [channel.followingTerminal sendNext:value3]; - expect(receivedValues).to(equal(values)); - }); - - qck_it(@"should send followingTerminal values as they change", ^{ - [channel.leadingTerminal sendNext:value1]; - - NSMutableArray *receivedValues = [NSMutableArray array]; - [channel.followingTerminal subscribeNext:^(id x) { - [receivedValues addObject:x]; - }]; - - [channel.leadingTerminal sendNext:value2]; - [channel.leadingTerminal sendNext:value3]; - expect(receivedValues).to(equal(values)); - }); - - qck_it(@"should complete both signals when the leadingTerminal is completed", ^{ - __block BOOL completedLeft = NO; - [channel.leadingTerminal subscribeCompleted:^{ - completedLeft = YES; - }]; - - __block BOOL completedRight = NO; - [channel.followingTerminal subscribeCompleted:^{ - completedRight = YES; - }]; - - [channel.leadingTerminal sendCompleted]; - expect(@(completedLeft)).to(beTruthy()); - expect(@(completedRight)).to(beTruthy()); - }); - - qck_it(@"should complete both signals when the followingTerminal is completed", ^{ - __block BOOL completedLeft = NO; - [channel.leadingTerminal subscribeCompleted:^{ - completedLeft = YES; - }]; - - __block BOOL completedRight = NO; - [channel.followingTerminal subscribeCompleted:^{ - completedRight = YES; - }]; - - [channel.followingTerminal sendCompleted]; - expect(@(completedLeft)).to(beTruthy()); - expect(@(completedRight)).to(beTruthy()); - }); - - qck_it(@"should replay completion to new subscribers", ^{ - [channel.leadingTerminal sendCompleted]; - - __block BOOL completedLeft = NO; - [channel.leadingTerminal subscribeCompleted:^{ - completedLeft = YES; - }]; - - __block BOOL completedRight = NO; - [channel.followingTerminal subscribeCompleted:^{ - completedRight = YES; - }]; - - expect(@(completedLeft)).to(beTruthy()); - expect(@(completedRight)).to(beTruthy()); - }); - }); - - sharedExamples(RACViewChannelExamples, ^(QCKDSLSharedExampleContext exampleContext) { - __block NSString *keyPath; - __block NSObject * (^getView)(void); - __block RACChannelTerminal * (^getTerminal)(NSObject *); - __block void (^setViewValue)(NSObject *view, NSNumber *value); - - __block NSObject *testView; - __block RACChannelTerminal *endpoint; - - qck_beforeEach(^{ - keyPath = exampleContext()[RACViewChannelExampleKeyPath]; - getTerminal = exampleContext()[RACViewChannelExampleCreateTerminalBlock]; - getView = exampleContext()[RACViewChannelExampleCreateViewBlock]; - setViewValue = exampleContext()[RACViewChannelExampleSetViewValueBlock]; - - testView = getView(); - endpoint = getTerminal(testView); - }); - - qck_it(@"should not send changes made by the channel itself", ^{ - __block BOOL receivedNext = NO; - [endpoint subscribeNext:^(id x) { - receivedNext = YES; - }]; - - expect(@(receivedNext)).to(beFalsy()); - - [endpoint sendNext:@0.1]; - expect(@(receivedNext)).to(beFalsy()); - - [endpoint sendNext:@0.2]; - expect(@(receivedNext)).to(beFalsy()); - - [endpoint sendCompleted]; - expect(@(receivedNext)).to(beFalsy()); - }); - - qck_it(@"should not send progammatic changes made to the view", ^{ - __block BOOL receivedNext = NO; - [endpoint subscribeNext:^(id x) { - receivedNext = YES; - }]; - - expect(@(receivedNext)).to(beFalsy()); - - [testView setValue:@0.1 forKeyPath:keyPath]; - expect(@(receivedNext)).to(beFalsy()); - - [testView setValue:@0.2 forKeyPath:keyPath]; - expect(@(receivedNext)).to(beFalsy()); - }); - - qck_it(@"should not have a starting value", ^{ - __block BOOL receivedNext = NO; - [endpoint subscribeNext:^(id x) { - receivedNext = YES; - }]; - - expect(@(receivedNext)).to(beFalsy()); - }); - - qck_it(@"should send view changes", ^{ - __block NSString *received; - [endpoint subscribeNext:^(id x) { - received = x; - }]; - - setViewValue(testView, @0.1); - expect(received).to(equal(@0.1)); - - setViewValue(testView, @0.2); - expect(received).to(equal(@0.2)); - }); - - qck_it(@"should set values on the view", ^{ - [endpoint sendNext:@0.1]; - expect([testView valueForKeyPath:keyPath]).to(equal(@0.1)); - - [endpoint sendNext:@0.2]; - expect([testView valueForKeyPath:keyPath]).to(equal(@0.2)); - }); - - qck_it(@"should not echo changes back to the channel", ^{ - __block NSUInteger receivedCount = 0; - [endpoint subscribeNext:^(id _) { - receivedCount++; - }]; - - expect(@(receivedCount)).to(equal(@0)); - - [endpoint sendNext:@0.1]; - expect(@(receivedCount)).to(equal(@0)); - - setViewValue(testView, @0.2); - expect(@(receivedCount)).to(equal(@1)); - }); - - qck_it(@"should complete when the view deallocates", ^{ - __block BOOL deallocated = NO; - __block BOOL completed = NO; - - @autoreleasepool { - NSObject *view __attribute__((objc_precise_lifetime)) = getView(); - [view.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - deallocated = YES; - }]]; - - RACChannelTerminal *terminal = getTerminal(view); - [terminal subscribeCompleted:^{ - completed = YES; - }]; - - expect(@(deallocated)).to(beFalsy()); - expect(@(completed)).to(beFalsy()); - } - - expect(@(deallocated)).to(beTruthy()); - expect(@(completed)).to(beTruthy()); - }); - - qck_it(@"should deallocate after the view deallocates", ^{ - __block BOOL viewDeallocated = NO; - __block BOOL terminalDeallocated = NO; - - @autoreleasepool { - NSObject *view __attribute__((objc_precise_lifetime)) = getView(); - [view.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - viewDeallocated = YES; - }]]; - - RACChannelTerminal *terminal = getTerminal(view); - [terminal.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - terminalDeallocated = YES; - }]]; - - expect(@(viewDeallocated)).to(beFalsy()); - expect(@(terminalDeallocated)).to(beFalsy()); - } - - expect(@(viewDeallocated)).to(beTruthy()); - expect(@(terminalDeallocated)).toEventually(beTruthy()); - }); - }); -} - -QuickConfigurationEnd diff --git a/ReactiveObjCTests/RACChannelSpec.m b/ReactiveObjCTests/RACChannelSpec.m deleted file mode 100644 index 40ea077872..0000000000 --- a/ReactiveObjCTests/RACChannelSpec.m +++ /dev/null @@ -1,76 +0,0 @@ -// -// RACChannelSpec.m -// ReactiveObjC -// -// Created by Uri Baghin on 30/12/2012. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACChannelExamples.h" - -#import "NSObject+RACDeallocating.h" -#import "RACChannel.h" -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACSignal.h" - -QuickSpecBegin(RACChannelSpec) - -qck_describe(@"RACChannel", ^{ - qck_itBehavesLike(RACChannelExamples, ^{ - return @{ - RACChannelExampleCreateBlock: [^{ - return [[RACChannel alloc] init]; - } copy] - }; - }); - - qck_describe(@"memory management", ^{ - qck_it(@"should dealloc when its subscribers are disposed", ^{ - RACDisposable *leadingDisposable = nil; - RACDisposable *followingDisposable = nil; - - __block BOOL deallocated = NO; - - @autoreleasepool { - RACChannel *channel __attribute__((objc_precise_lifetime)) = [[RACChannel alloc] init]; - [channel.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - deallocated = YES; - }]]; - - leadingDisposable = [channel.leadingTerminal subscribeCompleted:^{}]; - followingDisposable = [channel.followingTerminal subscribeCompleted:^{}]; - } - - [leadingDisposable dispose]; - [followingDisposable dispose]; - expect(@(deallocated)).toEventually(beTruthy()); - }); - - qck_it(@"should dealloc when its subscriptions are disposed", ^{ - RACDisposable *leadingDisposable = nil; - RACDisposable *followingDisposable = nil; - - __block BOOL deallocated = NO; - - @autoreleasepool { - RACChannel *channel __attribute__((objc_precise_lifetime)) = [[RACChannel alloc] init]; - [channel.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - deallocated = YES; - }]]; - - leadingDisposable = [[RACSignal never] subscribe:channel.leadingTerminal]; - followingDisposable = [[RACSignal never] subscribe:channel.followingTerminal]; - } - - [leadingDisposable dispose]; - [followingDisposable dispose]; - expect(@(deallocated)).toEventually(beTruthy()); - }); - }); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/RACCommandSpec.m b/ReactiveObjCTests/RACCommandSpec.m deleted file mode 100644 index c55de11738..0000000000 --- a/ReactiveObjCTests/RACCommandSpec.m +++ /dev/null @@ -1,545 +0,0 @@ -// -// RACCommandSpec.m -// ReactiveObjC -// -// Created by Josh Abernathy on 8/31/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "NSArray+RACSequenceAdditions.h" -#import "NSObject+RACDeallocating.h" -#import "NSObject+RACPropertySubscribing.h" -#import "RACCommand.h" -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACEvent.h" -#import "RACScheduler.h" -#import "RACSequence.h" -#import "RACSignal+Operations.h" -#import "RACSubject.h" -#import "RACUnit.h" - -QuickSpecBegin(RACCommandSpec) - -RACSignal * (^emptySignalBlock)(id) = ^(id _) { - return [RACSignal empty]; -}; - -qck_describe(@"with a simple signal block", ^{ - __block RACCommand *command; - - qck_beforeEach(^{ - command = [[RACCommand alloc] initWithSignalBlock:^(id value) { - return [RACSignal return:value]; - }]; - - expect(command).notTo(beNil()); - expect(@(command.allowsConcurrentExecution)).to(beFalsy()); - }); - - qck_it(@"should be enabled by default", ^{ - expect([command.enabled first]).to(equal(@YES)); - }); - - qck_it(@"should not be executing by default", ^{ - expect([command.executing first]).to(equal(@NO)); - }); - - qck_it(@"should create an execution signal", ^{ - __block NSUInteger signalsReceived = 0; - __block BOOL completed = NO; - - id value = NSNull.null; - [command.executionSignals subscribeNext:^(RACSignal *signal) { - signalsReceived++; - - [signal subscribeNext:^(id x) { - expect(x).to(equal(value)); - } completed:^{ - completed = YES; - }]; - }]; - - expect(@(signalsReceived)).to(equal(@0)); - - [command execute:value]; - expect(@(signalsReceived)).toEventually(equal(@1)); - expect(@(completed)).to(beTruthy()); - }); - - qck_it(@"should return the execution signal from -execute:", ^{ - __block BOOL completed = NO; - - id value = NSNull.null; - [[command - execute:value] - subscribeNext:^(id x) { - expect(x).to(equal(value)); - } completed:^{ - completed = YES; - }]; - - expect(@(completed)).toEventually(beTruthy()); - }); - - qck_it(@"should always send executionSignals on the main thread", ^{ - __block RACScheduler *receivedScheduler = nil; - [command.executionSignals subscribeNext:^(id _) { - receivedScheduler = RACScheduler.currentScheduler; - }]; - - [[RACScheduler scheduler] schedule:^{ - expect(@([[command execute:nil] waitUntilCompleted:NULL])).to(beTruthy()); - }]; - - expect(receivedScheduler).to(beNil()); - expect(receivedScheduler).toEventually(equal(RACScheduler.mainThreadScheduler)); - }); - - qck_it(@"should not send anything on 'errors' by default", ^{ - __block BOOL receivedError = NO; - [command.errors subscribeNext:^(id _) { - receivedError = YES; - }]; - - expect(@([[command execute:nil] asynchronouslyWaitUntilCompleted:NULL])).to(beTruthy()); - expect(@(receivedError)).to(beFalsy()); - }); - - qck_it(@"should be executing while an execution signal is running", ^{ - [command.executionSignals subscribeNext:^(RACSignal *signal) { - [signal subscribeNext:^(id x) { - expect([command.executing first]).to(equal(@YES)); - }]; - }]; - - expect(@([[command execute:nil] asynchronouslyWaitUntilCompleted:NULL])).to(beTruthy()); - expect([command.executing first]).to(equal(@NO)); - }); - - qck_it(@"should always update executing on the main thread", ^{ - __block RACScheduler *updatedScheduler = nil; - [[command.executing skip:1] subscribeNext:^(NSNumber *executing) { - if (!executing.boolValue) return; - - updatedScheduler = RACScheduler.currentScheduler; - }]; - - [[RACScheduler scheduler] schedule:^{ - expect(@([[command execute:nil] waitUntilCompleted:NULL])).to(beTruthy()); - }]; - - expect([command.executing first]).to(equal(@NO)); - expect(updatedScheduler).toEventually(equal(RACScheduler.mainThreadScheduler)); - }); - - qck_it(@"should dealloc without subscribers", ^{ - __block BOOL disposed = NO; - - @autoreleasepool { - RACCommand *command __attribute__((objc_precise_lifetime)) = [[RACCommand alloc] initWithSignalBlock:emptySignalBlock]; - [command.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - disposed = YES; - }]]; - } - - expect(@(disposed)).toEventually(beTruthy()); - }); - - qck_it(@"should complete signals on the main thread when deallocated", ^{ - __block RACScheduler *executionSignalsScheduler = nil; - __block RACScheduler *executingScheduler = nil; - __block RACScheduler *enabledScheduler = nil; - __block RACScheduler *errorsScheduler = nil; - - [[RACScheduler scheduler] schedule:^{ - @autoreleasepool { - RACCommand *command __attribute__((objc_precise_lifetime)) = [[RACCommand alloc] initWithSignalBlock:emptySignalBlock]; - - [command.executionSignals subscribeCompleted:^{ - executionSignalsScheduler = RACScheduler.currentScheduler; - }]; - - [command.executing subscribeCompleted:^{ - executingScheduler = RACScheduler.currentScheduler; - }]; - - [command.enabled subscribeCompleted:^{ - enabledScheduler = RACScheduler.currentScheduler; - }]; - - [command.errors subscribeCompleted:^{ - errorsScheduler = RACScheduler.currentScheduler; - }]; - } - }]; - - expect(executionSignalsScheduler).toEventually(equal(RACScheduler.mainThreadScheduler)); - expect(executingScheduler).toEventually(equal(RACScheduler.mainThreadScheduler)); - expect(enabledScheduler).toEventually(equal(RACScheduler.mainThreadScheduler)); - expect(errorsScheduler).toEventually(equal(RACScheduler.mainThreadScheduler)); - }); -}); - -qck_it(@"should invoke the signalBlock once per execution", ^{ - NSMutableArray *valuesReceived = [NSMutableArray array]; - RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^(id x) { - [valuesReceived addObject:x]; - return [RACSignal empty]; - }]; - - expect(@([[command execute:@"foo"] asynchronouslyWaitUntilCompleted:NULL])).to(beTruthy()); - expect(valuesReceived).to(equal((@[ @"foo" ]))); - - expect(@([[command execute:@"bar"] asynchronouslyWaitUntilCompleted:NULL])).to(beTruthy()); - expect(valuesReceived).to(equal((@[ @"foo", @"bar" ]))); -}); - -qck_it(@"should send on executionSignals in order of execution", ^{ - RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^(RACSequence *seq) { - return [seq signalWithScheduler:RACScheduler.immediateScheduler]; - }]; - - NSMutableArray *valuesReceived = [NSMutableArray array]; - [[command.executionSignals - concat] - subscribeNext:^(id x) { - [valuesReceived addObject:x]; - }]; - - RACSequence *first = @[ @"foo", @"bar" ].rac_sequence; - expect(@([[command execute:first] asynchronouslyWaitUntilCompleted:NULL])).to(beTruthy()); - - RACSequence *second = @[ @"buzz", @"baz" ].rac_sequence; - expect(@([[command execute:second] asynchronouslyWaitUntilCompleted:NULL])).to(beTruthy()); - - NSArray *expectedValues = @[ @"foo", @"bar", @"buzz", @"baz" ]; - expect(valuesReceived).to(equal(expectedValues)); -}); - -qck_it(@"should wait for all signals to complete or error before executing sends NO", ^{ - RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^(RACSignal *signal) { - return signal; - }]; - - command.allowsConcurrentExecution = YES; - - RACSubject *firstSubject = [RACSubject subject]; - expect([command execute:firstSubject]).notTo(beNil()); - - RACSubject *secondSubject = [RACSubject subject]; - expect([command execute:secondSubject]).notTo(beNil()); - - expect([command.executing first]).toEventually(equal(@YES)); - - [firstSubject sendError:nil]; - expect([command.executing first]).to(equal(@YES)); - - [secondSubject sendNext:nil]; - expect([command.executing first]).to(equal(@YES)); - - [secondSubject sendCompleted]; - expect([command.executing first]).toEventually(equal(@NO)); -}); - -qck_it(@"should have allowsConcurrentExecution be observable", ^{ - RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^(RACSignal *signal) { - return signal; - }]; - - RACSubject *completion = [RACSubject subject]; - RACSignal *allowsConcurrentExecution = [[RACObserve(command, allowsConcurrentExecution) - takeUntil:completion] - replayLast]; - - command.allowsConcurrentExecution = YES; - - expect([allowsConcurrentExecution first]).to(beTrue()); - [completion sendCompleted]; -}); - -qck_it(@"should not deliver errors from executionSignals", ^{ - RACSubject *subject = [RACSubject subject]; - NSMutableArray *receivedEvents = [NSMutableArray array]; - - RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^(id _) { - return subject; - }]; - - [[[command.executionSignals - flatten] - materialize] - subscribeNext:^(RACEvent *event) { - [receivedEvents addObject:event]; - }]; - - expect([command execute:nil]).notTo(beNil()); - expect([command.executing first]).toEventually(equal(@YES)); - - [subject sendNext:RACUnit.defaultUnit]; - - NSArray *expectedEvents = @[ [RACEvent eventWithValue:RACUnit.defaultUnit] ]; - expect(receivedEvents).toEventually(equal(expectedEvents)); - expect([command.executing first]).to(equal(@YES)); - - [subject sendNext:@"foo"]; - - expectedEvents = @[ [RACEvent eventWithValue:RACUnit.defaultUnit], [RACEvent eventWithValue:@"foo"] ]; - expect(receivedEvents).toEventually(equal(expectedEvents)); - expect([command.executing first]).to(equal(@YES)); - - NSError *error = [NSError errorWithDomain:@"" code:1 userInfo:nil]; - [subject sendError:error]; - - expect([command.executing first]).toEventually(equal(@NO)); - expect(receivedEvents).to(equal(expectedEvents)); -}); - -qck_it(@"should deliver errors from -execute:", ^{ - RACSubject *subject = [RACSubject subject]; - NSMutableArray *receivedEvents = [NSMutableArray array]; - - RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^(id _) { - return subject; - }]; - - [[[command - execute:nil] - materialize] - subscribeNext:^(RACEvent *event) { - [receivedEvents addObject:event]; - }]; - - expect([command.executing first]).toEventually(equal(@YES)); - - [subject sendNext:RACUnit.defaultUnit]; - - NSArray *expectedEvents = @[ [RACEvent eventWithValue:RACUnit.defaultUnit] ]; - expect(receivedEvents).toEventually(equal(expectedEvents)); - expect([command.executing first]).to(equal(@YES)); - - [subject sendNext:@"foo"]; - - expectedEvents = @[ [RACEvent eventWithValue:RACUnit.defaultUnit], [RACEvent eventWithValue:@"foo"] ]; - expect(receivedEvents).toEventually(equal(expectedEvents)); - expect([command.executing first]).to(equal(@YES)); - - NSError *error = [NSError errorWithDomain:@"" code:1 userInfo:nil]; - [subject sendError:error]; - - expectedEvents = @[ [RACEvent eventWithValue:RACUnit.defaultUnit], [RACEvent eventWithValue:@"foo"], [RACEvent eventWithError:error] ]; - expect(receivedEvents).toEventually(equal(expectedEvents)); - expect([command.executing first]).toEventually(equal(@NO)); -}); - -qck_it(@"should deliver errors onto 'errors'", ^{ - RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^(RACSignal *signal) { - return signal; - }]; - - command.allowsConcurrentExecution = YES; - - RACSubject *firstSubject = [RACSubject subject]; - expect([command execute:firstSubject]).notTo(beNil()); - - RACSubject *secondSubject = [RACSubject subject]; - expect([command execute:secondSubject]).notTo(beNil()); - - NSError *firstError = [NSError errorWithDomain:@"" code:1 userInfo:nil]; - NSError *secondError = [NSError errorWithDomain:@"" code:2 userInfo:nil]; - - // We should receive errors from our previously-started executions. - NSMutableArray *receivedErrors = [NSMutableArray array]; - [command.errors subscribeNext:^(NSError *error) { - [receivedErrors addObject:error]; - }]; - - expect([command.executing first]).toEventually(equal(@YES)); - - [firstSubject sendError:firstError]; - expect([command.executing first]).toEventually(equal(@YES)); - - NSArray *expected = @[ firstError ]; - expect(receivedErrors).toEventually(equal(expected)); - - [secondSubject sendError:secondError]; - expect([command.executing first]).toEventually(equal(@NO)); - - expected = @[ firstError, secondError ]; - expect(receivedErrors).toEventually(equal(expected)); -}); - -qck_it(@"should not deliver non-error events onto 'errors'", ^{ - RACSubject *subject = [RACSubject subject]; - RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^(id _) { - return subject; - }]; - - __block BOOL receivedEvent = NO; - [command.errors subscribeNext:^(id _) { - receivedEvent = YES; - }]; - - expect([command execute:nil]).notTo(beNil()); - expect([command.executing first]).toEventually(equal(@YES)); - - [subject sendNext:RACUnit.defaultUnit]; - [subject sendCompleted]; - - expect([command.executing first]).toEventually(equal(@NO)); - expect(@(receivedEvent)).to(beFalsy()); -}); - -qck_it(@"should send errors on the main thread", ^{ - RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^(RACSignal *signal) { - return signal; - }]; - - NSError *error = [NSError errorWithDomain:@"" code:1 userInfo:nil]; - - __block RACScheduler *receivedScheduler = nil; - [command.errors subscribeNext:^(NSError *e) { - expect(e).to(equal(error)); - receivedScheduler = RACScheduler.currentScheduler; - }]; - - RACSignal *errorSignal = [RACSignal error:error]; - - [[RACScheduler scheduler] schedule:^{ - [command execute:errorSignal]; - }]; - - expect(receivedScheduler).to(beNil()); - expect(receivedScheduler).toEventually(equal(RACScheduler.mainThreadScheduler)); -}); - -qck_describe(@"enabled signal", ^{ - __block RACSubject *enabledSubject; - __block RACCommand *command; - - qck_beforeEach(^{ - enabledSubject = [RACSubject subject]; - command = [[RACCommand alloc] initWithEnabled:enabledSubject signalBlock:^(id _) { - return [RACSignal return:RACUnit.defaultUnit]; - }]; - }); - - qck_it(@"should send YES by default", ^{ - expect([command.enabled first]).to(equal(@YES)); - }); - - qck_it(@"should send whatever the enabledSignal has sent most recently", ^{ - [enabledSubject sendNext:@NO]; - expect([command.enabled first]).toEventually(equal(@NO)); - - [enabledSubject sendNext:@YES]; - expect([command.enabled first]).toEventually(equal(@YES)); - - [enabledSubject sendNext:@NO]; - expect([command.enabled first]).toEventually(equal(@NO)); - }); - - qck_it(@"should sample enabledSignal synchronously at initialization time", ^{ - RACCommand *command = [[RACCommand alloc] initWithEnabled:[RACSignal return:@NO] signalBlock:^(id _) { - return [RACSignal empty]; - }]; - expect([command.enabled first]).to(equal(@NO)); - }); - - qck_it(@"should send NO while executing is YES and allowsConcurrentExecution is NO", ^{ - [[command.executionSignals flatten] subscribeNext:^(id _) { - expect([command.executing first]).to(equal(@YES)); - expect([command.enabled first]).to(equal(@NO)); - }]; - - expect([command.enabled first]).to(equal(@YES)); - expect(@([[command execute:nil] asynchronouslyWaitUntilCompleted:NULL])).to(beTruthy()); - expect([command.enabled first]).to(equal(@YES)); - }); - - qck_it(@"should send YES while executing is YES and allowsConcurrentExecution is YES", ^{ - command.allowsConcurrentExecution = YES; - - __block BOOL outerExecuted = NO; - __block BOOL innerExecuted = NO; - - // Prevent infinite recursion by only responding to the first value. - [[[command.executionSignals - take:1] - flatten] - subscribeNext:^(id _) { - outerExecuted = YES; - - expect([command.executing first]).to(equal(@YES)); - expect([command.enabled first]).to(equal(@YES)); - - [[command execute:nil] subscribeCompleted:^{ - innerExecuted = YES; - }]; - }]; - - expect([command.enabled first]).to(equal(@YES)); - - expect([command execute:nil]).notTo(beNil()); - expect(@(outerExecuted)).toEventually(beTruthy()); - expect(@(innerExecuted)).toEventually(beTruthy()); - - expect([command.enabled first]).to(equal(@YES)); - }); - - qck_it(@"should send an error from -execute: when NO", ^{ - [enabledSubject sendNext:@NO]; - - RACSignal *signal = [command execute:nil]; - expect(signal).notTo(beNil()); - - __block BOOL success = NO; - __block NSError *error = nil; - expect([signal firstOrDefault:nil success:&success error:&error]).to(beNil()); - expect(@(success)).to(beFalsy()); - - expect(error).notTo(beNil()); - expect(error.domain).to(equal(RACCommandErrorDomain)); - expect(@(error.code)).to(equal(@(RACCommandErrorNotEnabled))); - expect(error.userInfo[RACUnderlyingCommandErrorKey]).to(beIdenticalTo(command)); - }); - - qck_it(@"should always update on the main thread", ^{ - __block RACScheduler *updatedScheduler = nil; - [[command.enabled skip:1] subscribeNext:^(id _) { - updatedScheduler = RACScheduler.currentScheduler; - }]; - - [[RACScheduler scheduler] schedule:^{ - [enabledSubject sendNext:@NO]; - }]; - - expect([command.enabled first]).to(equal(@YES)); - expect([command.enabled first]).toEventually(equal(@NO)); - expect(updatedScheduler).to(equal(RACScheduler.mainThreadScheduler)); - }); - - qck_it(@"should complete when the command is deallocated even if the input signal hasn't", ^{ - __block BOOL deallocated = NO; - __block BOOL completed = NO; - - @autoreleasepool { - RACCommand *command __attribute__((objc_precise_lifetime)) = [[RACCommand alloc] initWithEnabled:enabledSubject signalBlock:emptySignalBlock]; - [command.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - deallocated = YES; - }]]; - - [command.enabled subscribeCompleted:^{ - completed = YES; - }]; - } - - expect(@(deallocated)).toEventually(beTruthy()); - expect(@(completed)).toEventually(beTruthy()); - }); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/RACCompoundDisposableSpec.m b/ReactiveObjCTests/RACCompoundDisposableSpec.m deleted file mode 100644 index a3f0704c49..0000000000 --- a/ReactiveObjCTests/RACCompoundDisposableSpec.m +++ /dev/null @@ -1,112 +0,0 @@ -// -// RACCompoundDisposableSpec.m -// ReactiveObjC -// -// Created by Josh Abernathy on 11/30/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACCompoundDisposable.h" - -QuickSpecBegin(RACCompoundDisposableSpec) - -qck_it(@"should dispose of all its contained disposables", ^{ - __block BOOL d1Disposed = NO; - RACDisposable *d1 = [RACDisposable disposableWithBlock:^{ - d1Disposed = YES; - }]; - - __block BOOL d2Disposed = NO; - RACDisposable *d2 = [RACDisposable disposableWithBlock:^{ - d2Disposed = YES; - }]; - - __block BOOL d3Disposed = NO; - RACDisposable *d3 = [RACDisposable disposableWithBlock:^{ - d3Disposed = YES; - }]; - - __block BOOL d4Disposed = NO; - RACDisposable *d4 = [RACDisposable disposableWithBlock:^{ - d4Disposed = YES; - }]; - - RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposableWithDisposables:@[ d1, d2, d3 ]]; - [disposable addDisposable:d4]; - - expect(@(d1Disposed)).to(beFalsy()); - expect(@(d2Disposed)).to(beFalsy()); - expect(@(d3Disposed)).to(beFalsy()); - expect(@(d4Disposed)).to(beFalsy()); - expect(@(disposable.disposed)).to(beFalsy()); - - [disposable dispose]; - - expect(@(d1Disposed)).to(beTruthy()); - expect(@(d2Disposed)).to(beTruthy()); - expect(@(d3Disposed)).to(beTruthy()); - expect(@(d4Disposed)).to(beTruthy()); - expect(@(disposable.disposed)).to(beTruthy()); -}); - -qck_it(@"should dispose of any added disposables immediately if it's already been disposed", ^{ - RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable]; - [disposable dispose]; - - RACDisposable *d = [[RACDisposable alloc] init]; - - expect(@(d.disposed)).to(beFalsy()); - [disposable addDisposable:d]; - expect(@(d.disposed)).to(beTruthy()); -}); - -qck_it(@"should work when initialized with -init", ^{ - RACCompoundDisposable *disposable = [[RACCompoundDisposable alloc] init]; - - __block BOOL disposed = NO; - RACDisposable *d = [RACDisposable disposableWithBlock:^{ - disposed = YES; - }]; - - [disposable addDisposable:d]; - expect(@(disposed)).to(beFalsy()); - - [disposable dispose]; - expect(@(disposed)).to(beTruthy()); -}); - -qck_it(@"should work when initialized with +disposableWithBlock:", ^{ - __block BOOL compoundDisposed = NO; - RACCompoundDisposable *disposable = [RACCompoundDisposable disposableWithBlock:^{ - compoundDisposed = YES; - }]; - - __block BOOL disposed = NO; - RACDisposable *d = [RACDisposable disposableWithBlock:^{ - disposed = YES; - }]; - - [disposable addDisposable:d]; - expect(@(disposed)).to(beFalsy()); - expect(@(compoundDisposed)).to(beFalsy()); - - [disposable dispose]; - expect(@(disposed)).to(beTruthy()); - expect(@(compoundDisposed)).to(beTruthy()); -}); - -qck_it(@"should allow disposables to be removed", ^{ - RACCompoundDisposable *disposable = [[RACCompoundDisposable alloc] init]; - RACDisposable *d = [[RACDisposable alloc] init]; - - [disposable addDisposable:d]; - [disposable removeDisposable:d]; - - [disposable dispose]; - expect(@(d.disposed)).to(beFalsy()); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/RACControlCommandExamples.h b/ReactiveObjCTests/RACControlCommandExamples.h deleted file mode 100644 index edafe42085..0000000000 --- a/ReactiveObjCTests/RACControlCommandExamples.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// RACControlCommandExamples.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-08-15. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -// The name of the shared examples for any control class that has -// `rac_command` and `isEnabled` properties. -extern NSString * const RACControlCommandExamples; - -// The control to test. -extern NSString * const RACControlCommandExampleControl; - -// A block of type `void (^)(id control)` which should activate the -// `rac_command` of the `control` by manipulating the control itself. -extern NSString * const RACControlCommandExampleActivateBlock; diff --git a/ReactiveObjCTests/RACControlCommandExamples.m b/ReactiveObjCTests/RACControlCommandExamples.m deleted file mode 100644 index ce1b9da572..0000000000 --- a/ReactiveObjCTests/RACControlCommandExamples.m +++ /dev/null @@ -1,86 +0,0 @@ -// -// RACControlCommandExamples.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-08-15. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACControlCommandExamples.h" - -#import "RACCommand.h" -#import "RACSubject.h" -#import "RACUnit.h" - -NSString * const RACControlCommandExamples = @"RACControlCommandExamples"; -NSString * const RACControlCommandExampleControl = @"RACControlCommandExampleControl"; -NSString * const RACControlCommandExampleActivateBlock = @"RACControlCommandExampleActivateBlock"; - -// Methods used by the unit test that would otherwise require platform-specific -// imports. -@interface NSObject (RACControlCommandExamples) - -@property (nonatomic, strong) RACCommand *rac_command; - -- (BOOL)isEnabled; - -@end - -QuickConfigurationBegin(RACControlCommandExampleGroups) - -+ (void)configure:(Configuration *)configuration { - sharedExamples(RACControlCommandExamples, ^(QCKDSLSharedExampleContext exampleContext) { - __block id control; - __block void (^activate)(id); - - __block RACSubject *enabledSubject; - __block RACCommand *command; - - qck_beforeEach(^{ - control = exampleContext()[RACControlCommandExampleControl]; - activate = [exampleContext()[RACControlCommandExampleActivateBlock] copy]; - - enabledSubject = [RACSubject subject]; - command = [[RACCommand alloc] initWithEnabled:enabledSubject signalBlock:^(id sender) { - return [RACSignal return:sender]; - }]; - - [control setRac_command:command]; - }); - - qck_it(@"should bind the control's enabledness to the command", ^{ - expect(@([control isEnabled])).toEventually(beTruthy()); - - [enabledSubject sendNext:@NO]; - expect(@([control isEnabled])).toEventually(beFalsy()); - - [enabledSubject sendNext:@YES]; - expect(@([control isEnabled])).toEventually(beTruthy()); - }); - - qck_it(@"should execute the control's command when activated", ^{ - __block BOOL executed = NO; - [[command.executionSignals flatten] subscribeNext:^(id sender) { - expect(sender).to(equal(control)); - executed = YES; - }]; - - activate(control); - expect(@(executed)).toEventually(beTruthy()); - }); - - qck_it(@"should overwrite an existing command when setting a new one", ^{ - RACCommand *secondCommand = [[RACCommand alloc] initWithSignalBlock:^(id _) { - return [RACSignal return:RACUnit.defaultUnit]; - }]; - - [control setRac_command:secondCommand]; - expect([control rac_command]).to(beIdenticalTo(secondCommand)); - }); - }); -} - -QuickConfigurationEnd diff --git a/ReactiveObjCTests/RACDelegateProxySpec.m b/ReactiveObjCTests/RACDelegateProxySpec.m deleted file mode 100644 index 135b96f7a2..0000000000 --- a/ReactiveObjCTests/RACDelegateProxySpec.m +++ /dev/null @@ -1,92 +0,0 @@ -// -// RACDelegateProxySpec.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-06-22. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "NSObject+RACSelectorSignal.h" -#import "RACDelegateProxy.h" -#import "RACSignal.h" -#import "RACTuple.h" -#import "RACCompoundDisposable.h" -#import "NSObject+RACDeallocating.h" - -@protocol TestDelegateProtocol -- (NSUInteger)lengthOfString:(NSString *)str; -@end - -@interface TestDelegate : NSObject -@property (nonatomic, assign) BOOL lengthOfStringInvoked; -@end - -QuickSpecBegin(RACDelegateProxySpec) - -__block id proxy; -__block TestDelegate *delegate; -__block Protocol *protocol; - -qck_beforeEach(^{ - protocol = @protocol(TestDelegateProtocol); - expect(protocol).notTo(beNil()); - - proxy = [[RACDelegateProxy alloc] initWithProtocol:protocol]; - expect(proxy).notTo(beNil()); - expect([proxy rac_proxiedDelegate]).to(beNil()); - - delegate = [[TestDelegate alloc] init]; - expect(delegate).notTo(beNil()); -}); - -qck_it(@"should not respond to selectors at first", ^{ - expect(@([proxy respondsToSelector:@selector(lengthOfString:)])).to(beFalsy()); -}); - -qck_it(@"should send on a signal for a protocol method", ^{ - __block RACTuple *tuple; - [[proxy signalForSelector:@selector(lengthOfString:)] subscribeNext:^(RACTuple *t) { - tuple = t; - }]; - - expect(@([proxy respondsToSelector:@selector(lengthOfString:)])).to(beTruthy()); - expect(@([proxy lengthOfString:@"foo"])).to(equal(@0)); - expect(tuple).to(equal(RACTuplePack(@"foo"))); -}); - -qck_it(@"should forward to the proxied delegate", ^{ - [proxy setRac_proxiedDelegate:delegate]; - - expect(@([proxy respondsToSelector:@selector(lengthOfString:)])).to(beTruthy()); - expect(@([proxy lengthOfString:@"foo"])).to(equal(@3)); - expect(@(delegate.lengthOfStringInvoked)).to(beTruthy()); -}); - -qck_it(@"should not send to the delegate when signals are applied", ^{ - [proxy setRac_proxiedDelegate:delegate]; - - __block RACTuple *tuple; - [[proxy signalForSelector:@selector(lengthOfString:)] subscribeNext:^(RACTuple *t) { - tuple = t; - }]; - - expect(@([proxy respondsToSelector:@selector(lengthOfString:)])).to(beTruthy()); - expect(@([proxy lengthOfString:@"foo"])).to(equal(@0)); - - expect(tuple).to(equal(RACTuplePack(@"foo"))); - expect(@(delegate.lengthOfStringInvoked)).to(beFalsy()); -}); - -QuickSpecEnd - -@implementation TestDelegate - -- (NSUInteger)lengthOfString:(NSString *)str { - self.lengthOfStringInvoked = YES; - return str.length; -} - -@end diff --git a/ReactiveObjCTests/RACDisposableSpec.m b/ReactiveObjCTests/RACDisposableSpec.m deleted file mode 100644 index 1d2a6216d4..0000000000 --- a/ReactiveObjCTests/RACDisposableSpec.m +++ /dev/null @@ -1,76 +0,0 @@ -// -// RACDisposableSpec.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-06-13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACDisposable.h" -#import "RACScopedDisposable.h" - -QuickSpecBegin(RACDisposableSpec) - -qck_it(@"should initialize without a block", ^{ - RACDisposable *disposable = [[RACDisposable alloc] init]; - expect(disposable).notTo(beNil()); - expect(@(disposable.disposed)).to(beFalsy()); - - [disposable dispose]; - expect(@(disposable.disposed)).to(beTruthy()); -}); - -qck_it(@"should execute a block upon disposal", ^{ - __block BOOL disposed = NO; - RACDisposable *disposable = [RACDisposable disposableWithBlock:^{ - disposed = YES; - }]; - - expect(disposable).notTo(beNil()); - expect(@(disposed)).to(beFalsy()); - expect(@(disposable.disposed)).to(beFalsy()); - - [disposable dispose]; - expect(@(disposed)).to(beTruthy()); - expect(@(disposable.disposed)).to(beTruthy()); -}); - -qck_it(@"should not dispose upon deallocation", ^{ - __block BOOL disposed = NO; - __weak RACDisposable *weakDisposable = nil; - - @autoreleasepool { - RACDisposable *disposable = [RACDisposable disposableWithBlock:^{ - disposed = YES; - }]; - - weakDisposable = disposable; - expect(weakDisposable).notTo(beNil()); - } - - expect(weakDisposable).to(beNil()); - expect(@(disposed)).to(beFalsy()); -}); - -qck_it(@"should create a scoped disposable", ^{ - __block BOOL disposed = NO; - __weak RACScopedDisposable *weakDisposable = nil; - - @autoreleasepool { - RACScopedDisposable *disposable __attribute__((objc_precise_lifetime)) = [RACScopedDisposable disposableWithBlock:^{ - disposed = YES; - }]; - - weakDisposable = disposable; - expect(weakDisposable).notTo(beNil()); - expect(@(disposed)).to(beFalsy()); - } - - expect(weakDisposable).to(beNil()); - expect(@(disposed)).to(beTruthy()); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/RACEventSpec.m b/ReactiveObjCTests/RACEventSpec.m deleted file mode 100644 index d9eec6ae26..0000000000 --- a/ReactiveObjCTests/RACEventSpec.m +++ /dev/null @@ -1,83 +0,0 @@ -// -// RACEventSpec.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-01-07. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACEvent.h" - -QuickSpecBegin(RACEventSpec) - -qck_it(@"should return the singleton completed event", ^{ - RACEvent *event = RACEvent.completedEvent; - expect(event).notTo(beNil()); - - expect(event).to(beIdenticalTo(RACEvent.completedEvent)); - expect([event copy]).to(beIdenticalTo(event)); - - expect(@(event.eventType)).to(equal(@(RACEventTypeCompleted))); - expect(@(event.finished)).to(beTruthy()); - expect(event.error).to(beNil()); - expect(event.value).to(beNil()); -}); - -qck_it(@"should return an error event", ^{ - NSError *error = [NSError errorWithDomain:@"foo" code:1 userInfo:nil]; - RACEvent *event = [RACEvent eventWithError:error]; - expect(event).notTo(beNil()); - - expect(event).to(equal([RACEvent eventWithError:error])); - expect([event copy]).to(equal(event)); - - expect(@(event.eventType)).to(equal(@(RACEventTypeError))); - expect(@(event.finished)).to(beTruthy()); - expect(event.error).to(equal(error)); - expect(event.value).to(beNil()); -}); - -qck_it(@"should return an error event with a nil error", ^{ - RACEvent *event = [RACEvent eventWithError:nil]; - expect(event).notTo(beNil()); - - expect(event).to(equal([RACEvent eventWithError:nil])); - expect([event copy]).to(equal(event)); - - expect(@(event.eventType)).to(equal(@(RACEventTypeError))); - expect(@(event.finished)).to(beTruthy()); - expect(event.error).to(beNil()); - expect(event.value).to(beNil()); -}); - -qck_it(@"should return a next event", ^{ - NSString *value = @"foo"; - RACEvent *event = [RACEvent eventWithValue:value]; - expect(event).notTo(beNil()); - - expect(event).to(equal([RACEvent eventWithValue:value])); - expect([event copy]).to(equal(event)); - - expect(@(event.eventType)).to(equal(@(RACEventTypeNext))); - expect(@(event.finished)).to(beFalsy()); - expect(event.error).to(beNil()); - expect(event.value).to(equal(value)); -}); - -qck_it(@"should return a next event with a nil value", ^{ - RACEvent *event = [RACEvent eventWithValue:nil]; - expect(event).notTo(beNil()); - - expect(event).to(equal([RACEvent eventWithValue:nil])); - expect([event copy]).to(equal(event)); - - expect(@(event.eventType)).to(equal(@(RACEventTypeNext))); - expect(@(event.finished)).to(beFalsy()); - expect(event.error).to(beNil()); - expect(event.value).to(beNil()); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/RACKVOChannelSpec.m b/ReactiveObjCTests/RACKVOChannelSpec.m deleted file mode 100644 index 782d42558a..0000000000 --- a/ReactiveObjCTests/RACKVOChannelSpec.m +++ /dev/null @@ -1,394 +0,0 @@ -// -// RACKVOChannelSpec.m -// ReactiveObjC -// -// Created by Uri Baghin on 16/12/2012. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACTestObject.h" -#import "RACChannelExamples.h" -#import "RACPropertySignalExamples.h" - -#import "NSObject+RACDeallocating.h" -#import "NSObject+RACKVOWrapper.h" -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACKVOChannel.h" -#import "RACSignal+Operations.h" - -QuickSpecBegin(RACKVOChannelSpec) - -qck_describe(@"RACKVOChannel", ^{ - __block RACTestObject *object; - __block RACKVOChannel *channel; - id value1 = @"test value 1"; - id value2 = @"test value 2"; - id value3 = @"test value 3"; - NSArray *values = @[ value1, value2, value3 ]; - - qck_beforeEach(^{ - object = [[RACTestObject alloc] init]; - channel = [[RACKVOChannel alloc] initWithTarget:object keyPath:@keypath(object.stringValue) nilValue:nil]; - }); - - id setupBlock = ^(RACTestObject *testObject, NSString *keyPath, id nilValue, RACSignal *signal) { - RACKVOChannel *channel = [[RACKVOChannel alloc] initWithTarget:testObject keyPath:keyPath nilValue:nilValue]; - [signal subscribe:channel.followingTerminal]; - }; - - qck_itBehavesLike(RACPropertySignalExamples, ^{ - return @{ RACPropertySignalExamplesSetupBlock: setupBlock }; - }); - - qck_itBehavesLike(RACChannelExamples, ^{ - return @{ - RACChannelExampleCreateBlock: [^{ - return [[RACKVOChannel alloc] initWithTarget:object keyPath:@keypath(object.stringValue) nilValue:nil]; - } copy] - }; - }); - - qck_it(@"should send the object's current value when subscribed to followingTerminal", ^{ - __block id receivedValue = @"received value should not be this"; - [[channel.followingTerminal take:1] subscribeNext:^(id x) { - receivedValue = x; - }]; - - expect(receivedValue).to(beNil()); - - object.stringValue = value1; - [[channel.followingTerminal take:1] subscribeNext:^(id x) { - receivedValue = x; - }]; - - expect(receivedValue).to(equal(value1)); - }); - - qck_it(@"should send the object's new value on followingTerminal when it's changed", ^{ - object.stringValue = value1; - - NSMutableArray *receivedValues = [NSMutableArray array]; - [channel.followingTerminal subscribeNext:^(id x) { - [receivedValues addObject:x]; - }]; - - object.stringValue = value2; - object.stringValue = value3; - expect(receivedValues).to(equal(values)); - }); - - qck_it(@"should set the object's value using values sent to the followingTerminal", ^{ - expect(object.stringValue).to(beNil()); - - [channel.followingTerminal sendNext:value1]; - expect(object.stringValue).to(equal(value1)); - - [channel.followingTerminal sendNext:value2]; - expect(object.stringValue).to(equal(value2)); - }); - - qck_it(@"should be able to subscribe to signals", ^{ - NSMutableArray *receivedValues = [NSMutableArray array]; - [object rac_observeKeyPath:@keypath(object.stringValue) options:0 observer:self block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) { - [receivedValues addObject:value]; - }]; - - RACSignal *signal = [RACSignal createSignal:^ RACDisposable * (id subscriber) { - [subscriber sendNext:value1]; - [subscriber sendNext:value2]; - [subscriber sendNext:value3]; - return nil; - }]; - - [signal subscribe:channel.followingTerminal]; - expect(receivedValues).to(equal(values)); - }); - - qck_it(@"should complete both terminals when the target deallocates", ^{ - __block BOOL leadingCompleted = NO; - __block BOOL followingCompleted = NO; - __block BOOL deallocated = NO; - - @autoreleasepool { - RACTestObject *object __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init]; - [object.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - deallocated = YES; - }]]; - - RACKVOChannel *channel = [[RACKVOChannel alloc] initWithTarget:object keyPath:@keypath(object.stringValue) nilValue:nil]; - [channel.leadingTerminal subscribeCompleted:^{ - leadingCompleted = YES; - }]; - - [channel.followingTerminal subscribeCompleted:^{ - followingCompleted = YES; - }]; - - expect(@(deallocated)).to(beFalsy()); - expect(@(leadingCompleted)).to(beFalsy()); - expect(@(followingCompleted)).to(beFalsy()); - } - - expect(@(deallocated)).to(beTruthy()); - expect(@(leadingCompleted)).to(beTruthy()); - expect(@(followingCompleted)).to(beTruthy()); - }); - - qck_it(@"should deallocate when the target deallocates", ^{ - __block BOOL targetDeallocated = NO; - __block BOOL channelDeallocated = NO; - - @autoreleasepool { - RACTestObject *object __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init]; - [object.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - targetDeallocated = YES; - }]]; - - RACKVOChannel *channel = [[RACKVOChannel alloc] initWithTarget:object keyPath:@keypath(object.stringValue) nilValue:nil]; - [channel.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - channelDeallocated = YES; - }]]; - - expect(@(targetDeallocated)).to(beFalsy()); - expect(@(channelDeallocated)).to(beFalsy()); - } - - expect(@(targetDeallocated)).to(beTruthy()); - expect(@(channelDeallocated)).to(beTruthy()); - }); -}); - -qck_describe(@"RACChannelTo", ^{ - __block RACTestObject *a; - __block RACTestObject *b; - __block RACTestObject *c; - __block NSString *testName1; - __block NSString *testName2; - __block NSString *testName3; - - qck_beforeEach(^{ - a = [[RACTestObject alloc] init]; - b = [[RACTestObject alloc] init]; - c = [[RACTestObject alloc] init]; - testName1 = @"sync it!"; - testName2 = @"sync it again!"; - testName3 = @"sync it once more!"; - }); - - qck_it(@"should keep objects' properties in sync", ^{ - RACChannelTo(a, stringValue) = RACChannelTo(b, stringValue); - expect(a.stringValue).to(beNil()); - expect(b.stringValue).to(beNil()); - - a.stringValue = testName1; - expect(a.stringValue).to(equal(testName1)); - expect(b.stringValue).to(equal(testName1)); - - b.stringValue = testName2; - expect(a.stringValue).to(equal(testName2)); - expect(b.stringValue).to(equal(testName2)); - - a.stringValue = nil; - expect(a.stringValue).to(beNil()); - expect(b.stringValue).to(beNil()); - }); - - qck_it(@"should keep properties identified by keypaths in sync", ^{ - RACChannelTo(a, strongTestObjectValue.stringValue) = RACChannelTo(b, strongTestObjectValue.stringValue); - a.strongTestObjectValue = [[RACTestObject alloc] init]; - b.strongTestObjectValue = [[RACTestObject alloc] init]; - - a.strongTestObjectValue.stringValue = testName1; - expect(a.strongTestObjectValue.stringValue).to(equal(testName1)); - expect(b.strongTestObjectValue.stringValue).to(equal(testName1)); - expect(a.strongTestObjectValue).notTo(equal(b.strongTestObjectValue)); - - b.strongTestObjectValue = nil; - expect(a.strongTestObjectValue.stringValue).to(beNil()); - - c.stringValue = testName2; - b.strongTestObjectValue = c; - expect(a.strongTestObjectValue.stringValue).to(equal(testName2)); - expect(b.strongTestObjectValue.stringValue).to(equal(testName2)); - expect(a.strongTestObjectValue).notTo(equal(b.strongTestObjectValue)); - }); - - qck_it(@"should update properties identified by keypaths when the intermediate values change", ^{ - RACChannelTo(a, strongTestObjectValue.stringValue) = RACChannelTo(b, strongTestObjectValue.stringValue); - a.strongTestObjectValue = [[RACTestObject alloc] init]; - b.strongTestObjectValue = [[RACTestObject alloc] init]; - c.stringValue = testName1; - b.strongTestObjectValue = c; - - expect(a.strongTestObjectValue.stringValue).to(equal(testName1)); - expect(a.strongTestObjectValue).notTo(equal(b.strongTestObjectValue)); - }); - - qck_it(@"should update properties identified by keypaths when the channel was created when one of the two objects had an intermediate nil value", ^{ - RACChannelTo(a, strongTestObjectValue.stringValue) = RACChannelTo(b, strongTestObjectValue.stringValue); - b.strongTestObjectValue = [[RACTestObject alloc] init]; - c.stringValue = testName1; - a.strongTestObjectValue = c; - - expect(a.strongTestObjectValue.stringValue).to(equal(testName1)); - expect(b.strongTestObjectValue.stringValue).to(equal(testName1)); - expect(a.strongTestObjectValue).notTo(equal(b.strongTestObjectValue)); - }); - - qck_it(@"should take the value of the object being bound to at the start", ^{ - a.stringValue = testName1; - b.stringValue = testName2; - - RACChannelTo(a, stringValue) = RACChannelTo(b, stringValue); - expect(a.stringValue).to(equal(testName2)); - expect(b.stringValue).to(equal(testName2)); - }); - - qck_it(@"should update the value even if it's the same value the object had before it was bound", ^{ - a.stringValue = testName1; - b.stringValue = testName2; - - RACChannelTo(a, stringValue) = RACChannelTo(b, stringValue); - expect(a.stringValue).to(equal(testName2)); - expect(b.stringValue).to(equal(testName2)); - - b.stringValue = testName1; - expect(a.stringValue).to(equal(testName1)); - expect(b.stringValue).to(equal(testName1)); - }); - - qck_it(@"should bind transitively", ^{ - a.stringValue = testName1; - b.stringValue = testName2; - c.stringValue = testName3; - - RACChannelTo(a, stringValue) = RACChannelTo(b, stringValue); - RACChannelTo(b, stringValue) = RACChannelTo(c, stringValue); - expect(a.stringValue).to(equal(testName3)); - expect(b.stringValue).to(equal(testName3)); - expect(c.stringValue).to(equal(testName3)); - - c.stringValue = testName1; - expect(a.stringValue).to(equal(testName1)); - expect(b.stringValue).to(equal(testName1)); - expect(c.stringValue).to(equal(testName1)); - - b.stringValue = testName2; - expect(a.stringValue).to(equal(testName2)); - expect(b.stringValue).to(equal(testName2)); - expect(c.stringValue).to(equal(testName2)); - - a.stringValue = testName3; - expect(a.stringValue).to(equal(testName3)); - expect(b.stringValue).to(equal(testName3)); - expect(c.stringValue).to(equal(testName3)); - }); - - qck_it(@"should bind changes made by KVC on arrays", ^{ - b.arrayValue = @[]; - RACChannelTo(a, arrayValue) = RACChannelTo(b, arrayValue); - - [[b mutableArrayValueForKeyPath:@keypath(b.arrayValue)] addObject:@1]; - expect(a.arrayValue).to(equal(b.arrayValue)); - }); - - qck_it(@"should bind changes made by KVC on sets", ^{ - b.setValue = [NSSet set]; - RACChannelTo(a, setValue) = RACChannelTo(b, setValue); - - [[b mutableSetValueForKeyPath:@keypath(b.setValue)] addObject:@1]; - expect(a.setValue).to(equal(b.setValue)); - }); - - qck_it(@"should bind changes made by KVC on ordered sets", ^{ - b.orderedSetValue = [NSOrderedSet orderedSet]; - RACChannelTo(a, orderedSetValue) = RACChannelTo(b, orderedSetValue); - - [[b mutableOrderedSetValueForKeyPath:@keypath(b.orderedSetValue)] addObject:@1]; - expect(a.orderedSetValue).to(equal(b.orderedSetValue)); - }); - - qck_it(@"should handle deallocation of intermediate objects correctly even without support from KVO", ^{ - __block BOOL wasDisposed = NO; - - RACChannelTo(a, weakTestObjectValue.stringValue) = RACChannelTo(b, strongTestObjectValue.stringValue); - b.strongTestObjectValue = [[RACTestObject alloc] init]; - - @autoreleasepool { - RACTestObject *object = [[RACTestObject alloc] init]; - [object.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - wasDisposed = YES; - }]]; - - a.weakTestObjectValue = object; - object.stringValue = testName1; - - expect(@(wasDisposed)).to(beFalsy()); - expect(b.strongTestObjectValue.stringValue).to(equal(testName1)); - } - - expect(@(wasDisposed)).toEventually(beTruthy()); - expect(b.strongTestObjectValue.stringValue).to(beNil()); - }); - - qck_it(@"should stop binding when disposed", ^{ - RACChannelTerminal *aTerminal = RACChannelTo(a, stringValue); - RACChannelTerminal *bTerminal = RACChannelTo(b, stringValue); - - a.stringValue = testName1; - RACDisposable *disposable = [aTerminal subscribe:bTerminal]; - - expect(a.stringValue).to(equal(testName1)); - expect(b.stringValue).to(equal(testName1)); - - a.stringValue = testName2; - expect(a.stringValue).to(equal(testName2)); - expect(b.stringValue).to(equal(testName2)); - - [disposable dispose]; - - a.stringValue = testName3; - expect(a.stringValue).to(equal(testName3)); - expect(b.stringValue).to(equal(testName2)); - }); - - qck_it(@"should use the nilValue when sent nil", ^{ - RACChannelTerminal *terminal = RACChannelTo(a, integerValue, @5); - expect(@(a.integerValue)).to(equal(@0)); - - [terminal sendNext:@2]; - expect(@(a.integerValue)).to(equal(@2)); - - [terminal sendNext:nil]; - expect(@(a.integerValue)).to(equal(@5)); - }); - - qck_it(@"should use the nilValue when an intermediate object is nil", ^{ - __block BOOL wasDisposed = NO; - - RACChannelTo(a, weakTestObjectValue.integerValue, @5) = RACChannelTo(b, strongTestObjectValue.integerValue, @5); - b.strongTestObjectValue = [[RACTestObject alloc] init]; - - @autoreleasepool { - RACTestObject *object = [[RACTestObject alloc] init]; - [object.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - wasDisposed = YES; - }]]; - - a.weakTestObjectValue = object; - object.integerValue = 2; - - expect(@(wasDisposed)).to(beFalsy()); - expect(@(b.strongTestObjectValue.integerValue)).to(equal(@2)); - } - - expect(@(wasDisposed)).toEventually(beTruthy()); - expect(@(b.strongTestObjectValue.integerValue)).to(equal(@5)); - }); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/RACKVOProxySpec.m b/ReactiveObjCTests/RACKVOProxySpec.m deleted file mode 100644 index 6be2039f8d..0000000000 --- a/ReactiveObjCTests/RACKVOProxySpec.m +++ /dev/null @@ -1,237 +0,0 @@ -// -// RACKVOProxySpec.m -// ReactiveObjC -// -// Created by Richard Speyer on 4/24/14. -// Copyright (c) 2014 GitHub, Inc. All rights reserved. -// - -#import -#import -#import - -#import "RACKVOProxy.h" - -#import "NSObject+RACKVOWrapper.h" -#import "NSObject+RACPropertySubscribing.h" -#import "RACSerialDisposable.h" -#import "RACSignal+Operations.h" -#import "RACScheduler.h" -#import "RACSubject.h" - -@interface TestObject : NSObject { - volatile int _testInt; - pthread_mutex_t _mutex; -} - -@property (assign, atomic) int testInt; - -@end - -@implementation TestObject - -- (instancetype)init { - if ((self = [super init])) { - pthread_mutex_init(&_mutex, nil); - } - - return self; -} - -- (int)testInt { - int test = 0; - pthread_mutex_lock(&_mutex); - test = _testInt; - pthread_mutex_unlock(&_mutex); - return test; -} - -// Use manual KVO notifications to avoid any possible race conditions within the -// automatic KVO implementation. -- (void)setTestInt:(int)value { - [self willChangeValueForKey:@keypath(self.testInt)]; - pthread_mutex_lock(&_mutex); - _testInt = value; - pthread_mutex_unlock(&_mutex); - [self didChangeValueForKey:@keypath(self.testInt)]; -} - -+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key { - return NO; -} - -@end - -QuickSpecBegin(RACKVOProxySpec) - -qck_describe(@"RACKVOProxy", ^{ - __block TestObject *testObject; - __block dispatch_queue_t concurrentQueue; - - qck_beforeEach(^{ - testObject = [[TestObject alloc] init]; - concurrentQueue = dispatch_queue_create("org.reactivecocoa.ReactiveObjC.RACKVOProxySpec.concurrentQueue", DISPATCH_QUEUE_CONCURRENT); - }); - - qck_afterEach(^{ - dispatch_barrier_sync(concurrentQueue, ^{ - testObject = nil; - }); - }); - - qck_describe(@"basic", ^{ - qck_it(@"should handle multiple observations on the same value", ^{ - __block int observedValue1 = 0; - __block int observedValue2 = 0; - - [[[RACObserve(testObject, testInt) - skip:1] - take:1] - subscribeNext:^(NSNumber *wrappedInt) { - observedValue1 = wrappedInt.intValue; - }]; - - [[[RACObserve(testObject, testInt) - skip:1] - take:1] - subscribeNext:^(NSNumber *wrappedInt) { - observedValue2 = wrappedInt.intValue; - }]; - - testObject.testInt = 2; - - expect(@(observedValue1)).toEventually(equal(@2)); - expect(@(observedValue2)).toEventually(equal(@2)); - }); - - qck_it(@"can remove individual observation", ^{ - __block int observedValue1 = 0; - __block int observedValue2 = 0; - - RACDisposable *disposable1 = [RACObserve(testObject, testInt) subscribeNext:^(NSNumber *wrappedInt) { - observedValue1 = wrappedInt.intValue; - }]; - - [RACObserve(testObject, testInt) subscribeNext:^(NSNumber *wrappedInt) { - observedValue2 = wrappedInt.intValue; - }]; - - testObject.testInt = 2; - - expect(@(observedValue1)).toEventually(equal(@2)); - expect(@(observedValue2)).toEventually(equal(@2)); - - [disposable1 dispose]; - testObject.testInt = 3; - - expect(@(observedValue2)).toEventually(equal(@3)); - expect(@(observedValue1)).to(equal(@2)); - }); - }); - - qck_describe(@"async", ^{ - qck_it(@"should handle changes being made on another queue", ^{ - __block int observedValue = 0; - [[[RACObserve(testObject, testInt) - skip:1] - take:1] - subscribeNext:^(NSNumber *wrappedInt) { - observedValue = wrappedInt.intValue; - }]; - - dispatch_async(concurrentQueue, ^{ - testObject.testInt = 2; - }); - - dispatch_barrier_sync(concurrentQueue, ^{}); - expect(@(observedValue)).toEventually(equal(@2)); - }); - - qck_it(@"should handle changes being made on another queue using deliverOn", ^{ - __block int observedValue = 0; - [[[[RACObserve(testObject, testInt) - skip:1] - take:1] - deliverOn:[RACScheduler mainThreadScheduler]] - subscribeNext:^(NSNumber *wrappedInt) { - observedValue = wrappedInt.intValue; - }]; - - dispatch_async(concurrentQueue, ^{ - testObject.testInt = 2; - }); - - dispatch_barrier_sync(concurrentQueue, ^{}); - expect(@(observedValue)).toEventually(equal(@2)); - }); - - qck_it(@"async disposal of target", ^{ - __block int observedValue; - [[RACObserve(testObject, testInt) - deliverOn:RACScheduler.mainThreadScheduler] - subscribeNext:^(NSNumber *wrappedInt) { - observedValue = wrappedInt.intValue; - }]; - - dispatch_async(concurrentQueue, ^{ - testObject.testInt = 2; - testObject = nil; - }); - - dispatch_barrier_sync(concurrentQueue, ^{}); - expect(@(observedValue)).toEventually(equal(@2)); - }); - }); - - qck_describe(@"stress", ^{ - static const size_t numIterations = 5000; - - __block dispatch_queue_t iterationQueue; - - beforeEach(^{ - iterationQueue = dispatch_queue_create("org.reactivecocoa.ReactiveObjC.RACKVOProxySpec.iterationQueue", DISPATCH_QUEUE_CONCURRENT); - }); - - // ReactiveCocoa/ReactiveCocoa#1122 - qck_it(@"async disposal of observer", ^{ - RACSerialDisposable *disposable = [[RACSerialDisposable alloc] init]; - - dispatch_apply(numIterations, iterationQueue, ^(size_t index) { - RACDisposable *newDisposable = [RACObserve(testObject, testInt) subscribeCompleted:^{}]; - [[disposable swapInDisposable:newDisposable] dispose]; - - dispatch_async(concurrentQueue, ^{ - testObject.testInt = (int)index; - }); - }); - - dispatch_barrier_sync(iterationQueue, ^{ - [disposable dispose]; - }); - }); - - qck_it(@"async disposal of signal with in-flight changes", ^{ - RACSubject *teardown = [RACSubject subject]; - - RACSignal *isEvenSignal = [[[[RACObserve(testObject, testInt) - map:^(NSNumber *wrappedInt) { - return @((wrappedInt.intValue % 2) == 0); - }] - deliverOn:RACScheduler.mainThreadScheduler] - takeUntil:teardown] - replayLast]; - - dispatch_apply(numIterations, iterationQueue, ^(size_t index) { - testObject.testInt = (int)index; - }); - - dispatch_barrier_async(iterationQueue, ^{ - [teardown sendNext:nil]; - }); - - expect(@([isEvenSignal asynchronouslyWaitUntilCompleted:NULL])).to(beTruthy()); - }); - }); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/RACKVOWrapperSpec.m b/ReactiveObjCTests/RACKVOWrapperSpec.m deleted file mode 100644 index 8f75fc3b29..0000000000 --- a/ReactiveObjCTests/RACKVOWrapperSpec.m +++ /dev/null @@ -1,675 +0,0 @@ -// -// RACKVOWrapperSpec.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-08-07. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "NSObject+RACKVOWrapper.h" - -#import -#import "NSObject+RACDeallocating.h" -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACKVOTrampoline.h" -#import "RACTestObject.h" - -@interface RACTestOperation : NSOperation -@end - -// The name of the examples. -static NSString * const RACKVOWrapperExamples = @"RACKVOWrapperExamples"; - -// A block that returns an object to observe in the examples. -static NSString * const RACKVOWrapperExamplesTargetBlock = @"RACKVOWrapperExamplesTargetBlock"; - -// The key path to observe in the examples. -// -// The key path must have at least one weak property in it. -static NSString * const RACKVOWrapperExamplesKeyPath = @"RACKVOWrapperExamplesKeyPath"; - -// A block that changes the value of a weak property in the observed key path. -// The block is passed the object the example is observing and the new value the -// weak property should be changed to( -static NSString * const RACKVOWrapperExamplesChangeBlock = @"RACKVOWrapperExamplesChangeBlock"; - -// A block that returns a valid value for the weak property changed by -// RACKVOWrapperExamplesChangeBlock. The value must deallocate -// normally. -static NSString * const RACKVOWrapperExamplesValueBlock = @"RACKVOWrapperExamplesValueBlock"; - -// Whether RACKVOWrapperExamplesChangeBlock changes the value -// of the last key path component in the key path directly. -static NSString * const RACKVOWrapperExamplesChangesValueDirectly = @"RACKVOWrapperExamplesChangesValueDirectly"; - -// The name of the examples. -static NSString * const RACKVOWrapperCollectionExamples = @"RACKVOWrapperCollectionExamples"; - -// A block that returns an object to observe in the examples. -static NSString * const RACKVOWrapperCollectionExamplesTargetBlock = @"RACKVOWrapperCollectionExamplesTargetBlock"; - -// The key path to observe in the examples. -// -// Must identify a property of type NSOrderedSet. -static NSString * const RACKVOWrapperCollectionExamplesKeyPath = @"RACKVOWrapperCollectionExamplesKeyPath"; - -QuickConfigurationBegin(RACKVOWrapperExampleGroups) - -+ (void)configure:(Configuration *)configuration { - sharedExamples(RACKVOWrapperExamples, ^(QCKDSLSharedExampleContext exampleContext) { - __block NSObject *target = nil; - __block NSString *keyPath = nil; - __block void (^changeBlock)(NSObject *, id) = nil; - __block id (^valueBlock)(void) = nil; - __block BOOL changesValueDirectly = NO; - - __block NSUInteger priorCallCount = 0; - __block NSUInteger posteriorCallCount = 0; - __block BOOL priorTriggeredByLastKeyPathComponent = NO; - __block BOOL posteriorTriggeredByLastKeyPathComponent = NO; - __block BOOL posteriorTriggeredByDeallocation = NO; - __block void (^callbackBlock)(id, NSDictionary *, BOOL, BOOL) = nil; - - qck_beforeEach(^{ - NSObject * (^targetBlock)(void) = exampleContext()[RACKVOWrapperExamplesTargetBlock]; - target = targetBlock(); - keyPath = exampleContext()[RACKVOWrapperExamplesKeyPath]; - changeBlock = exampleContext()[RACKVOWrapperExamplesChangeBlock]; - valueBlock = exampleContext()[RACKVOWrapperExamplesValueBlock]; - changesValueDirectly = [exampleContext()[RACKVOWrapperExamplesChangesValueDirectly] boolValue]; - - priorCallCount = 0; - posteriorCallCount = 0; - - callbackBlock = [^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) { - if ([change[NSKeyValueChangeNotificationIsPriorKey] boolValue]) { - priorTriggeredByLastKeyPathComponent = affectedOnlyLastComponent; - ++priorCallCount; - return; - } - posteriorTriggeredByLastKeyPathComponent = affectedOnlyLastComponent; - posteriorTriggeredByDeallocation = causedByDealloc; - ++posteriorCallCount; - } copy]; - }); - - qck_afterEach(^{ - target = nil; - keyPath = nil; - changeBlock = nil; - valueBlock = nil; - changesValueDirectly = NO; - - callbackBlock = nil; - }); - - qck_it(@"should not call the callback block on add if called without NSKeyValueObservingOptionInitial", ^{ - [target rac_observeKeyPath:keyPath options:NSKeyValueObservingOptionPrior observer:nil block:callbackBlock]; - expect(@(priorCallCount)).to(equal(@0)); - expect(@(posteriorCallCount)).to(equal(@0)); - }); - - qck_it(@"should call the callback block on add if called with NSKeyValueObservingOptionInitial", ^{ - [target rac_observeKeyPath:keyPath options:NSKeyValueObservingOptionPrior | NSKeyValueObservingOptionInitial observer:nil block:callbackBlock]; - expect(@(priorCallCount)).to(equal(@0)); - expect(@(posteriorCallCount)).to(equal(@1)); - }); - - qck_it(@"should call the callback block twice per change, once prior and once posterior", ^{ - [target rac_observeKeyPath:keyPath options:NSKeyValueObservingOptionPrior observer:nil block:callbackBlock]; - priorCallCount = 0; - posteriorCallCount = 0; - - id value1 = valueBlock(); - changeBlock(target, value1); - expect(@(priorCallCount)).to(equal(@1)); - expect(@(posteriorCallCount)).to(equal(@1)); - expect(@(priorTriggeredByLastKeyPathComponent)).to(equal(@(changesValueDirectly))); - expect(@(posteriorTriggeredByLastKeyPathComponent)).to(equal(@(changesValueDirectly))); - expect(@(posteriorTriggeredByDeallocation)).to(beFalsy()); - - id value2 = valueBlock(); - changeBlock(target, value2); - expect(@(priorCallCount)).to(equal(@2)); - expect(@(posteriorCallCount)).to(equal(@2)); - expect(@(priorTriggeredByLastKeyPathComponent)).to(equal(@(changesValueDirectly))); - expect(@(posteriorTriggeredByLastKeyPathComponent)).to(equal(@(changesValueDirectly))); - expect(@(posteriorTriggeredByDeallocation)).to(beFalsy()); - }); - - qck_it(@"should call the callback block with NSKeyValueChangeNotificationIsPriorKey set before the value is changed, and not set after the value is changed", ^{ - __block BOOL priorCalled = NO; - __block BOOL posteriorCalled = NO; - __block id priorValue = nil; - __block id posteriorValue = nil; - - id value1 = valueBlock(); - changeBlock(target, value1); - id oldValue = [target valueForKeyPath:keyPath]; - - [target rac_observeKeyPath:keyPath options:NSKeyValueObservingOptionPrior observer:nil block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) { - if ([change[NSKeyValueChangeNotificationIsPriorKey] boolValue]) { - priorCalled = YES; - priorValue = value; - expect(@(posteriorCalled)).to(beFalsy()); - return; - } - posteriorCalled = YES; - posteriorValue = value; - expect(@(priorCalled)).to(beTruthy()); - }]; - - id value2 = valueBlock(); - changeBlock(target, value2); - id newValue = [target valueForKeyPath:keyPath]; - expect(@(priorCalled)).to(beTruthy()); - expect(priorValue).to(equal(oldValue)); - expect(@(posteriorCalled)).to(beTruthy()); - expect(posteriorValue).to(equal(newValue)); - }); - - qck_it(@"should not call the callback block after it's been disposed", ^{ - RACDisposable *disposable = [target rac_observeKeyPath:keyPath options:NSKeyValueObservingOptionPrior observer:nil block:callbackBlock]; - priorCallCount = 0; - posteriorCallCount = 0; - - [disposable dispose]; - expect(@(priorCallCount)).to(equal(@0)); - expect(@(posteriorCallCount)).to(equal(@0)); - - id value = valueBlock(); - changeBlock(target, value); - expect(@(priorCallCount)).to(equal(@0)); - expect(@(posteriorCallCount)).to(equal(@0)); - }); - - qck_it(@"should call the callback block only once with NSKeyValueChangeNotificationIsPriorKey not set when the value is deallocated", ^{ - __block BOOL valueDidDealloc = NO; - - [target rac_observeKeyPath:keyPath options:NSKeyValueObservingOptionPrior observer:nil block:callbackBlock]; - - @autoreleasepool { - NSObject *value __attribute__((objc_precise_lifetime)) = valueBlock(); - [value.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - valueDidDealloc = YES; - }]]; - - changeBlock(target, value); - priorCallCount = 0; - posteriorCallCount = 0; - } - - expect(@(valueDidDealloc)).to(beTruthy()); - expect(@(priorCallCount)).to(equal(@0)); - expect(@(posteriorCallCount)).to(equal(@1)); - expect(@(posteriorTriggeredByDeallocation)).to(beTruthy()); - }); - }); - - qck_sharedExamples(RACKVOWrapperCollectionExamples, ^(QCKDSLSharedExampleContext exampleContext) { - __block NSObject *target = nil; - __block NSString *keyPath = nil; - __block NSMutableOrderedSet *mutableKeyPathProxy = nil; - __block void (^callbackBlock)(id, NSDictionary *, BOOL, BOOL) = nil; - - __block id priorValue = nil; - __block id posteriorValue = nil; - __block NSDictionary *priorChange = nil; - __block NSDictionary *posteriorChange = nil; - - qck_beforeEach(^{ - NSObject * (^targetBlock)(void) = exampleContext()[RACKVOWrapperCollectionExamplesTargetBlock]; - target = targetBlock(); - keyPath = exampleContext()[RACKVOWrapperCollectionExamplesKeyPath]; - - callbackBlock = [^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) { - if ([change[NSKeyValueChangeNotificationIsPriorKey] boolValue]) { - priorValue = value; - priorChange = change; - return; - } - posteriorValue = value; - posteriorChange = change; - } copy]; - - [target setValue:[NSOrderedSet orderedSetWithObject:@0] forKeyPath:keyPath]; - [target rac_observeKeyPath:keyPath options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld | NSKeyValueObservingOptionPrior observer:nil block:callbackBlock]; - mutableKeyPathProxy = [target mutableOrderedSetValueForKeyPath:keyPath]; - }); - - qck_afterEach(^{ - target = nil; - keyPath = nil; - callbackBlock = nil; - - priorValue = nil; - priorChange = nil; - posteriorValue = nil; - posteriorChange = nil; - }); - - qck_it(@"should support inserting elements into ordered collections", ^{ - [mutableKeyPathProxy insertObject:@1 atIndex:0]; - - expect(priorValue).to(equal([NSOrderedSet orderedSetWithArray:@[ @0 ]])); - expect(posteriorValue).to(equal([NSOrderedSet orderedSetWithArray:(@[ @1, @0 ])])); - expect(priorChange[NSKeyValueChangeKindKey]).to(equal(@(NSKeyValueChangeInsertion))); - expect(posteriorChange[NSKeyValueChangeKindKey]).to(equal(@(NSKeyValueChangeInsertion))); - expect(priorChange[NSKeyValueChangeOldKey]).to(beNil()); - expect(posteriorChange[NSKeyValueChangeNewKey]).to(equal(@[ @1 ])); - expect(priorChange[NSKeyValueChangeIndexesKey]).to(equal([NSIndexSet indexSetWithIndex:0])); - expect(posteriorChange[NSKeyValueChangeIndexesKey]).to(equal([NSIndexSet indexSetWithIndex:0])); - }); - - qck_it(@"should support removing elements from ordered collections", ^{ - [mutableKeyPathProxy removeObjectAtIndex:0]; - - expect(priorValue).to(equal([NSOrderedSet orderedSetWithArray:@[ @0 ]])); - expect(posteriorValue).to(equal([NSOrderedSet orderedSetWithArray:@[]])); - expect(priorChange[NSKeyValueChangeKindKey]).to(equal(@(NSKeyValueChangeRemoval))); - expect(posteriorChange[NSKeyValueChangeKindKey]).to(equal(@(NSKeyValueChangeRemoval))); - expect(priorChange[NSKeyValueChangeOldKey]).to(equal(@[ @0 ])); - expect(posteriorChange[NSKeyValueChangeNewKey]).to(beNil()); - expect(priorChange[NSKeyValueChangeIndexesKey]).to(equal([NSIndexSet indexSetWithIndex:0])); - expect(posteriorChange[NSKeyValueChangeIndexesKey]).to(equal([NSIndexSet indexSetWithIndex:0])); - }); - - qck_it(@"should support replacing elements in ordered collections", ^{ - [mutableKeyPathProxy replaceObjectAtIndex:0 withObject:@1]; - - expect(priorValue).to(equal([NSOrderedSet orderedSetWithArray:@[ @0 ]])); - expect(posteriorValue).to(equal([NSOrderedSet orderedSetWithArray:@[ @1 ]])); - expect(priorChange[NSKeyValueChangeKindKey]).to(equal(@(NSKeyValueChangeReplacement))); - expect(posteriorChange[NSKeyValueChangeKindKey]).to(equal(@(NSKeyValueChangeReplacement))); - expect(priorChange[NSKeyValueChangeOldKey]).to(equal(@[ @0 ])); - expect(posteriorChange[NSKeyValueChangeNewKey]).to(equal(@[ @1 ])); - expect(priorChange[NSKeyValueChangeIndexesKey]).to(equal([NSIndexSet indexSetWithIndex:0])); - expect(posteriorChange[NSKeyValueChangeIndexesKey]).to(equal([NSIndexSet indexSetWithIndex:0])); - }); - - qck_it(@"should support adding elements to unordered collections", ^{ - [mutableKeyPathProxy unionOrderedSet:[NSOrderedSet orderedSetWithObject:@1]]; - - expect(priorValue).to(equal([NSOrderedSet orderedSetWithArray:@[ @0 ]])); - expect(posteriorValue).to(equal([NSOrderedSet orderedSetWithArray:(@[ @0, @1 ])])); - expect(priorChange[NSKeyValueChangeKindKey]).to(equal(@(NSKeyValueChangeInsertion))); - expect(posteriorChange[NSKeyValueChangeKindKey]).to(equal(@(NSKeyValueChangeInsertion))); - expect(priorChange[NSKeyValueChangeOldKey]).to(beNil()); - expect(posteriorChange[NSKeyValueChangeNewKey]).to(equal(@[ @1 ])); - }); - - qck_it(@"should support removing elements from unordered collections", ^{ - [mutableKeyPathProxy minusOrderedSet:[NSOrderedSet orderedSetWithObject:@0]]; - - expect(priorValue).to(equal([NSOrderedSet orderedSetWithArray:@[ @0 ]])); - expect(posteriorValue).to(equal([NSOrderedSet orderedSetWithArray:@[]])); - expect(priorChange[NSKeyValueChangeKindKey]).to(equal(@(NSKeyValueChangeRemoval))); - expect(posteriorChange[NSKeyValueChangeKindKey]).to(equal(@(NSKeyValueChangeRemoval))); - expect(priorChange[NSKeyValueChangeOldKey]).to(equal(@[ @0 ])); - expect(posteriorChange[NSKeyValueChangeNewKey]).to(beNil()); - }); - }); -} - -QuickConfigurationEnd - -QuickSpecBegin(RACKVOWrapperSpec) - -qck_describe(@"-rac_observeKeyPath:options:observer:block:", ^{ - qck_describe(@"on simple keys", ^{ - NSObject * (^targetBlock)(void) = ^{ - return [[RACTestObject alloc] init]; - }; - - void (^changeBlock)(RACTestObject *, id) = ^(RACTestObject *target, id value) { - target.weakTestObjectValue = value; - }; - - id (^valueBlock)(void) = ^{ - return [[RACTestObject alloc] init]; - }; - - qck_itBehavesLike(RACKVOWrapperExamples, ^{ - return @{ - RACKVOWrapperExamplesTargetBlock: targetBlock, - RACKVOWrapperExamplesKeyPath: @keypath(RACTestObject.new, weakTestObjectValue), - RACKVOWrapperExamplesChangeBlock: changeBlock, - RACKVOWrapperExamplesValueBlock: valueBlock, - RACKVOWrapperExamplesChangesValueDirectly: @YES - }; - }); - - qck_itBehavesLike(RACKVOWrapperCollectionExamples, ^{ - return @{ - RACKVOWrapperCollectionExamplesTargetBlock: targetBlock, - RACKVOWrapperCollectionExamplesKeyPath: @keypath(RACTestObject.new, orderedSetValue) - }; - }); - }); - - qck_describe(@"on composite key paths'", ^{ - qck_describe(@"last key path components", ^{ - NSObject *(^targetBlock)(void) = ^{ - RACTestObject *object = [[RACTestObject alloc] init]; - object.strongTestObjectValue = [[RACTestObject alloc] init]; - return object; - }; - - void (^changeBlock)(RACTestObject *, id) = ^(RACTestObject *target, id value) { - target.strongTestObjectValue.weakTestObjectValue = value; - }; - - id (^valueBlock)(void) = ^{ - return [[RACTestObject alloc] init]; - }; - - qck_itBehavesLike(RACKVOWrapperExamples, ^{ - return @{ - RACKVOWrapperExamplesTargetBlock: targetBlock, - RACKVOWrapperExamplesKeyPath: @keypath(RACTestObject.new, strongTestObjectValue.weakTestObjectValue), - RACKVOWrapperExamplesChangeBlock: changeBlock, - RACKVOWrapperExamplesValueBlock: valueBlock, - RACKVOWrapperExamplesChangesValueDirectly: @YES - }; - }); - - qck_itBehavesLike(RACKVOWrapperCollectionExamples, ^{ - return @{ - RACKVOWrapperCollectionExamplesTargetBlock: targetBlock, - RACKVOWrapperCollectionExamplesKeyPath: @keypath(RACTestObject.new, strongTestObjectValue.orderedSetValue) - }; - }); - }); - - qck_describe(@"intermediate key path components", ^{ - NSObject *(^targetBlock)(void) = ^{ - return [[RACTestObject alloc] init]; - }; - - void (^changeBlock)(RACTestObject *, id) = ^(RACTestObject *target, id value) { - target.weakTestObjectValue = value; - }; - - id (^valueBlock)(void) = ^{ - RACTestObject *object = [[RACTestObject alloc] init]; - object.strongTestObjectValue = [[RACTestObject alloc] init]; - return object; - }; - - qck_itBehavesLike(RACKVOWrapperExamples, ^{ - return @{ - RACKVOWrapperExamplesTargetBlock: targetBlock, - RACKVOWrapperExamplesKeyPath: @keypath([[RACTestObject alloc] init], weakTestObjectValue.strongTestObjectValue), - RACKVOWrapperExamplesChangeBlock: changeBlock, - RACKVOWrapperExamplesValueBlock: valueBlock, - RACKVOWrapperExamplesChangesValueDirectly: @NO - }; - }); - }); - - qck_it(@"should not notice deallocation of the object returned by a dynamic final property", ^{ - RACTestObject *object = [[RACTestObject alloc] init]; - - __block id lastValue = nil; - @autoreleasepool { - [object rac_observeKeyPath:@keypath(object.dynamicObjectProperty) options:NSKeyValueObservingOptionInitial observer:nil block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) { - lastValue = value; - }]; - - expect(lastValue).to(beAKindOf(RACTestObject.class)); - } - - expect(lastValue).to(beAKindOf(RACTestObject.class)); - }); - - qck_it(@"should not notice deallocation of the object returned by a dynamic intermediate property", ^{ - RACTestObject *object = [[RACTestObject alloc] init]; - - __block id lastValue = nil; - @autoreleasepool { - [object rac_observeKeyPath:@keypath(object.dynamicObjectProperty.integerValue) options:NSKeyValueObservingOptionInitial observer:nil block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) { - lastValue = value; - }]; - - expect(lastValue).to(equal(@42)); - } - - expect(lastValue).to(equal(@42)); - }); - - qck_it(@"should not notice deallocation of the object returned by a dynamic method", ^{ - RACTestObject *object = [[RACTestObject alloc] init]; - - __block id lastValue = nil; - @autoreleasepool { - [object rac_observeKeyPath:@keypath(object.dynamicObjectMethod) options:NSKeyValueObservingOptionInitial observer:nil block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) { - lastValue = value; - }]; - - expect(lastValue).to(beAKindOf(RACTestObject.class)); - } - - expect(lastValue).to(beAKindOf(RACTestObject.class)); - }); - }); - - qck_it(@"should not call the callback block when the value is the observer", ^{ - __block BOOL observerDisposed = NO; - __block BOOL observerDeallocationTriggeredChange = NO; - __block BOOL targetDisposed = NO; - __block BOOL targetDeallocationTriggeredChange = NO; - - @autoreleasepool { - RACTestObject *observer __attribute__((objc_precise_lifetime)) = [RACTestObject new]; - [observer.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - observerDisposed = YES; - }]]; - - RACTestObject *target __attribute__((objc_precise_lifetime)) = [RACTestObject new]; - [target.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - targetDisposed = YES; - }]]; - - observer.weakTestObjectValue = observer; - target.weakTestObjectValue = target; - - // These observations can only result in dealloc triggered callbacks. - [observer rac_observeKeyPath:@keypath(target.weakTestObjectValue) options:0 observer:observer block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) { - observerDeallocationTriggeredChange = YES; - }]; - - [target rac_observeKeyPath:@keypath(target.weakTestObjectValue) options:0 observer:observer block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) { - targetDeallocationTriggeredChange = YES; - }]; - } - - expect(@(observerDisposed)).to(beTruthy()); - expect(@(observerDeallocationTriggeredChange)).to(beFalsy()); - - expect(@(targetDisposed)).to(beTruthy()); - expect(@(targetDeallocationTriggeredChange)).to(beTruthy()); - }); - - qck_it(@"should call the callback block for deallocation of the initial value of a single-key key path", ^{ - RACTestObject *target = [RACTestObject new]; - __block BOOL objectDisposed = NO; - __block BOOL objectDeallocationTriggeredChange = NO; - - @autoreleasepool { - RACTestObject *object __attribute__((objc_precise_lifetime)) = [RACTestObject new]; - target.weakTestObjectValue = object; - [object.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - objectDisposed = YES; - }]]; - - [target rac_observeKeyPath:@keypath(target.weakTestObjectValue) options:0 observer:target block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) { - objectDeallocationTriggeredChange = YES; - }]; - } - - expect(@(objectDisposed)).to(beTruthy()); - expect(@(objectDeallocationTriggeredChange)).to(beTruthy()); - }); - - qck_it(@"should call the callback block for deallocation of an object conforming to protocol property", ^{ - RACTestObject *target = [RACTestObject new]; - __block BOOL objectDisposed = NO; - __block BOOL objectDeallocationTriggeredChange = NO; - - @autoreleasepool { - RACTestObject *object __attribute__((objc_precise_lifetime)) = [RACTestObject new]; - target.weakObjectWithProtocol = object; - [object.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - objectDisposed = YES; - }]]; - - [target rac_observeKeyPath:@keypath(target.weakObjectWithProtocol) options:0 observer:target block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) { - objectDeallocationTriggeredChange = YES; - }]; - } - - expect(@(objectDisposed)).to(beTruthy()); - expect(@(objectDeallocationTriggeredChange)).to(beTruthy()); - }); -}); - -qck_describe(@"rac_addObserver:forKeyPath:options:block:", ^{ - qck_it(@"should add and remove an observer", ^{ - NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{}]; - expect(operation).notTo(beNil()); - - __block BOOL notified = NO; - RACDisposable *disposable = [operation rac_observeKeyPath:@"isFinished" options:NSKeyValueObservingOptionNew observer:self block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) { - expect([change objectForKey:NSKeyValueChangeNewKey]).to(equal(@YES)); - - expect(@(notified)).to(beFalsy()); - notified = YES; - }]; - - expect(disposable).notTo(beNil()); - - [operation start]; - [operation waitUntilFinished]; - - expect(@(notified)).toEventually(beTruthy()); - }); - - qck_it(@"should accept a nil observer", ^{ - NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{}]; - RACDisposable *disposable = [operation rac_observeKeyPath:@"isFinished" options:NSKeyValueObservingOptionNew observer:nil block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {}]; - - expect(disposable).notTo(beNil()); - }); - - qck_context(@"automatically stops KVO on subclasses when the target deallocates", ^{ - void (^testKVOOnSubclass)(Class targetClass, id observer) = ^(Class targetClass, id observer) { - __weak id weakTarget = nil; - __weak id identifier = nil; - - @autoreleasepool { - // Create an observable target that we control the memory management of. - CFTypeRef target = CFBridgingRetain([[targetClass alloc] init]); - expect((__bridge id)target).notTo(beNil()); - - weakTarget = (__bridge id)target; - expect(weakTarget).notTo(beNil()); - - identifier = [(__bridge id)target rac_observeKeyPath:@"isFinished" options:0 observer:observer block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {}]; - expect(identifier).notTo(beNil()); - - CFRelease(target); - } - - expect(weakTarget).to(beNil()); - expect(identifier).to(beNil()); - }; - - qck_it(@"stops KVO on NSObject subclasses", ^{ - testKVOOnSubclass(NSOperation.class, self); - }); - - qck_it(@"stops KVO on subclasses of already-swizzled classes", ^{ - testKVOOnSubclass(RACTestOperation.class, self); - }); - - qck_it(@"stops KVO on NSObject subclasses even with a nil observer", ^{ - testKVOOnSubclass(NSOperation.class, nil); - }); - - qck_it(@"stops KVO on subclasses of already-swizzled classes even with a nil observer", ^{ - testKVOOnSubclass(RACTestOperation.class, nil); - }); - }); - - qck_it(@"should automatically stop KVO when the observer deallocates", ^{ - __weak id weakObserver = nil; - __weak id weakIdentifier = nil; - - NSOperation *operation = [[NSOperation alloc] init]; - - @autoreleasepool { - // Create an observer that we control the memory management of. - CFTypeRef observer = CFBridgingRetain([[NSOperation alloc] init]); - expect((__bridge id)observer).notTo(beNil()); - - weakObserver = (__bridge id)observer; - expect(weakObserver).notTo(beNil()); - - id identifier = [operation rac_observeKeyPath:@"isFinished" options:0 observer:(__bridge id)observer block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {}]; - expect(identifier).notTo(beNil()); - - weakIdentifier = identifier; - expect(weakIdentifier).notTo(beNil()); - - CFRelease(observer); - } - - expect(weakObserver).to(beNil()); - expect(weakIdentifier).to(beNil()); - }); - - qck_it(@"should stop KVO when the observer is disposed", ^{ - NSOperationQueue *queue = [[NSOperationQueue alloc] init]; - __block NSString *name = nil; - - RACDisposable *disposable = [queue rac_observeKeyPath:@"name" options:0 observer:self block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) { - name = queue.name; - }]; - - queue.name = @"1"; - expect(name).to(equal(@"1")); - [disposable dispose]; - queue.name = @"2"; - expect(name).to(equal(@"1")); - }); - - qck_it(@"should distinguish between observers being disposed", ^{ - NSOperationQueue *queue = [[NSOperationQueue alloc] init]; - __block NSString *name1 = nil; - __block NSString *name2 = nil; - - RACDisposable *disposable = [queue rac_observeKeyPath:@"name" options:0 observer:self block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) { - name1 = queue.name; - }]; - [queue rac_observeKeyPath:@"name" options:0 observer:self block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) { - name2 = queue.name; - }]; - - queue.name = @"1"; - expect(name1).to(equal(@"1")); - expect(name2).to(equal(@"1")); - [disposable dispose]; - queue.name = @"2"; - expect(name1).to(equal(@"1")); - expect(name2).to(equal(@"2")); - }); -}); - -QuickSpecEnd - -@implementation RACTestOperation -@end diff --git a/ReactiveObjCTests/RACMulticastConnectionSpec.m b/ReactiveObjCTests/RACMulticastConnectionSpec.m deleted file mode 100644 index 52ad88593e..0000000000 --- a/ReactiveObjCTests/RACMulticastConnectionSpec.m +++ /dev/null @@ -1,150 +0,0 @@ -// -// RACMulticastConnectionSpec.m -// ReactiveObjC -// -// Created by Josh Abernathy on 10/8/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACMulticastConnection.h" -#import "RACDisposable.h" -#import "RACSignal+Operations.h" -#import "RACSubscriber.h" -#import "RACReplaySubject.h" -#import "RACScheduler.h" -#import - -QuickSpecBegin(RACMulticastConnectionSpec) - -__block NSUInteger subscriptionCount = 0; -__block RACMulticastConnection *connection; - -qck_beforeEach(^{ - subscriptionCount = 0; - connection = [[RACSignal createSignal:^(id subscriber) { - subscriptionCount++; - return (RACDisposable *)nil; - }] publish]; - - expect(@(subscriptionCount)).to(equal(@0)); -}); - -qck_describe(@"-connect", ^{ - qck_it(@"should subscribe to the underlying signal", ^{ - [connection connect]; - expect(@(subscriptionCount)).to(equal(@1)); - }); - - qck_it(@"should return the same disposable for each invocation", ^{ - RACDisposable *d1 = [connection connect]; - RACDisposable *d2 = [connection connect]; - expect(d1).to(equal(d2)); - expect(@(subscriptionCount)).to(equal(@1)); - }); - - qck_it(@"shouldn't reconnect after disposal", ^{ - RACDisposable *disposable1 = [connection connect]; - expect(@(subscriptionCount)).to(equal(@1)); - - [disposable1 dispose]; - - RACDisposable *disposable2 = [connection connect]; - expect(@(subscriptionCount)).to(equal(@1)); - expect(disposable1).to(equal(disposable2)); - }); - - qck_it(@"shouldn't race when connecting", ^{ - dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); - - RACMulticastConnection *connection = [[RACSignal - defer:^ id { - dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); - return nil; - }] - publish]; - - __block RACDisposable *disposable; - [RACScheduler.scheduler schedule:^{ - disposable = [connection connect]; - dispatch_semaphore_signal(semaphore); - }]; - - expect([connection connect]).notTo(beNil()); - dispatch_semaphore_signal(semaphore); - - expect(disposable).toEventuallyNot(beNil()); - }); -}); - -qck_describe(@"-autoconnect", ^{ - __block RACSignal *autoconnectedSignal; - - qck_beforeEach(^{ - autoconnectedSignal = [connection autoconnect]; - }); - - qck_it(@"should subscribe to the multicasted signal on the first subscription", ^{ - expect(@(subscriptionCount)).to(equal(@0)); - - [autoconnectedSignal subscribeNext:^(id x) {}]; - expect(@(subscriptionCount)).to(equal(@1)); - - [autoconnectedSignal subscribeNext:^(id x) {}]; - expect(@(subscriptionCount)).to(equal(@1)); - }); - - qck_it(@"should dispose of the multicasted subscription when the signal has no subscribers", ^{ - __block BOOL disposed = NO; - __block id connectionSubscriber = nil; - RACSignal *signal = [[[RACSignal createSignal:^(id subscriber) { - // Keep the subscriber alive so it doesn't trigger disposal on dealloc - connectionSubscriber = subscriber; - subscriptionCount++; - return [RACDisposable disposableWithBlock:^{ - disposed = YES; - }]; - }] publish] autoconnect]; - RACDisposable *disposable = [signal subscribeNext:^(id x) {}]; - - expect(@(disposed)).to(beFalsy()); - [disposable dispose]; - expect(@(disposed)).to(beTruthy()); - }); - - qck_it(@"shouldn't reconnect after disposal", ^{ - RACDisposable *disposable = [autoconnectedSignal subscribeNext:^(id x) {}]; - expect(@(subscriptionCount)).to(equal(@1)); - [disposable dispose]; - - disposable = [autoconnectedSignal subscribeNext:^(id x) {}]; - expect(@(subscriptionCount)).to(equal(@1)); - [disposable dispose]; - }); - - qck_it(@"should replay values after disposal when multicasted to a replay subject", ^{ - RACSubject *subject = [RACSubject subject]; - RACSignal *signal = [[subject multicast:[RACReplaySubject subject]] autoconnect]; - - NSMutableArray *results1 = [NSMutableArray array]; - RACDisposable *disposable = [signal subscribeNext:^(id x) { - [results1 addObject:x]; - }]; - - [subject sendNext:@1]; - [subject sendNext:@2]; - - expect(results1).to(equal((@[ @1, @2 ]))); - [disposable dispose]; - - NSMutableArray *results2 = [NSMutableArray array]; - [signal subscribeNext:^(id x) { - [results2 addObject:x]; - }]; - expect(results2).toEventually(equal((@[ @1, @2 ]))); - }); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/RACPropertySignalExamples.h b/ReactiveObjCTests/RACPropertySignalExamples.h deleted file mode 100644 index 06b7c51072..0000000000 --- a/ReactiveObjCTests/RACPropertySignalExamples.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// RACPropertySignalExamples.h -// ReactiveObjC -// -// Created by Josh Abernathy on 9/28/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -// The name of the shared examples for a signal-driven property. -extern NSString * const RACPropertySignalExamples; - -// The block should have the signature: -// -// void (^)(RACTestObject *testObject, NSString *keyPath, id nilValue, RACSignal *signal) -// -// and should tie the value of the key path on testObject to signal. `nilValue` -// will be used when the signal sends a `nil` value. -extern NSString * const RACPropertySignalExamplesSetupBlock; diff --git a/ReactiveObjCTests/RACPropertySignalExamples.m b/ReactiveObjCTests/RACPropertySignalExamples.m deleted file mode 100644 index a6339f7d0c..0000000000 --- a/ReactiveObjCTests/RACPropertySignalExamples.m +++ /dev/null @@ -1,144 +0,0 @@ -// -// RACPropertySignalExamples.m -// ReactiveObjC -// -// Created by Josh Abernathy on 9/28/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACTestObject.h" - -#import -#import "NSObject+RACDeallocating.h" -#import "NSObject+RACPropertySubscribing.h" -#import "NSObject+RACSelectorSignal.h" -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACSubject.h" - -NSString * const RACPropertySignalExamples = @"RACPropertySignalExamples"; -NSString * const RACPropertySignalExamplesSetupBlock = @"RACPropertySignalExamplesSetupBlock"; - -QuickConfigurationBegin(RACPropertySignalExampleGroups) - -+ (void)configure:(Configuration *)configuration { - sharedExamples(RACPropertySignalExamples, ^(QCKDSLSharedExampleContext exampleContext) { - __block RACTestObject *testObject = nil; - __block void (^setupBlock)(RACTestObject *, NSString *keyPath, id nilValue, RACSignal *); - - qck_beforeEach(^{ - setupBlock = exampleContext()[RACPropertySignalExamplesSetupBlock]; - testObject = [[RACTestObject alloc] init]; - }); - - qck_it(@"should set the value of the property with the latest value from the signal", ^{ - RACSubject *subject = [RACSubject subject]; - setupBlock(testObject, @keypath(testObject.objectValue), nil, subject); - expect(testObject.objectValue).to(beNil()); - - [subject sendNext:@1]; - expect(testObject.objectValue).to(equal(@1)); - - [subject sendNext:@2]; - expect(testObject.objectValue).to(equal(@2)); - - [subject sendNext:nil]; - expect(testObject.objectValue).to(beNil()); - }); - - qck_it(@"should set the given nilValue for an object property", ^{ - RACSubject *subject = [RACSubject subject]; - setupBlock(testObject, @keypath(testObject.objectValue), @"foo", subject); - expect(testObject.objectValue).to(beNil()); - - [subject sendNext:@1]; - expect(testObject.objectValue).to(equal(@1)); - - [subject sendNext:@2]; - expect(testObject.objectValue).to(equal(@2)); - - [subject sendNext:nil]; - expect(testObject.objectValue).to(equal(@"foo")); - }); - - qck_it(@"should leave the value of the property alone after the signal completes", ^{ - RACSubject *subject = [RACSubject subject]; - setupBlock(testObject, @keypath(testObject.objectValue), nil, subject); - expect(testObject.objectValue).to(beNil()); - - [subject sendNext:@1]; - expect(testObject.objectValue).to(equal(@1)); - - [subject sendCompleted]; - expect(testObject.objectValue).to(equal(@1)); - }); - - qck_it(@"should set the value of a non-object property with the latest value from the signal", ^{ - RACSubject *subject = [RACSubject subject]; - setupBlock(testObject, @keypath(testObject.integerValue), nil, subject); - expect(@(testObject.integerValue)).to(equal(@0)); - - [subject sendNext:@1]; - expect(@(testObject.integerValue)).to(equal(@1)); - - [subject sendNext:@2]; - expect(@(testObject.integerValue)).to(equal(@2)); - - [subject sendNext:@0]; - expect(@(testObject.integerValue)).to(equal(@0)); - }); - - qck_it(@"should set the given nilValue for a non-object property", ^{ - RACSubject *subject = [RACSubject subject]; - setupBlock(testObject, @keypath(testObject.integerValue), @42, subject); - expect(@(testObject.integerValue)).to(equal(@0)); - - [subject sendNext:@1]; - expect(@(testObject.integerValue)).to(equal(@1)); - - [subject sendNext:@2]; - expect(@(testObject.integerValue)).to(equal(@2)); - - [subject sendNext:nil]; - expect(@(testObject.integerValue)).to(equal(@42)); - }); - - qck_it(@"should not invoke -setNilValueForKey: with a nilValue", ^{ - RACSubject *subject = [RACSubject subject]; - setupBlock(testObject, @keypath(testObject.integerValue), @42, subject); - - __block BOOL setNilValueForKeyInvoked = NO; - [[testObject rac_signalForSelector:@selector(setNilValueForKey:)] subscribeNext:^(NSString *key) { - setNilValueForKeyInvoked = YES; - }]; - - [subject sendNext:nil]; - expect(@(testObject.integerValue)).to(equal(@42)); - expect(@(setNilValueForKeyInvoked)).to(beFalsy()); - }); - - qck_it(@"should invoke -setNilValueForKey: without a nilValue", ^{ - RACSubject *subject = [RACSubject subject]; - setupBlock(testObject, @keypath(testObject.integerValue), nil, subject); - - [subject sendNext:@1]; - expect(@(testObject.integerValue)).to(equal(@1)); - - testObject.catchSetNilValueForKey = YES; - - __block BOOL setNilValueForKeyInvoked = NO; - [[testObject rac_signalForSelector:@selector(setNilValueForKey:)] subscribeNext:^(NSString *key) { - setNilValueForKeyInvoked = YES; - }]; - - [subject sendNext:nil]; - expect(@(testObject.integerValue)).to(equal(@1)); - expect(@(setNilValueForKeyInvoked)).to(beTruthy()); - }); - }); -} - -QuickConfigurationEnd diff --git a/ReactiveObjCTests/RACSchedulerSpec.m b/ReactiveObjCTests/RACSchedulerSpec.m deleted file mode 100644 index ba44b11c1e..0000000000 --- a/ReactiveObjCTests/RACSchedulerSpec.m +++ /dev/null @@ -1,439 +0,0 @@ -// -// RACSchedulerSpec.m -// ReactiveObjC -// -// Created by Josh Abernathy on 11/29/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACScheduler.h" -#import "RACScheduler+Private.h" -#import "RACQueueScheduler+Subclass.h" -#import "RACDisposable.h" -#import -#import "RACTestExampleScheduler.h" -#import - -// This shouldn't be used directly. Use the `expectCurrentSchedulers` block -// below instead. -static void expectCurrentSchedulersInner(NSArray *schedulers, NSMutableArray *currentSchedulerArray) { - if (schedulers.count > 0) { - RACScheduler *topScheduler = schedulers[0]; - [topScheduler schedule:^{ - RACScheduler *currentScheduler = RACScheduler.currentScheduler; - if (currentScheduler != nil) [currentSchedulerArray addObject:currentScheduler]; - expectCurrentSchedulersInner([schedulers subarrayWithRange:NSMakeRange(1, schedulers.count - 1)], currentSchedulerArray); - }]; - } -} - -QuickSpecBegin(RACSchedulerSpec) - -qck_it(@"should know its current scheduler", ^{ - // Recursively schedules a block in each of the given schedulers and records - // the +currentScheduler at each step. It then expects the array of - // +currentSchedulers and the expected array to be equal. - // - // schedulers - The array of schedulers to recursively schedule. - // expectedCurrentSchedulers - The array of +currentSchedulers to expect. - void (^expectCurrentSchedulers)(NSArray *, NSArray *) = ^(NSArray *schedulers, NSArray *expectedCurrentSchedulers) { - NSMutableArray *currentSchedulerArray = [NSMutableArray array]; - expectCurrentSchedulersInner(schedulers, currentSchedulerArray); - expect(currentSchedulerArray).toEventually(equal(expectedCurrentSchedulers)); - }; - - RACScheduler *backgroundScheduler = [RACScheduler scheduler]; - - expectCurrentSchedulers(@[ backgroundScheduler, RACScheduler.immediateScheduler ], @[ backgroundScheduler, backgroundScheduler ]); - expectCurrentSchedulers(@[ backgroundScheduler, RACScheduler.subscriptionScheduler ], @[ backgroundScheduler, backgroundScheduler ]); - - NSArray *mainThreadJumper = @[ RACScheduler.mainThreadScheduler, backgroundScheduler, RACScheduler.mainThreadScheduler ]; - expectCurrentSchedulers(mainThreadJumper, mainThreadJumper); - - NSArray *backgroundJumper = @[ backgroundScheduler, RACScheduler.mainThreadScheduler, backgroundScheduler ]; - expectCurrentSchedulers(backgroundJumper, backgroundJumper); -}); - -qck_describe(@"+mainThreadScheduler", ^{ - qck_it(@"should cancel scheduled blocks when disposed", ^{ - __block BOOL firstBlockRan = NO; - __block BOOL secondBlockRan = NO; - - RACDisposable *disposable = [RACScheduler.mainThreadScheduler schedule:^{ - firstBlockRan = YES; - }]; - - expect(disposable).notTo(beNil()); - - [RACScheduler.mainThreadScheduler schedule:^{ - secondBlockRan = YES; - }]; - - [disposable dispose]; - - expect(@(secondBlockRan)).to(beFalsy()); - expect(@(secondBlockRan)).toEventually(beTruthy()); - expect(@(firstBlockRan)).to(beFalsy()); - }); - - qck_it(@"should schedule future blocks", ^{ - __block BOOL done = NO; - - [RACScheduler.mainThreadScheduler after:[NSDate date] schedule:^{ - done = YES; - }]; - - expect(@(done)).to(beFalsy()); - expect(@(done)).toEventually(beTruthy()); - }); - - qck_it(@"should cancel future blocks when disposed", ^{ - __block BOOL firstBlockRan = NO; - __block BOOL secondBlockRan = NO; - - RACDisposable *disposable = [RACScheduler.mainThreadScheduler after:[NSDate date] schedule:^{ - firstBlockRan = YES; - }]; - - expect(disposable).notTo(beNil()); - - [RACScheduler.mainThreadScheduler after:[NSDate date] schedule:^{ - secondBlockRan = YES; - }]; - - [disposable dispose]; - - expect(@(secondBlockRan)).to(beFalsy()); - expect(@(secondBlockRan)).toEventually(beTruthy()); - expect(@(firstBlockRan)).to(beFalsy()); - }); - - qck_it(@"should schedule recurring blocks", ^{ - __block NSUInteger count = 0; - - RACDisposable *disposable = [RACScheduler.mainThreadScheduler after:[NSDate date] repeatingEvery:0.05 withLeeway:0 schedule:^{ - count++; - }]; - - expect(@(count)).to(equal(@0)); - expect(@(count)).toEventually(beGreaterThanOrEqualTo(@1)); - expect(@(count)).toEventually(beGreaterThanOrEqualTo(@2)); - expect(@(count)).toEventually(beGreaterThanOrEqualTo(@3)); - - [disposable dispose]; - [NSRunLoop.mainRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; - - expect(@(count)).to(beGreaterThanOrEqualTo(@3)); - }); -}); - -qck_describe(@"+scheduler", ^{ - __block RACScheduler *scheduler; - __block NSDate * (^futureDate)(void); - - qck_beforeEach(^{ - scheduler = [RACScheduler scheduler]; - - futureDate = ^{ - return [NSDate dateWithTimeIntervalSinceNow:0.01]; - }; - }); - - qck_it(@"should cancel scheduled blocks when disposed", ^{ - __block BOOL firstBlockRan = NO; - __block BOOL secondBlockRan = NO; - - // Start off on the scheduler so the enqueued blocks won't run until we - // return. - [scheduler schedule:^{ - RACDisposable *disposable = [scheduler schedule:^{ - firstBlockRan = YES; - }]; - - expect(disposable).notTo(beNil()); - - [scheduler schedule:^{ - secondBlockRan = YES; - }]; - - [disposable dispose]; - }]; - - expect(@(secondBlockRan)).toEventually(beTruthy()); - expect(@(firstBlockRan)).to(beFalsy()); - }); - - qck_it(@"should schedule future blocks", ^{ - __block BOOL done = NO; - - [scheduler after:futureDate() schedule:^{ - done = YES; - }]; - - expect(@(done)).to(beFalsy()); - expect(@(done)).toEventually(beTruthy()); - }); - - qck_it(@"should cancel future blocks when disposed", ^{ - __block BOOL firstBlockRan = NO; - __block BOOL secondBlockRan = NO; - - NSDate *date = futureDate(); - RACDisposable *disposable = [scheduler after:date schedule:^{ - firstBlockRan = YES; - }]; - - expect(disposable).notTo(beNil()); - [disposable dispose]; - - [scheduler after:date schedule:^{ - secondBlockRan = YES; - }]; - - expect(@(secondBlockRan)).to(beFalsy()); - expect(@(secondBlockRan)).toEventually(beTruthy()); - expect(@(firstBlockRan)).to(beFalsy()); - }); - - qck_it(@"should schedule recurring blocks", ^{ - __block NSUInteger count = 0; - - RACDisposable *disposable = [scheduler after:[NSDate date] repeatingEvery:0.05 withLeeway:0 schedule:^{ - count++; - }]; - - expect(@(count)).to(beGreaterThanOrEqualTo(@0)); - expect(@(count)).toEventually(beGreaterThanOrEqualTo(@1)); - expect(@(count)).toEventually(beGreaterThanOrEqualTo(@2)); - expect(@(count)).toEventually(beGreaterThanOrEqualTo(@3)); - - [disposable dispose]; - [NSThread sleepForTimeInterval:0.1]; - - expect(@(count)).to(beGreaterThanOrEqualTo(@3)); - }); -}); - -qck_describe(@"+subscriptionScheduler", ^{ - qck_describe(@"setting +currentScheduler", ^{ - __block RACScheduler *currentScheduler; - - qck_beforeEach(^{ - currentScheduler = nil; - }); - - qck_it(@"should be the +mainThreadScheduler when scheduled from the main queue", ^{ - dispatch_async(dispatch_get_main_queue(), ^{ - [RACScheduler.subscriptionScheduler schedule:^{ - currentScheduler = RACScheduler.currentScheduler; - }]; - }); - - expect(currentScheduler).toEventually(equal(RACScheduler.mainThreadScheduler)); - }); - - qck_it(@"should be a +scheduler when scheduled from an unknown queue", ^{ - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [RACScheduler.subscriptionScheduler schedule:^{ - currentScheduler = RACScheduler.currentScheduler; - }]; - }); - - expect(currentScheduler).toEventuallyNot(beNil()); - expect(currentScheduler).notTo(equal(RACScheduler.mainThreadScheduler)); - }); - - qck_it(@"should equal the background scheduler from which the block was scheduled", ^{ - RACScheduler *backgroundScheduler = [RACScheduler scheduler]; - [backgroundScheduler schedule:^{ - [RACScheduler.subscriptionScheduler schedule:^{ - currentScheduler = RACScheduler.currentScheduler; - }]; - }]; - - expect(currentScheduler).toEventually(equal(backgroundScheduler)); - }); - }); - - qck_it(@"should execute scheduled blocks immediately if it's in a scheduler already", ^{ - __block BOOL done = NO; - __block BOOL executedImmediately = NO; - - [[RACScheduler scheduler] schedule:^{ - [RACScheduler.subscriptionScheduler schedule:^{ - executedImmediately = YES; - }]; - - done = YES; - }]; - - expect(@(done)).toEventually(beTruthy()); - expect(@(executedImmediately)).to(beTruthy()); - }); -}); - -qck_describe(@"+immediateScheduler", ^{ - qck_it(@"should immediately execute scheduled blocks", ^{ - __block BOOL executed = NO; - RACDisposable *disposable = [RACScheduler.immediateScheduler schedule:^{ - executed = YES; - }]; - - expect(disposable).to(beNil()); - expect(@(executed)).to(beTruthy()); - }); - - qck_it(@"should block for future scheduled blocks", ^{ - __block BOOL executed = NO; - RACDisposable *disposable = [RACScheduler.immediateScheduler after:[NSDate dateWithTimeIntervalSinceNow:0.01] schedule:^{ - executed = YES; - }]; - - expect(@(executed)).to(beTruthy()); - expect(disposable).to(beNil()); - }); -}); - -qck_describe(@"-scheduleRecursiveBlock:", ^{ - qck_describe(@"with a synchronous scheduler", ^{ - qck_it(@"should behave like a normal block when it doesn't invoke itself", ^{ - __block BOOL executed = NO; - [RACScheduler.immediateScheduler scheduleRecursiveBlock:^(void (^recurse)(void)) { - expect(@(executed)).to(beFalsy()); - executed = YES; - }]; - - expect(@(executed)).to(beTruthy()); - }); - - qck_it(@"should reschedule itself after the caller completes", ^{ - __block NSUInteger count = 0; - [RACScheduler.immediateScheduler scheduleRecursiveBlock:^(void (^recurse)(void)) { - NSUInteger thisCount = ++count; - if (thisCount < 3) { - recurse(); - - // The block shouldn't have been invoked again yet, only - // scheduled. - expect(@(count)).to(equal(@(thisCount))); - } - }]; - - expect(@(count)).to(equal(@3)); - }); - - qck_it(@"should unroll deep recursion", ^{ - static const NSUInteger depth = 100000; - __block NSUInteger scheduleCount = 0; - [RACScheduler.immediateScheduler scheduleRecursiveBlock:^(void (^recurse)(void)) { - scheduleCount++; - - if (scheduleCount < depth) recurse(); - }]; - - expect(@(scheduleCount)).to(equal(@(depth))); - }); - }); - - qck_describe(@"with an asynchronous scheduler", ^{ - qck_it(@"should behave like a normal block when it doesn't invoke itself", ^{ - __block BOOL executed = NO; - [RACScheduler.mainThreadScheduler scheduleRecursiveBlock:^(void (^recurse)(void)) { - expect(@(executed)).to(beFalsy()); - executed = YES; - }]; - - expect(@(executed)).toEventually(beTruthy()); - }); - - qck_it(@"should reschedule itself after the caller completes", ^{ - __block NSUInteger count = 0; - [RACScheduler.mainThreadScheduler scheduleRecursiveBlock:^(void (^recurse)(void)) { - NSUInteger thisCount = ++count; - if (thisCount < 3) { - recurse(); - - // The block shouldn't have been invoked again yet, only - // scheduled. - expect(@(count)).to(equal(@(thisCount))); - } - }]; - - expect(@(count)).toEventually(equal(@3)); - }); - - qck_it(@"should reschedule when invoked asynchronously", ^{ - __block NSUInteger count = 0; - - RACScheduler *asynchronousScheduler = [RACScheduler scheduler]; - [RACScheduler.mainThreadScheduler scheduleRecursiveBlock:^(void (^recurse)(void)) { - [asynchronousScheduler after:[NSDate dateWithTimeIntervalSinceNow:0.01] schedule:^{ - NSUInteger thisCount = ++count; - if (thisCount < 3) { - recurse(); - - // The block shouldn't have been invoked again yet, only - // scheduled. - expect(@(count)).to(equal(@(thisCount))); - } - }]; - }]; - - expect(@(count)).toEventually(equal(@3)); - }); - - qck_it(@"shouldn't reschedule itself when disposed", ^{ - __block NSUInteger count = 0; - __block RACDisposable *disposable = [RACScheduler.mainThreadScheduler scheduleRecursiveBlock:^(void (^recurse)(void)) { - ++count; - - expect(disposable).notTo(beNil()); - [disposable dispose]; - - recurse(); - }]; - - expect(@(count)).toEventually(equal(@1)); - }); - }); -}); - -qck_describe(@"subclassing", ^{ - __block RACTestExampleScheduler *scheduler; - - qck_beforeEach(^{ - scheduler = [[RACTestExampleScheduler alloc] initWithQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)]; - }); - - qck_it(@"should invoke blocks scheduled with -schedule:", ^{ - __block BOOL invoked = NO; - [scheduler schedule:^{ - invoked = YES; - }]; - - expect(@(invoked)).toEventually(beTruthy()); - }); - - qck_it(@"should invoke blocks scheduled with -after:schedule:", ^{ - __block BOOL invoked = NO; - [scheduler after:[NSDate dateWithTimeIntervalSinceNow:0.01] schedule:^{ - invoked = YES; - }]; - - expect(@(invoked)).toEventually(beTruthy()); - }); - - qck_it(@"should set a valid current scheduler", ^{ - __block RACScheduler *currentScheduler; - [scheduler schedule:^{ - currentScheduler = RACScheduler.currentScheduler; - }]; - - expect(currentScheduler).toEventually(equal(scheduler)); - }); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/RACSequenceAdditionsSpec.m b/ReactiveObjCTests/RACSequenceAdditionsSpec.m deleted file mode 100644 index 170aeb67f6..0000000000 --- a/ReactiveObjCTests/RACSequenceAdditionsSpec.m +++ /dev/null @@ -1,359 +0,0 @@ -// -// RACSequenceAdditionsSpec.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-11-01. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACSequenceExamples.h" - -#import "NSArray+RACSequenceAdditions.h" -#import "NSDictionary+RACSequenceAdditions.h" -#import "NSOrderedSet+RACSequenceAdditions.h" -#import "NSSet+RACSequenceAdditions.h" -#import "NSString+RACSequenceAdditions.h" -#import "NSIndexSet+RACSequenceAdditions.h" -#import "RACSequence.h" -#import "RACTuple.h" - -QuickSpecBegin(RACSequenceAdditionsSpec) - -__block NSArray *numbers; - -qck_beforeEach(^{ - NSMutableArray *mutableNumbers = [NSMutableArray array]; - for (NSUInteger i = 0; i < 100; i++) { - [mutableNumbers addObject:@(i)]; - } - - numbers = [mutableNumbers copy]; -}); - -qck_describe(@"NSArray sequences", ^{ - __block NSMutableArray *values; - __block RACSequence *sequence; - - qck_beforeEach(^{ - values = [numbers mutableCopy]; - sequence = values.rac_sequence; - expect(sequence).notTo(beNil()); - }); - - qck_itBehavesLike(RACSequenceExamples, ^{ - return @{ - RACSequenceExampleSequence: sequence, - RACSequenceExampleExpectedValues: values - }; - }); - - qck_describe(@"should be immutable", ^{ - __block NSArray *unchangedValues; - - qck_beforeEach(^{ - unchangedValues = [values copy]; - [values addObject:@6]; - }); - - qck_itBehavesLike(RACSequenceExamples, ^{ - return @{ - RACSequenceExampleSequence: sequence, - RACSequenceExampleExpectedValues: unchangedValues - }; - }); - }); - - qck_it(@"should fast enumerate after zipping", ^{ - // This certain list of values causes issues, for some reason. - NSArray *values = @[ @0, @0, @0, @0, @0, @0, @0, @0, @0, @0, @0, @0, @0, @0, @0, @0 ]; - RACSequence *zippedSequence = [RACSequence zip:@[ values.rac_sequence, values.rac_sequence ] reduce:^(id obj1, id obj2) { - return obj1; - }]; - - NSMutableArray *collectedValues = [NSMutableArray array]; - for (id value in zippedSequence) { - [collectedValues addObject:value]; - } - - expect(collectedValues).to(equal(values)); - }); -}); - -qck_describe(@"NSDictionary sequences", ^{ - __block NSMutableDictionary *dict; - - __block NSMutableArray *tuples; - __block RACSequence *tupleSequence; - - __block NSArray *keys; - __block RACSequence *keySequence; - - __block NSArray *values; - __block RACSequence *valueSequence; - - qck_beforeEach(^{ - dict = [@{ - @"foo": @"bar", - @"baz": @"buzz", - @5: NSNull.null - } mutableCopy]; - - tuples = [NSMutableArray array]; - for (id key in dict) { - RACTuple *tuple = RACTuplePack(key, dict[key]); - [tuples addObject:tuple]; - } - - tupleSequence = dict.rac_sequence; - expect(tupleSequence).notTo(beNil()); - - keys = [dict.allKeys copy]; - keySequence = dict.rac_keySequence; - expect(keySequence).notTo(beNil()); - - values = [dict.allValues copy]; - valueSequence = dict.rac_valueSequence; - expect(valueSequence).notTo(beNil()); - }); - - qck_itBehavesLike(RACSequenceExamples, ^{ - return @{ - RACSequenceExampleSequence: tupleSequence, - RACSequenceExampleExpectedValues: tuples - }; - }); - - qck_itBehavesLike(RACSequenceExamples, ^{ - return @{ - RACSequenceExampleSequence: keySequence, - RACSequenceExampleExpectedValues: keys - }; - }); - - qck_itBehavesLike(RACSequenceExamples, ^{ - return @{ - RACSequenceExampleSequence: valueSequence, - RACSequenceExampleExpectedValues: values - }; - }); - - qck_describe(@"should be immutable", ^{ - qck_beforeEach(^{ - dict[@"foo"] = @"rab"; - dict[@6] = @7; - }); - - qck_itBehavesLike(RACSequenceExamples, ^{ - return @{ - RACSequenceExampleSequence: tupleSequence, - RACSequenceExampleExpectedValues: tuples - }; - }); - - qck_itBehavesLike(RACSequenceExamples, ^{ - return @{ - RACSequenceExampleSequence: keySequence, - RACSequenceExampleExpectedValues: keys - }; - }); - - qck_itBehavesLike(RACSequenceExamples, ^{ - return @{ - RACSequenceExampleSequence: valueSequence, - RACSequenceExampleExpectedValues: values - }; - }); - }); -}); - -qck_describe(@"NSOrderedSet sequences", ^{ - __block NSMutableOrderedSet *values; - __block RACSequence *sequence; - - qck_beforeEach(^{ - values = [NSMutableOrderedSet orderedSetWithArray:numbers]; - sequence = values.rac_sequence; - expect(sequence).notTo(beNil()); - }); - - qck_itBehavesLike(RACSequenceExamples, ^{ - return @{ - RACSequenceExampleSequence: sequence, - RACSequenceExampleExpectedValues: values.array - }; - }); - - qck_describe(@"should be immutable", ^{ - __block NSArray *unchangedValues; - - qck_beforeEach(^{ - unchangedValues = [values.array copy]; - [values addObject:@6]; - }); - - qck_itBehavesLike(RACSequenceExamples, ^{ - return @{ - RACSequenceExampleSequence: sequence, - RACSequenceExampleExpectedValues: unchangedValues - }; - }); - }); -}); - -qck_describe(@"NSSet sequences", ^{ - __block NSMutableSet *values; - __block RACSequence *sequence; - - qck_beforeEach(^{ - values = [NSMutableSet setWithArray:numbers]; - sequence = values.rac_sequence; - expect(sequence).notTo(beNil()); - }); - - qck_itBehavesLike(RACSequenceExamples, ^{ - return @{ - RACSequenceExampleSequence: sequence, - RACSequenceExampleExpectedValues: values.allObjects - }; - }); - - qck_describe(@"should be immutable", ^{ - __block NSArray *unchangedValues; - - qck_beforeEach(^{ - unchangedValues = [values.allObjects copy]; - [values addObject:@6]; - }); - - qck_itBehavesLike(RACSequenceExamples, ^{ - return @{ - RACSequenceExampleSequence: sequence, - RACSequenceExampleExpectedValues: unchangedValues - }; - }); - }); -}); - -qck_describe(@"NSString sequences", ^{ - __block NSMutableString *string; - __block NSArray *values; - __block RACSequence *sequence; - - qck_beforeEach(^{ - string = [@"foobar" mutableCopy]; - values = @[ @"f", @"o", @"o", @"b", @"a", @"r" ]; - sequence = string.rac_sequence; - expect(sequence).notTo(beNil()); - }); - - qck_itBehavesLike(RACSequenceExamples, ^{ - return @{ - RACSequenceExampleSequence: sequence, - RACSequenceExampleExpectedValues: values - }; - }); - - qck_describe(@"should be immutable", ^{ - qck_beforeEach(^{ - [string appendString:@"buzz"]; - }); - - qck_itBehavesLike(RACSequenceExamples, ^{ - return @{ - RACSequenceExampleSequence: sequence, - RACSequenceExampleExpectedValues: values - }; - }); - }); - - qck_it(@"should work with composed characters", ^{ - NSString *string = @"\u2665\uFE0F\u2666\uFE0F"; - NSArray *expectedSequence = @[ @"\u2665\uFE0F", @"\u2666\uFE0F" ]; - expect(string.rac_sequence.array).to(equal(expectedSequence)); - }); -}); - -qck_describe(@"RACTuple sequences", ^{ - __block RACTuple *tuple; - __block RACSequence *sequence; - - qck_beforeEach(^{ - tuple = RACTuplePack(@"foo", nil, @"bar", NSNull.null, RACTupleNil.tupleNil); - - sequence = tuple.rac_sequence; - expect(sequence).notTo(beNil()); - }); - - qck_itBehavesLike(RACSequenceExamples, ^{ - return @{ - RACSequenceExampleSequence: sequence, - RACSequenceExampleExpectedValues: @[ @"foo", NSNull.null, @"bar", NSNull.null, NSNull.null ] - }; - }); -}); - -qck_describe(@"NSIndexSet sequences", ^{ - __block NSMutableIndexSet *values; - __block RACSequence *sequence; - - NSArray * (^valuesFromIndexSet)(NSIndexSet *indexSet) = ^NSArray *(NSIndexSet *indexSet) { - NSMutableArray *arr = [NSMutableArray array]; - [indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { - [arr addObject:@(idx)]; - }]; - - return [arr copy]; - }; - - qck_beforeEach(^{ - values = [NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 10)]; - sequence = values.rac_sequence; - expect(sequence).notTo(beNil()); - }); - - qck_itBehavesLike(RACSequenceExamples, ^{ - return @{ - RACSequenceExampleSequence: sequence, - RACSequenceExampleExpectedValues: valuesFromIndexSet(values) - }; - }); - - qck_describe(@"should be immutable", ^{ - __block NSArray *unchangedValues; - - qck_beforeEach(^{ - unchangedValues = valuesFromIndexSet(values); - [values addIndex:20]; - }); - - qck_itBehavesLike(RACSequenceExamples, ^{ - return @{ - RACSequenceExampleSequence: sequence, - RACSequenceExampleExpectedValues: unchangedValues - }; - }); - }); - - qck_describe(@"should not fire if empty", ^{ - __block NSIndexSet *emptyIndexSet; - __block RACSequence *emptySequence; - - qck_beforeEach(^{ - emptyIndexSet = [NSIndexSet indexSet]; - emptySequence = emptyIndexSet.rac_sequence; - expect(emptySequence).notTo(beNil()); - }); - - qck_itBehavesLike(RACSequenceExamples, ^{ - return @{ - RACSequenceExampleSequence: emptySequence, - RACSequenceExampleExpectedValues: valuesFromIndexSet(emptyIndexSet) - }; - }); - }); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/RACSequenceExamples.h b/ReactiveObjCTests/RACSequenceExamples.h deleted file mode 100644 index 2e56acc0f5..0000000000 --- a/ReactiveObjCTests/RACSequenceExamples.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// RACSequenceExamples.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-11-01. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -// The name of the shared examples for RACSequence instances. -extern NSString * const RACSequenceExamples; - -// RACSequence * -extern NSString * const RACSequenceExampleSequence; - -// NSArray * -extern NSString * const RACSequenceExampleExpectedValues; diff --git a/ReactiveObjCTests/RACSequenceExamples.m b/ReactiveObjCTests/RACSequenceExamples.m deleted file mode 100644 index c8e209732d..0000000000 --- a/ReactiveObjCTests/RACSequenceExamples.m +++ /dev/null @@ -1,131 +0,0 @@ -// -// RACSequenceExamples.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-11-01. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACSequenceExamples.h" - -#import "RACScheduler.h" -#import "RACSequence.h" -#import "RACSignal+Operations.h" - -NSString * const RACSequenceExamples = @"RACSequenceExamples"; -NSString * const RACSequenceExampleSequence = @"RACSequenceExampleSequence"; -NSString * const RACSequenceExampleExpectedValues = @"RACSequenceExampleExpectedValues"; - -QuickConfigurationBegin(RACSequenceExampleGroups) - -+ (void)configure:(Configuration *)configuration { - sharedExamples(RACSequenceExamples, ^(QCKDSLSharedExampleContext exampleContext) { - __block RACSequence *sequence; - __block NSArray *values; - - qck_beforeEach(^{ - sequence = exampleContext()[RACSequenceExampleSequence]; - values = [exampleContext()[RACSequenceExampleExpectedValues] copy]; - }); - - qck_it(@"should implement ", ^{ - NSMutableArray *collectedValues = [NSMutableArray array]; - for (id value in sequence) { - [collectedValues addObject:value]; - } - - expect(collectedValues).to(equal(values)); - }); - - qck_it(@"should return an array", ^{ - expect(sequence.array).to(equal(values)); - }); - - qck_describe(@"-signalWithScheduler:", ^{ - qck_it(@"should return an immediately scheduled signal", ^{ - RACSignal *signal = [sequence signalWithScheduler:RACScheduler.immediateScheduler]; - expect(signal.toArray).to(equal(values)); - }); - - qck_it(@"should return a background scheduled signal", ^{ - RACSignal *signal = [sequence signalWithScheduler:[RACScheduler scheduler]]; - expect(signal.toArray).to(equal(values)); - }); - - qck_it(@"should only evaluate one value per scheduling", ^{ - RACScheduler* scheduler = [RACScheduler schedulerWithPriority:RACSchedulerPriorityHigh]; - RACSignal *signal = [sequence signalWithScheduler:scheduler]; - - __block BOOL flag = YES; - __block BOOL completed = NO; - [signal subscribeNext:^(id x) { - expect(@(flag)).to(beTruthy()); - flag = NO; - - [scheduler schedule:^{ - // This should get executed before the next value (which - // verifies that it's YES). - flag = YES; - }]; - } completed:^{ - completed = YES; - }]; - - expect(@(completed)).toEventually(beTruthy()); - }); - }); - - qck_it(@"should be equal to itself", ^{ - expect(sequence).to(equal(sequence)); - }); - - qck_it(@"should be equal to the same sequence of values", ^{ - RACSequence *newSequence = RACSequence.empty; - for (id value in values) { - RACSequence *valueSeq = [RACSequence return:value]; - expect(valueSeq).notTo(beNil()); - - newSequence = [newSequence concat:valueSeq]; - } - - expect(sequence).to(equal(newSequence)); - expect(@(sequence.hash)).to(equal(@(newSequence.hash))); - }); - - qck_it(@"should not be equal to a different sequence of values", ^{ - RACSequence *anotherSequence = [RACSequence return:@(-1)]; - expect(sequence).notTo(equal(anotherSequence)); - }); - - qck_it(@"should return an identical object for -copy", ^{ - expect([sequence copy]).to(beIdenticalTo(sequence)); - }); - - qck_it(@"should archive", ^{ - NSData *data = [NSKeyedArchiver archivedDataWithRootObject:sequence]; - expect(data).notTo(beNil()); - - RACSequence *unarchived = [NSKeyedUnarchiver unarchiveObjectWithData:data]; - expect(unarchived).to(equal(sequence)); - }); - - qck_it(@"should fold right", ^{ - RACSequence *result = [sequence foldRightWithStart:[RACSequence empty] reduce:^(id first, RACSequence *rest) { - return [rest.head startWith:first]; - }]; - expect(result.array).to(equal(values)); - }); - - qck_it(@"should fold left", ^{ - RACSequence *result = [sequence foldLeftWithStart:[RACSequence empty] reduce:^(RACSequence *first, id rest) { - return [first concat:[RACSequence return:rest]]; - }]; - expect(result.array).to(equal(values)); - }); - }); -} - -QuickConfigurationEnd diff --git a/ReactiveObjCTests/RACSequenceSpec.m b/ReactiveObjCTests/RACSequenceSpec.m deleted file mode 100644 index c6f8e00cf2..0000000000 --- a/ReactiveObjCTests/RACSequenceSpec.m +++ /dev/null @@ -1,443 +0,0 @@ -// -// RACSequenceSpec.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-11-01. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACSequenceExamples.h" -#import "RACStreamExamples.h" - -#import "NSArray+RACSequenceAdditions.h" -#import "NSObject+RACDeallocating.h" -#import "NSObject+RACPropertySubscribing.h" -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACSequence.h" -#import "RACUnit.h" - -QuickSpecBegin(RACSequenceSpec) - -qck_describe(@"RACStream", ^{ - id verifyValues = ^(RACSequence *sequence, NSArray *expectedValues) { - NSMutableArray *collectedValues = [NSMutableArray array]; - while (sequence.head != nil) { - [collectedValues addObject:sequence.head]; - sequence = sequence.tail; - } - - expect(collectedValues).to(equal(expectedValues)); - }; - - __block RACSequence *infiniteSequence = [RACSequence sequenceWithHeadBlock:^{ - return RACUnit.defaultUnit; - } tailBlock:^{ - return infiniteSequence; - }]; - - qck_itBehavesLike(RACStreamExamples, ^{ - return @{ - RACStreamExamplesClass: RACSequence.class, - RACStreamExamplesVerifyValuesBlock: verifyValues, - RACStreamExamplesInfiniteStream: infiniteSequence - }; - }); -}); - -qck_describe(@"+sequenceWithHeadBlock:tailBlock:", ^{ - __block RACSequence *sequence; - __block BOOL headInvoked; - __block BOOL tailInvoked; - - qck_beforeEach(^{ - headInvoked = NO; - tailInvoked = NO; - - sequence = [RACSequence sequenceWithHeadBlock:^{ - headInvoked = YES; - return @0; - } tailBlock:^{ - tailInvoked = YES; - return [RACSequence return:@1]; - }]; - - expect(sequence).notTo(beNil()); - }); - - qck_it(@"should use the values from the head and tail blocks", ^{ - expect(sequence.head).to(equal(@0)); - expect(sequence.tail.head).to(equal(@1)); - expect(sequence.tail.tail).to(beNil()); - }); - - qck_it(@"should lazily invoke head and tail blocks", ^{ - expect(@(headInvoked)).to(beFalsy()); - expect(@(tailInvoked)).to(beFalsy()); - - expect(sequence.head).to(equal(@0)); - expect(@(headInvoked)).to(beTruthy()); - expect(@(tailInvoked)).to(beFalsy()); - - expect(sequence.tail).notTo(beNil()); - expect(@(tailInvoked)).to(beTruthy()); - }); - - qck_context(@"behaves like a sequence", ^{ - qck_itBehavesLike(RACSequenceExamples, ^{ - return @{ - RACSequenceExampleSequence: sequence, - RACSequenceExampleExpectedValues: @[ @0, @1 ] - }; - }); - }); -}); - -qck_describe(@"empty sequences", ^{ - qck_itBehavesLike(RACSequenceExamples, ^{ - return @{ - RACSequenceExampleSequence: [RACSequence empty], - RACSequenceExampleExpectedValues: @[] - }; - }); -}); - -qck_describe(@"non-empty sequences", ^{ - qck_itBehavesLike(RACSequenceExamples, ^{ - return @{ - RACSequenceExampleSequence: [[[RACSequence return:@0] concat:[RACSequence return:@1]] concat:[RACSequence return:@2]], - RACSequenceExampleExpectedValues: @[ @0, @1, @2 ] - }; - }); -}); - -qck_describe(@"eager sequences", ^{ - __block RACSequence *lazySequence; - __block BOOL headInvoked; - __block BOOL tailInvoked; - - NSArray *values = @[ @0, @1 ]; - - qck_beforeEach(^{ - headInvoked = NO; - tailInvoked = NO; - - lazySequence = [RACSequence sequenceWithHeadBlock:^{ - headInvoked = YES; - return @0; - } tailBlock:^{ - tailInvoked = YES; - return [RACSequence return:@1]; - }]; - - expect(lazySequence).notTo(beNil()); - }); - - qck_itBehavesLike(RACSequenceExamples, ^{ - return @{ - RACSequenceExampleSequence: lazySequence.eagerSequence, - RACSequenceExampleExpectedValues: values - }; - }); - - qck_it(@"should evaluate all values immediately", ^{ - RACSequence *eagerSequence = lazySequence.eagerSequence; - expect(@(headInvoked)).to(beTruthy()); - expect(@(tailInvoked)).to(beTruthy()); - expect(eagerSequence.array).to(equal(values)); - }); -}); - -qck_describe(@"-take:", ^{ - qck_it(@"should complete take: without needing the head of the second item in the sequence", ^{ - __block NSUInteger valuesTaken = 0; - - __block RACSequence *sequence = [RACSequence sequenceWithHeadBlock:^{ - ++valuesTaken; - return RACUnit.defaultUnit; - } tailBlock:^{ - return sequence; - }]; - - NSArray *values = [sequence take:1].array; - expect(values).to(equal(@[ RACUnit.defaultUnit ])); - expect(@(valuesTaken)).to(equal(@1)); - }); -}); - -qck_describe(@"-bind:", ^{ - qck_it(@"should only evaluate head when the resulting sequence is evaluated", ^{ - __block BOOL headInvoked = NO; - - RACSequence *original = [RACSequence sequenceWithHeadBlock:^{ - headInvoked = YES; - return RACUnit.defaultUnit; - } tailBlock:^ id { - return nil; - }]; - - RACSequence *bound = [original bind:^{ - return ^(id value, BOOL *stop) { - return [RACSequence return:value]; - }; - }]; - - expect(bound).notTo(beNil()); - expect(@(headInvoked)).to(beFalsy()); - - expect(bound.head).to(equal(RACUnit.defaultUnit)); - expect(@(headInvoked)).to(beTruthy()); - }); -}); - -qck_describe(@"-objectEnumerator", ^{ - qck_it(@"should only evaluate head as it's enumerated", ^{ - __block BOOL firstHeadInvoked = NO; - __block BOOL secondHeadInvoked = NO; - __block BOOL thirdHeadInvoked = NO; - - RACSequence *sequence = [RACSequence sequenceWithHeadBlock:^id{ - firstHeadInvoked = YES; - return @1; - } tailBlock:^RACSequence *{ - return [RACSequence sequenceWithHeadBlock:^id{ - secondHeadInvoked = YES; - return @2; - } tailBlock:^RACSequence *{ - return [RACSequence sequenceWithHeadBlock:^id{ - thirdHeadInvoked = YES; - return @3; - } tailBlock:^RACSequence *{ - return RACSequence.empty; - }]; - }]; - }]; - NSEnumerator *enumerator = sequence.objectEnumerator; - - expect(@(firstHeadInvoked)).to(beFalsy()); - expect(@(secondHeadInvoked)).to(beFalsy()); - expect(@(thirdHeadInvoked)).to(beFalsy()); - - expect([enumerator nextObject]).to(equal(@1)); - - expect(@(firstHeadInvoked)).to(beTruthy()); - expect(@(secondHeadInvoked)).to(beFalsy()); - expect(@(thirdHeadInvoked)).to(beFalsy()); - - expect([enumerator nextObject]).to(equal(@2)); - - expect(@(secondHeadInvoked)).to(beTruthy()); - expect(@(thirdHeadInvoked)).to(beFalsy()); - - expect([enumerator nextObject]).to(equal(@3)); - - expect(@(thirdHeadInvoked)).to(beTruthy()); - - expect([enumerator nextObject]).to(beNil()); - }); - - qck_it(@"should let the sequence dealloc as it's enumerated", ^{ - __block BOOL firstSequenceDeallocd = NO; - __block BOOL secondSequenceDeallocd = NO; - __block BOOL thirdSequenceDeallocd = NO; - - NSEnumerator *enumerator = nil; - - @autoreleasepool { - RACSequence *thirdSequence __attribute__((objc_precise_lifetime)) = [RACSequence sequenceWithHeadBlock:^id{ - return @3; - } tailBlock:^RACSequence *{ - return RACSequence.empty; - }]; - [thirdSequence.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - thirdSequenceDeallocd = YES; - }]]; - - RACSequence *secondSequence __attribute__((objc_precise_lifetime)) = [RACSequence sequenceWithHeadBlock:^id{ - return @2; - } tailBlock:^RACSequence *{ - return thirdSequence; - }]; - [secondSequence.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - secondSequenceDeallocd = YES; - }]]; - - RACSequence *firstSequence __attribute__((objc_precise_lifetime)) = [RACSequence sequenceWithHeadBlock:^id{ - return @1; - } tailBlock:^RACSequence *{ - return secondSequence; - }]; - [firstSequence.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - firstSequenceDeallocd = YES; - }]]; - - enumerator = firstSequence.objectEnumerator; - } - - @autoreleasepool { - expect([enumerator nextObject]).to(equal(@1)); - } - - @autoreleasepool { - expect([enumerator nextObject]).to(equal(@2)); - } - expect(@(firstSequenceDeallocd)).toEventually(beTruthy()); - - @autoreleasepool { - expect([enumerator nextObject]).to(equal(@3)); - } - expect(@(secondSequenceDeallocd)).toEventually(beTruthy()); - - @autoreleasepool { - expect([enumerator nextObject]).to(beNil()); - } - expect(@(thirdSequenceDeallocd)).toEventually(beTruthy()); - }); -}); - -qck_it(@"shouldn't overflow the stack when deallocated on a background queue", ^{ - NSUInteger length = 10000; - NSMutableArray *values = [NSMutableArray arrayWithCapacity:length]; - for (NSUInteger i = 0; i < length; ++i) { - [values addObject:@(i)]; - } - - __block BOOL finished = NO; - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - @autoreleasepool { - (void)[[values.rac_sequence map:^(id value) { - return value; - }] array]; - } - - finished = YES; - }); - - expect(@(finished)).toEventually(beTruthy()); -}); - -qck_describe(@"-foldLeftWithStart:reduce:", ^{ - qck_it(@"should reduce with start first", ^{ - RACSequence *sequence = [[[RACSequence return:@0] concat:[RACSequence return:@1]] concat:[RACSequence return:@2]]; - NSNumber *result = [sequence foldLeftWithStart:@3 reduce:^(NSNumber *first, NSNumber *rest) { - return first; - }]; - expect(result).to(equal(@3)); - }); - - qck_it(@"should be left associative", ^{ - RACSequence *sequence = [[[RACSequence return:@1] concat:[RACSequence return:@2]] concat:[RACSequence return:@3]]; - NSNumber *result = [sequence foldLeftWithStart:@0 reduce:^(NSNumber *first, NSNumber *rest) { - int difference = first.intValue - rest.intValue; - return @(difference); - }]; - expect(result).to(equal(@-6)); - }); -}); - -qck_describe(@"-foldRightWithStart:reduce:", ^{ - qck_it(@"should be lazy", ^{ - __block BOOL headInvoked = NO; - __block BOOL tailInvoked = NO; - RACSequence *sequence = [RACSequence sequenceWithHeadBlock:^{ - headInvoked = YES; - return @0; - } tailBlock:^{ - tailInvoked = YES; - return [RACSequence return:@1]; - }]; - - NSNumber *result = [sequence foldRightWithStart:@2 reduce:^(NSNumber *first, RACSequence *rest) { - return first; - }]; - - expect(result).to(equal(@0)); - expect(@(headInvoked)).to(beTruthy()); - expect(@(tailInvoked)).to(beFalsy()); - }); - - qck_it(@"should reduce with start last", ^{ - RACSequence *sequence = [[[RACSequence return:@0] concat:[RACSequence return:@1]] concat:[RACSequence return:@2]]; - NSNumber *result = [sequence foldRightWithStart:@3 reduce:^(NSNumber *first, RACSequence *rest) { - return rest.head; - }]; - expect(result).to(equal(@3)); - }); - - qck_it(@"should be right associative", ^{ - RACSequence *sequence = [[[RACSequence return:@1] concat:[RACSequence return:@2]] concat:[RACSequence return:@3]]; - NSNumber *result = [sequence foldRightWithStart:@0 reduce:^(NSNumber *first, RACSequence *rest) { - int difference = first.intValue - [rest.head intValue]; - return @(difference); - }]; - expect(result).to(equal(@2)); - }); -}); - -qck_describe(@"-any", ^{ - __block RACSequence *sequence; - qck_beforeEach(^{ - sequence = [[[RACSequence return:@0] concat:[RACSequence return:@1]] concat:[RACSequence return:@2]]; - }); - - qck_it(@"should return true when at least one exists", ^{ - BOOL result = [sequence any:^ BOOL (NSNumber *value) { - return value.integerValue > 0; - }]; - expect(@(result)).to(beTruthy()); - }); - - qck_it(@"should return false when no such thing exists", ^{ - BOOL result = [sequence any:^ BOOL (NSNumber *value) { - return value.integerValue == 3; - }]; - expect(@(result)).to(beFalsy()); - }); -}); - -qck_describe(@"-all", ^{ - __block RACSequence *sequence; - qck_beforeEach(^{ - sequence = [[[RACSequence return:@0] concat:[RACSequence return:@1]] concat:[RACSequence return:@2]]; - }); - - qck_it(@"should return true when all values pass", ^{ - BOOL result = [sequence all:^ BOOL (NSNumber *value) { - return value.integerValue >= 0; - }]; - expect(@(result)).to(beTruthy()); - }); - - qck_it(@"should return false when at least one value fails", ^{ - BOOL result = [sequence all:^ BOOL (NSNumber *value) { - return value.integerValue < 2; - }]; - expect(@(result)).to(beFalsy()); - }); -}); - -qck_describe(@"-objectPassingTest:", ^{ - __block RACSequence *sequence; - qck_beforeEach(^{ - sequence = [[[RACSequence return:@0] concat:[RACSequence return:@1]] concat:[RACSequence return:@2]]; - }); - - qck_it(@"should return leftmost object that passes the test", ^{ - NSNumber *result = [sequence objectPassingTest:^ BOOL (NSNumber *value) { - return value.intValue > 0; - }]; - expect(result).to(equal(@1)); - }); - - qck_it(@"should return nil if no objects pass the test", ^{ - NSNumber *result = [sequence objectPassingTest:^ BOOL (NSNumber *value) { - return value.intValue < 0; - }]; - expect(result).to(beNil()); - }); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/RACSerialDisposableSpec.m b/ReactiveObjCTests/RACSerialDisposableSpec.m deleted file mode 100644 index 3980fa8133..0000000000 --- a/ReactiveObjCTests/RACSerialDisposableSpec.m +++ /dev/null @@ -1,162 +0,0 @@ -// -// RACSerialDisposableSpec.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-07-22. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACSerialDisposable.h" - -QuickSpecBegin(RACSerialDisposableSpec) - -qck_it(@"should initialize with -init", ^{ - RACSerialDisposable *serial = [[RACSerialDisposable alloc] init]; - expect(serial).notTo(beNil()); - expect(serial.disposable).to(beNil()); -}); - -qck_it(@"should initialize an inner disposable with -initWithBlock:", ^{ - __block BOOL disposed = NO; - RACSerialDisposable *serial = [RACSerialDisposable disposableWithBlock:^{ - disposed = YES; - }]; - - expect(serial).notTo(beNil()); - expect(serial.disposable).notTo(beNil()); - - [serial.disposable dispose]; - expect(@(serial.disposed)).to(beFalsy()); - expect(@(disposed)).to(beTruthy()); -}); - -qck_it(@"should initialize with a disposable", ^{ - RACDisposable *inner = [[RACDisposable alloc] init]; - RACSerialDisposable *serial = [RACSerialDisposable serialDisposableWithDisposable:inner]; - expect(serial).notTo(beNil()); - expect(serial.disposable).to(equal(inner)); -}); - -qck_it(@"should dispose of the inner disposable", ^{ - __block BOOL disposed = NO; - RACDisposable *inner = [RACDisposable disposableWithBlock:^{ - disposed = YES; - }]; - - RACSerialDisposable *serial = [RACSerialDisposable serialDisposableWithDisposable:inner]; - expect(@(serial.disposed)).to(beFalsy()); - expect(@(disposed)).to(beFalsy()); - - [serial dispose]; - expect(@(serial.disposed)).to(beTruthy()); - expect(serial.disposable).to(beNil()); - expect(@(disposed)).to(beTruthy()); -}); - -qck_it(@"should dispose of a new inner disposable if it's already been disposed", ^{ - __block BOOL disposed = NO; - RACDisposable *inner = [RACDisposable disposableWithBlock:^{ - disposed = YES; - }]; - - RACSerialDisposable *serial = [[RACSerialDisposable alloc] init]; - expect(@(serial.disposed)).to(beFalsy()); - - [serial dispose]; - expect(@(serial.disposed)).to(beTruthy()); - expect(@(disposed)).to(beFalsy()); - - serial.disposable = inner; - expect(@(disposed)).to(beTruthy()); - expect(serial.disposable).to(beNil()); -}); - -qck_it(@"should allow the inner disposable to be set to nil", ^{ - __block BOOL disposed = NO; - RACDisposable *inner = [RACDisposable disposableWithBlock:^{ - disposed = YES; - }]; - - RACSerialDisposable *serial = [RACSerialDisposable serialDisposableWithDisposable:inner]; - expect(@(disposed)).to(beFalsy()); - - serial.disposable = nil; - expect(serial.disposable).to(beNil()); - - serial.disposable = inner; - expect(serial.disposable).to(equal(inner)); - - [serial dispose]; - expect(@(disposed)).to(beTruthy()); - expect(serial.disposable).to(beNil()); -}); - -qck_it(@"should swap inner disposables", ^{ - __block BOOL firstDisposed = NO; - RACDisposable *first = [RACDisposable disposableWithBlock:^{ - firstDisposed = YES; - }]; - - __block BOOL secondDisposed = NO; - RACDisposable *second = [RACDisposable disposableWithBlock:^{ - secondDisposed = YES; - }]; - - RACSerialDisposable *serial = [RACSerialDisposable serialDisposableWithDisposable:first]; - expect([serial swapInDisposable:second]).to(equal(first)); - - expect(@(serial.disposed)).to(beFalsy()); - expect(@(firstDisposed)).to(beFalsy()); - expect(@(secondDisposed)).to(beFalsy()); - - [serial dispose]; - expect(@(serial.disposed)).to(beTruthy()); - expect(serial.disposable).to(beNil()); - - expect(@(firstDisposed)).to(beFalsy()); - expect(@(secondDisposed)).to(beTruthy()); -}); - -qck_it(@"should release the inner disposable upon deallocation", ^{ - __weak RACDisposable *weakInnerDisposable; - __weak RACSerialDisposable *weakSerialDisposable; - - @autoreleasepool { - RACDisposable *innerDisposable __attribute__((objc_precise_lifetime)) = [[RACDisposable alloc] init]; - weakInnerDisposable = innerDisposable; - - RACSerialDisposable *serialDisposable __attribute__((objc_precise_lifetime)) = [[RACSerialDisposable alloc] init]; - serialDisposable.disposable = innerDisposable; - weakSerialDisposable = serialDisposable; - } - - expect(weakSerialDisposable).to(beNil()); - expect(weakInnerDisposable).to(beNil()); -}); - -qck_it(@"should not crash when racing between swapInDisposable and disposable", ^{ - __block BOOL stop = NO; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (long long)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - stop = YES; - }); - - RACSerialDisposable *serialDisposable = [[RACSerialDisposable alloc] init]; - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - while (!stop) { - [serialDisposable swapInDisposable:[RACDisposable disposableWithBlock:^{}]]; - } - }); - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - while (!stop) { - (void)[serialDisposable disposable]; - } - }); - - expect(@(stop)).toEventually(beTruthy()); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/RACSignalSpec.m b/ReactiveObjCTests/RACSignalSpec.m deleted file mode 100644 index d4faca5296..0000000000 --- a/ReactiveObjCTests/RACSignalSpec.m +++ /dev/null @@ -1,4080 +0,0 @@ -// -// RACSignalSpec.m -// ReactiveObjC -// -// Created by Josh Abernathy on 3/2/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACPropertySignalExamples.h" -#import "RACSequenceExamples.h" -#import "RACStreamExamples.h" -#import "RACTestObject.h" - -#import -#import "NSObject+RACDeallocating.h" -#import "NSObject+RACPropertySubscribing.h" -#import "RACBehaviorSubject.h" -#import "RACCommand.h" -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACEvent.h" -#import "RACGroupedSignal.h" -#import "RACMulticastConnection.h" -#import "RACReplaySubject.h" -#import "RACScheduler.h" -#import "RACSignal+Operations.h" -#import "RACSubject.h" -#import "RACSubscriber+Private.h" -#import "RACSubscriber.h" -#import "RACTestScheduler.h" -#import "RACTuple.h" -#import "RACUnit.h" -#import - -// Set in a beforeAll below. -static NSError *RACSignalTestError; - -static NSString * const RACSignalMergeConcurrentCompletionExampleGroup = @"RACSignalMergeConcurrentCompletionExampleGroup"; -static NSString * const RACSignalMaxConcurrent = @"RACSignalMaxConcurrent"; - -QuickConfigurationBegin(mergeConcurrentCompletionName) - -+ (void)configure:(Configuration *)configuration { - sharedExamples(RACSignalMergeConcurrentCompletionExampleGroup, ^(QCKDSLSharedExampleContext exampleContext) { - qck_it(@"should complete only after the source and all its signals have completed", ^{ - RACSubject *subject1 = [RACSubject subject]; - RACSubject *subject2 = [RACSubject subject]; - RACSubject *subject3 = [RACSubject subject]; - - RACSubject *signalsSubject = [RACSubject subject]; - __block BOOL completed = NO; - [[signalsSubject flatten:[exampleContext()[RACSignalMaxConcurrent] unsignedIntegerValue]] subscribeCompleted:^{ - completed = YES; - }]; - - [signalsSubject sendNext:subject1]; - [subject1 sendCompleted]; - - expect(@(completed)).to(beFalsy()); - - [signalsSubject sendNext:subject2]; - [signalsSubject sendNext:subject3]; - - [signalsSubject sendCompleted]; - - expect(@(completed)).to(beFalsy()); - - [subject2 sendCompleted]; - - expect(@(completed)).to(beFalsy()); - - [subject3 sendCompleted]; - - expect(@(completed)).to(beTruthy()); - }); - }); -} - -QuickConfigurationEnd - -QuickSpecBegin(RACSignalSpec) - -qck_beforeSuite(^{ - // We do this instead of a macro to ensure that to(equal() will work - // correctly (by matching identity), even if -[NSError isEqual:] is broken. - RACSignalTestError = [NSError errorWithDomain:@"foo" code:100 userInfo:nil]; -}); - -qck_describe(@"RACStream", ^{ - id verifyValues = ^(RACSignal *signal, NSArray *expectedValues) { - expect(signal).notTo(beNil()); - - NSMutableArray *collectedValues = [NSMutableArray array]; - - __block BOOL success = NO; - __block NSError *error = nil; - [signal subscribeNext:^(id value) { - [collectedValues addObject:value]; - } error:^(NSError *receivedError) { - error = receivedError; - } completed:^{ - success = YES; - }]; - - expect(@(success)).toEventually(beTruthy()); - expect(error).to(beNil()); - expect(collectedValues).to(equal(expectedValues)); - }; - - RACSignal *infiniteSignal = [RACSignal createSignal:^(id subscriber) { - __block volatile int32_t done = 0; - - [RACScheduler.mainThreadScheduler schedule:^{ - while (!done) { - [subscriber sendNext:RACUnit.defaultUnit]; - } - }]; - - return [RACDisposable disposableWithBlock:^{ - OSAtomicIncrement32Barrier(&done); - }]; - }]; - - qck_itBehavesLike(RACStreamExamples, ^{ - return @{ - RACStreamExamplesClass: RACSignal.class, - RACStreamExamplesVerifyValuesBlock: verifyValues, - RACStreamExamplesInfiniteStream: infiniteSignal - }; - }); -}); - -qck_describe(@"-bind:", ^{ - __block RACSubject *signals; - __block BOOL disposed; - __block id lastValue; - __block RACSubject *values; - - qck_beforeEach(^{ - // Tests send a (RACSignal, BOOL) pair that are used below in -bind:. - signals = [RACSubject subject]; - - disposed = NO; - RACSignal *source = [RACSignal createSignal:^(id subscriber) { - [signals subscribe:subscriber]; - - return [RACDisposable disposableWithBlock:^{ - disposed = YES; - }]; - }]; - - RACSignal *bind = [source bind:^{ - return ^(RACTuple *x, BOOL *stop) { - RACTupleUnpack(RACSignal *signal, NSNumber *stopValue) = x; - *stop = stopValue.boolValue; - return signal; - }; - }]; - - lastValue = nil; - [bind subscribeNext:^(id x) { - lastValue = x; - }]; - - // Send `bind` an open ended subject to subscribe to( These tests make - // use of this in two ways: - // 1. Used to test a regression bug where -bind: would not actually - // stop when instructed to. This bug manifested itself only when - // there were subscriptions that lived on past the point at which - // -bind: was stopped. This subject represents such a subscription. - // 2. Test that values sent by this subject are received by `bind`'s - // subscriber, even *after* -bind: has been instructed to stop. - values = [RACSubject subject]; - [signals sendNext:RACTuplePack(values, @NO)]; - expect(@(disposed)).to(beFalsy()); - }); - - qck_it(@"should dispose source signal when stopped with nil signal", ^{ - // Tell -bind: to stop by sending it a `nil` signal. - [signals sendNext:RACTuplePack(nil, @NO)]; - expect(@(disposed)).to(beTruthy()); - - // Should still receive values sent after stopping. - expect(lastValue).to(beNil()); - [values sendNext:RACUnit.defaultUnit]; - expect(lastValue).to(equal(RACUnit.defaultUnit)); - }); - - qck_it(@"should dispose source signal when stop flag set to YES", ^{ - // Tell -bind: to stop by setting the stop flag to YES. - [signals sendNext:RACTuplePack([RACSignal return:@1], @YES)]; - expect(@(disposed)).to(beTruthy()); - - // Should still recieve last signal sent at the time of setting stop to YES. - expect(lastValue).to(equal(@1)); - - // Should still receive values sent after stopping. - [values sendNext:@2]; - expect(lastValue).to(equal(@2)); - }); - - qck_it(@"should properly stop subscribing to new signals after error", ^{ - RACSignal *signal = [RACSignal createSignal:^ id (id subscriber) { - [subscriber sendNext:@0]; - [subscriber sendNext:@1]; - return nil; - }]; - - __block BOOL subscribedAfterError = NO; - RACSignal *bind = [signal bind:^{ - return ^(NSNumber *x, BOOL *stop) { - if (x.integerValue == 0) return [RACSignal error:nil]; - - return [RACSignal defer:^{ - subscribedAfterError = YES; - return [RACSignal empty]; - }]; - }; - }]; - - [bind subscribeCompleted:^{}]; - expect(@(subscribedAfterError)).to(beFalsy()); - }); - - qck_it(@"should not subscribe to signals following error in +merge:", ^{ - __block BOOL firstSubscribed = NO; - __block BOOL secondSubscribed = NO; - __block BOOL errored = NO; - - RACSignal *signal = [[RACSignal - merge:@[ - [RACSignal defer:^{ - firstSubscribed = YES; - return [RACSignal error:nil]; - }], - [RACSignal defer:^{ - secondSubscribed = YES; - return [RACSignal return:nil]; - }] - ]] - doError:^(NSError *error) { - errored = YES; - }]; - - [signal subscribeCompleted:^{}]; - - expect(@(firstSubscribed)).to(beTruthy()); - expect(@(secondSubscribed)).to(beFalsy()); - expect(@(errored)).to(beTruthy()); - }); - - qck_it(@"should not retain signals that are subscribed", ^{ - __weak RACSignal *weakSignal; - @autoreleasepool { - RACSignal *delaySignal = [[RACSignal return:@123] delay:1]; - [[delaySignal map:^id(id value) { - return @456; - }] subscribeNext:^(id x) { - }]; - weakSignal = delaySignal; - } - expect(weakSignal).to(beNil()); - }); -}); - -qck_describe(@"subscribing", ^{ - __block RACSignal *signal = nil; - id nextValueSent = @"1"; - - qck_beforeEach(^{ - signal = [RACSignal createSignal:^ RACDisposable * (id subscriber) { - [subscriber sendNext:nextValueSent]; - [subscriber sendCompleted]; - return nil; - }]; - }); - - qck_it(@"should get next values", ^{ - __block id nextValueReceived = nil; - [signal subscribeNext:^(id x) { - nextValueReceived = x; - } error:^(NSError *error) { - - } completed:^{ - - }]; - - expect(nextValueReceived).to(equal(nextValueSent)); - }); - - qck_it(@"should get completed", ^{ - __block BOOL didGetCompleted = NO; - [signal subscribeNext:^(id x) { - - } error:^(NSError *error) { - - } completed:^{ - didGetCompleted = YES; - }]; - - expect(@(didGetCompleted)).to(beTruthy()); - }); - - qck_it(@"should not get an error", ^{ - __block BOOL didGetError = NO; - [signal subscribeNext:^(id x) { - - } error:^(NSError *error) { - didGetError = YES; - } completed:^{ - - }]; - - expect(@(didGetError)).to(beFalsy()); - }); - - qck_it(@"shouldn't get anything after dispose", ^{ - RACTestScheduler *scheduler = [[RACTestScheduler alloc] init]; - NSMutableArray *receivedValues = [NSMutableArray array]; - - RACSignal *signal = [RACSignal createSignal:^ id (id subscriber) { - [subscriber sendNext:@0]; - - [scheduler afterDelay:0 schedule:^{ - [subscriber sendNext:@1]; - }]; - - return nil; - }]; - - RACDisposable *disposable = [signal subscribeNext:^(id x) { - [receivedValues addObject:x]; - }]; - - NSArray *expectedValues = @[ @0 ]; - expect(receivedValues).to(equal(expectedValues)); - - [disposable dispose]; - [scheduler stepAll]; - - expect(receivedValues).to(equal(expectedValues)); - }); - - qck_it(@"should have a current scheduler in didSubscribe block", ^{ - __block RACScheduler *currentScheduler; - RACSignal *signal = [RACSignal createSignal:^ RACDisposable * (id subscriber) { - currentScheduler = RACScheduler.currentScheduler; - [subscriber sendCompleted]; - return nil; - }]; - - [signal subscribeNext:^(id x) {}]; - expect(currentScheduler).notTo(beNil()); - - currentScheduler = nil; - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [signal subscribeNext:^(id x) {}]; - }); - expect(currentScheduler).toEventuallyNot(beNil()); - }); - - qck_it(@"should automatically dispose of other subscriptions from +createSignal:", ^{ - __block BOOL innerDisposed = NO; - __block id innerSubscriber = nil; - - RACSignal *innerSignal = [RACSignal createSignal:^(id subscriber) { - // Keep the subscriber alive so it doesn't trigger disposal on dealloc - innerSubscriber = subscriber; - return [RACDisposable disposableWithBlock:^{ - innerDisposed = YES; - }]; - }]; - - RACSignal *outerSignal = [RACSignal createSignal:^ RACDisposable * (id subscriber) { - [innerSignal subscribe:subscriber]; - return nil; - }]; - - RACDisposable *disposable = [outerSignal subscribeCompleted:^{}]; - expect(disposable).notTo(beNil()); - expect(@(innerDisposed)).to(beFalsy()); - - [disposable dispose]; - expect(@(innerDisposed)).to(beTruthy()); - }); -}); - -qck_describe(@"-takeUntil:", ^{ - qck_it(@"should support value as trigger", ^{ - __block BOOL shouldBeGettingItems = YES; - RACSubject *subject = [RACSubject subject]; - RACSubject *cutOffSubject = [RACSubject subject]; - [[subject takeUntil:cutOffSubject] subscribeNext:^(id x) { - expect(@(shouldBeGettingItems)).to(beTruthy()); - }]; - - shouldBeGettingItems = YES; - [subject sendNext:@"test 1"]; - [subject sendNext:@"test 2"]; - - [cutOffSubject sendNext:[RACUnit defaultUnit]]; - - shouldBeGettingItems = NO; - [subject sendNext:@"test 3"]; - }); - - qck_it(@"should support completion as trigger", ^{ - __block BOOL shouldBeGettingItems = YES; - RACSubject *subject = [RACSubject subject]; - RACSubject *cutOffSubject = [RACSubject subject]; - [[subject takeUntil:cutOffSubject] subscribeNext:^(id x) { - expect(@(shouldBeGettingItems)).to(beTruthy()); - }]; - - [cutOffSubject sendCompleted]; - - shouldBeGettingItems = NO; - [subject sendNext:@"should not go through"]; - }); - - qck_it(@"should squelch any values sent immediately upon subscription", ^{ - RACSignal *valueSignal = [RACSignal return:RACUnit.defaultUnit]; - RACSignal *cutOffSignal = [RACSignal empty]; - - __block BOOL gotNext = NO; - __block BOOL completed = NO; - - [[valueSignal takeUntil:cutOffSignal] subscribeNext:^(id _) { - gotNext = YES; - } completed:^{ - completed = YES; - }]; - - expect(@(gotNext)).to(beFalsy()); - expect(@(completed)).to(beTruthy()); - }); -}); - -qck_describe(@"-takeUntilReplacement:", ^{ - qck_it(@"should forward values from the receiver until it's replaced", ^{ - RACSubject *receiver = [RACSubject subject]; - RACSubject *replacement = [RACSubject subject]; - - NSMutableArray *receivedValues = [NSMutableArray array]; - - [[receiver takeUntilReplacement:replacement] subscribeNext:^(id x) { - [receivedValues addObject:x]; - }]; - - expect(receivedValues).to(equal(@[])); - - [receiver sendNext:@1]; - expect(receivedValues).to(equal(@[ @1 ])); - - [receiver sendNext:@2]; - expect(receivedValues).to(equal((@[ @1, @2 ]))); - - [replacement sendNext:@3]; - expect(receivedValues).to(equal((@[ @1, @2, @3 ]))); - - [receiver sendNext:@4]; - expect(receivedValues).to(equal((@[ @1, @2, @3 ]))); - - [replacement sendNext:@5]; - expect(receivedValues).to(equal((@[ @1, @2, @3, @5 ]))); - }); - - qck_it(@"should forward error from the receiver", ^{ - RACSubject *receiver = [RACSubject subject]; - __block BOOL receivedError = NO; - - [[receiver takeUntilReplacement:RACSignal.never] subscribeError:^(NSError *error) { - receivedError = YES; - }]; - - [receiver sendError:nil]; - expect(@(receivedError)).to(beTruthy()); - }); - - qck_it(@"should not forward completed from the receiver", ^{ - RACSubject *receiver = [RACSubject subject]; - __block BOOL receivedCompleted = NO; - - [[receiver takeUntilReplacement:RACSignal.never] subscribeCompleted: ^{ - receivedCompleted = YES; - }]; - - [receiver sendCompleted]; - expect(@(receivedCompleted)).to(beFalsy()); - }); - - qck_it(@"should forward error from the replacement signal", ^{ - RACSubject *replacement = [RACSubject subject]; - __block BOOL receivedError = NO; - - [[RACSignal.never takeUntilReplacement:replacement] subscribeError:^(NSError *error) { - receivedError = YES; - }]; - - [replacement sendError:nil]; - expect(@(receivedError)).to(beTruthy()); - }); - - qck_it(@"should forward completed from the replacement signal", ^{ - RACSubject *replacement = [RACSubject subject]; - __block BOOL receivedCompleted = NO; - - [[RACSignal.never takeUntilReplacement:replacement] subscribeCompleted: ^{ - receivedCompleted = YES; - }]; - - [replacement sendCompleted]; - expect(@(receivedCompleted)).to(beTruthy()); - }); - - qck_it(@"should not forward values from the receiver if both send synchronously", ^{ - RACSignal *receiver = [RACSignal createSignal:^ RACDisposable * (id subscriber) { - [subscriber sendNext:@1]; - [subscriber sendNext:@2]; - [subscriber sendNext:@3]; - return nil; - }]; - RACSignal *replacement = [RACSignal createSignal:^ RACDisposable * (id subscriber) { - [subscriber sendNext:@4]; - [subscriber sendNext:@5]; - [subscriber sendNext:@6]; - return nil; - }]; - - NSMutableArray *receivedValues = [NSMutableArray array]; - - [[receiver takeUntilReplacement:replacement] subscribeNext:^(id x) { - [receivedValues addObject:x]; - }]; - - expect(receivedValues).to(equal((@[ @4, @5, @6 ]))); - }); - - qck_it(@"should dispose of the receiver when it's disposed of", ^{ - __block BOOL receiverDisposed = NO; - RACSignal *receiver = [RACSignal createSignal:^(id subscriber) { - return [RACDisposable disposableWithBlock:^{ - receiverDisposed = YES; - }]; - }]; - - [[[receiver takeUntilReplacement:RACSignal.never] subscribeCompleted:^{}] dispose]; - - expect(@(receiverDisposed)).to(beTruthy()); - }); - - qck_it(@"should dispose of the replacement signal when it's disposed of", ^{ - __block BOOL replacementDisposed = NO; - RACSignal *replacement = [RACSignal createSignal:^(id subscriber) { - return [RACDisposable disposableWithBlock:^{ - replacementDisposed = YES; - }]; - }]; - - [[[RACSignal.never takeUntilReplacement:replacement] subscribeCompleted:^{}] dispose]; - - expect(@(replacementDisposed)).to(beTruthy()); - }); - - qck_it(@"should dispose of the receiver when the replacement signal sends an event", ^{ - __block BOOL receiverDisposed = NO; - __block id receiverSubscriber = nil; - RACSignal *receiver = [RACSignal createSignal:^(id subscriber) { - // Keep the subscriber alive so it doesn't trigger disposal on dealloc - receiverSubscriber = subscriber; - return [RACDisposable disposableWithBlock:^{ - receiverDisposed = YES; - }]; - }]; - RACSubject *replacement = [RACSubject subject]; - - [[receiver takeUntilReplacement:replacement] subscribeCompleted:^{}]; - - expect(@(receiverDisposed)).to(beFalsy()); - - [replacement sendNext:nil]; - - expect(@(receiverDisposed)).to(beTruthy()); - }); -}); - -qck_describe(@"disposal", ^{ - qck_it(@"should dispose of the didSubscribe disposable", ^{ - __block BOOL innerDisposed = NO; - RACSignal *signal = [RACSignal createSignal:^(id subscriber) { - return [RACDisposable disposableWithBlock:^{ - innerDisposed = YES; - }]; - }]; - - expect(@(innerDisposed)).to(beFalsy()); - - RACDisposable *disposable = [signal subscribeNext:^(id x) {}]; - expect(disposable).notTo(beNil()); - - [disposable dispose]; - expect(@(innerDisposed)).to(beTruthy()); - }); - - qck_it(@"should dispose of the didSubscribe disposable asynchronously", ^{ - __block BOOL innerDisposed = NO; - RACSignal *signal = [RACSignal createSignal:^(id subscriber) { - return [RACDisposable disposableWithBlock:^{ - innerDisposed = YES; - }]; - }]; - - [[RACScheduler scheduler] schedule:^{ - RACDisposable *disposable = [signal subscribeNext:^(id x) {}]; - [disposable dispose]; - }]; - - expect(@(innerDisposed)).toEventually(beTruthy()); - }); -}); - -qck_describe(@"querying", ^{ - __block RACSignal *signal = nil; - id nextValueSent = @"1"; - - qck_beforeEach(^{ - signal = [RACSignal createSignal:^ RACDisposable * (id subscriber) { - [subscriber sendNext:nextValueSent]; - [subscriber sendNext:@"other value"]; - [subscriber sendCompleted]; - return nil; - }]; - }); - - qck_it(@"should return first 'next' value with -firstOrDefault:success:error:", ^{ - RACSignal *signal = [RACSignal createSignal:^ id (id subscriber) { - [subscriber sendNext:@1]; - [subscriber sendNext:@2]; - [subscriber sendNext:@3]; - [subscriber sendCompleted]; - return nil; - }]; - - expect(signal).notTo(beNil()); - - __block BOOL success = NO; - __block NSError *error = nil; - expect([signal firstOrDefault:@5 success:&success error:&error]).to(equal(@1)); - expect(@(success)).to(beTruthy()); - expect(error).to(beNil()); - }); - - qck_it(@"should return first default value with -firstOrDefault:success:error:", ^{ - RACSignal *signal = [RACSignal createSignal:^ id (id subscriber) { - [subscriber sendCompleted]; - return nil; - }]; - - expect(signal).notTo(beNil()); - - __block BOOL success = NO; - __block NSError *error = nil; - expect([signal firstOrDefault:@5 success:&success error:&error]).to(equal(@5)); - expect(@(success)).to(beTruthy()); - expect(error).to(beNil()); - }); - - qck_it(@"should return error with -firstOrDefault:success:error:", ^{ - RACSignal *signal = [RACSignal createSignal:^ id (id subscriber) { - [subscriber sendError:RACSignalTestError]; - return nil; - }]; - - expect(signal).notTo(beNil()); - - __block BOOL success = NO; - __block NSError *error = nil; - expect([signal firstOrDefault:@5 success:&success error:&error]).to(equal(@5)); - expect(@(success)).to(beFalsy()); - expect(error).to(equal(RACSignalTestError)); - }); - - qck_it(@"shouldn't crash when returning an error from a background scheduler", ^{ - RACSignal *signal = [RACSignal createSignal:^ id (id subscriber) { - [[RACScheduler scheduler] schedule:^{ - [subscriber sendError:RACSignalTestError]; - }]; - - return nil; - }]; - - expect(signal).notTo(beNil()); - - __block BOOL success = NO; - __block NSError *error = nil; - expect([signal firstOrDefault:@5 success:&success error:&error]).to(equal(@5)); - expect(@(success)).to(beFalsy()); - expect(error).to(equal(RACSignalTestError)); - }); - - qck_it(@"should terminate the subscription after returning from -firstOrDefault:success:error:", ^{ - __block BOOL disposed = NO; - RACSignal *signal = [RACSignal createSignal:^(id subscriber) { - [subscriber sendNext:RACUnit.defaultUnit]; - - return [RACDisposable disposableWithBlock:^{ - disposed = YES; - }]; - }]; - - expect(signal).notTo(beNil()); - expect(@(disposed)).to(beFalsy()); - - expect([signal firstOrDefault:nil success:NULL error:NULL]).to(equal(RACUnit.defaultUnit)); - expect(@(disposed)).to(beTruthy()); - }); - - qck_it(@"should return YES from -waitUntilCompleted: when successful", ^{ - RACSignal *signal = [RACSignal createSignal:^ id (id subscriber) { - [subscriber sendNext:RACUnit.defaultUnit]; - [subscriber sendCompleted]; - return nil; - }]; - - __block NSError *error = nil; - expect(@([signal waitUntilCompleted:&error])).to(beTruthy()); - expect(error).to(beNil()); - }); - - qck_it(@"should return NO from -waitUntilCompleted: upon error", ^{ - RACSignal *signal = [RACSignal createSignal:^ id (id subscriber) { - [subscriber sendNext:RACUnit.defaultUnit]; - [subscriber sendError:RACSignalTestError]; - return nil; - }]; - - __block NSError *error = nil; - expect(@([signal waitUntilCompleted:&error])).to(beFalsy()); - expect(error).to(equal(RACSignalTestError)); - }); - - qck_it(@"should return a delayed value from -asynchronousFirstOrDefault:success:error:", ^{ - RACSignal *signal = [[RACSignal return:RACUnit.defaultUnit] delay:0]; - - __block BOOL scheduledBlockRan = NO; - [RACScheduler.mainThreadScheduler schedule:^{ - scheduledBlockRan = YES; - }]; - - expect(@(scheduledBlockRan)).to(beFalsy()); - - BOOL success = NO; - NSError *error = nil; - id value = [signal asynchronousFirstOrDefault:nil success:&success error:&error]; - - expect(@(scheduledBlockRan)).to(beTruthy()); - - expect(value).to(equal(RACUnit.defaultUnit)); - expect(@(success)).to(beTruthy()); - expect(error).to(beNil()); - }); - - qck_it(@"should return a default value from -asynchronousFirstOrDefault:success:error:", ^{ - RACSignal *signal = [[RACSignal error:RACSignalTestError] delay:0]; - - __block BOOL scheduledBlockRan = NO; - [RACScheduler.mainThreadScheduler schedule:^{ - scheduledBlockRan = YES; - }]; - - expect(@(scheduledBlockRan)).to(beFalsy()); - - BOOL success = NO; - NSError *error = nil; - id value = [signal asynchronousFirstOrDefault:RACUnit.defaultUnit success:&success error:&error]; - - expect(@(scheduledBlockRan)).to(beTruthy()); - - expect(value).to(equal(RACUnit.defaultUnit)); - expect(@(success)).to(beFalsy()); - expect(error).to(equal(RACSignalTestError)); - }); - - qck_it(@"should return a delayed error from -asynchronousFirstOrDefault:success:error:", ^{ - RACSignal *signal = [[RACSignal - createSignal:^(id subscriber) { - return [[RACScheduler scheduler] schedule:^{ - [subscriber sendError:RACSignalTestError]; - }]; - }] - deliverOn:RACScheduler.mainThreadScheduler]; - - __block NSError *error = nil; - __block BOOL success = NO; - expect([signal asynchronousFirstOrDefault:nil success:&success error:&error]).to(beNil()); - - expect(@(success)).to(beFalsy()); - expect(error).to(equal(RACSignalTestError)); - }); - - qck_it(@"should terminate the subscription after returning from -asynchronousFirstOrDefault:success:error:", ^{ - __block BOOL disposed = NO; - RACSignal *signal = [RACSignal createSignal:^(id subscriber) { - [[RACScheduler scheduler] schedule:^{ - [subscriber sendNext:RACUnit.defaultUnit]; - }]; - - return [RACDisposable disposableWithBlock:^{ - disposed = YES; - }]; - }]; - - expect(signal).notTo(beNil()); - expect(@(disposed)).to(beFalsy()); - - expect([signal asynchronousFirstOrDefault:nil success:NULL error:NULL]).to(equal(RACUnit.defaultUnit)); - expect(@(disposed)).toEventually(beTruthy()); - }); - - qck_it(@"should return a delayed success from -asynchronouslyWaitUntilCompleted:", ^{ - RACSignal *signal = [[RACSignal return:RACUnit.defaultUnit] delay:0]; - - __block BOOL scheduledBlockRan = NO; - [RACScheduler.mainThreadScheduler schedule:^{ - scheduledBlockRan = YES; - }]; - - expect(@(scheduledBlockRan)).to(beFalsy()); - - NSError *error = nil; - BOOL success = [signal asynchronouslyWaitUntilCompleted:&error]; - - expect(@(scheduledBlockRan)).to(beTruthy()); - - expect(@(success)).to(beTruthy()); - expect(error).to(beNil()); - }); -}); - -qck_describe(@"continuation", ^{ - qck_it(@"should repeat after completion", ^{ - __block NSUInteger numberOfSubscriptions = 0; - RACScheduler *scheduler = [RACScheduler scheduler]; - - RACSignal *signal = [RACSignal createSignal:^ RACDisposable * (id subscriber) { - return [scheduler schedule:^{ - if (numberOfSubscriptions == 3) { - [subscriber sendError:RACSignalTestError]; - return; - } - - numberOfSubscriptions++; - - [subscriber sendNext:@"1"]; - [subscriber sendCompleted]; - [subscriber sendError:RACSignalTestError]; - }]; - }]; - - __block NSUInteger nextCount = 0; - __block BOOL gotCompleted = NO; - [[signal repeat] subscribeNext:^(id x) { - nextCount++; - } error:^(NSError *error) { - - } completed:^{ - gotCompleted = YES; - }]; - - expect(@(nextCount)).toEventually(equal(@3)); - expect(@(gotCompleted)).to(beFalsy()); - }); - - qck_it(@"should stop repeating when disposed", ^{ - RACSignal *signal = [RACSignal createSignal:^ id (id subscriber) { - [subscriber sendNext:@1]; - [subscriber sendCompleted]; - return nil; - }]; - - NSMutableArray *values = [NSMutableArray array]; - - __block BOOL completed = NO; - __block RACDisposable *disposable = [[[signal - repeat] - subscribeOn:RACScheduler.mainThreadScheduler] - subscribeNext:^(id x) { - [values addObject:x]; - [disposable dispose]; - } completed:^{ - completed = YES; - }]; - - expect(values).toEventually(equal(@[ @1 ])); - expect(@(completed)).to(beFalsy()); - }); - - qck_it(@"should stop repeating when disposed by -take:", ^{ - RACSignal *signal = [RACSignal createSignal:^ id (id subscriber) { - [subscriber sendNext:@1]; - [subscriber sendCompleted]; - return nil; - }]; - - NSMutableArray *values = [NSMutableArray array]; - - __block BOOL completed = NO; - [[[signal repeat] take:1] subscribeNext:^(id x) { - [values addObject:x]; - } completed:^{ - completed = YES; - }]; - - expect(values).toEventually(equal(@[ @1 ])); - expect(@(completed)).to(beTruthy()); - }); -}); - -qck_describe(@"+combineLatestWith:", ^{ - __block RACSubject *subject1 = nil; - __block RACSubject *subject2 = nil; - __block RACSignal *combined = nil; - - qck_beforeEach(^{ - subject1 = [RACSubject subject]; - subject2 = [RACSubject subject]; - combined = [RACSignal combineLatest:@[ subject1, subject2 ]]; - }); - - qck_it(@"should send next only once both signals send next", ^{ - __block RACTuple *tuple; - - [combined subscribeNext:^(id x) { - tuple = x; - }]; - - expect(tuple).to(beNil()); - - [subject1 sendNext:@"1"]; - expect(tuple).to(beNil()); - - [subject2 sendNext:@"2"]; - expect(tuple).to(equal(RACTuplePack(@"1", @"2"))); - }); - - qck_it(@"should send nexts when either signal sends multiple times", ^{ - NSMutableArray *results = [NSMutableArray array]; - [combined subscribeNext:^(id x) { - [results addObject:x]; - }]; - - [subject1 sendNext:@"1"]; - [subject2 sendNext:@"2"]; - - [subject1 sendNext:@"3"]; - [subject2 sendNext:@"4"]; - - expect(results[0]).to(equal(RACTuplePack(@"1", @"2"))); - expect(results[1]).to(equal(RACTuplePack(@"3", @"2"))); - expect(results[2]).to(equal(RACTuplePack(@"3", @"4"))); - }); - - qck_it(@"should complete when only both signals complete", ^{ - __block BOOL completed = NO; - - [combined subscribeCompleted:^{ - completed = YES; - }]; - - expect(@(completed)).to(beFalsy()); - - [subject1 sendCompleted]; - expect(@(completed)).to(beFalsy()); - - [subject2 sendCompleted]; - expect(@(completed)).to(beTruthy()); - }); - - qck_it(@"should error when either signal errors", ^{ - __block NSError *receivedError = nil; - [combined subscribeError:^(NSError *error) { - receivedError = error; - }]; - - [subject1 sendError:RACSignalTestError]; - expect(receivedError).to(equal(RACSignalTestError)); - }); - - qck_it(@"shouldn't create a retain cycle", ^{ - __block BOOL subjectDeallocd = NO; - __block BOOL signalDeallocd = NO; - - @autoreleasepool { - RACSubject *subject __attribute__((objc_precise_lifetime)) = [RACSubject subject]; - [subject.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - subjectDeallocd = YES; - }]]; - - RACSignal *signal __attribute__((objc_precise_lifetime)) = [RACSignal combineLatest:@[ subject ]]; - [signal.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - signalDeallocd = YES; - }]]; - - [signal subscribeCompleted:^{}]; - [subject sendCompleted]; - } - - expect(@(subjectDeallocd)).toEventually(beTruthy()); - expect(@(signalDeallocd)).toEventually(beTruthy()); - }); - - qck_it(@"should combine the same signal", ^{ - RACSignal *combined = [subject1 combineLatestWith:subject1]; - - __block RACTuple *tuple; - [combined subscribeNext:^(id x) { - tuple = x; - }]; - - [subject1 sendNext:@"foo"]; - expect(tuple).to(equal(RACTuplePack(@"foo", @"foo"))); - - [subject1 sendNext:@"bar"]; - expect(tuple).to(equal(RACTuplePack(@"bar", @"bar"))); - }); - - qck_it(@"should combine the same side-effecting signal", ^{ - __block NSUInteger counter = 0; - RACSignal *sideEffectingSignal = [RACSignal createSignal:^ RACDisposable * (id subscriber) { - [subscriber sendNext:@(++counter)]; - [subscriber sendCompleted]; - return nil; - }]; - - RACSignal *combined = [sideEffectingSignal combineLatestWith:sideEffectingSignal]; - expect(@(counter)).to(equal(@0)); - - NSMutableArray *receivedValues = [NSMutableArray array]; - [combined subscribeNext:^(id x) { - [receivedValues addObject:x]; - }]; - - expect(@(counter)).to(equal(@2)); - - NSArray *expected = @[ RACTuplePack(@1, @2) ]; - expect(receivedValues).to(equal(expected)); - }); -}); - -qck_describe(@"+combineLatest:", ^{ - qck_it(@"should return tuples even when only combining one signal", ^{ - RACSubject *subject = [RACSubject subject]; - - __block RACTuple *tuple; - [[RACSignal combineLatest:@[ subject ]] subscribeNext:^(id x) { - tuple = x; - }]; - - [subject sendNext:@"foo"]; - expect(tuple).to(equal(RACTuplePack(@"foo"))); - }); - - qck_it(@"should complete immediately when not given any signals", ^{ - RACSignal *signal = [RACSignal combineLatest:@[]]; - - __block BOOL completed = NO; - [signal subscribeCompleted:^{ - completed = YES; - }]; - - expect(@(completed)).to(beTruthy()); - }); - - qck_it(@"should only complete after all its signals complete", ^{ - RACSubject *subject1 = [RACSubject subject]; - RACSubject *subject2 = [RACSubject subject]; - RACSubject *subject3 = [RACSubject subject]; - RACSignal *combined = [RACSignal combineLatest:@[ subject1, subject2, subject3 ]]; - - __block BOOL completed = NO; - [combined subscribeCompleted:^{ - completed = YES; - }]; - - expect(@(completed)).to(beFalsy()); - - [subject1 sendCompleted]; - expect(@(completed)).to(beFalsy()); - - [subject2 sendCompleted]; - expect(@(completed)).to(beFalsy()); - - [subject3 sendCompleted]; - expect(@(completed)).to(beTruthy()); - }); -}); - -qck_describe(@"+combineLatest:reduce:", ^{ - __block RACSubject *subject1; - __block RACSubject *subject2; - __block RACSubject *subject3; - - qck_beforeEach(^{ - subject1 = [RACSubject subject]; - subject2 = [RACSubject subject]; - subject3 = [RACSubject subject]; - }); - - qck_it(@"should send nils for nil values", ^{ - __block id receivedVal1; - __block id receivedVal2; - __block id receivedVal3; - - RACSignal *combined = [RACSignal combineLatest:@[ subject1, subject2, subject3 ] reduce:^ id (id val1, id val2, id val3) { - receivedVal1 = val1; - receivedVal2 = val2; - receivedVal3 = val3; - return nil; - }]; - - __block BOOL gotValue = NO; - [combined subscribeNext:^(id x) { - gotValue = YES; - }]; - - [subject1 sendNext:nil]; - [subject2 sendNext:nil]; - [subject3 sendNext:nil]; - - expect(@(gotValue)).to(beTruthy()); - expect(receivedVal1).to(beNil()); - expect(receivedVal2).to(beNil()); - expect(receivedVal3).to(beNil()); - }); - - qck_it(@"should send the return result of the reduce block", ^{ - RACSignal *combined = [RACSignal combineLatest:@[ subject1, subject2, subject3 ] reduce:^(NSString *string1, NSString *string2, NSString *string3) { - return [NSString stringWithFormat:@"%@: %@%@", string1, string2, string3]; - }]; - - __block id received; - [combined subscribeNext:^(id x) { - received = x; - }]; - - [subject1 sendNext:@"hello"]; - [subject2 sendNext:@"world"]; - [subject3 sendNext:@"!!1"]; - - expect(received).to(equal(@"hello: world!!1")); - }); - - qck_it(@"should handle multiples of the same signals", ^{ - RACSignal *combined = [RACSignal combineLatest:@[ subject1, subject2, subject1, subject3 ] reduce:^(NSString *string1, NSString *string2, NSString *string3, NSString *string4) { - return [NSString stringWithFormat:@"%@ : %@ = %@ : %@", string1, string2, string3, string4]; - }]; - - NSMutableArray *receivedValues = NSMutableArray.array; - - [combined subscribeNext:^(id x) { - [receivedValues addObject:x]; - }]; - - [subject1 sendNext:@"apples"]; - expect(receivedValues.lastObject).to(beNil()); - - [subject2 sendNext:@"oranges"]; - expect(receivedValues.lastObject).to(beNil()); - - [subject3 sendNext:@"cattle"]; - expect(receivedValues.lastObject).to(equal(@"apples : oranges = apples : cattle")); - - [subject1 sendNext:@"horses"]; - expect(receivedValues.lastObject).to(equal(@"horses : oranges = horses : cattle")); - }); - - qck_it(@"should handle multiples of the same side-effecting signal", ^{ - __block NSUInteger counter = 0; - RACSignal *sideEffectingSignal = [RACSignal createSignal:^ id (id subscriber) { - [subscriber sendNext:@(++counter)]; - [subscriber sendCompleted]; - return nil; - }]; - - RACSignal *combined = [RACSignal combineLatest:@[ sideEffectingSignal, sideEffectingSignal, sideEffectingSignal ] reduce:^(id x, id y, id z) { - return [NSString stringWithFormat:@"%@%@%@", x, y, z]; - }]; - - NSMutableArray *receivedValues = [NSMutableArray array]; - expect(@(counter)).to(equal(@0)); - - [combined subscribeNext:^(id x) { - [receivedValues addObject:x]; - }]; - - expect(@(counter)).to(equal(@3)); - expect(receivedValues).to(equal(@[ @"123" ])); - }); -}); - -qck_describe(@"distinctUntilChanged", ^{ - qck_it(@"should only send values that are distinct from the previous value", ^{ - RACSignal *sub = [[RACSignal createSignal:^ RACDisposable * (id subscriber) { - [subscriber sendNext:@1]; - [subscriber sendNext:@2]; - [subscriber sendNext:@2]; - [subscriber sendNext:@1]; - [subscriber sendNext:@1]; - [subscriber sendCompleted]; - return nil; - }] distinctUntilChanged]; - - NSArray *values = sub.toArray; - NSArray *expected = @[ @1, @2, @1 ]; - expect(values).to(equal(expected)); - }); - - qck_it(@"shouldn't consider nils to always be distinct", ^{ - RACSignal *sub = [[RACSignal createSignal:^ RACDisposable * (id subscriber) { - [subscriber sendNext:@1]; - [subscriber sendNext:nil]; - [subscriber sendNext:nil]; - [subscriber sendNext:nil]; - [subscriber sendNext:@1]; - [subscriber sendCompleted]; - return nil; - }] distinctUntilChanged]; - - NSArray *values = sub.toArray; - NSArray *expected = @[ @1, [NSNull null], @1 ]; - expect(values).to(equal(expected)); - }); - - qck_it(@"should consider initial nil to be distinct", ^{ - RACSignal *sub = [[RACSignal createSignal:^ RACDisposable * (id subscriber) { - [subscriber sendNext:nil]; - [subscriber sendNext:nil]; - [subscriber sendNext:@1]; - [subscriber sendCompleted]; - return nil; - }] distinctUntilChanged]; - - NSArray *values = sub.toArray; - NSArray *expected = @[ [NSNull null], @1 ]; - expect(values).to(equal(expected)); - }); -}); - -qck_describe(@"RACObserve", ^{ - __block RACTestObject *testObject; - - qck_beforeEach(^{ - testObject = [[RACTestObject alloc] init]; - }); - - qck_it(@"should work with object properties", ^{ - NSArray *expected = @[ @"hello", @"world" ]; - testObject.objectValue = expected[0]; - - NSMutableArray *valuesReceived = [NSMutableArray array]; - [RACObserve(testObject, objectValue) subscribeNext:^(id x) { - [valuesReceived addObject:x]; - }]; - - testObject.objectValue = expected[1]; - - expect(valuesReceived).to(equal(expected)); - }); - - qck_it(@"should work with non-object properties", ^{ - NSArray *expected = @[ @42, @43 ]; - testObject.integerValue = [expected[0] integerValue]; - - NSMutableArray *valuesReceived = [NSMutableArray array]; - [RACObserve(testObject, integerValue) subscribeNext:^(id x) { - [valuesReceived addObject:x]; - }]; - - testObject.integerValue = [expected[1] integerValue]; - - expect(valuesReceived).to(equal(expected)); - }); - - qck_it(@"should read the initial value upon subscription", ^{ - testObject.objectValue = @"foo"; - - RACSignal *signal = RACObserve(testObject, objectValue); - testObject.objectValue = @"bar"; - - expect([signal first]).to(equal(@"bar")); - }); -}); - -qck_describe(@"-setKeyPath:onObject:", ^{ - id setupBlock = ^(RACTestObject *testObject, NSString *keyPath, id nilValue, RACSignal *signal) { - [signal setKeyPath:keyPath onObject:testObject nilValue:nilValue]; - }; - - qck_itBehavesLike(RACPropertySignalExamples, ^{ - return @{ RACPropertySignalExamplesSetupBlock: setupBlock }; - }); - - qck_it(@"shouldn't send values to dealloc'd objects", ^{ - RACSubject *subject = [RACSubject subject]; - @autoreleasepool { - RACTestObject *testObject __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init]; - [subject setKeyPath:@keypath(testObject.objectValue) onObject:testObject]; - expect(testObject.objectValue).to(beNil()); - - [subject sendNext:@1]; - expect(testObject.objectValue).to(equal(@1)); - - [subject sendNext:@2]; - expect(testObject.objectValue).to(equal(@2)); - } - - // This shouldn't do anything. - [subject sendNext:@3]; - }); - - qck_it(@"should allow a new derivation after the signal's completed", ^{ - RACSubject *subject1 = [RACSubject subject]; - RACTestObject *testObject = [[RACTestObject alloc] init]; - [subject1 setKeyPath:@keypath(testObject.objectValue) onObject:testObject]; - [subject1 sendCompleted]; - - RACSubject *subject2 = [RACSubject subject]; - // This will assert if the previous completion didn't dispose of the - // subscription. - [subject2 setKeyPath:@keypath(testObject.objectValue) onObject:testObject]; - }); - - qck_it(@"should set the given value when nil is received", ^{ - RACSubject *subject = [RACSubject subject]; - RACTestObject *testObject = [[RACTestObject alloc] init]; - [subject setKeyPath:@keypath(testObject.integerValue) onObject:testObject nilValue:@5]; - - [subject sendNext:@1]; - expect(@(testObject.integerValue)).to(equal(@1)); - - [subject sendNext:nil]; - expect(@(testObject.integerValue)).to(equal(@5)); - - [subject sendCompleted]; - expect(@(testObject.integerValue)).to(equal(@5)); - }); - - qck_it(@"should keep object alive over -sendNext:", ^{ - RACSubject *subject = [RACSubject subject]; - __block RACTestObject *testObject = [[RACTestObject alloc] init]; - __block id deallocValue; - - __unsafe_unretained RACTestObject *unsafeTestObject = testObject; - [testObject.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - deallocValue = unsafeTestObject.slowObjectValue; - }]]; - - [subject setKeyPath:@keypath(testObject.slowObjectValue) onObject:testObject]; - expect(testObject.slowObjectValue).to(beNil()); - - // Attempt to deallocate concurrently. - [[RACScheduler scheduler] afterDelay:0.01 schedule:^{ - testObject = nil; - }]; - - expect(deallocValue).to(beNil()); - [subject sendNext:@1]; - expect(deallocValue).to(equal(@1)); - }); -}); - -qck_describe(@"memory management", ^{ - qck_it(@"should dealloc signals if the signal does nothing", ^{ - __block BOOL deallocd = NO; - @autoreleasepool { - RACSignal *signal __attribute__((objc_precise_lifetime)) = [RACSignal createSignal:^ id (id subscriber) { - return nil; - }]; - - [signal.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - deallocd = YES; - }]]; - } - - expect(@(deallocd)).toEventually(beTruthy()); - }); - - qck_it(@"should dealloc signals if the signal immediately completes", ^{ - __block BOOL deallocd = NO; - @autoreleasepool { - __block BOOL done = NO; - - RACSignal *signal __attribute__((objc_precise_lifetime)) = [RACSignal createSignal:^ id (id subscriber) { - [subscriber sendCompleted]; - return nil; - }]; - - [signal.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - deallocd = YES; - }]]; - - [signal subscribeCompleted:^{ - done = YES; - }]; - - expect(@(done)).toEventually(beTruthy()); - } - - expect(@(deallocd)).toEventually(beTruthy()); - }); - - qck_it(@"should dealloc a replay subject if it completes immediately", ^{ - __block BOOL completed = NO; - __block BOOL deallocd = NO; - @autoreleasepool { - RACReplaySubject *subject __attribute__((objc_precise_lifetime)) = [RACReplaySubject subject]; - [subject sendCompleted]; - - [subject.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - deallocd = YES; - }]]; - - [subject subscribeCompleted:^{ - completed = YES; - }]; - } - - expect(@(completed)).toEventually(beTruthy()); - expect(@(deallocd)).toEventually(beTruthy()); - }); - - qck_it(@"should dealloc if the signal was created on a background queue", ^{ - __block BOOL completed = NO; - __block BOOL deallocd = NO; - @autoreleasepool { - [[RACScheduler scheduler] schedule:^{ - RACSignal *signal __attribute__((objc_precise_lifetime)) = [RACSignal createSignal:^ id (id subscriber) { - [subscriber sendCompleted]; - return nil; - }]; - - [signal.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - deallocd = YES; - }]]; - - [signal subscribeCompleted:^{ - completed = YES; - }]; - }]; - } - - expect(@(completed)).toEventually(beTruthy()); - expect(@(deallocd)).toEventually(beTruthy()); - }); - - qck_it(@"should dealloc if the signal was created on a background queue, never gets any subscribers, and the background queue gets delayed", ^{ - __block BOOL deallocd = NO; - dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); - - @autoreleasepool { - [[RACScheduler scheduler] schedule:^{ - RACSignal *signal __attribute__((objc_precise_lifetime)) = [RACSignal createSignal:^ id (id subscriber) { - return nil; - }]; - - [signal.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - deallocd = YES; - dispatch_semaphore_signal(semaphore); - }]]; - - [NSThread sleepForTimeInterval:1]; - - expect(@(deallocd)).to(beFalsy()); - }]; - } - - dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); - expect(@(deallocd)).to(beTruthy()); - }); - - qck_it(@"should retain intermediate signals when subscribing", ^{ - RACSubject *subject = [RACSubject subject]; - expect(subject).notTo(beNil()); - - __block BOOL gotNext = NO; - __block BOOL completed = NO; - - RACDisposable *disposable; - - @autoreleasepool { - RACSignal *intermediateSignal = [subject doNext:^(id _) { - gotNext = YES; - }]; - - expect(intermediateSignal).notTo(beNil()); - - disposable = [intermediateSignal subscribeCompleted:^{ - completed = YES; - }]; - } - - [subject sendNext:@5]; - expect(@(gotNext)).to(beTruthy()); - - [subject sendCompleted]; - expect(@(completed)).to(beTruthy()); - - [disposable dispose]; - }); -}); - -qck_describe(@"-merge:", ^{ - __block RACSubject *sub1; - __block RACSubject *sub2; - __block RACSignal *merged; - qck_beforeEach(^{ - sub1 = [RACSubject subject]; - sub2 = [RACSubject subject]; - merged = [sub1 merge:sub2]; - }); - - qck_it(@"should send all values from both signals", ^{ - NSMutableArray *values = [NSMutableArray array]; - [merged subscribeNext:^(id x) { - [values addObject:x]; - }]; - - [sub1 sendNext:@1]; - [sub2 sendNext:@2]; - [sub2 sendNext:@3]; - [sub1 sendNext:@4]; - - NSArray *expected = @[ @1, @2, @3, @4 ]; - expect(values).to(equal(expected)); - }); - - qck_it(@"should send an error if one occurs", ^{ - __block NSError *errorReceived; - [merged subscribeError:^(NSError *error) { - errorReceived = error; - }]; - - [sub1 sendError:RACSignalTestError]; - expect(errorReceived).to(equal(RACSignalTestError)); - }); - - qck_it(@"should complete only after both signals complete", ^{ - NSMutableArray *values = [NSMutableArray array]; - __block BOOL completed = NO; - [merged subscribeNext:^(id x) { - [values addObject:x]; - } completed:^{ - completed = YES; - }]; - - [sub1 sendNext:@1]; - [sub2 sendNext:@2]; - [sub2 sendNext:@3]; - [sub2 sendCompleted]; - expect(@(completed)).to(beFalsy()); - - [sub1 sendNext:@4]; - [sub1 sendCompleted]; - expect(@(completed)).to(beTruthy()); - - NSArray *expected = @[ @1, @2, @3, @4 ]; - expect(values).to(equal(expected)); - }); - - qck_it(@"should complete only after both signals complete for any number of subscribers", ^{ - __block BOOL completed1 = NO; - __block BOOL completed2 = NO; - [merged subscribeCompleted:^{ - completed1 = YES; - }]; - - [merged subscribeCompleted:^{ - completed2 = YES; - }]; - - expect(@(completed1)).to(beFalsy()); - expect(@(completed2)).to(beFalsy()); - - [sub1 sendCompleted]; - [sub2 sendCompleted]; - expect(@(completed1)).to(beTruthy()); - expect(@(completed2)).to(beTruthy()); - }); -}); - -qck_describe(@"+merge:", ^{ - __block RACSubject *sub1; - __block RACSubject *sub2; - __block RACSignal *merged; - qck_beforeEach(^{ - sub1 = [RACSubject subject]; - sub2 = [RACSubject subject]; - merged = [RACSignal merge:@[ sub1, sub2 ].objectEnumerator]; - }); - - qck_it(@"should send all values from both signals", ^{ - NSMutableArray *values = [NSMutableArray array]; - [merged subscribeNext:^(id x) { - [values addObject:x]; - }]; - - [sub1 sendNext:@1]; - [sub2 sendNext:@2]; - [sub2 sendNext:@3]; - [sub1 sendNext:@4]; - - NSArray *expected = @[ @1, @2, @3, @4 ]; - expect(values).to(equal(expected)); - }); - - qck_it(@"should send an error if one occurs", ^{ - __block NSError *errorReceived; - [merged subscribeError:^(NSError *error) { - errorReceived = error; - }]; - - [sub1 sendError:RACSignalTestError]; - expect(errorReceived).to(equal(RACSignalTestError)); - }); - - qck_it(@"should complete only after both signals complete", ^{ - NSMutableArray *values = [NSMutableArray array]; - __block BOOL completed = NO; - [merged subscribeNext:^(id x) { - [values addObject:x]; - } completed:^{ - completed = YES; - }]; - - [sub1 sendNext:@1]; - [sub2 sendNext:@2]; - [sub2 sendNext:@3]; - [sub2 sendCompleted]; - expect(@(completed)).to(beFalsy()); - - [sub1 sendNext:@4]; - [sub1 sendCompleted]; - expect(@(completed)).to(beTruthy()); - - NSArray *expected = @[ @1, @2, @3, @4 ]; - expect(values).to(equal(expected)); - }); - - qck_it(@"should complete immediately when not given any signals", ^{ - RACSignal *signal = [RACSignal merge:@[].objectEnumerator]; - - __block BOOL completed = NO; - [signal subscribeCompleted:^{ - completed = YES; - }]; - - expect(@(completed)).to(beTruthy()); - }); - - qck_it(@"should complete only after both signals complete for any number of subscribers", ^{ - __block BOOL completed1 = NO; - __block BOOL completed2 = NO; - [merged subscribeCompleted:^{ - completed1 = YES; - }]; - - [merged subscribeCompleted:^{ - completed2 = YES; - }]; - - expect(@(completed1)).to(beFalsy()); - expect(@(completed2)).to(beFalsy()); - - [sub1 sendCompleted]; - [sub2 sendCompleted]; - expect(@(completed1)).to(beTruthy()); - expect(@(completed2)).to(beTruthy()); - }); -}); - -qck_describe(@"-flatten:", ^{ - __block BOOL subscribedTo1 = NO; - __block BOOL subscribedTo2 = NO; - __block BOOL subscribedTo3 = NO; - __block RACSignal *sub1; - __block RACSignal *sub2; - __block RACSignal *sub3; - __block RACSubject *subject1; - __block RACSubject *subject2; - __block RACSubject *subject3; - __block RACSubject *signalsSubject; - __block NSMutableArray *values; - - qck_beforeEach(^{ - subscribedTo1 = NO; - subject1 = [RACSubject subject]; - sub1 = [RACSignal defer:^{ - subscribedTo1 = YES; - return subject1; - }]; - - subscribedTo2 = NO; - subject2 = [RACSubject subject]; - sub2 = [RACSignal defer:^{ - subscribedTo2 = YES; - return subject2; - }]; - - subscribedTo3 = NO; - subject3 = [RACSubject subject]; - sub3 = [RACSignal defer:^{ - subscribedTo3 = YES; - return subject3; - }]; - - signalsSubject = [RACSubject subject]; - - values = [NSMutableArray array]; - }); - - qck_describe(@"when its max is 0", ^{ - qck_it(@"should merge all the signals concurrently", ^{ - [[signalsSubject flatten:0] subscribeNext:^(id x) { - [values addObject:x]; - }]; - - expect(@(subscribedTo1)).to(beFalsy()); - expect(@(subscribedTo2)).to(beFalsy()); - expect(@(subscribedTo3)).to(beFalsy()); - - [signalsSubject sendNext:sub1]; - [signalsSubject sendNext:sub2]; - - expect(@(subscribedTo1)).to(beTruthy()); - expect(@(subscribedTo2)).to(beTruthy()); - expect(@(subscribedTo3)).to(beFalsy()); - - [subject1 sendNext:@1]; - - [signalsSubject sendNext:sub3]; - - expect(@(subscribedTo1)).to(beTruthy()); - expect(@(subscribedTo2)).to(beTruthy()); - expect(@(subscribedTo3)).to(beTruthy()); - - [subject1 sendCompleted]; - - [subject2 sendNext:@2]; - [subject2 sendCompleted]; - - [subject3 sendNext:@3]; - [subject3 sendCompleted]; - - NSArray *expected = @[ @1, @2, @3 ]; - expect(values).to(equal(expected)); - }); - - qck_itBehavesLike(RACSignalMergeConcurrentCompletionExampleGroup, ^{ - return @{ RACSignalMaxConcurrent: @0 }; - }); - }); - - qck_describe(@"when its max is > 0", ^{ - qck_it(@"should merge only the given number at a time", ^{ - [[signalsSubject flatten:1] subscribeNext:^(id x) { - [values addObject:x]; - }]; - - expect(@(subscribedTo1)).to(beFalsy()); - expect(@(subscribedTo2)).to(beFalsy()); - expect(@(subscribedTo3)).to(beFalsy()); - - [signalsSubject sendNext:sub1]; - [signalsSubject sendNext:sub2]; - - expect(@(subscribedTo1)).to(beTruthy()); - expect(@(subscribedTo2)).to(beFalsy()); - expect(@(subscribedTo3)).to(beFalsy()); - - [subject1 sendNext:@1]; - - [signalsSubject sendNext:sub3]; - - expect(@(subscribedTo1)).to(beTruthy()); - expect(@(subscribedTo2)).to(beFalsy()); - expect(@(subscribedTo3)).to(beFalsy()); - - [signalsSubject sendCompleted]; - - expect(@(subscribedTo1)).to(beTruthy()); - expect(@(subscribedTo2)).to(beFalsy()); - expect(@(subscribedTo3)).to(beFalsy()); - - [subject1 sendCompleted]; - - expect(@(subscribedTo2)).to(beTruthy()); - expect(@(subscribedTo3)).to(beFalsy()); - - [subject2 sendNext:@2]; - [subject2 sendCompleted]; - - expect(@(subscribedTo3)).to(beTruthy()); - - [subject3 sendNext:@3]; - [subject3 sendCompleted]; - - NSArray *expected = @[ @1, @2, @3 ]; - expect(values).to(equal(expected)); - }); - - qck_itBehavesLike(RACSignalMergeConcurrentCompletionExampleGroup, ^{ - return @{ RACSignalMaxConcurrent: @1 }; - }); - }); - - qck_it(@"shouldn't create a retain cycle", ^{ - __block BOOL subjectDeallocd = NO; - __block BOOL signalDeallocd = NO; - @autoreleasepool { - RACSubject *subject __attribute__((objc_precise_lifetime)) = [RACSubject subject]; - [subject.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - subjectDeallocd = YES; - }]]; - - RACSignal *signal __attribute__((objc_precise_lifetime)) = [subject flatten]; - [signal.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - signalDeallocd = YES; - }]]; - - [signal subscribeCompleted:^{}]; - - [subject sendCompleted]; - } - - expect(@(subjectDeallocd)).toEventually(beTruthy()); - expect(@(signalDeallocd)).toEventually(beTruthy()); - }); - - qck_it(@"should not crash when disposing while subscribing", ^{ - RACDisposable *disposable = [[signalsSubject flatten:0] subscribeCompleted:^{ - }]; - - [signalsSubject sendNext:[RACSignal createSignal:^ RACDisposable * (id subscriber) { - [disposable dispose]; - [subscriber sendCompleted]; - return nil; - }]]; - - [signalsSubject sendCompleted]; - }); - - qck_it(@"should dispose after last synchronous signal subscription and should not crash", ^{ - - RACSignal *flattened = [signalsSubject flatten:1]; - RACDisposable *flattenDisposable = [flattened subscribeCompleted:^{}]; - - RACSignal *syncSignal = [RACSignal createSignal:^ RACDisposable *(id subscriber) { - expect(@(flattenDisposable.disposed)).to(beFalsy()); - [subscriber sendCompleted]; - expect(@(flattenDisposable.disposed)).to(beTruthy()); - return nil; - }]; - - RACSignal *asyncSignal = [sub1 delay:0]; - - [signalsSubject sendNext:asyncSignal]; - [signalsSubject sendNext:syncSignal]; - - [signalsSubject sendCompleted]; - - [subject1 sendCompleted]; - - expect(@(flattenDisposable.disposed)).toEventually(beTruthy()); - }); - - qck_it(@"should not crash when disposed because of takeUntil:", ^{ - for (int i = 0; i < 100; i++) { - RACSubject *flattenedReceiver = [RACSubject subject]; - RACSignal *done = [flattenedReceiver map:^(NSNumber *n) { - return @(n.integerValue == 1); - }]; - - RACSignal *flattened = [signalsSubject flatten:1]; - - RACDisposable *flattenDisposable = [[flattened takeUntil:[done ignore:@NO]] subscribe:flattenedReceiver]; - - RACSignal *syncSignal = [RACSignal createSignal:^ RACDisposable *(id subscriber) { - expect(@(flattenDisposable.disposed)).to(beFalsy()); - [subscriber sendNext:@1]; - expect(@(flattenDisposable.disposed)).to(beTruthy()); - [subscriber sendCompleted]; - return nil; - }]; - - RACSignal *asyncSignal = [sub1 delay:0]; - [subject1 sendNext:@0]; - - [signalsSubject sendNext:asyncSignal]; - [signalsSubject sendNext:syncSignal]; - [signalsSubject sendCompleted]; - - [subject1 sendCompleted]; - - expect(@(flattenDisposable.disposed)).toEventually(beTruthy()); - } - }); -}); - -qck_describe(@"-switchToLatest", ^{ - __block RACSubject *subject; - - __block NSMutableArray *values; - __block NSError *lastError = nil; - __block BOOL completed = NO; - - qck_beforeEach(^{ - subject = [RACSubject subject]; - - values = [NSMutableArray array]; - lastError = nil; - completed = NO; - - [[subject switchToLatest] subscribeNext:^(id x) { - expect(lastError).to(beNil()); - expect(@(completed)).to(beFalsy()); - - [values addObject:x]; - } error:^(NSError *error) { - expect(lastError).to(beNil()); - expect(@(completed)).to(beFalsy()); - - lastError = error; - } completed:^{ - expect(lastError).to(beNil()); - expect(@(completed)).to(beFalsy()); - - completed = YES; - }]; - }); - - qck_it(@"should send values from the most recent signal", ^{ - [subject sendNext:[RACSignal createSignal:^ RACDisposable * (id subscriber) { - [subscriber sendNext:@1]; - [subscriber sendNext:@2]; - return nil; - }]]; - - [subject sendNext:[RACSignal createSignal:^ RACDisposable * (id subscriber) { - [subscriber sendNext:@3]; - [subscriber sendNext:@4]; - return nil; - }]]; - - NSArray *expected = @[ @1, @2, @3, @4 ]; - expect(values).to(equal(expected)); - }); - - qck_it(@"should send errors from the most recent signal", ^{ - [subject sendNext:[RACSignal createSignal:^ id (id subscriber) { - [subscriber sendError:[NSError errorWithDomain:@"" code:-1 userInfo:nil]]; - return nil; - }]]; - - expect(lastError).notTo(beNil()); - }); - - qck_it(@"should not send completed if only the switching signal completes", ^{ - [subject sendNext:RACSignal.never]; - - expect(@(completed)).to(beFalsy()); - - [subject sendCompleted]; - expect(@(completed)).to(beFalsy()); - }); - - qck_it(@"should send completed when the switching signal completes and the last sent signal does", ^{ - [subject sendNext:RACSignal.empty]; - - expect(@(completed)).to(beFalsy()); - - [subject sendCompleted]; - expect(@(completed)).to(beTruthy()); - }); - - qck_it(@"should accept nil signals", ^{ - [subject sendNext:nil]; - [subject sendNext:[RACSignal createSignal:^ RACDisposable * (id subscriber) { - [subscriber sendNext:@1]; - [subscriber sendNext:@2]; - return nil; - }]]; - - NSArray *expected = @[ @1, @2 ]; - expect(values).to(equal(expected)); - }); - - qck_it(@"should return a cold signal", ^{ - __block NSUInteger subscriptions = 0; - RACSignal *signalOfSignals = [RACSignal createSignal:^ RACDisposable * (id subscriber) { - subscriptions++; - [subscriber sendNext:[RACSignal empty]]; - return nil; - }]; - - RACSignal *switched = [signalOfSignals switchToLatest]; - - [[switched publish] connect]; - expect(@(subscriptions)).to(equal(@1)); - - [[switched publish] connect]; - expect(@(subscriptions)).to(equal(@2)); - }); -}); - -qck_describe(@"+switch:cases:default:", ^{ - __block RACSubject *keySubject; - - __block RACSubject *subjectZero; - __block RACSubject *subjectOne; - __block RACSubject *subjectTwo; - - __block RACSubject *defaultSubject; - - __block NSMutableArray *values; - __block NSError *lastError = nil; - __block BOOL completed = NO; - - qck_beforeEach(^{ - keySubject = [RACSubject subject]; - - subjectZero = [RACSubject subject]; - subjectOne = [RACSubject subject]; - subjectTwo = [RACSubject subject]; - - defaultSubject = [RACSubject subject]; - - values = [NSMutableArray array]; - lastError = nil; - completed = NO; - }); - - qck_describe(@"switching between values with a default", ^{ - __block RACSignal *switchSignal; - - qck_beforeEach(^{ - switchSignal = [RACSignal switch:keySubject cases:@{ - @0: subjectZero, - @1: subjectOne, - @2: subjectTwo, - } default:[RACSignal never]]; - - [switchSignal subscribeNext:^(id x) { - expect(lastError).to(beNil()); - expect(@(completed)).to(beFalsy()); - - [values addObject:x]; - } error:^(NSError *error) { - expect(lastError).to(beNil()); - expect(@(completed)).to(beFalsy()); - - lastError = error; - } completed:^{ - expect(lastError).to(beNil()); - expect(@(completed)).to(beFalsy()); - - completed = YES; - }]; - }); - - qck_it(@"should not send any values before a key is sent", ^{ - [subjectZero sendNext:RACUnit.defaultUnit]; - [subjectOne sendNext:RACUnit.defaultUnit]; - [subjectTwo sendNext:RACUnit.defaultUnit]; - - expect(values).to(equal(@[])); - expect(lastError).to(beNil()); - expect(@(completed)).to(beFalsy()); - }); - - qck_it(@"should send events based on the latest key", ^{ - [keySubject sendNext:@0]; - - [subjectZero sendNext:@"zero"]; - [subjectZero sendNext:@"zero"]; - [subjectOne sendNext:@"one"]; - [subjectTwo sendNext:@"two"]; - - NSArray *expected = @[ @"zero", @"zero" ]; - expect(values).to(equal(expected)); - - [keySubject sendNext:@1]; - - [subjectZero sendNext:@"zero"]; - [subjectOne sendNext:@"one"]; - [subjectTwo sendNext:@"two"]; - - expected = @[ @"zero", @"zero", @"one" ]; - expect(values).to(equal(expected)); - - expect(lastError).to(beNil()); - expect(@(completed)).to(beFalsy()); - - [keySubject sendNext:@2]; - - [subjectZero sendError:[NSError errorWithDomain:@"" code:-1 userInfo:nil]]; - [subjectOne sendError:[NSError errorWithDomain:@"" code:-1 userInfo:nil]]; - expect(lastError).to(beNil()); - - [subjectTwo sendError:[NSError errorWithDomain:@"" code:-1 userInfo:nil]]; - expect(lastError).notTo(beNil()); - }); - - qck_it(@"should not send completed when only the key signal completes", ^{ - [keySubject sendNext:@0]; - [subjectZero sendNext:@"zero"]; - [keySubject sendCompleted]; - - expect(values).to(equal(@[ @"zero" ])); - expect(@(completed)).to(beFalsy()); - }); - - qck_it(@"should send completed when the key signal and the latest sent signal complete", ^{ - [keySubject sendNext:@0]; - [subjectZero sendNext:@"zero"]; - [keySubject sendCompleted]; - [subjectZero sendCompleted]; - - expect(values).to(equal(@[ @"zero" ])); - expect(@(completed)).to(beTruthy()); - }); - }); - - qck_it(@"should use the default signal if key that was sent does not have an associated signal", ^{ - [[RACSignal - switch:keySubject - cases:@{ - @0: subjectZero, - @1: subjectOne, - } - default:defaultSubject] - subscribeNext:^(id x) { - [values addObject:x]; - }]; - - [keySubject sendNext:@"not a valid key"]; - [defaultSubject sendNext:@"default"]; - - expect(values).to(equal(@[ @"default" ])); - - [keySubject sendNext:nil]; - [defaultSubject sendNext:@"default"]; - - expect(values).to(equal((@[ @"default", @"default" ]))); - }); - - qck_it(@"should send an error if key that was sent does not have an associated signal and there's no default", ^{ - [[RACSignal - switch:keySubject - cases:@{ - @0: subjectZero, - @1: subjectOne, - } - default:nil] - subscribeNext:^(id x) { - [values addObject:x]; - } error:^(NSError *error) { - lastError = error; - }]; - - [keySubject sendNext:@0]; - [subjectZero sendNext:@"zero"]; - - expect(values).to(equal(@[ @"zero" ])); - expect(lastError).to(beNil()); - - [keySubject sendNext:nil]; - - expect(values).to(equal(@[ @"zero" ])); - expect(lastError).notTo(beNil()); - expect(lastError.domain).to(equal(RACSignalErrorDomain)); - expect(@(lastError.code)).to(equal(@(RACSignalErrorNoMatchingCase))); - }); - - qck_it(@"should match RACTupleNil case when a nil value is sent", ^{ - [[RACSignal - switch:keySubject - cases:@{ - RACTupleNil.tupleNil: subjectZero, - } - default:defaultSubject] - subscribeNext:^(id x) { - [values addObject:x]; - }]; - - [keySubject sendNext:nil]; - [subjectZero sendNext:@"zero"]; - expect(values).to(equal(@[ @"zero" ])); - }); -}); - -qck_describe(@"+if:then:else", ^{ - __block RACSubject *boolSubject; - __block RACSubject *trueSubject; - __block RACSubject *falseSubject; - - __block NSMutableArray *values; - __block NSError *lastError = nil; - __block BOOL completed = NO; - - qck_beforeEach(^{ - boolSubject = [RACSubject subject]; - trueSubject = [RACSubject subject]; - falseSubject = [RACSubject subject]; - - values = [NSMutableArray array]; - lastError = nil; - completed = NO; - - [[RACSignal if:boolSubject then:trueSubject else:falseSubject] subscribeNext:^(id x) { - expect(lastError).to(beNil()); - expect(@(completed)).to(beFalsy()); - - [values addObject:x]; - } error:^(NSError *error) { - expect(lastError).to(beNil()); - expect(@(completed)).to(beFalsy()); - - lastError = error; - } completed:^{ - expect(lastError).to(beNil()); - expect(@(completed)).to(beFalsy()); - - completed = YES; - }]; - }); - - qck_it(@"should not send any values before a boolean is sent", ^{ - [trueSubject sendNext:RACUnit.defaultUnit]; - [falseSubject sendNext:RACUnit.defaultUnit]; - - expect(values).to(equal(@[])); - expect(lastError).to(beNil()); - expect(@(completed)).to(beFalsy()); - }); - - qck_it(@"should send events based on the latest boolean", ^{ - [boolSubject sendNext:@YES]; - - [trueSubject sendNext:@"foo"]; - [falseSubject sendNext:@"buzz"]; - [trueSubject sendNext:@"bar"]; - - NSArray *expected = @[ @"foo", @"bar" ]; - expect(values).to(equal(expected)); - expect(lastError).to(beNil()); - expect(@(completed)).to(beFalsy()); - - [boolSubject sendNext:@NO]; - - [trueSubject sendNext:@"baz"]; - [falseSubject sendNext:@"buzz"]; - [trueSubject sendNext:@"barfoo"]; - - expected = @[ @"foo", @"bar", @"buzz" ]; - expect(values).to(equal(expected)); - expect(lastError).to(beNil()); - expect(@(completed)).to(beFalsy()); - - [trueSubject sendError:[NSError errorWithDomain:@"" code:-1 userInfo:nil]]; - expect(lastError).to(beNil()); - - [falseSubject sendError:[NSError errorWithDomain:@"" code:-1 userInfo:nil]]; - expect(lastError).notTo(beNil()); - }); - - qck_it(@"should not send completed when only the BOOL signal completes", ^{ - [boolSubject sendNext:@YES]; - [trueSubject sendNext:@"foo"]; - [boolSubject sendCompleted]; - - expect(values).to(equal(@[ @"foo" ])); - expect(@(completed)).to(beFalsy()); - }); - - qck_it(@"should send completed when the BOOL signal and the latest sent signal complete", ^{ - [boolSubject sendNext:@YES]; - [trueSubject sendNext:@"foo"]; - [trueSubject sendCompleted]; - [boolSubject sendCompleted]; - - expect(values).to(equal(@[ @"foo" ])); - expect(@(completed)).to(beTruthy()); - }); -}); - -qck_describe(@"+interval:onScheduler: and +interval:onScheduler:withLeeway:", ^{ - static const NSTimeInterval interval = 0.1; - static const NSTimeInterval leeway = 0.2; - - __block void (^testTimer)(RACSignal *, NSNumber *, NSNumber *) = nil; - - qck_beforeEach(^{ - testTimer = [^(RACSignal *timer, NSNumber *minInterval, NSNumber *leeway) { - __block NSUInteger nextsReceived = 0; - - NSTimeInterval startTime = NSDate.timeIntervalSinceReferenceDate; - [[timer take:3] subscribeNext:^(NSDate *date) { - ++nextsReceived; - - NSTimeInterval currentTime = date.timeIntervalSinceReferenceDate; - - // Uniformly distribute the expected interval for all - // received values. We do this instead of saving a timestamp - // because a delayed interval may cause the _next_ value to - // send sooner than the interval. - NSTimeInterval expectedMinInterval = minInterval.doubleValue * nextsReceived; - NSTimeInterval expectedMaxInterval = expectedMinInterval + leeway.doubleValue * 3 + 0.1; - - expect(@(currentTime - startTime)).to(beGreaterThanOrEqualTo(@(expectedMinInterval))); - expect(@(currentTime - startTime)).to(beLessThanOrEqualTo(@(expectedMaxInterval))); - }]; - - expect(@(nextsReceived)).toEventually(equal(@3)); - } copy]; - }); - - qck_describe(@"+interval:onScheduler:", ^{ - qck_it(@"should work on the main thread scheduler", ^{ - testTimer([RACSignal interval:interval onScheduler:RACScheduler.mainThreadScheduler], @(interval), @0); - }); - - qck_it(@"should work on a background scheduler", ^{ - testTimer([RACSignal interval:interval onScheduler:[RACScheduler scheduler]], @(interval), @0); - }); - }); - - qck_describe(@"+interval:onScheduler:withLeeway:", ^{ - qck_it(@"should work on the main thread scheduler", ^{ - testTimer([RACSignal interval:interval onScheduler:RACScheduler.mainThreadScheduler withLeeway:leeway], @(interval), @(leeway)); - }); - - qck_it(@"should work on a background scheduler", ^{ - testTimer([RACSignal interval:interval onScheduler:[RACScheduler scheduler] withLeeway:leeway], @(interval), @(leeway)); - }); - }); -}); - -qck_describe(@"-timeout:onScheduler:", ^{ - __block RACSubject *subject; - - qck_beforeEach(^{ - subject = [RACSubject subject]; - }); - - qck_it(@"should time out", ^{ - RACTestScheduler *scheduler = [[RACTestScheduler alloc] init]; - - __block NSError *receivedError = nil; - [[subject timeout:1 onScheduler:scheduler] subscribeError:^(NSError *e) { - receivedError = e; - }]; - - expect(receivedError).to(beNil()); - - [scheduler stepAll]; - expect(receivedError).toEventuallyNot(beNil()); - expect(receivedError.domain).to(equal(RACSignalErrorDomain)); - expect(@(receivedError.code)).to(equal(@(RACSignalErrorTimedOut))); - }); - - qck_it(@"should pass through events while not timed out", ^{ - __block id next = nil; - __block BOOL completed = NO; - [[subject timeout:1 onScheduler:RACScheduler.mainThreadScheduler] subscribeNext:^(id x) { - next = x; - } completed:^{ - completed = YES; - }]; - - [subject sendNext:RACUnit.defaultUnit]; - expect(next).to(equal(RACUnit.defaultUnit)); - - [subject sendCompleted]; - expect(@(completed)).to(beTruthy()); - }); - - qck_it(@"should not time out after disposal", ^{ - RACTestScheduler *scheduler = [[RACTestScheduler alloc] init]; - - __block NSError *receivedError = nil; - RACDisposable *disposable = [[subject timeout:1 onScheduler:scheduler] subscribeError:^(NSError *e) { - receivedError = e; - }]; - - [disposable dispose]; - [scheduler stepAll]; - expect(receivedError).to(beNil()); - }); -}); - -qck_describe(@"-delay:", ^{ - __block RACSubject *subject; - __block RACSignal *delayedSignal; - - qck_beforeEach(^{ - subject = [RACSubject subject]; - delayedSignal = [subject delay:0]; - }); - - qck_it(@"should delay nexts", ^{ - __block id next = nil; - [delayedSignal subscribeNext:^(id x) { - next = x; - }]; - - [subject sendNext:@"foo"]; - expect(next).to(beNil()); - expect(next).toEventually(equal(@"foo")); - }); - - qck_it(@"should delay completed", ^{ - __block BOOL completed = NO; - [delayedSignal subscribeCompleted:^{ - completed = YES; - }]; - - [subject sendCompleted]; - expect(@(completed)).to(beFalsy()); - expect(@(completed)).toEventually(beTruthy()); - }); - - qck_it(@"should not delay errors", ^{ - __block NSError *error = nil; - [delayedSignal subscribeError:^(NSError *e) { - error = e; - }]; - - [subject sendError:RACSignalTestError]; - expect(error).to(equal(RACSignalTestError)); - }); - - qck_it(@"should cancel delayed events when disposed", ^{ - __block id next = nil; - RACDisposable *disposable = [delayedSignal subscribeNext:^(id x) { - next = x; - }]; - - [subject sendNext:@"foo"]; - - __block BOOL done = NO; - [RACScheduler.mainThreadScheduler after:[NSDate date] schedule:^{ - done = YES; - }]; - - [disposable dispose]; - - expect(@(done)).toEventually(beTruthy()); - expect(next).to(beNil()); - }); -}); - -qck_describe(@"-catch:", ^{ - qck_it(@"should subscribe to ensuing signal on error", ^{ - RACSubject *subject = [RACSubject subject]; - - RACSignal *signal = [subject catch:^(NSError *error) { - return [RACSignal return:@41]; - }]; - - __block id value = nil; - [signal subscribeNext:^(id x) { - value = x; - }]; - - [subject sendError:RACSignalTestError]; - expect(value).to(equal(@41)); - }); - - qck_it(@"should prevent source error from propagating", ^{ - RACSubject *subject = [RACSubject subject]; - - RACSignal *signal = [subject catch:^(NSError *error) { - return [RACSignal empty]; - }]; - - __block BOOL errorReceived = NO; - [signal subscribeError:^(NSError *error) { - errorReceived = YES; - }]; - - [subject sendError:RACSignalTestError]; - expect(@(errorReceived)).to(beFalsy()); - }); - - qck_it(@"should propagate error from ensuing signal", ^{ - RACSubject *subject = [RACSubject subject]; - - NSError *secondaryError = [NSError errorWithDomain:@"bubs" code:41 userInfo:nil]; - RACSignal *signal = [subject catch:^(NSError *error) { - return [RACSignal error:secondaryError]; - }]; - - __block NSError *errorReceived = nil; - [signal subscribeError:^(NSError *error) { - errorReceived = error; - }]; - - [subject sendError:RACSignalTestError]; - expect(errorReceived).to(equal(secondaryError)); - }); - - qck_it(@"should dispose ensuing signal", ^{ - RACSubject *subject = [RACSubject subject]; - - __block BOOL disposed = NO; - RACSignal *signal = [subject catch:^(NSError *error) { - return [RACSignal createSignal:^(id subscriber) { - return [RACDisposable disposableWithBlock:^{ - disposed = YES; - }]; - }]; - }]; - - RACDisposable *disposable = [signal subscribeCompleted:^{}]; - [subject sendError:RACSignalTestError]; - [disposable dispose]; - - expect(@(disposed)).toEventually(beTruthy()); - }); -}); - -qck_describe(@"+try:", ^{ - __block id value; - __block NSError *receivedError; - - qck_beforeEach(^{ - value = nil; - receivedError = nil; - }); - - qck_it(@"should pass the value if it is non-nil", ^{ - RACSignal *signal = [RACSignal try:^(NSError **error) { - return @"foo"; - }]; - - [signal subscribeNext:^(id x) { - value = x; - } error:^(NSError *error) { - receivedError = error; - }]; - - expect(value).to(equal(@"foo")); - expect(receivedError).to(beNil()); - }); - - qck_it(@"should ignore the error if the value is non-nil", ^{ - RACSignal *signal = [RACSignal try:^(NSError **error) { - if (error != nil) *error = RACSignalTestError; - - return @"foo"; - }]; - - [signal subscribeNext:^(id x) { - value = x; - } error:^(NSError *error) { - receivedError = error; - }]; - - expect(receivedError).to(beNil()); - expect(value).to(equal(@"foo")); - }); - - qck_it(@"should send the error if the return value is nil", ^{ - RACSignal *signal = [RACSignal try:^id(NSError **error) { - if (error) *error = RACSignalTestError; - - return nil; - }]; - - [signal subscribeNext:^(id x) { - value = x; - } error:^(NSError *error) { - receivedError = error; - }]; - - expect(value).to(beNil()); - expect(receivedError).to(equal(RACSignalTestError)); - }); -}); - -qck_describe(@"-try:", ^{ - __block RACSubject *subject; - __block NSError *receivedError; - __block NSMutableArray *nextValues; - __block BOOL completed; - - qck_beforeEach(^{ - subject = [RACSubject subject]; - nextValues = [NSMutableArray array]; - completed = NO; - receivedError = nil; - - [[subject try:^(NSString *value, NSError **error) { - if (value != nil) return YES; - - if (error != nil) *error = RACSignalTestError; - - return NO; - }] subscribeNext:^(id x) { - [nextValues addObject:x]; - } error:^(NSError *error) { - receivedError = error; - } completed:^{ - completed = YES; - }]; - }); - - qck_it(@"should pass values while YES is returned from the tryBlock", ^{ - [subject sendNext:@"foo"]; - [subject sendNext:@"bar"]; - [subject sendNext:@"baz"]; - [subject sendNext:@"buzz"]; - [subject sendCompleted]; - - NSArray *receivedValues = [nextValues copy]; - NSArray *expectedValues = @[ @"foo", @"bar", @"baz", @"buzz" ]; - - expect(receivedError).to(beNil()); - expect(receivedValues).to(equal(expectedValues)); - expect(@(completed)).to(beTruthy()); - }); - - qck_it(@"should pass values until NO is returned from the tryBlock", ^{ - [subject sendNext:@"foo"]; - [subject sendNext:@"bar"]; - [subject sendNext:nil]; - [subject sendNext:@"buzz"]; - [subject sendCompleted]; - - NSArray *receivedValues = [nextValues copy]; - NSArray *expectedValues = @[ @"foo", @"bar" ]; - - expect(receivedError).to(equal(RACSignalTestError)); - expect(receivedValues).to(equal(expectedValues)); - expect(@(completed)).to(beFalsy()); - }); -}); - -qck_describe(@"-tryMap:", ^{ - __block RACSubject *subject; - __block NSError *receivedError; - __block NSMutableArray *nextValues; - __block BOOL completed; - - qck_beforeEach(^{ - subject = [RACSubject subject]; - nextValues = [NSMutableArray array]; - completed = NO; - receivedError = nil; - - [[subject tryMap:^ id (NSString *value, NSError **error) { - if (value != nil) return [NSString stringWithFormat:@"%@_a", value]; - - if (error != nil) *error = RACSignalTestError; - - return nil; - }] subscribeNext:^(id x) { - [nextValues addObject:x]; - } error:^(NSError *error) { - receivedError = error; - } completed:^{ - completed = YES; - }]; - }); - - qck_it(@"should map values with the mapBlock", ^{ - [subject sendNext:@"foo"]; - [subject sendNext:@"bar"]; - [subject sendNext:@"baz"]; - [subject sendNext:@"buzz"]; - [subject sendCompleted]; - - NSArray *receivedValues = [nextValues copy]; - NSArray *expectedValues = @[ @"foo_a", @"bar_a", @"baz_a", @"buzz_a" ]; - - expect(receivedError).to(beNil()); - expect(receivedValues).to(equal(expectedValues)); - expect(@(completed)).to(beTruthy()); - }); - - qck_it(@"should map values with the mapBlock, until the mapBlock returns nil", ^{ - [subject sendNext:@"foo"]; - [subject sendNext:@"bar"]; - [subject sendNext:nil]; - [subject sendNext:@"buzz"]; - [subject sendCompleted]; - - NSArray *receivedValues = [nextValues copy]; - NSArray *expectedValues = @[ @"foo_a", @"bar_a" ]; - - expect(receivedError).to(equal(RACSignalTestError)); - expect(receivedValues).to(equal(expectedValues)); - expect(@(completed)).to(beFalsy()); - }); -}); - -qck_describe(@"throttling", ^{ - __block RACSubject *subject; - - qck_beforeEach(^{ - subject = [RACSubject subject]; - }); - - qck_describe(@"-throttle:", ^{ - __block RACSignal *throttledSignal; - - qck_beforeEach(^{ - throttledSignal = [subject throttle:0]; - }); - - qck_it(@"should throttle nexts", ^{ - NSMutableArray *valuesReceived = [NSMutableArray array]; - [throttledSignal subscribeNext:^(id x) { - [valuesReceived addObject:x]; - }]; - - [subject sendNext:@"foo"]; - [subject sendNext:@"bar"]; - expect(valuesReceived).to(equal(@[])); - - NSArray *expected = @[ @"bar" ]; - expect(valuesReceived).toEventually(equal(expected)); - - [subject sendNext:@"buzz"]; - expect(valuesReceived).to(equal(expected)); - - expected = @[ @"bar", @"buzz" ]; - expect(valuesReceived).toEventually(equal(expected)); - }); - - qck_it(@"should forward completed immediately", ^{ - __block BOOL completed = NO; - [throttledSignal subscribeCompleted:^{ - completed = YES; - }]; - - [subject sendCompleted]; - expect(@(completed)).to(beTruthy()); - }); - - qck_it(@"should forward errors immediately", ^{ - __block NSError *error = nil; - [throttledSignal subscribeError:^(NSError *e) { - error = e; - }]; - - [subject sendError:RACSignalTestError]; - expect(error).to(equal(RACSignalTestError)); - }); - - qck_it(@"should cancel future nexts when disposed", ^{ - __block id next = nil; - RACDisposable *disposable = [throttledSignal subscribeNext:^(id x) { - next = x; - }]; - - [subject sendNext:@"foo"]; - - __block BOOL done = NO; - [RACScheduler.mainThreadScheduler after:[NSDate date] schedule:^{ - done = YES; - }]; - - [disposable dispose]; - - expect(@(done)).toEventually(beTruthy()); - expect(next).to(beNil()); - }); - }); - - qck_describe(@"-throttle:valuesPassingTest:", ^{ - __block RACSignal *throttledSignal; - __block BOOL shouldThrottle; - - qck_beforeEach(^{ - shouldThrottle = YES; - - __block id value = nil; - throttledSignal = [[subject - doNext:^(id x) { - value = x; - }] - throttle:0 valuesPassingTest:^(id x) { - // Make sure that we're given the latest value. - expect(x).to(beIdenticalTo(value)); - - return shouldThrottle; - }]; - - expect(throttledSignal).notTo(beNil()); - }); - - qck_describe(@"nexts", ^{ - __block NSMutableArray *valuesReceived; - __block NSMutableArray *expected; - - qck_beforeEach(^{ - expected = [[NSMutableArray alloc] init]; - valuesReceived = [[NSMutableArray alloc] init]; - - [throttledSignal subscribeNext:^(id x) { - [valuesReceived addObject:x]; - }]; - }); - - qck_it(@"should forward unthrottled values immediately", ^{ - shouldThrottle = NO; - [subject sendNext:@"foo"]; - - [expected addObject:@"foo"]; - expect(valuesReceived).to(equal(expected)); - }); - - qck_it(@"should delay throttled values", ^{ - [subject sendNext:@"bar"]; - expect(valuesReceived).to(equal(expected)); - - [expected addObject:@"bar"]; - expect(valuesReceived).toEventually(equal(expected)); - }); - - qck_it(@"should drop buffered values when a throttled value arrives", ^{ - [subject sendNext:@"foo"]; - [subject sendNext:@"bar"]; - [subject sendNext:@"buzz"]; - expect(valuesReceived).to(equal(expected)); - - [expected addObject:@"buzz"]; - expect(valuesReceived).toEventually(equal(expected)); - }); - - qck_it(@"should drop buffered values when an immediate value arrives", ^{ - [subject sendNext:@"foo"]; - [subject sendNext:@"bar"]; - - shouldThrottle = NO; - [subject sendNext:@"buzz"]; - [expected addObject:@"buzz"]; - expect(valuesReceived).to(equal(expected)); - - // Make sure that nothing weird happens when sending another - // throttled value. - shouldThrottle = YES; - [subject sendNext:@"baz"]; - expect(valuesReceived).to(equal(expected)); - - [expected addObject:@"baz"]; - expect(valuesReceived).toEventually(equal(expected)); - }); - - qck_it(@"should not be resent upon completion", ^{ - [subject sendNext:@"bar"]; - [expected addObject:@"bar"]; - expect(valuesReceived).toEventually(equal(expected)); - - [subject sendCompleted]; - expect(valuesReceived).to(equal(expected)); - }); - }); - - qck_it(@"should forward completed immediately", ^{ - __block BOOL completed = NO; - [throttledSignal subscribeCompleted:^{ - completed = YES; - }]; - - [subject sendCompleted]; - expect(@(completed)).to(beTruthy()); - }); - - qck_it(@"should forward errors immediately", ^{ - __block NSError *error = nil; - [throttledSignal subscribeError:^(NSError *e) { - error = e; - }]; - - [subject sendError:RACSignalTestError]; - expect(error).to(equal(RACSignalTestError)); - }); - - qck_it(@"should cancel future nexts when disposed", ^{ - __block id next = nil; - RACDisposable *disposable = [throttledSignal subscribeNext:^(id x) { - next = x; - }]; - - [subject sendNext:@"foo"]; - - __block BOOL done = NO; - [RACScheduler.mainThreadScheduler after:[NSDate date] schedule:^{ - done = YES; - }]; - - [disposable dispose]; - - expect(@(done)).toEventually(beTruthy()); - expect(next).to(beNil()); - }); - }); -}); - -qck_describe(@"-then:", ^{ - qck_it(@"should continue onto returned signal", ^{ - RACSubject *subject = [RACSubject subject]; - - __block id value = nil; - [[subject then:^{ - return [RACSignal return:@2]; - }] subscribeNext:^(id x) { - value = x; - }]; - - [subject sendNext:@1]; - - // The value shouldn't change until the first signal completes. - expect(value).to(beNil()); - - [subject sendCompleted]; - - expect(value).to(equal(@2)); - }); - - qck_it(@"should sequence even if no next value is sent", ^{ - RACSubject *subject = [RACSubject subject]; - - __block id value = nil; - [[subject then:^{ - return [RACSignal return:RACUnit.defaultUnit]; - }] subscribeNext:^(id x) { - value = x; - }]; - - [subject sendCompleted]; - - expect(value).to(equal(RACUnit.defaultUnit)); - }); -}); - -qck_describe(@"-sequence", ^{ - RACSignal *signal = [RACSignal createSignal:^ RACDisposable * (id subscriber) { - [subscriber sendNext:@1]; - [subscriber sendNext:@2]; - [subscriber sendNext:@3]; - [subscriber sendNext:@4]; - [subscriber sendCompleted]; - return nil; - }]; - - qck_itBehavesLike(RACSequenceExamples, ^{ - return @{ - RACSequenceExampleSequence: signal.sequence, - RACSequenceExampleExpectedValues: @[ @1, @2, @3, @4 ] - }; - }); -}); - -qck_it(@"should complete take: even if the original signal doesn't", ^{ - RACSignal *sendOne = [RACSignal createSignal:^ RACDisposable * (id subscriber) { - [subscriber sendNext:RACUnit.defaultUnit]; - return nil; - }]; - - __block id value = nil; - __block BOOL completed = NO; - [[sendOne take:1] subscribeNext:^(id received) { - value = received; - } completed:^{ - completed = YES; - }]; - - expect(value).to(equal(RACUnit.defaultUnit)); - expect(@(completed)).to(beTruthy()); -}); - -qck_it(@"should complete take: even if the signal is recursive", ^{ - RACSubject *subject = [RACSubject subject]; - const NSUInteger number = 3; - const NSUInteger guard = number + 1; - - NSMutableArray *values = NSMutableArray.array; - __block BOOL completed = NO; - - [[subject take:number] subscribeNext:^(NSNumber* received) { - [values addObject:received]; - if (values.count >= guard) { - [subject sendError:RACSignalTestError]; - } - [subject sendNext:@(received.integerValue + 1)]; - } completed:^{ - completed = YES; - }]; - [subject sendNext:@0]; - - NSMutableArray* expectedValues = [NSMutableArray arrayWithCapacity:number]; - for (NSUInteger i = 0 ; i < number ; ++i) { - [expectedValues addObject:@(i)]; - } - - expect(values).to(equal(expectedValues)); - expect(@(completed)).to(beTruthy()); -}); - -qck_describe(@"+zip:", ^{ - __block RACSubject *subject1 = nil; - __block RACSubject *subject2 = nil; - __block BOOL hasSentError = NO; - __block BOOL hasSentCompleted = NO; - __block RACDisposable *disposable = nil; - __block void (^send2NextAndErrorTo1)(void) = nil; - __block void (^send3NextAndErrorTo1)(void) = nil; - __block void (^send2NextAndCompletedTo2)(void) = nil; - __block void (^send3NextAndCompletedTo2)(void) = nil; - - qck_beforeEach(^{ - send2NextAndErrorTo1 = [^{ - [subject1 sendNext:@1]; - [subject1 sendNext:@2]; - [subject1 sendError:RACSignalTestError]; - } copy]; - send3NextAndErrorTo1 = [^{ - [subject1 sendNext:@1]; - [subject1 sendNext:@2]; - [subject1 sendNext:@3]; - [subject1 sendError:RACSignalTestError]; - } copy]; - send2NextAndCompletedTo2 = [^{ - [subject2 sendNext:@1]; - [subject2 sendNext:@2]; - [subject2 sendCompleted]; - } copy]; - send3NextAndCompletedTo2 = [^{ - [subject2 sendNext:@1]; - [subject2 sendNext:@2]; - [subject2 sendNext:@3]; - [subject2 sendCompleted]; - } copy]; - subject1 = [RACSubject subject]; - subject2 = [RACSubject subject]; - hasSentError = NO; - hasSentCompleted = NO; - disposable = [[RACSignal zip:@[ subject1, subject2 ]] subscribeError:^(NSError *error) { - hasSentError = YES; - } completed:^{ - hasSentCompleted = YES; - }]; - }); - - qck_afterEach(^{ - [disposable dispose]; - }); - - qck_it(@"should complete as soon as no new zipped values are possible", ^{ - [subject1 sendNext:@1]; - [subject2 sendNext:@1]; - expect(@(hasSentCompleted)).to(beFalsy()); - - [subject1 sendNext:@2]; - [subject1 sendCompleted]; - expect(@(hasSentCompleted)).to(beFalsy()); - - [subject2 sendNext:@2]; - expect(@(hasSentCompleted)).to(beTruthy()); - }); - - qck_it(@"outcome should not be dependent on order of signals", ^{ - [subject2 sendCompleted]; - expect(@(hasSentCompleted)).to(beTruthy()); - }); - - qck_it(@"should forward errors sent earlier than (time-wise) and before (position-wise) a complete", ^{ - send2NextAndErrorTo1(); - send3NextAndCompletedTo2(); - expect(@(hasSentError)).to(beTruthy()); - expect(@(hasSentCompleted)).to(beFalsy()); - }); - - qck_it(@"should forward errors sent earlier than (time-wise) and after (position-wise) a complete", ^{ - send3NextAndErrorTo1(); - send2NextAndCompletedTo2(); - expect(@(hasSentError)).to(beTruthy()); - expect(@(hasSentCompleted)).to(beFalsy()); - }); - - qck_it(@"should forward errors sent later than (time-wise) and before (position-wise) a complete", ^{ - send3NextAndCompletedTo2(); - send2NextAndErrorTo1(); - expect(@(hasSentError)).to(beTruthy()); - expect(@(hasSentCompleted)).to(beFalsy()); - }); - - qck_it(@"should ignore errors sent later than (time-wise) and after (position-wise) a complete", ^{ - send2NextAndCompletedTo2(); - send3NextAndErrorTo1(); - expect(@(hasSentError)).to(beFalsy()); - expect(@(hasSentCompleted)).to(beTruthy()); - }); - - qck_it(@"should handle signals sending values unevenly", ^{ - __block NSError *receivedError = nil; - __block BOOL hasCompleted = NO; - - RACSubject *a = [RACSubject subject]; - RACSubject *b = [RACSubject subject]; - RACSubject *c = [RACSubject subject]; - - NSMutableArray *receivedValues = NSMutableArray.array; - NSArray *expectedValues = nil; - - [[RACSignal zip:@[ a, b, c ] reduce:^(NSNumber *a, NSNumber *b, NSNumber *c) { - return [NSString stringWithFormat:@"%@%@%@", a, b, c]; - }] subscribeNext:^(id x) { - [receivedValues addObject:x]; - } error:^(NSError *error) { - receivedError = error; - } completed:^{ - hasCompleted = YES; - }]; - - [a sendNext:@1]; - [a sendNext:@2]; - [a sendNext:@3]; - - [b sendNext:@1]; - - [c sendNext:@1]; - [c sendNext:@2]; - - // a: [===......] - // b: [=........] - // c: [==.......] - - expectedValues = @[ @"111" ]; - expect(receivedValues).to(equal(expectedValues)); - expect(receivedError).to(beNil()); - expect(@(hasCompleted)).to(beFalsy()); - - [b sendNext:@2]; - [b sendNext:@3]; - [b sendNext:@4]; - [b sendCompleted]; - - // a: [===......] - // b: [====C....] - // c: [==.......] - - expectedValues = @[ @"111", @"222" ]; - expect(receivedValues).to(equal(expectedValues)); - expect(receivedError).to(beNil()); - expect(@(hasCompleted)).to(beFalsy()); - - [c sendNext:@3]; - [c sendNext:@4]; - [c sendNext:@5]; - [c sendError:RACSignalTestError]; - - // a: [===......] - // b: [====C....] - // c: [=====E...] - - expectedValues = @[ @"111", @"222", @"333" ]; - expect(receivedValues).to(equal(expectedValues)); - expect(receivedError).to(equal(RACSignalTestError)); - expect(@(hasCompleted)).to(beFalsy()); - - [a sendNext:@4]; - [a sendNext:@5]; - [a sendNext:@6]; - [a sendNext:@7]; - - // a: [=======..] - // b: [====C....] - // c: [=====E...] - - expectedValues = @[ @"111", @"222", @"333" ]; - expect(receivedValues).to(equal(expectedValues)); - expect(receivedError).to(equal(RACSignalTestError)); - expect(@(hasCompleted)).to(beFalsy()); - }); - - qck_it(@"should handle multiples of the same side-effecting signal", ^{ - __block NSUInteger counter = 0; - RACSignal *sideEffectingSignal = [RACSignal createSignal:^ RACDisposable * (id subscriber) { - ++counter; - [subscriber sendNext:@1]; - [subscriber sendCompleted]; - return nil; - }]; - RACSignal *combined = [RACSignal zip:@[ sideEffectingSignal, sideEffectingSignal ] reduce:^ NSString * (id x, id y) { - return [NSString stringWithFormat:@"%@%@", x, y]; - }]; - NSMutableArray *receivedValues = NSMutableArray.array; - - expect(@(counter)).to(equal(@0)); - - [combined subscribeNext:^(id x) { - [receivedValues addObject:x]; - }]; - - expect(@(counter)).to(equal(@2)); - expect(receivedValues).to(equal(@[ @"11" ])); - }); -}); - -qck_describe(@"-sample:", ^{ - qck_it(@"should send the latest value when the sampler signal fires", ^{ - RACSubject *subject = [RACSubject subject]; - RACSubject *sampleSubject = [RACSubject subject]; - RACSignal *sampled = [subject sample:sampleSubject]; - NSMutableArray *values = [NSMutableArray array]; - [sampled subscribeNext:^(id x) { - [values addObject:x]; - }]; - - [sampleSubject sendNext:RACUnit.defaultUnit]; - expect(values).to(equal(@[])); - - [subject sendNext:@1]; - [subject sendNext:@2]; - expect(values).to(equal(@[])); - - [sampleSubject sendNext:RACUnit.defaultUnit]; - NSArray *expected = @[ @2 ]; - expect(values).to(equal(expected)); - - [subject sendNext:@3]; - expect(values).to(equal(expected)); - - [sampleSubject sendNext:RACUnit.defaultUnit]; - expected = @[ @2, @3 ]; - expect(values).to(equal(expected)); - - [sampleSubject sendNext:RACUnit.defaultUnit]; - expected = @[ @2, @3, @3 ]; - expect(values).to(equal(expected)); - }); -}); - -qck_describe(@"-collect", ^{ - __block RACSubject *subject; - __block RACSignal *collected; - - __block id value; - __block BOOL hasCompleted; - - qck_beforeEach(^{ - subject = [RACSubject subject]; - collected = [subject collect]; - - value = nil; - hasCompleted = NO; - - [collected subscribeNext:^(id x) { - value = x; - } completed:^{ - hasCompleted = YES; - }]; - }); - - qck_it(@"should send a single array when the original signal completes", ^{ - NSArray *expected = @[ @1, @2, @3 ]; - - [subject sendNext:@1]; - [subject sendNext:@2]; - [subject sendNext:@3]; - expect(value).to(beNil()); - - [subject sendCompleted]; - expect(value).to(equal(expected)); - expect(@(hasCompleted)).to(beTruthy()); - }); - - qck_it(@"should add NSNull to an array for nil values", ^{ - NSArray *expected = @[ NSNull.null, @1, NSNull.null ]; - - [subject sendNext:nil]; - [subject sendNext:@1]; - [subject sendNext:nil]; - expect(value).to(beNil()); - - [subject sendCompleted]; - expect(value).to(equal(expected)); - expect(@(hasCompleted)).to(beTruthy()); - }); -}); - -qck_describe(@"-bufferWithTime:onScheduler:", ^{ - __block RACTestScheduler *scheduler; - - __block RACSubject *input; - __block RACSignal *bufferedInput; - __block RACTuple *latestValue; - - qck_beforeEach(^{ - scheduler = [[RACTestScheduler alloc] init]; - - input = [RACSubject subject]; - bufferedInput = [input bufferWithTime:1 onScheduler:scheduler]; - latestValue = nil; - - [bufferedInput subscribeNext:^(RACTuple *x) { - latestValue = x; - }]; - }); - - qck_it(@"should buffer nexts", ^{ - [input sendNext:@1]; - [input sendNext:@2]; - - [scheduler stepAll]; - expect(latestValue).to(equal(RACTuplePack(@1, @2))); - - [input sendNext:@3]; - [input sendNext:@4]; - - [scheduler stepAll]; - expect(latestValue).to(equal(RACTuplePack(@3, @4))); - }); - - qck_it(@"should not perform buffering until a value is sent", ^{ - [input sendNext:@1]; - [input sendNext:@2]; - [scheduler stepAll]; - expect(latestValue).to(equal(RACTuplePack(@1, @2))); - - [scheduler stepAll]; - expect(latestValue).to(equal(RACTuplePack(@1, @2))); - - [input sendNext:@3]; - [input sendNext:@4]; - [scheduler stepAll]; - expect(latestValue).to(equal(RACTuplePack(@3, @4))); - }); - - qck_it(@"should flush any buffered nexts upon completion", ^{ - [input sendNext:@1]; - [input sendCompleted]; - [scheduler stepAll]; - expect(latestValue).to(equal(RACTuplePack(@1))); - }); - - qck_it(@"should support NSNull values", ^{ - [input sendNext:NSNull.null]; - [scheduler stepAll]; - expect(latestValue).to(equal(RACTuplePack(NSNull.null))); - }); - - qck_it(@"should buffer nil values", ^{ - [input sendNext:nil]; - [scheduler stepAll]; - expect(latestValue).to(equal(RACTuplePack(nil))); - }); -}); - -qck_describe(@"-concat", ^{ - __block RACSubject *subject; - - __block RACSignal *oneSignal; - __block RACSignal *twoSignal; - __block RACSignal *threeSignal; - - __block RACSignal *errorSignal; - __block RACSignal *completedSignal; - - qck_beforeEach(^{ - subject = [RACReplaySubject subject]; - - oneSignal = [RACSignal return:@1]; - twoSignal = [RACSignal return:@2]; - threeSignal = [RACSignal return:@3]; - - errorSignal = [RACSignal error:RACSignalTestError]; - completedSignal = RACSignal.empty; - }); - - qck_it(@"should concatenate the values of inner signals", ^{ - [subject sendNext:oneSignal]; - [subject sendNext:twoSignal]; - [subject sendNext:completedSignal]; - [subject sendNext:threeSignal]; - - NSMutableArray *values = [NSMutableArray array]; - [[subject concat] subscribeNext:^(id x) { - [values addObject:x]; - }]; - - NSArray *expected = @[ @1, @2, @3 ]; - expect(values).to(equal(expected)); - }); - - qck_it(@"should complete only after all signals complete", ^{ - RACReplaySubject *valuesSubject = [RACReplaySubject subject]; - - [subject sendNext:valuesSubject]; - [subject sendCompleted]; - - [valuesSubject sendNext:@1]; - [valuesSubject sendNext:@2]; - [valuesSubject sendCompleted]; - - NSArray *expected = @[ @1, @2 ]; - expect([[subject concat] toArray]).to(equal(expected)); - }); - - qck_it(@"should pass through errors", ^{ - [subject sendNext:errorSignal]; - - NSError *error = nil; - [[subject concat] firstOrDefault:nil success:NULL error:&error]; - expect(error).to(equal(RACSignalTestError)); - }); - - qck_it(@"should concat signals sent later", ^{ - [subject sendNext:oneSignal]; - - NSMutableArray *values = [NSMutableArray array]; - [[subject concat] subscribeNext:^(id x) { - [values addObject:x]; - }]; - - NSArray *expected = @[ @1 ]; - expect(values).to(equal(expected)); - - [subject sendNext:[twoSignal delay:0]]; - - expected = @[ @1, @2 ]; - expect(values).toEventually(equal(expected)); - - [subject sendNext:threeSignal]; - - expected = @[ @1, @2, @3 ]; - expect(values).to(equal(expected)); - }); - - qck_it(@"should dispose the current signal", ^{ - __block BOOL disposed = NO; - __block id innerSubscriber = nil; - RACSignal *innerSignal = [RACSignal createSignal:^(id subscriber) { - // Keep the subscriber alive so it doesn't trigger disposal on dealloc - innerSubscriber = subscriber; - return [RACDisposable disposableWithBlock:^{ - disposed = YES; - }]; - }]; - - RACDisposable *concatDisposable = [[subject concat] subscribeCompleted:^{}]; - - [subject sendNext:innerSignal]; - expect(@(disposed)).notTo(beTruthy()); - - [concatDisposable dispose]; - expect(@(disposed)).to(beTruthy()); - }); - - qck_it(@"should dispose later signals", ^{ - __block BOOL disposed = NO; - __block id laterSubscriber = nil; - RACSignal *laterSignal = [RACSignal createSignal:^(id subscriber) { - // Keep the subscriber alive so it doesn't trigger disposal on dealloc - laterSubscriber = subscriber; - return [RACDisposable disposableWithBlock:^{ - disposed = YES; - }]; - }]; - - RACSubject *firstSignal = [RACSubject subject]; - RACSignal *outerSignal = [RACSignal createSignal:^ id (id subscriber) { - [subscriber sendNext:firstSignal]; - [subscriber sendNext:laterSignal]; - return nil; - }]; - - RACDisposable *concatDisposable = [[outerSignal concat] subscribeCompleted:^{}]; - - [firstSignal sendCompleted]; - expect(@(disposed)).notTo(beTruthy()); - - [concatDisposable dispose]; - expect(@(disposed)).to(beTruthy()); - }); -}); - -qck_describe(@"-initially:", ^{ - __block RACSubject *subject; - - __block NSUInteger initiallyInvokedCount; - __block RACSignal *signal; - - qck_beforeEach(^{ - subject = [RACSubject subject]; - - initiallyInvokedCount = 0; - signal = [subject initially:^{ - ++initiallyInvokedCount; - }]; - }); - - qck_it(@"should not run without a subscription", ^{ - [subject sendCompleted]; - expect(@(initiallyInvokedCount)).to(equal(@0)); - }); - - qck_it(@"should run on subscription", ^{ - [signal subscribe:[RACSubscriber new]]; - expect(@(initiallyInvokedCount)).to(equal(@1)); - }); - - qck_it(@"should re-run for each subscription", ^{ - [signal subscribe:[RACSubscriber new]]; - [signal subscribe:[RACSubscriber new]]; - expect(@(initiallyInvokedCount)).to(equal(@2)); - }); -}); - -qck_describe(@"-finally:", ^{ - __block RACSubject *subject; - - __block BOOL finallyInvoked; - __block RACSignal *signal; - - qck_beforeEach(^{ - subject = [RACSubject subject]; - - finallyInvoked = NO; - signal = [subject finally:^{ - finallyInvoked = YES; - }]; - }); - - qck_it(@"should not run finally without a subscription", ^{ - [subject sendCompleted]; - expect(@(finallyInvoked)).to(beFalsy()); - }); - - qck_describe(@"with a subscription", ^{ - __block RACDisposable *disposable; - - qck_beforeEach(^{ - disposable = [signal subscribeCompleted:^{}]; - }); - - qck_afterEach(^{ - [disposable dispose]; - }); - - qck_it(@"should not run finally upon next", ^{ - [subject sendNext:RACUnit.defaultUnit]; - expect(@(finallyInvoked)).to(beFalsy()); - }); - - qck_it(@"should run finally upon completed", ^{ - [subject sendCompleted]; - expect(@(finallyInvoked)).to(beTruthy()); - }); - - qck_it(@"should run finally upon error", ^{ - [subject sendError:nil]; - expect(@(finallyInvoked)).to(beTruthy()); - }); - }); -}); - -qck_describe(@"-ignoreValues", ^{ - __block RACSubject *subject; - - __block BOOL gotNext; - __block BOOL gotCompleted; - __block NSError *receivedError; - - qck_beforeEach(^{ - subject = [RACSubject subject]; - - gotNext = NO; - gotCompleted = NO; - receivedError = nil; - - [[subject ignoreValues] subscribeNext:^(id _) { - gotNext = YES; - } error:^(NSError *error) { - receivedError = error; - } completed:^{ - gotCompleted = YES; - }]; - }); - - qck_it(@"should skip nexts and pass through completed", ^{ - [subject sendNext:RACUnit.defaultUnit]; - [subject sendCompleted]; - - expect(@(gotNext)).to(beFalsy()); - expect(@(gotCompleted)).to(beTruthy()); - expect(receivedError).to(beNil()); - }); - - qck_it(@"should skip nexts and pass through errors", ^{ - [subject sendNext:RACUnit.defaultUnit]; - [subject sendError:RACSignalTestError]; - - expect(@(gotNext)).to(beFalsy()); - expect(@(gotCompleted)).to(beFalsy()); - expect(receivedError).to(equal(RACSignalTestError)); - }); -}); - -qck_describe(@"-materialize", ^{ - qck_it(@"should convert nexts and completed into RACEvents", ^{ - NSArray *events = [[[RACSignal return:RACUnit.defaultUnit] materialize] toArray]; - NSArray *expected = @[ - [RACEvent eventWithValue:RACUnit.defaultUnit], - RACEvent.completedEvent - ]; - - expect(events).to(equal(expected)); - }); - - qck_it(@"should convert errors into RACEvents and complete", ^{ - NSArray *events = [[[RACSignal error:RACSignalTestError] materialize] toArray]; - NSArray *expected = @[ [RACEvent eventWithError:RACSignalTestError] ]; - expect(events).to(equal(expected)); - }); -}); - -qck_describe(@"-dematerialize", ^{ - qck_it(@"should convert nexts from RACEvents", ^{ - RACSignal *events = [RACSignal createSignal:^ id (id subscriber) { - [subscriber sendNext:[RACEvent eventWithValue:@1]]; - [subscriber sendNext:[RACEvent eventWithValue:@2]]; - [subscriber sendCompleted]; - return nil; - }]; - - NSArray *expected = @[ @1, @2 ]; - expect([[events dematerialize] toArray]).to(equal(expected)); - }); - - qck_it(@"should convert completed from a RACEvent", ^{ - RACSignal *events = [RACSignal createSignal:^ id (id subscriber) { - [subscriber sendNext:[RACEvent eventWithValue:@1]]; - [subscriber sendNext:RACEvent.completedEvent]; - [subscriber sendNext:[RACEvent eventWithValue:@2]]; - [subscriber sendCompleted]; - return nil; - }]; - - NSArray *expected = @[ @1 ]; - expect([[events dematerialize] toArray]).to(equal(expected)); - }); - - qck_it(@"should convert error from a RACEvent", ^{ - RACSignal *events = [RACSignal createSignal:^ id (id subscriber) { - [subscriber sendNext:[RACEvent eventWithError:RACSignalTestError]]; - [subscriber sendNext:[RACEvent eventWithValue:@1]]; - [subscriber sendCompleted]; - return nil; - }]; - - __block NSError *error = nil; - expect([[events dematerialize] firstOrDefault:nil success:NULL error:&error]).to(beNil()); - expect(error).to(equal(RACSignalTestError)); - }); -}); - -qck_describe(@"-not", ^{ - qck_it(@"should invert every BOOL sent", ^{ - RACSubject *subject = [RACReplaySubject subject]; - [subject sendNext:@NO]; - [subject sendNext:@YES]; - [subject sendCompleted]; - NSArray *results = [[subject not] toArray]; - NSArray *expected = @[ @YES, @NO ]; - expect(results).to(equal(expected)); - }); -}); - -qck_describe(@"-and", ^{ - qck_it(@"should return YES if all YES values are sent", ^{ - RACSubject *subject = [RACReplaySubject subject]; - - [subject sendNext:RACTuplePack(@YES, @NO, @YES)]; - [subject sendNext:RACTuplePack(@NO, @NO, @NO)]; - [subject sendNext:RACTuplePack(@YES, @YES, @YES)]; - [subject sendCompleted]; - - NSArray *results = [[subject and] toArray]; - NSArray *expected = @[ @NO, @NO, @YES ]; - - expect(results).to(equal(expected)); - }); -}); - -qck_describe(@"-or", ^{ - qck_it(@"should return YES for any YES values sent", ^{ - RACSubject *subject = [RACReplaySubject subject]; - - [subject sendNext:RACTuplePack(@YES, @NO, @YES)]; - [subject sendNext:RACTuplePack(@NO, @NO, @NO)]; - [subject sendCompleted]; - - NSArray *results = [[subject or] toArray]; - NSArray *expected = @[ @YES, @NO ]; - - expect(results).to(equal(expected)); - }); -}); - -qck_describe(@"-groupBy:", ^{ - qck_it(@"should send completed to all grouped signals.", ^{ - RACSubject *subject = [RACReplaySubject subject]; - - __block NSUInteger groupedSignalCount = 0; - __block NSUInteger completedGroupedSignalCount = 0; - [[subject groupBy:^(NSNumber *number) { - return @(floorf(number.floatValue)); - }] subscribeNext:^(RACGroupedSignal *groupedSignal) { - ++groupedSignalCount; - - [groupedSignal subscribeCompleted:^{ - ++completedGroupedSignalCount; - }]; - }]; - - [subject sendNext:@1]; - [subject sendNext:@2]; - [subject sendCompleted]; - - expect(@(completedGroupedSignalCount)).to(equal(@(groupedSignalCount))); - }); - - qck_it(@"should send error to all grouped signals.", ^{ - RACSubject *subject = [RACReplaySubject subject]; - - __block NSUInteger groupedSignalCount = 0; - __block NSUInteger erroneousGroupedSignalCount = 0; - [[subject groupBy:^(NSNumber *number) { - return @(floorf(number.floatValue)); - }] subscribeNext:^(RACGroupedSignal *groupedSignal) { - ++groupedSignalCount; - - [groupedSignal subscribeError:^(NSError *error) { - ++erroneousGroupedSignalCount; - - expect(error.domain).to(equal(@"TestDomain")); - expect(@(error.code)).to(equal(@123)); - }]; - }]; - - [subject sendNext:@1]; - [subject sendNext:@2]; - [subject sendError:[NSError errorWithDomain:@"TestDomain" code:123 userInfo:nil]]; - - expect(@(erroneousGroupedSignalCount)).to(equal(@(groupedSignalCount))); - }); - - - qck_it(@"should send completed in the order grouped signals were created.", ^{ - RACSubject *subject = [RACReplaySubject subject]; - - NSMutableArray *startedSignals = [NSMutableArray array]; - NSMutableArray *completedSignals = [NSMutableArray array]; - [[subject groupBy:^(NSNumber *number) { - return @(number.integerValue % 4); - }] subscribeNext:^(RACGroupedSignal *groupedSignal) { - [startedSignals addObject:groupedSignal]; - - [groupedSignal subscribeCompleted:^{ - [completedSignals addObject:groupedSignal]; - }]; - }]; - - for (NSInteger i = 0; i < 20; i++) - { - [subject sendNext:@(i)]; - } - [subject sendCompleted]; - - expect(completedSignals).to(equal(startedSignals)); - }); -}); - -qck_describe(@"starting signals", ^{ - qck_describe(@"+startLazilyWithScheduler:block:", ^{ - __block NSUInteger invokedCount = 0; - __block void (^subscribe)(void); - - qck_beforeEach(^{ - invokedCount = 0; - - RACSignal *signal = [RACSignal startLazilyWithScheduler:RACScheduler.immediateScheduler block:^(id subscriber) { - invokedCount++; - [subscriber sendNext:@42]; - [subscriber sendCompleted]; - }]; - - subscribe = [^{ - [signal subscribe:[RACSubscriber subscriberWithNext:nil error:nil completed:nil]]; - } copy]; - }); - - qck_it(@"should only invoke the block on subscription", ^{ - expect(@(invokedCount)).to(equal(@0)); - subscribe(); - expect(@(invokedCount)).to(equal(@1)); - }); - - qck_it(@"should only invoke the block once", ^{ - expect(@(invokedCount)).to(equal(@0)); - subscribe(); - expect(@(invokedCount)).to(equal(@1)); - subscribe(); - expect(@(invokedCount)).to(equal(@1)); - subscribe(); - expect(@(invokedCount)).to(equal(@1)); - }); - - qck_it(@"should invoke the block on the given scheduler", ^{ - RACScheduler *scheduler = [RACScheduler scheduler]; - __block RACScheduler *currentScheduler; - [[[RACSignal - startLazilyWithScheduler:scheduler block:^(id subscriber) { - currentScheduler = RACScheduler.currentScheduler; - }] - publish] - connect]; - - expect(currentScheduler).toEventually(equal(scheduler)); - }); - }); - - qck_describe(@"+startEagerlyWithScheduler:block:", ^{ - qck_it(@"should immediately invoke the block", ^{ - __block BOOL blockInvoked = NO; - [RACSignal startEagerlyWithScheduler:[RACScheduler scheduler] block:^(id subscriber) { - blockInvoked = YES; - }]; - - expect(@(blockInvoked)).toEventually(beTruthy()); - }); - - qck_it(@"should only invoke the block once", ^{ - __block NSUInteger invokedCount = 0; - RACSignal *signal = [RACSignal startEagerlyWithScheduler:RACScheduler.immediateScheduler block:^(id subscriber) { - invokedCount++; - }]; - - expect(@(invokedCount)).to(equal(@1)); - - [[signal publish] connect]; - expect(@(invokedCount)).to(equal(@1)); - - [[signal publish] connect]; - expect(@(invokedCount)).to(equal(@1)); - }); - - qck_it(@"should invoke the block on the given scheduler", ^{ - RACScheduler *scheduler = [RACScheduler scheduler]; - __block RACScheduler *currentScheduler; - [RACSignal startEagerlyWithScheduler:scheduler block:^(id subscriber) { - currentScheduler = RACScheduler.currentScheduler; - }]; - - expect(currentScheduler).toEventually(equal(scheduler)); - }); - }); -}); - -qck_describe(@"-toArray", ^{ - __block RACSubject *subject; - - qck_beforeEach(^{ - subject = [RACReplaySubject subject]; - }); - - qck_it(@"should return an array which contains NSNulls for nil values", ^{ - NSArray *expected = @[ NSNull.null, @1, NSNull.null ]; - - [subject sendNext:nil]; - [subject sendNext:@1]; - [subject sendNext:nil]; - [subject sendCompleted]; - - expect([subject toArray]).to(equal(expected)); - }); - - qck_it(@"should return nil upon error", ^{ - [subject sendError:nil]; - expect([subject toArray]).to(beNil()); - }); - - qck_it(@"should return nil upon error even if some nexts were sent", ^{ - [subject sendNext:@1]; - [subject sendNext:@2]; - [subject sendError:nil]; - - expect([subject toArray]).to(beNil()); - }); -}); - -qck_describe(@"-ignore:", ^{ - qck_it(@"should ignore nil", ^{ - RACSignal *signal = [[RACSignal - createSignal:^ id (id subscriber) { - [subscriber sendNext:@1]; - [subscriber sendNext:nil]; - [subscriber sendNext:@3]; - [subscriber sendNext:@4]; - [subscriber sendNext:nil]; - [subscriber sendCompleted]; - return nil; - }] - ignore:nil]; - - NSArray *expected = @[ @1, @3, @4 ]; - expect([signal toArray]).to(equal(expected)); - }); -}); - -qck_describe(@"-replayLazily", ^{ - __block NSUInteger subscriptionCount; - __block BOOL disposed; - - __block RACSignal *signal; - __block RACSubject *disposeSubject; - __block RACSignal *replayedSignal; - - qck_beforeEach(^{ - subscriptionCount = 0; - disposed = NO; - - signal = [RACSignal createSignal:^ RACDisposable * (id subscriber) { - subscriptionCount++; - [subscriber sendNext:RACUnit.defaultUnit]; - - RACDisposable *schedulingDisposable = [RACScheduler.mainThreadScheduler schedule:^{ - [subscriber sendNext:RACUnit.defaultUnit]; - [subscriber sendCompleted]; - }]; - - return [RACDisposable disposableWithBlock:^{ - [schedulingDisposable dispose]; - disposed = YES; - }]; - }]; - - disposeSubject = [RACSubject subject]; - replayedSignal = [[signal takeUntil:disposeSubject] replayLazily]; - }); - - qck_it(@"should forward the input signal upon subscription", ^{ - expect(@(subscriptionCount)).to(equal(@0)); - - expect(@([replayedSignal asynchronouslyWaitUntilCompleted:NULL])).to(beTruthy()); - expect(@(subscriptionCount)).to(equal(@1)); - }); - - qck_it(@"should replay the input signal for future subscriptions", ^{ - NSArray *events = [[[replayedSignal materialize] collect] asynchronousFirstOrDefault:nil success:NULL error:NULL]; - expect(events).notTo(beNil()); - - expect([[[replayedSignal materialize] collect] asynchronousFirstOrDefault:nil success:NULL error:NULL]).to(equal(events)); - expect(@(subscriptionCount)).to(equal(@1)); - }); - - qck_it(@"should replay even after disposal", ^{ - __block NSUInteger valueCount = 0; - [replayedSignal subscribeNext:^(id x) { - valueCount++; - }]; - - [disposeSubject sendCompleted]; - expect(@(valueCount)).to(equal(@1)); - expect(@([[replayedSignal toArray] count])).to(equal(@(valueCount))); - }); -}); - -qck_describe(@"-reduceApply", ^{ - qck_it(@"should apply a block to the rest of a tuple", ^{ - RACSubject *subject = [RACReplaySubject subject]; - - id sum = ^(NSNumber *a, NSNumber *b) { - return @(a.intValue + b.intValue); - }; - id madd = ^(NSNumber *a, NSNumber *b, NSNumber *c) { - return @(a.intValue * b.intValue + c.intValue); - }; - - [subject sendNext:RACTuplePack(sum, @1, @2)]; - [subject sendNext:RACTuplePack(madd, @2, @3, @1)]; - [subject sendCompleted]; - - NSArray *results = [[subject reduceApply] toArray]; - NSArray *expected = @[ @3, @7 ]; - - expect(results).to(equal(expected)); - }); -}); - -describe(@"-deliverOnMainThread", ^{ - void (^dispatchSyncInBackground)(dispatch_block_t) = ^(dispatch_block_t block) { - dispatch_group_t group = dispatch_group_create(); - dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), block); - dispatch_group_wait(group, DISPATCH_TIME_FOREVER); - }; - - beforeEach(^{ - expect(@(NSThread.isMainThread)).to(beTruthy()); - }); - - it(@"should deliver events immediately when on the main thread", ^{ - RACSubject *subject = [RACSubject subject]; - NSMutableArray *values = [NSMutableArray array]; - - [[subject deliverOnMainThread] subscribeNext:^(id value) { - [values addObject:value]; - }]; - - [subject sendNext:@0]; - expect(values).to(equal(@[ @0 ])); - - [subject sendNext:@1]; - [subject sendNext:@2]; - expect(values).to(equal(@[ @0, @1, @2 ])); - }); - - it(@"should enqueue events sent from the background", ^{ - RACSubject *subject = [RACSubject subject]; - NSMutableArray *values = [NSMutableArray array]; - - [[subject deliverOnMainThread] subscribeNext:^(id value) { - [values addObject:value]; - }]; - - dispatchSyncInBackground(^{ - [subject sendNext:@0]; - }); - - expect(values).to(equal(@[])); - expect(values).toEventually(equal(@[ @0 ])); - - dispatchSyncInBackground(^{ - [subject sendNext:@1]; - [subject sendNext:@2]; - }); - - expect(values).to(equal(@[ @0 ])); - expect(values).toEventually(equal(@[ @0, @1, @2 ])); - }); - - it(@"should enqueue events sent from the main thread after events from the background", ^{ - RACSubject *subject = [RACSubject subject]; - NSMutableArray *values = [NSMutableArray array]; - - [[subject deliverOnMainThread] subscribeNext:^(id value) { - [values addObject:value]; - }]; - - dispatchSyncInBackground(^{ - [subject sendNext:@0]; - }); - - [subject sendNext:@1]; - [subject sendNext:@2]; - - expect(values).to(equal(@[])); - expect(values).toEventually(equal(@[ @0, @1, @2 ])); - }); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/RACStreamExamples.h b/ReactiveObjCTests/RACStreamExamples.h deleted file mode 100644 index ddd1446c52..0000000000 --- a/ReactiveObjCTests/RACStreamExamples.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// RACStreamExamples.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-11-01. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -// The name of the shared examples for a RACStream subclass. -extern NSString * const RACStreamExamples; - -// The RACStream subclass to test. -extern NSString * const RACStreamExamplesClass; - -// An infinite RACStream to test, making sure that certain operations -// terminate. -// -// The stream should contain infinite RACUnit values. -extern NSString * const RACStreamExamplesInfiniteStream; - -// A block with the signature: -// -// void (^)(RACStream *stream, NSArray *expectedValues) -// -// … used to verify that a stream contains the expected values. -extern NSString * const RACStreamExamplesVerifyValuesBlock; diff --git a/ReactiveObjCTests/RACStreamExamples.m b/ReactiveObjCTests/RACStreamExamples.m deleted file mode 100644 index 2a682d9462..0000000000 --- a/ReactiveObjCTests/RACStreamExamples.m +++ /dev/null @@ -1,670 +0,0 @@ -// -// RACStreamExamples.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-11-01. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACStreamExamples.h" - -#import "RACStream.h" -#import "RACUnit.h" -#import "RACTuple.h" - -NSString * const RACStreamExamples = @"RACStreamExamples"; -NSString * const RACStreamExamplesClass = @"RACStreamExamplesClass"; -NSString * const RACStreamExamplesInfiniteStream = @"RACStreamExamplesInfiniteStream"; -NSString * const RACStreamExamplesVerifyValuesBlock = @"RACStreamExamplesVerifyValuesBlock"; - -QuickConfigurationBegin(RACStreamExampleGroups) - -+ (void)configure:(Configuration *)configuration { - sharedExamples(RACStreamExamples, ^(QCKDSLSharedExampleContext exampleContext) { - __block Class streamClass; - __block void (^verifyValues)(RACStream *, NSArray *); - __block RACStream *infiniteStream; - - __block RACStream *(^streamWithValues)(NSArray *); - - qck_beforeEach(^{ - streamClass = exampleContext()[RACStreamExamplesClass]; - verifyValues = exampleContext()[RACStreamExamplesVerifyValuesBlock]; - infiniteStream = exampleContext()[RACStreamExamplesInfiniteStream]; - streamWithValues = [^(NSArray *values) { - RACStream *stream = [streamClass empty]; - - for (id value in values) { - stream = [stream concat:[streamClass return:value]]; - } - - return stream; - } copy]; - }); - - qck_it(@"should return an empty stream", ^{ - RACStream *stream = [streamClass empty]; - verifyValues(stream, @[]); - }); - - qck_it(@"should lift a value into a stream", ^{ - RACStream *stream = [streamClass return:RACUnit.defaultUnit]; - verifyValues(stream, @[ RACUnit.defaultUnit ]); - }); - - qck_describe(@"-concat:", ^{ - qck_it(@"should concatenate two streams", ^{ - RACStream *stream = [[streamClass return:@0] concat:[streamClass return:@1]]; - verifyValues(stream, @[ @0, @1 ]); - }); - - qck_it(@"should concatenate three streams", ^{ - RACStream *stream = [[[streamClass return:@0] concat:[streamClass return:@1]] concat:[streamClass return:@2]]; - verifyValues(stream, @[ @0, @1, @2 ]); - }); - - qck_it(@"should concatenate around an empty stream", ^{ - RACStream *stream = [[[streamClass return:@0] concat:[streamClass empty]] concat:[streamClass return:@2]]; - verifyValues(stream, @[ @0, @2 ]); - }); - }); - - qck_it(@"should flatten", ^{ - RACStream *stream = [[streamClass return:[streamClass return:RACUnit.defaultUnit]] flatten]; - verifyValues(stream, @[ RACUnit.defaultUnit ]); - }); - - qck_describe(@"-bind:", ^{ - qck_it(@"should return the result of binding a single value", ^{ - RACStream *stream = [[streamClass return:@0] bind:^{ - return ^(NSNumber *value, BOOL *stop) { - NSNumber *newValue = @(value.integerValue + 1); - return [streamClass return:newValue]; - }; - }]; - - verifyValues(stream, @[ @1 ]); - }); - - qck_it(@"should concatenate the result of binding multiple values", ^{ - RACStream *baseStream = streamWithValues(@[ @0, @1 ]); - RACStream *stream = [baseStream bind:^{ - return ^(NSNumber *value, BOOL *stop) { - NSNumber *newValue = @(value.integerValue + 1); - return [streamClass return:newValue]; - }; - }]; - - verifyValues(stream, @[ @1, @2 ]); - }); - - qck_it(@"should concatenate with an empty result from binding a value", ^{ - RACStream *baseStream = streamWithValues(@[ @0, @1, @2 ]); - RACStream *stream = [baseStream bind:^{ - return ^(NSNumber *value, BOOL *stop) { - if (value.integerValue == 1) return [streamClass empty]; - - NSNumber *newValue = @(value.integerValue + 1); - return [streamClass return:newValue]; - }; - }]; - - verifyValues(stream, @[ @1, @3 ]); - }); - - qck_it(@"should terminate immediately when returning nil", ^{ - RACStream *stream = [infiniteStream bind:^{ - return ^ id (id _, BOOL *stop) { - return nil; - }; - }]; - - verifyValues(stream, @[]); - }); - - qck_it(@"should terminate after one value when setting 'stop'", ^{ - RACStream *stream = [infiniteStream bind:^{ - return ^ id (id value, BOOL *stop) { - *stop = YES; - return [streamClass return:value]; - }; - }]; - - verifyValues(stream, @[ RACUnit.defaultUnit ]); - }); - - qck_it(@"should terminate immediately when returning nil and setting 'stop'", ^{ - RACStream *stream = [infiniteStream bind:^{ - return ^ id (id _, BOOL *stop) { - *stop = YES; - return nil; - }; - }]; - - verifyValues(stream, @[]); - }); - - qck_it(@"should be restartable even with block state", ^{ - NSArray *values = @[ @0, @1, @2 ]; - RACStream *baseStream = streamWithValues(values); - - RACStream *countingStream = [baseStream bind:^{ - __block NSUInteger counter = 0; - - return ^(id x, BOOL *stop) { - return [streamClass return:@(counter++)]; - }; - }]; - - verifyValues(countingStream, @[ @0, @1, @2 ]); - verifyValues(countingStream, @[ @0, @1, @2 ]); - }); - - qck_it(@"should be interleavable even with block state", ^{ - NSArray *values = @[ @0, @1, @2 ]; - RACStream *baseStream = streamWithValues(values); - - RACStream *countingStream = [baseStream bind:^{ - __block NSUInteger counter = 0; - - return ^(id x, BOOL *stop) { - return [streamClass return:@(counter++)]; - }; - }]; - - // Just so +zip:reduce: thinks this is a unique stream. - RACStream *anotherStream = [[streamClass empty] concat:countingStream]; - - RACStream *zipped = [streamClass zip:@[ countingStream, anotherStream ] reduce:^(NSNumber *v1, NSNumber *v2) { - return @(v1.integerValue + v2.integerValue); - }]; - - verifyValues(zipped, @[ @0, @2, @4 ]); - }); - }); - - qck_describe(@"-flattenMap:", ^{ - qck_it(@"should return a single mapped result", ^{ - RACStream *stream = [[streamClass return:@0] flattenMap:^(NSNumber *value) { - NSNumber *newValue = @(value.integerValue + 1); - return [streamClass return:newValue]; - }]; - - verifyValues(stream, @[ @1 ]); - }); - - qck_it(@"should concatenate the results of mapping multiple values", ^{ - RACStream *baseStream = streamWithValues(@[ @0, @1 ]); - RACStream *stream = [baseStream flattenMap:^(NSNumber *value) { - NSNumber *newValue = @(value.integerValue + 1); - return [streamClass return:newValue]; - }]; - - verifyValues(stream, @[ @1, @2 ]); - }); - - qck_it(@"should concatenate with an empty result from mapping a value", ^{ - RACStream *baseStream = streamWithValues(@[ @0, @1, @2 ]); - RACStream *stream = [baseStream flattenMap:^(NSNumber *value) { - if (value.integerValue == 1) return [streamClass empty]; - - NSNumber *newValue = @(value.integerValue + 1); - return [streamClass return:newValue]; - }]; - - verifyValues(stream, @[ @1, @3 ]); - }); - - qck_it(@"should treat nil streams like empty streams", ^{ - RACStream *baseStream = streamWithValues(@[ @0, @1, @2 ]); - RACStream *stream = [baseStream flattenMap:^ RACStream * (NSNumber *value) { - if (value.integerValue == 1) return nil; - - NSNumber *newValue = @(value.integerValue + 1); - return [streamClass return:newValue]; - }]; - - verifyValues(stream, @[ @1, @3 ]); - }); - }); - - qck_it(@"should map", ^{ - RACStream *baseStream = streamWithValues(@[ @0, @1, @2 ]); - RACStream *stream = [baseStream map:^(NSNumber *value) { - return @(value.integerValue + 1); - }]; - - verifyValues(stream, @[ @1, @2, @3 ]); - }); - - qck_it(@"should map and replace", ^{ - RACStream *baseStream = streamWithValues(@[ @0, @1, @2 ]); - RACStream *stream = [baseStream mapReplace:RACUnit.defaultUnit]; - - verifyValues(stream, @[ RACUnit.defaultUnit, RACUnit.defaultUnit, RACUnit.defaultUnit ]); - }); - - qck_it(@"should filter", ^{ - RACStream *baseStream = streamWithValues(@[ @0, @1, @2, @3, @4, @5, @6 ]); - RACStream *stream = [baseStream filter:^ BOOL (NSNumber *value) { - return value.integerValue % 2 == 0; - }]; - - verifyValues(stream, @[ @0, @2, @4, @6 ]); - }); - - qck_describe(@"-ignore:", ^{ - qck_it(@"should ignore a value", ^{ - RACStream *baseStream = streamWithValues(@[ @0, @1, @2, @3, @4, @5, @6 ]); - RACStream *stream = [baseStream ignore:@1]; - - verifyValues(stream, @[ @0, @2, @3, @4, @5, @6 ]); - }); - - qck_it(@"should ignore based on object equality", ^{ - RACStream *baseStream = streamWithValues(@[ @"0", @"1", @"2", @"3", @"4", @"5", @"6" ]); - - NSMutableString *valueToIgnore = [[NSMutableString alloc] init]; - [valueToIgnore appendString:@"1"]; - RACStream *stream = [baseStream ignore:valueToIgnore]; - - verifyValues(stream, @[ @"0", @"2", @"3", @"4", @"5", @"6" ]); - }); - }); - - qck_it(@"should start with a value", ^{ - RACStream *stream = [[streamClass return:@1] startWith:@0]; - verifyValues(stream, @[ @0, @1 ]); - }); - - qck_describe(@"-skip:", ^{ - __block NSArray *values; - __block RACStream *stream; - - qck_beforeEach(^{ - values = @[ @0, @1, @2 ]; - stream = streamWithValues(values); - }); - - qck_it(@"should skip any valid number of values", ^{ - for (NSUInteger i = 0; i < values.count; i++) { - verifyValues([stream skip:i], [values subarrayWithRange:NSMakeRange(i, values.count - i)]); - } - }); - - qck_it(@"should return an empty stream when skipping too many values", ^{ - verifyValues([stream skip:4], @[]); - }); - }); - - qck_describe(@"-take:", ^{ - qck_describe(@"with three values", ^{ - __block NSArray *values; - __block RACStream *stream; - - qck_beforeEach(^{ - values = @[ @0, @1, @2 ]; - stream = streamWithValues(values); - }); - - qck_it(@"should take any valid number of values", ^{ - for (NSUInteger i = 0; i < values.count; i++) { - verifyValues([stream take:i], [values subarrayWithRange:NSMakeRange(0, i)]); - } - }); - - qck_it(@"should return the same stream when taking too many values", ^{ - verifyValues([stream take:4], values); - }); - }); - - qck_it(@"should take and terminate from an infinite stream", ^{ - verifyValues([infiniteStream take:0], @[]); - verifyValues([infiniteStream take:1], @[ RACUnit.defaultUnit ]); - verifyValues([infiniteStream take:2], @[ RACUnit.defaultUnit, RACUnit.defaultUnit ]); - }); - - qck_it(@"should take and terminate from a single-item stream", ^{ - NSArray *values = @[ RACUnit.defaultUnit ]; - RACStream *stream = streamWithValues(values); - verifyValues([stream take:1], values); - }); - }); - - qck_describe(@"zip stream creation methods", ^{ - __block NSArray *valuesOne; - - __block RACStream *streamOne; - __block RACStream *streamTwo; - __block RACStream *streamThree; - __block NSArray *threeStreams; - - __block NSArray *oneStreamTuples; - __block NSArray *twoStreamTuples; - __block NSArray *threeStreamTuples; - - qck_beforeEach(^{ - valuesOne = @[ @"Ada", @"Bob", @"Dea" ]; - NSArray *valuesTwo = @[ @"eats", @"cooks", @"jumps" ]; - NSArray *valuesThree = @[ @"fish", @"bear", @"rock" ]; - - streamOne = streamWithValues(valuesOne); - streamTwo = streamWithValues(valuesTwo); - streamThree = streamWithValues(valuesThree); - threeStreams = @[ streamOne, streamTwo, streamThree ]; - - oneStreamTuples = @[ - RACTuplePack(valuesOne[0]), - RACTuplePack(valuesOne[1]), - RACTuplePack(valuesOne[2]), - ]; - - twoStreamTuples = @[ - RACTuplePack(valuesOne[0], valuesTwo[0]), - RACTuplePack(valuesOne[1], valuesTwo[1]), - RACTuplePack(valuesOne[2], valuesTwo[2]), - ]; - - threeStreamTuples = @[ - RACTuplePack(valuesOne[0], valuesTwo[0], valuesThree[0]), - RACTuplePack(valuesOne[1], valuesTwo[1], valuesThree[1]), - RACTuplePack(valuesOne[2], valuesTwo[2], valuesThree[2]), - ]; - }); - - qck_describe(@"-zipWith:", ^{ - qck_it(@"should make a stream of tuples", ^{ - RACStream *stream = [streamOne zipWith:streamTwo]; - verifyValues(stream, twoStreamTuples); - }); - - qck_it(@"should truncate streams", ^{ - RACStream *shortStream = streamWithValues(@[ @"now", @"later" ]); - RACStream *stream = [streamOne zipWith:shortStream]; - - verifyValues(stream, @[ - RACTuplePack(valuesOne[0], @"now"), - RACTuplePack(valuesOne[1], @"later") - ]); - }); - - qck_it(@"should work on infinite streams", ^{ - RACStream *stream = [streamOne zipWith:infiniteStream]; - verifyValues(stream, @[ - RACTuplePack(valuesOne[0], RACUnit.defaultUnit), - RACTuplePack(valuesOne[1], RACUnit.defaultUnit), - RACTuplePack(valuesOne[2], RACUnit.defaultUnit) - ]); - }); - - qck_it(@"should handle multiples of the same stream", ^{ - RACStream *stream = [streamOne zipWith:streamOne]; - verifyValues(stream, @[ - RACTuplePack(valuesOne[0], valuesOne[0]), - RACTuplePack(valuesOne[1], valuesOne[1]), - RACTuplePack(valuesOne[2], valuesOne[2]), - ]); - }); - }); - - qck_describe(@"+zip:reduce:", ^{ - qck_it(@"should reduce values", ^{ - RACStream *stream = [streamClass zip:threeStreams reduce:^ NSString * (id x, id y, id z) { - return [NSString stringWithFormat:@"%@ %@ %@", x, y, z]; - }]; - verifyValues(stream, @[ @"Ada eats fish", @"Bob cooks bear", @"Dea jumps rock" ]); - }); - - qck_it(@"should truncate streams", ^{ - RACStream *shortStream = streamWithValues(@[ @"now", @"later" ]); - NSArray *streams = [threeStreams arrayByAddingObject:shortStream]; - RACStream *stream = [streamClass zip:streams reduce:^ NSString * (id w, id x, id y, id z) { - return [NSString stringWithFormat:@"%@ %@ %@ %@", w, x, y, z]; - }]; - verifyValues(stream, @[ @"Ada eats fish now", @"Bob cooks bear later" ]); - }); - - qck_it(@"should work on infinite streams", ^{ - NSArray *streams = [threeStreams arrayByAddingObject:infiniteStream]; - RACStream *stream = [streamClass zip:streams reduce:^ NSString * (id w, id x, id y, id z) { - return [NSString stringWithFormat:@"%@ %@ %@", w, x, y]; - }]; - verifyValues(stream, @[ @"Ada eats fish", @"Bob cooks bear", @"Dea jumps rock" ]); - }); - - qck_it(@"should handle multiples of the same stream", ^{ - NSArray *streams = @[ streamOne, streamOne, streamTwo, streamThree, streamTwo, streamThree ]; - RACStream *stream = [streamClass zip:streams reduce:^ NSString * (id x1, id x2, id y1, id z1, id y2, id z2) { - return [NSString stringWithFormat:@"%@ %@ %@ %@ %@ %@", x1, x2, y1, z1, y2, z2]; - }]; - verifyValues(stream, @[ @"Ada Ada eats fish eats fish", @"Bob Bob cooks bear cooks bear", @"Dea Dea jumps rock jumps rock" ]); - }); - }); - - qck_describe(@"+zip:", ^{ - qck_it(@"should make a stream of tuples out of single value", ^{ - RACStream *stream = [streamClass zip:@[ streamOne ]]; - verifyValues(stream, oneStreamTuples); - }); - - qck_it(@"should make a stream of tuples out of an array of streams", ^{ - RACStream *stream = [streamClass zip:threeStreams]; - verifyValues(stream, threeStreamTuples); - }); - - qck_it(@"should make an empty stream if given an empty array", ^{ - RACStream *stream = [streamClass zip:@[]]; - verifyValues(stream, @[]); - }); - - qck_it(@"should make a stream of tuples out of an enumerator of streams", ^{ - RACStream *stream = [streamClass zip:threeStreams.objectEnumerator]; - verifyValues(stream, threeStreamTuples); - }); - - qck_it(@"should make an empty stream if given an empty enumerator", ^{ - RACStream *stream = [streamClass zip:@[].objectEnumerator]; - verifyValues(stream, @[]); - }); - }); - }); - - qck_describe(@"+concat:", ^{ - __block NSArray *streams = nil; - __block NSArray *result = nil; - - qck_beforeEach(^{ - RACStream *a = [streamClass return:@0]; - RACStream *b = [streamClass empty]; - RACStream *c = streamWithValues(@[ @1, @2, @3 ]); - RACStream *d = [streamClass return:@4]; - RACStream *e = [streamClass return:@5]; - RACStream *f = [streamClass empty]; - RACStream *g = [streamClass empty]; - RACStream *h = streamWithValues(@[ @6, @7 ]); - streams = @[ a, b, c, d, e, f, g, h ]; - result = @[ @0, @1, @2, @3, @4, @5, @6, @7 ]; - }); - - qck_it(@"should concatenate an array of streams", ^{ - RACStream *stream = [streamClass concat:streams]; - verifyValues(stream, result); - }); - - qck_it(@"should concatenate an enumerator of streams", ^{ - RACStream *stream = [streamClass concat:streams.objectEnumerator]; - verifyValues(stream, result); - }); - }); - - qck_describe(@"scanning", ^{ - NSArray *values = @[ @1, @2, @3, @4 ]; - - __block RACStream *stream; - - qck_beforeEach(^{ - stream = streamWithValues(values); - }); - - qck_it(@"should scan", ^{ - RACStream *scanned = [stream scanWithStart:@0 reduce:^(NSNumber *running, NSNumber *next) { - return @(running.integerValue + next.integerValue); - }]; - - verifyValues(scanned, @[ @1, @3, @6, @10 ]); - }); - - qck_it(@"should scan with index", ^{ - RACStream *scanned = [stream scanWithStart:@0 reduceWithIndex:^(NSNumber *running, NSNumber *next, NSUInteger index) { - return @(running.integerValue + next.integerValue + (NSInteger)index); - }]; - - verifyValues(scanned, @[ @1, @4, @9, @16 ]); - }); - }); - - qck_describe(@"taking with a predicate", ^{ - NSArray *values = @[ @0, @1, @2, @3, @0, @2, @4 ]; - - __block RACStream *stream; - - qck_beforeEach(^{ - stream = streamWithValues(values); - }); - - qck_it(@"should take until a predicate is true", ^{ - RACStream *taken = [stream takeUntilBlock:^ BOOL (NSNumber *x) { - return x.integerValue >= 3; - }]; - - verifyValues(taken, @[ @0, @1, @2 ]); - }); - - qck_it(@"should take while a predicate is true", ^{ - RACStream *taken = [stream takeWhileBlock:^ BOOL (NSNumber *x) { - return x.integerValue <= 1; - }]; - - verifyValues(taken, @[ @0, @1 ]); - }); - - qck_it(@"should take a full stream", ^{ - RACStream *taken = [stream takeWhileBlock:^ BOOL (NSNumber *x) { - return x.integerValue <= 10; - }]; - - verifyValues(taken, values); - }); - - qck_it(@"should return an empty stream", ^{ - RACStream *taken = [stream takeWhileBlock:^ BOOL (NSNumber *x) { - return x.integerValue < 0; - }]; - - verifyValues(taken, @[]); - }); - - qck_it(@"should terminate an infinite stream", ^{ - RACStream *infiniteCounter = [infiniteStream scanWithStart:@0 reduce:^(NSNumber *running, id _) { - return @(running.unsignedIntegerValue + 1); - }]; - - RACStream *taken = [infiniteCounter takeWhileBlock:^ BOOL (NSNumber *x) { - return x.integerValue <= 5; - }]; - - verifyValues(taken, @[ @1, @2, @3, @4, @5 ]); - }); - }); - - qck_describe(@"skipping with a predicate", ^{ - NSArray *values = @[ @0, @1, @2, @3, @0, @2, @4 ]; - - __block RACStream *stream; - - qck_beforeEach(^{ - stream = streamWithValues(values); - }); - - qck_it(@"should skip until a predicate is true", ^{ - RACStream *taken = [stream skipUntilBlock:^ BOOL (NSNumber *x) { - return x.integerValue >= 3; - }]; - - verifyValues(taken, @[ @3, @0, @2, @4 ]); - }); - - qck_it(@"should skip while a predicate is true", ^{ - RACStream *taken = [stream skipWhileBlock:^ BOOL (NSNumber *x) { - return x.integerValue <= 1; - }]; - - verifyValues(taken, @[ @2, @3, @0, @2, @4 ]); - }); - - qck_it(@"should skip a full stream", ^{ - RACStream *taken = [stream skipWhileBlock:^ BOOL (NSNumber *x) { - return x.integerValue <= 10; - }]; - - verifyValues(taken, @[]); - }); - - qck_it(@"should finish skipping immediately", ^{ - RACStream *taken = [stream skipWhileBlock:^ BOOL (NSNumber *x) { - return x.integerValue < 0; - }]; - - verifyValues(taken, values); - }); - }); - - qck_describe(@"-combinePreviousWithStart:reduce:", ^{ - NSArray *values = @[ @1, @2, @3 ]; - __block RACStream *stream; - qck_beforeEach(^{ - stream = streamWithValues(values); - }); - - qck_it(@"should pass the previous next into the reduce block", ^{ - NSMutableArray *previouses = [NSMutableArray array]; - RACStream *mapped = [stream combinePreviousWithStart:nil reduce:^(id previous, id next) { - [previouses addObject:previous ?: RACTupleNil.tupleNil]; - return next; - }]; - - verifyValues(mapped, @[ @1, @2, @3 ]); - - NSArray *expected = @[ RACTupleNil.tupleNil, @1, @2 ]; - expect(previouses).to(equal(expected)); - }); - - qck_it(@"should send the combined value", ^{ - RACStream *mapped = [stream combinePreviousWithStart:@1 reduce:^(NSNumber *previous, NSNumber *next) { - return [NSString stringWithFormat:@"%lu - %lu", (unsigned long)previous.unsignedIntegerValue, (unsigned long)next.unsignedIntegerValue]; - }]; - - verifyValues(mapped, @[ @"1 - 1", @"1 - 2", @"2 - 3" ]); - }); - }); - - qck_it(@"should reduce tuples", ^{ - RACStream *stream = streamWithValues(@[ - RACTuplePack(@"foo", @"bar"), - RACTuplePack(@"buzz", @"baz"), - RACTuplePack(@"", @"_") - ]); - - RACStream *reduced = [stream reduceEach:^(NSString *a, NSString *b) { - return [a stringByAppendingString:b]; - }]; - - verifyValues(reduced, @[ @"foobar", @"buzzbaz", @"_" ]); - }); - }); -} - -QuickConfigurationEnd diff --git a/ReactiveObjCTests/RACSubclassObject.h b/ReactiveObjCTests/RACSubclassObject.h deleted file mode 100644 index 7af3fe185d..0000000000 --- a/ReactiveObjCTests/RACSubclassObject.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// RACSubclassObject.h -// ReactiveObjC -// -// Created by Josh Abernathy on 3/18/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACTestObject.h" - -@interface RACSubclassObject : RACTestObject - -// Set whenever -forwardInvocation: is invoked on the receiver. -@property (nonatomic, assign) SEL forwardedSelector; - -// Invokes the superclass implementation with `objectValue` concatenated to -// "SUBCLASS". -- (NSString *)combineObjectValue:(id)objectValue andIntegerValue:(NSInteger)integerValue; - -// Asynchronously invokes the superclass implementation on the current scheduler. -- (void)setObjectValue:(id)objectValue andSecondObjectValue:(id)secondObjectValue; - -@end diff --git a/ReactiveObjCTests/RACSubclassObject.m b/ReactiveObjCTests/RACSubclassObject.m deleted file mode 100644 index dd8ae6c9e7..0000000000 --- a/ReactiveObjCTests/RACSubclassObject.m +++ /dev/null @@ -1,38 +0,0 @@ -// -// RACSubclassObject.m -// ReactiveObjC -// -// Created by Josh Abernathy on 3/18/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACSubclassObject.h" -#import "RACScheduler.h" - -@implementation RACSubclassObject - -- (void)forwardInvocation:(NSInvocation *)invocation { - self.forwardedSelector = invocation.selector; -} - -- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector { - NSParameterAssert(selector != NULL); - - NSMethodSignature *signature = [super methodSignatureForSelector:selector]; - if (signature != nil) return signature; - - return [super methodSignatureForSelector:@selector(description)]; -} - -- (NSString *)combineObjectValue:(id)objectValue andIntegerValue:(NSInteger)integerValue { - NSString *appended = [[objectValue description] stringByAppendingString:@"SUBCLASS"]; - return [super combineObjectValue:appended andIntegerValue:integerValue]; -} - -- (void)setObjectValue:(id)objectValue andSecondObjectValue:(id)secondObjectValue { - [RACScheduler.currentScheduler schedule:^{ - [super setObjectValue:objectValue andSecondObjectValue:secondObjectValue]; - }]; -} - -@end diff --git a/ReactiveObjCTests/RACSubjectSpec.m b/ReactiveObjCTests/RACSubjectSpec.m deleted file mode 100644 index 3a5766a8bb..0000000000 --- a/ReactiveObjCTests/RACSubjectSpec.m +++ /dev/null @@ -1,366 +0,0 @@ -// -// RACSubjectSpec.m -// ReactiveObjC -// -// Created by Josh Abernathy on 6/24/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -@import Quick; -@import Nimble; - -#import "RACSubscriberExamples.h" - -#import -#import -#import "RACBehaviorSubject.h" -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACReplaySubject.h" -#import "RACScheduler.h" -#import "RACSignal+Operations.h" -#import "RACSubject.h" -#import "RACUnit.h" - -@interface RACTestSubscriber : NSObject -@property (nonatomic, strong, readonly) RACDisposable *disposable; -@end - -@implementation RACTestSubscriber - -- (instancetype)init { - self = [super init]; - _disposable = [RACDisposable new]; - return self; -} - -- (void)sendNext:(id)value {} -- (void)sendError:(NSError *)error {} -- (void)sendCompleted {} - -- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable { - [disposable addDisposable:self.disposable]; -} - -@end - -QuickSpecBegin(RACSubjectSpec) - -qck_describe(@"RACSubject", ^{ - __block RACSubject *subject; - __block NSMutableArray *values; - - __block BOOL success; - __block NSError *error; - - qck_beforeEach(^{ - values = [NSMutableArray array]; - - subject = [RACSubject subject]; - success = YES; - error = nil; - - [subject subscribeNext:^(id value) { - [values addObject:value]; - } error:^(NSError *e) { - error = e; - success = NO; - } completed:^{ - success = YES; - }]; - }); - - qck_it(@"should dispose the paired disposable when a subscription terminates", ^{ - RACSubject* subject = [RACSubject new]; - RACTestSubscriber* subscriber = [RACTestSubscriber new]; - - [[subject subscribe:subscriber] dispose]; - - expect(@(subscriber.disposable.disposed)).to(beTruthy()); - }); - - qck_itBehavesLike(RACSubscriberExamples, ^{ - return @{ - RACSubscriberExampleSubscriber: subject, - RACSubscriberExampleValuesReceivedBlock: [^{ return [values copy]; } copy], - RACSubscriberExampleErrorReceivedBlock: [^{ return error; } copy], - RACSubscriberExampleSuccessBlock: [^{ return success; } copy] - }; - }); -}); - -qck_describe(@"RACReplaySubject", ^{ - __block RACReplaySubject *subject = nil; - - qck_describe(@"with a capacity of 1", ^{ - qck_beforeEach(^{ - subject = [RACReplaySubject replaySubjectWithCapacity:1]; - }); - - qck_it(@"should send the last value", ^{ - id firstValue = @"blah"; - id secondValue = @"more blah"; - - [subject sendNext:firstValue]; - [subject sendNext:secondValue]; - - __block id valueReceived = nil; - [subject subscribeNext:^(id x) { - valueReceived = x; - }]; - - expect(valueReceived).to(equal(secondValue)); - }); - - qck_it(@"should send the last value to new subscribers after completion", ^{ - id firstValue = @"blah"; - id secondValue = @"more blah"; - - __block id valueReceived = nil; - __block NSUInteger nextsReceived = 0; - - [subject sendNext:firstValue]; - [subject sendNext:secondValue]; - - expect(@(nextsReceived)).to(equal(@0)); - expect(valueReceived).to(beNil()); - - [subject sendCompleted]; - - [subject subscribeNext:^(id x) { - valueReceived = x; - nextsReceived++; - }]; - - expect(@(nextsReceived)).to(equal(@1)); - expect(valueReceived).to(equal(secondValue)); - }); - - qck_it(@"should not send any values to new subscribers if none were sent originally", ^{ - [subject sendCompleted]; - - __block BOOL nextInvoked = NO; - [subject subscribeNext:^(id x) { - nextInvoked = YES; - }]; - - expect(@(nextInvoked)).to(beFalsy()); - }); - - qck_it(@"should resend errors", ^{ - NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain code:0 userInfo:nil]; - [subject sendError:error]; - - __block BOOL errorSent = NO; - [subject subscribeError:^(NSError *sentError) { - expect(sentError).to(equal(error)); - errorSent = YES; - }]; - - expect(@(errorSent)).to(beTruthy()); - }); - - qck_it(@"should resend nil errors", ^{ - [subject sendError:nil]; - - __block BOOL errorSent = NO; - [subject subscribeError:^(NSError *sentError) { - expect(sentError).to(beNil()); - errorSent = YES; - }]; - - expect(@(errorSent)).to(beTruthy()); - }); - }); - - qck_describe(@"with an unlimited capacity", ^{ - qck_beforeEach(^{ - subject = [RACReplaySubject subject]; - }); - - qck_itBehavesLike(RACSubscriberExamples, ^{ - return @{ - RACSubscriberExampleSubscriber: subject, - RACSubscriberExampleValuesReceivedBlock: [^{ - NSMutableArray *values = [NSMutableArray array]; - - // This subscription should synchronously dump all values already - // received into 'values'. - [subject subscribeNext:^(id value) { - [values addObject:value]; - }]; - - return values; - } copy], - RACSubscriberExampleErrorReceivedBlock: [^{ - __block NSError *error = nil; - - [subject subscribeError:^(NSError *x) { - error = x; - }]; - - return error; - } copy], - RACSubscriberExampleSuccessBlock: [^{ - __block BOOL success = YES; - - [subject subscribeError:^(NSError *x) { - success = NO; - }]; - - return success; - } copy] - }; - }); - - qck_it(@"should send both values to new subscribers after completion", ^{ - id firstValue = @"blah"; - id secondValue = @"more blah"; - - [subject sendNext:firstValue]; - [subject sendNext:secondValue]; - [subject sendCompleted]; - - __block BOOL completed = NO; - NSMutableArray *valuesReceived = [NSMutableArray array]; - [subject subscribeNext:^(id x) { - [valuesReceived addObject:x]; - } completed:^{ - completed = YES; - }]; - - expect(valuesReceived).to(haveCount(@2)); - NSArray *expected = [NSArray arrayWithObjects:firstValue, secondValue, nil]; - expect(valuesReceived).to(equal(expected)); - expect(@(completed)).to(beTruthy()); - }); - - qck_it(@"should send values in the same order live as when replaying", ^{ - NSUInteger count = 49317; - - // Just leak it, ain't no thang. - __unsafe_unretained volatile id *values = (__unsafe_unretained id *)calloc(count, sizeof(*values)); - __block volatile int32_t nextIndex = 0; - - [subject subscribeNext:^(NSNumber *value) { - int32_t indexPlusOne = OSAtomicIncrement32(&nextIndex); - values[indexPlusOne - 1] = value; - }]; - - dispatch_queue_t queue = dispatch_queue_create("org.reactivecocoa.ReactiveObjC.RACSubjectSpec", DISPATCH_QUEUE_CONCURRENT); - dispatch_suspend(queue); - - for (NSUInteger i = 0; i < count; i++) { - dispatch_async(queue, ^{ - [subject sendNext:@(i)]; - }); - } - - dispatch_resume(queue); - dispatch_barrier_sync(queue, ^{ - [subject sendCompleted]; - }); - - OSMemoryBarrier(); - - NSArray *liveValues = [NSArray arrayWithObjects:(id *)values count:(NSUInteger)nextIndex]; - expect(liveValues).to(haveCount(@(count))); - - NSArray *replayedValues = subject.toArray; - expect(replayedValues).to(haveCount(@(count))); - - // It should return the same ordering for multiple invocations too. - expect(replayedValues).to(equal(subject.toArray)); - - [replayedValues enumerateObjectsUsingBlock:^(id value, NSUInteger index, BOOL *stop) { - expect(liveValues[index]).to(equal(value)); - }]; - }); - - qck_it(@"should have a current scheduler when replaying", ^{ - [subject sendNext:RACUnit.defaultUnit]; - - __block RACScheduler *currentScheduler; - [subject subscribeNext:^(id x) { - currentScheduler = RACScheduler.currentScheduler; - }]; - - expect(currentScheduler).notTo(beNil()); - - currentScheduler = nil; - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [subject subscribeNext:^(id x) { - currentScheduler = RACScheduler.currentScheduler; - }]; - }); - - expect(currentScheduler).toEventuallyNot(beNil()); - }); - - qck_it(@"should stop replaying when the subscription is disposed", ^{ - NSMutableArray *values = [NSMutableArray array]; - - [subject sendNext:@0]; - [subject sendNext:@1]; - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - __block RACDisposable *disposable = [subject subscribeNext:^(id x) { - expect(disposable).notTo(beNil()); - - [values addObject:x]; - [disposable dispose]; - }]; - }); - - expect(values).toEventually(equal(@[ @0 ])); - }); - - qck_it(@"should finish replaying before completing", ^{ - [subject sendNext:@1]; - - __block id received; - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [subject subscribeNext:^(id x) { - received = x; - }]; - - [subject sendCompleted]; - }); - - expect(received).toEventually(equal(@1)); - }); - - qck_it(@"should finish replaying before erroring", ^{ - [subject sendNext:@1]; - - __block id received; - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [subject subscribeNext:^(id x) { - received = x; - }]; - - [subject sendError:[NSError errorWithDomain:@"blah" code:-99 userInfo:nil]]; - }); - - expect(received).toEventually(equal(@1)); - }); - - qck_it(@"should finish replaying before sending new values", ^{ - [subject sendNext:@1]; - - NSMutableArray *received = [NSMutableArray array]; - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [subject subscribeNext:^(id x) { - [received addObject:x]; - }]; - - [subject sendNext:@2]; - }); - - NSArray *expected = @[ @1, @2 ]; - expect(received).toEventually(equal(expected)); - }); - }); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/RACSubscriberExamples.h b/ReactiveObjCTests/RACSubscriberExamples.h deleted file mode 100644 index f2b0ccf84f..0000000000 --- a/ReactiveObjCTests/RACSubscriberExamples.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// RACSubscriberExamples.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-11-27. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -// The name of the shared examples for implementors of . -extern NSString * const RACSubscriberExamples; - -// id -extern NSString * const RACSubscriberExampleSubscriber; - -// A block which returns an NSArray of the values received so far. -extern NSString * const RACSubscriberExampleValuesReceivedBlock; - -// A block which returns any NSError received so far. -extern NSString * const RACSubscriberExampleErrorReceivedBlock; - -// A block which returns a BOOL indicating whether the subscriber is successful -// so far. -extern NSString * const RACSubscriberExampleSuccessBlock; diff --git a/ReactiveObjCTests/RACSubscriberExamples.m b/ReactiveObjCTests/RACSubscriberExamples.m deleted file mode 100644 index 769084a677..0000000000 --- a/ReactiveObjCTests/RACSubscriberExamples.m +++ /dev/null @@ -1,190 +0,0 @@ -// -// RACSubscriberExamples.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-11-27. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACSubscriberExamples.h" - -#import "NSObject+RACDeallocating.h" -#import "RACCompoundDisposable.h" -#import "RACDisposable.h" -#import "RACSubject.h" -#import "RACSubscriber.h" - -NSString * const RACSubscriberExamples = @"RACSubscriberExamples"; -NSString * const RACSubscriberExampleSubscriber = @"RACSubscriberExampleSubscriber"; -NSString * const RACSubscriberExampleValuesReceivedBlock = @"RACSubscriberExampleValuesReceivedBlock"; -NSString * const RACSubscriberExampleErrorReceivedBlock = @"RACSubscriberExampleErrorReceivedBlock"; -NSString * const RACSubscriberExampleSuccessBlock = @"RACSubscriberExampleSuccessBlock"; - -QuickConfigurationBegin(RACSubscriberExampleGroups) - -+ (void)configure:(Configuration *)configuration { - sharedExamples(RACSubscriberExamples, ^(QCKDSLSharedExampleContext exampleContext) { - __block NSArray * (^valuesReceived)(void); - __block NSError * (^errorReceived)(void); - __block BOOL (^success)(void); - __block id subscriber; - - qck_beforeEach(^{ - valuesReceived = exampleContext()[RACSubscriberExampleValuesReceivedBlock]; - errorReceived = exampleContext()[RACSubscriberExampleErrorReceivedBlock]; - success = exampleContext()[RACSubscriberExampleSuccessBlock]; - subscriber = exampleContext()[RACSubscriberExampleSubscriber]; - expect(subscriber).notTo(beNil()); - }); - - qck_it(@"should accept a nil error", ^{ - [subscriber sendError:nil]; - - expect(@(success())).to(beFalsy()); - expect(errorReceived()).to(beNil()); - expect(valuesReceived()).to(equal(@[])); - }); - - qck_describe(@"with values", ^{ - __block NSSet *values; - - qck_beforeEach(^{ - NSMutableSet *mutableValues = [NSMutableSet set]; - for (NSUInteger i = 0; i < 20; i++) { - [mutableValues addObject:@(i)]; - } - - values = [mutableValues copy]; - }); - - qck_it(@"should send nexts serially, even when delivered from multiple threads", ^{ - NSArray *allValues = values.allObjects; - dispatch_apply(allValues.count, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), [^(size_t index) { - [subscriber sendNext:allValues[index]]; - } copy]); - - expect(@(success())).to(beTruthy()); - expect(errorReceived()).to(beNil()); - - NSSet *valuesReceivedSet = [NSSet setWithArray:valuesReceived()]; - expect(valuesReceivedSet).to(equal(values)); - }); - }); - - qck_describe(@"multiple subscriptions", ^{ - __block RACSubject *first; - __block RACSubject *second; - - qck_beforeEach(^{ - first = [RACSubject subject]; - [first subscribe:subscriber]; - - second = [RACSubject subject]; - [second subscribe:subscriber]; - }); - - qck_it(@"should send values from all subscriptions", ^{ - [first sendNext:@"foo"]; - [second sendNext:@"bar"]; - [first sendNext:@"buzz"]; - [second sendNext:@"baz"]; - - expect(@(success())).to(beTruthy()); - expect(errorReceived()).to(beNil()); - - NSArray *expected = @[ @"foo", @"bar", @"buzz", @"baz" ]; - expect(valuesReceived()).to(equal(expected)); - }); - - qck_it(@"should terminate after the first error from any subscription", ^{ - NSError *error = [NSError errorWithDomain:@"" code:-1 userInfo:nil]; - - [first sendNext:@"foo"]; - [second sendError:error]; - [first sendNext:@"buzz"]; - - expect(@(success())).to(beFalsy()); - expect(errorReceived()).to(equal(error)); - - NSArray *expected = @[ @"foo" ]; - expect(valuesReceived()).to(equal(expected)); - }); - - qck_it(@"should terminate after the first completed from any subscription", ^{ - [first sendNext:@"foo"]; - [second sendNext:@"bar"]; - [first sendCompleted]; - [second sendNext:@"baz"]; - - expect(@(success())).to(beTruthy()); - expect(errorReceived()).to(beNil()); - - NSArray *expected = @[ @"foo", @"bar" ]; - expect(valuesReceived()).to(equal(expected)); - }); - - qck_it(@"should dispose of all current subscriptions upon termination", ^{ - __block BOOL firstDisposed = NO; - RACSignal *firstDisposableSignal = [RACSignal createSignal:^(id subscriber) { - return [RACDisposable disposableWithBlock:^{ - firstDisposed = YES; - }]; - }]; - - __block BOOL secondDisposed = NO; - RACSignal *secondDisposableSignal = [RACSignal createSignal:^(id subscriber) { - return [RACDisposable disposableWithBlock:^{ - secondDisposed = YES; - }]; - }]; - - [firstDisposableSignal subscribe:subscriber]; - [secondDisposableSignal subscribe:subscriber]; - - expect(@(firstDisposed)).to(beFalsy()); - expect(@(secondDisposed)).to(beFalsy()); - - [first sendCompleted]; - - expect(@(firstDisposed)).to(beTruthy()); - expect(@(secondDisposed)).to(beTruthy()); - }); - - qck_it(@"should dispose of future subscriptions upon termination", ^{ - __block BOOL disposed = NO; - RACSignal *disposableSignal = [RACSignal createSignal:^(id subscriber) { - return [RACDisposable disposableWithBlock:^{ - disposed = YES; - }]; - }]; - - [first sendCompleted]; - expect(@(disposed)).to(beFalsy()); - - [disposableSignal subscribe:subscriber]; - expect(@(disposed)).to(beTruthy()); - }); - }); - - qck_describe(@"memory management", ^{ - qck_it(@"should not retain disposed disposables", ^{ - __block BOOL disposableDeallocd = NO; - @autoreleasepool { - RACCompoundDisposable *disposable __attribute__((objc_precise_lifetime)) = [RACCompoundDisposable disposableWithBlock:^{}]; - [disposable.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ - disposableDeallocd = YES; - }]]; - - [subscriber didSubscribeWithDisposable:disposable]; - [disposable dispose]; - } - expect(@(disposableDeallocd)).to(beTruthy()); - }); - }); - }); -} - -QuickConfigurationEnd diff --git a/ReactiveObjCTests/RACSubscriberSpec.m b/ReactiveObjCTests/RACSubscriberSpec.m deleted file mode 100644 index 1b99580067..0000000000 --- a/ReactiveObjCTests/RACSubscriberSpec.m +++ /dev/null @@ -1,130 +0,0 @@ -// -// RACSubscriberSpec.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-11-27. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACSubscriberExamples.h" - -#import "RACSubscriber.h" -#import "RACSubscriber+Private.h" -#import - -QuickSpecBegin(RACSubscriberSpec) - -__block RACSubscriber *subscriber; -__block NSMutableArray *values; - -__block volatile BOOL finished; -__block volatile int32_t nextsAfterFinished; - -__block BOOL success; -__block NSError *error; - -qck_beforeEach(^{ - values = [NSMutableArray array]; - - finished = NO; - nextsAfterFinished = 0; - - success = YES; - error = nil; - - subscriber = [RACSubscriber subscriberWithNext:^(id value) { - if (finished) OSAtomicIncrement32Barrier(&nextsAfterFinished); - - [values addObject:value]; - } error:^(NSError *e) { - error = e; - success = NO; - } completed:^{ - success = YES; - }]; -}); - -qck_itBehavesLike(RACSubscriberExamples, ^{ - return @{ - RACSubscriberExampleSubscriber: subscriber, - RACSubscriberExampleValuesReceivedBlock: [^{ return [values copy]; } copy], - RACSubscriberExampleErrorReceivedBlock: [^{ return error; } copy], - RACSubscriberExampleSuccessBlock: [^{ return success; } copy] - }; -}); - -qck_describe(@"finishing", ^{ - __block void (^sendValues)(void); - __block BOOL expectedSuccess; - - __block dispatch_group_t dispatchGroup; - __block dispatch_queue_t concurrentQueue; - - qck_beforeEach(^{ - dispatchGroup = dispatch_group_create(); - expect(dispatchGroup).notTo(beNil()); - - concurrentQueue = dispatch_queue_create("org.reactivecocoa.ReactiveObjC.RACSubscriberSpec", DISPATCH_QUEUE_CONCURRENT); - expect(concurrentQueue).notTo(beNil()); - - dispatch_suspend(concurrentQueue); - - sendValues = [^{ - for (NSUInteger i = 0; i < 15; i++) { - dispatch_group_async(dispatchGroup, concurrentQueue, ^{ - [subscriber sendNext:@(i)]; - }); - } - } copy]; - - sendValues(); - }); - - qck_afterEach(^{ - sendValues(); - dispatch_resume(concurrentQueue); - - // Time out after one second. - dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)); - expect(@(dispatch_group_wait(dispatchGroup, time))).to(equal(@0)); - - dispatchGroup = NULL; - concurrentQueue = NULL; - - expect(@(nextsAfterFinished)).to(equal(@0)); - - if (expectedSuccess) { - expect(@(success)).to(beTruthy()); - expect(error).to(beNil()); - } else { - expect(@(success)).to(beFalsy()); - } - }); - - qck_it(@"should never invoke next after sending completed", ^{ - expectedSuccess = YES; - - dispatch_group_async(dispatchGroup, concurrentQueue, ^{ - [subscriber sendCompleted]; - - finished = YES; - OSMemoryBarrier(); - }); - }); - - qck_it(@"should never invoke next after sending error", ^{ - expectedSuccess = NO; - - dispatch_group_async(dispatchGroup, concurrentQueue, ^{ - [subscriber sendError:nil]; - - finished = YES; - OSMemoryBarrier(); - }); - }); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/RACSubscriptingAssignmentTrampolineSpec.m b/ReactiveObjCTests/RACSubscriptingAssignmentTrampolineSpec.m deleted file mode 100644 index 87f3ea44aa..0000000000 --- a/ReactiveObjCTests/RACSubscriptingAssignmentTrampolineSpec.m +++ /dev/null @@ -1,36 +0,0 @@ -// -// RACSubscriptingAssignmentTrampolineSpec.m -// ReactiveObjC -// -// Created by Josh Abernathy on 9/24/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACSubscriptingAssignmentTrampoline.h" -#import "RACPropertySignalExamples.h" -#import "RACTestObject.h" -#import "RACSubject.h" - -QuickSpecBegin(RACSubscriptingAssignmentTrampolineSpec) - -id setupBlock = ^(RACTestObject *testObject, NSString *keyPath, id nilValue, RACSignal *signal) { - [[RACSubscriptingAssignmentTrampoline alloc] initWithTarget:testObject nilValue:nilValue][keyPath] = signal; -}; - -qck_itBehavesLike(RACPropertySignalExamples, ^{ - return @{ RACPropertySignalExamplesSetupBlock: setupBlock }; -}); - -qck_it(@"should expand the RAC macro properly", ^{ - RACSubject *subject = [RACSubject subject]; - RACTestObject *testObject = [[RACTestObject alloc] init]; - RAC(testObject, objectValue) = subject; - - [subject sendNext:@1]; - expect(testObject.objectValue).to(equal(@1)); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/RACTargetQueueSchedulerSpec.m b/ReactiveObjCTests/RACTargetQueueSchedulerSpec.m deleted file mode 100644 index d5a9d29d7c..0000000000 --- a/ReactiveObjCTests/RACTargetQueueSchedulerSpec.m +++ /dev/null @@ -1,49 +0,0 @@ -// -// RACTargetQueueSchedulerSpec.m -// ReactiveObjC -// -// Created by Josh Abernathy on 6/7/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACTargetQueueScheduler.h" -#import - -QuickSpecBegin(RACTargetQueueSchedulerSpec) - -qck_it(@"should have a valid current scheduler", ^{ - dispatch_queue_t queue = dispatch_queue_create("test-queue", DISPATCH_QUEUE_SERIAL); - RACScheduler *scheduler = [[RACTargetQueueScheduler alloc] initWithName:@"test-scheduler" targetQueue:queue]; - __block RACScheduler *currentScheduler; - [scheduler schedule:^{ - currentScheduler = RACScheduler.currentScheduler; - }]; - - expect(currentScheduler).toEventually(equal(scheduler)); -}); - -qck_it(@"should schedule blocks FIFO even when given a concurrent queue", ^{ - dispatch_queue_t queue = dispatch_queue_create("test-queue", DISPATCH_QUEUE_CONCURRENT); - RACScheduler *scheduler = [[RACTargetQueueScheduler alloc] initWithName:@"test-scheduler" targetQueue:queue]; - __block volatile int32_t startedCount = 0; - __block volatile uint32_t waitInFirst = 1; - [scheduler schedule:^{ - OSAtomicIncrement32Barrier(&startedCount); - while (waitInFirst == 1) ; - }]; - - [scheduler schedule:^{ - OSAtomicIncrement32Barrier(&startedCount); - }]; - - expect(@(startedCount)).toEventually(equal(@1)); - - OSAtomicAnd32Barrier(0, &waitInFirst); - - expect(@(startedCount)).toEventually(equal(@2)); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/RACTestExampleScheduler.h b/ReactiveObjCTests/RACTestExampleScheduler.h deleted file mode 100644 index 46250bde9e..0000000000 --- a/ReactiveObjCTests/RACTestExampleScheduler.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// RACTestExampleScheduler.h -// ReactiveObjC -// -// Created by Josh Abernathy on 6/7/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -@interface RACTestExampleScheduler : RACQueueScheduler - -- (id)initWithQueue:(dispatch_queue_t)queue; - -@end diff --git a/ReactiveObjCTests/RACTestExampleScheduler.m b/ReactiveObjCTests/RACTestExampleScheduler.m deleted file mode 100644 index 14ef5ee50c..0000000000 --- a/ReactiveObjCTests/RACTestExampleScheduler.m +++ /dev/null @@ -1,39 +0,0 @@ -// -// RACTestExampleScheduler.m -// ReactiveObjC -// -// Created by Josh Abernathy on 6/7/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACTestExampleScheduler.h" -#import "RACQueueScheduler+Subclass.h" - -@implementation RACTestExampleScheduler - -#pragma mark Lifecycle - -- (id)initWithQueue:(dispatch_queue_t)queue { - return [super initWithName:nil queue:queue]; -} - -#pragma mark RACScheduler - -- (RACDisposable *)schedule:(void (^)(void))block { - dispatch_async(self.queue, ^{ - [self performAsCurrentScheduler:block]; - }); - - return nil; -} - -- (RACDisposable *)after:(NSDate *)date schedule:(void (^)(void))block { - dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)([date timeIntervalSinceNow] * NSEC_PER_SEC)); - dispatch_after(when, self.queue, ^{ - [self performAsCurrentScheduler:block]; - }); - - return nil; -} - -@end diff --git a/ReactiveObjCTests/RACTestObject.h b/ReactiveObjCTests/RACTestObject.h deleted file mode 100644 index 98dd7393cc..0000000000 --- a/ReactiveObjCTests/RACTestObject.h +++ /dev/null @@ -1,87 +0,0 @@ -// -// RACTestObject.h -// ReactiveObjC -// -// Created by Josh Abernathy on 9/18/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import -#import - -typedef struct { - long long integerField; - double doubleField; -} RACTestStruct; - -@protocol RACTestProtocol - -@optional -- (void)optionalProtocolMethodWithObjectValue:(id)objectValue; - -@end - -@interface RACTestObject : NSObject - -@property (nonatomic, strong) id objectValue; -@property (nonatomic, strong) id secondObjectValue; -@property (nonatomic, strong) RACTestObject *strongTestObjectValue; -@property (nonatomic, weak) RACTestObject *weakTestObjectValue; -@property (nonatomic, weak) id weakObjectWithProtocol; -@property (nonatomic, assign) NSInteger integerValue; -// Holds a copy of the string. -@property (nonatomic, assign) char *charPointerValue; -// Holds a copy of the string. -@property (nonatomic, assign) const char *constCharPointerValue; -@property (nonatomic, assign) CGRect rectValue; -@property (nonatomic, assign) CGSize sizeValue; -@property (nonatomic, assign) CGPoint pointValue; -@property (nonatomic, assign) NSRange rangeValue; -@property (nonatomic, assign) RACTestStruct structValue; -@property (nonatomic, assign) _Bool c99BoolValue; -@property (nonatomic, copy) NSString *stringValue; -@property (nonatomic, copy) NSArray *arrayValue; -@property (nonatomic, copy) NSSet *setValue; -@property (nonatomic, copy) NSOrderedSet *orderedSetValue; -@property (nonatomic, strong) id slowObjectValue; - -// Returns a new object each time, with the integerValue set to 42. -@property (nonatomic, copy, readonly) RACTestObject *dynamicObjectProperty; - -// Returns a new object each time, with the integerValue set to 42. -- (RACTestObject *)dynamicObjectMethod; - -// Whether to allow -setNilValueForKey: to be invoked without throwing an -// exception. -@property (nonatomic, assign) BOOL catchSetNilValueForKey; - -// Has -setObjectValue:andIntegerValue: been called? -@property (nonatomic, assign) BOOL hasInvokedSetObjectValueAndIntegerValue; - -// Has -setObjectValue:andSecondObjectValue: been called? -@property (nonatomic, assign) BOOL hasInvokedSetObjectValueAndSecondObjectValue; - -- (void)setObjectValue:(id)objectValue andIntegerValue:(NSInteger)integerValue; -- (void)setObjectValue:(id)objectValue andSecondObjectValue:(id)secondObjectValue; - -// Returns a string of the form "objectValue: integerValue". -- (NSString *)combineObjectValue:(id)objectValue andIntegerValue:(NSInteger)integerValue; -- (NSString *)combineObjectValue:(id)objectValue andSecondObjectValue:(id)secondObjectValue; - -- (void)lifeIsGood:(id)sender; - -+ (void)lifeIsGood:(id)sender; - -- (NSRange)returnRangeValueWithObjectValue:(id)objectValue andIntegerValue:(NSInteger)integerValue; - -// Writes 5 to the int pointed to by intPointer. -- (void)write5ToIntPointer:(int *)intPointer; - -- (NSInteger)doubleInteger:(NSInteger)integer; -- (char *)doubleString:(char *)string; -- (const char *)doubleConstString:(const char *)string; -- (RACTestStruct)doubleStruct:(RACTestStruct)testStruct; - -- (dispatch_block_t)wrapBlock:(dispatch_block_t)block; - -@end diff --git a/ReactiveObjCTests/RACTestObject.m b/ReactiveObjCTests/RACTestObject.m deleted file mode 100644 index 66aac010a1..0000000000 --- a/ReactiveObjCTests/RACTestObject.m +++ /dev/null @@ -1,121 +0,0 @@ -// -// RACTestObject.m -// ReactiveObjC -// -// Created by Josh Abernathy on 9/18/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "RACTestObject.h" - -@implementation RACTestObject - -- (void)dealloc { - free(_charPointerValue); - free((void *)_constCharPointerValue); -} - -- (void)setNilValueForKey:(NSString *)key { - if (!self.catchSetNilValueForKey) [super setNilValueForKey:key]; -} - -- (void)setCharPointerValue:(char *)charPointerValue { - if (charPointerValue == _charPointerValue) return; - free(_charPointerValue); - _charPointerValue = strdup(charPointerValue); -} - -- (void)setConstCharPointerValue:(const char *)constCharPointerValue { - if (constCharPointerValue == _constCharPointerValue) return; - free((void *)_constCharPointerValue); - _constCharPointerValue = strdup(constCharPointerValue); -} - -- (void)setObjectValue:(id)objectValue andIntegerValue:(NSInteger)integerValue { - self.hasInvokedSetObjectValueAndIntegerValue = YES; - self.objectValue = objectValue; - self.integerValue = integerValue; -} - -- (void)setObjectValue:(id)objectValue andSecondObjectValue:(id)secondObjectValue { - self.hasInvokedSetObjectValueAndSecondObjectValue = YES; - self.objectValue = objectValue; - self.secondObjectValue = secondObjectValue; -} - -- (void)setSlowObjectValue:(id)value { - [NSThread sleepForTimeInterval:0.02]; - _slowObjectValue = value; -} - -- (NSString *)combineObjectValue:(id)objectValue andIntegerValue:(NSInteger)integerValue { - return [NSString stringWithFormat:@"%@: %ld", objectValue, (long)integerValue]; -} - -- (NSString *)combineObjectValue:(id)objectValue andSecondObjectValue:(id)secondObjectValue { - return [NSString stringWithFormat:@"%@: %@", objectValue, secondObjectValue]; -} - -- (void)lifeIsGood:(id)sender { - -} - -+ (void)lifeIsGood:(id)sender { - -} - -- (NSRange)returnRangeValueWithObjectValue:(id)objectValue andIntegerValue:(NSInteger)integerValue { - return NSMakeRange((NSUInteger)[objectValue integerValue], (NSUInteger)integerValue); -} - -- (RACTestObject *)dynamicObjectProperty { - return [self dynamicObjectMethod]; -} - -- (RACTestObject *)dynamicObjectMethod { - RACTestObject *testObject = [[RACTestObject alloc] init]; - testObject.integerValue = 42; - return testObject; -} - -- (void)write5ToIntPointer:(int *)intPointer { - NSCParameterAssert(intPointer != NULL); - *intPointer = 5; -} - -- (NSInteger)doubleInteger:(NSInteger)integer { - return integer * 2; -} - -- (char *)doubleString:(char *)string { - size_t doubledSize = strlen(string) * 2 + 1; - char *doubledString = malloc(sizeof(char) * doubledSize); - - doubledString[0] = '\0'; - strlcat(doubledString, string, doubledSize); - strlcat(doubledString, string, doubledSize); - - dispatch_async(dispatch_get_main_queue(), ^{ - free(doubledString); - }); - - return doubledString; -} - -- (const char *)doubleConstString:(const char *)string { - return [self doubleString:(char *)string]; -} - -- (RACTestStruct)doubleStruct:(RACTestStruct)testStruct { - testStruct.integerField *= 2; - testStruct.doubleField *= 2; - return testStruct; -} - -- (dispatch_block_t)wrapBlock:(dispatch_block_t)block { - return ^{ - block(); - }; -} - -@end diff --git a/ReactiveObjCTests/RACTestSchedulerSpec.m b/ReactiveObjCTests/RACTestSchedulerSpec.m deleted file mode 100644 index 2ae51739f9..0000000000 --- a/ReactiveObjCTests/RACTestSchedulerSpec.m +++ /dev/null @@ -1,178 +0,0 @@ -// -// RACTestSchedulerSpec.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-07-06. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACTestScheduler.h" - -QuickSpecBegin(RACTestSchedulerSpec) - -__block RACTestScheduler *scheduler; - -qck_beforeEach(^{ - scheduler = [[RACTestScheduler alloc] init]; - expect(scheduler).notTo(beNil()); -}); - -qck_it(@"should do nothing when stepping while empty", ^{ - [scheduler step]; - [scheduler step:5]; - [scheduler stepAll]; -}); - -qck_it(@"should execute the earliest enqueued block when stepping", ^{ - __block BOOL firstExecuted = NO; - [scheduler schedule:^{ - firstExecuted = YES; - }]; - - __block BOOL secondExecuted = NO; - [scheduler schedule:^{ - secondExecuted = YES; - }]; - - expect(@(firstExecuted)).to(beFalsy()); - expect(@(secondExecuted)).to(beFalsy()); - - [scheduler step]; - expect(@(firstExecuted)).to(beTruthy()); - expect(@(secondExecuted)).to(beFalsy()); - - [scheduler step]; - expect(@(secondExecuted)).to(beTruthy()); -}); - -qck_it(@"should step multiple times", ^{ - __block BOOL firstExecuted = NO; - [scheduler schedule:^{ - firstExecuted = YES; - }]; - - __block BOOL secondExecuted = NO; - [scheduler schedule:^{ - secondExecuted = YES; - }]; - - __block BOOL thirdExecuted = NO; - [scheduler schedule:^{ - thirdExecuted = YES; - }]; - - expect(@(firstExecuted)).to(beFalsy()); - expect(@(secondExecuted)).to(beFalsy()); - expect(@(thirdExecuted)).to(beFalsy()); - - [scheduler step:2]; - expect(@(firstExecuted)).to(beTruthy()); - expect(@(secondExecuted)).to(beTruthy()); - expect(@(thirdExecuted)).to(beFalsy()); - - [scheduler step:1]; - expect(@(thirdExecuted)).to(beTruthy()); -}); - -qck_it(@"should step through all scheduled blocks", ^{ - __block NSUInteger executions = 0; - for (NSUInteger i = 0; i < 10; i++) { - [scheduler schedule:^{ - executions++; - }]; - } - - expect(@(executions)).to(equal(@0)); - - [scheduler stepAll]; - expect(@(executions)).to(equal(@10)); -}); - -qck_it(@"should execute blocks in date order when stepping", ^{ - __block BOOL laterExecuted = NO; - [scheduler after:[NSDate distantFuture] schedule:^{ - laterExecuted = YES; - }]; - - __block BOOL earlierExecuted = NO; - [scheduler after:[NSDate dateWithTimeIntervalSinceNow:20] schedule:^{ - earlierExecuted = YES; - }]; - - expect(@(earlierExecuted)).to(beFalsy()); - expect(@(laterExecuted)).to(beFalsy()); - - [scheduler step]; - expect(@(earlierExecuted)).to(beTruthy()); - expect(@(laterExecuted)).to(beFalsy()); - - [scheduler step]; - expect(@(laterExecuted)).to(beTruthy()); -}); - -qck_it(@"should execute delayed blocks in date order when stepping", ^{ - __block BOOL laterExecuted = NO; - [scheduler afterDelay:100 schedule:^{ - laterExecuted = YES; - }]; - - __block BOOL earlierExecuted = NO; - [scheduler afterDelay:50 schedule:^{ - earlierExecuted = YES; - }]; - - expect(@(earlierExecuted)).to(beFalsy()); - expect(@(laterExecuted)).to(beFalsy()); - - [scheduler step]; - expect(@(earlierExecuted)).to(beTruthy()); - expect(@(laterExecuted)).to(beFalsy()); - - [scheduler step]; - expect(@(laterExecuted)).to(beTruthy()); -}); - -qck_it(@"should execute a repeating blocks in date order", ^{ - __block NSUInteger firstExecutions = 0; - [scheduler after:[NSDate dateWithTimeIntervalSinceNow:20] repeatingEvery:5 withLeeway:0 schedule:^{ - firstExecutions++; - }]; - - __block NSUInteger secondExecutions = 0; - [scheduler after:[NSDate dateWithTimeIntervalSinceNow:22] repeatingEvery:10 withLeeway:0 schedule:^{ - secondExecutions++; - }]; - - expect(@(firstExecutions)).to(equal(@0)); - expect(@(secondExecutions)).to(equal(@0)); - - // 20 ticks - [scheduler step]; - expect(@(firstExecutions)).to(equal(@1)); - expect(@(secondExecutions)).to(equal(@0)); - - // 22 ticks - [scheduler step]; - expect(@(firstExecutions)).to(equal(@1)); - expect(@(secondExecutions)).to(equal(@1)); - - // 25 ticks - [scheduler step]; - expect(@(firstExecutions)).to(equal(@2)); - expect(@(secondExecutions)).to(equal(@1)); - - // 30 ticks - [scheduler step]; - expect(@(firstExecutions)).to(equal(@3)); - expect(@(secondExecutions)).to(equal(@1)); - - // 32 ticks - [scheduler step]; - expect(@(firstExecutions)).to(equal(@3)); - expect(@(secondExecutions)).to(equal(@2)); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/RACTestUIButton.h b/ReactiveObjCTests/RACTestUIButton.h deleted file mode 100644 index 5160af112b..0000000000 --- a/ReactiveObjCTests/RACTestUIButton.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// RACTestUIButton.h -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-06-15. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -// Enables use of -sendActionsForControlEvents: in unit tests. -@interface RACTestUIButton : UIButton - -+ (instancetype)button; - -@end diff --git a/ReactiveObjCTests/RACTestUIButton.m b/ReactiveObjCTests/RACTestUIButton.m deleted file mode 100644 index cff413f683..0000000000 --- a/ReactiveObjCTests/RACTestUIButton.m +++ /dev/null @@ -1,27 +0,0 @@ -// -// RACTestUIButton.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2013-06-15. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "RACTestUIButton.h" - -@implementation RACTestUIButton - -+ (instancetype)button { - RACTestUIButton *button = [self buttonWithType:UIButtonTypeCustom]; - return button; -} - -// Required for unit testing – controls don't work normally -// outside of normal apps. --(void)sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Warc-performSelector-leaks" - [target performSelector:action withObject:self]; -#pragma clang diagnostic pop -} - -@end diff --git a/ReactiveObjCTests/RACTupleSpec.m b/ReactiveObjCTests/RACTupleSpec.m deleted file mode 100644 index c8658fbb5f..0000000000 --- a/ReactiveObjCTests/RACTupleSpec.m +++ /dev/null @@ -1,123 +0,0 @@ -// -// RACTupleSpec.m -// ReactiveObjC -// -// Created by Justin Spahr-Summers on 2012-12-12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACTuple.h" -#import "RACUnit.h" - -QuickSpecBegin(RACTupleSpec) - -qck_describe(@"RACTupleUnpack", ^{ - qck_it(@"should unpack a single value", ^{ - RACTupleUnpack(RACUnit *value) = [RACTuple tupleWithObjects:RACUnit.defaultUnit, nil]; - expect(value).to(equal(RACUnit.defaultUnit)); - }); - - qck_it(@"should translate RACTupleNil", ^{ - RACTupleUnpack(id value) = [RACTuple tupleWithObjects:RACTupleNil.tupleNil, nil]; - expect(value).to(beNil()); - }); - - qck_it(@"should unpack multiple values", ^{ - RACTupleUnpack(NSString *str, NSNumber *num) = [RACTuple tupleWithObjects:@"foobar", @5, nil]; - - expect(str).to(equal(@"foobar")); - expect(num).to(equal(@5)); - }); - - qck_it(@"should fill in missing values with nil", ^{ - RACTupleUnpack(NSString *str, NSNumber *num) = [RACTuple tupleWithObjects:@"foobar", nil]; - - expect(str).to(equal(@"foobar")); - expect(num).to(beNil()); - }); - - qck_it(@"should skip any values not assigned to", ^{ - RACTupleUnpack(NSString *str, NSNumber *num) = [RACTuple tupleWithObjects:@"foobar", @5, RACUnit.defaultUnit, nil]; - - expect(str).to(equal(@"foobar")); - expect(num).to(equal(@5)); - }); - - qck_it(@"should keep an unpacked value alive when captured in a block", ^{ - __weak id weakPtr = nil; - id (^block)(void) = nil; - - @autoreleasepool { - RACTupleUnpack(NSString *str) = [RACTuple tupleWithObjects:[[NSMutableString alloc] init], nil]; - - weakPtr = str; - expect(weakPtr).notTo(beNil()); - - block = [^{ - return str; - } copy]; - } - - expect(weakPtr).notTo(beNil()); - expect(block()).to(equal(weakPtr)); - }); -}); - -qck_describe(@"RACTuplePack", ^{ - qck_it(@"should pack a single value", ^{ - RACTuple *tuple = [RACTuple tupleWithObjects:RACUnit.defaultUnit, nil]; - expect(RACTuplePack(RACUnit.defaultUnit)).to(equal(tuple)); - }); - - qck_it(@"should translate nil", ^{ - RACTuple *tuple = [RACTuple tupleWithObjects:RACTupleNil.tupleNil, nil]; - expect(RACTuplePack(nil)).to(equal(tuple)); - }); - - qck_it(@"should pack multiple values", ^{ - NSString *string = @"foobar"; - NSNumber *number = @5; - RACTuple *tuple = [RACTuple tupleWithObjects:string, number, nil]; - expect(RACTuplePack(string, number)).to(equal(tuple)); - }); -}); - -qck_describe(@"-tupleByAddingObject:", ^{ - __block RACTuple *tuple; - - qck_beforeEach(^{ - tuple = RACTuplePack(@"foo", nil, @"bar"); - }); - - qck_it(@"should add a non-nil object", ^{ - RACTuple *newTuple = [tuple tupleByAddingObject:@"buzz"]; - expect(@(newTuple.count)).to(equal(@4)); - expect(newTuple[0]).to(equal(@"foo")); - expect(newTuple[1]).to(beNil()); - expect(newTuple[2]).to(equal(@"bar")); - expect(newTuple[3]).to(equal(@"buzz")); - }); - - qck_it(@"should add nil", ^{ - RACTuple *newTuple = [tuple tupleByAddingObject:nil]; - expect(@(newTuple.count)).to(equal(@4)); - expect(newTuple[0]).to(equal(@"foo")); - expect(newTuple[1]).to(beNil()); - expect(newTuple[2]).to(equal(@"bar")); - expect(newTuple[3]).to(beNil()); - }); - - qck_it(@"should add NSNull", ^{ - RACTuple *newTuple = [tuple tupleByAddingObject:NSNull.null]; - expect(@(newTuple.count)).to(equal(@4)); - expect(newTuple[0]).to(equal(@"foo")); - expect(newTuple[1]).to(beNil()); - expect(newTuple[2]).to(equal(@"bar")); - expect(newTuple[3]).to(equal(NSNull.null)); - }); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/UIActionSheetRACSupportSpec.m b/ReactiveObjCTests/UIActionSheetRACSupportSpec.m deleted file mode 100644 index 848fc4cde9..0000000000 --- a/ReactiveObjCTests/UIActionSheetRACSupportSpec.m +++ /dev/null @@ -1,39 +0,0 @@ -// -// UIActionSheetRACSupportSpec.m -// ReactiveObjC -// -// Created by Dave Lee on 2013-06-22. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACSignal.h" -#import "RACSignal+Operations.h" -#import "UIActionSheet+RACSignalSupport.h" - -QuickSpecBegin(UIActionSheetRACSupportSpec) - -qck_describe(@"-rac_buttonClickedSignal", ^{ - __block UIActionSheet *actionSheet; - - qck_beforeEach(^{ - actionSheet = [[UIActionSheet alloc] init]; - [actionSheet addButtonWithTitle:@"Button 0"]; - [actionSheet addButtonWithTitle:@"Button 1"]; - expect(actionSheet).notTo(beNil()); - }); - - qck_it(@"should send the index of the clicked button", ^{ - __block NSNumber *index = nil; - [actionSheet.rac_buttonClickedSignal subscribeNext:^(NSNumber *i) { - index = i; - }]; - - [actionSheet.delegate actionSheet:actionSheet clickedButtonAtIndex:1]; - expect(index).to(equal(@1)); - }); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/UIAlertViewRACSupportSpec.m b/ReactiveObjCTests/UIAlertViewRACSupportSpec.m deleted file mode 100644 index c2a7ff846b..0000000000 --- a/ReactiveObjCTests/UIAlertViewRACSupportSpec.m +++ /dev/null @@ -1,47 +0,0 @@ -// -// UIAlertViewRACSupportSpec.m -// ReactiveObjC -// -// Created by Henrik Hodne on 6/16/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import -#import "RACSignal.h" -#import "UIAlertView+RACSignalSupport.h" - -QuickSpecBegin(UIAlertViewRACSupportSpec) - -qck_describe(@"UIAlertView", ^{ - __block UIAlertView *alertView; - - qck_beforeEach(^{ - alertView = [[UIAlertView alloc] initWithFrame:CGRectZero]; - expect(alertView).notTo(beNil()); - }); - - qck_it(@"sends the index of the clicked button to the buttonClickedSignal when a button is clicked", ^{ - __block NSInteger index = -1; - [alertView.rac_buttonClickedSignal subscribeNext:^(NSNumber *sentIndex) { - index = sentIndex.integerValue; - }]; - - [alertView.delegate alertView:alertView clickedButtonAtIndex:2]; - expect(@(index)).to(equal(@2)); - }); - - qck_it(@"sends the index of the appropriate button to the willDismissSignal when dismissed programatically", ^{ - __block NSInteger index = -1; - [alertView.rac_willDismissSignal subscribeNext:^(NSNumber *sentIndex) { - index = sentIndex.integerValue; - }]; - - [alertView.delegate alertView:alertView willDismissWithButtonIndex:2]; - expect(@(index)).to(equal(@2)); - }); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/UIBarButtonItemRACSupportSpec.m b/ReactiveObjCTests/UIBarButtonItemRACSupportSpec.m deleted file mode 100644 index dd90fd2b96..0000000000 --- a/ReactiveObjCTests/UIBarButtonItemRACSupportSpec.m +++ /dev/null @@ -1,43 +0,0 @@ -// -// UIBarButtonItemRACSupportSpec.m -// ReactiveObjC -// -// Created by Kyle LeNeau on 4/13/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACControlCommandExamples.h" - -#import "UIBarButtonItem+RACCommandSupport.h" -#import "RACCommand.h" -#import "RACDisposable.h" - -QuickSpecBegin(UIBarButtonItemRACSupportSpec) - -qck_describe(@"UIBarButtonItem", ^{ - __block UIBarButtonItem *button; - - qck_beforeEach(^{ - button = [[UIBarButtonItem alloc] init]; - expect(button).notTo(beNil()); - }); - - qck_itBehavesLike(RACControlCommandExamples, ^{ - return @{ - RACControlCommandExampleControl: button, - RACControlCommandExampleActivateBlock: ^(UIBarButtonItem *button) { - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[button.target methodSignatureForSelector:button.action]]; - invocation.selector = button.action; - - id target = button.target; - [invocation setArgument:&target atIndex:2]; - [invocation invokeWithTarget:target]; - } - }; - }); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/UIButtonRACSupportSpec.m b/ReactiveObjCTests/UIButtonRACSupportSpec.m deleted file mode 100644 index e50176cd31..0000000000 --- a/ReactiveObjCTests/UIButtonRACSupportSpec.m +++ /dev/null @@ -1,42 +0,0 @@ -// -// UIButtonRACSupportSpec.m -// ReactiveObjC -// -// Created by Ash Furrow on 2013-06-06. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "RACControlCommandExamples.h" -#import "RACTestUIButton.h" - -#import "UIButton+RACCommandSupport.h" -#import "RACCommand.h" -#import "RACDisposable.h" - -QuickSpecBegin(UIButtonRACSupportSpec) - -qck_describe(@"UIButton", ^{ - __block UIButton *button; - - qck_beforeEach(^{ - button = [RACTestUIButton button]; - expect(button).notTo(beNil()); - }); - - qck_itBehavesLike(RACControlCommandExamples, ^{ - return @{ - RACControlCommandExampleControl: button, - RACControlCommandExampleActivateBlock: ^(UIButton *button) { - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Warc-performSelector-leaks" - [button sendActionsForControlEvents:UIControlEventTouchUpInside]; - #pragma clang diagnostic pop - } - }; - }); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/UIImagePickerControllerRACSupportSpec.m b/ReactiveObjCTests/UIImagePickerControllerRACSupportSpec.m deleted file mode 100644 index 52a2d9a0a9..0000000000 --- a/ReactiveObjCTests/UIImagePickerControllerRACSupportSpec.m +++ /dev/null @@ -1,54 +0,0 @@ -// -// UIImagePickerControllerRACSupportSpec.m -// ReactiveObjC -// -// Created by Timur Kuchkarov on 17.04.14. -// Copyright (c) 2014 GitHub, Inc. All rights reserved. -// - -#import -#import - -#import "UIImagePickerController+RACSignalSupport.h" -#import "RACSignal.h" - -QuickSpecBegin(UIImagePickerControllerRACSupportSpec) - -qck_describe(@"UIImagePickerController", ^{ - __block UIImagePickerController *imagePicker; - - qck_beforeEach(^{ - imagePicker = [[UIImagePickerController alloc] init]; - expect(imagePicker).notTo(beNil()); - }); - - qck_it(@"sends the user info dictionary after confirmation", ^{ - __block NSDictionary *selectedImageUserInfo = nil; - [imagePicker.rac_imageSelectedSignal subscribeNext:^(NSDictionary *userInfo) { - selectedImageUserInfo = userInfo; - }]; - - NSDictionary *info = @{ - UIImagePickerControllerMediaType: @"public.image", - UIImagePickerControllerMediaMetadata: @{} - }; - [imagePicker.delegate imagePickerController:imagePicker didFinishPickingMediaWithInfo:info]; - expect(selectedImageUserInfo).to(equal(info)); - }); - - qck_it(@"cancels image picking process", ^{ - __block BOOL didSend = NO; - __block BOOL didComplete = NO; - [imagePicker.rac_imageSelectedSignal subscribeNext:^(NSDictionary *userInfo) { - didSend = YES; - } completed:^{ - didComplete = YES; - }]; - - [imagePicker.delegate imagePickerControllerDidCancel:imagePicker]; - expect(@(didSend)).to(beFalsy()); - expect(@(didComplete)).to(beTruthy()); - }); -}); - -QuickSpecEnd diff --git a/ReactiveObjCTests/test-data.json b/ReactiveObjCTests/test-data.json deleted file mode 100644 index 0599ee5fe0..0000000000 --- a/ReactiveObjCTests/test-data.json +++ /dev/null @@ -1,5 +0,0 @@ -[ - { "item": 1 }, - { "item": 2 }, - { "item": 3 } -] From ee148d5d7c4d61569650fd67230c5cc2c14f3c4a Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Sun, 18 Sep 2016 10:22:40 +0900 Subject: [PATCH 0382/1028] Remove Instruments templates Those should be only in ReactiveObjC. --- Instruments/Disposable Growth.tracetemplate | Bin 25325 -> 0 bytes Instruments/README.md | 14 -------------- Instruments/Signal Events.tracetemplate | Bin 6971272 -> 0 bytes 3 files changed, 14 deletions(-) delete mode 100644 Instruments/Disposable Growth.tracetemplate delete mode 100644 Instruments/README.md delete mode 100644 Instruments/Signal Events.tracetemplate diff --git a/Instruments/Disposable Growth.tracetemplate b/Instruments/Disposable Growth.tracetemplate deleted file mode 100644 index 24dc61ce79aad5d6c328f80b28f47f0c6a3f7308..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25325 zcmdsf2VhfG+y6QD=HAejwrPtX*fh6Ey4%dAZ73ZXophuFXj!o|X&Xp~NeUE1PZU7} zCkUdV3>j`wQE{L+5Jgai3%5AHfg*|ug8%1cbXnh**Z=GLe&5&9kbCaEXZ)V$JmYuH zy)(L;4!1|8I)pHy5RC+gL9B3_@UaH@Y`e?t=f<7CDNi?G!7LYBPvCgq013LUUUVz5-mhmp+#sh zx*9D(*Px|n8M+o-hptCApc~OmXgOMeZbmE7Ds&6F4c&q6L~GGHv>t6hccG1F6WWZn zpu5pM=w5UmdJgSG&!ZR6i|8e^3%!hXqXXz5I)vUtZ=tu*JLp~XKKcTEiH@RU=qvO! zI*xupzoOIV4Ehb7#Rw0_;Wz?EViAtQ(KrT=!WUr)F2IG@fQ{IM&A12`;}Tqo%Wyd! zk1KE`uEN#04%g!e_+s3O+i(Z ep}o{KNVSK@{ED!c?=i*Lfq@vZnad^=u)@4)Nv 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%0A5t4WhDOGRi*7?m7>l?FON} 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

Rco|##VQ5@COfQEV?dE>kZ5Cc|S%D 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 z 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

r&V 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

oh)3e}EiA`b2GCdFi)`A` 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�bH*+ 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%0D7d10IPC3G? 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

w-12f{{q5vh#AA! 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

  • a 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�^R(B)%|x4xlsuk1WDKYpkAfbWTCAR6=f%m1zX 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

    (_ other: P) -> AndProperty where P.Value == Bool { - return AndProperty(terms: terms + [Property(other)]) - } - - /// Creates a new property with an additional conjunctive term. - public func and(_ other: Property) -> AndProperty { - return AndProperty(terms: terms + [other]) - } - - fileprivate init(terms: [Property]) { - self.terms = terms - } -} - -/// Specialized `PropertyType` for the disjunction of a set of boolean properties. -public class OrProperty: PropertyProtocol { - public let terms: [Property] - - public var value: Bool { - return terms.reduce(false) { $0 || $1.value } - } - - public var producer: SignalProducer { - let producers = terms.map { $0.producer } - return SignalProducer.combineLatest(producers).map { values in - return values.reduce(false) { $0 || $1 } - } - } - - public var signal: Signal { - let signals = terms.map { $0.signal } - return Signal.combineLatest(signals).map { values in - return values.reduce(false) { $0 || $1 } - } - } - - /// Creates a new property with an additional disjunctive term. - 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. - public func or(_ other: Property) -> OrProperty { - return OrProperty(terms: terms + [other]) - } - - fileprivate init(terms: [Property]) { - self.terms = terms - } -} - -/// Specialized `PropertyType` for the negation of a boolean property. -public class NotProperty: PropertyProtocol { - private let source: Property - private let invert: Bool - - public var value: Bool { - return source.value != invert - } - - public var producer: SignalProducer { - return source.producer.map { $0 != self.invert } - } - - public var signal: Signal { - return source.signal.map { $0 != self.invert } - } - - /// A negated property of `self`. - public func not() -> NotProperty { - return NotProperty(source: source, invert: !invert) - } - - fileprivate init(source: Property, invert: Bool) { - self.source = source - self.invert = invert - } -} diff --git a/Sources/Signal+Extensions.swift b/Sources/Signal+Extensions.swift deleted file mode 100644 index 2e796ea496..0000000000 --- a/Sources/Signal+Extensions.swift +++ /dev/null @@ -1,120 +0,0 @@ -// -// Signal.swift -// Rex -// -// Created by Neil Pankey on 5/9/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - -import ReactiveSwift -import ReactiveCocoa -import enum Result.NoError - -extension SignalProtocol { - - /// Applies `transform` to values from `signal` with non-`nil` results unwrapped and - /// forwared on the returned signal. - public func filterMap(_ transform: @escaping (Value) -> U?) -> Signal { - return Signal { observer in - return self.observe { event in - switch event { - case let .next(value): - if let mapped = transform(value) { - observer.sendNext(mapped) - } - case let .failed(error): - observer.sendFailed(error) - case .completed: - observer.sendCompleted() - case .interrupted: - observer.sendInterrupted() - } - } - } - } - - /// Returns a signal that drops `Error` sending `replacement` terminal event - /// instead, defaulting to `Completed`. - 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): - observer.sendNext(value) - case .failed: - observer.action(replacement) - case .completed: - observer.sendCompleted() - case .interrupted: - observer.sendInterrupted() - } - } - } - } - - /// Forwards events from `signal` until `interval`. Then if signal isn't completed yet, - /// terminates with `event` on `scheduler`. - /// - /// 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. - 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.addingTimeInterval(interval) - disposable += scheduler.schedule(after: date) { - observer.action(event) - } - - disposable += self.observe(observer) - return disposable - } - } - - /// Forwards a value and then mutes the signal by dropping all subsequent values - /// for `interval` seconds. Once time elapses the next new value will be forwarded - /// and repeat the muting process. Error events are immediately forwarded even while - /// the signal is muted. - /// - /// This operator could be used to coalesce multiple notifications in a short time - /// frame by only showing the first one. - public func mute(for interval: TimeInterval, clock: DateSchedulerProtocol) -> Signal { - precondition(interval > 0) - - var expires = clock.currentDate - return filter { _ in - let now = clock.currentDate - - if expires.compare(now) != .orderedDescending { - expires = now.addingTimeInterval(interval) - return true - } - return false - } - } -} - -extension SignalProtocol where Value: Sequence { - /// Returns a signal that flattens sequences of elements. The inverse of `collect`. - public func uncollect() -> Signal { - return Signal { observer in - return self.observe { event in - switch event { - case let .next(sequence): - sequence.forEach { observer.sendNext($0) } - case let .failed(error): - observer.sendFailed(error) - case .completed: - observer.sendCompleted() - case .interrupted: - observer.sendInterrupted() - } - } - } - } -} diff --git a/Sources/SignalProducer+Extensions.swift b/Sources/SignalProducer+Extensions.swift deleted file mode 100644 index 46f08dc5c6..0000000000 --- a/Sources/SignalProducer+Extensions.swift +++ /dev/null @@ -1,256 +0,0 @@ -// -// SignalProducer.swift -// Rex -// -// Created by Neil Pankey on 5/9/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - -import ReactiveSwift -import ReactiveCocoa -import enum Result.NoError - -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. - 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] = [:] - - let lock = NSRecursiveLock() - lock.name = "me.neilpa.rex.groupBy" - - self.start { event in - switch event { - case let .next(value): - let key = grouping(value) - - lock.lock() - var group = groups[key] - if group == nil { - let (producer, innerObserver) = SignalProducer.bufferingProducer(upTo: Int.max) - observer.sendNext(key, producer) - - groups[key] = innerObserver - group = innerObserver - } - lock.unlock() - - group!.sendNext(value) - - case let .failed(error): - observer.sendFailed(error) - groups.values.forEach { $0.sendFailed(error) } - - case .completed: - observer.sendCompleted() - groups.values.forEach { $0.sendCompleted() } - - case .interrupted: - observer.sendInterrupted() - groups.values.forEach { $0.sendInterrupted() } - } - } - } - } - - /// Applies `transform` to values from self with non-`nil` results unwrapped and - /// forwared on the returned producer. - 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`. - public func ignoreError(replacement: Event = .completed) -> SignalProducer { - precondition(replacement.isTerminating) - return lift { $0.ignoreError(replacement: replacement) } - } - - /// Forwards events from self until `interval`. Then if producer isn't completed yet, - /// terminates with `event` on `scheduler`. - /// - /// 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. - 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 - /// for `interval` seconds. Once time elapses the next new value will be forwarded - /// and repeat the muting process. Error events are immediately forwarded even while - /// the producer is muted. - /// - /// This operator could be used to coalesce multiple notifications in a short time - /// frame by only showing the first one. - public func mute(for interval: TimeInterval, clock: DateSchedulerProtocol) -> SignalProducer { - return lift { $0.mute(for: interval, clock: clock) } - } - - /// Delays the start of the producer by `interval` on the provided scheduler. - public func `defer`(by interval: TimeInterval, on scheduler: DateSchedulerProtocol) -> SignalProducer { - return SignalProducer.empty - .delay(interval, on: scheduler) - .concat(self.producer) - } - - /// Delays retrying on failure by `interval` up to `count` attempts. - public func deferredRetry(_ interval: TimeInterval, on scheduler: DateSchedulerProtocol, count: Int = .max) -> SignalProducer { - precondition(count >= 0) - - if count == 0 { - return producer - } - - var retries = count - return flatMapError { error in - // The final attempt shouldn't defer the error if it fails - var producer = SignalProducer(error: error) - if retries > 0 { - producer = producer.defer(by: interval, on: scheduler) - } - - retries -= 1 - return producer - } - .retry(upTo: count) - } -} - -extension SignalProducerProtocol where Value: Sequence { - /// Returns a producer that flattens sequences of elements. The inverse of `collect`. - 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.. { .empty } - - var started = false - action - .rex_started - .observeNext { started = true } - - action - .apply() - .start() - - XCTAssertTrue(started) - } - - func testCompleted() { - let (signal, observer) = Signal.pipe() - let producer = SignalProducer(signal: signal) - - let action = Action { producer } - - var completed = false - action - .rex_completed - .observeNext { completed = true } - - action - .apply() - .start() - - observer.sendNext(1) - XCTAssertFalse(completed) - - observer.sendCompleted() - XCTAssertTrue(completed) - } - - func testCompletedOnFailed() { - let (signal, observer) = Signal.pipe() - let producer = SignalProducer(signal: signal) - - let action = Action { producer } - - var completed = false - action - .rex_completed - .observeNext { completed = true } - - action - .apply() - .start() - - observer.sendFailed(.unknown) - XCTAssertFalse(completed) - } -} diff --git a/Tests/ReactiveSwiftTests/PropertyTests.swift b/Tests/ReactiveSwiftTests/PropertyTests.swift deleted file mode 100644 index e198f15f3f..0000000000 --- a/Tests/ReactiveSwiftTests/PropertyTests.swift +++ /dev/null @@ -1,89 +0,0 @@ -// -// PropertyTests.swift -// Rex -// -// Created by Neil Pankey on 10/17/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - -import ReactiveSwift -import XCTest -import enum Result.NoError - -final class PropertyTests: XCTestCase { - - func testAndProperty() { - let lhs = MutableProperty(false), rhs = MutableProperty(false) - let and = lhs.and(rhs) - - var current: Bool! - and.producer.startWithNext { current = $0 } - - XCTAssertFalse(and.value) - XCTAssertFalse(current!) - - lhs.value = true - XCTAssertFalse(and.value) - XCTAssertFalse(current!) - - rhs.value = true - XCTAssertTrue(and.value) - XCTAssertTrue(current!) - - let (signal, pipe) = Signal.pipe() - let and2 = and.and(Property(initial: false, then: signal)) - and2.producer.startWithNext { current = $0 } - - XCTAssertFalse(and2.value) - XCTAssertFalse(current!) - - pipe.sendNext(true) - XCTAssertTrue(and2.value) - XCTAssertTrue(current!) - } - - func testOrProperty() { - let lhs = MutableProperty(true), rhs = MutableProperty(true) - let or = lhs.or(rhs) - - var current: Bool! - or.producer.startWithNext { current = $0 } - - XCTAssertTrue(or.value) - XCTAssertTrue(current!) - - lhs.value = false - XCTAssertTrue(or.value) - XCTAssertTrue(current!) - - rhs.value = false - XCTAssertFalse(or.value) - XCTAssertFalse(current!) - - let (signal, pipe) = Signal.pipe() - let or2 = or.or(Property(initial: true, then: signal)) - or2.producer.startWithNext { current = $0 } - - XCTAssertTrue(or2.value) - XCTAssertTrue(current!) - - pipe.sendNext(false) - XCTAssertFalse(or2.value) - XCTAssertFalse(current!) - } - - func testNotProperty() { - let source = MutableProperty(false) - let not = source.not() - - var current: Bool! - not.producer.startWithNext { current = $0 } - - XCTAssertTrue(not.value) - XCTAssertTrue(current!) - - source.value = true - XCTAssertFalse(not.value) - XCTAssertFalse(current!) - } -} diff --git a/Tests/ReactiveSwiftTests/SignalProducerTests.swift b/Tests/ReactiveSwiftTests/SignalProducerTests.swift deleted file mode 100644 index 97b1943b9d..0000000000 --- a/Tests/ReactiveSwiftTests/SignalProducerTests.swift +++ /dev/null @@ -1,162 +0,0 @@ -// -// SignalProducerTests.swift -// Rex -// -// Created by Neil Pankey on 5/9/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - -import ReactiveSwift -import XCTest -import enum Result.NoError - -final class SignalProducerTests: XCTestCase { - - func testGroupBy() { - let (signal, observer) = Signal.pipe() - let producer = SignalProducer(signal: signal) - - var evens: [Int] = [] - var odds: [Int] = [] - let disposable = CompositeDisposable() - var interrupted = false - var completed = false - - disposable += producer - .group { $0 % 2 == 0 } - .start(Observer(next: { key, group in - if key { - group.startWithNext { evens.append($0) } - } else { - group.startWithNext { odds.append($0) } - } - },completed: { - completed = true - }, interrupted: { - interrupted = true - })) - - observer.sendNext(1) - XCTAssert(evens == []) - XCTAssert(odds == [1]) - - observer.sendNext(2) - XCTAssert(evens == [2]) - XCTAssert(odds == [1]) - - observer.sendNext(3) - XCTAssert(evens == [2]) - XCTAssert(odds == [1, 3]) - - disposable.dispose() - - observer.sendNext(1) - XCTAssert(interrupted) - XCTAssertFalse(completed) - } - - func testDeferred() { - let scheduler = TestScheduler() - - var deferred = false - let producer = SignalProducer<(), NoError> { _ in deferred = true } - - var started = false - producer - .defer(by: 1, on: scheduler) - .on(started: { started = true }) - .start() - - XCTAssertTrue(started) - XCTAssertFalse(deferred) - - scheduler.advance() - XCTAssertFalse(deferred) - - scheduler.advance(by: 0.9) - XCTAssertFalse(deferred) - - scheduler.advance(by: 0.2) - XCTAssertTrue(deferred) - } - - func testDeferredRetry() { - let scheduler = TestScheduler() - - var count = 0 - let producer = SignalProducer { observer, _ in - if count < 2 { - scheduler.schedule { observer.sendNext(count) } - scheduler.schedule { observer.sendFailed(.default) } - } else { - scheduler.schedule { observer.sendCompleted() } - } - count += 1 - } - - var value = -1 - var completed = false - producer - .deferredRetry(1, on: scheduler) - .start(Observer( - next: { value = $0 }, - completed: { completed = true } - )) - - XCTAssertEqual(count, 1) - XCTAssertEqual(value, -1) - - scheduler.advance() - XCTAssertEqual(count, 1) - XCTAssertEqual(value, 1) - XCTAssertFalse(completed) - - scheduler.advance(by: 1) - XCTAssertEqual(count, 2) - XCTAssertEqual(value, 2) - XCTAssertFalse(completed) - - scheduler.advance(by: 1) - XCTAssertEqual(count, 3) - XCTAssertEqual(value, 2) - XCTAssertTrue(completed) - } - - func testDeferredRetryFailure() { - let scheduler = TestScheduler() - - var count = 0 - let producer = SignalProducer { observer, _ in - observer.sendNext(count) - observer.sendFailed(.default) - count += 1 - } - - var value = -1 - var failed = false - producer - .deferredRetry(1, on: scheduler, count: 2) - .start(Observer( - next: { value = $0 }, - failed: { _ in failed = true } - )) - - XCTAssertEqual(count, 1) - XCTAssertEqual(value, 0) - - scheduler.advance() - XCTAssertEqual(count, 1) - XCTAssertEqual(value, 0) - XCTAssertFalse(failed) - - scheduler.advance(by: 1) - XCTAssertEqual(count, 2) - XCTAssertEqual(value, 1) - XCTAssertFalse(failed) - - scheduler.advance(by: 1) - XCTAssertEqual(count, 3) - XCTAssertEqual(value, 2) - XCTAssertTrue(failed) - } -} diff --git a/Tests/ReactiveSwiftTests/SignalTests.swift b/Tests/ReactiveSwiftTests/SignalTests.swift deleted file mode 100644 index b51b1a137d..0000000000 --- a/Tests/ReactiveSwiftTests/SignalTests.swift +++ /dev/null @@ -1,190 +0,0 @@ -// -// SignalTests.swift -// Rex -// -// Created by Neil Pankey on 5/9/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - -import ReactiveSwift -import XCTest -import enum Result.NoError - -final class SignalTests: XCTestCase { - - func testFilterMap() { - let (signal, observer) = Signal.pipe() - var values: [String] = [] - - signal - .filterMap { - return $0 % 2 == 0 ? String($0) : nil - } - .observeNext { values.append($0) } - - observer.sendNext(1) - XCTAssert(values == []) - - observer.sendNext(2) - XCTAssert(values == ["2"]) - - observer.sendNext(3) - XCTAssert(values == ["2"]) - - observer.sendNext(6) - XCTAssert(values == ["2", "6"]) - } - - func testIgnoreErrorCompletion() { - let (signal, observer) = Signal.pipe() - var completed = false - - signal - .ignoreError() - .observeCompleted { completed = true } - - observer.sendNext(1) - XCTAssertFalse(completed) - - observer.sendFailed(.default) - XCTAssertTrue(completed) - } - - func testIgnoreErrorInterruption() { - let (signal, observer) = Signal.pipe() - var interrupted = false - - signal - .ignoreError(replacement: .interrupted) - .observeInterrupted { interrupted = true } - - observer.sendNext(1) - XCTAssertFalse(interrupted) - - observer.sendFailed(.default) - XCTAssertTrue(interrupted) - } - - func testTimeoutAfterTerminating() { - let scheduler = TestScheduler() - let (signal, observer) = Signal.pipe() - var interrupted = false - var completed = false - - signal - .timeout(after: 2, with: .interrupted, on: scheduler) - .observe(Observer( - completed: { completed = true }, - interrupted: { interrupted = true } - )) - - scheduler.schedule(after: 1) { observer.sendCompleted() } - - XCTAssertFalse(interrupted) - XCTAssertFalse(completed) - - scheduler.run() - XCTAssertTrue(completed) - XCTAssertFalse(interrupted) - } - - func testTimeoutAfterTimingOut() { - let scheduler = TestScheduler() - let (signal, observer) = Signal.pipe() - var interrupted = false - var completed = false - - signal - .timeout(after: 2, with: .interrupted, on: scheduler) - .observe(Observer( - completed: { completed = true }, - interrupted: { interrupted = true } - )) - - scheduler.schedule(after: 3) { observer.sendCompleted() } - - XCTAssertFalse(interrupted) - XCTAssertFalse(completed) - - scheduler.run() - XCTAssertTrue(interrupted) - XCTAssertFalse(completed) - } - - func testUncollect() { - let (signal, observer) = Signal<[Int], NoError>.pipe() - var values: [Int] = [] - - signal - .uncollect() - .observeNext { values.append($0) } - - observer.sendNext([]) - XCTAssert(values.isEmpty) - - observer.sendNext([1]) - XCTAssert(values == [1]) - - observer.sendNext([2, 3]) - XCTAssert(values == [1, 2, 3]) - } - - func testMuteForValues() { - let scheduler = TestScheduler() - let (signal, observer) = Signal.pipe() - var value = -1 - - signal - .mute(for: 1, clock: scheduler) - .observeNext { value = $0 } - - scheduler.schedule { observer.sendNext(1) } - scheduler.advance() - XCTAssertEqual(value, 1) - - scheduler.schedule { observer.sendNext(2) } - scheduler.advance() - XCTAssertEqual(value, 1) - - scheduler.schedule { observer.sendNext(3) } - scheduler.schedule { observer.sendNext(4) } - scheduler.advance() - XCTAssertEqual(value, 1) - - scheduler.advance(by: 1) - XCTAssertEqual(value, 1) - - scheduler.schedule { observer.sendNext(5) } - scheduler.schedule { observer.sendNext(6) } - scheduler.advance() - XCTAssertEqual(value, 5) - } - - func testMuteForFailure() { - let scheduler = TestScheduler() - let (signal, observer) = Signal.pipe() - var value = -1 - var failed = false - - signal - .mute(for: 1, clock: scheduler) - .observe(Observer( - next: { value = $0 }, - failed: { _ in failed = true } - )) - - scheduler.schedule { observer.sendNext(1) } - scheduler.advance() - XCTAssertEqual(value, 1) - - scheduler.schedule { observer.sendNext(2) } - scheduler.schedule { observer.sendFailed(.default) } - scheduler.advance() - XCTAssertTrue(failed) - XCTAssertEqual(value, 1) - } -} - -enum TestError: Error { - case `default` -} From 3d673c81a66491f08a6326ad5100f5708477eff5 Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 22 Sep 2016 12:34:40 +0200 Subject: [PATCH 0388/1028] Incorporate new source files into the project. --- ReactiveCocoa.xcodeproj/project.pbxproj | 264 ++++++++++++++++++ ...ol+EnableSendActionsForControlEvents.swift | 0 2 files changed, 264 insertions(+) rename ReactiveCocoaTests/{Helpers => UIKit}/UIControl+EnableSendActionsForControlEvents.swift (100%) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 265bc0ba20..190d418859 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -21,6 +21,87 @@ 7DFBED211CDB8D8300EE435B /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 7DFBED281CDB8DE300EE435B /* DynamicPropertySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */; }; 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */; }; + 9A1D05E01D93E99100ACF44C /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* Association.swift */; }; + 9A1D05E11D93E99100ACF44C /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* Association.swift */; }; + 9A1D05E21D93E99100ACF44C /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* Association.swift */; }; + 9A1D05E31D93E99100ACF44C /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* Association.swift */; }; + 9A1D05E51D93E9E200ACF44C /* Reusable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05E41D93E9E200ACF44C /* Reusable.swift */; }; + 9A1D05E61D93E9E200ACF44C /* Reusable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05E41D93E9E200ACF44C /* Reusable.swift */; }; + 9A1D05E71D93E9E200ACF44C /* Reusable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05E41D93E9E200ACF44C /* Reusable.swift */; }; + 9A1D05E81D93E9E200ACF44C /* Reusable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05E41D93E9E200ACF44C /* Reusable.swift */; }; + 9A1D05FE1D93E9F900ACF44C /* NSTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05EA1D93E9F100ACF44C /* NSTextField.swift */; }; + 9A1D05FF1D93EA0000ACF44C /* UIActivityIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05EC1D93E9F100ACF44C /* UIActivityIndicatorView.swift */; }; + 9A1D06001D93EA0000ACF44C /* UIBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05ED1D93E9F100ACF44C /* UIBarButtonItem.swift */; }; + 9A1D06011D93EA0000ACF44C /* UIBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05EE1D93E9F100ACF44C /* UIBarItem.swift */; }; + 9A1D06021D93EA0000ACF44C /* UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05EF1D93E9F100ACF44C /* UIButton.swift */; }; + 9A1D06031D93EA0000ACF44C /* UICollectionReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F01D93E9F100ACF44C /* UICollectionReusableView.swift */; }; + 9A1D06041D93EA0000ACF44C /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F11D93E9F100ACF44C /* UIControl.swift */; }; + 9A1D06051D93EA0000ACF44C /* UIDatePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F21D93E9F100ACF44C /* UIDatePicker.swift */; }; + 9A1D06061D93EA0000ACF44C /* UIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F31D93E9F100ACF44C /* UIImageView.swift */; }; + 9A1D06071D93EA0000ACF44C /* UILabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F41D93E9F100ACF44C /* UILabel.swift */; }; + 9A1D06081D93EA0000ACF44C /* UIProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F51D93E9F100ACF44C /* UIProgressView.swift */; }; + 9A1D06091D93EA0000ACF44C /* UISegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F61D93E9F100ACF44C /* UISegmentedControl.swift */; }; + 9A1D060A1D93EA0000ACF44C /* UISwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F71D93E9F100ACF44C /* UISwitch.swift */; }; + 9A1D060B1D93EA0000ACF44C /* UITableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F81D93E9F100ACF44C /* UITableViewCell.swift */; }; + 9A1D060C1D93EA0000ACF44C /* UITableViewHeaderFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F91D93E9F100ACF44C /* UITableViewHeaderFooterView.swift */; }; + 9A1D060D1D93EA0000ACF44C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05FA1D93E9F100ACF44C /* UITextField.swift */; }; + 9A1D060E1D93EA0000ACF44C /* UITextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05FB1D93E9F100ACF44C /* UITextView.swift */; }; + 9A1D060F1D93EA0000ACF44C /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05FC1D93E9F100ACF44C /* UIView.swift */; }; + 9A1D06101D93EA0000ACF44C /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05FD1D93E9F100ACF44C /* UIViewController.swift */; }; + 9A1D06111D93EA0100ACF44C /* UIActivityIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05EC1D93E9F100ACF44C /* UIActivityIndicatorView.swift */; }; + 9A1D06121D93EA0100ACF44C /* UIBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05ED1D93E9F100ACF44C /* UIBarButtonItem.swift */; }; + 9A1D06131D93EA0100ACF44C /* UIBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05EE1D93E9F100ACF44C /* UIBarItem.swift */; }; + 9A1D06141D93EA0100ACF44C /* UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05EF1D93E9F100ACF44C /* UIButton.swift */; }; + 9A1D06151D93EA0100ACF44C /* UICollectionReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F01D93E9F100ACF44C /* UICollectionReusableView.swift */; }; + 9A1D06161D93EA0100ACF44C /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F11D93E9F100ACF44C /* UIControl.swift */; }; + 9A1D06171D93EA0100ACF44C /* UIDatePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F21D93E9F100ACF44C /* UIDatePicker.swift */; }; + 9A1D06181D93EA0100ACF44C /* UIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F31D93E9F100ACF44C /* UIImageView.swift */; }; + 9A1D06191D93EA0100ACF44C /* UILabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F41D93E9F100ACF44C /* UILabel.swift */; }; + 9A1D061A1D93EA0100ACF44C /* UIProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F51D93E9F100ACF44C /* UIProgressView.swift */; }; + 9A1D061B1D93EA0100ACF44C /* UISegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F61D93E9F100ACF44C /* UISegmentedControl.swift */; }; + 9A1D061C1D93EA0100ACF44C /* UISwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F71D93E9F100ACF44C /* UISwitch.swift */; }; + 9A1D061D1D93EA0100ACF44C /* UITableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F81D93E9F100ACF44C /* UITableViewCell.swift */; }; + 9A1D061E1D93EA0100ACF44C /* UITableViewHeaderFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F91D93E9F100ACF44C /* UITableViewHeaderFooterView.swift */; }; + 9A1D061F1D93EA0100ACF44C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05FA1D93E9F100ACF44C /* UITextField.swift */; }; + 9A1D06201D93EA0100ACF44C /* UITextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05FB1D93E9F100ACF44C /* UITextView.swift */; }; + 9A1D06211D93EA0100ACF44C /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05FC1D93E9F100ACF44C /* UIView.swift */; }; + 9A1D06221D93EA0100ACF44C /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05FD1D93E9F100ACF44C /* UIViewController.swift */; }; + 9A1D06361D93EA7E00ACF44C /* UIActivityIndicatorViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06241D93EA7E00ACF44C /* UIActivityIndicatorViewTests.swift */; }; + 9A1D06371D93EA7E00ACF44C /* UIActivityIndicatorViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06241D93EA7E00ACF44C /* UIActivityIndicatorViewTests.swift */; }; + 9A1D06381D93EA7E00ACF44C /* UIBarButtonItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06251D93EA7E00ACF44C /* UIBarButtonItemTests.swift */; }; + 9A1D06391D93EA7E00ACF44C /* UIBarButtonItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06251D93EA7E00ACF44C /* UIBarButtonItemTests.swift */; }; + 9A1D063A1D93EA7E00ACF44C /* UIButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06261D93EA7E00ACF44C /* UIButtonTests.swift */; }; + 9A1D063B1D93EA7E00ACF44C /* UIButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06261D93EA7E00ACF44C /* UIButtonTests.swift */; }; + 9A1D063C1D93EA7E00ACF44C /* UICollectionReusableViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06271D93EA7E00ACF44C /* UICollectionReusableViewTests.swift */; }; + 9A1D063D1D93EA7E00ACF44C /* UICollectionReusableViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06271D93EA7E00ACF44C /* UICollectionReusableViewTests.swift */; }; + 9A1D063E1D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06281D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift */; }; + 9A1D063F1D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06281D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift */; }; + 9A1D06401D93EA7E00ACF44C /* UIControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06291D93EA7E00ACF44C /* UIControlTests.swift */; }; + 9A1D06411D93EA7E00ACF44C /* UIControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06291D93EA7E00ACF44C /* UIControlTests.swift */; }; + 9A1D06421D93EA7E00ACF44C /* UIDatePickerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062A1D93EA7E00ACF44C /* UIDatePickerTests.swift */; }; + 9A1D06431D93EA7E00ACF44C /* UIDatePickerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062A1D93EA7E00ACF44C /* UIDatePickerTests.swift */; }; + 9A1D06441D93EA7E00ACF44C /* UIImageViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062B1D93EA7E00ACF44C /* UIImageViewTests.swift */; }; + 9A1D06451D93EA7E00ACF44C /* UIImageViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062B1D93EA7E00ACF44C /* UIImageViewTests.swift */; }; + 9A1D06461D93EA7E00ACF44C /* UILabelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062C1D93EA7E00ACF44C /* UILabelTests.swift */; }; + 9A1D06471D93EA7E00ACF44C /* UILabelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062C1D93EA7E00ACF44C /* UILabelTests.swift */; }; + 9A1D06481D93EA7E00ACF44C /* UIProgressViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062D1D93EA7E00ACF44C /* UIProgressViewTests.swift */; }; + 9A1D06491D93EA7E00ACF44C /* UIProgressViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062D1D93EA7E00ACF44C /* UIProgressViewTests.swift */; }; + 9A1D064A1D93EA7E00ACF44C /* UISegmentedControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062E1D93EA7E00ACF44C /* UISegmentedControlTests.swift */; }; + 9A1D064B1D93EA7E00ACF44C /* UISegmentedControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062E1D93EA7E00ACF44C /* UISegmentedControlTests.swift */; }; + 9A1D064C1D93EA7E00ACF44C /* UISwitchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062F1D93EA7E00ACF44C /* UISwitchTests.swift */; }; + 9A1D064D1D93EA7E00ACF44C /* UISwitchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062F1D93EA7E00ACF44C /* UISwitchTests.swift */; }; + 9A1D064E1D93EA7E00ACF44C /* UITableViewCellTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06301D93EA7E00ACF44C /* UITableViewCellTests.swift */; }; + 9A1D064F1D93EA7E00ACF44C /* UITableViewCellTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06301D93EA7E00ACF44C /* UITableViewCellTests.swift */; }; + 9A1D06501D93EA7E00ACF44C /* UITableViewHeaderFooterViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06311D93EA7E00ACF44C /* UITableViewHeaderFooterViewTests.swift */; }; + 9A1D06511D93EA7E00ACF44C /* UITableViewHeaderFooterViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06311D93EA7E00ACF44C /* UITableViewHeaderFooterViewTests.swift */; }; + 9A1D06521D93EA7E00ACF44C /* UITextFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06321D93EA7E00ACF44C /* UITextFieldTests.swift */; }; + 9A1D06531D93EA7E00ACF44C /* UITextFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06321D93EA7E00ACF44C /* UITextFieldTests.swift */; }; + 9A1D06541D93EA7E00ACF44C /* UITextViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06331D93EA7E00ACF44C /* UITextViewTests.swift */; }; + 9A1D06551D93EA7E00ACF44C /* UITextViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06331D93EA7E00ACF44C /* UITextViewTests.swift */; }; + 9A1D06561D93EA7E00ACF44C /* UIViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06341D93EA7E00ACF44C /* UIViewControllerTests.swift */; }; + 9A1D06571D93EA7E00ACF44C /* UIViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06341D93EA7E00ACF44C /* UIViewControllerTests.swift */; }; + 9A1D06581D93EA7E00ACF44C /* UIViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06351D93EA7E00ACF44C /* UIViewTests.swift */; }; + 9A1D06591D93EA7E00ACF44C /* UIViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06351D93EA7E00ACF44C /* UIViewTests.swift */; }; 9A1E72BA1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; 9A1E72BB1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; 9A1E72BC1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; @@ -139,6 +220,45 @@ 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Framework.xcconfig"; sourceTree = ""; }; 57A4D2471BA13F9700F7D4B1 /* tvOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-StaticLibrary.xcconfig"; sourceTree = ""; }; 7DFBED031CDB8C9500EE435B /* ReactiveCocoaTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveCocoaTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 9A1D05DF1D93E99100ACF44C /* Association.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Association.swift; sourceTree = ""; }; + 9A1D05E41D93E9E200ACF44C /* Reusable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reusable.swift; sourceTree = ""; }; + 9A1D05EA1D93E9F100ACF44C /* NSTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSTextField.swift; sourceTree = ""; }; + 9A1D05EC1D93E9F100ACF44C /* UIActivityIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorView.swift; sourceTree = ""; }; + 9A1D05ED1D93E9F100ACF44C /* UIBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIBarButtonItem.swift; sourceTree = ""; }; + 9A1D05EE1D93E9F100ACF44C /* UIBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIBarItem.swift; sourceTree = ""; }; + 9A1D05EF1D93E9F100ACF44C /* UIButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIButton.swift; sourceTree = ""; }; + 9A1D05F01D93E9F100ACF44C /* UICollectionReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UICollectionReusableView.swift; sourceTree = ""; }; + 9A1D05F11D93E9F100ACF44C /* UIControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIControl.swift; sourceTree = ""; }; + 9A1D05F21D93E9F100ACF44C /* UIDatePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIDatePicker.swift; sourceTree = ""; }; + 9A1D05F31D93E9F100ACF44C /* UIImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIImageView.swift; sourceTree = ""; }; + 9A1D05F41D93E9F100ACF44C /* UILabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UILabel.swift; sourceTree = ""; }; + 9A1D05F51D93E9F100ACF44C /* UIProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIProgressView.swift; sourceTree = ""; }; + 9A1D05F61D93E9F100ACF44C /* UISegmentedControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UISegmentedControl.swift; sourceTree = ""; }; + 9A1D05F71D93E9F100ACF44C /* UISwitch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UISwitch.swift; sourceTree = ""; }; + 9A1D05F81D93E9F100ACF44C /* UITableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITableViewCell.swift; sourceTree = ""; }; + 9A1D05F91D93E9F100ACF44C /* UITableViewHeaderFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITableViewHeaderFooterView.swift; sourceTree = ""; }; + 9A1D05FA1D93E9F100ACF44C /* UITextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITextField.swift; sourceTree = ""; }; + 9A1D05FB1D93E9F100ACF44C /* UITextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITextView.swift; sourceTree = ""; }; + 9A1D05FC1D93E9F100ACF44C /* UIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = ""; }; + 9A1D05FD1D93E9F100ACF44C /* UIViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = ""; }; + 9A1D06241D93EA7E00ACF44C /* UIActivityIndicatorViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorViewTests.swift; sourceTree = ""; }; + 9A1D06251D93EA7E00ACF44C /* UIBarButtonItemTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBarButtonItemTests.swift; sourceTree = ""; }; + 9A1D06261D93EA7E00ACF44C /* UIButtonTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIButtonTests.swift; sourceTree = ""; }; + 9A1D06271D93EA7E00ACF44C /* UICollectionReusableViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UICollectionReusableViewTests.swift; sourceTree = ""; }; + 9A1D06281D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIControl+EnableSendActionsForControlEvents.swift"; sourceTree = ""; }; + 9A1D06291D93EA7E00ACF44C /* UIControlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIControlTests.swift; sourceTree = ""; }; + 9A1D062A1D93EA7E00ACF44C /* UIDatePickerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIDatePickerTests.swift; sourceTree = ""; }; + 9A1D062B1D93EA7E00ACF44C /* UIImageViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageViewTests.swift; sourceTree = ""; }; + 9A1D062C1D93EA7E00ACF44C /* UILabelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabelTests.swift; sourceTree = ""; }; + 9A1D062D1D93EA7E00ACF44C /* UIProgressViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIProgressViewTests.swift; sourceTree = ""; }; + 9A1D062E1D93EA7E00ACF44C /* UISegmentedControlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISegmentedControlTests.swift; sourceTree = ""; }; + 9A1D062F1D93EA7E00ACF44C /* UISwitchTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISwitchTests.swift; sourceTree = ""; }; + 9A1D06301D93EA7E00ACF44C /* UITableViewCellTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewCellTests.swift; sourceTree = ""; }; + 9A1D06311D93EA7E00ACF44C /* UITableViewHeaderFooterViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewHeaderFooterViewTests.swift; sourceTree = ""; }; + 9A1D06321D93EA7E00ACF44C /* UITextFieldTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextFieldTests.swift; sourceTree = ""; }; + 9A1D06331D93EA7E00ACF44C /* UITextViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextViewTests.swift; sourceTree = ""; }; + 9A1D06341D93EA7E00ACF44C /* UIViewControllerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewControllerTests.swift; sourceTree = ""; }; + 9A1D06351D93EA7E00ACF44C /* UIViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewTests.swift; sourceTree = ""; }; 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyValueObservingSpec.swift; sourceTree = ""; }; 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+KeyValueObserving.swift"; sourceTree = ""; }; A97451331B3A935E00F48E55 /* watchOS-Application.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Application.xcconfig"; sourceTree = ""; }; @@ -275,6 +395,64 @@ path = tvOS; sourceTree = ""; }; + 9A1D05E91D93E9F100ACF44C /* AppKit */ = { + isa = PBXGroup; + children = ( + 9A1D05EA1D93E9F100ACF44C /* NSTextField.swift */, + ); + path = AppKit; + sourceTree = ""; + }; + 9A1D05EB1D93E9F100ACF44C /* UIKit */ = { + isa = PBXGroup; + children = ( + 9A1D05EC1D93E9F100ACF44C /* UIActivityIndicatorView.swift */, + 9A1D05ED1D93E9F100ACF44C /* UIBarButtonItem.swift */, + 9A1D05EE1D93E9F100ACF44C /* UIBarItem.swift */, + 9A1D05EF1D93E9F100ACF44C /* UIButton.swift */, + 9A1D05F01D93E9F100ACF44C /* UICollectionReusableView.swift */, + 9A1D05F11D93E9F100ACF44C /* UIControl.swift */, + 9A1D05F21D93E9F100ACF44C /* UIDatePicker.swift */, + 9A1D05F31D93E9F100ACF44C /* UIImageView.swift */, + 9A1D05F41D93E9F100ACF44C /* UILabel.swift */, + 9A1D05F51D93E9F100ACF44C /* UIProgressView.swift */, + 9A1D05F61D93E9F100ACF44C /* UISegmentedControl.swift */, + 9A1D05F71D93E9F100ACF44C /* UISwitch.swift */, + 9A1D05F81D93E9F100ACF44C /* UITableViewCell.swift */, + 9A1D05F91D93E9F100ACF44C /* UITableViewHeaderFooterView.swift */, + 9A1D05FA1D93E9F100ACF44C /* UITextField.swift */, + 9A1D05FB1D93E9F100ACF44C /* UITextView.swift */, + 9A1D05FC1D93E9F100ACF44C /* UIView.swift */, + 9A1D05FD1D93E9F100ACF44C /* UIViewController.swift */, + ); + path = UIKit; + sourceTree = ""; + }; + 9A1D06231D93EA7E00ACF44C /* UIKit */ = { + isa = PBXGroup; + children = ( + 9A1D06241D93EA7E00ACF44C /* UIActivityIndicatorViewTests.swift */, + 9A1D06251D93EA7E00ACF44C /* UIBarButtonItemTests.swift */, + 9A1D06261D93EA7E00ACF44C /* UIButtonTests.swift */, + 9A1D06271D93EA7E00ACF44C /* UICollectionReusableViewTests.swift */, + 9A1D06281D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift */, + 9A1D06291D93EA7E00ACF44C /* UIControlTests.swift */, + 9A1D062A1D93EA7E00ACF44C /* UIDatePickerTests.swift */, + 9A1D062B1D93EA7E00ACF44C /* UIImageViewTests.swift */, + 9A1D062C1D93EA7E00ACF44C /* UILabelTests.swift */, + 9A1D062D1D93EA7E00ACF44C /* UIProgressViewTests.swift */, + 9A1D062E1D93EA7E00ACF44C /* UISegmentedControlTests.swift */, + 9A1D062F1D93EA7E00ACF44C /* UISwitchTests.swift */, + 9A1D06301D93EA7E00ACF44C /* UITableViewCellTests.swift */, + 9A1D06311D93EA7E00ACF44C /* UITableViewHeaderFooterViewTests.swift */, + 9A1D06321D93EA7E00ACF44C /* UITextFieldTests.swift */, + 9A1D06331D93EA7E00ACF44C /* UITextViewTests.swift */, + 9A1D06341D93EA7E00ACF44C /* UIViewControllerTests.swift */, + 9A1D06351D93EA7E00ACF44C /* UIViewTests.swift */, + ); + path = UIKit; + sourceTree = ""; + }; A97451321B3A935E00F48E55 /* watchOS */ = { isa = PBXGroup; children = ( @@ -334,6 +512,10 @@ CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */, 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */, 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */, + 9A1D05DF1D93E99100ACF44C /* Association.swift */, + 9A1D05E41D93E9E200ACF44C /* Reusable.swift */, + 9A1D05E91D93E9F100ACF44C /* AppKit */, + 9A1D05EB1D93E9F100ACF44C /* UIKit */, D04725ED19E49ED7006002AA /* Supporting Files */, ); path = ReactiveCocoa; @@ -351,6 +533,7 @@ D04725F919E49ED7006002AA /* ReactiveCocoaTests */ = { isa = PBXGroup; children = ( + 9A1D06231D93EA7E00ACF44C /* UIKit */, CD8401821CEE8ED7009F0ABF /* CocoaActionSpec.swift */, D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */, B696FB801A7640C00075236D /* TestError.swift */, @@ -728,10 +911,30 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9A1D06131D93EA0100ACF44C /* UIBarItem.swift in Sources */, + 9A1D05E81D93E9E200ACF44C /* Reusable.swift in Sources */, + 9A1D061F1D93EA0100ACF44C /* UITextField.swift in Sources */, + 9A1D06201D93EA0100ACF44C /* UITextView.swift in Sources */, + 9A1D06181D93EA0100ACF44C /* UIImageView.swift in Sources */, 9AD0F06D1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, C7142DBF1CDEA195009F402D /* CocoaAction.swift in Sources */, + 9A1D06211D93EA0100ACF44C /* UIView.swift in Sources */, + 9A1D061D1D93EA0100ACF44C /* UITableViewCell.swift in Sources */, + 9A1D06171D93EA0100ACF44C /* UIDatePicker.swift in Sources */, + 9A1D05E31D93E99100ACF44C /* Association.swift in Sources */, + 9A1D06161D93EA0100ACF44C /* UIControl.swift in Sources */, + 9A1D06141D93EA0100ACF44C /* UIButton.swift in Sources */, + 9A1D061C1D93EA0100ACF44C /* UISwitch.swift in Sources */, + 9A1D06221D93EA0100ACF44C /* UIViewController.swift in Sources */, + 9A1D06151D93EA0100ACF44C /* UICollectionReusableView.swift in Sources */, + 9A1D06191D93EA0100ACF44C /* UILabel.swift in Sources */, 4A0E11021D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, CD0C45E11CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, + 9A1D061A1D93EA0100ACF44C /* UIProgressView.swift in Sources */, + 9A1D061E1D93EA0100ACF44C /* UITableViewHeaderFooterView.swift in Sources */, + 9A1D06121D93EA0100ACF44C /* UIBarButtonItem.swift in Sources */, + 9A1D06111D93EA0100ACF44C /* UIActivityIndicatorView.swift in Sources */, + 9A1D061B1D93EA0100ACF44C /* UISegmentedControl.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -739,11 +942,29 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9A1D06591D93EA7E00ACF44C /* UIViewTests.swift in Sources */, 7DFBED281CDB8DE300EE435B /* DynamicPropertySpec.swift in Sources */, CD8401851CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, 9A1E72BC1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, + 9A1D064D1D93EA7E00ACF44C /* UISwitchTests.swift in Sources */, + 9A1D063D1D93EA7E00ACF44C /* UICollectionReusableViewTests.swift in Sources */, + 9A1D06451D93EA7E00ACF44C /* UIImageViewTests.swift in Sources */, + 9A1D06551D93EA7E00ACF44C /* UITextViewTests.swift in Sources */, + 9A1D063B1D93EA7E00ACF44C /* UIButtonTests.swift in Sources */, + 9A1D06571D93EA7E00ACF44C /* UIViewControllerTests.swift in Sources */, + 9A1D064F1D93EA7E00ACF44C /* UITableViewCellTests.swift in Sources */, + 9A1D064B1D93EA7E00ACF44C /* UISegmentedControlTests.swift in Sources */, + 9A1D063F1D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift in Sources */, + 9A1D06371D93EA7E00ACF44C /* UIActivityIndicatorViewTests.swift in Sources */, + 9A1D06471D93EA7E00ACF44C /* UILabelTests.swift in Sources */, + 9A1D06431D93EA7E00ACF44C /* UIDatePickerTests.swift in Sources */, + 9A1D06391D93EA7E00ACF44C /* UIBarButtonItemTests.swift in Sources */, 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */, + 9A1D06411D93EA7E00ACF44C /* UIControlTests.swift in Sources */, + 9A1D06491D93EA7E00ACF44C /* UIProgressViewTests.swift in Sources */, + 9A1D06511D93EA7E00ACF44C /* UITableViewHeaderFooterViewTests.swift in Sources */, BEE020661D637B0000DF261F /* TestError.swift in Sources */, + 9A1D06531D93EA7E00ACF44C /* UITextFieldTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -751,8 +972,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9A1D05E71D93E9E200ACF44C /* Reusable.swift in Sources */, C7142DBE1CDEA194009F402D /* CocoaAction.swift in Sources */, CD0C45E01CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, + 9A1D05E21D93E99100ACF44C /* Association.swift in Sources */, 4A0E11011D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, 9AD0F06C1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, ); @@ -762,8 +985,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9A1D05E51D93E9E200ACF44C /* Reusable.swift in Sources */, CD0C45DE1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, + 9A1D05FE1D93E9F900ACF44C /* NSTextField.swift in Sources */, + 9A1D05E01D93E99100ACF44C /* Association.swift in Sources */, C7142DBC1CDEA167009F402D /* CocoaAction.swift in Sources */, 4A0E10FF1D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, ); @@ -785,10 +1011,30 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9A1D06011D93EA0000ACF44C /* UIBarItem.swift in Sources */, + 9A1D05E61D93E9E200ACF44C /* Reusable.swift in Sources */, + 9A1D060D1D93EA0000ACF44C /* UITextField.swift in Sources */, + 9A1D060E1D93EA0000ACF44C /* UITextView.swift in Sources */, + 9A1D06061D93EA0000ACF44C /* UIImageView.swift in Sources */, CD0C45DF1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, C7142DBD1CDEA194009F402D /* CocoaAction.swift in Sources */, + 9A1D060F1D93EA0000ACF44C /* UIView.swift in Sources */, + 9A1D060B1D93EA0000ACF44C /* UITableViewCell.swift in Sources */, + 9A1D06051D93EA0000ACF44C /* UIDatePicker.swift in Sources */, + 9A1D05E11D93E99100ACF44C /* Association.swift in Sources */, + 9A1D06041D93EA0000ACF44C /* UIControl.swift in Sources */, + 9A1D06021D93EA0000ACF44C /* UIButton.swift in Sources */, + 9A1D060A1D93EA0000ACF44C /* UISwitch.swift in Sources */, + 9A1D06101D93EA0000ACF44C /* UIViewController.swift in Sources */, + 9A1D06031D93EA0000ACF44C /* UICollectionReusableView.swift in Sources */, + 9A1D06071D93EA0000ACF44C /* UILabel.swift in Sources */, 4A0E11001D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, 9AD0F06B1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, + 9A1D06081D93EA0000ACF44C /* UIProgressView.swift in Sources */, + 9A1D060C1D93EA0000ACF44C /* UITableViewHeaderFooterView.swift in Sources */, + 9A1D06001D93EA0000ACF44C /* UIBarButtonItem.swift in Sources */, + 9A1D05FF1D93EA0000ACF44C /* UIActivityIndicatorView.swift in Sources */, + 9A1D06091D93EA0000ACF44C /* UISegmentedControl.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -796,11 +1042,29 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9A1D06581D93EA7E00ACF44C /* UIViewTests.swift in Sources */, D0A2260F1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */, BFA6B94E1A7604D500C846D1 /* SignalProducerNimbleMatchers.swift in Sources */, B696FB821A7640C00075236D /* TestError.swift in Sources */, + 9A1D064C1D93EA7E00ACF44C /* UISwitchTests.swift in Sources */, + 9A1D063C1D93EA7E00ACF44C /* UICollectionReusableViewTests.swift in Sources */, + 9A1D06441D93EA7E00ACF44C /* UIImageViewTests.swift in Sources */, + 9A1D06541D93EA7E00ACF44C /* UITextViewTests.swift in Sources */, + 9A1D063A1D93EA7E00ACF44C /* UIButtonTests.swift in Sources */, + 9A1D06561D93EA7E00ACF44C /* UIViewControllerTests.swift in Sources */, + 9A1D064E1D93EA7E00ACF44C /* UITableViewCellTests.swift in Sources */, + 9A1D064A1D93EA7E00ACF44C /* UISegmentedControlTests.swift in Sources */, + 9A1D063E1D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift in Sources */, + 9A1D06361D93EA7E00ACF44C /* UIActivityIndicatorViewTests.swift in Sources */, + 9A1D06461D93EA7E00ACF44C /* UILabelTests.swift in Sources */, + 9A1D06421D93EA7E00ACF44C /* UIDatePickerTests.swift in Sources */, + 9A1D06381D93EA7E00ACF44C /* UIBarButtonItemTests.swift in Sources */, 9A1E72BB1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, + 9A1D06401D93EA7E00ACF44C /* UIControlTests.swift in Sources */, + 9A1D06481D93EA7E00ACF44C /* UIProgressViewTests.swift in Sources */, + 9A1D06501D93EA7E00ACF44C /* UITableViewHeaderFooterViewTests.swift in Sources */, CD8401841CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, + 9A1D06521D93EA7E00ACF44C /* UITextFieldTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ReactiveCocoaTests/Helpers/UIControl+EnableSendActionsForControlEvents.swift b/ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift similarity index 100% rename from ReactiveCocoaTests/Helpers/UIControl+EnableSendActionsForControlEvents.swift rename to ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift From 42a8c347961a3a0177f40f2f995efa084d919871 Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 22 Sep 2016 12:37:35 +0200 Subject: [PATCH 0389/1028] Reindented the new source files. --- ReactiveCocoa/AppKit/NSTextField.swift | 16 +-- ReactiveCocoa/Association.swift | 59 +++++----- ReactiveCocoa/Reusable.swift | 54 +++++----- .../UIKit/UIActivityIndicatorView.swift | 24 ++--- ReactiveCocoa/UIKit/UIBarButtonItem.swift | 36 +++---- ReactiveCocoa/UIKit/UIBarItem.swift | 8 +- ReactiveCocoa/UIKit/UIButton.swift | 46 ++++---- ReactiveCocoa/UIKit/UIControl.swift | 76 ++++++------- ReactiveCocoa/UIKit/UIDatePicker.swift | 9 +- ReactiveCocoa/UIKit/UIImageView.swift | 18 ++-- ReactiveCocoa/UIKit/UILabel.swift | 26 ++--- ReactiveCocoa/UIKit/UIProgressView.swift | 8 +- ReactiveCocoa/UIKit/UISegmentedControl.swift | 14 +-- ReactiveCocoa/UIKit/UISwitch.swift | 9 +- ReactiveCocoa/UIKit/UITextField.swift | 31 +++--- ReactiveCocoa/UIKit/UITextView.swift | 13 ++- ReactiveCocoa/UIKit/UIView.swift | 28 ++--- ReactiveCocoa/UIKit/UIViewController.swift | 102 +++++++++--------- 18 files changed, 286 insertions(+), 291 deletions(-) diff --git a/ReactiveCocoa/AppKit/NSTextField.swift b/ReactiveCocoa/AppKit/NSTextField.swift index 5f81f08c90..e249cab085 100644 --- a/ReactiveCocoa/AppKit/NSTextField.swift +++ b/ReactiveCocoa/AppKit/NSTextField.swift @@ -11,12 +11,12 @@ import AppKit import enum Result.NoError extension NSTextField { - /// Sends the field's string value whenever it changes. - public var rex_textSignal: SignalProducer { - return NotificationCenter.default - .rac_notifications(forName: .NSControlTextDidChange, object: self) - .map { notification in - (notification.object as! NSTextField).stringValue - } - } + /// Sends the field's string value whenever it changes. + public var rex_textSignal: SignalProducer { + return NotificationCenter.default + .rac_notifications(forName: .NSControlTextDidChange, object: self) + .map { notification in + (notification.object as! NSTextField).stringValue + } + } } diff --git a/ReactiveCocoa/Association.swift b/ReactiveCocoa/Association.swift index 77858c642c..f2eff75638 100644 --- a/ReactiveCocoa/Association.swift +++ b/ReactiveCocoa/Association.swift @@ -7,7 +7,6 @@ // import ReactiveSwift -import ReactiveCocoa /// Attaches a `MutableProperty` value to the `host` object using KVC to get the initial /// value and write subsequent updates from the property's producer. Note that `keyPath` @@ -17,13 +16,13 @@ import ReactiveCocoa /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. public func associatedProperty(_ host: AnyObject, keyPath: StaticString) -> MutableProperty { - 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: String(describing: keyPath)) - } - return associatedProperty(host, key: keyPath.utf8Start, initial: initial, setter: setter) + 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: String(describing: keyPath)) + } + return associatedProperty(host, key: keyPath.utf8Start, initial: initial, setter: setter) } /// Attaches a `MutableProperty` value to the `host` object using KVC to get the initial @@ -34,12 +33,12 @@ public func associatedProperty(_ host: AnyObject, keyPath: StaticString) -> Muta /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. public func associatedProperty(_ host: AnyObject, keyPath: StaticString, placeholder: () -> T) -> MutableProperty { - let setter: (AnyObject, T) -> () = { host, newValue in - host.setValue(newValue, forKeyPath: String(describing: keyPath)) - } - return associatedProperty(host, key: keyPath.utf8Start, initial: { host in - host.value(forKeyPath: String(describing: keyPath)) as? T ?? placeholder() - }, setter: setter) + let setter: (AnyObject, T) -> () = { host, newValue in + host.setValue(newValue, forKeyPath: String(describing: keyPath)) + } + return associatedProperty(host, key: keyPath.utf8Start, initial: { host in + host.value(forKeyPath: String(describing: keyPath)) as? T ?? placeholder() + }, setter: setter) } /// Attaches a `MutableProperty` value to the `host` object under `key`. The property is @@ -49,29 +48,29 @@ public func associatedProperty(_ host: AnyObject, keyPath: StaticS /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. public func associatedProperty(_ host: Host, key: UnsafeRawPointer, initial: (Host) -> T, setter: @escaping (Host, T) -> (), setUp: (MutableProperty) -> () = { _ in }) -> MutableProperty { - return associatedObject(host, key: key) { host in - let property = MutableProperty(initial(host)) + return associatedObject(host, key: key) { host in + let property = MutableProperty(initial(host)) - setUp(property) + setUp(property) - property.producer.startWithNext { [weak host] next in - if let host = host { - setter(host, next) - } - } + property.producer.startWithNext { [weak host] next in + if let host = host { + setter(host, next) + } + } - return property - } + return property + } } /// On first use attaches the object returned from `initial` to the `host` object using /// `key` via `objc_setAssociatedObject`. On subsequent usage, returns said object via /// `objc_getAssociatedObject`. 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) - objc_setAssociatedObject(host, key, value, .OBJC_ASSOCIATION_RETAIN) - } - return value! + var value = objc_getAssociatedObject(host, key) as? T + if value == nil { + value = initial(host) + objc_setAssociatedObject(host, key, value, .OBJC_ASSOCIATION_RETAIN) + } + return value! } diff --git a/ReactiveCocoa/Reusable.swift b/ReactiveCocoa/Reusable.swift index 4fc3dce2ff..0ed394a7b1 100644 --- a/ReactiveCocoa/Reusable.swift +++ b/ReactiveCocoa/Reusable.swift @@ -13,35 +13,35 @@ import enum Result.NoError /// A protocol for components that can be reused using `prepareForReuse`. public protocol Reusable { - var rac_prepareForReuseSignal: RACSignal! { get } + var rac_prepareForReuseSignal: RACSignal! { get } } extension Reusable { - /// A signal which will send a `Next` event whenever `prepareForReuse` is invoked upon - /// the receiver. - /// - /// - Note: This signal is particular useful to be used as a trigger for the `takeUntil` - /// operator. - /// - /// #### Examples - /// - /// ``` - /// button - /// .rex_controlEvents(.TouchUpInside) - /// .takeUntil(self.rex_prepareForReuse) - /// .startWithNext { _ in - /// // do other things - /// } - /// - /// label.rex_text <~ - /// titleProperty - /// .producer - /// .takeUntil(self.rex_prepareForReuse) - /// ``` - /// - public var rex_prepareForReuse: Signal { - return rac_prepareForReuseSignal - .rex_toTriggerSignal() - } + /// A signal which will send a `Next` event whenever `prepareForReuse` is invoked upon + /// the receiver. + /// + /// - Note: This signal is particular useful to be used as a trigger for the `takeUntil` + /// operator. + /// + /// #### Examples + /// + /// ``` + /// button + /// .rex_controlEvents(.TouchUpInside) + /// .takeUntil(self.rex_prepareForReuse) + /// .startWithNext { _ in + /// // do other things + /// } + /// + /// label.rex_text <~ + /// titleProperty + /// .producer + /// .takeUntil(self.rex_prepareForReuse) + /// ``` + /// + public var rex_prepareForReuse: Signal { + return rac_prepareForReuseSignal + .rex_toTriggerSignal() + } } diff --git a/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift b/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift index 5342819d36..072be740d3 100644 --- a/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift +++ b/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift @@ -11,18 +11,18 @@ import UIKit extension UIActivityIndicatorView { - /// Wraps an indicator's `isAnimating()` state in a bindable property. - /// Setting a new value to the property would call `startAnimating()` or - /// `stopAnimating()` depending on the value. - public var rex_animating: MutableProperty { - return associatedProperty(self, key: &animatingKey, initial: { $0.isAnimating }, setter: { host, animating in - if animating { - host.startAnimating() - } else { - host.stopAnimating() - } - }) - } + /// Wraps an indicator's `isAnimating()` state in a bindable property. + /// Setting a new value to the property would call `startAnimating()` or + /// `stopAnimating()` depending on the value. + public var rex_animating: MutableProperty { + return associatedProperty(self, key: &animatingKey, initial: { $0.isAnimating }, setter: { host, animating in + if animating { + host.startAnimating() + } else { + host.stopAnimating() + } + }) + } } private var animatingKey: UInt8 = 0 diff --git a/ReactiveCocoa/UIKit/UIBarButtonItem.swift b/ReactiveCocoa/UIKit/UIBarButtonItem.swift index 4841b65de9..bea89d2b32 100644 --- a/ReactiveCocoa/UIKit/UIBarButtonItem.swift +++ b/ReactiveCocoa/UIKit/UIBarButtonItem.swift @@ -10,26 +10,26 @@ import ReactiveSwift import UIKit extension UIBarButtonItem { - /// Exposes a property that binds an action to bar button item. The action is set as - /// a target of the button. When property changes occur the previous action is - /// overwritten. 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) { host in - let initial = CocoaAction.rex_disabled - let property = MutableProperty(initial) - - property.producer - .startWithNext { [weak host] action in - host?.target = action - host?.action = CocoaAction.selector - } + /// Exposes a property that binds an action to bar button item. The action is set as + /// a target of the button. When property changes occur the previous action is + /// overwritten. 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) { host in + let initial = CocoaAction.rex_disabled + let property = MutableProperty(initial) - host.rex_enabled <~ property.producer.flatMap(.latest) { $0.rex_enabledProducer } + property.producer + .startWithNext { [weak host] action in + host?.target = action + host?.action = CocoaAction.selector + } - return property - } - } + host.rex_enabled <~ property.producer.flatMap(.latest) { $0.rex_enabledProducer } + + return property + } + } } private var actionKey: UInt8 = 0 diff --git a/ReactiveCocoa/UIKit/UIBarItem.swift b/ReactiveCocoa/UIKit/UIBarItem.swift index 345f7f0964..fb90309c9a 100644 --- a/ReactiveCocoa/UIKit/UIBarItem.swift +++ b/ReactiveCocoa/UIKit/UIBarItem.swift @@ -10,10 +10,10 @@ import ReactiveSwift 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.isEnabled }, setter: { $0.isEnabled = $1 }) - } + /// Wraps a UIBarItem's `enabled` state in a bindable property. + public var rex_enabled: MutableProperty { + return associatedProperty(self, key: &enabledKey, initial: { $0.isEnabled }, setter: { $0.isEnabled = $1 }) + } } private var enabledKey: UInt8 = 0 diff --git a/ReactiveCocoa/UIKit/UIButton.swift b/ReactiveCocoa/UIKit/UIButton.swift index 201f8e02e3..59f757b9ad 100644 --- a/ReactiveCocoa/UIKit/UIButton.swift +++ b/ReactiveCocoa/UIKit/UIButton.swift @@ -10,33 +10,33 @@ import ReactiveSwift import UIKit extension UIButton { - /// Exposes a property that binds an action to button presses. The action is set as - /// a target of the button for `TouchUpInside` events. When property changes occur the - /// previous action is removed as a target. This also binds the enabled state of the - /// action to the `rex_enabled` property on the button. - public var rex_pressed: MutableProperty { - return associatedObject(self, key: &pressedKey) { host in - let initial = CocoaAction.rex_disabled - let property = MutableProperty(initial) + /// Exposes a property that binds an action to button presses. The action is set as + /// a target of the button for `TouchUpInside` events. When property changes occur the + /// previous action is removed as a target. This also binds the enabled state of the + /// action to the `rex_enabled` property on the button. + public var rex_pressed: MutableProperty { + return associatedObject(self, key: &pressedKey) { host in + let initial = CocoaAction.rex_disabled + let property = MutableProperty(initial) - property.producer - .combinePrevious(initial) - .startWithNext { [weak host] previous, next in - host?.removeTarget(previous, action: CocoaAction.selector, for: .touchUpInside) - host?.addTarget(next, action: CocoaAction.selector, for: .touchUpInside) - } + property.producer + .combinePrevious(initial) + .startWithNext { [weak host] previous, next in + 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 - } - } + return property + } + } - /// 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.title(for: .normal) ?? "" }, setter: { $0.setTitle($1, for: .normal) }) - } + /// 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.title(for: .normal) ?? "" }, setter: { $0.setTitle($1, for: .normal) }) + } } private var pressedKey: UInt8 = 0 diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index 045b839974..3c686daac2 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -12,44 +12,44 @@ import enum Result.NoError extension UIControl { -#if os(iOS) - /// Creates a producer for the sender whenever a specified control event is triggered. - public func rex_controlEvents(_ events: UIControlEvents) -> SignalProducer { - return rac_signal(for: events) - .toSignalProducer() - .map { $0 as? UIControl } - .flatMapError { _ in SignalProducer(value: nil) } - } - - /// Creates a bindable property to wrap a control's value. - /// - /// This property uses `UIControlEvents.ValueChanged` and `UIControlEvents.EditingChanged` - /// events to detect changes and keep the value up-to-date. - // - 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]) - .filterMap { $0 as? Host } - .filterMap(getter) - } - } -#endif - - /// 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 `selected` state in a bindable property. - public var rex_selected: MutableProperty { - 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.isHighlighted }, setter: { $0.isHighlighted = $1 }) - } + #if os(iOS) + /// Creates a producer for the sender whenever a specified control event is triggered. + public func rex_controlEvents(_ events: UIControlEvents) -> SignalProducer { + return rac_signal(for: events) + .toSignalProducer() + .map { $0 as? UIControl } + .flatMapError { _ in SignalProducer(value: nil) } + } + + /// Creates a bindable property to wrap a control's value. + /// + /// This property uses `UIControlEvents.ValueChanged` and `UIControlEvents.EditingChanged` + /// events to detect changes and keep the value up-to-date. + // + 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]) + .filterMap { $0 as? Host } + .filterMap(getter) + } + } + #endif + + /// 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 `selected` state in a bindable property. + public var rex_selected: MutableProperty { + 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.isHighlighted }, setter: { $0.isHighlighted = $1 }) + } } private var enabledKey: UInt8 = 0 diff --git a/ReactiveCocoa/UIKit/UIDatePicker.swift b/ReactiveCocoa/UIKit/UIDatePicker.swift index 9a3293d6e8..fe5d4e1c6b 100644 --- a/ReactiveCocoa/UIKit/UIDatePicker.swift +++ b/ReactiveCocoa/UIKit/UIDatePicker.swift @@ -10,9 +10,8 @@ import ReactiveSwift import UIKit extension UIDatePicker { - - // Wraps a datePicker's `date` value in a bindable property. - public var rex_date: MutableProperty { - return UIControl.rex_value(self, getter: { $0.date }, setter: { $0.date = $1 }) - } + // Wraps a datePicker's `date` value in a bindable property. + public var rex_date: MutableProperty { + return UIControl.rex_value(self, getter: { $0.date }, setter: { $0.date = $1 }) + } } diff --git a/ReactiveCocoa/UIKit/UIImageView.swift b/ReactiveCocoa/UIKit/UIImageView.swift index 266d6bb2de..436d6b8424 100644 --- a/ReactiveCocoa/UIKit/UIImageView.swift +++ b/ReactiveCocoa/UIKit/UIImageView.swift @@ -10,15 +10,15 @@ import ReactiveSwift import UIKit extension UIImageView { - /// Wraps a imageView's `image` value in a bindable property. - public var rex_image: MutableProperty { - return associatedProperty(self, key: &imageKey, initial: { $0.image }, setter: { $0.image = $1 }) - } - - /// Wraps a imageView's `highlightedImage` value in a bindable property. - public var rex_highlightedImage: MutableProperty { - return associatedProperty(self, key: &highlightedImageKey, initial: { $0.highlightedImage }, setter: { $0.highlightedImage = $1 }) - } + /// Wraps a imageView's `image` value in a bindable property. + public var rex_image: MutableProperty { + return associatedProperty(self, key: &imageKey, initial: { $0.image }, setter: { $0.image = $1 }) + } + + /// Wraps a imageView's `highlightedImage` value in a bindable property. + public var rex_highlightedImage: MutableProperty { + return associatedProperty(self, key: &highlightedImageKey, initial: { $0.highlightedImage }, setter: { $0.highlightedImage = $1 }) + } } private var imageKey: UInt8 = 0 diff --git a/ReactiveCocoa/UIKit/UILabel.swift b/ReactiveCocoa/UIKit/UILabel.swift index bf3b0b2974..9df03bd788 100644 --- a/ReactiveCocoa/UIKit/UILabel.swift +++ b/ReactiveCocoa/UIKit/UILabel.swift @@ -10,20 +10,20 @@ import ReactiveSwift import UIKit extension UILabel { - /// Wraps a label's `text` value in a bindable property. - public var rex_text: MutableProperty { - return associatedProperty(self, key: &attributedTextKey, initial: { $0.text }, setter: { $0.text = $1 }) - } - - /// Wraps a label's `attributedText` value in a bindable property. - public var rex_attributedText: MutableProperty { - return associatedProperty(self, key: &attributedTextKey, initial: { $0.attributedText }, setter: { $0.attributedText = $1 }) - } + /// Wraps a label's `text` value in a bindable property. + public var rex_text: MutableProperty { + return associatedProperty(self, key: &attributedTextKey, initial: { $0.text }, setter: { $0.text = $1 }) + } - /// Wraps a label's `textColor` value in a bindable property. - public var rex_textColor: MutableProperty { - return associatedProperty(self, key: &textColorKey, initial: { $0.textColor }, setter: { $0.textColor = $1 }) - } + /// Wraps a label's `attributedText` value in a bindable property. + public var rex_attributedText: MutableProperty { + return associatedProperty(self, key: &attributedTextKey, initial: { $0.attributedText }, setter: { $0.attributedText = $1 }) + } + + /// Wraps a label's `textColor` value in a bindable property. + public var rex_textColor: MutableProperty { + return associatedProperty(self, key: &textColorKey, initial: { $0.textColor }, setter: { $0.textColor = $1 }) + } } private var textKey: UInt8 = 0 diff --git a/ReactiveCocoa/UIKit/UIProgressView.swift b/ReactiveCocoa/UIKit/UIProgressView.swift index a635cae14e..37af2255fd 100644 --- a/ReactiveCocoa/UIKit/UIProgressView.swift +++ b/ReactiveCocoa/UIKit/UIProgressView.swift @@ -10,10 +10,10 @@ import ReactiveSwift import UIKit extension UIProgressView { - /// Wraps a progressView's `progress` value in a bindable property. - public var rex_progress: MutableProperty { - return associatedProperty(self, key: &progressKey, initial: { $0.progress }, setter: { $0.progress = $1 }) - } + /// Wraps a progressView's `progress` value in a bindable property. + public var rex_progress: MutableProperty { + return associatedProperty(self, key: &progressKey, initial: { $0.progress }, setter: { $0.progress = $1 }) + } } private var progressKey: UInt8 = 0 diff --git a/ReactiveCocoa/UIKit/UISegmentedControl.swift b/ReactiveCocoa/UIKit/UISegmentedControl.swift index c961952822..975b3db9d5 100644 --- a/ReactiveCocoa/UIKit/UISegmentedControl.swift +++ b/ReactiveCocoa/UIKit/UISegmentedControl.swift @@ -10,13 +10,13 @@ import ReactiveSwift import UIKit 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) - .filterMap { ($0 as? UISegmentedControl)?.selectedSegmentIndex } - return property - } + /// 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) + .filterMap { ($0 as? UISegmentedControl)?.selectedSegmentIndex } + return property + } } private var selectedSegmentIndexKey: UInt8 = 0 diff --git a/ReactiveCocoa/UIKit/UISwitch.swift b/ReactiveCocoa/UIKit/UISwitch.swift index 73984aac89..d5dbdb5c59 100644 --- a/ReactiveCocoa/UIKit/UISwitch.swift +++ b/ReactiveCocoa/UIKit/UISwitch.swift @@ -10,9 +10,8 @@ import ReactiveSwift import UIKit extension UISwitch { - - /// Wraps a switch's `on` value in a bindable property. - public var rex_on: MutableProperty { - return UIControl.rex_value(self, getter: { $0.isOn }, setter: { $0.isOn = $1 }) - } + /// Wraps a switch's `on` value in a bindable property. + public var rex_on: MutableProperty { + return UIControl.rex_value(self, getter: { $0.isOn }, setter: { $0.isOn = $1 }) + } } diff --git a/ReactiveCocoa/UIKit/UITextField.swift b/ReactiveCocoa/UIKit/UITextField.swift index 60c35e26a4..03b8ddff18 100644 --- a/ReactiveCocoa/UIKit/UITextField.swift +++ b/ReactiveCocoa/UIKit/UITextField.swift @@ -10,22 +10,21 @@ import ReactiveSwift import UIKit extension UITextField { - - /// Wraps a textField's `text` value in a bindable property. - public var rex_text: MutableProperty { - 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 <~ - NotificationCenter.default - .rac_notifications(forName: .UITextFieldTextDidChange, object: self) - .filterMap { ($0.object as? UITextField)?.text } - } -#endif - } + /// Wraps a textField's `text` value in a bindable property. + public var rex_text: MutableProperty { + 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 <~ + NotificationCenter.default + .rac_notifications(forName: .UITextFieldTextDidChange, object: self) + .filterMap { ($0.object as? UITextField)?.text } + } + #endif + } } diff --git a/ReactiveCocoa/UIKit/UITextView.swift b/ReactiveCocoa/UIKit/UITextView.swift index 86818c3d05..9302abe7c6 100644 --- a/ReactiveCocoa/UIKit/UITextView.swift +++ b/ReactiveCocoa/UIKit/UITextView.swift @@ -11,11 +11,10 @@ import UIKit import enum Result.NoError extension UITextView { - - /// Sends the textView's string value whenever it changes. - public var rex_text: SignalProducer { - return NotificationCenter.default - .rac_notifications(forName: .UITextViewTextDidChange, object: self) - .filterMap { ($0.object as? UITextView)?.text } - } + /// Sends the textView's string value whenever it changes. + public var rex_text: SignalProducer { + return NotificationCenter.default + .rac_notifications(forName: .UITextViewTextDidChange, object: self) + .filterMap { ($0.object as? UITextView)?.text } + } } diff --git a/ReactiveCocoa/UIKit/UIView.swift b/ReactiveCocoa/UIKit/UIView.swift index ba0ff4dc85..48740c0ef2 100644 --- a/ReactiveCocoa/UIKit/UIView.swift +++ b/ReactiveCocoa/UIKit/UIView.swift @@ -10,21 +10,21 @@ import ReactiveSwift import UIKit extension UIView { - /// Wraps a view's `alpha` value in a bindable property. - public var rex_alpha: MutableProperty { - return associatedProperty(self, key: &alphaKey, initial: { $0.alpha }, setter: { $0.alpha = $1 }) - } - - /// Wraps a view's `hidden` state in a bindable property. - public var rex_hidden: MutableProperty { - return associatedProperty(self, key: &hiddenKey, initial: { $0.isHidden }, setter: { $0.isHidden = $1 }) - } - + /// Wraps a view's `alpha` value in a bindable property. + public var rex_alpha: MutableProperty { + return associatedProperty(self, key: &alphaKey, initial: { $0.alpha }, setter: { $0.alpha = $1 }) + } - /// Wraps a view's `userInteractionEnabled` state in a bindable property. - public var rex_userInteractionEnabled: MutableProperty { - return associatedProperty(self, key: &userInteractionEnabledKey, initial: { $0.isUserInteractionEnabled }, setter: { $0.isUserInteractionEnabled = $1 }) - } + /// Wraps a view's `hidden` state in a bindable property. + public var rex_hidden: MutableProperty { + 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.isUserInteractionEnabled }, setter: { $0.isUserInteractionEnabled = $1 }) + } } private var alphaKey: UInt8 = 0 diff --git a/ReactiveCocoa/UIKit/UIViewController.swift b/ReactiveCocoa/UIKit/UIViewController.swift index d96d486a66..17028fa0af 100644 --- a/ReactiveCocoa/UIKit/UIViewController.swift +++ b/ReactiveCocoa/UIKit/UIViewController.swift @@ -11,67 +11,67 @@ import Result import UIKit extension UIViewController { - /// Returns a `Signal`, that will be triggered - /// when `self`'s `viewDidDisappear` is called - public var rex_viewDidDisappear: Signal<(), NoError> { - return trigger(for: #selector(UIViewController.viewDidDisappear(_:))) - } + /// Returns a `Signal`, that will be triggered + /// when `self`'s `viewDidDisappear` is called + public var rex_viewDidDisappear: Signal<(), NoError> { + 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 trigger(for: #selector(UIViewController.viewWillDisappear(_:))) - } + /// Returns a `Signal`, that will be triggered + /// when `self`'s `viewWillDisappear` is called + public var rex_viewWillDisappear: Signal<(), NoError> { + 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 trigger(for: #selector(UIViewController.viewDidAppear(_:))) - } + /// Returns a `Signal`, that will be triggered + /// when `self`'s `viewDidAppear` is called + public var rex_viewDidAppear: Signal<(), NoError> { + 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 trigger(for: #selector(UIViewController.viewWillAppear(_:))) - } + /// Returns a `Signal`, that will be triggered + /// when `self`'s `viewWillAppear` is called + public var rex_viewWillAppear: Signal<(), NoError> { + return trigger(for: #selector(UIViewController.viewWillAppear(_:))) + } - private func trigger(for selector: Selector) -> Signal<(), NoError> { - return self - .rac_signal(for: selector) - .rex_toTriggerSignal() - } + private func trigger(for selector: Selector) -> Signal<(), NoError> { + return self + .rac_signal(for: selector) + .rex_toTriggerSignal() + } - public typealias DismissingCompletion = ((Void) -> Void)? - public typealias DismissingInformation = (animated: Bool, completion: DismissingCompletion)? + 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`. - /// E.g: - /// ``` - /// //Dismissed with animation (`true`) and `nil` completion - /// viewController.rex_dismissAnimated <~ aProducer.map { _ in (true, nil) } - /// ``` - /// The dismissal observation can be made either with binding (example above) - /// or `viewController.dismissViewControllerAnimated(true, completion: nil)` - public var rex_dismissAnimated: MutableProperty { + /// 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`. + /// E.g: + /// ``` + /// //Dismissed with animation (`true`) and `nil` completion + /// viewController.rex_dismissAnimated <~ aProducer.map { _ in (true, nil) } + /// ``` + /// The dismissal observation can be made either with binding (example above) + /// or `viewController.dismissViewControllerAnimated(true, completion: nil)` + public var rex_dismissAnimated: MutableProperty { - let initial: (UIViewController) -> DismissingInformation = { _ in nil } - let setter: (UIViewController, DismissingInformation) -> Void = { host, dismissingInfo in + let initial: (UIViewController) -> DismissingInformation = { _ in nil } + let setter: (UIViewController, DismissingInformation) -> Void = { host, dismissingInfo in - guard let unwrapped = dismissingInfo else { return } - host.dismiss(animated: unwrapped.animated, completion: unwrapped.completion) - } + guard let unwrapped = dismissingInfo else { return } + host.dismiss(animated: unwrapped.animated, completion: unwrapped.completion) + } - let property = associatedProperty(self, key: &dismissModally, initial: initial, setter: setter) { property in - property <~ self.rac_signal(for: #selector(UIViewController.dismiss)) - .take { _ in property.value != nil } - .rex_toTriggerSignal() - .map { _ in return nil } - } + let property = associatedProperty(self, key: &dismissModally, initial: initial, setter: setter) { property in + property <~ self.rac_signal(for: #selector(UIViewController.dismiss)) + .take { _ in property.value != nil } + .rex_toTriggerSignal() + .map { _ in return nil } + } - return property - } + return property + } } private var dismissModally: UInt8 = 0 From 21ba72b34c775cc41b9acdb272f8837e4f87ed2d Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 22 Sep 2016 13:09:27 +0200 Subject: [PATCH 0390/1028] Made the new source files buildable. --- ReactiveCocoa.xcodeproj/project.pbxproj | 20 ++++++++++------- ReactiveCocoa/Association.swift | 2 +- ReactiveCocoa/CocoaAction.swift | 22 ++++++++++++++++++- ReactiveCocoa/NSObject+Intercepting.swift | 9 ++++++++ ReactiveCocoa/Reusable.swift | 12 +++++----- ReactiveCocoa/UIKit/UIBarButtonItem.swift | 6 ++--- ReactiveCocoa/UIKit/UIButton.swift | 6 ++--- ReactiveCocoa/UIKit/UIControl.swift | 16 +++++--------- ReactiveCocoa/UIKit/UISegmentedControl.swift | 4 ++-- ReactiveCocoa/UIKit/UITextField.swift | 2 +- ReactiveCocoa/UIKit/UITextView.swift | 3 ++- ReactiveCocoa/UIKit/UIViewController.swift | 17 +++++--------- .../UIKit/UIActivityIndicatorViewTests.swift | 4 ++-- .../UIKit/UIBarButtonItemTests.swift | 4 ++-- ReactiveCocoaTests/UIKit/UIButtonTests.swift | 4 ++-- ReactiveCocoaTests/UIKit/UIControlTests.swift | 20 ++++++++--------- .../UIKit/UIDatePickerTests.swift | 3 +-- .../UIKit/UIImageViewTests.swift | 8 +++---- ReactiveCocoaTests/UIKit/UILabelTests.swift | 14 ++++++------ .../UIKit/UIProgressViewTests.swift | 4 ++-- .../UIKit/UISegmentedControlTests.swift | 4 ++-- ReactiveCocoaTests/UIKit/UISwitchTests.swift | 4 ++-- .../UIKit/UITextFieldTests.swift | 2 +- .../UIKit/UITextViewTests.swift | 2 +- .../UIKit/UIViewControllerTests.swift | 12 +++++----- ReactiveCocoaTests/UIKit/UIViewTests.swift | 12 +++++----- 26 files changed, 117 insertions(+), 99 deletions(-) create mode 100644 ReactiveCocoa/NSObject+Intercepting.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 190d418859..52d0bf0f71 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -54,12 +54,10 @@ 9A1D06141D93EA0100ACF44C /* UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05EF1D93E9F100ACF44C /* UIButton.swift */; }; 9A1D06151D93EA0100ACF44C /* UICollectionReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F01D93E9F100ACF44C /* UICollectionReusableView.swift */; }; 9A1D06161D93EA0100ACF44C /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F11D93E9F100ACF44C /* UIControl.swift */; }; - 9A1D06171D93EA0100ACF44C /* UIDatePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F21D93E9F100ACF44C /* UIDatePicker.swift */; }; 9A1D06181D93EA0100ACF44C /* UIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F31D93E9F100ACF44C /* UIImageView.swift */; }; 9A1D06191D93EA0100ACF44C /* UILabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F41D93E9F100ACF44C /* UILabel.swift */; }; 9A1D061A1D93EA0100ACF44C /* UIProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F51D93E9F100ACF44C /* UIProgressView.swift */; }; 9A1D061B1D93EA0100ACF44C /* UISegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F61D93E9F100ACF44C /* UISegmentedControl.swift */; }; - 9A1D061C1D93EA0100ACF44C /* UISwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F71D93E9F100ACF44C /* UISwitch.swift */; }; 9A1D061D1D93EA0100ACF44C /* UITableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F81D93E9F100ACF44C /* UITableViewCell.swift */; }; 9A1D061E1D93EA0100ACF44C /* UITableViewHeaderFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F91D93E9F100ACF44C /* UITableViewHeaderFooterView.swift */; }; 9A1D061F1D93EA0100ACF44C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05FA1D93E9F100ACF44C /* UITextField.swift */; }; @@ -79,7 +77,6 @@ 9A1D06401D93EA7E00ACF44C /* UIControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06291D93EA7E00ACF44C /* UIControlTests.swift */; }; 9A1D06411D93EA7E00ACF44C /* UIControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06291D93EA7E00ACF44C /* UIControlTests.swift */; }; 9A1D06421D93EA7E00ACF44C /* UIDatePickerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062A1D93EA7E00ACF44C /* UIDatePickerTests.swift */; }; - 9A1D06431D93EA7E00ACF44C /* UIDatePickerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062A1D93EA7E00ACF44C /* UIDatePickerTests.swift */; }; 9A1D06441D93EA7E00ACF44C /* UIImageViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062B1D93EA7E00ACF44C /* UIImageViewTests.swift */; }; 9A1D06451D93EA7E00ACF44C /* UIImageViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062B1D93EA7E00ACF44C /* UIImageViewTests.swift */; }; 9A1D06461D93EA7E00ACF44C /* UILabelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062C1D93EA7E00ACF44C /* UILabelTests.swift */; }; @@ -89,7 +86,6 @@ 9A1D064A1D93EA7E00ACF44C /* UISegmentedControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062E1D93EA7E00ACF44C /* UISegmentedControlTests.swift */; }; 9A1D064B1D93EA7E00ACF44C /* UISegmentedControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062E1D93EA7E00ACF44C /* UISegmentedControlTests.swift */; }; 9A1D064C1D93EA7E00ACF44C /* UISwitchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062F1D93EA7E00ACF44C /* UISwitchTests.swift */; }; - 9A1D064D1D93EA7E00ACF44C /* UISwitchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062F1D93EA7E00ACF44C /* UISwitchTests.swift */; }; 9A1D064E1D93EA7E00ACF44C /* UITableViewCellTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06301D93EA7E00ACF44C /* UITableViewCellTests.swift */; }; 9A1D064F1D93EA7E00ACF44C /* UITableViewCellTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06301D93EA7E00ACF44C /* UITableViewCellTests.swift */; }; 9A1D06501D93EA7E00ACF44C /* UITableViewHeaderFooterViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06311D93EA7E00ACF44C /* UITableViewHeaderFooterViewTests.swift */; }; @@ -102,6 +98,11 @@ 9A1D06571D93EA7E00ACF44C /* UIViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06341D93EA7E00ACF44C /* UIViewControllerTests.swift */; }; 9A1D06581D93EA7E00ACF44C /* UIViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06351D93EA7E00ACF44C /* UIViewTests.swift */; }; 9A1D06591D93EA7E00ACF44C /* UIViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06351D93EA7E00ACF44C /* UIViewTests.swift */; }; + 9A1D065B1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; + 9A1D065C1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; + 9A1D065D1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; + 9A1D065E1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; + 9A1D065F1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; 9A1E72BA1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; 9A1E72BB1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; 9A1E72BC1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; @@ -259,6 +260,7 @@ 9A1D06331D93EA7E00ACF44C /* UITextViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextViewTests.swift; sourceTree = ""; }; 9A1D06341D93EA7E00ACF44C /* UIViewControllerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewControllerTests.swift; sourceTree = ""; }; 9A1D06351D93EA7E00ACF44C /* UIViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewTests.swift; sourceTree = ""; }; + 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Intercepting.swift"; sourceTree = ""; }; 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyValueObservingSpec.swift; sourceTree = ""; }; 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+KeyValueObserving.swift"; sourceTree = ""; }; A97451331B3A935E00F48E55 /* watchOS-Application.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Application.xcconfig"; sourceTree = ""; }; @@ -511,6 +513,7 @@ C7142DBB1CDEA167009F402D /* CocoaAction.swift */, CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */, 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */, + 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */, 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */, 9A1D05DF1D93E99100ACF44C /* Association.swift */, 9A1D05E41D93E9E200ACF44C /* Reusable.swift */, @@ -920,12 +923,11 @@ C7142DBF1CDEA195009F402D /* CocoaAction.swift in Sources */, 9A1D06211D93EA0100ACF44C /* UIView.swift in Sources */, 9A1D061D1D93EA0100ACF44C /* UITableViewCell.swift in Sources */, - 9A1D06171D93EA0100ACF44C /* UIDatePicker.swift in Sources */, 9A1D05E31D93E99100ACF44C /* Association.swift in Sources */, 9A1D06161D93EA0100ACF44C /* UIControl.swift in Sources */, 9A1D06141D93EA0100ACF44C /* UIButton.swift in Sources */, - 9A1D061C1D93EA0100ACF44C /* UISwitch.swift in Sources */, 9A1D06221D93EA0100ACF44C /* UIViewController.swift in Sources */, + 9A1D065E1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, 9A1D06151D93EA0100ACF44C /* UICollectionReusableView.swift in Sources */, 9A1D06191D93EA0100ACF44C /* UILabel.swift in Sources */, 4A0E11021D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, @@ -946,7 +948,6 @@ 7DFBED281CDB8DE300EE435B /* DynamicPropertySpec.swift in Sources */, CD8401851CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, 9A1E72BC1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, - 9A1D064D1D93EA7E00ACF44C /* UISwitchTests.swift in Sources */, 9A1D063D1D93EA7E00ACF44C /* UICollectionReusableViewTests.swift in Sources */, 9A1D06451D93EA7E00ACF44C /* UIImageViewTests.swift in Sources */, 9A1D06551D93EA7E00ACF44C /* UITextViewTests.swift in Sources */, @@ -957,12 +958,12 @@ 9A1D063F1D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift in Sources */, 9A1D06371D93EA7E00ACF44C /* UIActivityIndicatorViewTests.swift in Sources */, 9A1D06471D93EA7E00ACF44C /* UILabelTests.swift in Sources */, - 9A1D06431D93EA7E00ACF44C /* UIDatePickerTests.swift in Sources */, 9A1D06391D93EA7E00ACF44C /* UIBarButtonItemTests.swift in Sources */, 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */, 9A1D06411D93EA7E00ACF44C /* UIControlTests.swift in Sources */, 9A1D06491D93EA7E00ACF44C /* UIProgressViewTests.swift in Sources */, 9A1D06511D93EA7E00ACF44C /* UITableViewHeaderFooterViewTests.swift in Sources */, + 9A1D065F1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, BEE020661D637B0000DF261F /* TestError.swift in Sources */, 9A1D06531D93EA7E00ACF44C /* UITextFieldTests.swift in Sources */, ); @@ -973,6 +974,7 @@ buildActionMask = 2147483647; files = ( 9A1D05E71D93E9E200ACF44C /* Reusable.swift in Sources */, + 9A1D065D1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, C7142DBE1CDEA194009F402D /* CocoaAction.swift in Sources */, CD0C45E01CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9A1D05E21D93E99100ACF44C /* Association.swift in Sources */, @@ -990,6 +992,7 @@ 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, 9A1D05FE1D93E9F900ACF44C /* NSTextField.swift in Sources */, 9A1D05E01D93E99100ACF44C /* Association.swift in Sources */, + 9A1D065B1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, C7142DBC1CDEA167009F402D /* CocoaAction.swift in Sources */, 4A0E10FF1D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, ); @@ -1026,6 +1029,7 @@ 9A1D06021D93EA0000ACF44C /* UIButton.swift in Sources */, 9A1D060A1D93EA0000ACF44C /* UISwitch.swift in Sources */, 9A1D06101D93EA0000ACF44C /* UIViewController.swift in Sources */, + 9A1D065C1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, 9A1D06031D93EA0000ACF44C /* UICollectionReusableView.swift in Sources */, 9A1D06071D93EA0000ACF44C /* UILabel.swift in Sources */, 4A0E11001D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, diff --git a/ReactiveCocoa/Association.swift b/ReactiveCocoa/Association.swift index f2eff75638..b2e73cf5ae 100644 --- a/ReactiveCocoa/Association.swift +++ b/ReactiveCocoa/Association.swift @@ -53,7 +53,7 @@ public func associatedProperty(_ host: Host, key: UnsafeRawP setUp(property) - property.producer.startWithNext { [weak host] next in + property.producer.startWithValues { [weak host] next in if let host = host { setter(host, next) } diff --git a/ReactiveCocoa/CocoaAction.swift b/ReactiveCocoa/CocoaAction.swift index f4e65b57ab..e327fb4bd7 100644 --- a/ReactiveCocoa/CocoaAction.swift +++ b/ReactiveCocoa/CocoaAction.swift @@ -1,9 +1,17 @@ import Foundation import ReactiveSwift +import enum Result.NoError /// Wraps an Action for use by a GUI control (such as `NSControl` or /// `UIControl`), with KVO, or with Cocoa Bindings. public final class CocoaAction: NSObject { + /// Creates an always disabled action that can be used as a default for + /// things like `rac_pressed`. + public static var disabled: CocoaAction { + return CocoaAction(Action(enabledIf: Property(value: false)) { _ in .empty }, + input: nil) + } + /// The selector that a caller should invoke upon a CocoaAction in order to /// execute it. public static let selector: Selector = #selector(CocoaAction.execute(_:)) @@ -19,7 +27,19 @@ public final class CocoaAction: NSObject { /// This property will only change on the main thread, and will generate a /// KVO notification for every change. public private(set) var isExecuting: Bool = false - + + /// Creates a producer for the `enabled` state of a CocoaAction. + public var isEnabledProducer: SignalProducer { + return values(forKeyPath: #keyPath(CocoaAction.isEnabled)) + .map { $0 as! Bool } + } + + /// Creates a producer for the `executing` state of a CocoaAction. + public var isExecutingProducer: SignalProducer { + return values(forKeyPath: #keyPath(CocoaAction.isExecuting)) + .map { $0 as! Bool } + } + private let _execute: (AnyObject?) -> Void private let disposable = CompositeDisposable() diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift new file mode 100644 index 0000000000..8f3597a2d2 --- /dev/null +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -0,0 +1,9 @@ +import Foundation +import ReactiveSwift +import enum Result.NoError + +extension NSObject { + public func signal(for selector: Selector) -> Signal<(), NoError> { + return .empty + } +} diff --git a/ReactiveCocoa/Reusable.swift b/ReactiveCocoa/Reusable.swift index 0ed394a7b1..97578ca05a 100644 --- a/ReactiveCocoa/Reusable.swift +++ b/ReactiveCocoa/Reusable.swift @@ -6,18 +6,17 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import Foundation import ReactiveSwift -import ReactiveCocoa import enum Result.NoError /// A protocol for components that can be reused using `prepareForReuse`. -public protocol Reusable { - var rac_prepareForReuseSignal: RACSignal! { get } +@objc public protocol Reusable: class { + func prepareForReuse() } -extension Reusable { - +extension Reusable where Self: NSObject { /// A signal which will send a `Next` event whenever `prepareForReuse` is invoked upon /// the receiver. /// @@ -41,7 +40,6 @@ extension Reusable { /// ``` /// public var rex_prepareForReuse: Signal { - return rac_prepareForReuseSignal - .rex_toTriggerSignal() + return signal(for: #selector(prepareForReuse)) } } diff --git a/ReactiveCocoa/UIKit/UIBarButtonItem.swift b/ReactiveCocoa/UIKit/UIBarButtonItem.swift index bea89d2b32..cfa1d72a53 100644 --- a/ReactiveCocoa/UIKit/UIBarButtonItem.swift +++ b/ReactiveCocoa/UIKit/UIBarButtonItem.swift @@ -16,16 +16,16 @@ extension UIBarButtonItem { /// property on the button. public var rex_action: MutableProperty { return associatedObject(self, key: &actionKey) { host in - let initial = CocoaAction.rex_disabled + let initial = CocoaAction.disabled let property = MutableProperty(initial) property.producer - .startWithNext { [weak host] action in + .startWithValues { [weak host] action in host?.target = action host?.action = CocoaAction.selector } - host.rex_enabled <~ property.producer.flatMap(.latest) { $0.rex_enabledProducer } + host.rex_enabled <~ property.producer.flatMap(.latest) { $0.isEnabledProducer } return property } diff --git a/ReactiveCocoa/UIKit/UIButton.swift b/ReactiveCocoa/UIKit/UIButton.swift index 59f757b9ad..6457dfaadb 100644 --- a/ReactiveCocoa/UIKit/UIButton.swift +++ b/ReactiveCocoa/UIKit/UIButton.swift @@ -16,17 +16,17 @@ extension UIButton { /// action to the `rex_enabled` property on the button. public var rex_pressed: MutableProperty { return associatedObject(self, key: &pressedKey) { host in - let initial = CocoaAction.rex_disabled + let initial = CocoaAction.disabled let property = MutableProperty(initial) property.producer .combinePrevious(initial) - .startWithNext { [weak host] previous, next in + .startWithValues { [weak host] previous, next in 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.isEnabledProducer } return property } diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index 3c686daac2..35066e6ddb 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -11,16 +11,11 @@ import UIKit import enum Result.NoError extension UIControl { - - #if os(iOS) - /// Creates a producer for the sender whenever a specified control event is triggered. - public func rex_controlEvents(_ events: UIControlEvents) -> SignalProducer { - return rac_signal(for: events) - .toSignalProducer() - .map { $0 as? UIControl } - .flatMapError { _ in SignalProducer(value: nil) } + public func trigger(for events: UIControlEvents) -> Signal<(), NoError> { + return .empty } + #if os(iOS) /// Creates a bindable property to wrap a control's value. /// /// This property uses `UIControlEvents.ValueChanged` and `UIControlEvents.EditingChanged` @@ -29,9 +24,8 @@ extension UIControl { 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]) - .filterMap { $0 as? Host } - .filterMap(getter) + host.trigger(for: [.valueChanged, .editingChanged]) + .map { [unowned host] in getter(host) } } } #endif diff --git a/ReactiveCocoa/UIKit/UISegmentedControl.swift b/ReactiveCocoa/UIKit/UISegmentedControl.swift index 975b3db9d5..6daa7d2036 100644 --- a/ReactiveCocoa/UIKit/UISegmentedControl.swift +++ b/ReactiveCocoa/UIKit/UISegmentedControl.swift @@ -13,8 +13,8 @@ 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) - .filterMap { ($0 as? UISegmentedControl)?.selectedSegmentIndex } + property <~ trigger(for: .valueChanged) + .map { [unowned self] in self.selectedSegmentIndex } return property } } diff --git a/ReactiveCocoa/UIKit/UITextField.swift b/ReactiveCocoa/UIKit/UITextField.swift index 03b8ddff18..e6b5bd4e72 100644 --- a/ReactiveCocoa/UIKit/UITextField.swift +++ b/ReactiveCocoa/UIKit/UITextField.swift @@ -21,7 +21,7 @@ extension UITextField { property <~ NotificationCenter.default .rac_notifications(forName: .UITextFieldTextDidChange, object: self) - .filterMap { ($0.object as? UITextField)?.text } + .map { ($0.object as! UITextField).text } } #endif } diff --git a/ReactiveCocoa/UIKit/UITextView.swift b/ReactiveCocoa/UIKit/UITextView.swift index 9302abe7c6..e4fecb4cce 100644 --- a/ReactiveCocoa/UIKit/UITextView.swift +++ b/ReactiveCocoa/UIKit/UITextView.swift @@ -15,6 +15,7 @@ extension UITextView { public var rex_text: SignalProducer { return NotificationCenter.default .rac_notifications(forName: .UITextViewTextDidChange, object: self) - .filterMap { ($0.object as? UITextView)?.text } + .map { ($0.object as? UITextView)?.text } + .skipNil() } } diff --git a/ReactiveCocoa/UIKit/UIViewController.swift b/ReactiveCocoa/UIKit/UIViewController.swift index 17028fa0af..03bbb1f5fe 100644 --- a/ReactiveCocoa/UIKit/UIViewController.swift +++ b/ReactiveCocoa/UIKit/UIViewController.swift @@ -14,31 +14,25 @@ extension UIViewController { /// Returns a `Signal`, that will be triggered /// when `self`'s `viewDidDisappear` is called public var rex_viewDidDisappear: Signal<(), NoError> { - return trigger(for: #selector(UIViewController.viewDidDisappear(_:))) + return signal(for: #selector(UIViewController.viewDidDisappear(_:))) } /// Returns a `Signal`, that will be triggered /// when `self`'s `viewWillDisappear` is called public var rex_viewWillDisappear: Signal<(), NoError> { - return trigger(for: #selector(UIViewController.viewWillDisappear(_:))) + return signal(for: #selector(UIViewController.viewWillDisappear(_:))) } /// Returns a `Signal`, that will be triggered /// when `self`'s `viewDidAppear` is called public var rex_viewDidAppear: Signal<(), NoError> { - return trigger(for: #selector(UIViewController.viewDidAppear(_:))) + return signal(for: #selector(UIViewController.viewDidAppear(_:))) } /// Returns a `Signal`, that will be triggered /// when `self`'s `viewWillAppear` is called public var rex_viewWillAppear: Signal<(), NoError> { - return trigger(for: #selector(UIViewController.viewWillAppear(_:))) - } - - private func trigger(for selector: Selector) -> Signal<(), NoError> { - return self - .rac_signal(for: selector) - .rex_toTriggerSignal() + return signal(for: #selector(UIViewController.viewWillAppear(_:))) } public typealias DismissingCompletion = ((Void) -> Void)? @@ -64,9 +58,8 @@ extension UIViewController { } let property = associatedProperty(self, key: &dismissModally, initial: initial, setter: setter) { property in - property <~ self.rac_signal(for: #selector(UIViewController.dismiss)) + property <~ self.signal(for: #selector(UIViewController.dismiss)) .take { _ in property.value != nil } - .rex_toTriggerSignal() .map { _ in return nil } } diff --git a/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift b/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift index 67e7edde80..1750daf5db 100644 --- a/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift @@ -27,9 +27,9 @@ class UIActivityIndicatorTests: XCTestCase { let (pipeSignal, observer) = Signal.pipe() indicatorView.rex_animating <~ SignalProducer(signal: pipeSignal) - observer.sendNext(true) + observer.send(value: true) XCTAssertTrue(indicatorView.isAnimating) - observer.sendNext(false) + observer.send(value: false) XCTAssertFalse(indicatorView.isAnimating) } } diff --git a/ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift b/ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift index 97e79d9636..99c410c61f 100644 --- a/ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift +++ b/ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift @@ -39,9 +39,9 @@ class UIBarButtonItemTests: XCTestCase { barButtonItem.rex_enabled <~ SignalProducer(signal: pipeSignal) - observer.sendNext(false) + observer.send(value: false) XCTAssertFalse(barButtonItem.isEnabled) - observer.sendNext(true) + observer.send(value: true) XCTAssertTrue(barButtonItem.isEnabled) } } diff --git a/ReactiveCocoaTests/UIKit/UIButtonTests.swift b/ReactiveCocoaTests/UIKit/UIButtonTests.swift index 1be613dc23..144379bee3 100644 --- a/ReactiveCocoaTests/UIKit/UIButtonTests.swift +++ b/ReactiveCocoaTests/UIKit/UIButtonTests.swift @@ -63,12 +63,12 @@ class UIButtonTests: XCTestCase { button.setTitle("", for: .selected) button.setTitle("", for: .highlighted) - observer.sendNext(firstTitle) + observer.send(value: firstTitle) XCTAssertEqual(button.title(for: UIControlState()), firstTitle) XCTAssertEqual(button.title(for: .highlighted), "") XCTAssertEqual(button.title(for: .selected), "") - observer.sendNext(secondTitle) + observer.send(value: secondTitle) XCTAssertEqual(button.title(for: UIControlState()), secondTitle) XCTAssertEqual(button.title(for: .highlighted), "") XCTAssertEqual(button.title(for: .selected), "") diff --git a/ReactiveCocoaTests/UIKit/UIControlTests.swift b/ReactiveCocoaTests/UIKit/UIControlTests.swift index 9715c80100..18aab88adb 100644 --- a/ReactiveCocoaTests/UIKit/UIControlTests.swift +++ b/ReactiveCocoaTests/UIKit/UIControlTests.swift @@ -52,9 +52,9 @@ class UIControlTests: XCTestCase { let (pipeSignal, observer) = Signal.pipe() control.rex_enabled <~ SignalProducer(signal: pipeSignal) - observer.sendNext(true) + observer.send(value: true) XCTAssertTrue(control.isEnabled) - observer.sendNext(false) + observer.send(value: false) XCTAssertFalse(control.isEnabled) } @@ -65,9 +65,9 @@ class UIControlTests: XCTestCase { let (pipeSignal, observer) = Signal.pipe() control.rex_selected <~ SignalProducer(signal: pipeSignal) - observer.sendNext(true) + observer.send(value: true) XCTAssertTrue(control.isSelected) - observer.sendNext(false) + observer.send(value: false) XCTAssertFalse(control.isSelected) } @@ -78,9 +78,9 @@ class UIControlTests: XCTestCase { let (pipeSignal, observer) = Signal.pipe() control.rex_highlighted <~ SignalProducer(signal: pipeSignal) - observer.sendNext(true) + observer.send(value: true) XCTAssertTrue(control.isHighlighted) - observer.sendNext(false) + observer.send(value: false) XCTAssertFalse(control.isHighlighted) } @@ -94,14 +94,14 @@ class UIControlTests: XCTestCase { control.rex_selected <~ SignalProducer(signal: pipeSignalSelected) control.rex_enabled <~ SignalProducer(signal: pipeSignalEnabled) - observerSelected.sendNext(true) - observerEnabled.sendNext(true) + observerSelected.send(value: true) + observerEnabled.send(value: true) XCTAssertTrue(control.isEnabled) XCTAssertTrue(control.isSelected) - observerSelected.sendNext(false) + observerSelected.send(value: false) XCTAssertTrue(control.isEnabled) XCTAssertFalse(control.isSelected) - observerEnabled.sendNext(false) + observerEnabled.send(value: false) XCTAssertFalse(control.isEnabled) XCTAssertFalse(control.isSelected) } diff --git a/ReactiveCocoaTests/UIKit/UIDatePickerTests.swift b/ReactiveCocoaTests/UIKit/UIDatePickerTests.swift index f7dac8f377..0a2259c66b 100644 --- a/ReactiveCocoaTests/UIKit/UIDatePickerTests.swift +++ b/ReactiveCocoaTests/UIKit/UIDatePickerTests.swift @@ -10,7 +10,6 @@ import ReactiveSwift import ReactiveCocoa import UIKit import XCTest -import Rex class UIDatePickerTests: XCTestCase { @@ -35,7 +34,7 @@ class UIDatePickerTests: XCTestCase { 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 + picker.rex_date.signal.observeValues { changedDate in XCTAssertEqual(changedDate, self.date) expectation.fulfill() } diff --git a/ReactiveCocoaTests/UIKit/UIImageViewTests.swift b/ReactiveCocoaTests/UIKit/UIImageViewTests.swift index ccb1fcc2c7..1df2a1484d 100644 --- a/ReactiveCocoaTests/UIKit/UIImageViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UIImageViewTests.swift @@ -50,9 +50,9 @@ class UIImageViewTests: XCTestCase { let (pipeSignal, observer) = Signal.pipe() imageView.rex_image <~ SignalProducer(signal: pipeSignal) - observer.sendNext(firstChange) + observer.send(value: firstChange) XCTAssertEqual(imageView.image, firstChange) - observer.sendNext(secondChange) + observer.send(value: secondChange) XCTAssertEqual(imageView.image, secondChange) } @@ -65,9 +65,9 @@ class UIImageViewTests: XCTestCase { let (pipeSignal, observer) = Signal.pipe() imageView.rex_highlightedImage <~ SignalProducer(signal: pipeSignal) - observer.sendNext(firstChange) + observer.send(value: firstChange) XCTAssertEqual(imageView.highlightedImage, firstChange) - observer.sendNext(secondChange) + observer.send(value: secondChange) XCTAssertEqual(imageView.highlightedImage, secondChange) } } diff --git a/ReactiveCocoaTests/UIKit/UILabelTests.swift b/ReactiveCocoaTests/UIKit/UILabelTests.swift index 8c826c3e9c..565d0da85c 100644 --- a/ReactiveCocoaTests/UIKit/UILabelTests.swift +++ b/ReactiveCocoaTests/UIKit/UILabelTests.swift @@ -39,11 +39,11 @@ class UILabelTests: XCTestCase { let (pipeSignal, observer) = Signal.pipe() label.rex_text <~ SignalProducer(signal: pipeSignal) - observer.sendNext(firstChange) + observer.send(value: firstChange) XCTAssertEqual(label.text, firstChange) - observer.sendNext(secondChange) + observer.send(value: secondChange) XCTAssertEqual(label.text, secondChange) - observer.sendNext(nil) + observer.send(value: nil) XCTAssertNil(label.text) } @@ -65,9 +65,9 @@ class UILabelTests: XCTestCase { let (pipeSignal, observer) = Signal.pipe() label.rex_attributedText <~ SignalProducer(signal: pipeSignal) - observer.sendNext(firstChange) + observer.send(value: firstChange) XCTAssertEqual(label.attributedText, firstChange) - observer.sendNext(secondChange) + observer.send(value: secondChange) XCTAssertEqual(label.attributedText, secondChange) } @@ -81,9 +81,9 @@ class UILabelTests: XCTestCase { label.textColor = UIColor.black label.rex_textColor <~ SignalProducer(signal: pipeSignal) - observer.sendNext(firstChange) + observer.send(value: firstChange) XCTAssertEqual(label.textColor, firstChange) - observer.sendNext(secondChange) + observer.send(value: secondChange) XCTAssertEqual(label.textColor, secondChange) } } diff --git a/ReactiveCocoaTests/UIKit/UIProgressViewTests.swift b/ReactiveCocoaTests/UIKit/UIProgressViewTests.swift index c8300d9326..73d6d5f78b 100644 --- a/ReactiveCocoaTests/UIKit/UIProgressViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UIProgressViewTests.swift @@ -38,9 +38,9 @@ class UIProgressViewTests: XCTestCase { let (pipeSignal, observer) = Signal.pipe() progressView.rex_progress <~ SignalProducer(signal: pipeSignal) - observer.sendNext(firstChange) + observer.send(value: firstChange) XCTAssertEqual(progressView.progress, firstChange) - observer.sendNext(secondChange) + observer.send(value: secondChange) XCTAssertEqual(progressView.progress, secondChange) } } diff --git a/ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift b/ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift index 9a9f8e7206..944c62ceeb 100644 --- a/ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift +++ b/ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift @@ -22,9 +22,9 @@ class UISegmentedControlTests: XCTestCase { s.rex_selectedSegmentIndex <~ SignalProducer(signal: pipeSignal) XCTAssertEqual(s.selectedSegmentIndex, UISegmentedControlNoSegment) - observer.sendNext(1) + observer.send(value: 1) XCTAssertEqual(s.selectedSegmentIndex, 1) - observer.sendNext(2) + observer.send(value: 2) XCTAssertEqual(s.selectedSegmentIndex, 2) } } diff --git a/ReactiveCocoaTests/UIKit/UISwitchTests.swift b/ReactiveCocoaTests/UIKit/UISwitchTests.swift index 684914e4b6..823b03de15 100644 --- a/ReactiveCocoaTests/UIKit/UISwitchTests.swift +++ b/ReactiveCocoaTests/UIKit/UISwitchTests.swift @@ -20,9 +20,9 @@ class UISwitchTests: XCTestCase { let (pipeSignal, observer) = Signal.pipe() `switch`.rex_on <~ SignalProducer(signal: pipeSignal) - observer.sendNext(true) + observer.send(value: true) XCTAssertTrue(`switch`.isOn) - observer.sendNext(false) + observer.send(value: false) XCTAssertFalse(`switch`.isOn) `switch`.isOn = true diff --git a/ReactiveCocoaTests/UIKit/UITextFieldTests.swift b/ReactiveCocoaTests/UIKit/UITextFieldTests.swift index 67434e0b96..f11de7fd64 100644 --- a/ReactiveCocoaTests/UIKit/UITextFieldTests.swift +++ b/ReactiveCocoaTests/UIKit/UITextFieldTests.swift @@ -20,7 +20,7 @@ class UITextFieldTests: XCTestCase { let textField = UITextField(frame: CGRect.zero) textField.text = "Test" - textField.rex_text.signal.observeNext { text in + textField.rex_text.signal.observeValues { text in XCTAssertEqual(text, textField.text) expectation.fulfill() } diff --git a/ReactiveCocoaTests/UIKit/UITextViewTests.swift b/ReactiveCocoaTests/UIKit/UITextViewTests.swift index 28a0836add..8fea03588e 100644 --- a/ReactiveCocoaTests/UIKit/UITextViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UITextViewTests.swift @@ -20,7 +20,7 @@ class UITextViewTests: XCTestCase { let textView = UITextView(frame: CGRect.zero) textView.text = "Test" - textView.rex_text.startWithNext { text in + textView.rex_text.startWithValues { text in XCTAssertEqual(text, textView.text) expectation.fulfill() } diff --git a/ReactiveCocoaTests/UIKit/UIViewControllerTests.swift b/ReactiveCocoaTests/UIKit/UIViewControllerTests.swift index 931992ea3a..5276025160 100644 --- a/ReactiveCocoaTests/UIKit/UIViewControllerTests.swift +++ b/ReactiveCocoaTests/UIKit/UIViewControllerTests.swift @@ -29,7 +29,7 @@ class UIViewControllerTests: XCTestCase { let viewController = UIViewController() _viewController = viewController - viewController.rex_viewDidDisappear.observeNext { + viewController.rex_viewDidDisappear.observeValues { expectation.fulfill() } @@ -44,7 +44,7 @@ class UIViewControllerTests: XCTestCase { let viewController = UIViewController() _viewController = viewController - viewController.rex_viewWillDisappear.observeNext { + viewController.rex_viewWillDisappear.observeValues { expectation.fulfill() } @@ -59,7 +59,7 @@ class UIViewControllerTests: XCTestCase { let viewController = UIViewController() _viewController = viewController - viewController.rex_viewDidAppear.observeNext { + viewController.rex_viewDidAppear.observeValues { expectation.fulfill() } @@ -74,7 +74,7 @@ class UIViewControllerTests: XCTestCase { let viewController = UIViewController() _viewController = viewController - viewController.rex_viewWillAppear.observeNext { + viewController.rex_viewWillAppear.observeValues { expectation.fulfill() } @@ -89,7 +89,7 @@ class UIViewControllerTests: XCTestCase { let viewController = UIViewController() _viewController = viewController - viewController.rex_dismissAnimated.signal.observeNext { _ in + viewController.rex_dismissAnimated.signal.observeValues { _ in expectation.fulfill() } @@ -104,7 +104,7 @@ class UIViewControllerTests: XCTestCase { let viewController = UIViewController() _viewController = viewController - viewController.rex_dismissAnimated.signal.observeNext { _ in + viewController.rex_dismissAnimated.signal.observeValues { _ in expectation.fulfill() } diff --git a/ReactiveCocoaTests/UIKit/UIViewTests.swift b/ReactiveCocoaTests/UIKit/UIViewTests.swift index 6a5b9c6df6..4c8bfd2793 100644 --- a/ReactiveCocoaTests/UIKit/UIViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UIViewTests.swift @@ -44,9 +44,9 @@ class UIViewTests: XCTestCase { let (pipeSignal, observer) = Signal.pipe() view.rex_hidden <~ SignalProducer(signal: pipeSignal) - observer.sendNext(true) + observer.send(value: true) XCTAssertTrue(view.isHidden) - observer.sendNext(false) + observer.send(value: false) XCTAssertFalse(view.isHidden) } @@ -60,9 +60,9 @@ class UIViewTests: XCTestCase { let (pipeSignal, observer) = Signal.pipe() view.rex_alpha <~ SignalProducer(signal: pipeSignal) - observer.sendNext(firstChange) + observer.send(value: firstChange) XCTAssertEqualWithAccuracy(view.alpha, firstChange, accuracy: 0.01) - observer.sendNext(secondChange) + observer.send(value: secondChange) XCTAssertEqualWithAccuracy(view.alpha, secondChange, accuracy: 0.01) } @@ -73,9 +73,9 @@ class UIViewTests: XCTestCase { let (pipeSignal, observer) = Signal.pipe() view.rex_userInteractionEnabled <~ SignalProducer(signal: pipeSignal) - observer.sendNext(true) + observer.send(value: true) XCTAssertTrue(view.isUserInteractionEnabled) - observer.sendNext(false) + observer.send(value: false) XCTAssertFalse(view.isUserInteractionEnabled) } } From 060990f44f63ab02d3ac8319bbdb6b8b30e7fafa Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 22 Sep 2016 13:14:50 +0200 Subject: [PATCH 0391/1028] Renamed `rex_` to `rac_`. --- ReactiveCocoa/AppKit/NSTextField.swift | 2 +- ReactiveCocoa/Reusable.swift | 10 +++---- .../UIKit/UIActivityIndicatorView.swift | 2 +- ReactiveCocoa/UIKit/UIBarButtonItem.swift | 6 ++--- ReactiveCocoa/UIKit/UIBarItem.swift | 2 +- ReactiveCocoa/UIKit/UIButton.swift | 8 +++--- ReactiveCocoa/UIKit/UIControl.swift | 8 +++--- ReactiveCocoa/UIKit/UIDatePicker.swift | 4 +-- ReactiveCocoa/UIKit/UIImageView.swift | 4 +-- ReactiveCocoa/UIKit/UILabel.swift | 6 ++--- ReactiveCocoa/UIKit/UIProgressView.swift | 2 +- ReactiveCocoa/UIKit/UISegmentedControl.swift | 2 +- ReactiveCocoa/UIKit/UISwitch.swift | 4 +-- ReactiveCocoa/UIKit/UITextField.swift | 4 +-- ReactiveCocoa/UIKit/UITextView.swift | 2 +- ReactiveCocoa/UIKit/UIView.swift | 6 ++--- ReactiveCocoa/UIKit/UIViewController.swift | 12 ++++----- .../UIKit/UIActivityIndicatorViewTests.swift | 2 +- .../UIKit/UIBarButtonItemTests.swift | 4 +-- ReactiveCocoaTests/UIKit/UIButtonTests.swift | 10 +++---- .../UIKit/UICollectionReusableViewTests.swift | 4 +-- ...ol+EnableSendActionsForControlEvents.swift | 8 +++--- ReactiveCocoaTests/UIKit/UIControlTests.swift | 16 ++++++------ .../UIKit/UIDatePickerTests.swift | 6 ++--- .../UIKit/UIImageViewTests.swift | 8 +++--- ReactiveCocoaTests/UIKit/UILabelTests.swift | 10 +++---- .../UIKit/UIProgressViewTests.swift | 4 +-- .../UIKit/UISegmentedControlTests.swift | 2 +- ReactiveCocoaTests/UIKit/UISwitchTests.swift | 4 +-- .../UIKit/UITableViewCellTests.swift | 4 +-- .../UITableViewHeaderFooterViewTests.swift | 4 +-- .../UIKit/UITextFieldTests.swift | 4 +-- .../UIKit/UITextViewTests.swift | 4 +-- .../UIKit/UIViewControllerTests.swift | 26 +++++++++---------- ReactiveCocoaTests/UIKit/UIViewTests.swift | 10 +++---- 35 files changed, 107 insertions(+), 107 deletions(-) diff --git a/ReactiveCocoa/AppKit/NSTextField.swift b/ReactiveCocoa/AppKit/NSTextField.swift index e249cab085..6da27fdb6f 100644 --- a/ReactiveCocoa/AppKit/NSTextField.swift +++ b/ReactiveCocoa/AppKit/NSTextField.swift @@ -12,7 +12,7 @@ import enum Result.NoError extension NSTextField { /// Sends the field's string value whenever it changes. - public var rex_textSignal: SignalProducer { + public var rac_textSignal: SignalProducer { return NotificationCenter.default .rac_notifications(forName: .NSControlTextDidChange, object: self) .map { notification in diff --git a/ReactiveCocoa/Reusable.swift b/ReactiveCocoa/Reusable.swift index 97578ca05a..d6d42c0dfe 100644 --- a/ReactiveCocoa/Reusable.swift +++ b/ReactiveCocoa/Reusable.swift @@ -27,19 +27,19 @@ extension Reusable where Self: NSObject { /// /// ``` /// button - /// .rex_controlEvents(.TouchUpInside) - /// .takeUntil(self.rex_prepareForReuse) + /// .rac_controlEvents(.TouchUpInside) + /// .takeUntil(self.rac_prepareForReuse) /// .startWithNext { _ in /// // do other things /// } /// - /// label.rex_text <~ + /// label.rac_text <~ /// titleProperty /// .producer - /// .takeUntil(self.rex_prepareForReuse) + /// .takeUntil(self.rac_prepareForReuse) /// ``` /// - public var rex_prepareForReuse: Signal { + public var rac_prepareForReuse: Signal { return signal(for: #selector(prepareForReuse)) } } diff --git a/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift b/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift index 072be740d3..696940ea46 100644 --- a/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift +++ b/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift @@ -14,7 +14,7 @@ extension UIActivityIndicatorView { /// Wraps an indicator's `isAnimating()` state in a bindable property. /// Setting a new value to the property would call `startAnimating()` or /// `stopAnimating()` depending on the value. - public var rex_animating: MutableProperty { + public var rac_animating: MutableProperty { return associatedProperty(self, key: &animatingKey, initial: { $0.isAnimating }, setter: { host, animating in if animating { host.startAnimating() diff --git a/ReactiveCocoa/UIKit/UIBarButtonItem.swift b/ReactiveCocoa/UIKit/UIBarButtonItem.swift index cfa1d72a53..d45f2b3f9c 100644 --- a/ReactiveCocoa/UIKit/UIBarButtonItem.swift +++ b/ReactiveCocoa/UIKit/UIBarButtonItem.swift @@ -12,9 +12,9 @@ import UIKit extension UIBarButtonItem { /// Exposes a property that binds an action to bar button item. The action is set as /// a target of the button. When property changes occur the previous action is - /// overwritten. This also binds the enabled state of the action to the `rex_enabled` + /// overwritten. This also binds the enabled state of the action to the `rac_enabled` /// property on the button. - public var rex_action: MutableProperty { + public var rac_action: MutableProperty { return associatedObject(self, key: &actionKey) { host in let initial = CocoaAction.disabled let property = MutableProperty(initial) @@ -25,7 +25,7 @@ extension UIBarButtonItem { host?.action = CocoaAction.selector } - host.rex_enabled <~ property.producer.flatMap(.latest) { $0.isEnabledProducer } + host.rac_enabled <~ property.producer.flatMap(.latest) { $0.isEnabledProducer } return property } diff --git a/ReactiveCocoa/UIKit/UIBarItem.swift b/ReactiveCocoa/UIKit/UIBarItem.swift index fb90309c9a..95806e1ee1 100644 --- a/ReactiveCocoa/UIKit/UIBarItem.swift +++ b/ReactiveCocoa/UIKit/UIBarItem.swift @@ -11,7 +11,7 @@ import UIKit extension UIBarItem { /// Wraps a UIBarItem's `enabled` state in a bindable property. - public var rex_enabled: MutableProperty { + public var rac_enabled: MutableProperty { return associatedProperty(self, key: &enabledKey, initial: { $0.isEnabled }, setter: { $0.isEnabled = $1 }) } } diff --git a/ReactiveCocoa/UIKit/UIButton.swift b/ReactiveCocoa/UIKit/UIButton.swift index 6457dfaadb..50b7a56c99 100644 --- a/ReactiveCocoa/UIKit/UIButton.swift +++ b/ReactiveCocoa/UIKit/UIButton.swift @@ -13,8 +13,8 @@ extension UIButton { /// Exposes a property that binds an action to button presses. The action is set as /// a target of the button for `TouchUpInside` events. When property changes occur the /// previous action is removed as a target. This also binds the enabled state of the - /// action to the `rex_enabled` property on the button. - public var rex_pressed: MutableProperty { + /// action to the `rac_enabled` property on the button. + public var rac_pressed: MutableProperty { return associatedObject(self, key: &pressedKey) { host in let initial = CocoaAction.disabled let property = MutableProperty(initial) @@ -26,7 +26,7 @@ extension UIButton { host?.addTarget(next, action: CocoaAction.selector, for: .touchUpInside) } - host.rex_enabled <~ property.producer.flatMap(.latest) { $0.isEnabledProducer } + host.rac_enabled <~ property.producer.flatMap(.latest) { $0.isEnabledProducer } return property } @@ -34,7 +34,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 { + public var rac_title: MutableProperty { return associatedProperty(self, key: &titleKey, initial: { $0.title(for: .normal) ?? "" }, setter: { $0.setTitle($1, for: .normal) }) } } diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index 35066e6ddb..8131ae9b64 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -21,7 +21,7 @@ extension UIControl { /// This property uses `UIControlEvents.ValueChanged` and `UIControlEvents.EditingChanged` /// events to detect changes and keep the value up-to-date. // - class func rex_value(_ host: Host, getter: @escaping (Host) -> T, setter: @escaping (Host, T) -> ()) -> MutableProperty { + class func rac_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.trigger(for: [.valueChanged, .editingChanged]) @@ -31,17 +31,17 @@ extension UIControl { #endif /// Wraps a control's `enabled` state in a bindable property. - public var rex_enabled: MutableProperty { + public var rac_enabled: MutableProperty { 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 { + public var rac_selected: MutableProperty { 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 { + public var rac_highlighted: MutableProperty { return associatedProperty(self, key: &highlightedKey, initial: { $0.isHighlighted }, setter: { $0.isHighlighted = $1 }) } } diff --git a/ReactiveCocoa/UIKit/UIDatePicker.swift b/ReactiveCocoa/UIKit/UIDatePicker.swift index fe5d4e1c6b..8db51420b3 100644 --- a/ReactiveCocoa/UIKit/UIDatePicker.swift +++ b/ReactiveCocoa/UIKit/UIDatePicker.swift @@ -11,7 +11,7 @@ import UIKit extension UIDatePicker { // Wraps a datePicker's `date` value in a bindable property. - public var rex_date: MutableProperty { - return UIControl.rex_value(self, getter: { $0.date }, setter: { $0.date = $1 }) + public var rac_date: MutableProperty { + return UIControl.rac_value(self, getter: { $0.date }, setter: { $0.date = $1 }) } } diff --git a/ReactiveCocoa/UIKit/UIImageView.swift b/ReactiveCocoa/UIKit/UIImageView.swift index 436d6b8424..15a694bffd 100644 --- a/ReactiveCocoa/UIKit/UIImageView.swift +++ b/ReactiveCocoa/UIKit/UIImageView.swift @@ -11,12 +11,12 @@ import UIKit extension UIImageView { /// Wraps a imageView's `image` value in a bindable property. - public var rex_image: MutableProperty { + public var rac_image: MutableProperty { return associatedProperty(self, key: &imageKey, initial: { $0.image }, setter: { $0.image = $1 }) } /// Wraps a imageView's `highlightedImage` value in a bindable property. - public var rex_highlightedImage: MutableProperty { + public var rac_highlightedImage: MutableProperty { return associatedProperty(self, key: &highlightedImageKey, initial: { $0.highlightedImage }, setter: { $0.highlightedImage = $1 }) } } diff --git a/ReactiveCocoa/UIKit/UILabel.swift b/ReactiveCocoa/UIKit/UILabel.swift index 9df03bd788..669fdaf7c8 100644 --- a/ReactiveCocoa/UIKit/UILabel.swift +++ b/ReactiveCocoa/UIKit/UILabel.swift @@ -11,17 +11,17 @@ import UIKit extension UILabel { /// Wraps a label's `text` value in a bindable property. - public var rex_text: MutableProperty { + public var rac_text: MutableProperty { return associatedProperty(self, key: &attributedTextKey, initial: { $0.text }, setter: { $0.text = $1 }) } /// Wraps a label's `attributedText` value in a bindable property. - public var rex_attributedText: MutableProperty { + public var rac_attributedText: MutableProperty { return associatedProperty(self, key: &attributedTextKey, initial: { $0.attributedText }, setter: { $0.attributedText = $1 }) } /// Wraps a label's `textColor` value in a bindable property. - public var rex_textColor: MutableProperty { + public var rac_textColor: MutableProperty { return associatedProperty(self, key: &textColorKey, initial: { $0.textColor }, setter: { $0.textColor = $1 }) } } diff --git a/ReactiveCocoa/UIKit/UIProgressView.swift b/ReactiveCocoa/UIKit/UIProgressView.swift index 37af2255fd..2639987b6e 100644 --- a/ReactiveCocoa/UIKit/UIProgressView.swift +++ b/ReactiveCocoa/UIKit/UIProgressView.swift @@ -11,7 +11,7 @@ import UIKit extension UIProgressView { /// Wraps a progressView's `progress` value in a bindable property. - public var rex_progress: MutableProperty { + public var rac_progress: MutableProperty { return associatedProperty(self, key: &progressKey, initial: { $0.progress }, setter: { $0.progress = $1 }) } } diff --git a/ReactiveCocoa/UIKit/UISegmentedControl.swift b/ReactiveCocoa/UIKit/UISegmentedControl.swift index 6daa7d2036..8c14b1b1ad 100644 --- a/ReactiveCocoa/UIKit/UISegmentedControl.swift +++ b/ReactiveCocoa/UIKit/UISegmentedControl.swift @@ -11,7 +11,7 @@ import UIKit extension UISegmentedControl { /// Wraps a segmentedControls `selectedSegmentIndex` state in a bindable property. - public var rex_selectedSegmentIndex: MutableProperty { + public var rac_selectedSegmentIndex: MutableProperty { let property = associatedProperty(self, key: &selectedSegmentIndexKey, initial: { $0.selectedSegmentIndex }, setter: { $0.selectedSegmentIndex = $1 }) property <~ trigger(for: .valueChanged) .map { [unowned self] in self.selectedSegmentIndex } diff --git a/ReactiveCocoa/UIKit/UISwitch.swift b/ReactiveCocoa/UIKit/UISwitch.swift index d5dbdb5c59..80002f7f86 100644 --- a/ReactiveCocoa/UIKit/UISwitch.swift +++ b/ReactiveCocoa/UIKit/UISwitch.swift @@ -11,7 +11,7 @@ import UIKit extension UISwitch { /// Wraps a switch's `on` value in a bindable property. - public var rex_on: MutableProperty { - return UIControl.rex_value(self, getter: { $0.isOn }, setter: { $0.isOn = $1 }) + public var rac_on: MutableProperty { + return UIControl.rac_value(self, getter: { $0.isOn }, setter: { $0.isOn = $1 }) } } diff --git a/ReactiveCocoa/UIKit/UITextField.swift b/ReactiveCocoa/UIKit/UITextField.swift index e6b5bd4e72..fa1ec5940c 100644 --- a/ReactiveCocoa/UIKit/UITextField.swift +++ b/ReactiveCocoa/UIKit/UITextField.swift @@ -11,11 +11,11 @@ import UIKit extension UITextField { /// Wraps a textField's `text` value in a bindable property. - public var rex_text: MutableProperty { + public var rac_text: MutableProperty { 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) + return UIControl.rac_value(self, getter: getter, setter: setter) #else return associatedProperty(self, key: &textKey, initial: getter, setter: setter) { property in property <~ diff --git a/ReactiveCocoa/UIKit/UITextView.swift b/ReactiveCocoa/UIKit/UITextView.swift index e4fecb4cce..46c295dc92 100644 --- a/ReactiveCocoa/UIKit/UITextView.swift +++ b/ReactiveCocoa/UIKit/UITextView.swift @@ -12,7 +12,7 @@ import enum Result.NoError extension UITextView { /// Sends the textView's string value whenever it changes. - public var rex_text: SignalProducer { + public var rac_text: SignalProducer { return NotificationCenter.default .rac_notifications(forName: .UITextViewTextDidChange, object: self) .map { ($0.object as? UITextView)?.text } diff --git a/ReactiveCocoa/UIKit/UIView.swift b/ReactiveCocoa/UIKit/UIView.swift index 48740c0ef2..c179b7df1b 100644 --- a/ReactiveCocoa/UIKit/UIView.swift +++ b/ReactiveCocoa/UIKit/UIView.swift @@ -11,18 +11,18 @@ import UIKit extension UIView { /// Wraps a view's `alpha` value in a bindable property. - public var rex_alpha: MutableProperty { + public var rac_alpha: MutableProperty { return associatedProperty(self, key: &alphaKey, initial: { $0.alpha }, setter: { $0.alpha = $1 }) } /// Wraps a view's `hidden` state in a bindable property. - public var rex_hidden: MutableProperty { + public var rac_hidden: MutableProperty { 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 { + public var rac_userInteractionEnabled: MutableProperty { return associatedProperty(self, key: &userInteractionEnabledKey, initial: { $0.isUserInteractionEnabled }, setter: { $0.isUserInteractionEnabled = $1 }) } } diff --git a/ReactiveCocoa/UIKit/UIViewController.swift b/ReactiveCocoa/UIKit/UIViewController.swift index 03bbb1f5fe..861bed044b 100644 --- a/ReactiveCocoa/UIKit/UIViewController.swift +++ b/ReactiveCocoa/UIKit/UIViewController.swift @@ -13,25 +13,25 @@ import UIKit extension UIViewController { /// Returns a `Signal`, that will be triggered /// when `self`'s `viewDidDisappear` is called - public var rex_viewDidDisappear: Signal<(), NoError> { + public var rac_viewDidDisappear: Signal<(), NoError> { return signal(for: #selector(UIViewController.viewDidDisappear(_:))) } /// Returns a `Signal`, that will be triggered /// when `self`'s `viewWillDisappear` is called - public var rex_viewWillDisappear: Signal<(), NoError> { + public var rac_viewWillDisappear: Signal<(), NoError> { return signal(for: #selector(UIViewController.viewWillDisappear(_:))) } /// Returns a `Signal`, that will be triggered /// when `self`'s `viewDidAppear` is called - public var rex_viewDidAppear: Signal<(), NoError> { + public var rac_viewDidAppear: Signal<(), NoError> { return signal(for: #selector(UIViewController.viewDidAppear(_:))) } /// Returns a `Signal`, that will be triggered /// when `self`'s `viewWillAppear` is called - public var rex_viewWillAppear: Signal<(), NoError> { + public var rac_viewWillAppear: Signal<(), NoError> { return signal(for: #selector(UIViewController.viewWillAppear(_:))) } @@ -44,11 +44,11 @@ extension UIViewController { /// E.g: /// ``` /// //Dismissed with animation (`true`) and `nil` completion - /// viewController.rex_dismissAnimated <~ aProducer.map { _ in (true, nil) } + /// viewController.rac_dismissAnimated <~ aProducer.map { _ in (true, nil) } /// ``` /// The dismissal observation can be made either with binding (example above) /// or `viewController.dismissViewControllerAnimated(true, completion: nil)` - public var rex_dismissAnimated: MutableProperty { + public var rac_dismissAnimated: MutableProperty { let initial: (UIViewController) -> DismissingInformation = { _ in nil } let setter: (UIViewController, DismissingInformation) -> Void = { host, dismissingInfo in diff --git a/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift b/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift index 1750daf5db..01e51f8fea 100644 --- a/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift @@ -25,7 +25,7 @@ class UIActivityIndicatorTests: XCTestCase { _activityIndicatorView = indicatorView let (pipeSignal, observer) = Signal.pipe() - indicatorView.rex_animating <~ SignalProducer(signal: pipeSignal) + indicatorView.rac_animating <~ SignalProducer(signal: pipeSignal) observer.send(value: true) XCTAssertTrue(indicatorView.isAnimating) diff --git a/ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift b/ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift index 99c410c61f..2cd30823eb 100644 --- a/ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift +++ b/ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift @@ -28,7 +28,7 @@ class UIBarButtonItemTests: XCTestCase { let action = Action<(),(),NoError> { SignalProducer(value: ()) } - barButtonItem.rex_action <~ SignalProducer(value: CocoaAction(action, input: ())) + barButtonItem.rac_action <~ SignalProducer(value: CocoaAction(action, input: ())) } func testEnabledProperty() { @@ -36,7 +36,7 @@ class UIBarButtonItemTests: XCTestCase { barButtonItem.isEnabled = true let (pipeSignal, observer) = Signal.pipe() - barButtonItem.rex_enabled <~ SignalProducer(signal: pipeSignal) + barButtonItem.rac_enabled <~ SignalProducer(signal: pipeSignal) observer.send(value: false) diff --git a/ReactiveCocoaTests/UIKit/UIButtonTests.swift b/ReactiveCocoaTests/UIKit/UIButtonTests.swift index 144379bee3..d56c02e75f 100644 --- a/ReactiveCocoaTests/UIKit/UIButtonTests.swift +++ b/ReactiveCocoaTests/UIKit/UIButtonTests.swift @@ -32,7 +32,7 @@ class UIButtonTests: XCTestCase { let button = UIButton(frame: CGRect.zero) _button = button - button.rex_enabled <~ SignalProducer(value: false) + button.rac_enabled <~ SignalProducer(value: false) XCTAssert(_button?.isEnabled == false) } @@ -43,14 +43,14 @@ class UIButtonTests: XCTestCase { let action = Action<(),(),NoError> { SignalProducer(value: ()) } - button.rex_pressed <~ SignalProducer(value: CocoaAction(action, input: ())) + button.rac_pressed <~ SignalProducer(value: CocoaAction(action, input: ())) } func testTitlePropertyDoesntCreateRetainCycle() { let button = UIButton(frame: CGRect.zero) _button = button - button.rex_title <~ SignalProducer(value: "button") + button.rac_title <~ SignalProducer(value: "button") XCTAssert(_button?.title(for: UIControlState()) == "button") } @@ -59,7 +59,7 @@ class UIButtonTests: XCTestCase { let secondTitle = "Second title" let button = UIButton(frame: CGRect.zero) let (pipeSignal, observer) = Signal.pipe() - button.rex_title <~ SignalProducer(signal: pipeSignal) + button.rac_title <~ SignalProducer(signal: pipeSignal) button.setTitle("", for: .selected) button.setTitle("", for: .highlighted) @@ -85,7 +85,7 @@ class UIButtonTests: XCTestCase { } pressed <~ SignalProducer(signal: action.values) - button.rex_pressed <~ SignalProducer(value: CocoaAction(action, input: ())) + button.rac_pressed <~ SignalProducer(value: CocoaAction(action, input: ())) XCTAssertFalse(pressed.value) diff --git a/ReactiveCocoaTests/UIKit/UICollectionReusableViewTests.swift b/ReactiveCocoaTests/UIKit/UICollectionReusableViewTests.swift index 68b3c93eb0..a1f37eef0e 100644 --- a/ReactiveCocoaTests/UIKit/UICollectionReusableViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UICollectionReusableViewTests.swift @@ -17,10 +17,10 @@ class UICollectionReusableViewTests: XCTestCase { let cell = UICollectionViewCell() - cell.rex_hidden <~ + cell.rac_hidden <~ hiddenProperty .producer - .take(until: cell.rex_prepareForReuse) + .take(until: cell.rac_prepareForReuse) XCTAssertFalse(cell.isHidden) diff --git a/ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift b/ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift index 2df3f0a2c7..a1ad6b6656 100644 --- a/ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift +++ b/ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift @@ -8,9 +8,9 @@ import UIKit -private let rex_swizzleToken: Void = { +private let rac_swizzleToken: Void = { let originalSelector = #selector(UIControl.sendAction(_:to:for:)) - let swizzledSelector = #selector(UIControl.rex_sendAction(_:to:forEvent:)) + let swizzledSelector = #selector(UIControl.rac_sendAction(_:to:forEvent:)) let originalMethod = class_getInstanceMethod(UIControl.self, originalSelector) let swizzledMethod = class_getInstanceMethod(UIControl.self, swizzledSelector) @@ -44,12 +44,12 @@ extension UIControl { return } - _ = rex_swizzleToken + _ = rac_swizzleToken } // MARK: - Method Swizzling - func rex_sendAction(_ action: Selector, to target: AnyObject?, forEvent event: UIEvent?) { + func rac_sendAction(_ action: Selector, to target: AnyObject?, forEvent event: UIEvent?) { _ = target?.perform(action, with: self) } } diff --git a/ReactiveCocoaTests/UIKit/UIControlTests.swift b/ReactiveCocoaTests/UIKit/UIControlTests.swift index 18aab88adb..ac059a9e3d 100644 --- a/ReactiveCocoaTests/UIKit/UIControlTests.swift +++ b/ReactiveCocoaTests/UIKit/UIControlTests.swift @@ -25,7 +25,7 @@ class UIControlTests: XCTestCase { let control = UIControl(frame: CGRect.zero) _control = control - control.rex_enabled <~ SignalProducer(value: false) + control.rac_enabled <~ SignalProducer(value: false) XCTAssert(_control?.isEnabled == false) } @@ -33,7 +33,7 @@ class UIControlTests: XCTestCase { let control = UIControl(frame: CGRect.zero) _control = control - control.rex_selected <~ SignalProducer(value: true) + control.rac_selected <~ SignalProducer(value: true) XCTAssert(_control?.isSelected == true) } @@ -41,7 +41,7 @@ class UIControlTests: XCTestCase { let control = UIControl(frame: CGRect.zero) _control = control - control.rex_highlighted <~ SignalProducer(value: true) + control.rac_highlighted <~ SignalProducer(value: true) XCTAssert(_control?.isHighlighted == true) } @@ -50,7 +50,7 @@ class UIControlTests: XCTestCase { control.isEnabled = false let (pipeSignal, observer) = Signal.pipe() - control.rex_enabled <~ SignalProducer(signal: pipeSignal) + control.rac_enabled <~ SignalProducer(signal: pipeSignal) observer.send(value: true) XCTAssertTrue(control.isEnabled) @@ -63,7 +63,7 @@ class UIControlTests: XCTestCase { control.isSelected = false let (pipeSignal, observer) = Signal.pipe() - control.rex_selected <~ SignalProducer(signal: pipeSignal) + control.rac_selected <~ SignalProducer(signal: pipeSignal) observer.send(value: true) XCTAssertTrue(control.isSelected) @@ -76,7 +76,7 @@ class UIControlTests: XCTestCase { control.isHighlighted = false let (pipeSignal, observer) = Signal.pipe() - control.rex_highlighted <~ SignalProducer(signal: pipeSignal) + control.rac_highlighted <~ SignalProducer(signal: pipeSignal) observer.send(value: true) XCTAssertTrue(control.isHighlighted) @@ -91,8 +91,8 @@ class UIControlTests: XCTestCase { let (pipeSignalSelected, observerSelected) = Signal.pipe() let (pipeSignalEnabled, observerEnabled) = Signal.pipe() - control.rex_selected <~ SignalProducer(signal: pipeSignalSelected) - control.rex_enabled <~ SignalProducer(signal: pipeSignalEnabled) + control.rac_selected <~ SignalProducer(signal: pipeSignalSelected) + control.rac_enabled <~ SignalProducer(signal: pipeSignalEnabled) observerSelected.send(value: true) observerEnabled.send(value: true) diff --git a/ReactiveCocoaTests/UIKit/UIDatePickerTests.swift b/ReactiveCocoaTests/UIKit/UIDatePickerTests.swift index 0a2259c66b..0df8eac0da 100644 --- a/ReactiveCocoaTests/UIKit/UIDatePickerTests.swift +++ b/ReactiveCocoaTests/UIKit/UIDatePickerTests.swift @@ -25,16 +25,16 @@ class UIDatePickerTests: XCTestCase { } func testUpdatePickerFromProperty() { - picker.rex_date.value = date + picker.rac_date.value = date XCTAssertEqual(picker.date, date) } func testUpdatePropertyFromPicker() { - let expectation = self.expectation(description: "Expected rex_date to send an event when picker's date value is changed by a UI event") + let expectation = self.expectation(description: "Expected rac_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.observeValues { changedDate in + picker.rac_date.signal.observeValues { changedDate in XCTAssertEqual(changedDate, self.date) expectation.fulfill() } diff --git a/ReactiveCocoaTests/UIKit/UIImageViewTests.swift b/ReactiveCocoaTests/UIKit/UIImageViewTests.swift index 1df2a1484d..7bf0838c22 100644 --- a/ReactiveCocoaTests/UIKit/UIImageViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UIImageViewTests.swift @@ -27,7 +27,7 @@ class UIImageViewTests: XCTestCase { let image = UIImage() - imageView.rex_image <~ SignalProducer(value: image) + imageView.rac_image <~ SignalProducer(value: image) XCTAssert(_imageView?.image == image) } @@ -37,7 +37,7 @@ class UIImageViewTests: XCTestCase { let image = UIImage() - imageView.rex_highlightedImage <~ SignalProducer(value: image) + imageView.rac_highlightedImage <~ SignalProducer(value: image) XCTAssert(_imageView?.highlightedImage == image) } @@ -48,7 +48,7 @@ class UIImageViewTests: XCTestCase { let secondChange = UIImage() let (pipeSignal, observer) = Signal.pipe() - imageView.rex_image <~ SignalProducer(signal: pipeSignal) + imageView.rac_image <~ SignalProducer(signal: pipeSignal) observer.send(value: firstChange) XCTAssertEqual(imageView.image, firstChange) @@ -63,7 +63,7 @@ class UIImageViewTests: XCTestCase { let secondChange = UIImage() let (pipeSignal, observer) = Signal.pipe() - imageView.rex_highlightedImage <~ SignalProducer(signal: pipeSignal) + imageView.rac_highlightedImage <~ SignalProducer(signal: pipeSignal) observer.send(value: firstChange) XCTAssertEqual(imageView.highlightedImage, firstChange) diff --git a/ReactiveCocoaTests/UIKit/UILabelTests.swift b/ReactiveCocoaTests/UIKit/UILabelTests.swift index 565d0da85c..817b7d6ba1 100644 --- a/ReactiveCocoaTests/UIKit/UILabelTests.swift +++ b/ReactiveCocoaTests/UIKit/UILabelTests.swift @@ -25,7 +25,7 @@ class UILabelTests: XCTestCase { let label = UILabel(frame: CGRect.zero) _label = label - label.rex_text <~ SignalProducer(value: "Test") + label.rac_text <~ SignalProducer(value: "Test") XCTAssert(_label?.text == "Test") } @@ -37,7 +37,7 @@ class UILabelTests: XCTestCase { label.text = "" let (pipeSignal, observer) = Signal.pipe() - label.rex_text <~ SignalProducer(signal: pipeSignal) + label.rac_text <~ SignalProducer(signal: pipeSignal) observer.send(value: firstChange) XCTAssertEqual(label.text, firstChange) @@ -51,7 +51,7 @@ class UILabelTests: XCTestCase { let label = UILabel(frame: CGRect.zero) _label = label - label.rex_attributedText <~ SignalProducer(value: NSAttributedString(string: "Test")) + label.rac_attributedText <~ SignalProducer(value: NSAttributedString(string: "Test")) XCTAssert(_label?.attributedText?.string == "Test") } @@ -63,7 +63,7 @@ class UILabelTests: XCTestCase { label.attributedText = NSAttributedString(string: "") let (pipeSignal, observer) = Signal.pipe() - label.rex_attributedText <~ SignalProducer(signal: pipeSignal) + label.rac_attributedText <~ SignalProducer(signal: pipeSignal) observer.send(value: firstChange) XCTAssertEqual(label.attributedText, firstChange) @@ -79,7 +79,7 @@ class UILabelTests: XCTestCase { let (pipeSignal, observer) = Signal.pipe() label.textColor = UIColor.black - label.rex_textColor <~ SignalProducer(signal: pipeSignal) + label.rac_textColor <~ SignalProducer(signal: pipeSignal) observer.send(value: firstChange) XCTAssertEqual(label.textColor, firstChange) diff --git a/ReactiveCocoaTests/UIKit/UIProgressViewTests.swift b/ReactiveCocoaTests/UIKit/UIProgressViewTests.swift index 73d6d5f78b..83939b510b 100644 --- a/ReactiveCocoaTests/UIKit/UIProgressViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UIProgressViewTests.swift @@ -24,7 +24,7 @@ class UIProgressViewTests: XCTestCase { let progressView = UIProgressView(frame: CGRect.zero) _progressView = progressView - progressView.rex_progress <~ SignalProducer(value: 0.5) + progressView.rac_progress <~ SignalProducer(value: 0.5) XCTAssert(_progressView?.progress == 0.5) } @@ -36,7 +36,7 @@ class UIProgressViewTests: XCTestCase { progressView.progress = 1.0 let (pipeSignal, observer) = Signal.pipe() - progressView.rex_progress <~ SignalProducer(signal: pipeSignal) + progressView.rac_progress <~ SignalProducer(signal: pipeSignal) observer.send(value: firstChange) XCTAssertEqual(progressView.progress, firstChange) diff --git a/ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift b/ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift index 944c62ceeb..d2fe259e8c 100644 --- a/ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift +++ b/ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift @@ -19,7 +19,7 @@ class UISegmentedControlTests: XCTestCase { XCTAssertEqual(s.numberOfSegments, 3) let (pipeSignal, observer) = Signal.pipe() - s.rex_selectedSegmentIndex <~ SignalProducer(signal: pipeSignal) + s.rac_selectedSegmentIndex <~ SignalProducer(signal: pipeSignal) XCTAssertEqual(s.selectedSegmentIndex, UISegmentedControlNoSegment) observer.send(value: 1) diff --git a/ReactiveCocoaTests/UIKit/UISwitchTests.swift b/ReactiveCocoaTests/UIKit/UISwitchTests.swift index 823b03de15..d77dbf4174 100644 --- a/ReactiveCocoaTests/UIKit/UISwitchTests.swift +++ b/ReactiveCocoaTests/UIKit/UISwitchTests.swift @@ -18,7 +18,7 @@ class UISwitchTests: XCTestCase { `switch`.isOn = false let (pipeSignal, observer) = Signal.pipe() - `switch`.rex_on <~ SignalProducer(signal: pipeSignal) + `switch`.rac_on <~ SignalProducer(signal: pipeSignal) observer.send(value: true) XCTAssertTrue(`switch`.isOn) @@ -27,6 +27,6 @@ class UISwitchTests: XCTestCase { `switch`.isOn = true `switch`.sendActions(for: .valueChanged) - XCTAssertTrue(`switch`.rex_on.value) + XCTAssertTrue(`switch`.rac_on.value) } } diff --git a/ReactiveCocoaTests/UIKit/UITableViewCellTests.swift b/ReactiveCocoaTests/UIKit/UITableViewCellTests.swift index 2ff7ccadc3..055691a51d 100644 --- a/ReactiveCocoaTests/UIKit/UITableViewCellTests.swift +++ b/ReactiveCocoaTests/UIKit/UITableViewCellTests.swift @@ -21,10 +21,10 @@ class UITableViewCellTests: XCTestCase { fatalError() } - label.rex_text <~ + label.rac_text <~ titleProperty .producer - .take(until: cell.rex_prepareForReuse) + .take(until: cell.rac_prepareForReuse) XCTAssertEqual(label.text, "John") diff --git a/ReactiveCocoaTests/UIKit/UITableViewHeaderFooterViewTests.swift b/ReactiveCocoaTests/UIKit/UITableViewHeaderFooterViewTests.swift index 20eb2eeed2..a81f7626d8 100644 --- a/ReactiveCocoaTests/UIKit/UITableViewHeaderFooterViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UITableViewHeaderFooterViewTests.swift @@ -17,10 +17,10 @@ class UITableViewHeaderFooterViewTests: XCTestCase { let header = UITableViewHeaderFooterView() - header.rex_hidden <~ + header.rac_hidden <~ hiddenProperty .producer - .take(until: header.rex_prepareForReuse) + .take(until: header.rac_prepareForReuse) XCTAssertFalse(header.isHidden) diff --git a/ReactiveCocoaTests/UIKit/UITextFieldTests.swift b/ReactiveCocoaTests/UIKit/UITextFieldTests.swift index f11de7fd64..3903891ac3 100644 --- a/ReactiveCocoaTests/UIKit/UITextFieldTests.swift +++ b/ReactiveCocoaTests/UIKit/UITextFieldTests.swift @@ -14,13 +14,13 @@ import XCTest class UITextFieldTests: XCTestCase { func testTextProperty() { - let expectation = self.expectation(description: "Expected `rex_text`'s value to equal to the textField's text") + let expectation = self.expectation(description: "Expected `rac_text`'s value to equal to the textField's text") defer { self.waitForExpectations(timeout: 2, handler: nil) } let textField = UITextField(frame: CGRect.zero) textField.text = "Test" - textField.rex_text.signal.observeValues { text in + textField.rac_text.signal.observeValues { text in XCTAssertEqual(text, textField.text) expectation.fulfill() } diff --git a/ReactiveCocoaTests/UIKit/UITextViewTests.swift b/ReactiveCocoaTests/UIKit/UITextViewTests.swift index 8fea03588e..c4e40a508b 100644 --- a/ReactiveCocoaTests/UIKit/UITextViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UITextViewTests.swift @@ -14,13 +14,13 @@ import XCTest class UITextViewTests: XCTestCase { func testTextProperty() { - let expectation = self.expectation(description: "Expected `rex_text`'s value to equal to the textViews's text") + let expectation = self.expectation(description: "Expected `rac_text`'s value to equal to the textViews's text") defer { self.waitForExpectations(timeout: 2, handler: nil) } let textView = UITextView(frame: CGRect.zero) textView.text = "Test" - textView.rex_text.startWithValues { text in + textView.rac_text.startWithValues { text in XCTAssertEqual(text, textView.text) expectation.fulfill() } diff --git a/ReactiveCocoaTests/UIKit/UIViewControllerTests.swift b/ReactiveCocoaTests/UIKit/UIViewControllerTests.swift index 5276025160..15d8fdd548 100644 --- a/ReactiveCocoaTests/UIKit/UIViewControllerTests.swift +++ b/ReactiveCocoaTests/UIKit/UIViewControllerTests.swift @@ -23,13 +23,13 @@ class UIViewControllerTests: XCTestCase { func testViewDidDisappear() { - let expectation = self.expectation(description: "Expected rex_viewDidDisappear to be triggered") + let expectation = self.expectation(description: "Expected rac_viewDidDisappear to be triggered") defer { self.waitForExpectations(timeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController - viewController.rex_viewDidDisappear.observeValues { + viewController.rac_viewDidDisappear.observeValues { expectation.fulfill() } @@ -38,13 +38,13 @@ class UIViewControllerTests: XCTestCase { func testViewWillDisappear() { - let expectation = self.expectation(description: "Expected rex_viewWillDisappear to be triggered") + let expectation = self.expectation(description: "Expected rac_viewWillDisappear to be triggered") defer { self.waitForExpectations(timeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController - viewController.rex_viewWillDisappear.observeValues { + viewController.rac_viewWillDisappear.observeValues { expectation.fulfill() } @@ -53,13 +53,13 @@ class UIViewControllerTests: XCTestCase { func testViewDidAppear() { - let expectation = self.expectation(description: "Expected rex_viewDidAppear to be triggered") + let expectation = self.expectation(description: "Expected rac_viewDidAppear to be triggered") defer { self.waitForExpectations(timeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController - viewController.rex_viewDidAppear.observeValues { + viewController.rac_viewDidAppear.observeValues { expectation.fulfill() } @@ -68,13 +68,13 @@ class UIViewControllerTests: XCTestCase { func testViewWillAppear() { - let expectation = self.expectation(description: "Expected rex_viewWillAppear to be triggered") + let expectation = self.expectation(description: "Expected rac_viewWillAppear to be triggered") defer { self.waitForExpectations(timeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController - viewController.rex_viewWillAppear.observeValues { + viewController.rac_viewWillAppear.observeValues { expectation.fulfill() } @@ -83,28 +83,28 @@ class UIViewControllerTests: XCTestCase { func testDismissViewController_via_property() { - let expectation = self.expectation(description: "Expected rex_dismissModally to be triggered") + let expectation = self.expectation(description: "Expected rac_dismissModally to be triggered") defer { self.waitForExpectations(timeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController - viewController.rex_dismissAnimated.signal.observeValues { _ in + viewController.rac_dismissAnimated.signal.observeValues { _ in expectation.fulfill() } - viewController.rex_dismissAnimated <~ SignalProducer(value: (animated: true, completion: nil)) + viewController.rac_dismissAnimated <~ SignalProducer(value: (animated: true, completion: nil)) } func testDismissViewController_via_cocoaDismiss() { - let expectation = self.expectation(description: "Expected rex_dismissModally to be triggered") + let expectation = self.expectation(description: "Expected rac_dismissModally to be triggered") defer { self.waitForExpectations(timeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController - viewController.rex_dismissAnimated.signal.observeValues { _ in + viewController.rac_dismissAnimated.signal.observeValues { _ in expectation.fulfill() } diff --git a/ReactiveCocoaTests/UIKit/UIViewTests.swift b/ReactiveCocoaTests/UIKit/UIViewTests.swift index 4c8bfd2793..fc6ce0a49f 100644 --- a/ReactiveCocoaTests/UIKit/UIViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UIViewTests.swift @@ -25,7 +25,7 @@ class UIViewTests: XCTestCase { let view = UIView(frame: CGRect.zero) _view = view - view.rex_alpha <~ SignalProducer(value: 0.5) + view.rac_alpha <~ SignalProducer(value: 0.5) XCTAssertEqualWithAccuracy(_view!.alpha, 0.5, accuracy: 0.01) } @@ -33,7 +33,7 @@ class UIViewTests: XCTestCase { let view = UIView(frame: CGRect.zero) _view = view - view.rex_hidden <~ SignalProducer(value: true) + view.rac_hidden <~ SignalProducer(value: true) XCTAssert(_view?.isHidden == true) } @@ -42,7 +42,7 @@ class UIViewTests: XCTestCase { view.isHidden = true let (pipeSignal, observer) = Signal.pipe() - view.rex_hidden <~ SignalProducer(signal: pipeSignal) + view.rac_hidden <~ SignalProducer(signal: pipeSignal) observer.send(value: true) XCTAssertTrue(view.isHidden) @@ -58,7 +58,7 @@ class UIViewTests: XCTestCase { let secondChange = CGFloat(0.7) let (pipeSignal, observer) = Signal.pipe() - view.rex_alpha <~ SignalProducer(signal: pipeSignal) + view.rac_alpha <~ SignalProducer(signal: pipeSignal) observer.send(value: firstChange) XCTAssertEqualWithAccuracy(view.alpha, firstChange, accuracy: 0.01) @@ -71,7 +71,7 @@ class UIViewTests: XCTestCase { view.isUserInteractionEnabled = true let (pipeSignal, observer) = Signal.pipe() - view.rex_userInteractionEnabled <~ SignalProducer(signal: pipeSignal) + view.rac_userInteractionEnabled <~ SignalProducer(signal: pipeSignal) observer.send(value: true) XCTAssertTrue(view.isUserInteractionEnabled) From d7a382f77a415cf968bc57bc2a8048da7d99a47e Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 22 Sep 2016 15:57:24 +0200 Subject: [PATCH 0392/1028] Implementation for `NSObject.signal(for:)`. --- ReactiveCocoa.xcodeproj/project.pbxproj | 100 +++++- .../xcschemes/ReactiveCocoa-iOS.xcscheme | 14 + ReactiveCocoa/NSObject+Intercepting.swift | 33 +- ReactiveCocoa/RACObjCRuntimeUtilities.h | 8 + ReactiveCocoa/RACObjCRuntimeUtilities.m | 310 ++++++++++++++++++ .../ReactiveCocoaPrivate/module.modulemap | 4 + 6 files changed, 466 insertions(+), 3 deletions(-) create mode 100644 ReactiveCocoa/RACObjCRuntimeUtilities.h create mode 100644 ReactiveCocoa/RACObjCRuntimeUtilities.m create mode 100644 ReactiveCocoa/ReactiveCocoaPrivate/module.modulemap diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 52d0bf0f71..002e0d8a8f 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -102,7 +102,11 @@ 9A1D065C1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; 9A1D065D1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; 9A1D065E1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; - 9A1D065F1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; + 9A1D06771D9415FF00ACF44C /* RACObjCRuntimeUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06761D9415FB00ACF44C /* RACObjCRuntimeUtilities.m */; }; + 9A1D06781D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06761D9415FB00ACF44C /* RACObjCRuntimeUtilities.m */; }; + 9A1D06791D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06761D9415FB00ACF44C /* RACObjCRuntimeUtilities.m */; }; + 9A1D067A1D94160100ACF44C /* RACObjCRuntimeUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06761D9415FB00ACF44C /* RACObjCRuntimeUtilities.m */; }; + 9A1D067B1D94164900ACF44C /* RACObjCRuntimeUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A1D06751D9415FB00ACF44C /* RACObjCRuntimeUtilities.h */; }; 9A1E72BA1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; 9A1E72BB1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; 9A1E72BC1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; @@ -261,6 +265,8 @@ 9A1D06341D93EA7E00ACF44C /* UIViewControllerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewControllerTests.swift; sourceTree = ""; }; 9A1D06351D93EA7E00ACF44C /* UIViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewTests.swift; sourceTree = ""; }; 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Intercepting.swift"; sourceTree = ""; }; + 9A1D06751D9415FB00ACF44C /* RACObjCRuntimeUtilities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RACObjCRuntimeUtilities.h; sourceTree = ""; }; + 9A1D06761D9415FB00ACF44C /* RACObjCRuntimeUtilities.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RACObjCRuntimeUtilities.m; sourceTree = ""; }; 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyValueObservingSpec.swift; sourceTree = ""; }; 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+KeyValueObserving.swift"; sourceTree = ""; }; A97451331B3A935E00F48E55 /* watchOS-Application.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Application.xcconfig"; sourceTree = ""; }; @@ -510,6 +516,8 @@ isa = PBXGroup; children = ( D04725EF19E49ED7006002AA /* ReactiveCocoa.h */, + 9A1D06751D9415FB00ACF44C /* RACObjCRuntimeUtilities.h */, + 9A1D06761D9415FB00ACF44C /* RACObjCRuntimeUtilities.m */, C7142DBB1CDEA167009F402D /* CocoaAction.swift */, CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */, 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */, @@ -658,6 +666,7 @@ buildActionMask = 2147483647; files = ( D037666419EDA43C00A782A9 /* ReactiveCocoa.h in Headers */, + 9A1D067B1D94164900ACF44C /* RACObjCRuntimeUtilities.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -922,6 +931,7 @@ 9AD0F06D1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, C7142DBF1CDEA195009F402D /* CocoaAction.swift in Sources */, 9A1D06211D93EA0100ACF44C /* UIView.swift in Sources */, + 9A1D067A1D94160100ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9A1D061D1D93EA0100ACF44C /* UITableViewCell.swift in Sources */, 9A1D05E31D93E99100ACF44C /* Association.swift in Sources */, 9A1D06161D93EA0100ACF44C /* UIControl.swift in Sources */, @@ -963,7 +973,6 @@ 9A1D06411D93EA7E00ACF44C /* UIControlTests.swift in Sources */, 9A1D06491D93EA7E00ACF44C /* UIProgressViewTests.swift in Sources */, 9A1D06511D93EA7E00ACF44C /* UITableViewHeaderFooterViewTests.swift in Sources */, - 9A1D065F1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, BEE020661D637B0000DF261F /* TestError.swift in Sources */, 9A1D06531D93EA7E00ACF44C /* UITextFieldTests.swift in Sources */, ); @@ -973,6 +982,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9A1D06791D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9A1D05E71D93E9E200ACF44C /* Reusable.swift in Sources */, 9A1D065D1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, C7142DBE1CDEA194009F402D /* CocoaAction.swift in Sources */, @@ -989,6 +999,7 @@ files = ( 9A1D05E51D93E9E200ACF44C /* Reusable.swift in Sources */, CD0C45DE1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, + 9A1D06771D9415FF00ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, 9A1D05FE1D93E9F900ACF44C /* NSTextField.swift in Sources */, 9A1D05E01D93E99100ACF44C /* Association.swift in Sources */, @@ -1020,6 +1031,7 @@ 9A1D060E1D93EA0000ACF44C /* UITextView.swift in Sources */, 9A1D06061D93EA0000ACF44C /* UIImageView.swift in Sources */, CD0C45DF1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, + 9A1D06781D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */, C7142DBD1CDEA194009F402D /* CocoaAction.swift in Sources */, 9A1D060F1D93EA0000ACF44C /* UIView.swift in Sources */, 9A1D060B1D93EA0000ACF44C /* UITableViewCell.swift in Sources */, @@ -1097,6 +1109,8 @@ isa = XCBuildConfiguration; baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; @@ -1105,6 +1119,10 @@ "DTRACE_PROBES_DISABLED=1", ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -1112,6 +1130,8 @@ isa = XCBuildConfiguration; baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; @@ -1120,6 +1140,10 @@ "DTRACE_PROBES_DISABLED=1", ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; }; name = Test; }; @@ -1127,6 +1151,8 @@ isa = XCBuildConfiguration; baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; @@ -1135,6 +1161,10 @@ "DTRACE_PROBES_DISABLED=1", ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; }; name = Release; }; @@ -1142,6 +1172,8 @@ isa = XCBuildConfiguration; baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; @@ -1150,6 +1182,9 @@ "DTRACE_PROBES_DISABLED=1", ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + SWIFT_VERSION = 3.0; }; name = Profile; }; @@ -1217,6 +1252,8 @@ isa = XCBuildConfiguration; baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; @@ -1225,6 +1262,10 @@ "DTRACE_PROBES_DISABLED=1", ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -1232,6 +1273,8 @@ isa = XCBuildConfiguration; baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; @@ -1240,6 +1283,10 @@ "DTRACE_PROBES_DISABLED=1", ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; }; name = Test; }; @@ -1247,6 +1294,8 @@ isa = XCBuildConfiguration; baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; @@ -1255,6 +1304,10 @@ "DTRACE_PROBES_DISABLED=1", ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; }; name = Release; }; @@ -1262,6 +1315,8 @@ isa = XCBuildConfiguration; baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; @@ -1270,6 +1325,9 @@ "DTRACE_PROBES_DISABLED=1", ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + SWIFT_VERSION = 3.0; }; name = Profile; }; @@ -1317,11 +1375,16 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; INFOPLIST_FILE = ReactiveCocoa/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; USER_HEADER_SEARCH_PATHS = ReactiveCocoa/extobjc; }; name = Debug; @@ -1330,11 +1393,16 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; INFOPLIST_FILE = ReactiveCocoa/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; USER_HEADER_SEARCH_PATHS = ReactiveCocoa/extobjc; }; name = Release; @@ -1371,11 +1439,16 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; USER_HEADER_SEARCH_PATHS = ReactiveCocoa/extobjc; }; name = Debug; @@ -1384,11 +1457,16 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; USER_HEADER_SEARCH_PATHS = ReactiveCocoa/extobjc; }; name = Release; @@ -1444,11 +1522,15 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; INFOPLIST_FILE = ReactiveCocoa/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + SWIFT_VERSION = 3.0; USER_HEADER_SEARCH_PATHS = ReactiveCocoa/extobjc; }; name = Profile; @@ -1471,11 +1553,15 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + SWIFT_VERSION = 3.0; USER_HEADER_SEARCH_PATHS = ReactiveCocoa/extobjc; }; name = Profile; @@ -1517,11 +1603,16 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; INFOPLIST_FILE = ReactiveCocoa/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; USER_HEADER_SEARCH_PATHS = ReactiveCocoa/extobjc; }; name = Test; @@ -1544,11 +1635,16 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; USER_HEADER_SEARCH_PATHS = ReactiveCocoa/extobjc; }; name = Test; diff --git a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme index 2e8560ffbe..395f3fad28 100644 --- a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme +++ b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme @@ -108,6 +108,20 @@ BlueprintName = "ReactiveCocoa-iOSTests" ReferencedContainer = "container:ReactiveCocoa.xcodeproj"> + + + + + + + + + + diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 8f3597a2d2..897b9bcda6 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -1,9 +1,40 @@ import Foundation import ReactiveSwift import enum Result.NoError +import ReactiveCocoaPrivate extension NSObject { public func signal(for selector: Selector) -> Signal<(), NoError> { - return .empty + objc_sync_enter(self) + defer { objc_sync_exit(self) } + + let map: NSMutableDictionary = { + if let map = objc_getAssociatedObject(self, &mapKey) as? NSMutableDictionary { + return map + } else { + let map = NSMutableDictionary() + objc_setAssociatedObject(self, &mapKey, map, .OBJC_ASSOCIATION_RETAIN) + return map + } + }() + + let selectorName = String(describing: selector) as NSString + if let signal = map.object(forKey: selectorName) as? Signal<(), NoError> { + return signal + } + + let (signal, observer) = Signal<(), NoError>.pipe() + + let isSuccessful = RACRegisterBlockForSelector(self, selector, nil, { + observer.send(value: ()) + }) + assert(isSuccessful) + + rac_lifetime.ended.observeCompleted(observer.sendCompleted) + map.setObject(signal, forKey: selectorName) + + return signal } } + +private var mapKey = 0 diff --git a/ReactiveCocoa/RACObjCRuntimeUtilities.h b/ReactiveCocoa/RACObjCRuntimeUtilities.h new file mode 100644 index 0000000000..7b5d0312f2 --- /dev/null +++ b/ReactiveCocoa/RACObjCRuntimeUtilities.h @@ -0,0 +1,8 @@ +#import + +typedef void (^rac_receiver_t)(void); + +/// Register a block which would be triggered when `selector` is called. +/// +/// Warning: The callee is responsible for synchronization. +BOOL RACRegisterBlockForSelector(NSObject *self, SEL selector, Protocol *protocol, rac_receiver_t receiver); diff --git a/ReactiveCocoa/RACObjCRuntimeUtilities.m b/ReactiveCocoa/RACObjCRuntimeUtilities.m new file mode 100644 index 0000000000..8da3347d48 --- /dev/null +++ b/ReactiveCocoa/RACObjCRuntimeUtilities.m @@ -0,0 +1,310 @@ +// +// RACObjCRuntimeUtilities.m +// ReactiveCocoa +// +// Created by Josh Abernathy on 3/18/13. +// Copyright (c) 2013 GitHub, Inc. All rights reserved. +// + +#import "RACObjCRuntimeUtilities.h" +#import +#import +#import + +NSString * const RACSelectorSignalErrorDomain = @"RACSelectorSignalErrorDomain"; +const NSInteger RACSelectorSignalErrorMethodSwizzlingRace = 1; +const NSExceptionName RACSwizzleException = @"RACSwizzleException"; + +static NSString * const RACSignalForSelectorAliasPrefix = @"rac_alias_"; +static NSString * const RACSubclassSuffix = @"_RACSelectorSignal"; +static void *RACSubclassAssociationKey = &RACSubclassAssociationKey; + +static NSMutableSet *swizzledClasses() { + static NSMutableSet *set; + static dispatch_once_t pred; + + dispatch_once(&pred, ^{ + set = [[NSMutableSet alloc] init]; + }); + + return set; +} + +static SEL RACAliasForSelector(SEL originalSelector) { + NSString *selectorName = NSStringFromSelector(originalSelector); + return NSSelectorFromString([RACSignalForSelectorAliasPrefix stringByAppendingString:selectorName]); +} + +static BOOL RACForwardInvocation(id self, NSInvocation *invocation) { + SEL aliasSelector = RACAliasForSelector(invocation.selector); + __block rac_receiver_t receiver = objc_getAssociatedObject(self, aliasSelector); + + Class class = object_getClass(invocation.target); + BOOL respondsToAlias = [class instancesRespondToSelector:aliasSelector]; + if (respondsToAlias) { + invocation.selector = aliasSelector; + [invocation invoke]; + } + + if (receiver == nil) return respondsToAlias; + + receiver(); + return YES; +} + +static void RACSwizzleForwardInvocation(Class class) { + SEL forwardInvocationSEL = @selector(forwardInvocation:); + Method forwardInvocationMethod = class_getInstanceMethod(class, forwardInvocationSEL); + + // Preserve any existing implementation of -forwardInvocation:. + void (*originalForwardInvocation)(id, SEL, NSInvocation *) = NULL; + if (forwardInvocationMethod != NULL) { + originalForwardInvocation = (__typeof__(originalForwardInvocation))method_getImplementation(forwardInvocationMethod); + } + + // Set up a new version of -forwardInvocation:. + // + // If the selector has been passed to -rac_signalForSelector:, invoke + // the aliased method, and forward the arguments to any attached signals. + // + // If the selector has not been passed to -rac_signalForSelector:, + // invoke any existing implementation of -forwardInvocation:. If there + // was no existing implementation, throw an unrecognized selector + // exception. + id newForwardInvocation = ^(id self, NSInvocation *invocation) { + BOOL matched = RACForwardInvocation(self, invocation); + if (matched) return; + + if (originalForwardInvocation == NULL) { + [self doesNotRecognizeSelector:invocation.selector]; + } else { + originalForwardInvocation(self, forwardInvocationSEL, invocation); + } + }; + + class_replaceMethod(class, forwardInvocationSEL, imp_implementationWithBlock(newForwardInvocation), "v@:@"); +} + +Method rac_getImmediateInstanceMethod (Class aClass, SEL aSelector) { + unsigned methodCount = 0; + Method *methods = class_copyMethodList(aClass, &methodCount); + Method foundMethod = NULL; + + for (unsigned methodIndex = 0;methodIndex < methodCount;++methodIndex) { + if (method_getName(methods[methodIndex]) == aSelector) { + foundMethod = methods[methodIndex]; + break; + } + } + + free(methods); + return foundMethod; +} + +static void RACSwizzleRespondsToSelector(Class class) { + SEL respondsToSelectorSEL = @selector(respondsToSelector:); + + // Preserve existing implementation of -respondsToSelector:. + Method respondsToSelectorMethod = class_getInstanceMethod(class, respondsToSelectorSEL); + BOOL (*originalRespondsToSelector)(id, SEL, SEL) = (__typeof__(originalRespondsToSelector))method_getImplementation(respondsToSelectorMethod); + + // Set up a new version of -respondsToSelector: that returns YES for methods + // added by -rac_signalForSelector:. + // + // If the selector has a method defined on the receiver's actual class, and + // if that method's implementation is _objc_msgForward, then returns whether + // the instance has a signal for the selector. + // Otherwise, call the original -respondsToSelector:. + id newRespondsToSelector = ^ BOOL (id self, SEL selector) { + Method method = rac_getImmediateInstanceMethod(class, selector); + + if (method != NULL && method_getImplementation(method) == _objc_msgForward) { + SEL aliasSelector = (selector); + if (objc_getAssociatedObject(self, aliasSelector) != nil) return YES; + } + + return originalRespondsToSelector(self, respondsToSelectorSEL, selector); + }; + + class_replaceMethod(class, respondsToSelectorSEL, imp_implementationWithBlock(newRespondsToSelector), method_getTypeEncoding(respondsToSelectorMethod)); +} + +static void RACSwizzleGetClass(Class class, Class statedClass) { + SEL selector = @selector(class); + Method method = class_getInstanceMethod(class, selector); + IMP newIMP = imp_implementationWithBlock(^(id self) { + return statedClass; + }); + class_replaceMethod(class, selector, newIMP, method_getTypeEncoding(method)); +} + +static void RACSwizzleMethodSignatureForSelector(Class class) { + IMP newIMP = imp_implementationWithBlock(^(id self, SEL selector) { + // Don't send the -class message to the receiver because we've changed + // that to return the original class. + Class actualClass = object_getClass(self); + Method method = class_getInstanceMethod(actualClass, selector); + if (method == NULL) { + // Messages that the original class dynamically implements fall + // here. + // + // Call the original class' -methodSignatureForSelector:. + struct objc_super target = { + .super_class = class_getSuperclass(class), + .receiver = self, + }; + NSMethodSignature * (*messageSend)(struct objc_super *, SEL, SEL) = (__typeof__(messageSend))objc_msgSendSuper; + return messageSend(&target, @selector(methodSignatureForSelector:), selector); + } + + char const *encoding = method_getTypeEncoding(method); + return [NSMethodSignature signatureWithObjCTypes:encoding]; + }); + + SEL selector = @selector(methodSignatureForSelector:); + Method methodSignatureForSelectorMethod = class_getInstanceMethod(class, selector); + class_replaceMethod(class, selector, newIMP, method_getTypeEncoding(methodSignatureForSelectorMethod)); +} + +// It's hard to tell which struct return types use _objc_msgForward, and +// which use _objc_msgForward_stret instead, so just exclude all struct, array, +// union, complex and vector return types. +static void RACCheckTypeEncoding(const char *typeEncoding) { +#if !NS_BLOCK_ASSERTIONS + // Some types, including vector types, are not encoded. In these cases the + // signature starts with the size of the argument frame. + NSCAssert(*typeEncoding < '1' || *typeEncoding > '9', @"unknown method return type not supported in type encoding: %s", typeEncoding); + NSCAssert(strstr(typeEncoding, "(") != typeEncoding, @"union method return type not supported"); + NSCAssert(strstr(typeEncoding, "{") != typeEncoding, @"struct method return type not supported"); + NSCAssert(strstr(typeEncoding, "[") != typeEncoding, @"array method return type not supported"); + NSCAssert(strstr(typeEncoding, @encode(_Complex float)) != typeEncoding, @"complex float method return type not supported"); + NSCAssert(strstr(typeEncoding, @encode(_Complex double)) != typeEncoding, @"complex double method return type not supported"); + NSCAssert(strstr(typeEncoding, @encode(_Complex long double)) != typeEncoding, @"complex long double method return type not supported"); + +#endif // !NS_BLOCK_ASSERTIONS +} + +static const char *RACSignatureForUndefinedSelector(SEL selector) { + const char *name = sel_getName(selector); + NSMutableString *signature = [NSMutableString stringWithString:@"v@:"]; + + while ((name = strchr(name, ':')) != NULL) { + [signature appendString:@"@"]; + name++; + } + + return signature.UTF8String; +} + +static Class RACSwizzleClass(NSObject *self) { + Class statedClass = self.class; + Class baseClass = object_getClass(self); + + // The "known dynamic subclass" is the subclass generated by RAC. + // It's stored as an associated object on every instance that's already + // been swizzled, so that even if something else swizzles the class of + // this instance, we can still access the RAC generated subclass. + Class knownDynamicSubclass = objc_getAssociatedObject(self, RACSubclassAssociationKey); + if (knownDynamicSubclass != Nil) return knownDynamicSubclass; + + NSString *className = NSStringFromClass(baseClass); + + if (statedClass != baseClass) { + // If the class is already lying about what it is, it's probably a KVO + // dynamic subclass or something else that we shouldn't subclass + // ourselves. + // + // Just swizzle -forwardInvocation: in-place. Since the object's class + // was almost certainly dynamically changed, we shouldn't see another of + // these classes in the hierarchy. + // + // Additionally, swizzle -respondsToSelector: because the default + // implementation may be ignorant of methods added to this class. + @synchronized (swizzledClasses()) { + if (![swizzledClasses() containsObject:className]) { + RACSwizzleForwardInvocation(baseClass); + RACSwizzleRespondsToSelector(baseClass); + RACSwizzleGetClass(baseClass, statedClass); + RACSwizzleGetClass(object_getClass(baseClass), statedClass); + RACSwizzleMethodSignatureForSelector(baseClass); + [swizzledClasses() addObject:className]; + } + } + + return baseClass; + } + + const char *subclassName = [className stringByAppendingString:RACSubclassSuffix].UTF8String; + Class subclass = objc_getClass(subclassName); + + if (subclass == nil) { + subclass = objc_allocateClassPair(baseClass, subclassName, 0); + if (subclass == nil) return nil; + + RACSwizzleForwardInvocation(subclass); + RACSwizzleRespondsToSelector(subclass); + + RACSwizzleGetClass(subclass, statedClass); + RACSwizzleGetClass(object_getClass(subclass), statedClass); + + RACSwizzleMethodSignatureForSelector(subclass); + + objc_registerClassPair(subclass); + } + + object_setClass(self, subclass); + objc_setAssociatedObject(self, RACSubclassAssociationKey, subclass, OBJC_ASSOCIATION_ASSIGN); + return subclass; +} + +BOOL RACRegisterBlockForSelector(NSObject *self, SEL selector, Protocol *protocol, rac_receiver_t receiver) { + SEL aliasSelector = RACAliasForSelector(selector); + + __block void (^existingReceiver)(void) = objc_getAssociatedObject(self, aliasSelector); + if (existingReceiver != nil) return NO; + + Class class = RACSwizzleClass(self); + NSCAssert(class != nil, @"Could not swizzle class of %@", self); + + objc_setAssociatedObject(self, aliasSelector, receiver, OBJC_ASSOCIATION_RETAIN); + + Method targetMethod = class_getInstanceMethod(class, selector); + if (targetMethod == NULL) { + const char *typeEncoding; + if (protocol == NULL) { + typeEncoding = RACSignatureForUndefinedSelector(selector); + } else { + // Look for the selector as an optional instance method. + struct objc_method_description methodDescription = protocol_getMethodDescription(protocol, selector, NO, YES); + + if (methodDescription.name == NULL) { + // Then fall back to looking for a required instance + // method. + methodDescription = protocol_getMethodDescription(protocol, selector, YES, YES); + NSCAssert(methodDescription.name != NULL, @"Selector %@ does not exist in <%s>", NSStringFromSelector(selector), protocol_getName(protocol)); + } + + typeEncoding = methodDescription.types; + } + + RACCheckTypeEncoding(typeEncoding); + + // Define the selector to call -forwardInvocation:. + if (!class_addMethod(class, selector, _objc_msgForward, typeEncoding)) { + @throw [[NSException alloc] initWithName:RACSwizzleException reason:nil userInfo:nil]; + } + } else if (method_getImplementation(targetMethod) != _objc_msgForward) { + // Make a method alias for the existing method implementation. + const char *typeEncoding = method_getTypeEncoding(targetMethod); + + RACCheckTypeEncoding(typeEncoding); + + BOOL addedAlias __attribute__((unused)) = class_addMethod(class, aliasSelector, method_getImplementation(targetMethod), typeEncoding); + NSCAssert(addedAlias, @"Original implementation for %@ is already copied to %@ on %@", NSStringFromSelector(selector), NSStringFromSelector(aliasSelector), class); + + // Redefine the selector to call -forwardInvocation:. + class_replaceMethod(class, selector, _objc_msgForward, method_getTypeEncoding(targetMethod)); + } + + return YES; +} diff --git a/ReactiveCocoa/ReactiveCocoaPrivate/module.modulemap b/ReactiveCocoa/ReactiveCocoaPrivate/module.modulemap new file mode 100644 index 0000000000..ceeae718d0 --- /dev/null +++ b/ReactiveCocoa/ReactiveCocoaPrivate/module.modulemap @@ -0,0 +1,4 @@ +module ReactiveCocoaPrivate { + header "../RACObjCRuntimeUtilities.h" + export * +} From 7dfd5a4fd3b54c38c09be8bcc50e884fba262a2f Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 22 Sep 2016 16:36:22 +0200 Subject: [PATCH 0393/1028] Implementation of `UIControl.trigger(for:)`. --- ReactiveCocoa/UIKit/UIControl.swift | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index 8131ae9b64..ed1a98651d 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -10,9 +10,31 @@ import ReactiveSwift import UIKit import enum Result.NoError +private class UnsafeControlReceiver: NSObject { + private let observer: Observer<(), NoError> + + fileprivate init(observer: Observer<(), NoError>) { + self.observer = observer + } + + @objc fileprivate func sendNext() { + observer.send(value: ()) + } +} + extension UIControl { public func trigger(for events: UIControlEvents) -> Signal<(), NoError> { - return .empty + return Signal { observer in + let receiver = UnsafeControlReceiver(observer: observer) + addTarget(receiver, action: #selector(UnsafeControlReceiver.sendNext), for: events) + + let disposable = rac_lifetime.ended.observeCompleted(observer.sendCompleted) + + return ActionDisposable { [weak self] in + disposable?.dispose() + self?.removeTarget(receiver, action: #selector(UnsafeControlReceiver.sendNext), for: events) + } + } } #if os(iOS) From 31fe17073ac0be2e67332a48cda992e117f02d0f Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Sat, 24 Sep 2016 09:25:08 +0900 Subject: [PATCH 0394/1028] Update Quick to v0.10.0 --- Cartfile.private | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/Quick | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cartfile.private b/Cartfile.private index 02554af5d9..36e1c8b92d 100644 --- a/Cartfile.private +++ b/Cartfile.private @@ -1,3 +1,3 @@ github "jspahrsummers/xcconfigs" "3d9d996" -github "Quick/Quick" "swift-3.0" +github "Quick/Quick" ~> 0.10 github "Quick/Nimble" ~> 5.0 diff --git a/Cartfile.resolved b/Cartfile.resolved index 1bea4cc2ca..710e851376 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,5 +1,5 @@ github "Quick/Nimble" "v5.0.0" -github "Quick/Quick" "36ebbc75e093c7553a5145ed5dd21484364cd78d" +github "Quick/Quick" "v0.10.0" github "antitypical/Result" "3.0.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" github "ReactiveCocoa/ReactiveSwift" "228fd564561383a387f338558bd777cd14ca4291" diff --git a/Carthage/Checkouts/Quick b/Carthage/Checkouts/Quick index 36ebbc75e0..b441ba9d4a 160000 --- a/Carthage/Checkouts/Quick +++ b/Carthage/Checkouts/Quick @@ -1 +1 @@ -Subproject commit 36ebbc75e093c7553a5145ed5dd21484364cd78d +Subproject commit b441ba9d4abdc87fc153c55de947834b737bbe04 From d6a29dcab56f9a4b208cdec504e08d05773f74c1 Mon Sep 17 00:00:00 2001 From: Zigii Wong Date: Sat, 24 Sep 2016 11:36:45 +0800 Subject: [PATCH 0395/1028] Update Cartfile --- Cartfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cartfile b/Cartfile index 929ff6d5f0..a27dbdbda7 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" "228fd564561383a387f338558bd777cd14ca4291" +github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.1" From c66e5c8ea082ccc40851743c9b89f8bc51448aa1 Mon Sep 17 00:00:00 2001 From: Wong Zigii Date: Sat, 24 Sep 2016 12:03:14 +0800 Subject: [PATCH 0396/1028] Update Cartfile.resolved to 1.0.0-alpha.1 --- Cartfile.resolved | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cartfile.resolved b/Cartfile.resolved index 710e851376..e9e3c050e8 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -2,4 +2,4 @@ github "Quick/Nimble" "v5.0.0" github "Quick/Quick" "v0.10.0" github "antitypical/Result" "3.0.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" -github "ReactiveCocoa/ReactiveSwift" "228fd564561383a387f338558bd777cd14ca4291" +github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.1" From fb6f1dcd14ccefbf92bb72ce6fc023059ea7bed3 Mon Sep 17 00:00:00 2001 From: Wong Zigii Date: Sat, 24 Sep 2016 12:09:31 +0800 Subject: [PATCH 0397/1028] Update checkout of ReactiveSwift --- Carthage/Checkouts/ReactiveSwift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index 228fd56456..bc409d30ff 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit 228fd564561383a387f338558bd777cd14ca4291 +Subproject commit bc409d30ff4daa059c8904fb9b505492cc48cfbe From 72bff0ad6f62b656b7be64f17db7f79f67f0689e Mon Sep 17 00:00:00 2001 From: Wong Zigii Date: Sat, 24 Sep 2016 12:25:52 +0800 Subject: [PATCH 0398/1028] Update .swift-version to 3.0 --- .swift-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.swift-version b/.swift-version index d54e0b2d65..9f55b2ccb5 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -3.0-PREVIEW-4 +3.0 From a8dd99001af77015b94f01c4a2e75b5185d91e95 Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 27 Sep 2016 01:12:23 +0200 Subject: [PATCH 0399/1028] Refactored and Renamed `signal(for:)` to `trigger(for:)`. --- ReactiveCocoa/NSObject+Intercepting.swift | 18 +++++++++++------- ReactiveCocoa/Reusable.swift | 2 +- ReactiveCocoa/UIKit/UIViewController.swift | 10 +++++----- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 897b9bcda6..8bf7538815 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -4,7 +4,13 @@ import enum Result.NoError import ReactiveCocoaPrivate extension NSObject { - public func signal(for selector: Selector) -> Signal<(), NoError> { + public func trigger(for selector: Selector) -> Signal<(), NoError> { + return signal(for: selector) { observer in + return { _ in observer.send(value: ()) } + } + } + + private func signal(for selector: Selector, action: (Observer) -> rac_receiver_t) -> Signal { objc_sync_enter(self) defer { objc_sync_exit(self) } @@ -19,15 +25,13 @@ extension NSObject { }() let selectorName = String(describing: selector) as NSString - if let signal = map.object(forKey: selectorName) as? Signal<(), NoError> { + if let signal = map.object(forKey: selectorName) as? Signal { return signal } - let (signal, observer) = Signal<(), NoError>.pipe() - - let isSuccessful = RACRegisterBlockForSelector(self, selector, nil, { - observer.send(value: ()) - }) + let (signal, observer) = Signal.pipe() + let action = action(observer) + let isSuccessful = RACRegisterBlockForSelector(self, selector, nil, action) assert(isSuccessful) rac_lifetime.ended.observeCompleted(observer.sendCompleted) diff --git a/ReactiveCocoa/Reusable.swift b/ReactiveCocoa/Reusable.swift index d6d42c0dfe..8441173298 100644 --- a/ReactiveCocoa/Reusable.swift +++ b/ReactiveCocoa/Reusable.swift @@ -40,6 +40,6 @@ extension Reusable where Self: NSObject { /// ``` /// public var rac_prepareForReuse: Signal { - return signal(for: #selector(prepareForReuse)) + return trigger(for: #selector(prepareForReuse)) } } diff --git a/ReactiveCocoa/UIKit/UIViewController.swift b/ReactiveCocoa/UIKit/UIViewController.swift index 861bed044b..cb5e0a6b01 100644 --- a/ReactiveCocoa/UIKit/UIViewController.swift +++ b/ReactiveCocoa/UIKit/UIViewController.swift @@ -14,25 +14,25 @@ extension UIViewController { /// Returns a `Signal`, that will be triggered /// when `self`'s `viewDidDisappear` is called public var rac_viewDidDisappear: Signal<(), NoError> { - return signal(for: #selector(UIViewController.viewDidDisappear(_:))) + return trigger(for: #selector(UIViewController.viewDidDisappear(_:))) } /// Returns a `Signal`, that will be triggered /// when `self`'s `viewWillDisappear` is called public var rac_viewWillDisappear: Signal<(), NoError> { - return signal(for: #selector(UIViewController.viewWillDisappear(_:))) + return trigger(for: #selector(UIViewController.viewWillDisappear(_:))) } /// Returns a `Signal`, that will be triggered /// when `self`'s `viewDidAppear` is called public var rac_viewDidAppear: Signal<(), NoError> { - return signal(for: #selector(UIViewController.viewDidAppear(_:))) + return trigger(for: #selector(UIViewController.viewDidAppear(_:))) } /// Returns a `Signal`, that will be triggered /// when `self`'s `viewWillAppear` is called public var rac_viewWillAppear: Signal<(), NoError> { - return signal(for: #selector(UIViewController.viewWillAppear(_:))) + return trigger(for: #selector(UIViewController.viewWillAppear(_:))) } public typealias DismissingCompletion = ((Void) -> Void)? @@ -58,7 +58,7 @@ extension UIViewController { } let property = associatedProperty(self, key: &dismissModally, initial: initial, setter: setter) { property in - property <~ self.signal(for: #selector(UIViewController.dismiss)) + property <~ self.trigger(for: #selector(UIViewController.dismiss)) .take { _ in property.value != nil } .map { _ in return nil } } From 0c5a4da8247636554d0af6b3a6cec60144df7223 Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 27 Sep 2016 01:13:38 +0200 Subject: [PATCH 0400/1028] Nullability annotation for the private header. --- ReactiveCocoa/RACObjCRuntimeUtilities.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ReactiveCocoa/RACObjCRuntimeUtilities.h b/ReactiveCocoa/RACObjCRuntimeUtilities.h index 7b5d0312f2..4f1d78808b 100644 --- a/ReactiveCocoa/RACObjCRuntimeUtilities.h +++ b/ReactiveCocoa/RACObjCRuntimeUtilities.h @@ -1,8 +1,10 @@ #import +NS_ASSUME_NONNULL_BEGIN typedef void (^rac_receiver_t)(void); /// Register a block which would be triggered when `selector` is called. /// /// Warning: The callee is responsible for synchronization. -BOOL RACRegisterBlockForSelector(NSObject *self, SEL selector, Protocol *protocol, rac_receiver_t receiver); +BOOL RACRegisterBlockForSelector(NSObject *self, SEL selector, Protocol * _Nullable protocol, rac_receiver_t receiver); +NS_ASSUME_NONNULL_END From 888d7e4ea173b56390cc68e1bf10e595cd8622ec Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 27 Sep 2016 01:15:32 +0200 Subject: [PATCH 0401/1028] Removed the private header from the iOS target. --- ReactiveCocoa.xcodeproj/project.pbxproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 002e0d8a8f..761f082a8e 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -106,7 +106,6 @@ 9A1D06781D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06761D9415FB00ACF44C /* RACObjCRuntimeUtilities.m */; }; 9A1D06791D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06761D9415FB00ACF44C /* RACObjCRuntimeUtilities.m */; }; 9A1D067A1D94160100ACF44C /* RACObjCRuntimeUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06761D9415FB00ACF44C /* RACObjCRuntimeUtilities.m */; }; - 9A1D067B1D94164900ACF44C /* RACObjCRuntimeUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A1D06751D9415FB00ACF44C /* RACObjCRuntimeUtilities.h */; }; 9A1E72BA1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; 9A1E72BB1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; 9A1E72BC1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; @@ -666,7 +665,6 @@ buildActionMask = 2147483647; files = ( D037666419EDA43C00A782A9 /* ReactiveCocoa.h in Headers */, - 9A1D067B1D94164900ACF44C /* RACObjCRuntimeUtilities.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; From 8e402f5491b3b365d36d4929e180ce7d455e6564 Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Wed, 28 Sep 2016 15:04:45 +0200 Subject: [PATCH 0402/1028] fix broken link to legacy documentation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b16224d7c0..59af26479d 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ introductory examples of using it. [Code]: ReactiveCocoa [Documentation]: Documentation [Framework Overview]: Documentation/FrameworkOverview.md -[Legacy Documentation]: Documentation/Legacy +[Legacy Documentation]: https://github.com/ReactiveCocoa/ReactiveObjC/blob/master/Documentation/ [Objective-C API]: ReactiveCocoa/Objective-C [Objective-C Bridging]: Documentation/ObjectiveCBridging.md [Signal producers]: Documentation/FrameworkOverview.md#signal-producers From 38909f84cfc5385b2675234f2a8d4573bc1967cf Mon Sep 17 00:00:00 2001 From: Anders Date: Wed, 28 Sep 2016 16:19:12 +0200 Subject: [PATCH 0403/1028] Targeted the merge commit of ReactiveSwift#30. --- Cartfile | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cartfile b/Cartfile index a27dbdbda7..328f4857bb 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.1" +github "ReactiveCocoa/ReactiveSwift" "33f8a6d4a9bd6513dd529d17908331b8e66c4874" diff --git a/Cartfile.resolved b/Cartfile.resolved index e9e3c050e8..bb5ff4c355 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -2,4 +2,4 @@ github "Quick/Nimble" "v5.0.0" github "Quick/Quick" "v0.10.0" github "antitypical/Result" "3.0.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" -github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.1" +github "ReactiveCocoa/ReactiveSwift" "33f8a6d4a9bd6513dd529d17908331b8e66c4874" diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index bc409d30ff..33f8a6d4a9 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit bc409d30ff4daa059c8904fb9b505492cc48cfbe +Subproject commit 33f8a6d4a9bd6513dd529d17908331b8e66c4874 From 15d37dd66dcd61db20f1f5a9d85d6aeb39c977e8 Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 27 Sep 2016 12:43:50 +0200 Subject: [PATCH 0404/1028] Deployed `BindingTarget` in the UIKit extensions. --- ReactiveCocoa.xcodeproj/project.pbxproj | 10 +++++++ ReactiveCocoa/NSObject+BindingTarget.swift | 29 +++++++++++++++++++ .../UIKit/UIActivityIndicatorView.swift | 14 ++++----- ReactiveCocoa/UIKit/UIBarItem.swift | 6 ++-- ReactiveCocoa/UIKit/UIButton.swift | 5 ++-- ReactiveCocoa/UIKit/UIControl.swift | 15 ++++------ ReactiveCocoa/UIKit/UIImageView.swift | 11 +++---- ReactiveCocoa/UIKit/UILabel.swift | 16 ++++------ ReactiveCocoa/UIKit/UIProgressView.swift | 6 ++-- ReactiveCocoa/UIKit/UIView.swift | 16 ++++------ ReactiveCocoa/UIKit/UIViewController.swift | 21 +++----------- .../UIKit/UIViewControllerTests.swift | 12 ++++++-- 12 files changed, 86 insertions(+), 75 deletions(-) create mode 100644 ReactiveCocoa/NSObject+BindingTarget.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 761f082a8e..c2f5b1d669 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -113,6 +113,10 @@ 9AD0F06B1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; 9AD0F06C1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; 9AD0F06D1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; + 9AF0EA751D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; + 9AF0EA761D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; + 9AF0EA771D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; + 9AF0EA781D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; A9B315C91B3940980001CB9C /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; A9B315CA1B3940AB0001CB9C /* ReactiveCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; }; B696FB811A7640C00075236D /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B696FB801A7640C00075236D /* TestError.swift */; }; @@ -268,6 +272,7 @@ 9A1D06761D9415FB00ACF44C /* RACObjCRuntimeUtilities.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RACObjCRuntimeUtilities.m; sourceTree = ""; }; 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyValueObservingSpec.swift; sourceTree = ""; }; 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+KeyValueObserving.swift"; sourceTree = ""; }; + 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+BindingTarget.swift"; sourceTree = ""; }; A97451331B3A935E00F48E55 /* watchOS-Application.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Application.xcconfig"; sourceTree = ""; }; A97451341B3A935E00F48E55 /* watchOS-Base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Base.xcconfig"; sourceTree = ""; }; A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Framework.xcconfig"; sourceTree = ""; }; @@ -522,6 +527,7 @@ 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */, 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */, 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */, + 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */, 9A1D05DF1D93E99100ACF44C /* Association.swift */, 9A1D05E41D93E9E200ACF44C /* Reusable.swift */, 9A1D05E91D93E9F100ACF44C /* AppKit */, @@ -937,6 +943,7 @@ 9A1D06221D93EA0100ACF44C /* UIViewController.swift in Sources */, 9A1D065E1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, 9A1D06151D93EA0100ACF44C /* UICollectionReusableView.swift in Sources */, + 9AF0EA781D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, 9A1D06191D93EA0100ACF44C /* UILabel.swift in Sources */, 4A0E11021D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, CD0C45E11CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, @@ -985,6 +992,7 @@ 9A1D065D1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, C7142DBE1CDEA194009F402D /* CocoaAction.swift in Sources */, CD0C45E01CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, + 9AF0EA771D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, 9A1D05E21D93E99100ACF44C /* Association.swift in Sources */, 4A0E11011D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, 9AD0F06C1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, @@ -1001,6 +1009,7 @@ 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, 9A1D05FE1D93E9F900ACF44C /* NSTextField.swift in Sources */, 9A1D05E01D93E99100ACF44C /* Association.swift in Sources */, + 9AF0EA751D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, 9A1D065B1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, C7142DBC1CDEA167009F402D /* CocoaAction.swift in Sources */, 4A0E10FF1D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, @@ -1028,6 +1037,7 @@ 9A1D060D1D93EA0000ACF44C /* UITextField.swift in Sources */, 9A1D060E1D93EA0000ACF44C /* UITextView.swift in Sources */, 9A1D06061D93EA0000ACF44C /* UIImageView.swift in Sources */, + 9AF0EA761D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, CD0C45DF1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9A1D06781D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */, C7142DBD1CDEA194009F402D /* CocoaAction.swift in Sources */, diff --git a/ReactiveCocoa/NSObject+BindingTarget.swift b/ReactiveCocoa/NSObject+BindingTarget.swift new file mode 100644 index 0000000000..d647b34927 --- /dev/null +++ b/ReactiveCocoa/NSObject+BindingTarget.swift @@ -0,0 +1,29 @@ +import Foundation +import ReactiveSwift + +internal protocol NSObjectBindingTargetProtocol: class { + var rac_lifetime: Lifetime { get } +} + +extension NSObjectBindingTargetProtocol { + /// Creates a binding target which uses the lifetime of `self`, and weakly + /// references `self` so that the supplied `action` is triggered only if + /// `self` has not deinitialized. + /// + /// - important: The resulting binding target is bound to the main queue. + /// + /// - parameters: + /// - action: The action to consume values from the bindings. + /// + /// - returns: + /// A binding target that holds no strong references to `self`. + internal func bindingTarget(action: @escaping (Self, U) -> Void) -> BindingTarget { + return BindingTarget(on: .main, lifetime: rac_lifetime) { [weak self] value in + if let strongSelf = self { + action(strongSelf, value) + } + } + } +} + +extension NSObject: NSObjectBindingTargetProtocol {} diff --git a/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift b/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift index 696940ea46..8f647f7559 100644 --- a/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift +++ b/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift @@ -14,15 +14,13 @@ extension UIActivityIndicatorView { /// Wraps an indicator's `isAnimating()` state in a bindable property. /// Setting a new value to the property would call `startAnimating()` or /// `stopAnimating()` depending on the value. - public var rac_animating: MutableProperty { - return associatedProperty(self, key: &animatingKey, initial: { $0.isAnimating }, setter: { host, animating in - if animating { - host.startAnimating() + public var rac_animating: BindingTarget { + return bindingTarget { _self, isAnimating in + if isAnimating { + _self.startAnimating() } else { - host.stopAnimating() + _self.stopAnimating() } - }) + } } } - -private var animatingKey: UInt8 = 0 diff --git a/ReactiveCocoa/UIKit/UIBarItem.swift b/ReactiveCocoa/UIKit/UIBarItem.swift index 95806e1ee1..879c1c884e 100644 --- a/ReactiveCocoa/UIKit/UIBarItem.swift +++ b/ReactiveCocoa/UIKit/UIBarItem.swift @@ -11,9 +11,7 @@ import UIKit extension UIBarItem { /// Wraps a UIBarItem's `enabled` state in a bindable property. - public var rac_enabled: MutableProperty { - return associatedProperty(self, key: &enabledKey, initial: { $0.isEnabled }, setter: { $0.isEnabled = $1 }) + public var rac_enabled: BindingTarget { + return bindingTarget { $0.isEnabled = $1 } } } - -private var enabledKey: UInt8 = 0 diff --git a/ReactiveCocoa/UIKit/UIButton.swift b/ReactiveCocoa/UIKit/UIButton.swift index 50b7a56c99..843cd3f2f9 100644 --- a/ReactiveCocoa/UIKit/UIButton.swift +++ b/ReactiveCocoa/UIKit/UIButton.swift @@ -34,10 +34,9 @@ extension UIButton { /// Wraps a button's `title` text in a bindable property. Note that this only applies /// to `UIControlState.Normal`. - public var rac_title: MutableProperty { - return associatedProperty(self, key: &titleKey, initial: { $0.title(for: .normal) ?? "" }, setter: { $0.setTitle($1, for: .normal) }) + public var rac_title: BindingTarget { + return bindingTarget { $0.setTitle($1, for: .normal) } } } private var pressedKey: UInt8 = 0 -private var titleKey: UInt8 = 0 diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index ed1a98651d..3e5ff0c184 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -53,22 +53,19 @@ extension UIControl { #endif /// Wraps a control's `enabled` state in a bindable property. - public var rac_enabled: MutableProperty { - return associatedProperty(self, key: &enabledKey, initial: { $0.isEnabled }, setter: { $0.isEnabled = $1 }) + public var rac_enabled: BindingTarget { + return bindingTarget { $0.isEnabled = $1 } } /// Wraps a control's `selected` state in a bindable property. - public var rac_selected: MutableProperty { - return associatedProperty(self, key: &selectedKey, initial: { $0.isSelected }, setter: { $0.isSelected = $1 }) + public var rac_selected: BindingTarget { + return bindingTarget { $0.isSelected = $1 } } /// Wraps a control's `highlighted` state in a bindable property. - public var rac_highlighted: MutableProperty { - return associatedProperty(self, key: &highlightedKey, initial: { $0.isHighlighted }, setter: { $0.isHighlighted = $1 }) + public var rac_highlighted: BindingTarget { + return bindingTarget { $0.isHighlighted = $1 } } } -private var enabledKey: UInt8 = 0 -private var selectedKey: UInt8 = 0 -private var highlightedKey: UInt8 = 0 private var valueChangedKey: UInt8 = 0 diff --git a/ReactiveCocoa/UIKit/UIImageView.swift b/ReactiveCocoa/UIKit/UIImageView.swift index 15a694bffd..24b3c2501b 100644 --- a/ReactiveCocoa/UIKit/UIImageView.swift +++ b/ReactiveCocoa/UIKit/UIImageView.swift @@ -11,15 +11,12 @@ import UIKit extension UIImageView { /// Wraps a imageView's `image` value in a bindable property. - public var rac_image: MutableProperty { - return associatedProperty(self, key: &imageKey, initial: { $0.image }, setter: { $0.image = $1 }) + public var rac_image: BindingTarget { + return bindingTarget { $0.image = $1 } } /// Wraps a imageView's `highlightedImage` value in a bindable property. - public var rac_highlightedImage: MutableProperty { - return associatedProperty(self, key: &highlightedImageKey, initial: { $0.highlightedImage }, setter: { $0.highlightedImage = $1 }) + public var rac_highlightedImage: BindingTarget { + return bindingTarget { $0.highlightedImage = $1 } } } - -private var imageKey: UInt8 = 0 -private var highlightedImageKey: UInt8 = 0 diff --git a/ReactiveCocoa/UIKit/UILabel.swift b/ReactiveCocoa/UIKit/UILabel.swift index 669fdaf7c8..9da5a91bd9 100644 --- a/ReactiveCocoa/UIKit/UILabel.swift +++ b/ReactiveCocoa/UIKit/UILabel.swift @@ -11,21 +11,17 @@ import UIKit extension UILabel { /// Wraps a label's `text` value in a bindable property. - public var rac_text: MutableProperty { - return associatedProperty(self, key: &attributedTextKey, initial: { $0.text }, setter: { $0.text = $1 }) + public var rac_text: BindingTarget { + return bindingTarget { $0.text = $1 } } /// Wraps a label's `attributedText` value in a bindable property. - public var rac_attributedText: MutableProperty { - return associatedProperty(self, key: &attributedTextKey, initial: { $0.attributedText }, setter: { $0.attributedText = $1 }) + public var rac_attributedText: BindingTarget { + return bindingTarget { $0.attributedText = $1 } } /// Wraps a label's `textColor` value in a bindable property. - public var rac_textColor: MutableProperty { - return associatedProperty(self, key: &textColorKey, initial: { $0.textColor }, setter: { $0.textColor = $1 }) + public var rac_textColor: BindingTarget { + return bindingTarget { $0.textColor = $1 } } } - -private var textKey: UInt8 = 0 -private var attributedTextKey: UInt8 = 0 -private var textColorKey: UInt8 = 0 diff --git a/ReactiveCocoa/UIKit/UIProgressView.swift b/ReactiveCocoa/UIKit/UIProgressView.swift index 2639987b6e..caf8cae6cb 100644 --- a/ReactiveCocoa/UIKit/UIProgressView.swift +++ b/ReactiveCocoa/UIKit/UIProgressView.swift @@ -11,9 +11,7 @@ import UIKit extension UIProgressView { /// Wraps a progressView's `progress` value in a bindable property. - public var rac_progress: MutableProperty { - return associatedProperty(self, key: &progressKey, initial: { $0.progress }, setter: { $0.progress = $1 }) + public var rac_progress: BindingTarget { + return bindingTarget { $0.progress = $1 } } } - -private var progressKey: UInt8 = 0 diff --git a/ReactiveCocoa/UIKit/UIView.swift b/ReactiveCocoa/UIKit/UIView.swift index c179b7df1b..63d566245f 100644 --- a/ReactiveCocoa/UIKit/UIView.swift +++ b/ReactiveCocoa/UIKit/UIView.swift @@ -11,22 +11,18 @@ import UIKit extension UIView { /// Wraps a view's `alpha` value in a bindable property. - public var rac_alpha: MutableProperty { - return associatedProperty(self, key: &alphaKey, initial: { $0.alpha }, setter: { $0.alpha = $1 }) + public var rac_alpha: BindingTarget { + return bindingTarget { $0.alpha = $1 } } /// Wraps a view's `hidden` state in a bindable property. - public var rac_hidden: MutableProperty { - return associatedProperty(self, key: &hiddenKey, initial: { $0.isHidden }, setter: { $0.isHidden = $1 }) + public var rac_hidden: BindingTarget { + return bindingTarget { $0.isHidden = $1 } } /// Wraps a view's `userInteractionEnabled` state in a bindable property. - public var rac_userInteractionEnabled: MutableProperty { - return associatedProperty(self, key: &userInteractionEnabledKey, initial: { $0.isUserInteractionEnabled }, setter: { $0.isUserInteractionEnabled = $1 }) + public var rac_userInteractionEnabled: BindingTarget { + return bindingTarget { $0.isUserInteractionEnabled = $1 } } } - -private var alphaKey: UInt8 = 0 -private var hiddenKey: UInt8 = 0 -private var userInteractionEnabledKey: UInt8 = 0 diff --git a/ReactiveCocoa/UIKit/UIViewController.swift b/ReactiveCocoa/UIKit/UIViewController.swift index cb5e0a6b01..e9eb156ce5 100644 --- a/ReactiveCocoa/UIKit/UIViewController.swift +++ b/ReactiveCocoa/UIKit/UIViewController.swift @@ -36,7 +36,7 @@ extension UIViewController { } public typealias DismissingCompletion = ((Void) -> Void)? - public typealias DismissingInformation = (animated: Bool, completion: DismissingCompletion)? + 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 @@ -48,22 +48,9 @@ extension UIViewController { /// ``` /// The dismissal observation can be made either with binding (example above) /// or `viewController.dismissViewControllerAnimated(true, completion: nil)` - public var rac_dismissAnimated: MutableProperty { - - let initial: (UIViewController) -> DismissingInformation = { _ in nil } - let setter: (UIViewController, DismissingInformation) -> Void = { host, dismissingInfo in - - guard let unwrapped = dismissingInfo else { return } - host.dismiss(animated: unwrapped.animated, completion: unwrapped.completion) - } - - let property = associatedProperty(self, key: &dismissModally, initial: initial, setter: setter) { property in - property <~ self.trigger(for: #selector(UIViewController.dismiss)) - .take { _ in property.value != nil } - .map { _ in return nil } - } - - return property + public var rac_dismissAnimated: BindingTarget { + /// TODO: Convert into `Action`? + return bindingTarget { $0.dismiss(animated: $1.animated, completion: $1.completion) } } } diff --git a/ReactiveCocoaTests/UIKit/UIViewControllerTests.swift b/ReactiveCocoaTests/UIKit/UIViewControllerTests.swift index 15d8fdd548..abaa551882 100644 --- a/ReactiveCocoaTests/UIKit/UIViewControllerTests.swift +++ b/ReactiveCocoaTests/UIKit/UIViewControllerTests.swift @@ -89,7 +89,10 @@ class UIViewControllerTests: XCTestCase { let viewController = UIViewController() _viewController = viewController - viewController.rac_dismissAnimated.signal.observeValues { _ in + viewController + .trigger(for: #selector(UIViewController.dismiss(animated:completion:))) + .signal + .observeValues { _ in expectation.fulfill() } @@ -104,8 +107,11 @@ class UIViewControllerTests: XCTestCase { let viewController = UIViewController() _viewController = viewController - viewController.rac_dismissAnimated.signal.observeValues { _ in - expectation.fulfill() + viewController + .trigger(for: #selector(UIViewController.dismiss(animated:completion:))) + .signal + .observeValues { _ in + expectation.fulfill() } viewController.dismiss(animated: true, completion: nil) From 23ae7c002fa2b3c05fb04797ed0457c0c8e05ee4 Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 30 Sep 2016 12:24:03 +0200 Subject: [PATCH 0405/1028] Reverted accidential changes to the build and project settings. --- ReactiveCocoa.xcodeproj/project.pbxproj | 88 ------------------- .../xcschemes/ReactiveCocoa-iOS.xcscheme | 14 --- .../xcschemes/ReactiveCocoa-macOS.xcscheme | 2 +- 3 files changed, 1 insertion(+), 103 deletions(-) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index c2f5b1d669..365676ce59 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -1117,8 +1117,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; buildSettings = { - CLANG_ENABLE_MODULES = YES; - DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; @@ -1127,10 +1125,7 @@ "DTRACE_PROBES_DISABLED=1", ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -1138,8 +1133,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; buildSettings = { - CLANG_ENABLE_MODULES = YES; - DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; @@ -1148,10 +1141,7 @@ "DTRACE_PROBES_DISABLED=1", ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; }; name = Test; }; @@ -1159,8 +1149,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; buildSettings = { - CLANG_ENABLE_MODULES = YES; - DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; @@ -1169,10 +1157,7 @@ "DTRACE_PROBES_DISABLED=1", ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; }; name = Release; }; @@ -1180,8 +1165,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; buildSettings = { - CLANG_ENABLE_MODULES = YES; - DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; @@ -1190,9 +1173,7 @@ "DTRACE_PROBES_DISABLED=1", ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; - SWIFT_VERSION = 3.0; }; name = Profile; }; @@ -1206,7 +1187,6 @@ "$(inherited)", ); INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; }; name = Debug; @@ -1221,7 +1201,6 @@ "$(inherited)", ); INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; }; name = Test; @@ -1236,7 +1215,6 @@ "$(inherited)", ); INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; }; name = Release; @@ -1251,7 +1229,6 @@ "$(inherited)", ); INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; }; name = Profile; @@ -1260,8 +1237,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; buildSettings = { - CLANG_ENABLE_MODULES = YES; - DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; @@ -1270,10 +1245,7 @@ "DTRACE_PROBES_DISABLED=1", ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -1281,8 +1253,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; buildSettings = { - CLANG_ENABLE_MODULES = YES; - DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; @@ -1291,10 +1261,7 @@ "DTRACE_PROBES_DISABLED=1", ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; }; name = Test; }; @@ -1302,8 +1269,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; buildSettings = { - CLANG_ENABLE_MODULES = YES; - DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; @@ -1312,10 +1277,7 @@ "DTRACE_PROBES_DISABLED=1", ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; }; name = Release; }; @@ -1323,8 +1285,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; buildSettings = { - CLANG_ENABLE_MODULES = YES; - DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; @@ -1333,9 +1293,7 @@ "DTRACE_PROBES_DISABLED=1", ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; - SWIFT_VERSION = 3.0; }; name = Profile; }; @@ -1383,16 +1341,11 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; buildSettings = { - CLANG_ENABLE_MODULES = YES; - DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; USER_HEADER_SEARCH_PATHS = ReactiveCocoa/extobjc; }; name = Debug; @@ -1401,16 +1354,11 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; buildSettings = { - CLANG_ENABLE_MODULES = YES; - DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; USER_HEADER_SEARCH_PATHS = ReactiveCocoa/extobjc; }; name = Release; @@ -1424,7 +1372,6 @@ "$(inherited)", ); INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; }; name = Debug; @@ -1438,7 +1385,6 @@ "$(inherited)", ); INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; }; name = Release; @@ -1447,16 +1393,11 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; buildSettings = { - CLANG_ENABLE_MODULES = YES; - DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; USER_HEADER_SEARCH_PATHS = ReactiveCocoa/extobjc; }; name = Debug; @@ -1465,16 +1406,11 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; buildSettings = { - CLANG_ENABLE_MODULES = YES; - DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; USER_HEADER_SEARCH_PATHS = ReactiveCocoa/extobjc; }; name = Release; @@ -1488,7 +1424,6 @@ "$(inherited)", ); INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; }; name = Debug; @@ -1502,7 +1437,6 @@ "$(inherited)", ); INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; }; name = Release; @@ -1530,15 +1464,11 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; buildSettings = { - CLANG_ENABLE_MODULES = YES; - DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; - SWIFT_VERSION = 3.0; USER_HEADER_SEARCH_PATHS = ReactiveCocoa/extobjc; }; name = Profile; @@ -1552,7 +1482,6 @@ "$(inherited)", ); INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; }; name = Profile; @@ -1561,15 +1490,11 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; buildSettings = { - CLANG_ENABLE_MODULES = YES; - DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; - SWIFT_VERSION = 3.0; USER_HEADER_SEARCH_PATHS = ReactiveCocoa/extobjc; }; name = Profile; @@ -1583,7 +1508,6 @@ "$(inherited)", ); INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; }; name = Profile; @@ -1611,16 +1535,11 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; buildSettings = { - CLANG_ENABLE_MODULES = YES; - DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; USER_HEADER_SEARCH_PATHS = ReactiveCocoa/extobjc; }; name = Test; @@ -1634,7 +1553,6 @@ "$(inherited)", ); INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; }; name = Test; @@ -1643,16 +1561,11 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; buildSettings = { - CLANG_ENABLE_MODULES = YES; - DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; USER_HEADER_SEARCH_PATHS = ReactiveCocoa/extobjc; }; name = Test; @@ -1666,7 +1579,6 @@ "$(inherited)", ); INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; }; name = Test; diff --git a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme index 395f3fad28..2e8560ffbe 100644 --- a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme +++ b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme @@ -108,20 +108,6 @@ BlueprintName = "ReactiveCocoa-iOSTests" ReferencedContainer = "container:ReactiveCocoa.xcodeproj"> - - - - - - - - - - diff --git a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-macOS.xcscheme b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-macOS.xcscheme index 17a53ee08c..9b570d656e 100644 --- a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-macOS.xcscheme +++ b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-macOS.xcscheme @@ -72,7 +72,7 @@ BuildableIdentifier = "primary" BlueprintIdentifier = "DAEB6B8D1943873100289F44" BuildableName = "Quick.framework" - BlueprintName = "Quick-OSX" + BlueprintName = "Quick-macOS" ReferencedContainer = "container:Carthage/Checkouts/Quick/Quick.xcodeproj"> From 81a882cfecf396232490d884f2fc4fa82075452c Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 30 Sep 2016 12:38:31 +0200 Subject: [PATCH 0406/1028] Removed the user header search path setting for extobjc. --- ReactiveCocoa.xcodeproj/project.pbxproj | 8 -------- 1 file changed, 8 deletions(-) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 365676ce59..d91504cfa8 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -1346,7 +1346,6 @@ FRAMEWORK_VERSION = A; INFOPLIST_FILE = ReactiveCocoa/Info.plist; SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; - USER_HEADER_SEARCH_PATHS = ReactiveCocoa/extobjc; }; name = Debug; }; @@ -1359,7 +1358,6 @@ FRAMEWORK_VERSION = A; INFOPLIST_FILE = ReactiveCocoa/Info.plist; SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; - USER_HEADER_SEARCH_PATHS = ReactiveCocoa/extobjc; }; name = Release; }; @@ -1398,7 +1396,6 @@ ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; - USER_HEADER_SEARCH_PATHS = ReactiveCocoa/extobjc; }; name = Debug; }; @@ -1411,7 +1408,6 @@ ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; - USER_HEADER_SEARCH_PATHS = ReactiveCocoa/extobjc; }; name = Release; }; @@ -1469,7 +1465,6 @@ FRAMEWORK_VERSION = A; INFOPLIST_FILE = ReactiveCocoa/Info.plist; SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; - USER_HEADER_SEARCH_PATHS = ReactiveCocoa/extobjc; }; name = Profile; }; @@ -1495,7 +1490,6 @@ ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; - USER_HEADER_SEARCH_PATHS = ReactiveCocoa/extobjc; }; name = Profile; }; @@ -1540,7 +1534,6 @@ FRAMEWORK_VERSION = A; INFOPLIST_FILE = ReactiveCocoa/Info.plist; SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; - USER_HEADER_SEARCH_PATHS = ReactiveCocoa/extobjc; }; name = Test; }; @@ -1566,7 +1559,6 @@ ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; - USER_HEADER_SEARCH_PATHS = ReactiveCocoa/extobjc; }; name = Test; }; From 2bacf27709efef538fc896b3100e098807d7e33a Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 30 Sep 2016 12:44:53 +0200 Subject: [PATCH 0407/1028] Made the associated object API internal. --- ReactiveCocoa/Association.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ReactiveCocoa/Association.swift b/ReactiveCocoa/Association.swift index b2e73cf5ae..2aad38ee39 100644 --- a/ReactiveCocoa/Association.swift +++ b/ReactiveCocoa/Association.swift @@ -15,7 +15,7 @@ import ReactiveSwift /// /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. -public func associatedProperty(_ host: AnyObject, keyPath: StaticString) -> MutableProperty { +internal func associatedProperty(_ host: AnyObject, keyPath: StaticString) -> MutableProperty { let initial: (AnyObject) -> String = { host in host.value(forKeyPath: String(describing: keyPath)) as? String ?? "" } @@ -32,7 +32,7 @@ public func associatedProperty(_ host: AnyObject, keyPath: StaticString) -> Muta /// /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. -public func associatedProperty(_ host: AnyObject, keyPath: StaticString, placeholder: () -> T) -> MutableProperty { +internal func associatedProperty(_ host: AnyObject, keyPath: StaticString, placeholder: () -> T) -> MutableProperty { let setter: (AnyObject, T) -> () = { host, newValue in host.setValue(newValue, forKeyPath: String(describing: keyPath)) } @@ -47,7 +47,7 @@ public func associatedProperty(_ host: AnyObject, keyPath: StaticS /// /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. -public func associatedProperty(_ host: Host, key: UnsafeRawPointer, initial: (Host) -> T, setter: @escaping (Host, T) -> (), setUp: (MutableProperty) -> () = { _ in }) -> MutableProperty { +internal 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)) @@ -66,7 +66,7 @@ public func associatedProperty(_ host: Host, key: UnsafeRawP /// 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: UnsafeRawPointer, initial: (Host) -> T) -> T { +internal func associatedObject(_ host: Host, key: UnsafeRawPointer, initial: (Host) -> T) -> T { var value = objc_getAssociatedObject(host, key) as? T if value == nil { value = initial(host) From 6952c5310449f27a8ddd3ac199bbf0c2872dcc41 Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 30 Sep 2016 13:36:21 +0200 Subject: [PATCH 0408/1028] Consolidated the interface of `CocoaAction`. --- ReactiveCocoa/CocoaAction.swift | 48 ++++------------------- ReactiveCocoa/UIKit/UIBarButtonItem.swift | 2 +- ReactiveCocoa/UIKit/UIButton.swift | 4 +- ReactiveCocoaTests/CocoaActionSpec.swift | 16 +++----- 4 files changed, 16 insertions(+), 54 deletions(-) diff --git a/ReactiveCocoa/CocoaAction.swift b/ReactiveCocoa/CocoaAction.swift index e327fb4bd7..d5ab40f0ed 100644 --- a/ReactiveCocoa/CocoaAction.swift +++ b/ReactiveCocoa/CocoaAction.swift @@ -20,29 +20,16 @@ public final class CocoaAction: NSObject { /// /// This property will only change on the main thread, and will generate a /// KVO notification for every change. - public private(set) var isEnabled: Bool = false + public let isEnabled: Property /// Whether the action is executing. /// /// This property will only change on the main thread, and will generate a /// KVO notification for every change. - public private(set) var isExecuting: Bool = false - - /// Creates a producer for the `enabled` state of a CocoaAction. - public var isEnabledProducer: SignalProducer { - return values(forKeyPath: #keyPath(CocoaAction.isEnabled)) - .map { $0 as! Bool } - } - - /// Creates a producer for the `executing` state of a CocoaAction. - public var isExecutingProducer: SignalProducer { - return values(forKeyPath: #keyPath(CocoaAction.isExecuting)) - .map { $0 as! Bool } - } + public let isExecuting: Property private let _execute: (AnyObject?) -> Void - private let disposable = CompositeDisposable() - + /// Initializes a Cocoa action that will invoke the given Action by /// transforming the object given to execute(). /// @@ -61,24 +48,11 @@ public final class CocoaAction: NSObject { let producer = action.apply(inputTransform(input)) producer.start() } + + isEnabled = action.isEnabled + isExecuting = action.isExecuting super.init() - - disposable += action.isEnabled.producer - .observe(on: UIScheduler()) - .startWithValues { [weak self] value in - self?.willChangeValue(forKey: #keyPath(CocoaAction.isEnabled)) - self?.isEnabled = value - self?.didChangeValue(forKey: #keyPath(CocoaAction.isEnabled)) - } - - disposable += action.isExecuting.producer - .observe(on: UIScheduler()) - .startWithValues { [weak self] value in - self?.willChangeValue(forKey: #keyPath(CocoaAction.isExecuting)) - self?.isExecuting = value - self?.didChangeValue(forKey: #keyPath(CocoaAction.isExecuting)) - } } /// Initializes a Cocoa action that will invoke the given Action by always @@ -90,11 +64,7 @@ public final class CocoaAction: NSObject { public convenience init(_ action: Action, input: Input) { self.init(action, { _ in input }) } - - deinit { - disposable.dispose() - } - + /// Attempts to execute the underlying action with the given input, subject /// to the behavior described by the initializer that was used. /// @@ -103,10 +73,6 @@ public final class CocoaAction: NSObject { @IBAction public func execute(_ input: AnyObject?) { _execute(input) } - - public override class func automaticallyNotifiesObservers(forKey key: String) -> Bool { - return false - } } extension Action { diff --git a/ReactiveCocoa/UIKit/UIBarButtonItem.swift b/ReactiveCocoa/UIKit/UIBarButtonItem.swift index d45f2b3f9c..2b4fc7dfcc 100644 --- a/ReactiveCocoa/UIKit/UIBarButtonItem.swift +++ b/ReactiveCocoa/UIKit/UIBarButtonItem.swift @@ -25,7 +25,7 @@ extension UIBarButtonItem { host?.action = CocoaAction.selector } - host.rac_enabled <~ property.producer.flatMap(.latest) { $0.isEnabledProducer } + host.rac_enabled <~ property.flatMap(.latest) { $0.isEnabled } return property } diff --git a/ReactiveCocoa/UIKit/UIButton.swift b/ReactiveCocoa/UIKit/UIButton.swift index 843cd3f2f9..ed406b205d 100644 --- a/ReactiveCocoa/UIKit/UIButton.swift +++ b/ReactiveCocoa/UIKit/UIButton.swift @@ -24,9 +24,9 @@ extension UIButton { .startWithValues { [weak host] previous, next in host?.removeTarget(previous, action: CocoaAction.selector, for: .touchUpInside) host?.addTarget(next, action: CocoaAction.selector, for: .touchUpInside) - } + } - host.rac_enabled <~ property.producer.flatMap(.latest) { $0.isEnabledProducer } + host.rac_enabled <~ property.flatMap(.latest) { $0.isEnabled } return property } diff --git a/ReactiveCocoaTests/CocoaActionSpec.swift b/ReactiveCocoaTests/CocoaActionSpec.swift index 13b14dadaa..590b9c5a2f 100644 --- a/ReactiveCocoaTests/CocoaActionSpec.swift +++ b/ReactiveCocoaTests/CocoaActionSpec.swift @@ -12,7 +12,7 @@ class CocoaActionSpec: QuickSpec { action = Action { value in SignalProducer(value: value + 1) } expect(action.isEnabled.value) == true - expect(action.unsafeCocoaAction.isEnabled).toEventually(beTruthy()) + expect(action.unsafeCocoaAction.isEnabled.value).toEventually(beTruthy()) } #if os(OSX) @@ -30,14 +30,12 @@ class CocoaActionSpec: QuickSpec { } #endif - it("should generate KVO notifications for enabled") { + it("should emit changes for enabled") { var values: [Bool] = [] let cocoaAction = action.unsafeCocoaAction - cocoaAction - .values(forKeyPath: #keyPath(CocoaAction.isEnabled)) - .map { $0! as! Bool } - .start(Observer(value: { values.append($0) })) + cocoaAction.isEnabled.producer + .startWithValues { values.append($0) } expect(values) == [ true ] @@ -52,10 +50,8 @@ class CocoaActionSpec: QuickSpec { var values: [Bool] = [] let cocoaAction = action.unsafeCocoaAction - cocoaAction - .values(forKeyPath: #keyPath(CocoaAction.isExecuting)) - .map { $0! as! Bool } - .start(Observer(value: { values.append($0) })) + cocoaAction.isExecuting.producer + .startWithValues { values.append($0) } expect(values) == [ false ] From df661a2af9b6c7480930158546b5942f7ed56344 Mon Sep 17 00:00:00 2001 From: Anders Date: Sat, 1 Oct 2016 15:51:13 +0200 Subject: [PATCH 0409/1028] Use `()` instead of `Void`. --- ReactiveCocoa/Reusable.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa/Reusable.swift b/ReactiveCocoa/Reusable.swift index 8441173298..7c64a17f0f 100644 --- a/ReactiveCocoa/Reusable.swift +++ b/ReactiveCocoa/Reusable.swift @@ -39,7 +39,7 @@ extension Reusable where Self: NSObject { /// .takeUntil(self.rac_prepareForReuse) /// ``` /// - public var rac_prepareForReuse: Signal { + public var rac_prepareForReuse: Signal<(), NoError> { return trigger(for: #selector(prepareForReuse)) } } From ad8f9be41a3e616ee15db78d73a952ab27cf94fe Mon Sep 17 00:00:00 2001 From: Anders Date: Sat, 1 Oct 2016 17:00:31 +0200 Subject: [PATCH 0410/1028] Replaced `rac_` prefixes with `Reactivity`. --- ReactiveCocoa.xcodeproj/project.pbxproj | 10 ++++++ ReactiveCocoa/AppKit/NSTextField.swift | 8 ++--- ReactiveCocoa/DynamicProperty.swift | 4 +-- ReactiveCocoa/NSObject+BindingTarget.swift | 26 ++++++-------- ReactiveCocoa/NSObject+Intercepting.swift | 2 +- .../NSObject+KeyValueObserving.swift | 6 ++-- ReactiveCocoa/NSObject+Lifetime.swift | 14 ++++---- ReactiveCocoa/Reactive.swift | 28 +++++++++++++++ ReactiveCocoa/Reusable.swift | 6 ++-- .../UIKit/UIActivityIndicatorView.swift | 4 +-- ReactiveCocoa/UIKit/UIBarButtonItem.swift | 10 +++--- ReactiveCocoa/UIKit/UIBarItem.swift | 4 +-- ReactiveCocoa/UIKit/UIButton.swift | 12 +++---- ReactiveCocoa/UIKit/UIControl.swift | 36 +++++++++---------- ReactiveCocoa/UIKit/UIDatePicker.swift | 6 ++-- ReactiveCocoa/UIKit/UIImageView.swift | 6 ++-- ReactiveCocoa/UIKit/UILabel.swift | 8 ++--- ReactiveCocoa/UIKit/UIProgressView.swift | 4 +-- ReactiveCocoa/UIKit/UISegmentedControl.swift | 13 ++++--- ReactiveCocoa/UIKit/UISwitch.swift | 6 ++-- ReactiveCocoa/UIKit/UITextField.swift | 10 +++--- ReactiveCocoa/UIKit/UITextView.swift | 6 ++-- ReactiveCocoa/UIKit/UIView.swift | 8 ++--- ReactiveCocoa/UIKit/UIViewController.swift | 22 ++++++------ .../UIKit/UIActivityIndicatorViewTests.swift | 2 +- .../UIKit/UIBarButtonItemTests.swift | 4 +-- ReactiveCocoaTests/UIKit/UIButtonTests.swift | 10 +++--- .../UIKit/UICollectionReusableViewTests.swift | 4 +-- ReactiveCocoaTests/UIKit/UIControlTests.swift | 16 ++++----- .../UIKit/UIDatePickerTests.swift | 4 +-- .../UIKit/UIImageViewTests.swift | 8 ++--- ReactiveCocoaTests/UIKit/UILabelTests.swift | 10 +++--- .../UIKit/UIProgressViewTests.swift | 4 +-- .../UIKit/UISegmentedControlTests.swift | 2 +- ReactiveCocoaTests/UIKit/UISwitchTests.swift | 4 +-- .../UIKit/UITableViewCellTests.swift | 4 +-- .../UITableViewHeaderFooterViewTests.swift | 4 +-- .../UIKit/UITextFieldTests.swift | 2 +- .../UIKit/UITextViewTests.swift | 2 +- .../UIKit/UIViewControllerTests.swift | 10 +++--- ReactiveCocoaTests/UIKit/UIViewTests.swift | 10 +++--- 41 files changed, 196 insertions(+), 163 deletions(-) create mode 100644 ReactiveCocoa/Reactive.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index d91504cfa8..9ddc196f35 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -21,6 +21,10 @@ 7DFBED211CDB8D8300EE435B /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 7DFBED281CDB8DE300EE435B /* DynamicPropertySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */; }; 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */; }; + 9A090C081D9FF6E400EE97CA /* Reactive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A090C071D9FF6E400EE97CA /* Reactive.swift */; }; + 9A090C091D9FF6E400EE97CA /* Reactive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A090C071D9FF6E400EE97CA /* Reactive.swift */; }; + 9A090C0A1D9FF6E400EE97CA /* Reactive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A090C071D9FF6E400EE97CA /* Reactive.swift */; }; + 9A090C0B1D9FF6E400EE97CA /* Reactive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A090C071D9FF6E400EE97CA /* Reactive.swift */; }; 9A1D05E01D93E99100ACF44C /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* Association.swift */; }; 9A1D05E11D93E99100ACF44C /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* Association.swift */; }; 9A1D05E21D93E99100ACF44C /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* Association.swift */; }; @@ -228,6 +232,7 @@ 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Framework.xcconfig"; sourceTree = ""; }; 57A4D2471BA13F9700F7D4B1 /* tvOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-StaticLibrary.xcconfig"; sourceTree = ""; }; 7DFBED031CDB8C9500EE435B /* ReactiveCocoaTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveCocoaTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 9A090C071D9FF6E400EE97CA /* Reactive.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reactive.swift; sourceTree = ""; }; 9A1D05DF1D93E99100ACF44C /* Association.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Association.swift; sourceTree = ""; }; 9A1D05E41D93E9E200ACF44C /* Reusable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reusable.swift; sourceTree = ""; }; 9A1D05EA1D93E9F100ACF44C /* NSTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSTextField.swift; sourceTree = ""; }; @@ -530,6 +535,7 @@ 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */, 9A1D05DF1D93E99100ACF44C /* Association.swift */, 9A1D05E41D93E9E200ACF44C /* Reusable.swift */, + 9A090C071D9FF6E400EE97CA /* Reactive.swift */, 9A1D05E91D93E9F100ACF44C /* AppKit */, 9A1D05EB1D93E9F100ACF44C /* UIKit */, D04725ED19E49ED7006002AA /* Supporting Files */, @@ -941,6 +947,7 @@ 9A1D06161D93EA0100ACF44C /* UIControl.swift in Sources */, 9A1D06141D93EA0100ACF44C /* UIButton.swift in Sources */, 9A1D06221D93EA0100ACF44C /* UIViewController.swift in Sources */, + 9A090C0B1D9FF6E400EE97CA /* Reactive.swift in Sources */, 9A1D065E1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, 9A1D06151D93EA0100ACF44C /* UICollectionReusableView.swift in Sources */, 9AF0EA781D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, @@ -991,6 +998,7 @@ 9A1D05E71D93E9E200ACF44C /* Reusable.swift in Sources */, 9A1D065D1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, C7142DBE1CDEA194009F402D /* CocoaAction.swift in Sources */, + 9A090C0A1D9FF6E400EE97CA /* Reactive.swift in Sources */, CD0C45E01CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9AF0EA771D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, 9A1D05E21D93E99100ACF44C /* Association.swift in Sources */, @@ -1008,6 +1016,7 @@ 9A1D06771D9415FF00ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, 9A1D05FE1D93E9F900ACF44C /* NSTextField.swift in Sources */, + 9A090C081D9FF6E400EE97CA /* Reactive.swift in Sources */, 9A1D05E01D93E99100ACF44C /* Association.swift in Sources */, 9AF0EA751D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, 9A1D065B1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, @@ -1044,6 +1053,7 @@ 9A1D060F1D93EA0000ACF44C /* UIView.swift in Sources */, 9A1D060B1D93EA0000ACF44C /* UITableViewCell.swift in Sources */, 9A1D06051D93EA0000ACF44C /* UIDatePicker.swift in Sources */, + 9A090C091D9FF6E400EE97CA /* Reactive.swift in Sources */, 9A1D05E11D93E99100ACF44C /* Association.swift in Sources */, 9A1D06041D93EA0000ACF44C /* UIControl.swift in Sources */, 9A1D06021D93EA0000ACF44C /* UIButton.swift in Sources */, diff --git a/ReactiveCocoa/AppKit/NSTextField.swift b/ReactiveCocoa/AppKit/NSTextField.swift index 6da27fdb6f..fde58f5280 100644 --- a/ReactiveCocoa/AppKit/NSTextField.swift +++ b/ReactiveCocoa/AppKit/NSTextField.swift @@ -10,13 +10,13 @@ import ReactiveSwift import AppKit import enum Result.NoError -extension NSTextField { +extension Reactivity where Reactant: NSTextField { /// Sends the field's string value whenever it changes. - public var rac_textSignal: SignalProducer { + public var textSignal: SignalProducer { return NotificationCenter.default - .rac_notifications(forName: .NSControlTextDidChange, object: self) + .rac_notifications(forName: .NSControlTextDidChange, object: reactant) .map { notification in (notification.object as! NSTextField).stringValue - } + } } } diff --git a/ReactiveCocoa/DynamicProperty.swift b/ReactiveCocoa/DynamicProperty.swift index a4a0020474..fa04cc9e07 100644 --- a/ReactiveCocoa/DynamicProperty.swift +++ b/ReactiveCocoa/DynamicProperty.swift @@ -39,7 +39,7 @@ public final class DynamicProperty: MutablePropertyProtocol { /// The lifetime of the property. public var lifetime: Lifetime { - return object?.rac_lifetime ?? .empty + return object?.rac.lifetime ?? .empty } /// A producer that will create a Key-Value Observer for the given object, @@ -84,7 +84,7 @@ public final class DynamicProperty: MutablePropertyProtocol { /// A DynamicProperty will stay alive as long as its object is alive. /// This is made possible by strong reference cycles. - _ = object?.rac_lifetime.ended.observeCompleted { _ = self } + _ = object?.rac.lifetime.ended.observeCompleted { _ = self } } } diff --git a/ReactiveCocoa/NSObject+BindingTarget.swift b/ReactiveCocoa/NSObject+BindingTarget.swift index d647b34927..105248702a 100644 --- a/ReactiveCocoa/NSObject+BindingTarget.swift +++ b/ReactiveCocoa/NSObject+BindingTarget.swift @@ -1,29 +1,23 @@ import Foundation import ReactiveSwift -internal protocol NSObjectBindingTargetProtocol: class { - var rac_lifetime: Lifetime { get } -} - -extension NSObjectBindingTargetProtocol { - /// Creates a binding target which uses the lifetime of `self`, and weakly - /// references `self` so that the supplied `action` is triggered only if - /// `self` has not deinitialized. +extension Reactivity where Reactant: NSObject { + /// Creates a binding target which uses the lifetime of `reactant`, and weakly + /// references `reactant` so that the supplied `action` is triggered only if + /// `reactant` has not deinitialized. /// - /// - important: The resulting binding target is bound to the main queue. + /// - important: The binding target is bound to the main queue. /// /// - parameters: /// - action: The action to consume values from the bindings. /// /// - returns: - /// A binding target that holds no strong references to `self`. - internal func bindingTarget(action: @escaping (Self, U) -> Void) -> BindingTarget { - return BindingTarget(on: .main, lifetime: rac_lifetime) { [weak self] value in - if let strongSelf = self { - action(strongSelf, value) + /// A binding target that holds no strong references to `reactant`. + internal func bindingTarget(action: @escaping (Reactant, U) -> Void) -> BindingTarget { + return BindingTarget(on: .main, lifetime: reactant.rac.lifetime) { [weak reactant] value in + if let reactant = reactant { + action(reactant, value) } } } } - -extension NSObject: NSObjectBindingTargetProtocol {} diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 8bf7538815..be92181c22 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -34,7 +34,7 @@ extension NSObject { let isSuccessful = RACRegisterBlockForSelector(self, selector, nil, action) assert(isSuccessful) - rac_lifetime.ended.observeCompleted(observer.sendCompleted) + rac.lifetime.ended.observeCompleted(observer.sendCompleted) map.setObject(signal, forKey: selectorName) return signal diff --git a/ReactiveCocoa/NSObject+KeyValueObserving.swift b/ReactiveCocoa/NSObject+KeyValueObserving.swift index 8114005973..3b5d3065fa 100644 --- a/ReactiveCocoa/NSObject+KeyValueObserving.swift +++ b/ReactiveCocoa/NSObject+KeyValueObserving.swift @@ -21,7 +21,7 @@ extension NSObject { options: [.initial, .new], action: observer.send ) - disposable += self.rac_lifetime.ended.observeCompleted(observer.sendCompleted) + disposable += self.rac.lifetime.ended.observeCompleted(observer.sendCompleted) } } } @@ -136,7 +136,7 @@ extension KeyValueObserver { headSerialDisposable.innerDisposable = headDisposable if shouldObserveDeinit { - let disposable = value.rac_lifetime.ended.observeCompleted { + let disposable = value.rac.lifetime.ended.observeCompleted { action(nil) } headDisposable += disposable @@ -162,7 +162,7 @@ extension KeyValueObserver { } if shouldObserveDeinit { - let disposable = value.rac_lifetime.ended.observeCompleted { + let disposable = value.rac.lifetime.ended.observeCompleted { action(nil) } headSerialDisposable.innerDisposable = disposable diff --git a/ReactiveCocoa/NSObject+Lifetime.swift b/ReactiveCocoa/NSObject+Lifetime.swift index 1f220d12d4..26bd335984 100644 --- a/ReactiveCocoa/NSObject+Lifetime.swift +++ b/ReactiveCocoa/NSObject+Lifetime.swift @@ -4,21 +4,21 @@ import ReactiveSwift private var lifetimeKey: UInt8 = 0 private var lifetimeTokenKey: UInt8 = 0 -extension NSObject { +extension Reactivity where Reactant: NSObject { /// Returns a lifetime that ends when the receiver is deallocated. - @nonobjc public var rac_lifetime: Lifetime { - objc_sync_enter(self) - defer { objc_sync_exit(self) } + @nonobjc public var lifetime: Lifetime { + objc_sync_enter(reactant) + defer { objc_sync_exit(reactant) } - if let lifetime = objc_getAssociatedObject(self, &lifetimeKey) as! Lifetime? { + if let lifetime = objc_getAssociatedObject(reactant, &lifetimeKey) as! Lifetime? { return lifetime } let token = Lifetime.Token() let lifetime = Lifetime(token) - objc_setAssociatedObject(self, &lifetimeTokenKey, token, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - objc_setAssociatedObject(self, &lifetimeKey, lifetime, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + objc_setAssociatedObject(reactant, &lifetimeTokenKey, token, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + objc_setAssociatedObject(reactant, &lifetimeKey, lifetime, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) return lifetime } diff --git a/ReactiveCocoa/Reactive.swift b/ReactiveCocoa/Reactive.swift new file mode 100644 index 0000000000..822083a4d3 --- /dev/null +++ b/ReactiveCocoa/Reactive.swift @@ -0,0 +1,28 @@ +/// Describes a class which has been extended for reactivity. +/// +/// - note: `ExtendedForReactivity` is only intended for extensions to types +/// that are not owned by the module. Non-conforming types may carry +/// first-party reactive elements. +public protocol ExtendedForReactivity: class {} + +extension ExtendedForReactivity { + /// A proxy which exposes the reactivity of `self`. + public var rac: Reactivity { + return Reactivity(self) + } +} + +extension NSObject: ExtendedForReactivity {} + +// A proxy which exposes the reactivity of the Reactant type. +public struct Reactivity { + public let reactant: Reactant + + // Construct a proxy. + // + // - parameters: + // - reactant: The object to be proxied. + fileprivate init(_ reactant: Reactant) { + self.reactant = reactant + } +} diff --git a/ReactiveCocoa/Reusable.swift b/ReactiveCocoa/Reusable.swift index 7c64a17f0f..9485fc6937 100644 --- a/ReactiveCocoa/Reusable.swift +++ b/ReactiveCocoa/Reusable.swift @@ -16,7 +16,7 @@ import enum Result.NoError func prepareForReuse() } -extension Reusable where Self: NSObject { +extension Reactivity where Reactant: NSObject, Reactant: Reusable { /// A signal which will send a `Next` event whenever `prepareForReuse` is invoked upon /// the receiver. /// @@ -39,7 +39,7 @@ extension Reusable where Self: NSObject { /// .takeUntil(self.rac_prepareForReuse) /// ``` /// - public var rac_prepareForReuse: Signal<(), NoError> { - return trigger(for: #selector(prepareForReuse)) + public var prepareForReuse: Signal<(), NoError> { + return reactant.trigger(for: #selector(Reactant.prepareForReuse)) } } diff --git a/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift b/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift index 8f647f7559..17dfa5d92c 100644 --- a/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift +++ b/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift @@ -9,12 +9,12 @@ import ReactiveSwift import UIKit -extension UIActivityIndicatorView { +extension Reactivity where Reactant: UIActivityIndicatorView { /// Wraps an indicator's `isAnimating()` state in a bindable property. /// Setting a new value to the property would call `startAnimating()` or /// `stopAnimating()` depending on the value. - public var rac_animating: BindingTarget { + public var isAnimating: BindingTarget { return bindingTarget { _self, isAnimating in if isAnimating { _self.startAnimating() diff --git a/ReactiveCocoa/UIKit/UIBarButtonItem.swift b/ReactiveCocoa/UIKit/UIBarButtonItem.swift index 2b4fc7dfcc..4705d8fa33 100644 --- a/ReactiveCocoa/UIKit/UIBarButtonItem.swift +++ b/ReactiveCocoa/UIKit/UIBarButtonItem.swift @@ -9,13 +9,13 @@ import ReactiveSwift import UIKit -extension UIBarButtonItem { +extension Reactivity where Reactant: UIBarButtonItem { /// Exposes a property that binds an action to bar button item. The action is set as /// a target of the button. When property changes occur the previous action is - /// overwritten. This also binds the enabled state of the action to the `rac_enabled` + /// overwritten. This also binds the enabled state of the action to the `enabled` /// property on the button. - public var rac_action: MutableProperty { - return associatedObject(self, key: &actionKey) { host in + public var action: MutableProperty { + return associatedObject(reactant, key: &actionKey) { host in let initial = CocoaAction.disabled let property = MutableProperty(initial) @@ -25,7 +25,7 @@ extension UIBarButtonItem { host?.action = CocoaAction.selector } - host.rac_enabled <~ property.flatMap(.latest) { $0.isEnabled } + host.rac.isEnabled <~ property.flatMap(.latest) { $0.isEnabled } return property } diff --git a/ReactiveCocoa/UIKit/UIBarItem.swift b/ReactiveCocoa/UIKit/UIBarItem.swift index 879c1c884e..ad21dd97a6 100644 --- a/ReactiveCocoa/UIKit/UIBarItem.swift +++ b/ReactiveCocoa/UIKit/UIBarItem.swift @@ -9,9 +9,9 @@ import ReactiveSwift import UIKit -extension UIBarItem { +extension Reactivity where Reactant: UIBarItem { /// Wraps a UIBarItem's `enabled` state in a bindable property. - public var rac_enabled: BindingTarget { + public var isEnabled: BindingTarget { return bindingTarget { $0.isEnabled = $1 } } } diff --git a/ReactiveCocoa/UIKit/UIButton.swift b/ReactiveCocoa/UIKit/UIButton.swift index ed406b205d..711acc48b0 100644 --- a/ReactiveCocoa/UIKit/UIButton.swift +++ b/ReactiveCocoa/UIKit/UIButton.swift @@ -9,13 +9,13 @@ import ReactiveSwift import UIKit -extension UIButton { +extension Reactivity where Reactant: UIButton { /// Exposes a property that binds an action to button presses. The action is set as /// a target of the button for `TouchUpInside` events. When property changes occur the /// previous action is removed as a target. This also binds the enabled state of the - /// action to the `rac_enabled` property on the button. - public var rac_pressed: MutableProperty { - return associatedObject(self, key: &pressedKey) { host in + /// action to the `enabled` property on the button. + public var pressed: MutableProperty { + return associatedObject(reactant, key: &pressedKey) { host in let initial = CocoaAction.disabled let property = MutableProperty(initial) @@ -26,7 +26,7 @@ extension UIButton { host?.addTarget(next, action: CocoaAction.selector, for: .touchUpInside) } - host.rac_enabled <~ property.flatMap(.latest) { $0.isEnabled } + host.rac.isEnabled <~ property.flatMap(.latest) { $0.isEnabled } return property } @@ -34,7 +34,7 @@ extension UIButton { /// Wraps a button's `title` text in a bindable property. Note that this only applies /// to `UIControlState.Normal`. - public var rac_title: BindingTarget { + public var title: BindingTarget { return bindingTarget { $0.setTitle($1, for: .normal) } } } diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index 3e5ff0c184..c400b9a032 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -10,29 +10,29 @@ import ReactiveSwift import UIKit import enum Result.NoError -private class UnsafeControlReceiver: NSObject { - private let observer: Observer<(), NoError> +private class UnsafeControlReceiver: NSObject { + private let observer: Observer - fileprivate init(observer: Observer<(), NoError>) { + fileprivate init(observer: Observer) { self.observer = observer } - @objc fileprivate func sendNext() { - observer.send(value: ()) + @objc fileprivate func sendNext(_ receiver: Any?) { + observer.send(value: receiver as! Control) } } -extension UIControl { - public func trigger(for events: UIControlEvents) -> Signal<(), NoError> { +extension Reactivity where Reactant: UIControl { + public func trigger(for events: UIControlEvents) -> Signal { return Signal { observer in let receiver = UnsafeControlReceiver(observer: observer) - addTarget(receiver, action: #selector(UnsafeControlReceiver.sendNext), for: events) + reactant.addTarget(receiver, action: #selector(UnsafeControlReceiver.sendNext), for: events) - let disposable = rac_lifetime.ended.observeCompleted(observer.sendCompleted) + let disposable = reactant.rac.lifetime.ended.observeCompleted(observer.sendCompleted) - return ActionDisposable { [weak self] in + return ActionDisposable { [weak reactant] in disposable?.dispose() - self?.removeTarget(receiver, action: #selector(UnsafeControlReceiver.sendNext), for: events) + reactant?.removeTarget(receiver, action: #selector(UnsafeControlReceiver.sendNext), for: events) } } } @@ -43,27 +43,25 @@ extension UIControl { /// This property uses `UIControlEvents.ValueChanged` and `UIControlEvents.EditingChanged` /// events to detect changes and keep the value up-to-date. // - class func rac_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.trigger(for: [.valueChanged, .editingChanged]) - .map { [unowned host] in getter(host) } + internal func value(getter: @escaping (Reactant) -> T, setter: @escaping (Reactant, T) -> ()) -> MutableProperty { + return associatedProperty(reactant, key: &valueChangedKey, initial: getter, setter: setter) { property in + property <~ self.trigger(for: [.valueChanged, .editingChanged]).map(getter) } } #endif /// Wraps a control's `enabled` state in a bindable property. - public var rac_enabled: BindingTarget { + public var isEnabled: BindingTarget { return bindingTarget { $0.isEnabled = $1 } } /// Wraps a control's `selected` state in a bindable property. - public var rac_selected: BindingTarget { + public var isSelected: BindingTarget { return bindingTarget { $0.isSelected = $1 } } /// Wraps a control's `highlighted` state in a bindable property. - public var rac_highlighted: BindingTarget { + public var isHighlighted: BindingTarget { return bindingTarget { $0.isHighlighted = $1 } } } diff --git a/ReactiveCocoa/UIKit/UIDatePicker.swift b/ReactiveCocoa/UIKit/UIDatePicker.swift index 8db51420b3..1ef8f47bbb 100644 --- a/ReactiveCocoa/UIKit/UIDatePicker.swift +++ b/ReactiveCocoa/UIKit/UIDatePicker.swift @@ -9,9 +9,9 @@ import ReactiveSwift import UIKit -extension UIDatePicker { +extension Reactivity where Reactant: UIDatePicker { // Wraps a datePicker's `date` value in a bindable property. - public var rac_date: MutableProperty { - return UIControl.rac_value(self, getter: { $0.date }, setter: { $0.date = $1 }) + public var date: MutableProperty { + return value(getter: { $0.date }, setter: { $0.date = $1 }) } } diff --git a/ReactiveCocoa/UIKit/UIImageView.swift b/ReactiveCocoa/UIKit/UIImageView.swift index 24b3c2501b..ddf813d8b9 100644 --- a/ReactiveCocoa/UIKit/UIImageView.swift +++ b/ReactiveCocoa/UIKit/UIImageView.swift @@ -9,14 +9,14 @@ import ReactiveSwift import UIKit -extension UIImageView { +extension Reactivity where Reactant: UIImageView { /// Wraps a imageView's `image` value in a bindable property. - public var rac_image: BindingTarget { + public var image: BindingTarget { return bindingTarget { $0.image = $1 } } /// Wraps a imageView's `highlightedImage` value in a bindable property. - public var rac_highlightedImage: BindingTarget { + public var highlightedImage: BindingTarget { return bindingTarget { $0.highlightedImage = $1 } } } diff --git a/ReactiveCocoa/UIKit/UILabel.swift b/ReactiveCocoa/UIKit/UILabel.swift index 9da5a91bd9..593cd41ae7 100644 --- a/ReactiveCocoa/UIKit/UILabel.swift +++ b/ReactiveCocoa/UIKit/UILabel.swift @@ -9,19 +9,19 @@ import ReactiveSwift import UIKit -extension UILabel { +extension Reactivity where Reactant: UILabel { /// Wraps a label's `text` value in a bindable property. - public var rac_text: BindingTarget { + public var text: BindingTarget { return bindingTarget { $0.text = $1 } } /// Wraps a label's `attributedText` value in a bindable property. - public var rac_attributedText: BindingTarget { + public var attributedText: BindingTarget { return bindingTarget { $0.attributedText = $1 } } /// Wraps a label's `textColor` value in a bindable property. - public var rac_textColor: BindingTarget { + public var textColor: BindingTarget { return bindingTarget { $0.textColor = $1 } } } diff --git a/ReactiveCocoa/UIKit/UIProgressView.swift b/ReactiveCocoa/UIKit/UIProgressView.swift index caf8cae6cb..c11e7e46da 100644 --- a/ReactiveCocoa/UIKit/UIProgressView.swift +++ b/ReactiveCocoa/UIKit/UIProgressView.swift @@ -9,9 +9,9 @@ import ReactiveSwift import UIKit -extension UIProgressView { +extension Reactivity where Reactant: UIProgressView { /// Wraps a progressView's `progress` value in a bindable property. - public var rac_progress: BindingTarget { + public var progress: BindingTarget { return bindingTarget { $0.progress = $1 } } } diff --git a/ReactiveCocoa/UIKit/UISegmentedControl.swift b/ReactiveCocoa/UIKit/UISegmentedControl.swift index 8c14b1b1ad..8691adbb8b 100644 --- a/ReactiveCocoa/UIKit/UISegmentedControl.swift +++ b/ReactiveCocoa/UIKit/UISegmentedControl.swift @@ -9,12 +9,15 @@ import ReactiveSwift import UIKit -extension UISegmentedControl { +extension Reactivity where Reactant: UISegmentedControl { /// Wraps a segmentedControls `selectedSegmentIndex` state in a bindable property. - public var rac_selectedSegmentIndex: MutableProperty { - let property = associatedProperty(self, key: &selectedSegmentIndexKey, initial: { $0.selectedSegmentIndex }, setter: { $0.selectedSegmentIndex = $1 }) - property <~ trigger(for: .valueChanged) - .map { [unowned self] in self.selectedSegmentIndex } + public var selectedSegmentIndex: MutableProperty { + let property = associatedProperty(reactant, + key: &selectedSegmentIndexKey, + initial: { $0.selectedSegmentIndex }, + setter: { $0.selectedSegmentIndex = $1 }) + + property <~ trigger(for: .valueChanged).map { $0.selectedSegmentIndex } return property } } diff --git a/ReactiveCocoa/UIKit/UISwitch.swift b/ReactiveCocoa/UIKit/UISwitch.swift index 80002f7f86..dcf396104f 100644 --- a/ReactiveCocoa/UIKit/UISwitch.swift +++ b/ReactiveCocoa/UIKit/UISwitch.swift @@ -9,9 +9,9 @@ import ReactiveSwift import UIKit -extension UISwitch { +extension Reactivity where Reactant: UISwitch { /// Wraps a switch's `on` value in a bindable property. - public var rac_on: MutableProperty { - return UIControl.rac_value(self, getter: { $0.isOn }, setter: { $0.isOn = $1 }) + public var isOn: MutableProperty { + return value(getter: { $0.isOn }, setter: { $0.isOn = $1 }) } } diff --git a/ReactiveCocoa/UIKit/UITextField.swift b/ReactiveCocoa/UIKit/UITextField.swift index fa1ec5940c..5e8e5def50 100644 --- a/ReactiveCocoa/UIKit/UITextField.swift +++ b/ReactiveCocoa/UIKit/UITextField.swift @@ -9,18 +9,18 @@ import ReactiveSwift import UIKit -extension UITextField { +extension Reactivity where Reactant: UITextField { /// Wraps a textField's `text` value in a bindable property. - public var rac_text: MutableProperty { + public var text: MutableProperty { let getter: (UITextField) -> String? = { $0.text } let setter: (UITextField, String?) -> () = { $0.text = $1 } #if os(iOS) - return UIControl.rac_value(self, getter: getter, setter: setter) + return value(getter: getter, setter: setter) #else - return associatedProperty(self, key: &textKey, initial: getter, setter: setter) { property in + return associatedProperty(reactant, key: &textKey, initial: getter, setter: setter) { property in property <~ NotificationCenter.default - .rac_notifications(forName: .UITextFieldTextDidChange, object: self) + .rac_notifications(forName: .UITextFieldTextDidChange, object: reactant) .map { ($0.object as! UITextField).text } } #endif diff --git a/ReactiveCocoa/UIKit/UITextView.swift b/ReactiveCocoa/UIKit/UITextView.swift index 46c295dc92..4edd397991 100644 --- a/ReactiveCocoa/UIKit/UITextView.swift +++ b/ReactiveCocoa/UIKit/UITextView.swift @@ -10,11 +10,11 @@ import ReactiveSwift import UIKit import enum Result.NoError -extension UITextView { +extension Reactivity where Reactant: UITextView { /// Sends the textView's string value whenever it changes. - public var rac_text: SignalProducer { + public var text: SignalProducer { return NotificationCenter.default - .rac_notifications(forName: .UITextViewTextDidChange, object: self) + .rac_notifications(forName: .UITextViewTextDidChange, object: reactant) .map { ($0.object as? UITextView)?.text } .skipNil() } diff --git a/ReactiveCocoa/UIKit/UIView.swift b/ReactiveCocoa/UIKit/UIView.swift index 63d566245f..fa269ead65 100644 --- a/ReactiveCocoa/UIKit/UIView.swift +++ b/ReactiveCocoa/UIKit/UIView.swift @@ -9,20 +9,20 @@ import ReactiveSwift import UIKit -extension UIView { +extension Reactivity where Reactant: UIView { /// Wraps a view's `alpha` value in a bindable property. - public var rac_alpha: BindingTarget { + public var alpha: BindingTarget { return bindingTarget { $0.alpha = $1 } } /// Wraps a view's `hidden` state in a bindable property. - public var rac_hidden: BindingTarget { + public var isHidden: BindingTarget { return bindingTarget { $0.isHidden = $1 } } /// Wraps a view's `userInteractionEnabled` state in a bindable property. - public var rac_userInteractionEnabled: BindingTarget { + public var isUserInteractionEnabled: BindingTarget { return bindingTarget { $0.isUserInteractionEnabled = $1 } } } diff --git a/ReactiveCocoa/UIKit/UIViewController.swift b/ReactiveCocoa/UIKit/UIViewController.swift index e9eb156ce5..56ed436fe2 100644 --- a/ReactiveCocoa/UIKit/UIViewController.swift +++ b/ReactiveCocoa/UIKit/UIViewController.swift @@ -10,29 +10,29 @@ import ReactiveSwift import Result import UIKit -extension UIViewController { +extension Reactivity where Reactant: UIViewController { /// Returns a `Signal`, that will be triggered /// when `self`'s `viewDidDisappear` is called - public var rac_viewDidDisappear: Signal<(), NoError> { - return trigger(for: #selector(UIViewController.viewDidDisappear(_:))) + public var viewDidDisappear: Signal<(), NoError> { + return reactant.trigger(for: #selector(UIViewController.viewDidDisappear(_:))) } /// Returns a `Signal`, that will be triggered /// when `self`'s `viewWillDisappear` is called - public var rac_viewWillDisappear: Signal<(), NoError> { - return trigger(for: #selector(UIViewController.viewWillDisappear(_:))) + public var viewWillDisappear: Signal<(), NoError> { + return reactant.trigger(for: #selector(UIViewController.viewWillDisappear(_:))) } /// Returns a `Signal`, that will be triggered /// when `self`'s `viewDidAppear` is called - public var rac_viewDidAppear: Signal<(), NoError> { - return trigger(for: #selector(UIViewController.viewDidAppear(_:))) + public var viewDidAppear: Signal<(), NoError> { + return reactant.trigger(for: #selector(UIViewController.viewDidAppear(_:))) } /// Returns a `Signal`, that will be triggered /// when `self`'s `viewWillAppear` is called - public var rac_viewWillAppear: Signal<(), NoError> { - return trigger(for: #selector(UIViewController.viewWillAppear(_:))) + public var viewWillAppear: Signal<(), NoError> { + return reactant.trigger(for: #selector(UIViewController.viewWillAppear(_:))) } public typealias DismissingCompletion = ((Void) -> Void)? @@ -44,11 +44,11 @@ extension UIViewController { /// E.g: /// ``` /// //Dismissed with animation (`true`) and `nil` completion - /// viewController.rac_dismissAnimated <~ aProducer.map { _ in (true, nil) } + /// viewController.dismissAnimated <~ aProducer.map { _ in (true, nil) } /// ``` /// The dismissal observation can be made either with binding (example above) /// or `viewController.dismissViewControllerAnimated(true, completion: nil)` - public var rac_dismissAnimated: BindingTarget { + public var dismissAnimated: BindingTarget { /// TODO: Convert into `Action`? return bindingTarget { $0.dismiss(animated: $1.animated, completion: $1.completion) } } diff --git a/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift b/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift index 01e51f8fea..772ccff442 100644 --- a/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift @@ -25,7 +25,7 @@ class UIActivityIndicatorTests: XCTestCase { _activityIndicatorView = indicatorView let (pipeSignal, observer) = Signal.pipe() - indicatorView.rac_animating <~ SignalProducer(signal: pipeSignal) + indicatorView.rac.isAnimating <~ SignalProducer(signal: pipeSignal) observer.send(value: true) XCTAssertTrue(indicatorView.isAnimating) diff --git a/ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift b/ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift index 2cd30823eb..b8648f4048 100644 --- a/ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift +++ b/ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift @@ -28,7 +28,7 @@ class UIBarButtonItemTests: XCTestCase { let action = Action<(),(),NoError> { SignalProducer(value: ()) } - barButtonItem.rac_action <~ SignalProducer(value: CocoaAction(action, input: ())) + barButtonItem.rac.action <~ SignalProducer(value: CocoaAction(action, input: ())) } func testEnabledProperty() { @@ -36,7 +36,7 @@ class UIBarButtonItemTests: XCTestCase { barButtonItem.isEnabled = true let (pipeSignal, observer) = Signal.pipe() - barButtonItem.rac_enabled <~ SignalProducer(signal: pipeSignal) + barButtonItem.rac.isEnabled <~ SignalProducer(signal: pipeSignal) observer.send(value: false) diff --git a/ReactiveCocoaTests/UIKit/UIButtonTests.swift b/ReactiveCocoaTests/UIKit/UIButtonTests.swift index d56c02e75f..c66d5b6250 100644 --- a/ReactiveCocoaTests/UIKit/UIButtonTests.swift +++ b/ReactiveCocoaTests/UIKit/UIButtonTests.swift @@ -32,7 +32,7 @@ class UIButtonTests: XCTestCase { let button = UIButton(frame: CGRect.zero) _button = button - button.rac_enabled <~ SignalProducer(value: false) + button.rac.isEnabled <~ SignalProducer(value: false) XCTAssert(_button?.isEnabled == false) } @@ -43,14 +43,14 @@ class UIButtonTests: XCTestCase { let action = Action<(),(),NoError> { SignalProducer(value: ()) } - button.rac_pressed <~ SignalProducer(value: CocoaAction(action, input: ())) + button.rac.pressed <~ SignalProducer(value: CocoaAction(action, input: ())) } func testTitlePropertyDoesntCreateRetainCycle() { let button = UIButton(frame: CGRect.zero) _button = button - button.rac_title <~ SignalProducer(value: "button") + button.rac.title <~ SignalProducer(value: "button") XCTAssert(_button?.title(for: UIControlState()) == "button") } @@ -59,7 +59,7 @@ class UIButtonTests: XCTestCase { let secondTitle = "Second title" let button = UIButton(frame: CGRect.zero) let (pipeSignal, observer) = Signal.pipe() - button.rac_title <~ SignalProducer(signal: pipeSignal) + button.rac.title <~ SignalProducer(signal: pipeSignal) button.setTitle("", for: .selected) button.setTitle("", for: .highlighted) @@ -85,7 +85,7 @@ class UIButtonTests: XCTestCase { } pressed <~ SignalProducer(signal: action.values) - button.rac_pressed <~ SignalProducer(value: CocoaAction(action, input: ())) + button.rac.pressed <~ SignalProducer(value: CocoaAction(action, input: ())) XCTAssertFalse(pressed.value) diff --git a/ReactiveCocoaTests/UIKit/UICollectionReusableViewTests.swift b/ReactiveCocoaTests/UIKit/UICollectionReusableViewTests.swift index a1f37eef0e..58d82f1b36 100644 --- a/ReactiveCocoaTests/UIKit/UICollectionReusableViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UICollectionReusableViewTests.swift @@ -17,10 +17,10 @@ class UICollectionReusableViewTests: XCTestCase { let cell = UICollectionViewCell() - cell.rac_hidden <~ + cell.rac.isHidden <~ hiddenProperty .producer - .take(until: cell.rac_prepareForReuse) + .take(until: cell.rac.prepareForReuse) XCTAssertFalse(cell.isHidden) diff --git a/ReactiveCocoaTests/UIKit/UIControlTests.swift b/ReactiveCocoaTests/UIKit/UIControlTests.swift index ac059a9e3d..1b6fc97222 100644 --- a/ReactiveCocoaTests/UIKit/UIControlTests.swift +++ b/ReactiveCocoaTests/UIKit/UIControlTests.swift @@ -25,7 +25,7 @@ class UIControlTests: XCTestCase { let control = UIControl(frame: CGRect.zero) _control = control - control.rac_enabled <~ SignalProducer(value: false) + control.rac.isEnabled <~ SignalProducer(value: false) XCTAssert(_control?.isEnabled == false) } @@ -33,7 +33,7 @@ class UIControlTests: XCTestCase { let control = UIControl(frame: CGRect.zero) _control = control - control.rac_selected <~ SignalProducer(value: true) + control.rac.isSelected <~ SignalProducer(value: true) XCTAssert(_control?.isSelected == true) } @@ -41,7 +41,7 @@ class UIControlTests: XCTestCase { let control = UIControl(frame: CGRect.zero) _control = control - control.rac_highlighted <~ SignalProducer(value: true) + control.rac.isHighlighted <~ SignalProducer(value: true) XCTAssert(_control?.isHighlighted == true) } @@ -50,7 +50,7 @@ class UIControlTests: XCTestCase { control.isEnabled = false let (pipeSignal, observer) = Signal.pipe() - control.rac_enabled <~ SignalProducer(signal: pipeSignal) + control.rac.isEnabled <~ SignalProducer(signal: pipeSignal) observer.send(value: true) XCTAssertTrue(control.isEnabled) @@ -63,7 +63,7 @@ class UIControlTests: XCTestCase { control.isSelected = false let (pipeSignal, observer) = Signal.pipe() - control.rac_selected <~ SignalProducer(signal: pipeSignal) + control.rac.isSelected <~ SignalProducer(signal: pipeSignal) observer.send(value: true) XCTAssertTrue(control.isSelected) @@ -76,7 +76,7 @@ class UIControlTests: XCTestCase { control.isHighlighted = false let (pipeSignal, observer) = Signal.pipe() - control.rac_highlighted <~ SignalProducer(signal: pipeSignal) + control.rac.isHighlighted <~ SignalProducer(signal: pipeSignal) observer.send(value: true) XCTAssertTrue(control.isHighlighted) @@ -91,8 +91,8 @@ class UIControlTests: XCTestCase { let (pipeSignalSelected, observerSelected) = Signal.pipe() let (pipeSignalEnabled, observerEnabled) = Signal.pipe() - control.rac_selected <~ SignalProducer(signal: pipeSignalSelected) - control.rac_enabled <~ SignalProducer(signal: pipeSignalEnabled) + control.rac.isSelected <~ SignalProducer(signal: pipeSignalSelected) + control.rac.isEnabled <~ SignalProducer(signal: pipeSignalEnabled) observerSelected.send(value: true) observerEnabled.send(value: true) diff --git a/ReactiveCocoaTests/UIKit/UIDatePickerTests.swift b/ReactiveCocoaTests/UIKit/UIDatePickerTests.swift index 0df8eac0da..bf5f7c21a6 100644 --- a/ReactiveCocoaTests/UIKit/UIDatePickerTests.swift +++ b/ReactiveCocoaTests/UIKit/UIDatePickerTests.swift @@ -25,7 +25,7 @@ class UIDatePickerTests: XCTestCase { } func testUpdatePickerFromProperty() { - picker.rac_date.value = date + picker.rac.date.value = date XCTAssertEqual(picker.date, date) } @@ -34,7 +34,7 @@ class UIDatePickerTests: XCTestCase { let expectation = self.expectation(description: "Expected rac_date to send an event when picker's date value is changed by a UI event") defer { self.waitForExpectations(timeout: 2, handler: nil) } - picker.rac_date.signal.observeValues { changedDate in + picker.rac.date.signal.observeValues { changedDate in XCTAssertEqual(changedDate, self.date) expectation.fulfill() } diff --git a/ReactiveCocoaTests/UIKit/UIImageViewTests.swift b/ReactiveCocoaTests/UIKit/UIImageViewTests.swift index 7bf0838c22..a7393dbe5b 100644 --- a/ReactiveCocoaTests/UIKit/UIImageViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UIImageViewTests.swift @@ -27,7 +27,7 @@ class UIImageViewTests: XCTestCase { let image = UIImage() - imageView.rac_image <~ SignalProducer(value: image) + imageView.rac.image <~ SignalProducer(value: image) XCTAssert(_imageView?.image == image) } @@ -37,7 +37,7 @@ class UIImageViewTests: XCTestCase { let image = UIImage() - imageView.rac_highlightedImage <~ SignalProducer(value: image) + imageView.rac.highlightedImage <~ SignalProducer(value: image) XCTAssert(_imageView?.highlightedImage == image) } @@ -48,7 +48,7 @@ class UIImageViewTests: XCTestCase { let secondChange = UIImage() let (pipeSignal, observer) = Signal.pipe() - imageView.rac_image <~ SignalProducer(signal: pipeSignal) + imageView.rac.image <~ SignalProducer(signal: pipeSignal) observer.send(value: firstChange) XCTAssertEqual(imageView.image, firstChange) @@ -63,7 +63,7 @@ class UIImageViewTests: XCTestCase { let secondChange = UIImage() let (pipeSignal, observer) = Signal.pipe() - imageView.rac_highlightedImage <~ SignalProducer(signal: pipeSignal) + imageView.rac.highlightedImage <~ SignalProducer(signal: pipeSignal) observer.send(value: firstChange) XCTAssertEqual(imageView.highlightedImage, firstChange) diff --git a/ReactiveCocoaTests/UIKit/UILabelTests.swift b/ReactiveCocoaTests/UIKit/UILabelTests.swift index 817b7d6ba1..82cbe897c7 100644 --- a/ReactiveCocoaTests/UIKit/UILabelTests.swift +++ b/ReactiveCocoaTests/UIKit/UILabelTests.swift @@ -25,7 +25,7 @@ class UILabelTests: XCTestCase { let label = UILabel(frame: CGRect.zero) _label = label - label.rac_text <~ SignalProducer(value: "Test") + label.rac.text <~ SignalProducer(value: "Test") XCTAssert(_label?.text == "Test") } @@ -37,7 +37,7 @@ class UILabelTests: XCTestCase { label.text = "" let (pipeSignal, observer) = Signal.pipe() - label.rac_text <~ SignalProducer(signal: pipeSignal) + label.rac.text <~ SignalProducer(signal: pipeSignal) observer.send(value: firstChange) XCTAssertEqual(label.text, firstChange) @@ -51,7 +51,7 @@ class UILabelTests: XCTestCase { let label = UILabel(frame: CGRect.zero) _label = label - label.rac_attributedText <~ SignalProducer(value: NSAttributedString(string: "Test")) + label.rac.attributedText <~ SignalProducer(value: NSAttributedString(string: "Test")) XCTAssert(_label?.attributedText?.string == "Test") } @@ -63,7 +63,7 @@ class UILabelTests: XCTestCase { label.attributedText = NSAttributedString(string: "") let (pipeSignal, observer) = Signal.pipe() - label.rac_attributedText <~ SignalProducer(signal: pipeSignal) + label.rac.attributedText <~ SignalProducer(signal: pipeSignal) observer.send(value: firstChange) XCTAssertEqual(label.attributedText, firstChange) @@ -79,7 +79,7 @@ class UILabelTests: XCTestCase { let (pipeSignal, observer) = Signal.pipe() label.textColor = UIColor.black - label.rac_textColor <~ SignalProducer(signal: pipeSignal) + label.rac.textColor <~ SignalProducer(signal: pipeSignal) observer.send(value: firstChange) XCTAssertEqual(label.textColor, firstChange) diff --git a/ReactiveCocoaTests/UIKit/UIProgressViewTests.swift b/ReactiveCocoaTests/UIKit/UIProgressViewTests.swift index 83939b510b..943aa050ff 100644 --- a/ReactiveCocoaTests/UIKit/UIProgressViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UIProgressViewTests.swift @@ -24,7 +24,7 @@ class UIProgressViewTests: XCTestCase { let progressView = UIProgressView(frame: CGRect.zero) _progressView = progressView - progressView.rac_progress <~ SignalProducer(value: 0.5) + progressView.rac.progress <~ SignalProducer(value: 0.5) XCTAssert(_progressView?.progress == 0.5) } @@ -36,7 +36,7 @@ class UIProgressViewTests: XCTestCase { progressView.progress = 1.0 let (pipeSignal, observer) = Signal.pipe() - progressView.rac_progress <~ SignalProducer(signal: pipeSignal) + progressView.rac.progress <~ SignalProducer(signal: pipeSignal) observer.send(value: firstChange) XCTAssertEqual(progressView.progress, firstChange) diff --git a/ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift b/ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift index d2fe259e8c..f997c8fc7d 100644 --- a/ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift +++ b/ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift @@ -19,7 +19,7 @@ class UISegmentedControlTests: XCTestCase { XCTAssertEqual(s.numberOfSegments, 3) let (pipeSignal, observer) = Signal.pipe() - s.rac_selectedSegmentIndex <~ SignalProducer(signal: pipeSignal) + s.rac.selectedSegmentIndex <~ SignalProducer(signal: pipeSignal) XCTAssertEqual(s.selectedSegmentIndex, UISegmentedControlNoSegment) observer.send(value: 1) diff --git a/ReactiveCocoaTests/UIKit/UISwitchTests.swift b/ReactiveCocoaTests/UIKit/UISwitchTests.swift index d77dbf4174..3202dbdb77 100644 --- a/ReactiveCocoaTests/UIKit/UISwitchTests.swift +++ b/ReactiveCocoaTests/UIKit/UISwitchTests.swift @@ -18,7 +18,7 @@ class UISwitchTests: XCTestCase { `switch`.isOn = false let (pipeSignal, observer) = Signal.pipe() - `switch`.rac_on <~ SignalProducer(signal: pipeSignal) + `switch`.rac.isOn <~ SignalProducer(signal: pipeSignal) observer.send(value: true) XCTAssertTrue(`switch`.isOn) @@ -27,6 +27,6 @@ class UISwitchTests: XCTestCase { `switch`.isOn = true `switch`.sendActions(for: .valueChanged) - XCTAssertTrue(`switch`.rac_on.value) + XCTAssertTrue(`switch`.rac.isOn.value) } } diff --git a/ReactiveCocoaTests/UIKit/UITableViewCellTests.swift b/ReactiveCocoaTests/UIKit/UITableViewCellTests.swift index 055691a51d..5fa6219236 100644 --- a/ReactiveCocoaTests/UIKit/UITableViewCellTests.swift +++ b/ReactiveCocoaTests/UIKit/UITableViewCellTests.swift @@ -21,10 +21,10 @@ class UITableViewCellTests: XCTestCase { fatalError() } - label.rac_text <~ + label.rac.text <~ titleProperty .producer - .take(until: cell.rac_prepareForReuse) + .take(until: cell.rac.prepareForReuse) XCTAssertEqual(label.text, "John") diff --git a/ReactiveCocoaTests/UIKit/UITableViewHeaderFooterViewTests.swift b/ReactiveCocoaTests/UIKit/UITableViewHeaderFooterViewTests.swift index a81f7626d8..22e75dafee 100644 --- a/ReactiveCocoaTests/UIKit/UITableViewHeaderFooterViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UITableViewHeaderFooterViewTests.swift @@ -17,10 +17,10 @@ class UITableViewHeaderFooterViewTests: XCTestCase { let header = UITableViewHeaderFooterView() - header.rac_hidden <~ + header.rac.isHidden <~ hiddenProperty .producer - .take(until: header.rac_prepareForReuse) + .take(until: header.rac.prepareForReuse) XCTAssertFalse(header.isHidden) diff --git a/ReactiveCocoaTests/UIKit/UITextFieldTests.swift b/ReactiveCocoaTests/UIKit/UITextFieldTests.swift index 3903891ac3..5b7ea66be0 100644 --- a/ReactiveCocoaTests/UIKit/UITextFieldTests.swift +++ b/ReactiveCocoaTests/UIKit/UITextFieldTests.swift @@ -20,7 +20,7 @@ class UITextFieldTests: XCTestCase { let textField = UITextField(frame: CGRect.zero) textField.text = "Test" - textField.rac_text.signal.observeValues { text in + textField.rac.text.signal.observeValues { text in XCTAssertEqual(text, textField.text) expectation.fulfill() } diff --git a/ReactiveCocoaTests/UIKit/UITextViewTests.swift b/ReactiveCocoaTests/UIKit/UITextViewTests.swift index c4e40a508b..7966b9e8c3 100644 --- a/ReactiveCocoaTests/UIKit/UITextViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UITextViewTests.swift @@ -20,7 +20,7 @@ class UITextViewTests: XCTestCase { let textView = UITextView(frame: CGRect.zero) textView.text = "Test" - textView.rac_text.startWithValues { text in + textView.rac.text.startWithValues { text in XCTAssertEqual(text, textView.text) expectation.fulfill() } diff --git a/ReactiveCocoaTests/UIKit/UIViewControllerTests.swift b/ReactiveCocoaTests/UIKit/UIViewControllerTests.swift index abaa551882..d83103e183 100644 --- a/ReactiveCocoaTests/UIKit/UIViewControllerTests.swift +++ b/ReactiveCocoaTests/UIKit/UIViewControllerTests.swift @@ -29,7 +29,7 @@ class UIViewControllerTests: XCTestCase { let viewController = UIViewController() _viewController = viewController - viewController.rac_viewDidDisappear.observeValues { + viewController.rac.viewDidDisappear.observeValues { expectation.fulfill() } @@ -44,7 +44,7 @@ class UIViewControllerTests: XCTestCase { let viewController = UIViewController() _viewController = viewController - viewController.rac_viewWillDisappear.observeValues { + viewController.rac.viewWillDisappear.observeValues { expectation.fulfill() } @@ -59,7 +59,7 @@ class UIViewControllerTests: XCTestCase { let viewController = UIViewController() _viewController = viewController - viewController.rac_viewDidAppear.observeValues { + viewController.rac.viewDidAppear.observeValues { expectation.fulfill() } @@ -74,7 +74,7 @@ class UIViewControllerTests: XCTestCase { let viewController = UIViewController() _viewController = viewController - viewController.rac_viewWillAppear.observeValues { + viewController.rac.viewWillAppear.observeValues { expectation.fulfill() } @@ -96,7 +96,7 @@ class UIViewControllerTests: XCTestCase { expectation.fulfill() } - viewController.rac_dismissAnimated <~ SignalProducer(value: (animated: true, completion: nil)) + viewController.rac.dismissAnimated <~ SignalProducer(value: (animated: true, completion: nil)) } func testDismissViewController_via_cocoaDismiss() { diff --git a/ReactiveCocoaTests/UIKit/UIViewTests.swift b/ReactiveCocoaTests/UIKit/UIViewTests.swift index fc6ce0a49f..8a537d2546 100644 --- a/ReactiveCocoaTests/UIKit/UIViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UIViewTests.swift @@ -25,7 +25,7 @@ class UIViewTests: XCTestCase { let view = UIView(frame: CGRect.zero) _view = view - view.rac_alpha <~ SignalProducer(value: 0.5) + view.rac.alpha <~ SignalProducer(value: 0.5) XCTAssertEqualWithAccuracy(_view!.alpha, 0.5, accuracy: 0.01) } @@ -33,7 +33,7 @@ class UIViewTests: XCTestCase { let view = UIView(frame: CGRect.zero) _view = view - view.rac_hidden <~ SignalProducer(value: true) + view.rac.isHidden <~ SignalProducer(value: true) XCTAssert(_view?.isHidden == true) } @@ -42,7 +42,7 @@ class UIViewTests: XCTestCase { view.isHidden = true let (pipeSignal, observer) = Signal.pipe() - view.rac_hidden <~ SignalProducer(signal: pipeSignal) + view.rac.isHidden <~ SignalProducer(signal: pipeSignal) observer.send(value: true) XCTAssertTrue(view.isHidden) @@ -58,7 +58,7 @@ class UIViewTests: XCTestCase { let secondChange = CGFloat(0.7) let (pipeSignal, observer) = Signal.pipe() - view.rac_alpha <~ SignalProducer(signal: pipeSignal) + view.rac.alpha <~ SignalProducer(signal: pipeSignal) observer.send(value: firstChange) XCTAssertEqualWithAccuracy(view.alpha, firstChange, accuracy: 0.01) @@ -71,7 +71,7 @@ class UIViewTests: XCTestCase { view.isUserInteractionEnabled = true let (pipeSignal, observer) = Signal.pipe() - view.rac_userInteractionEnabled <~ SignalProducer(signal: pipeSignal) + view.rac.isUserInteractionEnabled <~ SignalProducer(signal: pipeSignal) observer.send(value: true) XCTAssertTrue(view.isUserInteractionEnabled) From c7ad0467614dd78324dce9a702b266ce9e361672 Mon Sep 17 00:00:00 2001 From: Anders Date: Sat, 1 Oct 2016 17:50:10 +0200 Subject: [PATCH 0411/1028] Renaming: `Reactivity.makeBindingTarget`. --- ReactiveCocoa/NSObject+BindingTarget.swift | 2 +- ReactiveCocoa/UIKit/UIActivityIndicatorView.swift | 2 +- ReactiveCocoa/UIKit/UIBarItem.swift | 2 +- ReactiveCocoa/UIKit/UIButton.swift | 2 +- ReactiveCocoa/UIKit/UIControl.swift | 6 +++--- ReactiveCocoa/UIKit/UIImageView.swift | 4 ++-- ReactiveCocoa/UIKit/UILabel.swift | 6 +++--- ReactiveCocoa/UIKit/UIProgressView.swift | 2 +- ReactiveCocoa/UIKit/UIView.swift | 6 +++--- ReactiveCocoa/UIKit/UIViewController.swift | 2 +- 10 files changed, 17 insertions(+), 17 deletions(-) diff --git a/ReactiveCocoa/NSObject+BindingTarget.swift b/ReactiveCocoa/NSObject+BindingTarget.swift index 105248702a..29eedccae4 100644 --- a/ReactiveCocoa/NSObject+BindingTarget.swift +++ b/ReactiveCocoa/NSObject+BindingTarget.swift @@ -13,7 +13,7 @@ extension Reactivity where Reactant: NSObject { /// /// - returns: /// A binding target that holds no strong references to `reactant`. - internal func bindingTarget(action: @escaping (Reactant, U) -> Void) -> BindingTarget { + internal func makeBindingTarget(action: @escaping (Reactant, U) -> Void) -> BindingTarget { return BindingTarget(on: .main, lifetime: reactant.rac.lifetime) { [weak reactant] value in if let reactant = reactant { action(reactant, value) diff --git a/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift b/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift index 17dfa5d92c..30667b4a09 100644 --- a/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift +++ b/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift @@ -15,7 +15,7 @@ extension Reactivity where Reactant: UIActivityIndicatorView { /// Setting a new value to the property would call `startAnimating()` or /// `stopAnimating()` depending on the value. public var isAnimating: BindingTarget { - return bindingTarget { _self, isAnimating in + return makeBindingTarget { _self, isAnimating in if isAnimating { _self.startAnimating() } else { diff --git a/ReactiveCocoa/UIKit/UIBarItem.swift b/ReactiveCocoa/UIKit/UIBarItem.swift index ad21dd97a6..e73419c23b 100644 --- a/ReactiveCocoa/UIKit/UIBarItem.swift +++ b/ReactiveCocoa/UIKit/UIBarItem.swift @@ -12,6 +12,6 @@ import UIKit extension Reactivity where Reactant: UIBarItem { /// Wraps a UIBarItem's `enabled` state in a bindable property. public var isEnabled: BindingTarget { - return bindingTarget { $0.isEnabled = $1 } + return makeBindingTarget { $0.isEnabled = $1 } } } diff --git a/ReactiveCocoa/UIKit/UIButton.swift b/ReactiveCocoa/UIKit/UIButton.swift index 711acc48b0..170d9905f7 100644 --- a/ReactiveCocoa/UIKit/UIButton.swift +++ b/ReactiveCocoa/UIKit/UIButton.swift @@ -35,7 +35,7 @@ extension Reactivity where Reactant: UIButton { /// Wraps a button's `title` text in a bindable property. Note that this only applies /// to `UIControlState.Normal`. public var title: BindingTarget { - return bindingTarget { $0.setTitle($1, for: .normal) } + return makeBindingTarget { $0.setTitle($1, for: .normal) } } } diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index c400b9a032..71a86581c1 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -52,17 +52,17 @@ extension Reactivity where Reactant: UIControl { /// Wraps a control's `enabled` state in a bindable property. public var isEnabled: BindingTarget { - return bindingTarget { $0.isEnabled = $1 } + return makeBindingTarget { $0.isEnabled = $1 } } /// Wraps a control's `selected` state in a bindable property. public var isSelected: BindingTarget { - return bindingTarget { $0.isSelected = $1 } + return makeBindingTarget { $0.isSelected = $1 } } /// Wraps a control's `highlighted` state in a bindable property. public var isHighlighted: BindingTarget { - return bindingTarget { $0.isHighlighted = $1 } + return makeBindingTarget { $0.isHighlighted = $1 } } } diff --git a/ReactiveCocoa/UIKit/UIImageView.swift b/ReactiveCocoa/UIKit/UIImageView.swift index ddf813d8b9..7c12a516d9 100644 --- a/ReactiveCocoa/UIKit/UIImageView.swift +++ b/ReactiveCocoa/UIKit/UIImageView.swift @@ -12,11 +12,11 @@ import UIKit extension Reactivity where Reactant: UIImageView { /// Wraps a imageView's `image` value in a bindable property. public var image: BindingTarget { - return bindingTarget { $0.image = $1 } + return makeBindingTarget { $0.image = $1 } } /// Wraps a imageView's `highlightedImage` value in a bindable property. public var highlightedImage: BindingTarget { - return bindingTarget { $0.highlightedImage = $1 } + return makeBindingTarget { $0.highlightedImage = $1 } } } diff --git a/ReactiveCocoa/UIKit/UILabel.swift b/ReactiveCocoa/UIKit/UILabel.swift index 593cd41ae7..834bc55aee 100644 --- a/ReactiveCocoa/UIKit/UILabel.swift +++ b/ReactiveCocoa/UIKit/UILabel.swift @@ -12,16 +12,16 @@ import UIKit extension Reactivity where Reactant: UILabel { /// Wraps a label's `text` value in a bindable property. public var text: BindingTarget { - return bindingTarget { $0.text = $1 } + return makeBindingTarget { $0.text = $1 } } /// Wraps a label's `attributedText` value in a bindable property. public var attributedText: BindingTarget { - return bindingTarget { $0.attributedText = $1 } + return makeBindingTarget { $0.attributedText = $1 } } /// Wraps a label's `textColor` value in a bindable property. public var textColor: BindingTarget { - return bindingTarget { $0.textColor = $1 } + return makeBindingTarget { $0.textColor = $1 } } } diff --git a/ReactiveCocoa/UIKit/UIProgressView.swift b/ReactiveCocoa/UIKit/UIProgressView.swift index c11e7e46da..74ad17cbe5 100644 --- a/ReactiveCocoa/UIKit/UIProgressView.swift +++ b/ReactiveCocoa/UIKit/UIProgressView.swift @@ -12,6 +12,6 @@ import UIKit extension Reactivity where Reactant: UIProgressView { /// Wraps a progressView's `progress` value in a bindable property. public var progress: BindingTarget { - return bindingTarget { $0.progress = $1 } + return makeBindingTarget { $0.progress = $1 } } } diff --git a/ReactiveCocoa/UIKit/UIView.swift b/ReactiveCocoa/UIKit/UIView.swift index fa269ead65..1b67112182 100644 --- a/ReactiveCocoa/UIKit/UIView.swift +++ b/ReactiveCocoa/UIKit/UIView.swift @@ -12,17 +12,17 @@ import UIKit extension Reactivity where Reactant: UIView { /// Wraps a view's `alpha` value in a bindable property. public var alpha: BindingTarget { - return bindingTarget { $0.alpha = $1 } + return makeBindingTarget { $0.alpha = $1 } } /// Wraps a view's `hidden` state in a bindable property. public var isHidden: BindingTarget { - return bindingTarget { $0.isHidden = $1 } + return makeBindingTarget { $0.isHidden = $1 } } /// Wraps a view's `userInteractionEnabled` state in a bindable property. public var isUserInteractionEnabled: BindingTarget { - return bindingTarget { $0.isUserInteractionEnabled = $1 } + return makeBindingTarget { $0.isUserInteractionEnabled = $1 } } } diff --git a/ReactiveCocoa/UIKit/UIViewController.swift b/ReactiveCocoa/UIKit/UIViewController.swift index 56ed436fe2..072246ed52 100644 --- a/ReactiveCocoa/UIKit/UIViewController.swift +++ b/ReactiveCocoa/UIKit/UIViewController.swift @@ -50,7 +50,7 @@ extension Reactivity where Reactant: UIViewController { /// or `viewController.dismissViewControllerAnimated(true, completion: nil)` public var dismissAnimated: BindingTarget { /// TODO: Convert into `Action`? - return bindingTarget { $0.dismiss(animated: $1.animated, completion: $1.completion) } + return makeBindingTarget { $0.dismiss(animated: $1.animated, completion: $1.completion) } } } From 560870139256af74d27e16a21eac984ee1c0b885 Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Wed, 5 Oct 2016 14:38:49 -0400 Subject: [PATCH 0412/1028] Remove AnyObject constraint on DynamicProperty.Value --- ReactiveCocoa/DynamicProperty.swift | 30 +++++++++----------- ReactiveCocoaTests/DynamicPropertySpec.swift | 12 ++++++++ 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/ReactiveCocoa/DynamicProperty.swift b/ReactiveCocoa/DynamicProperty.swift index a4a0020474..ea08748ad6 100644 --- a/ReactiveCocoa/DynamicProperty.swift +++ b/ReactiveCocoa/DynamicProperty.swift @@ -59,6 +59,19 @@ public final class DynamicProperty: MutablePropertyProtocol { return signal }() + /// Initializes a property that will observe and set the given key path of + /// the given object, where `Value` is a reference type that can be + /// represented directly in Objective-C via `AnyObject`. + /// + /// - important: `object` must support weak references! + /// + /// - parameters: + /// - object: An object to be observed. + /// - keyPath: Key path to observe on the object. + public convenience init(object: NSObject?, keyPath: String) { + self.init(object: object, keyPath: keyPath, representable: DirectRepresentation.self) + } + /// Initializes a property that will observe and set the given key path of /// the given object, using the supplied representation. /// @@ -103,23 +116,8 @@ extension DynamicProperty where Value: _ObjectiveCBridgeable { } } -extension DynamicProperty where Value: AnyObject { - /// Initializes a property that will observe and set the given key path of - /// the given object, where `Value` is a reference type that can be - /// represented directly in Objective-C via `AnyObject`. - /// - /// - important: `object` must support weak references! - /// - /// - parameters: - /// - object: An object to be observed. - /// - keyPath: Key path to observe on the object. - public convenience init(object: NSObject?, keyPath: String) { - self.init(object: object, keyPath: keyPath, representable: DirectRepresentation.self) - } -} - /// Represents values in Objective-C directly, via `AnyObject`. -private struct DirectRepresentation: ObjectiveCRepresentable { +private struct DirectRepresentation: ObjectiveCRepresentable { static func extract(from representation: Any) -> Value { return representation as! Value } diff --git a/ReactiveCocoaTests/DynamicPropertySpec.swift b/ReactiveCocoaTests/DynamicPropertySpec.swift index 4d4e9edcde..95851f6611 100644 --- a/ReactiveCocoaTests/DynamicPropertySpec.swift +++ b/ReactiveCocoaTests/DynamicPropertySpec.swift @@ -180,6 +180,12 @@ class DynamicPropertySpec: QuickSpec { expect(object.rac_reference.value) == "foo" } + it("should support un-bridged value types") { + let dynamicProperty = DynamicProperty(object: object, keyPath: "rac_unbridged") + dynamicProperty.value = .changed + expect(object.rac_unbridged as? UnbridgedValue) == UnbridgedValue.changed + } + it("should expose a lifetime that ends upon the deinitialization of its underlying object") { var isEnded = false property!.lifetime.ended.observeCompleted { @@ -238,6 +244,7 @@ class DynamicPropertySpec: QuickSpec { private class ObservableObject: NSObject { dynamic var rac_value: Int = 0 dynamic var rac_reference: UnbridgedObject = UnbridgedObject("") + dynamic var rac_unbridged: Any = UnbridgedValue.starting } private class UnbridgedObject: NSObject { @@ -246,3 +253,8 @@ private class UnbridgedObject: NSObject { self.value = value } } + +private enum UnbridgedValue { + case starting + case changed +} From a766389db912c4677b8e061c12c96816fb12e468 Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Wed, 5 Oct 2016 15:32:57 -0400 Subject: [PATCH 0413/1028] Replace explicit bridging in DynamicProperty with id-as-Any --- ReactiveCocoa/DynamicProperty.swift | 82 +------------------ .../NSObject+KeyValueObserving.swift | 2 +- 2 files changed, 5 insertions(+), 79 deletions(-) diff --git a/ReactiveCocoa/DynamicProperty.swift b/ReactiveCocoa/DynamicProperty.swift index ea08748ad6..0d551dbe24 100644 --- a/ReactiveCocoa/DynamicProperty.swift +++ b/ReactiveCocoa/DynamicProperty.swift @@ -2,14 +2,6 @@ import Foundation import ReactiveSwift import enum Result.NoError -/// Models types that can be represented in Objective-C (i.e., reference -/// types, including generic types when boxed via `AnyObject`). -private protocol ObjectiveCRepresentable { - associatedtype Value - static func extract(from representation: Any) -> Value - static func represent(_ value: Value) -> Any -} - /// Wraps a `dynamic` property, or one defined in Objective-C, using Key-Value /// Coding and Key-Value Observing. /// @@ -20,20 +12,17 @@ public final class DynamicProperty: MutablePropertyProtocol { private weak var object: NSObject? private let keyPath: String - private let extractValue: (_ from: Any) -> Value - private let represent: (Value) -> Any - private var property: MutableProperty? /// The current value of the property, as read and written using Key-Value /// Coding. public var value: Value? { get { - return object?.value(forKeyPath: keyPath).map(extractValue) + return object?.value(forKeyPath: keyPath) as! Value } set(newValue) { - object?.setValue(newValue.map(represent), forKeyPath: keyPath) + object?.setValue(newValue, forKeyPath: keyPath) } } @@ -50,7 +39,7 @@ public final class DynamicProperty: MutablePropertyProtocol { /// Most UI controls are not! public var producer: SignalProducer { return (object.map { $0.values(forKeyPath: keyPath) } ?? .empty) - .map { [extractValue = self.extractValue] in $0.map(extractValue) } + .map { $0 as! Value } } public lazy var signal: Signal = { [unowned self] in @@ -68,75 +57,12 @@ public final class DynamicProperty: MutablePropertyProtocol { /// - parameters: /// - object: An object to be observed. /// - keyPath: Key path to observe on the object. - public convenience init(object: NSObject?, keyPath: String) { - self.init(object: object, keyPath: keyPath, representable: DirectRepresentation.self) - } - - /// Initializes a property that will observe and set the given key path of - /// the given object, using the supplied representation. - /// - /// - important: `object` must support weak references! - /// - /// - parameters: - /// - object: An object to be observed. - /// - keyPath: Key path to observe on the object. - /// - representable: A representation that bridges the values across the - /// language boundary. - fileprivate init( - object: NSObject?, - keyPath: String, - representable: Representatable.Type - ) - where Representatable.Value == Value - { + public init(object: NSObject?, keyPath: String) { self.object = object self.keyPath = keyPath - self.extractValue = Representatable.extract(from:) - self.represent = Representatable.represent - /// A DynamicProperty will stay alive as long as its object is alive. /// This is made possible by strong reference cycles. _ = object?.rac_lifetime.ended.observeCompleted { _ = self } } } - -extension DynamicProperty where Value: _ObjectiveCBridgeable { - /// Initializes a property that will observe and set the given key path of - /// the given object, where `Value` is a value type that is bridgeable - /// to Objective-C. - /// - /// - important: `object` must support weak references! - /// - /// - parameters: - /// - object: An object to be observed. - /// - keyPath: Key path to observe on the object. - public convenience init(object: NSObject?, keyPath: String) { - self.init(object: object, keyPath: keyPath, representable: BridgeableRepresentation.self) - } -} - -/// Represents values in Objective-C directly, via `AnyObject`. -private struct DirectRepresentation: ObjectiveCRepresentable { - static func extract(from representation: Any) -> Value { - return representation as! Value - } - - static func represent(_ value: Value) -> Any { - return value - } -} - -/// Represents values in Objective-C indirectly, via bridging. -private struct BridgeableRepresentation: ObjectiveCRepresentable { - static func extract(from representation: Any) -> Value { - let object = representation as! Value._ObjectiveCType - var result: Value? - Value._forceBridgeFromObjectiveC(object, result: &result) - return result! - } - - static func represent(_ value: Value) -> Any { - return value._bridgeToObjectiveC() - } -} diff --git a/ReactiveCocoa/NSObject+KeyValueObserving.swift b/ReactiveCocoa/NSObject+KeyValueObserving.swift index 8114005973..6de78c216d 100644 --- a/ReactiveCocoa/NSObject+KeyValueObserving.swift +++ b/ReactiveCocoa/NSObject+KeyValueObserving.swift @@ -13,7 +13,7 @@ extension NSObject { /// /// - returns: /// A producer emitting values of the property specified by the key path. - public func values(forKeyPath keyPath: String) -> SignalProducer { + public func values(forKeyPath keyPath: String) -> SignalProducer { return SignalProducer { observer, disposable in disposable += KeyValueObserver.observe( self, From 906f449861a8a4a45852d84dde3a1042c59ff265 Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Wed, 5 Oct 2016 17:10:31 -0400 Subject: [PATCH 0414/1028] Update DynamicProperty docs to remove AnyObject --- ReactiveCocoa/DynamicProperty.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoa/DynamicProperty.swift b/ReactiveCocoa/DynamicProperty.swift index 0d551dbe24..20c85d89e8 100644 --- a/ReactiveCocoa/DynamicProperty.swift +++ b/ReactiveCocoa/DynamicProperty.swift @@ -49,8 +49,8 @@ public final class DynamicProperty: MutablePropertyProtocol { }() /// Initializes a property that will observe and set the given key path of - /// the given object, where `Value` is a reference type that can be - /// represented directly in Objective-C via `AnyObject`. + /// the given object. The generic type `Value` can be any Swift type, and will + /// be bridged to Objective-C via `Any`. /// /// - important: `object` must support weak references! /// From 4191b56b8b5c8b14a955e7c9992b484e2034f01a Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 6 Oct 2016 22:32:18 +0200 Subject: [PATCH 0415/1028] Updated dependency: ReactiveSwift, `reactivity` branch. --- Cartfile | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- ReactiveCocoa.xcodeproj/project.pbxproj | 20 ++++++------- ReactiveCocoa/AppKit/NSTextField.swift | 6 ++-- ReactiveCocoa/DynamicProperty.swift | 6 ++-- ReactiveCocoa/NSObject+BindingTarget.swift | 22 ++++++++------ ReactiveCocoa/NSObject+Intercepting.swift | 6 ++-- .../NSObject+KeyValueObserving.swift | 10 +++---- ReactiveCocoa/NSObject+Lifetime.swift | 12 ++++---- .../NSObject+ReactiveExtensionsProvider.swift | 4 +++ ReactiveCocoa/Reactive.swift | 28 ----------------- ReactiveCocoa/Reusable.swift | 4 +-- .../UIKit/UIActivityIndicatorView.swift | 2 +- ReactiveCocoa/UIKit/UIBarButtonItem.swift | 6 ++-- ReactiveCocoa/UIKit/UIBarItem.swift | 2 +- ReactiveCocoa/UIKit/UIButton.swift | 6 ++-- ReactiveCocoa/UIKit/UIControl.swift | 16 +++++----- ReactiveCocoa/UIKit/UIDatePicker.swift | 2 +- ReactiveCocoa/UIKit/UIImageView.swift | 2 +- ReactiveCocoa/UIKit/UILabel.swift | 2 +- ReactiveCocoa/UIKit/UIProgressView.swift | 2 +- ReactiveCocoa/UIKit/UISegmentedControl.swift | 4 +-- ReactiveCocoa/UIKit/UISwitch.swift | 2 +- ReactiveCocoa/UIKit/UITextField.swift | 8 ++--- ReactiveCocoa/UIKit/UITextView.swift | 6 ++-- ReactiveCocoa/UIKit/UIView.swift | 2 +- ReactiveCocoa/UIKit/UIViewController.swift | 10 +++---- .../KeyValueObservingSpec.swift | 30 ++++++++++++------- .../UIKit/UIActivityIndicatorViewTests.swift | 2 +- .../UIKit/UIBarButtonItemTests.swift | 4 +-- ReactiveCocoaTests/UIKit/UIButtonTests.swift | 10 +++---- .../UIKit/UICollectionReusableViewTests.swift | 4 +-- ReactiveCocoaTests/UIKit/UIControlTests.swift | 16 +++++----- .../UIKit/UIDatePickerTests.swift | 4 +-- .../UIKit/UIImageViewTests.swift | 8 ++--- ReactiveCocoaTests/UIKit/UILabelTests.swift | 10 +++---- .../UIKit/UIProgressViewTests.swift | 4 +-- .../UIKit/UISegmentedControlTests.swift | 2 +- ReactiveCocoaTests/UIKit/UISwitchTests.swift | 4 +-- .../UIKit/UITableViewCellTests.swift | 4 +-- .../UITableViewHeaderFooterViewTests.swift | 4 +-- .../UIKit/UITextFieldTests.swift | 2 +- .../UIKit/UITextViewTests.swift | 2 +- .../UIKit/UIViewControllerTests.swift | 12 ++++---- ReactiveCocoaTests/UIKit/UIViewTests.swift | 10 +++---- 46 files changed, 160 insertions(+), 168 deletions(-) create mode 100644 ReactiveCocoa/NSObject+ReactiveExtensionsProvider.swift delete mode 100644 ReactiveCocoa/Reactive.swift diff --git a/Cartfile b/Cartfile index 328f4857bb..1a7b7f1c06 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" "33f8a6d4a9bd6513dd529d17908331b8e66c4874" +github "ReactiveCocoa/ReactiveSwift" "reactivity" diff --git a/Cartfile.resolved b/Cartfile.resolved index bb5ff4c355..4a61170718 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -2,4 +2,4 @@ github "Quick/Nimble" "v5.0.0" github "Quick/Quick" "v0.10.0" github "antitypical/Result" "3.0.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" -github "ReactiveCocoa/ReactiveSwift" "33f8a6d4a9bd6513dd529d17908331b8e66c4874" +github "ReactiveCocoa/ReactiveSwift" "1f24443560cc767e5706b7b48c0dc5e169479a00" diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index 33f8a6d4a9..1f24443560 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit 33f8a6d4a9bd6513dd529d17908331b8e66c4874 +Subproject commit 1f24443560cc767e5706b7b48c0dc5e169479a00 diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 9ddc196f35..9e1ae5d089 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -21,10 +21,6 @@ 7DFBED211CDB8D8300EE435B /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 7DFBED281CDB8DE300EE435B /* DynamicPropertySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */; }; 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */; }; - 9A090C081D9FF6E400EE97CA /* Reactive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A090C071D9FF6E400EE97CA /* Reactive.swift */; }; - 9A090C091D9FF6E400EE97CA /* Reactive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A090C071D9FF6E400EE97CA /* Reactive.swift */; }; - 9A090C0A1D9FF6E400EE97CA /* Reactive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A090C071D9FF6E400EE97CA /* Reactive.swift */; }; - 9A090C0B1D9FF6E400EE97CA /* Reactive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A090C071D9FF6E400EE97CA /* Reactive.swift */; }; 9A1D05E01D93E99100ACF44C /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* Association.swift */; }; 9A1D05E11D93E99100ACF44C /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* Association.swift */; }; 9A1D05E21D93E99100ACF44C /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* Association.swift */; }; @@ -117,6 +113,10 @@ 9AD0F06B1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; 9AD0F06C1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; 9AD0F06D1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; + 9ADE4A911DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADE4A901DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift */; }; + 9ADE4A921DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADE4A901DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift */; }; + 9ADE4A931DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADE4A901DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift */; }; + 9ADE4A941DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADE4A901DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift */; }; 9AF0EA751D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; 9AF0EA761D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; 9AF0EA771D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; @@ -232,7 +232,6 @@ 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Framework.xcconfig"; sourceTree = ""; }; 57A4D2471BA13F9700F7D4B1 /* tvOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-StaticLibrary.xcconfig"; sourceTree = ""; }; 7DFBED031CDB8C9500EE435B /* ReactiveCocoaTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveCocoaTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 9A090C071D9FF6E400EE97CA /* Reactive.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reactive.swift; sourceTree = ""; }; 9A1D05DF1D93E99100ACF44C /* Association.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Association.swift; sourceTree = ""; }; 9A1D05E41D93E9E200ACF44C /* Reusable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reusable.swift; sourceTree = ""; }; 9A1D05EA1D93E9F100ACF44C /* NSTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSTextField.swift; sourceTree = ""; }; @@ -277,6 +276,7 @@ 9A1D06761D9415FB00ACF44C /* RACObjCRuntimeUtilities.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RACObjCRuntimeUtilities.m; sourceTree = ""; }; 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyValueObservingSpec.swift; sourceTree = ""; }; 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+KeyValueObserving.swift"; sourceTree = ""; }; + 9ADE4A901DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+ReactiveExtensionsProvider.swift"; sourceTree = ""; }; 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+BindingTarget.swift"; sourceTree = ""; }; A97451331B3A935E00F48E55 /* watchOS-Application.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Application.xcconfig"; sourceTree = ""; }; A97451341B3A935E00F48E55 /* watchOS-Base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Base.xcconfig"; sourceTree = ""; }; @@ -529,13 +529,13 @@ 9A1D06761D9415FB00ACF44C /* RACObjCRuntimeUtilities.m */, C7142DBB1CDEA167009F402D /* CocoaAction.swift */, CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */, + 9ADE4A901DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift */, 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */, 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */, 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */, 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */, 9A1D05DF1D93E99100ACF44C /* Association.swift */, 9A1D05E41D93E9E200ACF44C /* Reusable.swift */, - 9A090C071D9FF6E400EE97CA /* Reactive.swift */, 9A1D05E91D93E9F100ACF44C /* AppKit */, 9A1D05EB1D93E9F100ACF44C /* UIKit */, D04725ED19E49ED7006002AA /* Supporting Files */, @@ -942,12 +942,12 @@ C7142DBF1CDEA195009F402D /* CocoaAction.swift in Sources */, 9A1D06211D93EA0100ACF44C /* UIView.swift in Sources */, 9A1D067A1D94160100ACF44C /* RACObjCRuntimeUtilities.m in Sources */, + 9ADE4A941DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, 9A1D061D1D93EA0100ACF44C /* UITableViewCell.swift in Sources */, 9A1D05E31D93E99100ACF44C /* Association.swift in Sources */, 9A1D06161D93EA0100ACF44C /* UIControl.swift in Sources */, 9A1D06141D93EA0100ACF44C /* UIButton.swift in Sources */, 9A1D06221D93EA0100ACF44C /* UIViewController.swift in Sources */, - 9A090C0B1D9FF6E400EE97CA /* Reactive.swift in Sources */, 9A1D065E1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, 9A1D06151D93EA0100ACF44C /* UICollectionReusableView.swift in Sources */, 9AF0EA781D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, @@ -995,10 +995,10 @@ buildActionMask = 2147483647; files = ( 9A1D06791D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */, + 9ADE4A931DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, 9A1D05E71D93E9E200ACF44C /* Reusable.swift in Sources */, 9A1D065D1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, C7142DBE1CDEA194009F402D /* CocoaAction.swift in Sources */, - 9A090C0A1D9FF6E400EE97CA /* Reactive.swift in Sources */, CD0C45E01CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9AF0EA771D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, 9A1D05E21D93E99100ACF44C /* Association.swift in Sources */, @@ -1016,9 +1016,9 @@ 9A1D06771D9415FF00ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, 9A1D05FE1D93E9F900ACF44C /* NSTextField.swift in Sources */, - 9A090C081D9FF6E400EE97CA /* Reactive.swift in Sources */, 9A1D05E01D93E99100ACF44C /* Association.swift in Sources */, 9AF0EA751D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, + 9ADE4A911DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, 9A1D065B1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, C7142DBC1CDEA167009F402D /* CocoaAction.swift in Sources */, 4A0E10FF1D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, @@ -1053,7 +1053,6 @@ 9A1D060F1D93EA0000ACF44C /* UIView.swift in Sources */, 9A1D060B1D93EA0000ACF44C /* UITableViewCell.swift in Sources */, 9A1D06051D93EA0000ACF44C /* UIDatePicker.swift in Sources */, - 9A090C091D9FF6E400EE97CA /* Reactive.swift in Sources */, 9A1D05E11D93E99100ACF44C /* Association.swift in Sources */, 9A1D06041D93EA0000ACF44C /* UIControl.swift in Sources */, 9A1D06021D93EA0000ACF44C /* UIButton.swift in Sources */, @@ -1063,6 +1062,7 @@ 9A1D06031D93EA0000ACF44C /* UICollectionReusableView.swift in Sources */, 9A1D06071D93EA0000ACF44C /* UILabel.swift in Sources */, 4A0E11001D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, + 9ADE4A921DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, 9AD0F06B1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, 9A1D06081D93EA0000ACF44C /* UIProgressView.swift in Sources */, 9A1D060C1D93EA0000ACF44C /* UITableViewHeaderFooterView.swift in Sources */, diff --git a/ReactiveCocoa/AppKit/NSTextField.swift b/ReactiveCocoa/AppKit/NSTextField.swift index fde58f5280..a201034cff 100644 --- a/ReactiveCocoa/AppKit/NSTextField.swift +++ b/ReactiveCocoa/AppKit/NSTextField.swift @@ -10,11 +10,11 @@ import ReactiveSwift import AppKit import enum Result.NoError -extension Reactivity where Reactant: NSTextField { +extension Reactive where Base: NSTextField { /// Sends the field's string value whenever it changes. public var textSignal: SignalProducer { - return NotificationCenter.default - .rac_notifications(forName: .NSControlTextDidChange, object: reactant) + return NotificationCenter.default.reactive + .notifications(forName: .NSControlTextDidChange, object: base) .map { notification in (notification.object as! NSTextField).stringValue } diff --git a/ReactiveCocoa/DynamicProperty.swift b/ReactiveCocoa/DynamicProperty.swift index fa04cc9e07..0e0c56911d 100644 --- a/ReactiveCocoa/DynamicProperty.swift +++ b/ReactiveCocoa/DynamicProperty.swift @@ -39,7 +39,7 @@ public final class DynamicProperty: MutablePropertyProtocol { /// The lifetime of the property. public var lifetime: Lifetime { - return object?.rac.lifetime ?? .empty + return object?.reactive.lifetime ?? .empty } /// A producer that will create a Key-Value Observer for the given object, @@ -49,7 +49,7 @@ public final class DynamicProperty: MutablePropertyProtocol { /// - important: This only works if the object given to init() is KVO-compliant. /// Most UI controls are not! public var producer: SignalProducer { - return (object.map { $0.values(forKeyPath: keyPath) } ?? .empty) + return (object.map { $0.reactive.values(forKeyPath: keyPath) } ?? .empty) .map { [extractValue = self.extractValue] in $0.map(extractValue) } } @@ -84,7 +84,7 @@ public final class DynamicProperty: MutablePropertyProtocol { /// A DynamicProperty will stay alive as long as its object is alive. /// This is made possible by strong reference cycles. - _ = object?.rac.lifetime.ended.observeCompleted { _ = self } + _ = object?.reactive.lifetime.ended.observeCompleted { _ = self } } } diff --git a/ReactiveCocoa/NSObject+BindingTarget.swift b/ReactiveCocoa/NSObject+BindingTarget.swift index 29eedccae4..b1ac07a7a3 100644 --- a/ReactiveCocoa/NSObject+BindingTarget.swift +++ b/ReactiveCocoa/NSObject+BindingTarget.swift @@ -1,10 +1,10 @@ import Foundation import ReactiveSwift -extension Reactivity where Reactant: NSObject { - /// Creates a binding target which uses the lifetime of `reactant`, and weakly - /// references `reactant` so that the supplied `action` is triggered only if - /// `reactant` has not deinitialized. +extension Reactive where Base: NSObject { + /// Creates a binding target which uses the lifetime of `base`, and weakly + /// references `base` so that the supplied `action` is triggered only if + /// `base` has not deinitialized. /// /// - important: The binding target is bound to the main queue. /// @@ -12,12 +12,16 @@ extension Reactivity where Reactant: NSObject { /// - action: The action to consume values from the bindings. /// /// - returns: - /// A binding target that holds no strong references to `reactant`. - internal func makeBindingTarget(action: @escaping (Reactant, U) -> Void) -> BindingTarget { - return BindingTarget(on: .main, lifetime: reactant.rac.lifetime) { [weak reactant] value in - if let reactant = reactant { - action(reactant, value) + /// A binding target that holds no strong references to `base`. + internal func makeBindingTarget(action: @escaping (Base, U) -> Void) -> BindingTarget { + let scheduler = associatedObject(base, key: &schedulerKey, initial: { _ in UIScheduler() }) + + return BindingTarget(on: scheduler, lifetime: lifetime) { [weak base] value in + if let base = base { + action(base, value) } } } } + +private var schedulerKey = 0 diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index be92181c22..175160dcd2 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -3,7 +3,7 @@ import ReactiveSwift import enum Result.NoError import ReactiveCocoaPrivate -extension NSObject { +extension Reactive where Base: NSObject { public func trigger(for selector: Selector) -> Signal<(), NoError> { return signal(for: selector) { observer in return { _ in observer.send(value: ()) } @@ -31,10 +31,10 @@ extension NSObject { let (signal, observer) = Signal.pipe() let action = action(observer) - let isSuccessful = RACRegisterBlockForSelector(self, selector, nil, action) + let isSuccessful = RACRegisterBlockForSelector(base, selector, nil, action) assert(isSuccessful) - rac.lifetime.ended.observeCompleted(observer.sendCompleted) + lifetime.ended.observeCompleted(observer.sendCompleted) map.setObject(signal, forKey: selectorName) return signal diff --git a/ReactiveCocoa/NSObject+KeyValueObserving.swift b/ReactiveCocoa/NSObject+KeyValueObserving.swift index 3b5d3065fa..106025edf2 100644 --- a/ReactiveCocoa/NSObject+KeyValueObserving.swift +++ b/ReactiveCocoa/NSObject+KeyValueObserving.swift @@ -2,7 +2,7 @@ import Foundation import ReactiveSwift import enum Result.NoError -extension NSObject { +extension Reactive where Base: NSObject { /// Create a producer which sends the current value and all the subsequent /// changes of the property specified by the key path. /// @@ -16,12 +16,12 @@ extension NSObject { public func values(forKeyPath keyPath: String) -> SignalProducer { return SignalProducer { observer, disposable in disposable += KeyValueObserver.observe( - self, + self.base, keyPath: keyPath, options: [.initial, .new], action: observer.send ) - disposable += self.rac.lifetime.ended.observeCompleted(observer.sendCompleted) + disposable += self.lifetime.ended.observeCompleted(observer.sendCompleted) } } } @@ -136,7 +136,7 @@ extension KeyValueObserver { headSerialDisposable.innerDisposable = headDisposable if shouldObserveDeinit { - let disposable = value.rac.lifetime.ended.observeCompleted { + let disposable = value.reactive.lifetime.ended.observeCompleted { action(nil) } headDisposable += disposable @@ -162,7 +162,7 @@ extension KeyValueObserver { } if shouldObserveDeinit { - let disposable = value.rac.lifetime.ended.observeCompleted { + let disposable = value.reactive.lifetime.ended.observeCompleted { action(nil) } headSerialDisposable.innerDisposable = disposable diff --git a/ReactiveCocoa/NSObject+Lifetime.swift b/ReactiveCocoa/NSObject+Lifetime.swift index 26bd335984..c5d4e52342 100644 --- a/ReactiveCocoa/NSObject+Lifetime.swift +++ b/ReactiveCocoa/NSObject+Lifetime.swift @@ -4,21 +4,21 @@ import ReactiveSwift private var lifetimeKey: UInt8 = 0 private var lifetimeTokenKey: UInt8 = 0 -extension Reactivity where Reactant: NSObject { +extension Reactive where Base: NSObject { /// Returns a lifetime that ends when the receiver is deallocated. @nonobjc public var lifetime: Lifetime { - objc_sync_enter(reactant) - defer { objc_sync_exit(reactant) } + objc_sync_enter(base) + defer { objc_sync_exit(base) } - if let lifetime = objc_getAssociatedObject(reactant, &lifetimeKey) as! Lifetime? { + if let lifetime = objc_getAssociatedObject(base, &lifetimeKey) as! Lifetime? { return lifetime } let token = Lifetime.Token() let lifetime = Lifetime(token) - objc_setAssociatedObject(reactant, &lifetimeTokenKey, token, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - objc_setAssociatedObject(reactant, &lifetimeKey, lifetime, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + objc_setAssociatedObject(base, &lifetimeTokenKey, token, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + objc_setAssociatedObject(base, &lifetimeKey, lifetime, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) return lifetime } diff --git a/ReactiveCocoa/NSObject+ReactiveExtensionsProvider.swift b/ReactiveCocoa/NSObject+ReactiveExtensionsProvider.swift new file mode 100644 index 0000000000..62b568f79d --- /dev/null +++ b/ReactiveCocoa/NSObject+ReactiveExtensionsProvider.swift @@ -0,0 +1,4 @@ +import Foundation +import ReactiveSwift + +extension NSObject: ReactiveExtensionsProvider {} diff --git a/ReactiveCocoa/Reactive.swift b/ReactiveCocoa/Reactive.swift deleted file mode 100644 index 822083a4d3..0000000000 --- a/ReactiveCocoa/Reactive.swift +++ /dev/null @@ -1,28 +0,0 @@ -/// Describes a class which has been extended for reactivity. -/// -/// - note: `ExtendedForReactivity` is only intended for extensions to types -/// that are not owned by the module. Non-conforming types may carry -/// first-party reactive elements. -public protocol ExtendedForReactivity: class {} - -extension ExtendedForReactivity { - /// A proxy which exposes the reactivity of `self`. - public var rac: Reactivity { - return Reactivity(self) - } -} - -extension NSObject: ExtendedForReactivity {} - -// A proxy which exposes the reactivity of the Reactant type. -public struct Reactivity { - public let reactant: Reactant - - // Construct a proxy. - // - // - parameters: - // - reactant: The object to be proxied. - fileprivate init(_ reactant: Reactant) { - self.reactant = reactant - } -} diff --git a/ReactiveCocoa/Reusable.swift b/ReactiveCocoa/Reusable.swift index 9485fc6937..08bb807b4c 100644 --- a/ReactiveCocoa/Reusable.swift +++ b/ReactiveCocoa/Reusable.swift @@ -16,7 +16,7 @@ import enum Result.NoError func prepareForReuse() } -extension Reactivity where Reactant: NSObject, Reactant: Reusable { +extension Reactive where Base: NSObject, Base: Reusable { /// A signal which will send a `Next` event whenever `prepareForReuse` is invoked upon /// the receiver. /// @@ -40,6 +40,6 @@ extension Reactivity where Reactant: NSObject, Reactant: Reusable { /// ``` /// public var prepareForReuse: Signal<(), NoError> { - return reactant.trigger(for: #selector(Reactant.prepareForReuse)) + return trigger(for: #selector(Base.prepareForReuse)) } } diff --git a/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift b/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift index 30667b4a09..a314928827 100644 --- a/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift +++ b/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift @@ -9,7 +9,7 @@ import ReactiveSwift import UIKit -extension Reactivity where Reactant: UIActivityIndicatorView { +extension Reactive where Base: UIActivityIndicatorView { /// Wraps an indicator's `isAnimating()` state in a bindable property. /// Setting a new value to the property would call `startAnimating()` or diff --git a/ReactiveCocoa/UIKit/UIBarButtonItem.swift b/ReactiveCocoa/UIKit/UIBarButtonItem.swift index 4705d8fa33..f3ef173a17 100644 --- a/ReactiveCocoa/UIKit/UIBarButtonItem.swift +++ b/ReactiveCocoa/UIKit/UIBarButtonItem.swift @@ -9,13 +9,13 @@ import ReactiveSwift import UIKit -extension Reactivity where Reactant: UIBarButtonItem { +extension Reactive where Base: UIBarButtonItem { /// Exposes a property that binds an action to bar button item. The action is set as /// a target of the button. When property changes occur the previous action is /// overwritten. This also binds the enabled state of the action to the `enabled` /// property on the button. public var action: MutableProperty { - return associatedObject(reactant, key: &actionKey) { host in + return associatedObject(base, key: &actionKey) { host in let initial = CocoaAction.disabled let property = MutableProperty(initial) @@ -25,7 +25,7 @@ extension Reactivity where Reactant: UIBarButtonItem { host?.action = CocoaAction.selector } - host.rac.isEnabled <~ property.flatMap(.latest) { $0.isEnabled } + isEnabled <~ property.flatMap(.latest) { $0.isEnabled } return property } diff --git a/ReactiveCocoa/UIKit/UIBarItem.swift b/ReactiveCocoa/UIKit/UIBarItem.swift index e73419c23b..1442887a34 100644 --- a/ReactiveCocoa/UIKit/UIBarItem.swift +++ b/ReactiveCocoa/UIKit/UIBarItem.swift @@ -9,7 +9,7 @@ import ReactiveSwift import UIKit -extension Reactivity where Reactant: UIBarItem { +extension Reactive where Base: UIBarItem { /// Wraps a UIBarItem's `enabled` state in a bindable property. public var isEnabled: BindingTarget { return makeBindingTarget { $0.isEnabled = $1 } diff --git a/ReactiveCocoa/UIKit/UIButton.swift b/ReactiveCocoa/UIKit/UIButton.swift index 170d9905f7..65aa73eecc 100644 --- a/ReactiveCocoa/UIKit/UIButton.swift +++ b/ReactiveCocoa/UIKit/UIButton.swift @@ -9,13 +9,13 @@ import ReactiveSwift import UIKit -extension Reactivity where Reactant: UIButton { +extension Reactive where Base: UIButton { /// Exposes a property that binds an action to button presses. The action is set as /// a target of the button for `TouchUpInside` events. When property changes occur the /// previous action is removed as a target. This also binds the enabled state of the /// action to the `enabled` property on the button. public var pressed: MutableProperty { - return associatedObject(reactant, key: &pressedKey) { host in + return associatedObject(base, key: &pressedKey) { host in let initial = CocoaAction.disabled let property = MutableProperty(initial) @@ -26,7 +26,7 @@ extension Reactivity where Reactant: UIButton { host?.addTarget(next, action: CocoaAction.selector, for: .touchUpInside) } - host.rac.isEnabled <~ property.flatMap(.latest) { $0.isEnabled } + isEnabled <~ property.flatMap(.latest) { $0.isEnabled } return property } diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index 71a86581c1..3b68e43a22 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -22,17 +22,17 @@ private class UnsafeControlReceiver: NSObject { } } -extension Reactivity where Reactant: UIControl { - public func trigger(for events: UIControlEvents) -> Signal { +extension Reactive where Base: UIControl { + public func trigger(for events: UIControlEvents) -> Signal { return Signal { observer in let receiver = UnsafeControlReceiver(observer: observer) - reactant.addTarget(receiver, action: #selector(UnsafeControlReceiver.sendNext), for: events) + base.addTarget(receiver, action: #selector(UnsafeControlReceiver.sendNext), for: events) - let disposable = reactant.rac.lifetime.ended.observeCompleted(observer.sendCompleted) + let disposable = lifetime.ended.observeCompleted(observer.sendCompleted) - return ActionDisposable { [weak reactant] in + return ActionDisposable { [weak base] in disposable?.dispose() - reactant?.removeTarget(receiver, action: #selector(UnsafeControlReceiver.sendNext), for: events) + base?.removeTarget(receiver, action: #selector(UnsafeControlReceiver.sendNext), for: events) } } } @@ -43,8 +43,8 @@ extension Reactivity where Reactant: UIControl { /// This property uses `UIControlEvents.ValueChanged` and `UIControlEvents.EditingChanged` /// events to detect changes and keep the value up-to-date. // - internal func value(getter: @escaping (Reactant) -> T, setter: @escaping (Reactant, T) -> ()) -> MutableProperty { - return associatedProperty(reactant, key: &valueChangedKey, initial: getter, setter: setter) { property in + internal func value(getter: @escaping (Base) -> T, setter: @escaping (Base, T) -> ()) -> MutableProperty { + return associatedProperty(base, key: &valueChangedKey, initial: getter, setter: setter) { property in property <~ self.trigger(for: [.valueChanged, .editingChanged]).map(getter) } } diff --git a/ReactiveCocoa/UIKit/UIDatePicker.swift b/ReactiveCocoa/UIKit/UIDatePicker.swift index 1ef8f47bbb..0a7a3ec20f 100644 --- a/ReactiveCocoa/UIKit/UIDatePicker.swift +++ b/ReactiveCocoa/UIKit/UIDatePicker.swift @@ -9,7 +9,7 @@ import ReactiveSwift import UIKit -extension Reactivity where Reactant: UIDatePicker { +extension Reactive where Base: UIDatePicker { // Wraps a datePicker's `date` value in a bindable property. public var date: MutableProperty { return value(getter: { $0.date }, setter: { $0.date = $1 }) diff --git a/ReactiveCocoa/UIKit/UIImageView.swift b/ReactiveCocoa/UIKit/UIImageView.swift index 7c12a516d9..3c214581a3 100644 --- a/ReactiveCocoa/UIKit/UIImageView.swift +++ b/ReactiveCocoa/UIKit/UIImageView.swift @@ -9,7 +9,7 @@ import ReactiveSwift import UIKit -extension Reactivity where Reactant: UIImageView { +extension Reactive where Base: UIImageView { /// Wraps a imageView's `image` value in a bindable property. public var image: BindingTarget { return makeBindingTarget { $0.image = $1 } diff --git a/ReactiveCocoa/UIKit/UILabel.swift b/ReactiveCocoa/UIKit/UILabel.swift index 834bc55aee..4a6c2f8918 100644 --- a/ReactiveCocoa/UIKit/UILabel.swift +++ b/ReactiveCocoa/UIKit/UILabel.swift @@ -9,7 +9,7 @@ import ReactiveSwift import UIKit -extension Reactivity where Reactant: UILabel { +extension Reactive where Base: UILabel { /// Wraps a label's `text` value in a bindable property. public var text: BindingTarget { return makeBindingTarget { $0.text = $1 } diff --git a/ReactiveCocoa/UIKit/UIProgressView.swift b/ReactiveCocoa/UIKit/UIProgressView.swift index 74ad17cbe5..06dd847132 100644 --- a/ReactiveCocoa/UIKit/UIProgressView.swift +++ b/ReactiveCocoa/UIKit/UIProgressView.swift @@ -9,7 +9,7 @@ import ReactiveSwift import UIKit -extension Reactivity where Reactant: UIProgressView { +extension Reactive where Base: UIProgressView { /// Wraps a progressView's `progress` value in a bindable property. public var progress: BindingTarget { return makeBindingTarget { $0.progress = $1 } diff --git a/ReactiveCocoa/UIKit/UISegmentedControl.swift b/ReactiveCocoa/UIKit/UISegmentedControl.swift index 8691adbb8b..22282ea9a1 100644 --- a/ReactiveCocoa/UIKit/UISegmentedControl.swift +++ b/ReactiveCocoa/UIKit/UISegmentedControl.swift @@ -9,10 +9,10 @@ import ReactiveSwift import UIKit -extension Reactivity where Reactant: UISegmentedControl { +extension Reactive where Base: UISegmentedControl { /// Wraps a segmentedControls `selectedSegmentIndex` state in a bindable property. public var selectedSegmentIndex: MutableProperty { - let property = associatedProperty(reactant, + let property = associatedProperty(base, key: &selectedSegmentIndexKey, initial: { $0.selectedSegmentIndex }, setter: { $0.selectedSegmentIndex = $1 }) diff --git a/ReactiveCocoa/UIKit/UISwitch.swift b/ReactiveCocoa/UIKit/UISwitch.swift index dcf396104f..7d281ac3cc 100644 --- a/ReactiveCocoa/UIKit/UISwitch.swift +++ b/ReactiveCocoa/UIKit/UISwitch.swift @@ -9,7 +9,7 @@ import ReactiveSwift import UIKit -extension Reactivity where Reactant: UISwitch { +extension Reactive where Base: UISwitch { /// Wraps a switch's `on` value in a bindable property. public var isOn: MutableProperty { return value(getter: { $0.isOn }, setter: { $0.isOn = $1 }) diff --git a/ReactiveCocoa/UIKit/UITextField.swift b/ReactiveCocoa/UIKit/UITextField.swift index 5e8e5def50..91e2f641e3 100644 --- a/ReactiveCocoa/UIKit/UITextField.swift +++ b/ReactiveCocoa/UIKit/UITextField.swift @@ -9,7 +9,7 @@ import ReactiveSwift import UIKit -extension Reactivity where Reactant: UITextField { +extension Reactive where Base: UITextField { /// Wraps a textField's `text` value in a bindable property. public var text: MutableProperty { let getter: (UITextField) -> String? = { $0.text } @@ -17,10 +17,10 @@ extension Reactivity where Reactant: UITextField { #if os(iOS) return value(getter: getter, setter: setter) #else - return associatedProperty(reactant, key: &textKey, initial: getter, setter: setter) { property in + return associatedProperty(base, key: &textKey, initial: getter, setter: setter) { property in property <~ - NotificationCenter.default - .rac_notifications(forName: .UITextFieldTextDidChange, object: reactant) + NotificationCenter.default.reactive + .notifications(forName: .UITextFieldTextDidChange, object: base) .map { ($0.object as! UITextField).text } } #endif diff --git a/ReactiveCocoa/UIKit/UITextView.swift b/ReactiveCocoa/UIKit/UITextView.swift index 4edd397991..171c41c928 100644 --- a/ReactiveCocoa/UIKit/UITextView.swift +++ b/ReactiveCocoa/UIKit/UITextView.swift @@ -10,11 +10,11 @@ import ReactiveSwift import UIKit import enum Result.NoError -extension Reactivity where Reactant: UITextView { +extension Reactive where Base: UITextView { /// Sends the textView's string value whenever it changes. public var text: SignalProducer { - return NotificationCenter.default - .rac_notifications(forName: .UITextViewTextDidChange, object: reactant) + return NotificationCenter.default.reactive + .notifications(forName: .UITextViewTextDidChange, object: base) .map { ($0.object as? UITextView)?.text } .skipNil() } diff --git a/ReactiveCocoa/UIKit/UIView.swift b/ReactiveCocoa/UIKit/UIView.swift index 1b67112182..d7a80d5cf0 100644 --- a/ReactiveCocoa/UIKit/UIView.swift +++ b/ReactiveCocoa/UIKit/UIView.swift @@ -9,7 +9,7 @@ import ReactiveSwift import UIKit -extension Reactivity where Reactant: UIView { +extension Reactive where Base: UIView { /// Wraps a view's `alpha` value in a bindable property. public var alpha: BindingTarget { return makeBindingTarget { $0.alpha = $1 } diff --git a/ReactiveCocoa/UIKit/UIViewController.swift b/ReactiveCocoa/UIKit/UIViewController.swift index 072246ed52..15c4411df7 100644 --- a/ReactiveCocoa/UIKit/UIViewController.swift +++ b/ReactiveCocoa/UIKit/UIViewController.swift @@ -10,29 +10,29 @@ import ReactiveSwift import Result import UIKit -extension Reactivity where Reactant: UIViewController { +extension Reactive where Base: UIViewController { /// Returns a `Signal`, that will be triggered /// when `self`'s `viewDidDisappear` is called public var viewDidDisappear: Signal<(), NoError> { - return reactant.trigger(for: #selector(UIViewController.viewDidDisappear(_:))) + return trigger(for: #selector(UIViewController.viewDidDisappear(_:))) } /// Returns a `Signal`, that will be triggered /// when `self`'s `viewWillDisappear` is called public var viewWillDisappear: Signal<(), NoError> { - return reactant.trigger(for: #selector(UIViewController.viewWillDisappear(_:))) + return trigger(for: #selector(UIViewController.viewWillDisappear(_:))) } /// Returns a `Signal`, that will be triggered /// when `self`'s `viewDidAppear` is called public var viewDidAppear: Signal<(), NoError> { - return reactant.trigger(for: #selector(UIViewController.viewDidAppear(_:))) + return trigger(for: #selector(UIViewController.viewDidAppear(_:))) } /// Returns a `Signal`, that will be triggered /// when `self`'s `viewWillAppear` is called public var viewWillAppear: Signal<(), NoError> { - return reactant.trigger(for: #selector(UIViewController.viewWillAppear(_:))) + return trigger(for: #selector(UIViewController.viewWillAppear(_:))) } public typealias DismissingCompletion = ((Void) -> Void)? diff --git a/ReactiveCocoaTests/KeyValueObservingSpec.swift b/ReactiveCocoaTests/KeyValueObservingSpec.swift index 946e730f8e..fbde95cc87 100644 --- a/ReactiveCocoaTests/KeyValueObservingSpec.swift +++ b/ReactiveCocoaTests/KeyValueObservingSpec.swift @@ -11,7 +11,7 @@ class KeyValueObservingSpec: QuickSpec { it("should sends the current value and then the changes for the key path") { let object = ObservableObject() var values: [Int] = [] - object.values(forKeyPath: "rac_value").startWithValues { value in + object.reactive.values(forKeyPath: "rac_value").startWithValues { value in expect(value).notTo(beNil()) values.append(value as! Int) } @@ -28,7 +28,7 @@ class KeyValueObservingSpec: QuickSpec { it("should sends the current value and then the changes for the key path, even if the value actually remains unchanged") { let object = ObservableObject() var values: [Int] = [] - object.values(forKeyPath: "rac_value").startWithValues { value in + object.reactive.values(forKeyPath: "rac_value").startWithValues { value in expect(value).notTo(beNil()) values.append(value as! Int) } @@ -49,7 +49,7 @@ class KeyValueObservingSpec: QuickSpec { // Use a closure so this object has a shorter lifetime. let object = ObservableObject() - object.values(forKeyPath: "rac_value").startWithCompleted { + object.reactive.values(forKeyPath: "rac_value").startWithCompleted { completed = true } @@ -63,7 +63,7 @@ class KeyValueObservingSpec: QuickSpec { var interrupted = false let object = ObservableObject() - let disposable = object.values(forKeyPath: "rac_value") + let disposable = object.reactive.values(forKeyPath: "rac_value") .startWithInterrupted { interrupted = true } expect(interrupted) == false @@ -77,6 +77,7 @@ class KeyValueObservingSpec: QuickSpec { var values: [Int] = [] parentObject + .reactive .values(forKeyPath: "rac_object.rac_value") .map { $0 as! NSNumber } .map { $0.intValue } @@ -107,6 +108,7 @@ class KeyValueObservingSpec: QuickSpec { var values: [Int] = [] parentObject + .reactive .values(forKeyPath: "rac_weakObject.rac_value") .map { $0 as! NSNumber } .map { $0.intValue } @@ -156,7 +158,9 @@ class KeyValueObservingSpec: QuickSpec { it("should handle changes being made on another queue") { var observedValue = 0 - testObject.values(forKeyPath: "rac_value") + testObject + .reactive + .values(forKeyPath: "rac_value") .skip(first: 1) .take(first: 1) .map { $0 as! NSNumber } @@ -176,7 +180,9 @@ class KeyValueObservingSpec: QuickSpec { it("should handle changes being made on another queue using deliverOn") { var observedValue = 0 - testObject.values(forKeyPath: "rac_value") + testObject + .reactive + .values(forKeyPath: "rac_value") .skip(first: 1) .take(first: 1) .observe(on: UIScheduler()) @@ -197,7 +203,9 @@ class KeyValueObservingSpec: QuickSpec { it("async disposal of target") { var observedValue = 0 - testObject.values(forKeyPath: "rac_value") + testObject + .reactive + .values(forKeyPath: "rac_value") .observe(on: UIScheduler()) .map { $0 as! NSNumber } .map { $0.intValue } @@ -241,7 +249,9 @@ class KeyValueObservingSpec: QuickSpec { var atomicCounter = Int64(0) DispatchQueue.concurrentPerform(iterations: numIterations) { index in - testObject.values(forKeyPath: "rac_value") + testObject + .reactive + .values(forKeyPath: "rac_value") .skip(first: 1) .observe(on: deliveringObserver) .map { $0 as! NSNumber } @@ -262,7 +272,7 @@ class KeyValueObservingSpec: QuickSpec { iterationQueue.async { DispatchQueue.concurrentPerform(iterations: numIterations) { index in - let disposable = testObject.values(forKeyPath: "rac_value").startWithCompleted {} + let disposable = testObject.reactive.values(forKeyPath: "rac_value").startWithCompleted {} serialDisposable.innerDisposable = disposable concurrentQueue.async { @@ -285,7 +295,7 @@ class KeyValueObservingSpec: QuickSpec { otherScheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) } - let replayProducer = testObject.values(forKeyPath: "rac_value") + let replayProducer = testObject.reactive.values(forKeyPath: "rac_value") .map { $0 as! NSNumber } .map { $0.intValue } .map { $0 % 2 == 0 } diff --git a/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift b/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift index 772ccff442..774c076e63 100644 --- a/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift @@ -25,7 +25,7 @@ class UIActivityIndicatorTests: XCTestCase { _activityIndicatorView = indicatorView let (pipeSignal, observer) = Signal.pipe() - indicatorView.rac.isAnimating <~ SignalProducer(signal: pipeSignal) + indicatorView.reactive.isAnimating <~ SignalProducer(signal: pipeSignal) observer.send(value: true) XCTAssertTrue(indicatorView.isAnimating) diff --git a/ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift b/ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift index b8648f4048..17c68da12a 100644 --- a/ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift +++ b/ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift @@ -28,7 +28,7 @@ class UIBarButtonItemTests: XCTestCase { let action = Action<(),(),NoError> { SignalProducer(value: ()) } - barButtonItem.rac.action <~ SignalProducer(value: CocoaAction(action, input: ())) + barButtonItem.reactive.action <~ SignalProducer(value: CocoaAction(action, input: ())) } func testEnabledProperty() { @@ -36,7 +36,7 @@ class UIBarButtonItemTests: XCTestCase { barButtonItem.isEnabled = true let (pipeSignal, observer) = Signal.pipe() - barButtonItem.rac.isEnabled <~ SignalProducer(signal: pipeSignal) + barButtonItem.reactive.isEnabled <~ SignalProducer(signal: pipeSignal) observer.send(value: false) diff --git a/ReactiveCocoaTests/UIKit/UIButtonTests.swift b/ReactiveCocoaTests/UIKit/UIButtonTests.swift index c66d5b6250..2be3bd9c03 100644 --- a/ReactiveCocoaTests/UIKit/UIButtonTests.swift +++ b/ReactiveCocoaTests/UIKit/UIButtonTests.swift @@ -32,7 +32,7 @@ class UIButtonTests: XCTestCase { let button = UIButton(frame: CGRect.zero) _button = button - button.rac.isEnabled <~ SignalProducer(value: false) + button.reactive.isEnabled <~ SignalProducer(value: false) XCTAssert(_button?.isEnabled == false) } @@ -43,14 +43,14 @@ class UIButtonTests: XCTestCase { let action = Action<(),(),NoError> { SignalProducer(value: ()) } - button.rac.pressed <~ SignalProducer(value: CocoaAction(action, input: ())) + button.reactive.pressed <~ SignalProducer(value: CocoaAction(action, input: ())) } func testTitlePropertyDoesntCreateRetainCycle() { let button = UIButton(frame: CGRect.zero) _button = button - button.rac.title <~ SignalProducer(value: "button") + button.reactive.title <~ SignalProducer(value: "button") XCTAssert(_button?.title(for: UIControlState()) == "button") } @@ -59,7 +59,7 @@ class UIButtonTests: XCTestCase { let secondTitle = "Second title" let button = UIButton(frame: CGRect.zero) let (pipeSignal, observer) = Signal.pipe() - button.rac.title <~ SignalProducer(signal: pipeSignal) + button.reactive.title <~ SignalProducer(signal: pipeSignal) button.setTitle("", for: .selected) button.setTitle("", for: .highlighted) @@ -85,7 +85,7 @@ class UIButtonTests: XCTestCase { } pressed <~ SignalProducer(signal: action.values) - button.rac.pressed <~ SignalProducer(value: CocoaAction(action, input: ())) + button.reactive.pressed <~ SignalProducer(value: CocoaAction(action, input: ())) XCTAssertFalse(pressed.value) diff --git a/ReactiveCocoaTests/UIKit/UICollectionReusableViewTests.swift b/ReactiveCocoaTests/UIKit/UICollectionReusableViewTests.swift index 58d82f1b36..c50a44cccf 100644 --- a/ReactiveCocoaTests/UIKit/UICollectionReusableViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UICollectionReusableViewTests.swift @@ -17,10 +17,10 @@ class UICollectionReusableViewTests: XCTestCase { let cell = UICollectionViewCell() - cell.rac.isHidden <~ + cell.reactive.isHidden <~ hiddenProperty .producer - .take(until: cell.rac.prepareForReuse) + .take(until: cell.reactive.prepareForReuse) XCTAssertFalse(cell.isHidden) diff --git a/ReactiveCocoaTests/UIKit/UIControlTests.swift b/ReactiveCocoaTests/UIKit/UIControlTests.swift index 1b6fc97222..8e4cc7d2f4 100644 --- a/ReactiveCocoaTests/UIKit/UIControlTests.swift +++ b/ReactiveCocoaTests/UIKit/UIControlTests.swift @@ -25,7 +25,7 @@ class UIControlTests: XCTestCase { let control = UIControl(frame: CGRect.zero) _control = control - control.rac.isEnabled <~ SignalProducer(value: false) + control.reactive.isEnabled <~ SignalProducer(value: false) XCTAssert(_control?.isEnabled == false) } @@ -33,7 +33,7 @@ class UIControlTests: XCTestCase { let control = UIControl(frame: CGRect.zero) _control = control - control.rac.isSelected <~ SignalProducer(value: true) + control.reactive.isSelected <~ SignalProducer(value: true) XCTAssert(_control?.isSelected == true) } @@ -41,7 +41,7 @@ class UIControlTests: XCTestCase { let control = UIControl(frame: CGRect.zero) _control = control - control.rac.isHighlighted <~ SignalProducer(value: true) + control.reactive.isHighlighted <~ SignalProducer(value: true) XCTAssert(_control?.isHighlighted == true) } @@ -50,7 +50,7 @@ class UIControlTests: XCTestCase { control.isEnabled = false let (pipeSignal, observer) = Signal.pipe() - control.rac.isEnabled <~ SignalProducer(signal: pipeSignal) + control.reactive.isEnabled <~ SignalProducer(signal: pipeSignal) observer.send(value: true) XCTAssertTrue(control.isEnabled) @@ -63,7 +63,7 @@ class UIControlTests: XCTestCase { control.isSelected = false let (pipeSignal, observer) = Signal.pipe() - control.rac.isSelected <~ SignalProducer(signal: pipeSignal) + control.reactive.isSelected <~ SignalProducer(signal: pipeSignal) observer.send(value: true) XCTAssertTrue(control.isSelected) @@ -76,7 +76,7 @@ class UIControlTests: XCTestCase { control.isHighlighted = false let (pipeSignal, observer) = Signal.pipe() - control.rac.isHighlighted <~ SignalProducer(signal: pipeSignal) + control.reactive.isHighlighted <~ SignalProducer(signal: pipeSignal) observer.send(value: true) XCTAssertTrue(control.isHighlighted) @@ -91,8 +91,8 @@ class UIControlTests: XCTestCase { let (pipeSignalSelected, observerSelected) = Signal.pipe() let (pipeSignalEnabled, observerEnabled) = Signal.pipe() - control.rac.isSelected <~ SignalProducer(signal: pipeSignalSelected) - control.rac.isEnabled <~ SignalProducer(signal: pipeSignalEnabled) + control.reactive.isSelected <~ SignalProducer(signal: pipeSignalSelected) + control.reactive.isEnabled <~ SignalProducer(signal: pipeSignalEnabled) observerSelected.send(value: true) observerEnabled.send(value: true) diff --git a/ReactiveCocoaTests/UIKit/UIDatePickerTests.swift b/ReactiveCocoaTests/UIKit/UIDatePickerTests.swift index bf5f7c21a6..7b19d8f2fe 100644 --- a/ReactiveCocoaTests/UIKit/UIDatePickerTests.swift +++ b/ReactiveCocoaTests/UIKit/UIDatePickerTests.swift @@ -25,7 +25,7 @@ class UIDatePickerTests: XCTestCase { } func testUpdatePickerFromProperty() { - picker.rac.date.value = date + picker.reactive.date.value = date XCTAssertEqual(picker.date, date) } @@ -34,7 +34,7 @@ class UIDatePickerTests: XCTestCase { let expectation = self.expectation(description: "Expected rac_date to send an event when picker's date value is changed by a UI event") defer { self.waitForExpectations(timeout: 2, handler: nil) } - picker.rac.date.signal.observeValues { changedDate in + picker.reactive.date.signal.observeValues { changedDate in XCTAssertEqual(changedDate, self.date) expectation.fulfill() } diff --git a/ReactiveCocoaTests/UIKit/UIImageViewTests.swift b/ReactiveCocoaTests/UIKit/UIImageViewTests.swift index a7393dbe5b..7f1a5d6b68 100644 --- a/ReactiveCocoaTests/UIKit/UIImageViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UIImageViewTests.swift @@ -27,7 +27,7 @@ class UIImageViewTests: XCTestCase { let image = UIImage() - imageView.rac.image <~ SignalProducer(value: image) + imageView.reactive.image <~ SignalProducer(value: image) XCTAssert(_imageView?.image == image) } @@ -37,7 +37,7 @@ class UIImageViewTests: XCTestCase { let image = UIImage() - imageView.rac.highlightedImage <~ SignalProducer(value: image) + imageView.reactive.highlightedImage <~ SignalProducer(value: image) XCTAssert(_imageView?.highlightedImage == image) } @@ -48,7 +48,7 @@ class UIImageViewTests: XCTestCase { let secondChange = UIImage() let (pipeSignal, observer) = Signal.pipe() - imageView.rac.image <~ SignalProducer(signal: pipeSignal) + imageView.reactive.image <~ SignalProducer(signal: pipeSignal) observer.send(value: firstChange) XCTAssertEqual(imageView.image, firstChange) @@ -63,7 +63,7 @@ class UIImageViewTests: XCTestCase { let secondChange = UIImage() let (pipeSignal, observer) = Signal.pipe() - imageView.rac.highlightedImage <~ SignalProducer(signal: pipeSignal) + imageView.reactive.highlightedImage <~ SignalProducer(signal: pipeSignal) observer.send(value: firstChange) XCTAssertEqual(imageView.highlightedImage, firstChange) diff --git a/ReactiveCocoaTests/UIKit/UILabelTests.swift b/ReactiveCocoaTests/UIKit/UILabelTests.swift index 82cbe897c7..afb630a597 100644 --- a/ReactiveCocoaTests/UIKit/UILabelTests.swift +++ b/ReactiveCocoaTests/UIKit/UILabelTests.swift @@ -25,7 +25,7 @@ class UILabelTests: XCTestCase { let label = UILabel(frame: CGRect.zero) _label = label - label.rac.text <~ SignalProducer(value: "Test") + label.reactive.text <~ SignalProducer(value: "Test") XCTAssert(_label?.text == "Test") } @@ -37,7 +37,7 @@ class UILabelTests: XCTestCase { label.text = "" let (pipeSignal, observer) = Signal.pipe() - label.rac.text <~ SignalProducer(signal: pipeSignal) + label.reactive.text <~ SignalProducer(signal: pipeSignal) observer.send(value: firstChange) XCTAssertEqual(label.text, firstChange) @@ -51,7 +51,7 @@ class UILabelTests: XCTestCase { let label = UILabel(frame: CGRect.zero) _label = label - label.rac.attributedText <~ SignalProducer(value: NSAttributedString(string: "Test")) + label.reactive.attributedText <~ SignalProducer(value: NSAttributedString(string: "Test")) XCTAssert(_label?.attributedText?.string == "Test") } @@ -63,7 +63,7 @@ class UILabelTests: XCTestCase { label.attributedText = NSAttributedString(string: "") let (pipeSignal, observer) = Signal.pipe() - label.rac.attributedText <~ SignalProducer(signal: pipeSignal) + label.reactive.attributedText <~ SignalProducer(signal: pipeSignal) observer.send(value: firstChange) XCTAssertEqual(label.attributedText, firstChange) @@ -79,7 +79,7 @@ class UILabelTests: XCTestCase { let (pipeSignal, observer) = Signal.pipe() label.textColor = UIColor.black - label.rac.textColor <~ SignalProducer(signal: pipeSignal) + label.reactive.textColor <~ SignalProducer(signal: pipeSignal) observer.send(value: firstChange) XCTAssertEqual(label.textColor, firstChange) diff --git a/ReactiveCocoaTests/UIKit/UIProgressViewTests.swift b/ReactiveCocoaTests/UIKit/UIProgressViewTests.swift index 943aa050ff..bfaf6d5414 100644 --- a/ReactiveCocoaTests/UIKit/UIProgressViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UIProgressViewTests.swift @@ -24,7 +24,7 @@ class UIProgressViewTests: XCTestCase { let progressView = UIProgressView(frame: CGRect.zero) _progressView = progressView - progressView.rac.progress <~ SignalProducer(value: 0.5) + progressView.reactive.progress <~ SignalProducer(value: 0.5) XCTAssert(_progressView?.progress == 0.5) } @@ -36,7 +36,7 @@ class UIProgressViewTests: XCTestCase { progressView.progress = 1.0 let (pipeSignal, observer) = Signal.pipe() - progressView.rac.progress <~ SignalProducer(signal: pipeSignal) + progressView.reactive.progress <~ SignalProducer(signal: pipeSignal) observer.send(value: firstChange) XCTAssertEqual(progressView.progress, firstChange) diff --git a/ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift b/ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift index f997c8fc7d..fe75735907 100644 --- a/ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift +++ b/ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift @@ -19,7 +19,7 @@ class UISegmentedControlTests: XCTestCase { XCTAssertEqual(s.numberOfSegments, 3) let (pipeSignal, observer) = Signal.pipe() - s.rac.selectedSegmentIndex <~ SignalProducer(signal: pipeSignal) + s.reactive.selectedSegmentIndex <~ SignalProducer(signal: pipeSignal) XCTAssertEqual(s.selectedSegmentIndex, UISegmentedControlNoSegment) observer.send(value: 1) diff --git a/ReactiveCocoaTests/UIKit/UISwitchTests.swift b/ReactiveCocoaTests/UIKit/UISwitchTests.swift index 3202dbdb77..3559abafb3 100644 --- a/ReactiveCocoaTests/UIKit/UISwitchTests.swift +++ b/ReactiveCocoaTests/UIKit/UISwitchTests.swift @@ -18,7 +18,7 @@ class UISwitchTests: XCTestCase { `switch`.isOn = false let (pipeSignal, observer) = Signal.pipe() - `switch`.rac.isOn <~ SignalProducer(signal: pipeSignal) + `switch`.reactive.isOn <~ SignalProducer(signal: pipeSignal) observer.send(value: true) XCTAssertTrue(`switch`.isOn) @@ -27,6 +27,6 @@ class UISwitchTests: XCTestCase { `switch`.isOn = true `switch`.sendActions(for: .valueChanged) - XCTAssertTrue(`switch`.rac.isOn.value) + XCTAssertTrue(`switch`.reactive.isOn.value) } } diff --git a/ReactiveCocoaTests/UIKit/UITableViewCellTests.swift b/ReactiveCocoaTests/UIKit/UITableViewCellTests.swift index 5fa6219236..1cd530d2ab 100644 --- a/ReactiveCocoaTests/UIKit/UITableViewCellTests.swift +++ b/ReactiveCocoaTests/UIKit/UITableViewCellTests.swift @@ -21,10 +21,10 @@ class UITableViewCellTests: XCTestCase { fatalError() } - label.rac.text <~ + label.reactive.text <~ titleProperty .producer - .take(until: cell.rac.prepareForReuse) + .take(until: cell.reactive.prepareForReuse) XCTAssertEqual(label.text, "John") diff --git a/ReactiveCocoaTests/UIKit/UITableViewHeaderFooterViewTests.swift b/ReactiveCocoaTests/UIKit/UITableViewHeaderFooterViewTests.swift index 22e75dafee..124dc0b760 100644 --- a/ReactiveCocoaTests/UIKit/UITableViewHeaderFooterViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UITableViewHeaderFooterViewTests.swift @@ -17,10 +17,10 @@ class UITableViewHeaderFooterViewTests: XCTestCase { let header = UITableViewHeaderFooterView() - header.rac.isHidden <~ + header.reactive.isHidden <~ hiddenProperty .producer - .take(until: header.rac.prepareForReuse) + .take(until: header.reactive.prepareForReuse) XCTAssertFalse(header.isHidden) diff --git a/ReactiveCocoaTests/UIKit/UITextFieldTests.swift b/ReactiveCocoaTests/UIKit/UITextFieldTests.swift index 5b7ea66be0..c15d77ce5b 100644 --- a/ReactiveCocoaTests/UIKit/UITextFieldTests.swift +++ b/ReactiveCocoaTests/UIKit/UITextFieldTests.swift @@ -20,7 +20,7 @@ class UITextFieldTests: XCTestCase { let textField = UITextField(frame: CGRect.zero) textField.text = "Test" - textField.rac.text.signal.observeValues { text in + textField.reactive.text.signal.observeValues { text in XCTAssertEqual(text, textField.text) expectation.fulfill() } diff --git a/ReactiveCocoaTests/UIKit/UITextViewTests.swift b/ReactiveCocoaTests/UIKit/UITextViewTests.swift index 7966b9e8c3..4239d37925 100644 --- a/ReactiveCocoaTests/UIKit/UITextViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UITextViewTests.swift @@ -20,7 +20,7 @@ class UITextViewTests: XCTestCase { let textView = UITextView(frame: CGRect.zero) textView.text = "Test" - textView.rac.text.startWithValues { text in + textView.reactive.text.startWithValues { text in XCTAssertEqual(text, textView.text) expectation.fulfill() } diff --git a/ReactiveCocoaTests/UIKit/UIViewControllerTests.swift b/ReactiveCocoaTests/UIKit/UIViewControllerTests.swift index d83103e183..b5303d1dfb 100644 --- a/ReactiveCocoaTests/UIKit/UIViewControllerTests.swift +++ b/ReactiveCocoaTests/UIKit/UIViewControllerTests.swift @@ -29,7 +29,7 @@ class UIViewControllerTests: XCTestCase { let viewController = UIViewController() _viewController = viewController - viewController.rac.viewDidDisappear.observeValues { + viewController.reactive.viewDidDisappear.observeValues { expectation.fulfill() } @@ -44,7 +44,7 @@ class UIViewControllerTests: XCTestCase { let viewController = UIViewController() _viewController = viewController - viewController.rac.viewWillDisappear.observeValues { + viewController.reactive.viewWillDisappear.observeValues { expectation.fulfill() } @@ -59,7 +59,7 @@ class UIViewControllerTests: XCTestCase { let viewController = UIViewController() _viewController = viewController - viewController.rac.viewDidAppear.observeValues { + viewController.reactive.viewDidAppear.observeValues { expectation.fulfill() } @@ -74,7 +74,7 @@ class UIViewControllerTests: XCTestCase { let viewController = UIViewController() _viewController = viewController - viewController.rac.viewWillAppear.observeValues { + viewController.reactive.viewWillAppear.observeValues { expectation.fulfill() } @@ -90,13 +90,14 @@ class UIViewControllerTests: XCTestCase { _viewController = viewController viewController + .reactive .trigger(for: #selector(UIViewController.dismiss(animated:completion:))) .signal .observeValues { _ in expectation.fulfill() } - viewController.rac.dismissAnimated <~ SignalProducer(value: (animated: true, completion: nil)) + viewController.reactive.dismissAnimated <~ SignalProducer(value: (animated: true, completion: nil)) } func testDismissViewController_via_cocoaDismiss() { @@ -108,6 +109,7 @@ class UIViewControllerTests: XCTestCase { _viewController = viewController viewController + .reactive .trigger(for: #selector(UIViewController.dismiss(animated:completion:))) .signal .observeValues { _ in diff --git a/ReactiveCocoaTests/UIKit/UIViewTests.swift b/ReactiveCocoaTests/UIKit/UIViewTests.swift index 8a537d2546..b16b23cecb 100644 --- a/ReactiveCocoaTests/UIKit/UIViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UIViewTests.swift @@ -25,7 +25,7 @@ class UIViewTests: XCTestCase { let view = UIView(frame: CGRect.zero) _view = view - view.rac.alpha <~ SignalProducer(value: 0.5) + view.reactive.alpha <~ SignalProducer(value: 0.5) XCTAssertEqualWithAccuracy(_view!.alpha, 0.5, accuracy: 0.01) } @@ -33,7 +33,7 @@ class UIViewTests: XCTestCase { let view = UIView(frame: CGRect.zero) _view = view - view.rac.isHidden <~ SignalProducer(value: true) + view.reactive.isHidden <~ SignalProducer(value: true) XCTAssert(_view?.isHidden == true) } @@ -42,7 +42,7 @@ class UIViewTests: XCTestCase { view.isHidden = true let (pipeSignal, observer) = Signal.pipe() - view.rac.isHidden <~ SignalProducer(signal: pipeSignal) + view.reactive.isHidden <~ SignalProducer(signal: pipeSignal) observer.send(value: true) XCTAssertTrue(view.isHidden) @@ -58,7 +58,7 @@ class UIViewTests: XCTestCase { let secondChange = CGFloat(0.7) let (pipeSignal, observer) = Signal.pipe() - view.rac.alpha <~ SignalProducer(signal: pipeSignal) + view.reactive.alpha <~ SignalProducer(signal: pipeSignal) observer.send(value: firstChange) XCTAssertEqualWithAccuracy(view.alpha, firstChange, accuracy: 0.01) @@ -71,7 +71,7 @@ class UIViewTests: XCTestCase { view.isUserInteractionEnabled = true let (pipeSignal, observer) = Signal.pipe() - view.rac.isUserInteractionEnabled <~ SignalProducer(signal: pipeSignal) + view.reactive.isUserInteractionEnabled <~ SignalProducer(signal: pipeSignal) observer.send(value: true) XCTAssertTrue(view.isUserInteractionEnabled) From 75c57d3f8cc378aacffe11280630103eb23aab91 Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 6 Oct 2016 22:38:16 +0200 Subject: [PATCH 0416/1028] Removed shorthands of `NSObject.reactive.trigger(for:)`. --- ReactiveCocoa.xcodeproj/project.pbxproj | 58 --------- ReactiveCocoa/Reusable.swift | 45 ------- .../UIKit/UICollectionReusableView.swift | 11 -- ReactiveCocoa/UIKit/UITableViewCell.swift | 12 -- .../UIKit/UITableViewHeaderFooterView.swift | 11 -- ReactiveCocoa/UIKit/UIViewController.swift | 57 --------- .../UIKit/UICollectionReusableViewTests.swift | 35 ----- .../UIKit/UITableViewCellTests.swift | 39 ------ .../UITableViewHeaderFooterViewTests.swift | 35 ----- .../UIKit/UIViewControllerTests.swift | 121 ------------------ 10 files changed, 424 deletions(-) delete mode 100644 ReactiveCocoa/Reusable.swift delete mode 100644 ReactiveCocoa/UIKit/UICollectionReusableView.swift delete mode 100644 ReactiveCocoa/UIKit/UITableViewCell.swift delete mode 100644 ReactiveCocoa/UIKit/UITableViewHeaderFooterView.swift delete mode 100644 ReactiveCocoa/UIKit/UIViewController.swift delete mode 100644 ReactiveCocoaTests/UIKit/UICollectionReusableViewTests.swift delete mode 100644 ReactiveCocoaTests/UIKit/UITableViewCellTests.swift delete mode 100644 ReactiveCocoaTests/UIKit/UITableViewHeaderFooterViewTests.swift delete mode 100644 ReactiveCocoaTests/UIKit/UIViewControllerTests.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 9e1ae5d089..fe0d073790 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -25,16 +25,11 @@ 9A1D05E11D93E99100ACF44C /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* Association.swift */; }; 9A1D05E21D93E99100ACF44C /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* Association.swift */; }; 9A1D05E31D93E99100ACF44C /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* Association.swift */; }; - 9A1D05E51D93E9E200ACF44C /* Reusable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05E41D93E9E200ACF44C /* Reusable.swift */; }; - 9A1D05E61D93E9E200ACF44C /* Reusable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05E41D93E9E200ACF44C /* Reusable.swift */; }; - 9A1D05E71D93E9E200ACF44C /* Reusable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05E41D93E9E200ACF44C /* Reusable.swift */; }; - 9A1D05E81D93E9E200ACF44C /* Reusable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05E41D93E9E200ACF44C /* Reusable.swift */; }; 9A1D05FE1D93E9F900ACF44C /* NSTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05EA1D93E9F100ACF44C /* NSTextField.swift */; }; 9A1D05FF1D93EA0000ACF44C /* UIActivityIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05EC1D93E9F100ACF44C /* UIActivityIndicatorView.swift */; }; 9A1D06001D93EA0000ACF44C /* UIBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05ED1D93E9F100ACF44C /* UIBarButtonItem.swift */; }; 9A1D06011D93EA0000ACF44C /* UIBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05EE1D93E9F100ACF44C /* UIBarItem.swift */; }; 9A1D06021D93EA0000ACF44C /* UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05EF1D93E9F100ACF44C /* UIButton.swift */; }; - 9A1D06031D93EA0000ACF44C /* UICollectionReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F01D93E9F100ACF44C /* UICollectionReusableView.swift */; }; 9A1D06041D93EA0000ACF44C /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F11D93E9F100ACF44C /* UIControl.swift */; }; 9A1D06051D93EA0000ACF44C /* UIDatePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F21D93E9F100ACF44C /* UIDatePicker.swift */; }; 9A1D06061D93EA0000ACF44C /* UIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F31D93E9F100ACF44C /* UIImageView.swift */; }; @@ -42,36 +37,27 @@ 9A1D06081D93EA0000ACF44C /* UIProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F51D93E9F100ACF44C /* UIProgressView.swift */; }; 9A1D06091D93EA0000ACF44C /* UISegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F61D93E9F100ACF44C /* UISegmentedControl.swift */; }; 9A1D060A1D93EA0000ACF44C /* UISwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F71D93E9F100ACF44C /* UISwitch.swift */; }; - 9A1D060B1D93EA0000ACF44C /* UITableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F81D93E9F100ACF44C /* UITableViewCell.swift */; }; - 9A1D060C1D93EA0000ACF44C /* UITableViewHeaderFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F91D93E9F100ACF44C /* UITableViewHeaderFooterView.swift */; }; 9A1D060D1D93EA0000ACF44C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05FA1D93E9F100ACF44C /* UITextField.swift */; }; 9A1D060E1D93EA0000ACF44C /* UITextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05FB1D93E9F100ACF44C /* UITextView.swift */; }; 9A1D060F1D93EA0000ACF44C /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05FC1D93E9F100ACF44C /* UIView.swift */; }; - 9A1D06101D93EA0000ACF44C /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05FD1D93E9F100ACF44C /* UIViewController.swift */; }; 9A1D06111D93EA0100ACF44C /* UIActivityIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05EC1D93E9F100ACF44C /* UIActivityIndicatorView.swift */; }; 9A1D06121D93EA0100ACF44C /* UIBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05ED1D93E9F100ACF44C /* UIBarButtonItem.swift */; }; 9A1D06131D93EA0100ACF44C /* UIBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05EE1D93E9F100ACF44C /* UIBarItem.swift */; }; 9A1D06141D93EA0100ACF44C /* UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05EF1D93E9F100ACF44C /* UIButton.swift */; }; - 9A1D06151D93EA0100ACF44C /* UICollectionReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F01D93E9F100ACF44C /* UICollectionReusableView.swift */; }; 9A1D06161D93EA0100ACF44C /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F11D93E9F100ACF44C /* UIControl.swift */; }; 9A1D06181D93EA0100ACF44C /* UIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F31D93E9F100ACF44C /* UIImageView.swift */; }; 9A1D06191D93EA0100ACF44C /* UILabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F41D93E9F100ACF44C /* UILabel.swift */; }; 9A1D061A1D93EA0100ACF44C /* UIProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F51D93E9F100ACF44C /* UIProgressView.swift */; }; 9A1D061B1D93EA0100ACF44C /* UISegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F61D93E9F100ACF44C /* UISegmentedControl.swift */; }; - 9A1D061D1D93EA0100ACF44C /* UITableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F81D93E9F100ACF44C /* UITableViewCell.swift */; }; - 9A1D061E1D93EA0100ACF44C /* UITableViewHeaderFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F91D93E9F100ACF44C /* UITableViewHeaderFooterView.swift */; }; 9A1D061F1D93EA0100ACF44C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05FA1D93E9F100ACF44C /* UITextField.swift */; }; 9A1D06201D93EA0100ACF44C /* UITextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05FB1D93E9F100ACF44C /* UITextView.swift */; }; 9A1D06211D93EA0100ACF44C /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05FC1D93E9F100ACF44C /* UIView.swift */; }; - 9A1D06221D93EA0100ACF44C /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05FD1D93E9F100ACF44C /* UIViewController.swift */; }; 9A1D06361D93EA7E00ACF44C /* UIActivityIndicatorViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06241D93EA7E00ACF44C /* UIActivityIndicatorViewTests.swift */; }; 9A1D06371D93EA7E00ACF44C /* UIActivityIndicatorViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06241D93EA7E00ACF44C /* UIActivityIndicatorViewTests.swift */; }; 9A1D06381D93EA7E00ACF44C /* UIBarButtonItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06251D93EA7E00ACF44C /* UIBarButtonItemTests.swift */; }; 9A1D06391D93EA7E00ACF44C /* UIBarButtonItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06251D93EA7E00ACF44C /* UIBarButtonItemTests.swift */; }; 9A1D063A1D93EA7E00ACF44C /* UIButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06261D93EA7E00ACF44C /* UIButtonTests.swift */; }; 9A1D063B1D93EA7E00ACF44C /* UIButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06261D93EA7E00ACF44C /* UIButtonTests.swift */; }; - 9A1D063C1D93EA7E00ACF44C /* UICollectionReusableViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06271D93EA7E00ACF44C /* UICollectionReusableViewTests.swift */; }; - 9A1D063D1D93EA7E00ACF44C /* UICollectionReusableViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06271D93EA7E00ACF44C /* UICollectionReusableViewTests.swift */; }; 9A1D063E1D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06281D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift */; }; 9A1D063F1D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06281D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift */; }; 9A1D06401D93EA7E00ACF44C /* UIControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06291D93EA7E00ACF44C /* UIControlTests.swift */; }; @@ -86,16 +72,10 @@ 9A1D064A1D93EA7E00ACF44C /* UISegmentedControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062E1D93EA7E00ACF44C /* UISegmentedControlTests.swift */; }; 9A1D064B1D93EA7E00ACF44C /* UISegmentedControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062E1D93EA7E00ACF44C /* UISegmentedControlTests.swift */; }; 9A1D064C1D93EA7E00ACF44C /* UISwitchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062F1D93EA7E00ACF44C /* UISwitchTests.swift */; }; - 9A1D064E1D93EA7E00ACF44C /* UITableViewCellTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06301D93EA7E00ACF44C /* UITableViewCellTests.swift */; }; - 9A1D064F1D93EA7E00ACF44C /* UITableViewCellTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06301D93EA7E00ACF44C /* UITableViewCellTests.swift */; }; - 9A1D06501D93EA7E00ACF44C /* UITableViewHeaderFooterViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06311D93EA7E00ACF44C /* UITableViewHeaderFooterViewTests.swift */; }; - 9A1D06511D93EA7E00ACF44C /* UITableViewHeaderFooterViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06311D93EA7E00ACF44C /* UITableViewHeaderFooterViewTests.swift */; }; 9A1D06521D93EA7E00ACF44C /* UITextFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06321D93EA7E00ACF44C /* UITextFieldTests.swift */; }; 9A1D06531D93EA7E00ACF44C /* UITextFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06321D93EA7E00ACF44C /* UITextFieldTests.swift */; }; 9A1D06541D93EA7E00ACF44C /* UITextViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06331D93EA7E00ACF44C /* UITextViewTests.swift */; }; 9A1D06551D93EA7E00ACF44C /* UITextViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06331D93EA7E00ACF44C /* UITextViewTests.swift */; }; - 9A1D06561D93EA7E00ACF44C /* UIViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06341D93EA7E00ACF44C /* UIViewControllerTests.swift */; }; - 9A1D06571D93EA7E00ACF44C /* UIViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06341D93EA7E00ACF44C /* UIViewControllerTests.swift */; }; 9A1D06581D93EA7E00ACF44C /* UIViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06351D93EA7E00ACF44C /* UIViewTests.swift */; }; 9A1D06591D93EA7E00ACF44C /* UIViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06351D93EA7E00ACF44C /* UIViewTests.swift */; }; 9A1D065B1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; @@ -233,13 +213,11 @@ 57A4D2471BA13F9700F7D4B1 /* tvOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-StaticLibrary.xcconfig"; sourceTree = ""; }; 7DFBED031CDB8C9500EE435B /* ReactiveCocoaTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveCocoaTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 9A1D05DF1D93E99100ACF44C /* Association.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Association.swift; sourceTree = ""; }; - 9A1D05E41D93E9E200ACF44C /* Reusable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reusable.swift; sourceTree = ""; }; 9A1D05EA1D93E9F100ACF44C /* NSTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSTextField.swift; sourceTree = ""; }; 9A1D05EC1D93E9F100ACF44C /* UIActivityIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorView.swift; sourceTree = ""; }; 9A1D05ED1D93E9F100ACF44C /* UIBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIBarButtonItem.swift; sourceTree = ""; }; 9A1D05EE1D93E9F100ACF44C /* UIBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIBarItem.swift; sourceTree = ""; }; 9A1D05EF1D93E9F100ACF44C /* UIButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIButton.swift; sourceTree = ""; }; - 9A1D05F01D93E9F100ACF44C /* UICollectionReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UICollectionReusableView.swift; sourceTree = ""; }; 9A1D05F11D93E9F100ACF44C /* UIControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIControl.swift; sourceTree = ""; }; 9A1D05F21D93E9F100ACF44C /* UIDatePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIDatePicker.swift; sourceTree = ""; }; 9A1D05F31D93E9F100ACF44C /* UIImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIImageView.swift; sourceTree = ""; }; @@ -247,16 +225,12 @@ 9A1D05F51D93E9F100ACF44C /* UIProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIProgressView.swift; sourceTree = ""; }; 9A1D05F61D93E9F100ACF44C /* UISegmentedControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UISegmentedControl.swift; sourceTree = ""; }; 9A1D05F71D93E9F100ACF44C /* UISwitch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UISwitch.swift; sourceTree = ""; }; - 9A1D05F81D93E9F100ACF44C /* UITableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITableViewCell.swift; sourceTree = ""; }; - 9A1D05F91D93E9F100ACF44C /* UITableViewHeaderFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITableViewHeaderFooterView.swift; sourceTree = ""; }; 9A1D05FA1D93E9F100ACF44C /* UITextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITextField.swift; sourceTree = ""; }; 9A1D05FB1D93E9F100ACF44C /* UITextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITextView.swift; sourceTree = ""; }; 9A1D05FC1D93E9F100ACF44C /* UIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = ""; }; - 9A1D05FD1D93E9F100ACF44C /* UIViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = ""; }; 9A1D06241D93EA7E00ACF44C /* UIActivityIndicatorViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorViewTests.swift; sourceTree = ""; }; 9A1D06251D93EA7E00ACF44C /* UIBarButtonItemTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBarButtonItemTests.swift; sourceTree = ""; }; 9A1D06261D93EA7E00ACF44C /* UIButtonTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIButtonTests.swift; sourceTree = ""; }; - 9A1D06271D93EA7E00ACF44C /* UICollectionReusableViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UICollectionReusableViewTests.swift; sourceTree = ""; }; 9A1D06281D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIControl+EnableSendActionsForControlEvents.swift"; sourceTree = ""; }; 9A1D06291D93EA7E00ACF44C /* UIControlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIControlTests.swift; sourceTree = ""; }; 9A1D062A1D93EA7E00ACF44C /* UIDatePickerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIDatePickerTests.swift; sourceTree = ""; }; @@ -265,11 +239,8 @@ 9A1D062D1D93EA7E00ACF44C /* UIProgressViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIProgressViewTests.swift; sourceTree = ""; }; 9A1D062E1D93EA7E00ACF44C /* UISegmentedControlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISegmentedControlTests.swift; sourceTree = ""; }; 9A1D062F1D93EA7E00ACF44C /* UISwitchTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISwitchTests.swift; sourceTree = ""; }; - 9A1D06301D93EA7E00ACF44C /* UITableViewCellTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewCellTests.swift; sourceTree = ""; }; - 9A1D06311D93EA7E00ACF44C /* UITableViewHeaderFooterViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewHeaderFooterViewTests.swift; sourceTree = ""; }; 9A1D06321D93EA7E00ACF44C /* UITextFieldTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextFieldTests.swift; sourceTree = ""; }; 9A1D06331D93EA7E00ACF44C /* UITextViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextViewTests.swift; sourceTree = ""; }; - 9A1D06341D93EA7E00ACF44C /* UIViewControllerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewControllerTests.swift; sourceTree = ""; }; 9A1D06351D93EA7E00ACF44C /* UIViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewTests.swift; sourceTree = ""; }; 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Intercepting.swift"; sourceTree = ""; }; 9A1D06751D9415FB00ACF44C /* RACObjCRuntimeUtilities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RACObjCRuntimeUtilities.h; sourceTree = ""; }; @@ -427,7 +398,6 @@ 9A1D05ED1D93E9F100ACF44C /* UIBarButtonItem.swift */, 9A1D05EE1D93E9F100ACF44C /* UIBarItem.swift */, 9A1D05EF1D93E9F100ACF44C /* UIButton.swift */, - 9A1D05F01D93E9F100ACF44C /* UICollectionReusableView.swift */, 9A1D05F11D93E9F100ACF44C /* UIControl.swift */, 9A1D05F21D93E9F100ACF44C /* UIDatePicker.swift */, 9A1D05F31D93E9F100ACF44C /* UIImageView.swift */, @@ -435,12 +405,9 @@ 9A1D05F51D93E9F100ACF44C /* UIProgressView.swift */, 9A1D05F61D93E9F100ACF44C /* UISegmentedControl.swift */, 9A1D05F71D93E9F100ACF44C /* UISwitch.swift */, - 9A1D05F81D93E9F100ACF44C /* UITableViewCell.swift */, - 9A1D05F91D93E9F100ACF44C /* UITableViewHeaderFooterView.swift */, 9A1D05FA1D93E9F100ACF44C /* UITextField.swift */, 9A1D05FB1D93E9F100ACF44C /* UITextView.swift */, 9A1D05FC1D93E9F100ACF44C /* UIView.swift */, - 9A1D05FD1D93E9F100ACF44C /* UIViewController.swift */, ); path = UIKit; sourceTree = ""; @@ -451,7 +418,6 @@ 9A1D06241D93EA7E00ACF44C /* UIActivityIndicatorViewTests.swift */, 9A1D06251D93EA7E00ACF44C /* UIBarButtonItemTests.swift */, 9A1D06261D93EA7E00ACF44C /* UIButtonTests.swift */, - 9A1D06271D93EA7E00ACF44C /* UICollectionReusableViewTests.swift */, 9A1D06281D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift */, 9A1D06291D93EA7E00ACF44C /* UIControlTests.swift */, 9A1D062A1D93EA7E00ACF44C /* UIDatePickerTests.swift */, @@ -460,11 +426,8 @@ 9A1D062D1D93EA7E00ACF44C /* UIProgressViewTests.swift */, 9A1D062E1D93EA7E00ACF44C /* UISegmentedControlTests.swift */, 9A1D062F1D93EA7E00ACF44C /* UISwitchTests.swift */, - 9A1D06301D93EA7E00ACF44C /* UITableViewCellTests.swift */, - 9A1D06311D93EA7E00ACF44C /* UITableViewHeaderFooterViewTests.swift */, 9A1D06321D93EA7E00ACF44C /* UITextFieldTests.swift */, 9A1D06331D93EA7E00ACF44C /* UITextViewTests.swift */, - 9A1D06341D93EA7E00ACF44C /* UIViewControllerTests.swift */, 9A1D06351D93EA7E00ACF44C /* UIViewTests.swift */, ); path = UIKit; @@ -535,7 +498,6 @@ 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */, 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */, 9A1D05DF1D93E99100ACF44C /* Association.swift */, - 9A1D05E41D93E9E200ACF44C /* Reusable.swift */, 9A1D05E91D93E9F100ACF44C /* AppKit */, 9A1D05EB1D93E9F100ACF44C /* UIKit */, D04725ED19E49ED7006002AA /* Supporting Files */, @@ -934,7 +896,6 @@ buildActionMask = 2147483647; files = ( 9A1D06131D93EA0100ACF44C /* UIBarItem.swift in Sources */, - 9A1D05E81D93E9E200ACF44C /* Reusable.swift in Sources */, 9A1D061F1D93EA0100ACF44C /* UITextField.swift in Sources */, 9A1D06201D93EA0100ACF44C /* UITextView.swift in Sources */, 9A1D06181D93EA0100ACF44C /* UIImageView.swift in Sources */, @@ -943,19 +904,15 @@ 9A1D06211D93EA0100ACF44C /* UIView.swift in Sources */, 9A1D067A1D94160100ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9ADE4A941DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, - 9A1D061D1D93EA0100ACF44C /* UITableViewCell.swift in Sources */, 9A1D05E31D93E99100ACF44C /* Association.swift in Sources */, 9A1D06161D93EA0100ACF44C /* UIControl.swift in Sources */, 9A1D06141D93EA0100ACF44C /* UIButton.swift in Sources */, - 9A1D06221D93EA0100ACF44C /* UIViewController.swift in Sources */, 9A1D065E1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, - 9A1D06151D93EA0100ACF44C /* UICollectionReusableView.swift in Sources */, 9AF0EA781D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, 9A1D06191D93EA0100ACF44C /* UILabel.swift in Sources */, 4A0E11021D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, CD0C45E11CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9A1D061A1D93EA0100ACF44C /* UIProgressView.swift in Sources */, - 9A1D061E1D93EA0100ACF44C /* UITableViewHeaderFooterView.swift in Sources */, 9A1D06121D93EA0100ACF44C /* UIBarButtonItem.swift in Sources */, 9A1D06111D93EA0100ACF44C /* UIActivityIndicatorView.swift in Sources */, 9A1D061B1D93EA0100ACF44C /* UISegmentedControl.swift in Sources */, @@ -970,12 +927,9 @@ 7DFBED281CDB8DE300EE435B /* DynamicPropertySpec.swift in Sources */, CD8401851CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, 9A1E72BC1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, - 9A1D063D1D93EA7E00ACF44C /* UICollectionReusableViewTests.swift in Sources */, 9A1D06451D93EA7E00ACF44C /* UIImageViewTests.swift in Sources */, 9A1D06551D93EA7E00ACF44C /* UITextViewTests.swift in Sources */, 9A1D063B1D93EA7E00ACF44C /* UIButtonTests.swift in Sources */, - 9A1D06571D93EA7E00ACF44C /* UIViewControllerTests.swift in Sources */, - 9A1D064F1D93EA7E00ACF44C /* UITableViewCellTests.swift in Sources */, 9A1D064B1D93EA7E00ACF44C /* UISegmentedControlTests.swift in Sources */, 9A1D063F1D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift in Sources */, 9A1D06371D93EA7E00ACF44C /* UIActivityIndicatorViewTests.swift in Sources */, @@ -984,7 +938,6 @@ 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */, 9A1D06411D93EA7E00ACF44C /* UIControlTests.swift in Sources */, 9A1D06491D93EA7E00ACF44C /* UIProgressViewTests.swift in Sources */, - 9A1D06511D93EA7E00ACF44C /* UITableViewHeaderFooterViewTests.swift in Sources */, BEE020661D637B0000DF261F /* TestError.swift in Sources */, 9A1D06531D93EA7E00ACF44C /* UITextFieldTests.swift in Sources */, ); @@ -996,7 +949,6 @@ files = ( 9A1D06791D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9ADE4A931DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, - 9A1D05E71D93E9E200ACF44C /* Reusable.swift in Sources */, 9A1D065D1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, C7142DBE1CDEA194009F402D /* CocoaAction.swift in Sources */, CD0C45E01CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, @@ -1011,7 +963,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 9A1D05E51D93E9E200ACF44C /* Reusable.swift in Sources */, CD0C45DE1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9A1D06771D9415FF00ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, @@ -1042,7 +993,6 @@ buildActionMask = 2147483647; files = ( 9A1D06011D93EA0000ACF44C /* UIBarItem.swift in Sources */, - 9A1D05E61D93E9E200ACF44C /* Reusable.swift in Sources */, 9A1D060D1D93EA0000ACF44C /* UITextField.swift in Sources */, 9A1D060E1D93EA0000ACF44C /* UITextView.swift in Sources */, 9A1D06061D93EA0000ACF44C /* UIImageView.swift in Sources */, @@ -1051,21 +1001,17 @@ 9A1D06781D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */, C7142DBD1CDEA194009F402D /* CocoaAction.swift in Sources */, 9A1D060F1D93EA0000ACF44C /* UIView.swift in Sources */, - 9A1D060B1D93EA0000ACF44C /* UITableViewCell.swift in Sources */, 9A1D06051D93EA0000ACF44C /* UIDatePicker.swift in Sources */, 9A1D05E11D93E99100ACF44C /* Association.swift in Sources */, 9A1D06041D93EA0000ACF44C /* UIControl.swift in Sources */, 9A1D06021D93EA0000ACF44C /* UIButton.swift in Sources */, 9A1D060A1D93EA0000ACF44C /* UISwitch.swift in Sources */, - 9A1D06101D93EA0000ACF44C /* UIViewController.swift in Sources */, 9A1D065C1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, - 9A1D06031D93EA0000ACF44C /* UICollectionReusableView.swift in Sources */, 9A1D06071D93EA0000ACF44C /* UILabel.swift in Sources */, 4A0E11001D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, 9ADE4A921DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, 9AD0F06B1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, 9A1D06081D93EA0000ACF44C /* UIProgressView.swift in Sources */, - 9A1D060C1D93EA0000ACF44C /* UITableViewHeaderFooterView.swift in Sources */, 9A1D06001D93EA0000ACF44C /* UIBarButtonItem.swift in Sources */, 9A1D05FF1D93EA0000ACF44C /* UIActivityIndicatorView.swift in Sources */, 9A1D06091D93EA0000ACF44C /* UISegmentedControl.swift in Sources */, @@ -1081,12 +1027,9 @@ BFA6B94E1A7604D500C846D1 /* SignalProducerNimbleMatchers.swift in Sources */, B696FB821A7640C00075236D /* TestError.swift in Sources */, 9A1D064C1D93EA7E00ACF44C /* UISwitchTests.swift in Sources */, - 9A1D063C1D93EA7E00ACF44C /* UICollectionReusableViewTests.swift in Sources */, 9A1D06441D93EA7E00ACF44C /* UIImageViewTests.swift in Sources */, 9A1D06541D93EA7E00ACF44C /* UITextViewTests.swift in Sources */, 9A1D063A1D93EA7E00ACF44C /* UIButtonTests.swift in Sources */, - 9A1D06561D93EA7E00ACF44C /* UIViewControllerTests.swift in Sources */, - 9A1D064E1D93EA7E00ACF44C /* UITableViewCellTests.swift in Sources */, 9A1D064A1D93EA7E00ACF44C /* UISegmentedControlTests.swift in Sources */, 9A1D063E1D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift in Sources */, 9A1D06361D93EA7E00ACF44C /* UIActivityIndicatorViewTests.swift in Sources */, @@ -1096,7 +1039,6 @@ 9A1E72BB1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, 9A1D06401D93EA7E00ACF44C /* UIControlTests.swift in Sources */, 9A1D06481D93EA7E00ACF44C /* UIProgressViewTests.swift in Sources */, - 9A1D06501D93EA7E00ACF44C /* UITableViewHeaderFooterViewTests.swift in Sources */, CD8401841CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, 9A1D06521D93EA7E00ACF44C /* UITextFieldTests.swift in Sources */, ); diff --git a/ReactiveCocoa/Reusable.swift b/ReactiveCocoa/Reusable.swift deleted file mode 100644 index 08bb807b4c..0000000000 --- a/ReactiveCocoa/Reusable.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// Reusable.swift -// Rex -// -// Created by David Rodrigues on 20/04/16. -// Copyright © 2016 Neil Pankey. All rights reserved. -// - -import Foundation -import ReactiveSwift -import enum Result.NoError - - -/// A protocol for components that can be reused using `prepareForReuse`. -@objc public protocol Reusable: class { - func prepareForReuse() -} - -extension Reactive where Base: NSObject, Base: Reusable { - /// A signal which will send a `Next` event whenever `prepareForReuse` is invoked upon - /// the receiver. - /// - /// - Note: This signal is particular useful to be used as a trigger for the `takeUntil` - /// operator. - /// - /// #### Examples - /// - /// ``` - /// button - /// .rac_controlEvents(.TouchUpInside) - /// .takeUntil(self.rac_prepareForReuse) - /// .startWithNext { _ in - /// // do other things - /// } - /// - /// label.rac_text <~ - /// titleProperty - /// .producer - /// .takeUntil(self.rac_prepareForReuse) - /// ``` - /// - public var prepareForReuse: Signal<(), NoError> { - return trigger(for: #selector(Base.prepareForReuse)) - } -} diff --git a/ReactiveCocoa/UIKit/UICollectionReusableView.swift b/ReactiveCocoa/UIKit/UICollectionReusableView.swift deleted file mode 100644 index 827b6e537c..0000000000 --- a/ReactiveCocoa/UIKit/UICollectionReusableView.swift +++ /dev/null @@ -1,11 +0,0 @@ -// -// UICollectionReusableView.swift -// Rex -// -// Created by David Rodrigues on 20/04/16. -// Copyright © 2016 Neil Pankey. All rights reserved. -// - -import UIKit - -extension UICollectionReusableView: Reusable {} diff --git a/ReactiveCocoa/UIKit/UITableViewCell.swift b/ReactiveCocoa/UIKit/UITableViewCell.swift deleted file mode 100644 index 87a1177e00..0000000000 --- a/ReactiveCocoa/UIKit/UITableViewCell.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// UITableViewCell.swift -// Rex -// -// Created by David Rodrigues on 19/04/16. -// modified by Andrew Ware on 6/7/16. -// Copyright © 2016 Neil Pankey. All rights reserved. -// - -import UIKit - -extension UITableViewCell: Reusable {} diff --git a/ReactiveCocoa/UIKit/UITableViewHeaderFooterView.swift b/ReactiveCocoa/UIKit/UITableViewHeaderFooterView.swift deleted file mode 100644 index d013da0fd3..0000000000 --- a/ReactiveCocoa/UIKit/UITableViewHeaderFooterView.swift +++ /dev/null @@ -1,11 +0,0 @@ -// -// UITableViewHeaderFooterView.swift -// Rex -// -// Created by David Rodrigues on 19/04/16. -// Copyright © 2016 Neil Pankey. All rights reserved. -// - -import UIKit - -extension UITableViewHeaderFooterView: Reusable {} diff --git a/ReactiveCocoa/UIKit/UIViewController.swift b/ReactiveCocoa/UIKit/UIViewController.swift deleted file mode 100644 index 15c4411df7..0000000000 --- a/ReactiveCocoa/UIKit/UIViewController.swift +++ /dev/null @@ -1,57 +0,0 @@ -// -// UIViewController.swift -// Rex -// -// Created by Rui Peres on 14/04/2016. -// Copyright © 2016 Neil Pankey. All rights reserved. -// - -import ReactiveSwift -import Result -import UIKit - -extension Reactive where Base: UIViewController { - /// Returns a `Signal`, that will be triggered - /// when `self`'s `viewDidDisappear` is called - public var viewDidDisappear: Signal<(), NoError> { - return trigger(for: #selector(UIViewController.viewDidDisappear(_:))) - } - - /// Returns a `Signal`, that will be triggered - /// when `self`'s `viewWillDisappear` is called - public var viewWillDisappear: Signal<(), NoError> { - return trigger(for: #selector(UIViewController.viewWillDisappear(_:))) - } - - /// Returns a `Signal`, that will be triggered - /// when `self`'s `viewDidAppear` is called - public var viewDidAppear: Signal<(), NoError> { - return trigger(for: #selector(UIViewController.viewDidAppear(_:))) - } - - /// Returns a `Signal`, that will be triggered - /// when `self`'s `viewWillAppear` is called - public var viewWillAppear: Signal<(), NoError> { - return trigger(for: #selector(UIViewController.viewWillAppear(_:))) - } - - 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`. - /// E.g: - /// ``` - /// //Dismissed with animation (`true`) and `nil` completion - /// viewController.dismissAnimated <~ aProducer.map { _ in (true, nil) } - /// ``` - /// The dismissal observation can be made either with binding (example above) - /// or `viewController.dismissViewControllerAnimated(true, completion: nil)` - public var dismissAnimated: BindingTarget { - /// TODO: Convert into `Action`? - return makeBindingTarget { $0.dismiss(animated: $1.animated, completion: $1.completion) } - } -} - -private var dismissModally: UInt8 = 0 diff --git a/ReactiveCocoaTests/UIKit/UICollectionReusableViewTests.swift b/ReactiveCocoaTests/UIKit/UICollectionReusableViewTests.swift deleted file mode 100644 index c50a44cccf..0000000000 --- a/ReactiveCocoaTests/UIKit/UICollectionReusableViewTests.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// UICollectionReusableViewTests.swift -// Rex -// -// Created by David Rodrigues on 20/04/16. -// Copyright © 2016 Neil Pankey. All rights reserved. -// - -import XCTest -import ReactiveSwift -import ReactiveCocoa -class UICollectionReusableViewTests: XCTestCase { - - func testPrepareForReuse() { - - let hiddenProperty = MutableProperty(false) - - let cell = UICollectionViewCell() - - cell.reactive.isHidden <~ - hiddenProperty - .producer - .take(until: cell.reactive.prepareForReuse) - - XCTAssertFalse(cell.isHidden) - - hiddenProperty <~ SignalProducer(value: true) - XCTAssertTrue(cell.isHidden) - - cell.prepareForReuse() - - hiddenProperty <~ SignalProducer(value: false) - XCTAssertTrue(cell.isHidden) - } -} diff --git a/ReactiveCocoaTests/UIKit/UITableViewCellTests.swift b/ReactiveCocoaTests/UIKit/UITableViewCellTests.swift deleted file mode 100644 index 1cd530d2ab..0000000000 --- a/ReactiveCocoaTests/UIKit/UITableViewCellTests.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// UITableViewCellTests.swift -// Rex -// -// Created by David Rodrigues on 19/04/16. -// Copyright © 2016 Neil Pankey. All rights reserved. -// - -import XCTest -import ReactiveSwift -import ReactiveCocoa -class UITableViewCellTests: XCTestCase { - - func testPrepareForReuse() { - - let titleProperty = MutableProperty("John") - - let cell = UITableViewCell() - - guard let label = cell.textLabel else { - fatalError() - } - - label.reactive.text <~ - titleProperty - .producer - .take(until: cell.reactive.prepareForReuse) - - XCTAssertEqual(label.text, "John") - - titleProperty <~ SignalProducer(value: "Frank") - XCTAssertEqual(label.text, "Frank") - - cell.prepareForReuse() - - titleProperty <~ SignalProducer(value: "Will") - XCTAssertEqual(label.text, "Frank") - } -} diff --git a/ReactiveCocoaTests/UIKit/UITableViewHeaderFooterViewTests.swift b/ReactiveCocoaTests/UIKit/UITableViewHeaderFooterViewTests.swift deleted file mode 100644 index 124dc0b760..0000000000 --- a/ReactiveCocoaTests/UIKit/UITableViewHeaderFooterViewTests.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// UITableViewHeaderFooterViewTests.swift -// Rex -// -// Created by David Rodrigues on 19/04/16. -// Copyright © 2016 Neil Pankey. All rights reserved. -// - -import XCTest -import ReactiveSwift -import ReactiveCocoa -class UITableViewHeaderFooterViewTests: XCTestCase { - - func testPrepareForReuse() { - - let hiddenProperty = MutableProperty(false) - - let header = UITableViewHeaderFooterView() - - header.reactive.isHidden <~ - hiddenProperty - .producer - .take(until: header.reactive.prepareForReuse) - - XCTAssertFalse(header.isHidden) - - hiddenProperty <~ SignalProducer(value: true) - XCTAssertTrue(header.isHidden) - - header.prepareForReuse() - - hiddenProperty <~ SignalProducer(value: false) - XCTAssertTrue(header.isHidden) - } -} diff --git a/ReactiveCocoaTests/UIKit/UIViewControllerTests.swift b/ReactiveCocoaTests/UIKit/UIViewControllerTests.swift deleted file mode 100644 index b5303d1dfb..0000000000 --- a/ReactiveCocoaTests/UIKit/UIViewControllerTests.swift +++ /dev/null @@ -1,121 +0,0 @@ -// -// UIViewControllerTests.swift -// Rex -// -// Created by Rui Peres on 16/04/2016. -// Copyright © 2016 Neil Pankey. All rights reserved. -// - -import ReactiveSwift -import ReactiveCocoa -import UIKit -import XCTest -import enum Result.NoError - -class UIViewControllerTests: XCTestCase { - - weak var _viewController: UIViewController? - - override func tearDown() { - XCTAssert(_viewController == nil, "Retain cycle detected in UIViewController properties") - super.tearDown() - } - - func testViewDidDisappear() { - - let expectation = self.expectation(description: "Expected rac_viewDidDisappear to be triggered") - defer { self.waitForExpectations(timeout: 2, handler: nil) } - - let viewController = UIViewController() - _viewController = viewController - - viewController.reactive.viewDidDisappear.observeValues { - expectation.fulfill() - } - - viewController.viewDidDisappear(true) - } - - func testViewWillDisappear() { - - let expectation = self.expectation(description: "Expected rac_viewWillDisappear to be triggered") - defer { self.waitForExpectations(timeout: 2, handler: nil) } - - let viewController = UIViewController() - _viewController = viewController - - viewController.reactive.viewWillDisappear.observeValues { - expectation.fulfill() - } - - viewController.viewWillDisappear(true) - } - - func testViewDidAppear() { - - let expectation = self.expectation(description: "Expected rac_viewDidAppear to be triggered") - defer { self.waitForExpectations(timeout: 2, handler: nil) } - - let viewController = UIViewController() - _viewController = viewController - - viewController.reactive.viewDidAppear.observeValues { - expectation.fulfill() - } - - viewController.viewDidAppear(true) - } - - func testViewWillAppear() { - - let expectation = self.expectation(description: "Expected rac_viewWillAppear to be triggered") - defer { self.waitForExpectations(timeout: 2, handler: nil) } - - let viewController = UIViewController() - _viewController = viewController - - viewController.reactive.viewWillAppear.observeValues { - expectation.fulfill() - } - - viewController.viewWillAppear(true) - } - - func testDismissViewController_via_property() { - - let expectation = self.expectation(description: "Expected rac_dismissModally to be triggered") - defer { self.waitForExpectations(timeout: 2, handler: nil) } - - let viewController = UIViewController() - _viewController = viewController - - viewController - .reactive - .trigger(for: #selector(UIViewController.dismiss(animated:completion:))) - .signal - .observeValues { _ in - expectation.fulfill() - } - - viewController.reactive.dismissAnimated <~ SignalProducer(value: (animated: true, completion: nil)) - } - - func testDismissViewController_via_cocoaDismiss() { - - let expectation = self.expectation(description: "Expected rac_dismissModally to be triggered") - defer { self.waitForExpectations(timeout: 2, handler: nil) } - - let viewController = UIViewController() - _viewController = viewController - - viewController - .reactive - .trigger(for: #selector(UIViewController.dismiss(animated:completion:))) - .signal - .observeValues { _ in - expectation.fulfill() - } - - viewController.dismiss(animated: true, completion: nil) - } -} From 21e3ae2e6aabd9ad692ba066886c6b05ca38c9a9 Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 6 Oct 2016 23:02:30 +0200 Subject: [PATCH 0417/1028] New control binding API for `Action`s. --- ReactiveCocoa/CocoaAction.swift | 52 +++--- ReactiveCocoa/UIKit/UIBarButtonItem.swift | 31 ++-- ReactiveCocoa/UIKit/UIButton.swift | 26 ++- ReactiveCocoa/UIKit/UIControl.swift | 56 ++++++- ReactiveCocoaTests/CocoaActionSpec.swift | 21 +-- .../UIKit/UIBarButtonItemTests.swift | 59 ++++--- ReactiveCocoaTests/UIKit/UIButtonTests.swift | 152 +++++++++--------- 7 files changed, 211 insertions(+), 186 deletions(-) diff --git a/ReactiveCocoa/CocoaAction.swift b/ReactiveCocoa/CocoaAction.swift index d5ab40f0ed..13c137f446 100644 --- a/ReactiveCocoa/CocoaAction.swift +++ b/ReactiveCocoa/CocoaAction.swift @@ -4,18 +4,14 @@ import enum Result.NoError /// Wraps an Action for use by a GUI control (such as `NSControl` or /// `UIControl`), with KVO, or with Cocoa Bindings. -public final class CocoaAction: NSObject { - /// Creates an always disabled action that can be used as a default for - /// things like `rac_pressed`. - public static var disabled: CocoaAction { - return CocoaAction(Action(enabledIf: Property(value: false)) { _ in .empty }, - input: nil) +/// +/// - important: The `Action` is weakly referenced. +public final class CocoaAction: NSObject { + /// The selector for message senders. + public static var selector: Selector { + return #selector(CocoaAction.execute(_:)) } - /// The selector that a caller should invoke upon a CocoaAction in order to - /// execute it. - public static let selector: Selector = #selector(CocoaAction.execute(_:)) - /// Whether the action is enabled. /// /// This property will only change on the main thread, and will generate a @@ -33,9 +29,7 @@ public final class CocoaAction: NSObject { /// Initializes a Cocoa action that will invoke the given Action by /// transforming the object given to execute(). /// - /// - note: You must cast the passed in object to the control type you need - /// since there is no way to know where this cocoa action will be - /// added as a target. + /// - important: The `Action` is weakly referenced. /// /// - parameters: /// - action: Executable action. @@ -43,10 +37,13 @@ public final class CocoaAction: NSObject { /// action and returns a value (e.g. /// `(UISwitch) -> (Bool)` to reflect whether a provided /// switch is currently on. - public init(_ action: Action, _ inputTransform: @escaping (AnyObject?) -> Input) { - _execute = { input in - let producer = action.apply(inputTransform(input)) - producer.start() + public init(_ action: Action, _ inputTransform: @escaping (Control) -> Input) { + _execute = { [weak action] input in + if let action = action { + let control = input as! Control + let producer = action.apply(inputTransform(control)) + producer.start() + } } isEnabled = action.isEnabled @@ -54,6 +51,14 @@ public final class CocoaAction: NSObject { super.init() } + + /// Initializes a Cocoa action that will invoke the given Action. + /// + /// - parameters: + /// - action: Executable action. + public convenience init(_ action: Action<(), Output, Error>) { + self.init(action, { _ in }) + } /// Initializes a Cocoa action that will invoke the given Action by always /// providing the given input. @@ -74,16 +79,3 @@ public final class CocoaAction: NSObject { _execute(input) } } - -extension Action { - /// A UI bindable `CocoaAction`. - /// - /// - warning: The default behavior force casts the `AnyObject?` input to - /// match the action's `Input` type. This makes it unsafe for use - /// when the action is parameterized for something like `Void` - /// input. In those cases, explicitly assign a value to this - /// property that transforms the input to suit your needs. - public var unsafeCocoaAction: CocoaAction { - return CocoaAction(self) { $0 as! Input } - } -} diff --git a/ReactiveCocoa/UIKit/UIBarButtonItem.swift b/ReactiveCocoa/UIKit/UIBarButtonItem.swift index f3ef173a17..143f644962 100644 --- a/ReactiveCocoa/UIKit/UIBarButtonItem.swift +++ b/ReactiveCocoa/UIKit/UIBarButtonItem.swift @@ -10,26 +10,23 @@ import ReactiveSwift import UIKit extension Reactive where Base: UIBarButtonItem { - /// Exposes a property that binds an action to bar button item. The action is set as - /// a target of the button. When property changes occur the previous action is - /// overwritten. This also binds the enabled state of the action to the `enabled` - /// property on the button. - public var action: MutableProperty { - return associatedObject(base, key: &actionKey) { host in - let initial = CocoaAction.disabled - let property = MutableProperty(initial) - - property.producer - .startWithValues { [weak host] action in - host?.target = action - host?.action = CocoaAction.selector - } + private var associatedAction: Atomic?> { + return associatedObject(base, + key: &associatedActionKey, + initial: { _ in Atomic(nil) }) + } - isEnabled <~ property.flatMap(.latest) { $0.isEnabled } + public var pressed: CocoaAction? { + get { + return associatedAction.value + } - return property + nonmutating set { + associatedAction.value = newValue + base.target = newValue + base.action = newValue.map { _ in CocoaAction.selector } } } } -private var actionKey: UInt8 = 0 +private var associatedActionKey = 0 diff --git a/ReactiveCocoa/UIKit/UIButton.swift b/ReactiveCocoa/UIKit/UIButton.swift index 65aa73eecc..21804669f8 100644 --- a/ReactiveCocoa/UIKit/UIButton.swift +++ b/ReactiveCocoa/UIKit/UIButton.swift @@ -10,25 +10,17 @@ import ReactiveSwift import UIKit extension Reactive where Base: UIButton { - /// Exposes a property that binds an action to button presses. The action is set as - /// a target of the button for `TouchUpInside` events. When property changes occur the - /// previous action is removed as a target. This also binds the enabled state of the - /// action to the `enabled` property on the button. - public var pressed: MutableProperty { - return associatedObject(base, key: &pressedKey) { host in - let initial = CocoaAction.disabled - let property = MutableProperty(initial) - - property.producer - .combinePrevious(initial) - .startWithValues { [weak host] previous, next in - host?.removeTarget(previous, action: CocoaAction.selector, for: .touchUpInside) - host?.addTarget(next, action: CocoaAction.selector, for: .touchUpInside) + public var pressed: CocoaAction? { + get { + return associatedAction.withValue { info in + return info.flatMap { info in + return info.controlEvents == .touchUpInside ? info.action : nil } + } + } - isEnabled <~ property.flatMap(.latest) { $0.isEnabled } - - return property + nonmutating set { + setAction(newValue, for: .touchUpInside) } } diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index 3b68e43a22..dfa051a186 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -10,29 +10,70 @@ import ReactiveSwift import UIKit import enum Result.NoError -private class UnsafeControlReceiver: NSObject { - private let observer: Observer +private class UnsafeControlReceiver: NSObject { + private let observer: Observer<(), NoError> - fileprivate init(observer: Observer) { + fileprivate init(observer: Observer<(), NoError>) { self.observer = observer } @objc fileprivate func sendNext(_ receiver: Any?) { - observer.send(value: receiver as! Control) + observer.send(value: ()) } } extension Reactive where Base: UIControl { - public func trigger(for events: UIControlEvents) -> Signal { + internal var associatedAction: Atomic<(action: CocoaAction, controlEvents: UIControlEvents, disposable: Disposable)?> { + return associatedObject(base, + key: &associatedActionKey, + initial: { _ in Atomic(nil) }) + } + + public var action: CocoaAction? { + return associatedAction.value?.action + } + + public var controlEventsForAction: UIControlEvents? { + return associatedAction.value?.controlEvents + } + + public func setAction(_ action: CocoaAction?, for controlEvents: UIControlEvents) { + associatedAction.modify { associatedAction in + if let old = associatedAction { + old.disposable.dispose() + } + + if let action = action { + base.addTarget(action, action: CocoaAction.selector, for: controlEvents) + + let disposable = CompositeDisposable() + disposable += isEnabled <~ action.isEnabled + disposable += { [weak base] in + base?.removeTarget(action, action: CocoaAction.selector, for: controlEvents) + } + + associatedAction = (action, controlEvents, ScopedDisposable(disposable)) + } else { + associatedAction = nil + } + } + } + + public func trigger(for controlEvents: UIControlEvents) -> Signal<(), NoError> { return Signal { observer in let receiver = UnsafeControlReceiver(observer: observer) - base.addTarget(receiver, action: #selector(UnsafeControlReceiver.sendNext), for: events) + base.addTarget(receiver, + action: #selector(UnsafeControlReceiver.sendNext), + for: controlEvents) let disposable = lifetime.ended.observeCompleted(observer.sendCompleted) return ActionDisposable { [weak base] in disposable?.dispose() - base?.removeTarget(receiver, action: #selector(UnsafeControlReceiver.sendNext), for: events) + + base?.removeTarget(receiver, + action: #selector(UnsafeControlReceiver.sendNext), + for: controlEvents) } } } @@ -66,4 +107,5 @@ extension Reactive where Base: UIControl { } } +private var associatedActionKey = 0 private var valueChangedKey: UInt8 = 0 diff --git a/ReactiveCocoaTests/CocoaActionSpec.swift b/ReactiveCocoaTests/CocoaActionSpec.swift index 590b9c5a2f..bf21d87877 100644 --- a/ReactiveCocoaTests/CocoaActionSpec.swift +++ b/ReactiveCocoaTests/CocoaActionSpec.swift @@ -7,25 +7,31 @@ import ReactiveCocoa class CocoaActionSpec: QuickSpec { override func spec() { var action: Action! + #if os(OSX) + var cocoaAction: CocoaAction! + #else + var cocoaAction: CocoaAction! + #endif beforeEach { action = Action { value in SignalProducer(value: value + 1) } expect(action.isEnabled.value) == true - expect(action.unsafeCocoaAction.isEnabled.value).toEventually(beTruthy()) + cocoaAction = CocoaAction(action) { _ in 1 } + expect(cocoaAction.isEnabled.value).toEventually(beTruthy()) } #if os(OSX) it("should be compatible with AppKit") { let control = NSControl(frame: NSZeroRect) - control.target = action.unsafeCocoaAction - control.action = CocoaAction.selector + control.target = cocoaAction + control.action = CocoaAction.selector control.performClick(nil) } #elseif os(iOS) it("should be compatible with UIKit") { let control = UIControl(frame: .zero) - control.addTarget(action.unsafeCocoaAction, action: CocoaAction.selector, for: .touchDown) + control.addTarget(cocoaAction, action: CocoaAction.selector, for: .touchDown) control.sendActions(for: .touchDown) } #endif @@ -33,7 +39,6 @@ class CocoaActionSpec: QuickSpec { it("should emit changes for enabled") { var values: [Bool] = [] - let cocoaAction = action.unsafeCocoaAction cocoaAction.isEnabled.producer .startWithValues { values.append($0) } @@ -49,7 +54,6 @@ class CocoaActionSpec: QuickSpec { it("should generate KVO notifications for executing") { var values: [Bool] = [] - let cocoaAction = action.unsafeCocoaAction cocoaAction.isExecuting.producer .startWithValues { values.append($0) } @@ -61,7 +65,7 @@ class CocoaActionSpec: QuickSpec { _ = cocoaAction } - + context("lifetime") { it("unsafeCocoaAction should not create a retain cycle") { weak var weakAction: Action? @@ -70,8 +74,7 @@ class CocoaActionSpec: QuickSpec { } weakAction = action expect(weakAction).notTo(beNil()) - - _ = action!.unsafeCocoaAction + action = nil expect(weakAction).to(beNil()) } diff --git a/ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift b/ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift index 17c68da12a..5e491c368a 100644 --- a/ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift +++ b/ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift @@ -13,35 +13,34 @@ import XCTest import enum Result.NoError class UIBarButtonItemTests: XCTestCase { + weak var _barButtonItem: UIBarButtonItem? - weak var _barButtonItem: UIBarButtonItem? - - override func tearDown() { - XCTAssert(_barButtonItem == nil, "Retain cycle detected in UIBarButtonItem properties") - super.tearDown() - } - - func testActionPropertyDoesntCreateRetainCycle() { - let barButtonItem = UIBarButtonItem() - _barButtonItem = barButtonItem - - let action = Action<(),(),NoError> { - SignalProducer(value: ()) - } - barButtonItem.reactive.action <~ SignalProducer(value: CocoaAction(action, input: ())) - } - - func testEnabledProperty() { - let barButtonItem = UIBarButtonItem() - barButtonItem.isEnabled = true - - let (pipeSignal, observer) = Signal.pipe() - barButtonItem.reactive.isEnabled <~ SignalProducer(signal: pipeSignal) - - - observer.send(value: false) - XCTAssertFalse(barButtonItem.isEnabled) - observer.send(value: true) - XCTAssertTrue(barButtonItem.isEnabled) - } + override func tearDown() { + XCTAssert(_barButtonItem == nil, "Retain cycle detected in UIBarButtonItem properties") + super.tearDown() + } + + func testActionPropertyDoesntCreateRetainCycle() { + let barButtonItem = UIBarButtonItem() + _barButtonItem = barButtonItem + + let action = Action<(),(),NoError> { + SignalProducer(value: ()) + } + + barButtonItem.reactive.pressed = CocoaAction(action) + } + + func testEnabledProperty() { + let barButtonItem = UIBarButtonItem() + barButtonItem.isEnabled = true + + let (pipeSignal, observer) = Signal.pipe() + barButtonItem.reactive.isEnabled <~ SignalProducer(signal: pipeSignal) + + observer.send(value: false) + XCTAssertFalse(barButtonItem.isEnabled) + observer.send(value: true) + XCTAssertTrue(barButtonItem.isEnabled) + } } diff --git a/ReactiveCocoaTests/UIKit/UIButtonTests.swift b/ReactiveCocoaTests/UIKit/UIButtonTests.swift index 2be3bd9c03..f5ed97924e 100644 --- a/ReactiveCocoaTests/UIKit/UIButtonTests.swift +++ b/ReactiveCocoaTests/UIKit/UIButtonTests.swift @@ -13,84 +13,84 @@ import XCTest import enum Result.NoError extension UIButton { - static func button() -> UIButton { - let button = UIButton(type: UIButtonType.custom) - return button - } + static func button() -> UIButton { + let button = UIButton(type: UIButtonType.custom) + return button + } } class UIButtonTests: XCTestCase { + weak var _button: UIButton? - weak var _button: UIButton? - - override func tearDown() { - XCTAssert(_button == nil, "Retain cycle detected in UIButton properties") - super.tearDown() - } - - func testEnabledPropertyDoesntCreateRetainCycle() { - let button = UIButton(frame: CGRect.zero) - _button = button - - button.reactive.isEnabled <~ SignalProducer(value: false) - XCTAssert(_button?.isEnabled == false) - } - - func testPressedPropertyDoesntCreateRetainCycle() { - let button = UIButton(frame: CGRect.zero) - _button = button - - let action = Action<(),(),NoError> { - SignalProducer(value: ()) - } - button.reactive.pressed <~ SignalProducer(value: CocoaAction(action, input: ())) - } - - func testTitlePropertyDoesntCreateRetainCycle() { - let button = UIButton(frame: CGRect.zero) - _button = button - - button.reactive.title <~ SignalProducer(value: "button") - XCTAssert(_button?.title(for: UIControlState()) == "button") - } - - func testTitleProperty() { - let firstTitle = "First title" - let secondTitle = "Second title" - let button = UIButton(frame: CGRect.zero) - let (pipeSignal, observer) = Signal.pipe() - button.reactive.title <~ SignalProducer(signal: pipeSignal) - button.setTitle("", for: .selected) - button.setTitle("", for: .highlighted) - - observer.send(value: firstTitle) - XCTAssertEqual(button.title(for: UIControlState()), firstTitle) - XCTAssertEqual(button.title(for: .highlighted), "") - XCTAssertEqual(button.title(for: .selected), "") - - observer.send(value: secondTitle) - XCTAssertEqual(button.title(for: UIControlState()), secondTitle) - XCTAssertEqual(button.title(for: .highlighted), "") - XCTAssertEqual(button.title(for: .selected), "") - } - - func testPressedProperty() { - let button = UIButton(frame: CGRect.zero) - button.isEnabled = true - button.isUserInteractionEnabled = true - - let pressed = MutableProperty(false) - let action = Action<(), Bool, NoError> { _ in - SignalProducer(value: true) - } - - pressed <~ SignalProducer(signal: action.values) - button.reactive.pressed <~ SignalProducer(value: CocoaAction(action, input: ())) - - XCTAssertFalse(pressed.value) - - button.sendActions(for: .touchUpInside) - - XCTAssertTrue(pressed.value) - } + override func tearDown() { + XCTAssert(_button == nil, "Retain cycle detected in UIButton properties") + super.tearDown() + } + + func testEnabledPropertyDoesntCreateRetainCycle() { + let button = UIButton(frame: CGRect.zero) + _button = button + + button.reactive.isEnabled <~ SignalProducer(value: false) + XCTAssert(_button?.isEnabled == false) + } + + func testPressedPropertyDoesntCreateRetainCycle() { + let button = UIButton(frame: CGRect.zero) + _button = button + + let action = Action<(),(),NoError> { + SignalProducer(value: ()) + } + + button.reactive.pressed = CocoaAction(action) + } + + func testTitlePropertyDoesntCreateRetainCycle() { + let button = UIButton(frame: CGRect.zero) + _button = button + + button.reactive.title <~ SignalProducer(value: "button") + XCTAssert(_button?.title(for: UIControlState()) == "button") + } + + func testTitleProperty() { + let firstTitle = "First title" + let secondTitle = "Second title" + let button = UIButton(frame: CGRect.zero) + let (pipeSignal, observer) = Signal.pipe() + button.reactive.title <~ SignalProducer(signal: pipeSignal) + button.setTitle("", for: .selected) + button.setTitle("", for: .highlighted) + + observer.send(value: firstTitle) + XCTAssertEqual(button.title(for: UIControlState()), firstTitle) + XCTAssertEqual(button.title(for: .highlighted), "") + XCTAssertEqual(button.title(for: .selected), "") + + observer.send(value: secondTitle) + XCTAssertEqual(button.title(for: UIControlState()), secondTitle) + XCTAssertEqual(button.title(for: .highlighted), "") + XCTAssertEqual(button.title(for: .selected), "") + } + + func testPressedProperty() { + let button = UIButton(frame: CGRect.zero) + button.isEnabled = true + button.isUserInteractionEnabled = true + + let pressed = MutableProperty(false) + let action = Action<(), Bool, NoError> { _ in + SignalProducer(value: true) + } + + pressed <~ SignalProducer(signal: action.values) + button.reactive.pressed = CocoaAction(action) + + XCTAssertFalse(pressed.value) + + button.sendActions(for: .touchUpInside) + + XCTAssertTrue(pressed.value) + } } From fb731d8e8ae6b89529ffedcb9b4dbc864891d720 Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 6 Oct 2016 23:06:23 +0200 Subject: [PATCH 0418/1028] Expose control values as binding targets and signals. --- ReactiveCocoa.xcodeproj/project.pbxproj | 45 +++++-- ReactiveCocoa/AppKit/NSControl.swift | 109 ++++++++++++++++ ReactiveCocoa/AppKit/NSTextField.swift | 16 ++- ReactiveCocoa/UIKit/UIControl.swift | 14 -- ReactiveCocoa/UIKit/UIDatePicker.swift | 9 +- ReactiveCocoa/UIKit/UISegmentedControl.swift | 15 +-- ReactiveCocoa/UIKit/UISwitch.swift | 9 +- ReactiveCocoa/UIKit/UITextField.swift | 26 ++-- ReactiveCocoa/UIKit/UITextView.swift | 31 ++++- ReactiveCocoaTests/AppKit/NSControlSpec.swift | 123 ++++++++++++++++++ .../UIKit/UIDatePickerTests.swift | 63 +++++---- .../UIKit/UISegmentedControlTests.swift | 29 ++--- ReactiveCocoaTests/UIKit/UISwitchTests.swift | 30 +++-- .../UIKit/UITextFieldTests.swift | 46 ++++--- .../UIKit/UITextViewTests.swift | 44 ++++--- 15 files changed, 448 insertions(+), 161 deletions(-) create mode 100644 ReactiveCocoa/AppKit/NSControl.swift create mode 100644 ReactiveCocoaTests/AppKit/NSControlSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index fe0d073790..fcf9bc975c 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -25,7 +25,6 @@ 9A1D05E11D93E99100ACF44C /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* Association.swift */; }; 9A1D05E21D93E99100ACF44C /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* Association.swift */; }; 9A1D05E31D93E99100ACF44C /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* Association.swift */; }; - 9A1D05FE1D93E9F900ACF44C /* NSTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05EA1D93E9F100ACF44C /* NSTextField.swift */; }; 9A1D05FF1D93EA0000ACF44C /* UIActivityIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05EC1D93E9F100ACF44C /* UIActivityIndicatorView.swift */; }; 9A1D06001D93EA0000ACF44C /* UIBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05ED1D93E9F100ACF44C /* UIBarButtonItem.swift */; }; 9A1D06011D93EA0000ACF44C /* UIBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05EE1D93E9F100ACF44C /* UIBarItem.swift */; }; @@ -93,10 +92,17 @@ 9AD0F06B1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; 9AD0F06C1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; 9AD0F06D1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; + 9ADE4A7C1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */; }; + 9ADE4A7D1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */; }; + 9ADE4A7E1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */; }; + 9ADE4A7F1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */; }; + 9ADE4A891DA6D206005C2AC8 /* NSControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADE4A881DA6D206005C2AC8 /* NSControl.swift */; }; + 9ADE4A8F1DA6DA20005C2AC8 /* NSControlSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADE4A8D1DA6D965005C2AC8 /* NSControlSpec.swift */; }; 9ADE4A911DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADE4A901DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift */; }; 9ADE4A921DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADE4A901DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift */; }; 9ADE4A931DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADE4A901DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift */; }; 9ADE4A941DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADE4A901DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift */; }; + 9ADE4A961DA6F018005C2AC8 /* NSTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADE4A951DA6F018005C2AC8 /* NSTextField.swift */; }; 9AF0EA751D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; 9AF0EA761D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; 9AF0EA771D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; @@ -115,10 +121,6 @@ BEE020661D637B0000DF261F /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B696FB801A7640C00075236D /* TestError.swift */; }; BFA6B94D1A7604D400C846D1 /* SignalProducerNimbleMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */; }; BFA6B94E1A7604D500C846D1 /* SignalProducerNimbleMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */; }; - C7142DBC1CDEA167009F402D /* CocoaAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7142DBB1CDEA167009F402D /* CocoaAction.swift */; }; - C7142DBD1CDEA194009F402D /* CocoaAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7142DBB1CDEA167009F402D /* CocoaAction.swift */; }; - C7142DBE1CDEA194009F402D /* CocoaAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7142DBB1CDEA167009F402D /* CocoaAction.swift */; }; - C7142DBF1CDEA195009F402D /* CocoaAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7142DBB1CDEA167009F402D /* CocoaAction.swift */; }; CD0C45DE1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */; }; CD0C45DF1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */; }; CD0C45E01CC9A288009F5BF0 /* DynamicProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */; }; @@ -213,7 +215,6 @@ 57A4D2471BA13F9700F7D4B1 /* tvOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-StaticLibrary.xcconfig"; sourceTree = ""; }; 7DFBED031CDB8C9500EE435B /* ReactiveCocoaTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveCocoaTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 9A1D05DF1D93E99100ACF44C /* Association.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Association.swift; sourceTree = ""; }; - 9A1D05EA1D93E9F100ACF44C /* NSTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSTextField.swift; sourceTree = ""; }; 9A1D05EC1D93E9F100ACF44C /* UIActivityIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorView.swift; sourceTree = ""; }; 9A1D05ED1D93E9F100ACF44C /* UIBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIBarButtonItem.swift; sourceTree = ""; }; 9A1D05EE1D93E9F100ACF44C /* UIBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIBarItem.swift; sourceTree = ""; }; @@ -247,7 +248,11 @@ 9A1D06761D9415FB00ACF44C /* RACObjCRuntimeUtilities.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RACObjCRuntimeUtilities.m; sourceTree = ""; }; 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyValueObservingSpec.swift; sourceTree = ""; }; 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+KeyValueObserving.swift"; sourceTree = ""; }; + 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaAction.swift; sourceTree = ""; }; + 9ADE4A881DA6D206005C2AC8 /* NSControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSControl.swift; sourceTree = ""; }; + 9ADE4A8D1DA6D965005C2AC8 /* NSControlSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NSControlSpec.swift; path = AppKit/NSControlSpec.swift; sourceTree = ""; }; 9ADE4A901DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+ReactiveExtensionsProvider.swift"; sourceTree = ""; }; + 9ADE4A951DA6F018005C2AC8 /* NSTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSTextField.swift; sourceTree = ""; }; 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+BindingTarget.swift"; sourceTree = ""; }; A97451331B3A935E00F48E55 /* watchOS-Application.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Application.xcconfig"; sourceTree = ""; }; A97451341B3A935E00F48E55 /* watchOS-Base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Base.xcconfig"; sourceTree = ""; }; @@ -263,7 +268,6 @@ BE330A181D634F5900806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-appletvos/ReactiveSwift.framework"; sourceTree = ""; }; BE330A1A1D634F5F00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-appletvos/ReactiveSwift.framework"; sourceTree = ""; }; BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalProducerNimbleMatchers.swift; sourceTree = ""; }; - C7142DBB1CDEA167009F402D /* CocoaAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaAction.swift; sourceTree = ""; }; CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DynamicProperty.swift; sourceTree = ""; }; CD8401821CEE8ED7009F0ABF /* CocoaActionSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaActionSpec.swift; sourceTree = ""; }; CDC42E2E1AE7AB8B00965373 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -386,7 +390,8 @@ 9A1D05E91D93E9F100ACF44C /* AppKit */ = { isa = PBXGroup; children = ( - 9A1D05EA1D93E9F100ACF44C /* NSTextField.swift */, + 9ADE4A951DA6F018005C2AC8 /* NSTextField.swift */, + 9ADE4A881DA6D206005C2AC8 /* NSControl.swift */, ); path = AppKit; sourceTree = ""; @@ -433,6 +438,14 @@ path = UIKit; sourceTree = ""; }; + 9ADE4A8C1DA6D94C005C2AC8 /* AppKit */ = { + isa = PBXGroup; + children = ( + 9ADE4A8D1DA6D965005C2AC8 /* NSControlSpec.swift */, + ); + name = AppKit; + sourceTree = ""; + }; A97451321B3A935E00F48E55 /* watchOS */ = { isa = PBXGroup; children = ( @@ -490,7 +503,7 @@ D04725EF19E49ED7006002AA /* ReactiveCocoa.h */, 9A1D06751D9415FB00ACF44C /* RACObjCRuntimeUtilities.h */, 9A1D06761D9415FB00ACF44C /* RACObjCRuntimeUtilities.m */, - C7142DBB1CDEA167009F402D /* CocoaAction.swift */, + 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */, CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */, 9ADE4A901DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift */, 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */, @@ -517,6 +530,7 @@ D04725F919E49ED7006002AA /* ReactiveCocoaTests */ = { isa = PBXGroup; children = ( + 9ADE4A8C1DA6D94C005C2AC8 /* AppKit */, 9A1D06231D93EA7E00ACF44C /* UIKit */, CD8401821CEE8ED7009F0ABF /* CocoaActionSpec.swift */, D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */, @@ -897,10 +911,10 @@ files = ( 9A1D06131D93EA0100ACF44C /* UIBarItem.swift in Sources */, 9A1D061F1D93EA0100ACF44C /* UITextField.swift in Sources */, + 9ADE4A7F1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, 9A1D06201D93EA0100ACF44C /* UITextView.swift in Sources */, 9A1D06181D93EA0100ACF44C /* UIImageView.swift in Sources */, 9AD0F06D1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, - C7142DBF1CDEA195009F402D /* CocoaAction.swift in Sources */, 9A1D06211D93EA0100ACF44C /* UIView.swift in Sources */, 9A1D067A1D94160100ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9ADE4A941DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, @@ -950,7 +964,8 @@ 9A1D06791D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9ADE4A931DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, 9A1D065D1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, - C7142DBE1CDEA194009F402D /* CocoaAction.swift in Sources */, + 9ADE4A7E1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, + 9A1D065D1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, CD0C45E01CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9AF0EA771D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, 9A1D05E21D93E99100ACF44C /* Association.swift in Sources */, @@ -966,12 +981,13 @@ CD0C45DE1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9A1D06771D9415FF00ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, - 9A1D05FE1D93E9F900ACF44C /* NSTextField.swift in Sources */, 9A1D05E01D93E99100ACF44C /* Association.swift in Sources */, + 9ADE4A7C1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, + 9ADE4A961DA6F018005C2AC8 /* NSTextField.swift in Sources */, + 9ADE4A891DA6D206005C2AC8 /* NSControl.swift in Sources */, 9AF0EA751D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, 9ADE4A911DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, 9A1D065B1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, - C7142DBC1CDEA167009F402D /* CocoaAction.swift in Sources */, 4A0E10FF1D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -980,6 +996,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9ADE4A8F1DA6DA20005C2AC8 /* NSControlSpec.swift in Sources */, D0A2260E1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */, B696FB811A7640C00075236D /* TestError.swift in Sources */, BFA6B94D1A7604D400C846D1 /* SignalProducerNimbleMatchers.swift in Sources */, @@ -999,7 +1016,6 @@ 9AF0EA761D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, CD0C45DF1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9A1D06781D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */, - C7142DBD1CDEA194009F402D /* CocoaAction.swift in Sources */, 9A1D060F1D93EA0000ACF44C /* UIView.swift in Sources */, 9A1D06051D93EA0000ACF44C /* UIDatePicker.swift in Sources */, 9A1D05E11D93E99100ACF44C /* Association.swift in Sources */, @@ -1015,6 +1031,7 @@ 9A1D06001D93EA0000ACF44C /* UIBarButtonItem.swift in Sources */, 9A1D05FF1D93EA0000ACF44C /* UIActivityIndicatorView.swift in Sources */, 9A1D06091D93EA0000ACF44C /* UISegmentedControl.swift in Sources */, + 9ADE4A7D1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ReactiveCocoa/AppKit/NSControl.swift b/ReactiveCocoa/AppKit/NSControl.swift new file mode 100644 index 0000000000..8b8f7446f2 --- /dev/null +++ b/ReactiveCocoa/AppKit/NSControl.swift @@ -0,0 +1,109 @@ +// +// NSControl.swift +// ReactiveCocoa +// +// Created by Yury Lapitsky on 7/8/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import ReactiveSwift +import enum Result.NoError +import AppKit + +extension Reactive where Base: NSControl { + public var attributedString: BindingTarget { + return makeBindingTarget { $0.attributedStringValue = $1 } + } + + public var attributedStringValues: Signal { + return trigger.map { [unowned base] in base.attributedStringValue } + } + + public var boolValue: BindingTarget { + return makeBindingTarget { $0.integerValue = $1 ? NSOnState : NSOffState } + } + + public var boolValues: Signal { + return trigger.map { [unowned base] in base.integerValue == NSOffState ? false : true } + } + + public var doubleValue: BindingTarget { + return makeBindingTarget { $0.doubleValue = $1 } + } + + public var doubleValues: Signal { + return trigger.map { [unowned base] in base.doubleValue } + } + + public var floatValue: BindingTarget { + return makeBindingTarget { $0.floatValue = $1 } + } + + public var floatValues: Signal { + return trigger.map { [unowned base] in base.floatValue } + } + + public var intValue: BindingTarget { + return makeBindingTarget { $0.intValue = $1 } + } + + public var intValues: Signal { + return trigger.map { [unowned base] in base.intValue } + } + + public var integerValue: BindingTarget { + return makeBindingTarget { $0.integerValue = $1 } + } + + public var integerValues: Signal { + return trigger.map { [unowned base] in base.integerValue } + } + + public var objectValue: BindingTarget { + return makeBindingTarget { $0.objectValue = $1 } + } + + public var objectValues: Signal { + return trigger.map { [unowned base] in base.objectValue } + } + + public var stringValue: BindingTarget { + return makeBindingTarget { $0.stringValue = $1 } + } + + public var stringValues: Signal { + return trigger.map { [unowned base] in base.stringValue } + } + + private var trigger: Signal<(), NoError> { + let receiver: ActionMessageReceiver = associatedObject(base, key: &controlTriggerKey) { base in + let receiver = ActionMessageReceiver() + base.target = receiver + base.action = #selector(ActionMessageReceiver.receive) + + return receiver + } + + return receiver.trigger + } +} + +private class ActionMessageReceiver: NSObject { + let trigger: Signal<(), NoError> + private let observer: Signal<(), NoError>.Observer + + override init() { + (trigger, observer) = Signal<(), NoError>.pipe() + super.init() + } + + deinit { + observer.sendCompleted() + } + + @objc func receive(_ sender: Any?) { + observer.send(value: ()) + } +} + +var controlTriggerKey = 0 diff --git a/ReactiveCocoa/AppKit/NSTextField.swift b/ReactiveCocoa/AppKit/NSTextField.swift index a201034cff..0588f00316 100644 --- a/ReactiveCocoa/AppKit/NSTextField.swift +++ b/ReactiveCocoa/AppKit/NSTextField.swift @@ -11,12 +11,16 @@ import AppKit import enum Result.NoError extension Reactive where Base: NSTextField { - /// Sends the field's string value whenever it changes. - public var textSignal: SignalProducer { - return NotificationCenter.default.reactive + public var continuousStringValues: Signal { + var signal: Signal! + + NotificationCenter.default + .reactive .notifications(forName: .NSControlTextDidChange, object: base) - .map { notification in - (notification.object as! NSTextField).stringValue - } + .take(during: lifetime) + .map { ($0.object as! NSTextField).stringValue } + .startWithSignal { innerSignal, _ in signal = innerSignal } + + return signal } } diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index dfa051a186..4ba0571709 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -78,19 +78,6 @@ extension Reactive where Base: UIControl { } } - #if os(iOS) - /// Creates a bindable property to wrap a control's value. - /// - /// This property uses `UIControlEvents.ValueChanged` and `UIControlEvents.EditingChanged` - /// events to detect changes and keep the value up-to-date. - // - internal func value(getter: @escaping (Base) -> T, setter: @escaping (Base, T) -> ()) -> MutableProperty { - return associatedProperty(base, key: &valueChangedKey, initial: getter, setter: setter) { property in - property <~ self.trigger(for: [.valueChanged, .editingChanged]).map(getter) - } - } - #endif - /// Wraps a control's `enabled` state in a bindable property. public var isEnabled: BindingTarget { return makeBindingTarget { $0.isEnabled = $1 } @@ -108,4 +95,3 @@ extension Reactive where Base: UIControl { } private var associatedActionKey = 0 -private var valueChangedKey: UInt8 = 0 diff --git a/ReactiveCocoa/UIKit/UIDatePicker.swift b/ReactiveCocoa/UIKit/UIDatePicker.swift index 0a7a3ec20f..af0e8953bb 100644 --- a/ReactiveCocoa/UIKit/UIDatePicker.swift +++ b/ReactiveCocoa/UIKit/UIDatePicker.swift @@ -7,11 +7,16 @@ // import ReactiveSwift +import enum Result.NoError import UIKit extension Reactive where Base: UIDatePicker { // Wraps a datePicker's `date` value in a bindable property. - public var date: MutableProperty { - return value(getter: { $0.date }, setter: { $0.date = $1 }) + public var date: BindingTarget { + return makeBindingTarget { $0.date = $1 } + } + + public var dates: Signal { + return trigger(for: .valueChanged).map { [unowned base] in base.date } } } diff --git a/ReactiveCocoa/UIKit/UISegmentedControl.swift b/ReactiveCocoa/UIKit/UISegmentedControl.swift index 22282ea9a1..687c4beb83 100644 --- a/ReactiveCocoa/UIKit/UISegmentedControl.swift +++ b/ReactiveCocoa/UIKit/UISegmentedControl.swift @@ -7,19 +7,16 @@ // import ReactiveSwift +import enum Result.NoError import UIKit extension Reactive where Base: UISegmentedControl { /// Wraps a segmentedControls `selectedSegmentIndex` state in a bindable property. - public var selectedSegmentIndex: MutableProperty { - let property = associatedProperty(base, - key: &selectedSegmentIndexKey, - initial: { $0.selectedSegmentIndex }, - setter: { $0.selectedSegmentIndex = $1 }) + public var selectedSegmentIndex: BindingTarget { + return makeBindingTarget { $0.selectedSegmentIndex = $1 } + } - property <~ trigger(for: .valueChanged).map { $0.selectedSegmentIndex } - return property + public var selectedSegmentIndexes: Signal { + return trigger(for: .valueChanged).map { [unowned base] in base.selectedSegmentIndex } } } - -private var selectedSegmentIndexKey: UInt8 = 0 diff --git a/ReactiveCocoa/UIKit/UISwitch.swift b/ReactiveCocoa/UIKit/UISwitch.swift index 7d281ac3cc..1b3a8bf099 100644 --- a/ReactiveCocoa/UIKit/UISwitch.swift +++ b/ReactiveCocoa/UIKit/UISwitch.swift @@ -7,11 +7,16 @@ // import ReactiveSwift +import enum Result.NoError import UIKit extension Reactive where Base: UISwitch { /// Wraps a switch's `on` value in a bindable property. - public var isOn: MutableProperty { - return value(getter: { $0.isOn }, setter: { $0.isOn = $1 }) + public var isOn: BindingTarget { + return makeBindingTarget { $0.isOn = $1 } + } + + public var isOnValues: Signal { + return trigger(for: .valueChanged).map { [unowned base] in base.isOn } } } diff --git a/ReactiveCocoa/UIKit/UITextField.swift b/ReactiveCocoa/UIKit/UITextField.swift index 91e2f641e3..f76c0f1d01 100644 --- a/ReactiveCocoa/UIKit/UITextField.swift +++ b/ReactiveCocoa/UIKit/UITextField.swift @@ -7,25 +7,19 @@ // import ReactiveSwift +import enum Result.NoError import UIKit extension Reactive where Base: UITextField { - /// Wraps a textField's `text` value in a bindable property. - public var text: MutableProperty { - let getter: (UITextField) -> String? = { $0.text } - let setter: (UITextField, String?) -> () = { $0.text = $1 } - #if os(iOS) - return value(getter: getter, setter: setter) - #else - return associatedProperty(base, key: &textKey, initial: getter, setter: setter) { property in - property <~ - NotificationCenter.default.reactive - .notifications(forName: .UITextFieldTextDidChange, object: base) - .map { ($0.object as! UITextField).text } - } - #endif + public var text: BindingTarget { + return makeBindingTarget { $0.text = $1 } } -} + public var texts: Signal { + return trigger(for: .editingDidEnd).map { [unowned base] in base.text } + } -private var textKey: UInt8 = 0 + public var continuousTexts: Signal { + return trigger(for: .editingChanged).map { [unowned base] in base.text } + } +} diff --git a/ReactiveCocoa/UIKit/UITextView.swift b/ReactiveCocoa/UIKit/UITextView.swift index 171c41c928..52260e5d99 100644 --- a/ReactiveCocoa/UIKit/UITextView.swift +++ b/ReactiveCocoa/UIKit/UITextView.swift @@ -12,10 +12,33 @@ import enum Result.NoError extension Reactive where Base: UITextView { /// Sends the textView's string value whenever it changes. - public var text: SignalProducer { - return NotificationCenter.default.reactive + public var text: BindingTarget { + return makeBindingTarget { $0.text = $1 } + } + + public var texts: Signal { + var signal: Signal! + + NotificationCenter.default + .reactive + .notifications(forName: .UITextViewTextDidEndEditing, object: base) + .take(during: lifetime) + .map { ($0.object as! UITextView).text! } + .startWithSignal { innerSignal, _ in signal = innerSignal } + + return signal + } + + public var continuousTexts: Signal { + var signal: Signal! + + NotificationCenter.default + .reactive .notifications(forName: .UITextViewTextDidChange, object: base) - .map { ($0.object as? UITextView)?.text } - .skipNil() + .take(during: lifetime) + .map { ($0.object as! UITextView).text! } + .startWithSignal { innerSignal, _ in signal = innerSignal } + + return signal } } diff --git a/ReactiveCocoaTests/AppKit/NSControlSpec.swift b/ReactiveCocoaTests/AppKit/NSControlSpec.swift new file mode 100644 index 0000000000..7fbabc403d --- /dev/null +++ b/ReactiveCocoaTests/AppKit/NSControlSpec.swift @@ -0,0 +1,123 @@ +import Quick +import Nimble +import Result +import ReactiveSwift +import ReactiveCocoa +import AppKit + +class NSControlSpec: QuickSpec { + override func spec() { + describe("NSControl") { + var window: NSWindow! + var control: NSButton! + + beforeEach { + window = NSWindow() + + control = NSButton(frame: .zero) + control.setButtonType(.onOff) + control.state = NSOffState + + window.contentView!.addSubview(control) + } + + it("should emit changes in Int") { + var values = [Int]() + control.reactive.integerValues.observeValues { values.append($0) } + + control.performClick(nil) + control.performClick(nil) + + expect(values) == [1, 0] + } + + it("should emit changes in Bool") { + var values = [Bool]() + control.reactive.boolValues.observeValues { values.append($0) } + + control.performClick(nil) + control.performClick(nil) + + expect(values) == [true, false] + } + + it("should emit changes in Int32") { + var values = [Int32]() + control.reactive.intValues.observeValues { values.append($0) } + + control.performClick(nil) + control.performClick(nil) + + expect(values) == [1, 0] + } + + it("should emit changes in Double") { + var values = [Double]() + control.reactive.doubleValues.observeValues { values.append($0) } + + control.performClick(nil) + control.performClick(nil) + + expect(values) == [1.0, 0.0] + } + + it("should emit changes in Float") { + var values = [Float]() + control.reactive.floatValues.observeValues { values.append($0) } + + control.performClick(nil) + control.performClick(nil) + + expect(values) == [1.0, 0.0] + } + + it("should emit changes in String") { + var values = [String]() + control.reactive.stringValues.observeValues { values.append($0) } + + control.performClick(nil) + control.performClick(nil) + + expect(values) == ["1", "0"] + } + + it("should emit changes in AttributedString") { + var values = [NSAttributedString]() + control.reactive.attributedStringValues.observeValues { values.append($0) } + + control.performClick(nil) + control.performClick(nil) + + expect(values) == [NSAttributedString(string: "1"), NSAttributedString(string: "0")] + } + + it("should emit changes as objects") { + let values = NSMutableArray() + control.reactive.objectValues.observeValues { values.add($0!) } + + control.performClick(nil) + control.performClick(nil) + + expect(values) == [NSNumber(value: 1), NSNumber(value: 0)] + } + + it("should emit changes for multiple signals") { + var valuesA = [String]() + control.reactive.stringValues.observeValues { valuesA.append($0) } + + var valuesB = [Bool]() + control.reactive.boolValues.observeValues { valuesB.append($0) } + + var valuesC = [Int]() + control.reactive.integerValues.observeValues { valuesC.append($0) } + + control.performClick(nil) + control.performClick(nil) + + expect(valuesA) == ["1", "0"] + expect(valuesB) == [true, false] + expect(valuesC) == [1, 0] + } + } + } +} diff --git a/ReactiveCocoaTests/UIKit/UIDatePickerTests.swift b/ReactiveCocoaTests/UIKit/UIDatePickerTests.swift index 7b19d8f2fe..74d26260a4 100644 --- a/ReactiveCocoaTests/UIKit/UIDatePickerTests.swift +++ b/ReactiveCocoaTests/UIKit/UIDatePickerTests.swift @@ -12,36 +12,35 @@ import UIKit import XCTest class UIDatePickerTests: XCTestCase { - - var date: Date! - var picker: UIDatePicker! - - override func setUp() { - let formatter = DateFormatter() - formatter.dateFormat = "MM/dd/YYYY" - date = formatter.date(from: "11/29/1988")! - - picker = UIDatePicker(frame: CGRect.zero) - } - - func testUpdatePickerFromProperty() { - picker.reactive.date.value = date - - XCTAssertEqual(picker.date, date) - } - - func testUpdatePropertyFromPicker() { - let expectation = self.expectation(description: "Expected rac_date to send an event when picker's date value is changed by a UI event") - defer { self.waitForExpectations(timeout: 2, handler: nil) } - - picker.reactive.date.signal.observeValues { changedDate in - XCTAssertEqual(changedDate, self.date) - expectation.fulfill() - } - - picker.date = date - picker.isEnabled = true - picker.isUserInteractionEnabled = true - picker.sendActions(for: .valueChanged) - } + var date: Date! + var picker: UIDatePicker! + + override func setUp() { + let formatter = DateFormatter() + formatter.dateFormat = "MM/dd/YYYY" + date = formatter.date(from: "11/29/1988")! + + picker = UIDatePicker(frame: CGRect.zero) + } + + func testUpdatePickerFromProperty() { + picker.reactive.date.consume(date) + + XCTAssertEqual(picker.date, date) + } + + func testUpdatePropertyFromPicker() { + let expectation = self.expectation(description: "Expected rac_date to send an event when picker's date value is changed by a UI event") + defer { self.waitForExpectations(timeout: 2, handler: nil) } + + picker.reactive.dates.observeValues { changedDate in + XCTAssertEqual(changedDate, self.date) + expectation.fulfill() + } + + picker.date = date + picker.isEnabled = true + picker.isUserInteractionEnabled = true + picker.sendActions(for: .valueChanged) + } } diff --git a/ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift b/ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift index fe75735907..37f0af603d 100644 --- a/ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift +++ b/ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift @@ -12,19 +12,18 @@ import ReactiveCocoa import Result class UISegmentedControlTests: XCTestCase { - - func testSelectedSegmentIndexProperty() { - let s = UISegmentedControl(items: ["0", "1", "2"]) - s.selectedSegmentIndex = UISegmentedControlNoSegment - XCTAssertEqual(s.numberOfSegments, 3) - - let (pipeSignal, observer) = Signal.pipe() - s.reactive.selectedSegmentIndex <~ SignalProducer(signal: pipeSignal) - - XCTAssertEqual(s.selectedSegmentIndex, UISegmentedControlNoSegment) - observer.send(value: 1) - XCTAssertEqual(s.selectedSegmentIndex, 1) - observer.send(value: 2) - XCTAssertEqual(s.selectedSegmentIndex, 2) - } + func testSelectedSegmentIndexProperty() { + let s = UISegmentedControl(items: ["0", "1", "2"]) + s.selectedSegmentIndex = UISegmentedControlNoSegment + XCTAssertEqual(s.numberOfSegments, 3) + + let (pipeSignal, observer) = Signal.pipe() + s.reactive.selectedSegmentIndex <~ SignalProducer(signal: pipeSignal) + + XCTAssertEqual(s.selectedSegmentIndex, UISegmentedControlNoSegment) + observer.send(value: 1) + XCTAssertEqual(s.selectedSegmentIndex, 1) + observer.send(value: 2) + XCTAssertEqual(s.selectedSegmentIndex, 2) + } } diff --git a/ReactiveCocoaTests/UIKit/UISwitchTests.swift b/ReactiveCocoaTests/UIKit/UISwitchTests.swift index 3559abafb3..fdcdaa1c86 100644 --- a/ReactiveCocoaTests/UIKit/UISwitchTests.swift +++ b/ReactiveCocoaTests/UIKit/UISwitchTests.swift @@ -12,21 +12,23 @@ import ReactiveCocoa import Result class UISwitchTests: XCTestCase { - - func testOnProperty() { - let `switch` = UISwitch(frame: CGRect.zero) - `switch`.isOn = false + func testOnProperty() { + let toggle = UISwitch(frame: CGRect.zero) + toggle.isOn = false - let (pipeSignal, observer) = Signal.pipe() - `switch`.reactive.isOn <~ SignalProducer(signal: pipeSignal) + let (pipeSignal, observer) = Signal.pipe() + toggle.reactive.isOn <~ SignalProducer(signal: pipeSignal) - observer.send(value: true) - XCTAssertTrue(`switch`.isOn) - observer.send(value: false) - XCTAssertFalse(`switch`.isOn) + observer.send(value: true) + XCTAssertTrue(toggle.isOn) + observer.send(value: false) + XCTAssertFalse(toggle.isOn) - `switch`.isOn = true - `switch`.sendActions(for: .valueChanged) - XCTAssertTrue(`switch`.reactive.isOn.value) - } + var latestValue: Bool? + toggle.reactive.isOnValues.observeValues { latestValue = $0 } + + toggle.isOn = true + toggle.sendActions(for: .valueChanged) + XCTAssertTrue(latestValue!) + } } diff --git a/ReactiveCocoaTests/UIKit/UITextFieldTests.swift b/ReactiveCocoaTests/UIKit/UITextFieldTests.swift index c15d77ce5b..a22e391919 100644 --- a/ReactiveCocoaTests/UIKit/UITextFieldTests.swift +++ b/ReactiveCocoaTests/UIKit/UITextFieldTests.swift @@ -12,23 +12,33 @@ import UIKit import XCTest class UITextFieldTests: XCTestCase { + func testTexts() { + let expectation = self.expectation(description: "Expected `texts`'s value to equal to the textField's text") + defer { self.waitForExpectations(timeout: 2, handler: nil) } - func testTextProperty() { - let expectation = self.expectation(description: "Expected `rac_text`'s value to equal to the textField's text") - defer { self.waitForExpectations(timeout: 2, handler: nil) } - - let textField = UITextField(frame: CGRect.zero) - textField.text = "Test" - - textField.reactive.text.signal.observeValues { text in - XCTAssertEqual(text, textField.text) - expectation.fulfill() - } - -#if os(iOS) - textField.sendActions(for: .editingChanged) -#else - NotificationCenter.default.post(name: .UITextFieldTextDidChange, object: textField) -#endif - } + let textField = UITextField(frame: CGRect.zero) + textField.text = "Test" + + textField.reactive.texts.observeValues { text in + XCTAssertEqual(text, textField.text) + expectation.fulfill() + } + + textField.sendActions(for: .editingDidEnd) + } + + func testContinuousTexts() { + let expectation = self.expectation(description: "Expected `continuousTexts`'s value to equal to the textField's text") + defer { self.waitForExpectations(timeout: 2, handler: nil) } + + let textField = UITextField(frame: CGRect.zero) + textField.text = "Test" + + textField.reactive.continuousTexts.observeValues { text in + XCTAssertEqual(text, textField.text) + expectation.fulfill() + } + + textField.sendActions(for: .editingChanged) + } } diff --git a/ReactiveCocoaTests/UIKit/UITextViewTests.swift b/ReactiveCocoaTests/UIKit/UITextViewTests.swift index 4239d37925..f256f79b35 100644 --- a/ReactiveCocoaTests/UIKit/UITextViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UITextViewTests.swift @@ -12,19 +12,33 @@ import UIKit import XCTest class UITextViewTests: XCTestCase { - - func testTextProperty() { - let expectation = self.expectation(description: "Expected `rac_text`'s value to equal to the textViews's text") - defer { self.waitForExpectations(timeout: 2, handler: nil) } - - let textView = UITextView(frame: CGRect.zero) - textView.text = "Test" - - textView.reactive.text.startWithValues { text in - XCTAssertEqual(text, textView.text) - expectation.fulfill() - } - - NotificationCenter.default.post(name: NSNotification.Name.UITextViewTextDidChange, object: textView) - } + func testTexts() { + let expectation = self.expectation(description: "Expected `texts`'s value to equal to the textViews's text") + defer { self.waitForExpectations(timeout: 2, handler: nil) } + + let textView = UITextView(frame: CGRect.zero) + textView.text = "Test" + + textView.reactive.texts.observeValues { text in + XCTAssertEqual(text, textView.text) + expectation.fulfill() + } + + NotificationCenter.default.post(name: NSNotification.Name.UITextViewTextDidEndEditing, object: textView) + } + + func testContinuousTexts() { + let expectation = self.expectation(description: "Expected `continuousTexts`'s value to equal to the textViews's text") + defer { self.waitForExpectations(timeout: 2, handler: nil) } + + let textView = UITextView(frame: CGRect.zero) + textView.text = "Test" + + textView.reactive.continuousTexts.observeValues { text in + XCTAssertEqual(text, textView.text) + expectation.fulfill() + } + + NotificationCenter.default.post(name: NSNotification.Name.UITextViewTextDidChange, object: textView) + } } From cd727d77b8aa2fd6c03aec5ff6337adedffae682 Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 6 Oct 2016 23:07:28 +0200 Subject: [PATCH 0419/1028] Reformatted the remaining test cases from Rex. --- .../UIKit/UIActivityIndicatorViewTests.swift | 33 ++-- ...ol+EnableSendActionsForControlEvents.swift | 63 +++--- ReactiveCocoaTests/UIKit/UIControlTests.swift | 183 +++++++++--------- .../UIKit/UIImageViewTests.swift | 113 ++++++----- ReactiveCocoaTests/UIKit/UILabelTests.swift | 143 +++++++------- .../UIKit/UIProgressViewTests.swift | 60 +++--- ReactiveCocoaTests/UIKit/UIViewTests.swift | 129 ++++++------ 7 files changed, 359 insertions(+), 365 deletions(-) diff --git a/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift b/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift index 774c076e63..05309d65bd 100644 --- a/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift @@ -12,24 +12,23 @@ import ReactiveCocoa import Result class UIActivityIndicatorTests: XCTestCase { + weak var _activityIndicatorView: UIActivityIndicatorView? - weak var _activityIndicatorView: UIActivityIndicatorView? + override func tearDown() { + XCTAssert(_activityIndicatorView == nil, "Retain cycle detected in UIActivityIndicatorView properties") + super.tearDown() + } - override func tearDown() { - XCTAssert(_activityIndicatorView == nil, "Retain cycle detected in UIActivityIndicatorView properties") - super.tearDown() - } + func testAnimatingProperty() { + let indicatorView = UIActivityIndicatorView(frame: CGRect.zero) + _activityIndicatorView = indicatorView - func testAnimatingProperty() { - let indicatorView = UIActivityIndicatorView(frame: CGRect.zero) - _activityIndicatorView = indicatorView - - let (pipeSignal, observer) = Signal.pipe() - indicatorView.reactive.isAnimating <~ SignalProducer(signal: pipeSignal) - - observer.send(value: true) - XCTAssertTrue(indicatorView.isAnimating) - observer.send(value: false) - XCTAssertFalse(indicatorView.isAnimating) - } + let (pipeSignal, observer) = Signal.pipe() + indicatorView.reactive.isAnimating <~ SignalProducer(signal: pipeSignal) + + observer.send(value: true) + XCTAssertTrue(indicatorView.isAnimating) + observer.send(value: false) + XCTAssertFalse(indicatorView.isAnimating) + } } diff --git a/ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift b/ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift index a1ad6b6656..8a9d974c3f 100644 --- a/ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift +++ b/ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift @@ -9,27 +9,27 @@ import UIKit private let rac_swizzleToken: Void = { - let originalSelector = #selector(UIControl.sendAction(_:to:for:)) - let swizzledSelector = #selector(UIControl.rac_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 () + let originalSelector = #selector(UIControl.sendAction(_:to:for:)) + let swizzledSelector = #selector(UIControl.rac_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` @@ -38,18 +38,17 @@ private let rac_swizzleToken: Void = { /// in the future. To be able to test them, we're now using swizzling to manually invoke /// the pair target+action. extension UIControl { + override open class func initialize() { + if self !== UIControl.self { + return + } - override open class func initialize() { - if self !== UIControl.self { - return - } + _ = rac_swizzleToken + } - _ = rac_swizzleToken - } + // MARK: - Method Swizzling - // MARK: - Method Swizzling - - func rac_sendAction(_ action: Selector, to target: AnyObject?, forEvent event: UIEvent?) { - _ = target?.perform(action, with: self) - } + func rac_sendAction(_ action: Selector, to target: AnyObject?, forEvent event: UIEvent?) { + _ = target?.perform(action, with: self) + } } diff --git a/ReactiveCocoaTests/UIKit/UIControlTests.swift b/ReactiveCocoaTests/UIKit/UIControlTests.swift index 8e4cc7d2f4..ecb4cbc4b8 100644 --- a/ReactiveCocoaTests/UIKit/UIControlTests.swift +++ b/ReactiveCocoaTests/UIKit/UIControlTests.swift @@ -13,96 +13,95 @@ import XCTest import enum Result.NoError class UIControlTests: XCTestCase { - - weak var _control: UIControl? - - override func tearDown() { - XCTAssert(_control == nil, "Retain cycle detected in UIControl properties") - super.tearDown() - } - - func testEnabledPropertyDoesntCreateRetainCycle() { - let control = UIControl(frame: CGRect.zero) - _control = control - - control.reactive.isEnabled <~ SignalProducer(value: false) - XCTAssert(_control?.isEnabled == false) - } - - func testSelectedPropertyDoesntCreateRetainCycle() { - let control = UIControl(frame: CGRect.zero) - _control = control - - control.reactive.isSelected <~ SignalProducer(value: true) - XCTAssert(_control?.isSelected == true) - } - - func testHighlightedPropertyDoesntCreateRetainCycle() { - let control = UIControl(frame: CGRect.zero) - _control = control - - control.reactive.isHighlighted <~ SignalProducer(value: true) - XCTAssert(_control?.isHighlighted == true) - } - - func testEnabledProperty () { - let control = UIControl(frame: CGRect.zero) - control.isEnabled = false - - let (pipeSignal, observer) = Signal.pipe() - control.reactive.isEnabled <~ SignalProducer(signal: pipeSignal) - - observer.send(value: true) - XCTAssertTrue(control.isEnabled) - observer.send(value: false) - XCTAssertFalse(control.isEnabled) - } - - func testSelectedProperty() { - let control = UIControl(frame: CGRect.zero) - control.isSelected = false - - let (pipeSignal, observer) = Signal.pipe() - control.reactive.isSelected <~ SignalProducer(signal: pipeSignal) - - observer.send(value: true) - XCTAssertTrue(control.isSelected) - observer.send(value: false) - XCTAssertFalse(control.isSelected) - } - - func testHighlightedProperty() { - let control = UIControl(frame: CGRect.zero) - control.isHighlighted = false - - let (pipeSignal, observer) = Signal.pipe() - control.reactive.isHighlighted <~ SignalProducer(signal: pipeSignal) - - observer.send(value: true) - XCTAssertTrue(control.isHighlighted) - observer.send(value: false) - XCTAssertFalse(control.isHighlighted) - } - - func testEnabledAndSelectedProperty() { - let control = UIControl(frame: CGRect.zero) - control.isSelected = false - control.isEnabled = false - - let (pipeSignalSelected, observerSelected) = Signal.pipe() - let (pipeSignalEnabled, observerEnabled) = Signal.pipe() - control.reactive.isSelected <~ SignalProducer(signal: pipeSignalSelected) - control.reactive.isEnabled <~ SignalProducer(signal: pipeSignalEnabled) - - observerSelected.send(value: true) - observerEnabled.send(value: true) - XCTAssertTrue(control.isEnabled) - XCTAssertTrue(control.isSelected) - observerSelected.send(value: false) - XCTAssertTrue(control.isEnabled) - XCTAssertFalse(control.isSelected) - observerEnabled.send(value: false) - XCTAssertFalse(control.isEnabled) - XCTAssertFalse(control.isSelected) - } + weak var _control: UIControl? + + override func tearDown() { + XCTAssert(_control == nil, "Retain cycle detected in UIControl properties") + super.tearDown() + } + + func testEnabledPropertyDoesntCreateRetainCycle() { + let control = UIControl(frame: CGRect.zero) + _control = control + + control.reactive.isEnabled <~ SignalProducer(value: false) + XCTAssert(_control?.isEnabled == false) + } + + func testSelectedPropertyDoesntCreateRetainCycle() { + let control = UIControl(frame: CGRect.zero) + _control = control + + control.reactive.isSelected <~ SignalProducer(value: true) + XCTAssert(_control?.isSelected == true) + } + + func testHighlightedPropertyDoesntCreateRetainCycle() { + let control = UIControl(frame: CGRect.zero) + _control = control + + control.reactive.isHighlighted <~ SignalProducer(value: true) + XCTAssert(_control?.isHighlighted == true) + } + + func testEnabledProperty () { + let control = UIControl(frame: CGRect.zero) + control.isEnabled = false + + let (pipeSignal, observer) = Signal.pipe() + control.reactive.isEnabled <~ SignalProducer(signal: pipeSignal) + + observer.send(value: true) + XCTAssertTrue(control.isEnabled) + observer.send(value: false) + XCTAssertFalse(control.isEnabled) + } + + func testSelectedProperty() { + let control = UIControl(frame: CGRect.zero) + control.isSelected = false + + let (pipeSignal, observer) = Signal.pipe() + control.reactive.isSelected <~ SignalProducer(signal: pipeSignal) + + observer.send(value: true) + XCTAssertTrue(control.isSelected) + observer.send(value: false) + XCTAssertFalse(control.isSelected) + } + + func testHighlightedProperty() { + let control = UIControl(frame: CGRect.zero) + control.isHighlighted = false + + let (pipeSignal, observer) = Signal.pipe() + control.reactive.isHighlighted <~ SignalProducer(signal: pipeSignal) + + observer.send(value: true) + XCTAssertTrue(control.isHighlighted) + observer.send(value: false) + XCTAssertFalse(control.isHighlighted) + } + + func testEnabledAndSelectedProperty() { + let control = UIControl(frame: CGRect.zero) + control.isSelected = false + control.isEnabled = false + + let (pipeSignalSelected, observerSelected) = Signal.pipe() + let (pipeSignalEnabled, observerEnabled) = Signal.pipe() + control.reactive.isSelected <~ SignalProducer(signal: pipeSignalSelected) + control.reactive.isEnabled <~ SignalProducer(signal: pipeSignalEnabled) + + observerSelected.send(value: true) + observerEnabled.send(value: true) + XCTAssertTrue(control.isEnabled) + XCTAssertTrue(control.isSelected) + observerSelected.send(value: false) + XCTAssertTrue(control.isEnabled) + XCTAssertFalse(control.isSelected) + observerEnabled.send(value: false) + XCTAssertFalse(control.isEnabled) + XCTAssertFalse(control.isSelected) + } } diff --git a/ReactiveCocoaTests/UIKit/UIImageViewTests.swift b/ReactiveCocoaTests/UIKit/UIImageViewTests.swift index 7f1a5d6b68..57de8ca2ae 100644 --- a/ReactiveCocoaTests/UIKit/UIImageViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UIImageViewTests.swift @@ -13,61 +13,60 @@ import XCTest import enum Result.NoError class UIImageViewTests: XCTestCase { - - weak var _imageView: UIImageView? - - override func tearDown() { - XCTAssert(_imageView == nil, "Retain cycle detected in UIImageView properties") - super.tearDown() - } - - func testImagePropertyDoesntCreateRetainCycle() { - let imageView = UIImageView(frame: CGRect.zero) - _imageView = imageView - - let image = UIImage() - - imageView.reactive.image <~ SignalProducer(value: image) - XCTAssert(_imageView?.image == image) - } - - func testHighlightedImagePropertyDoesntCreateRetainCycle() { - let imageView = UIImageView(frame: CGRect.zero) - _imageView = imageView - - let image = UIImage() - - imageView.reactive.highlightedImage <~ SignalProducer(value: image) - XCTAssert(_imageView?.highlightedImage == image) - } - - func testImageProperty() { - let imageView = UIImageView(frame: CGRect.zero) - - let firstChange = UIImage() - let secondChange = UIImage() - - let (pipeSignal, observer) = Signal.pipe() - imageView.reactive.image <~ SignalProducer(signal: pipeSignal) - - observer.send(value: firstChange) - XCTAssertEqual(imageView.image, firstChange) - observer.send(value: secondChange) - XCTAssertEqual(imageView.image, secondChange) - } - - func testHighlightedImageProperty() { - let imageView = UIImageView(frame: CGRect.zero) - - let firstChange = UIImage() - let secondChange = UIImage() - - let (pipeSignal, observer) = Signal.pipe() - imageView.reactive.highlightedImage <~ SignalProducer(signal: pipeSignal) - - observer.send(value: firstChange) - XCTAssertEqual(imageView.highlightedImage, firstChange) - observer.send(value: secondChange) - XCTAssertEqual(imageView.highlightedImage, secondChange) - } + weak var _imageView: UIImageView? + + override func tearDown() { + XCTAssert(_imageView == nil, "Retain cycle detected in UIImageView properties") + super.tearDown() + } + + func testImagePropertyDoesntCreateRetainCycle() { + let imageView = UIImageView(frame: CGRect.zero) + _imageView = imageView + + let image = UIImage() + + imageView.reactive.image <~ SignalProducer(value: image) + XCTAssert(_imageView?.image == image) + } + + func testHighlightedImagePropertyDoesntCreateRetainCycle() { + let imageView = UIImageView(frame: CGRect.zero) + _imageView = imageView + + let image = UIImage() + + imageView.reactive.highlightedImage <~ SignalProducer(value: image) + XCTAssert(_imageView?.highlightedImage == image) + } + + func testImageProperty() { + let imageView = UIImageView(frame: CGRect.zero) + + let firstChange = UIImage() + let secondChange = UIImage() + + let (pipeSignal, observer) = Signal.pipe() + imageView.reactive.image <~ SignalProducer(signal: pipeSignal) + + observer.send(value: firstChange) + XCTAssertEqual(imageView.image, firstChange) + observer.send(value: secondChange) + XCTAssertEqual(imageView.image, secondChange) + } + + func testHighlightedImageProperty() { + let imageView = UIImageView(frame: CGRect.zero) + + let firstChange = UIImage() + let secondChange = UIImage() + + let (pipeSignal, observer) = Signal.pipe() + imageView.reactive.highlightedImage <~ SignalProducer(signal: pipeSignal) + + observer.send(value: firstChange) + XCTAssertEqual(imageView.highlightedImage, firstChange) + observer.send(value: secondChange) + XCTAssertEqual(imageView.highlightedImage, secondChange) + } } diff --git a/ReactiveCocoaTests/UIKit/UILabelTests.swift b/ReactiveCocoaTests/UIKit/UILabelTests.swift index afb630a597..ab96396921 100644 --- a/ReactiveCocoaTests/UIKit/UILabelTests.swift +++ b/ReactiveCocoaTests/UIKit/UILabelTests.swift @@ -13,77 +13,76 @@ import XCTest import enum Result.NoError class UILabelTests: XCTestCase { + weak var _label: UILabel? - weak var _label: UILabel? - - override func tearDown() { - XCTAssert(_label == nil, "Retain cycle detected in UILabel properties") - super.tearDown() - } - - func testTextPropertyDoesntCreateRetainCycle() { - let label = UILabel(frame: CGRect.zero) - _label = label - - label.reactive.text <~ SignalProducer(value: "Test") - XCTAssert(_label?.text == "Test") - } - - func testTextProperty() { - let firstChange = "first" - let secondChange = "second" - - let label = UILabel(frame: CGRect.zero) - label.text = "" - - let (pipeSignal, observer) = Signal.pipe() - label.reactive.text <~ SignalProducer(signal: pipeSignal) - - observer.send(value: firstChange) - XCTAssertEqual(label.text, firstChange) - observer.send(value: secondChange) - XCTAssertEqual(label.text, secondChange) - observer.send(value: nil) - XCTAssertNil(label.text) - } - - func testAttributedTextPropertyDoesntCreateRetainCycle() { - let label = UILabel(frame: CGRect.zero) - _label = label - - label.reactive.attributedText <~ SignalProducer(value: NSAttributedString(string: "Test")) - XCTAssert(_label?.attributedText?.string == "Test") - } - - func testAttributedTextProperty() { - let firstChange = NSAttributedString(string: "first") - let secondChange = NSAttributedString(string: "second") - - let label = UILabel(frame: CGRect.zero) - label.attributedText = NSAttributedString(string: "") - - let (pipeSignal, observer) = Signal.pipe() - label.reactive.attributedText <~ SignalProducer(signal: pipeSignal) - - observer.send(value: firstChange) - XCTAssertEqual(label.attributedText, firstChange) - observer.send(value: secondChange) - XCTAssertEqual(label.attributedText, secondChange) - } - - func testTextColorProperty() { - let firstChange = UIColor.red - let secondChange = UIColor.black - - let label = UILabel(frame: CGRect.zero) - - let (pipeSignal, observer) = Signal.pipe() - label.textColor = UIColor.black - label.reactive.textColor <~ SignalProducer(signal: pipeSignal) - - observer.send(value: firstChange) - XCTAssertEqual(label.textColor, firstChange) - observer.send(value: secondChange) - XCTAssertEqual(label.textColor, secondChange) - } + override func tearDown() { + XCTAssert(_label == nil, "Retain cycle detected in UILabel properties") + super.tearDown() + } + + func testTextPropertyDoesntCreateRetainCycle() { + let label = UILabel(frame: CGRect.zero) + _label = label + + label.reactive.text <~ SignalProducer(value: "Test") + XCTAssert(_label?.text == "Test") + } + + func testTextProperty() { + let firstChange = "first" + let secondChange = "second" + + let label = UILabel(frame: CGRect.zero) + label.text = "" + + let (pipeSignal, observer) = Signal.pipe() + label.reactive.text <~ SignalProducer(signal: pipeSignal) + + observer.send(value: firstChange) + XCTAssertEqual(label.text, firstChange) + observer.send(value: secondChange) + XCTAssertEqual(label.text, secondChange) + observer.send(value: nil) + XCTAssertNil(label.text) + } + + func testAttributedTextPropertyDoesntCreateRetainCycle() { + let label = UILabel(frame: CGRect.zero) + _label = label + + label.reactive.attributedText <~ SignalProducer(value: NSAttributedString(string: "Test")) + XCTAssert(_label?.attributedText?.string == "Test") + } + + func testAttributedTextProperty() { + let firstChange = NSAttributedString(string: "first") + let secondChange = NSAttributedString(string: "second") + + let label = UILabel(frame: CGRect.zero) + label.attributedText = NSAttributedString(string: "") + + let (pipeSignal, observer) = Signal.pipe() + label.reactive.attributedText <~ SignalProducer(signal: pipeSignal) + + observer.send(value: firstChange) + XCTAssertEqual(label.attributedText, firstChange) + observer.send(value: secondChange) + XCTAssertEqual(label.attributedText, secondChange) + } + + func testTextColorProperty() { + let firstChange = UIColor.red + let secondChange = UIColor.black + + let label = UILabel(frame: CGRect.zero) + + let (pipeSignal, observer) = Signal.pipe() + label.textColor = UIColor.black + label.reactive.textColor <~ SignalProducer(signal: pipeSignal) + + observer.send(value: firstChange) + XCTAssertEqual(label.textColor, firstChange) + observer.send(value: secondChange) + XCTAssertEqual(label.textColor, secondChange) + } } diff --git a/ReactiveCocoaTests/UIKit/UIProgressViewTests.swift b/ReactiveCocoaTests/UIKit/UIProgressViewTests.swift index bfaf6d5414..9ed7b52e76 100644 --- a/ReactiveCocoaTests/UIKit/UIProgressViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UIProgressViewTests.swift @@ -13,34 +13,34 @@ import XCTest import enum Result.NoError class UIProgressViewTests: XCTestCase { - weak var _progressView: UIProgressView? - - override func tearDown() { - XCTAssert(_progressView == nil, "Retain cycle detected in UIProgressView properties") - super.tearDown() - } - - func testProgressPropertyDoesntCreateRetainCycle() { - let progressView = UIProgressView(frame: CGRect.zero) - _progressView = progressView - - progressView.reactive.progress <~ SignalProducer(value: 0.5) - XCTAssert(_progressView?.progress == 0.5) - } - - func testProgressProperty() { - let firstChange: Float = 0.5 - let secondChange: Float = 0.0 - - let progressView = UIProgressView(frame: CGRect.zero) - progressView.progress = 1.0 - - let (pipeSignal, observer) = Signal.pipe() - progressView.reactive.progress <~ SignalProducer(signal: pipeSignal) - - observer.send(value: firstChange) - XCTAssertEqual(progressView.progress, firstChange) - observer.send(value: secondChange) - XCTAssertEqual(progressView.progress, secondChange) - } + weak var _progressView: UIProgressView? + + override func tearDown() { + XCTAssert(_progressView == nil, "Retain cycle detected in UIProgressView properties") + super.tearDown() + } + + func testProgressPropertyDoesntCreateRetainCycle() { + let progressView = UIProgressView(frame: CGRect.zero) + _progressView = progressView + + progressView.reactive.progress <~ SignalProducer(value: 0.5) + XCTAssert(_progressView?.progress == 0.5) + } + + func testProgressProperty() { + let firstChange: Float = 0.5 + let secondChange: Float = 0.0 + + let progressView = UIProgressView(frame: CGRect.zero) + progressView.progress = 1.0 + + let (pipeSignal, observer) = Signal.pipe() + progressView.reactive.progress <~ SignalProducer(signal: pipeSignal) + + observer.send(value: firstChange) + XCTAssertEqual(progressView.progress, firstChange) + observer.send(value: secondChange) + XCTAssertEqual(progressView.progress, secondChange) + } } diff --git a/ReactiveCocoaTests/UIKit/UIViewTests.swift b/ReactiveCocoaTests/UIKit/UIViewTests.swift index b16b23cecb..8e75790255 100644 --- a/ReactiveCocoaTests/UIKit/UIViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UIViewTests.swift @@ -13,69 +13,68 @@ import XCTest import enum Result.NoError class UIViewTests: XCTestCase { - - weak var _view: UIView? - - override func tearDown() { - XCTAssert(_view == nil, "Retain cycle detected in UIView properties") - super.tearDown() - } - - func testAlphaPropertyDoesntCreateRetainCycle() { - let view = UIView(frame: CGRect.zero) - _view = view - - view.reactive.alpha <~ SignalProducer(value: 0.5) - XCTAssertEqualWithAccuracy(_view!.alpha, 0.5, accuracy: 0.01) - } - - func testHiddenPropertyDoesntCreateRetainCycle() { - let view = UIView(frame: CGRect.zero) - _view = view - - view.reactive.isHidden <~ SignalProducer(value: true) - XCTAssert(_view?.isHidden == true) - } - - func testHiddenProperty() { - let view = UIView(frame: CGRect.zero) - view.isHidden = true - - let (pipeSignal, observer) = Signal.pipe() - view.reactive.isHidden <~ SignalProducer(signal: pipeSignal) - - observer.send(value: true) - XCTAssertTrue(view.isHidden) - observer.send(value: false) - XCTAssertFalse(view.isHidden) - } - - func testAlphaProperty() { - let view = UIView(frame: CGRect.zero) - view.alpha = 0.0 - - let firstChange = CGFloat(0.5) - let secondChange = CGFloat(0.7) - - let (pipeSignal, observer) = Signal.pipe() - view.reactive.alpha <~ SignalProducer(signal: pipeSignal) - - observer.send(value: firstChange) - XCTAssertEqualWithAccuracy(view.alpha, firstChange, accuracy: 0.01) - observer.send(value: secondChange) - XCTAssertEqualWithAccuracy(view.alpha, secondChange, accuracy: 0.01) - } - - func testUserInteractionEnabledProperty() { - let view = UIView(frame: CGRect.zero) - view.isUserInteractionEnabled = true - - let (pipeSignal, observer) = Signal.pipe() - view.reactive.isUserInteractionEnabled <~ SignalProducer(signal: pipeSignal) - - observer.send(value: true) - XCTAssertTrue(view.isUserInteractionEnabled) - observer.send(value: false) - XCTAssertFalse(view.isUserInteractionEnabled) - } + weak var _view: UIView? + + override func tearDown() { + XCTAssert(_view == nil, "Retain cycle detected in UIView properties") + super.tearDown() + } + + func testAlphaPropertyDoesntCreateRetainCycle() { + let view = UIView(frame: CGRect.zero) + _view = view + + view.reactive.alpha <~ SignalProducer(value: 0.5) + XCTAssertEqualWithAccuracy(_view!.alpha, 0.5, accuracy: 0.01) + } + + func testHiddenPropertyDoesntCreateRetainCycle() { + let view = UIView(frame: CGRect.zero) + _view = view + + view.reactive.isHidden <~ SignalProducer(value: true) + XCTAssert(_view?.isHidden == true) + } + + func testHiddenProperty() { + let view = UIView(frame: CGRect.zero) + view.isHidden = true + + let (pipeSignal, observer) = Signal.pipe() + view.reactive.isHidden <~ SignalProducer(signal: pipeSignal) + + observer.send(value: true) + XCTAssertTrue(view.isHidden) + observer.send(value: false) + XCTAssertFalse(view.isHidden) + } + + func testAlphaProperty() { + let view = UIView(frame: CGRect.zero) + view.alpha = 0.0 + + let firstChange = CGFloat(0.5) + let secondChange = CGFloat(0.7) + + let (pipeSignal, observer) = Signal.pipe() + view.reactive.alpha <~ SignalProducer(signal: pipeSignal) + + observer.send(value: firstChange) + XCTAssertEqualWithAccuracy(view.alpha, firstChange, accuracy: 0.01) + observer.send(value: secondChange) + XCTAssertEqualWithAccuracy(view.alpha, secondChange, accuracy: 0.01) + } + + func testUserInteractionEnabledProperty() { + let view = UIView(frame: CGRect.zero) + view.isUserInteractionEnabled = true + + let (pipeSignal, observer) = Signal.pipe() + view.reactive.isUserInteractionEnabled <~ SignalProducer(signal: pipeSignal) + + observer.send(value: true) + XCTAssertTrue(view.isUserInteractionEnabled) + observer.send(value: false) + XCTAssertFalse(view.isUserInteractionEnabled) + } } From 0448e120d7dfc58d8a02f113872bc308dae46eba Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 7 Oct 2016 12:39:25 +0200 Subject: [PATCH 0420/1028] Reverted the weak behavior of `CocoaAction`. Fixed its retain cycle test case. --- ReactiveCocoa/CocoaAction.swift | 10 ++++------ ReactiveCocoaTests/CocoaActionSpec.swift | 11 +++++------ 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/ReactiveCocoa/CocoaAction.swift b/ReactiveCocoa/CocoaAction.swift index 13c137f446..4134401d9c 100644 --- a/ReactiveCocoa/CocoaAction.swift +++ b/ReactiveCocoa/CocoaAction.swift @@ -38,12 +38,10 @@ public final class CocoaAction: NSObject { /// `(UISwitch) -> (Bool)` to reflect whether a provided /// switch is currently on. public init(_ action: Action, _ inputTransform: @escaping (Control) -> Input) { - _execute = { [weak action] input in - if let action = action { - let control = input as! Control - let producer = action.apply(inputTransform(control)) - producer.start() - } + _execute = { input in + let control = input as! Control + let producer = action.apply(inputTransform(control)) + producer.start() } isEnabled = action.isEnabled diff --git a/ReactiveCocoaTests/CocoaActionSpec.swift b/ReactiveCocoaTests/CocoaActionSpec.swift index bf21d87877..85eb5322e6 100644 --- a/ReactiveCocoaTests/CocoaActionSpec.swift +++ b/ReactiveCocoaTests/CocoaActionSpec.swift @@ -67,15 +67,14 @@ class CocoaActionSpec: QuickSpec { } context("lifetime") { - it("unsafeCocoaAction should not create a retain cycle") { - weak var weakAction: Action? - var action: Action? = Action { _ in - return SignalProducer(value: 42) - } - weakAction = action + it("CocoaAction should not create a retain cycle") { + weak var weakAction = action expect(weakAction).notTo(beNil()) action = nil + expect(weakAction).toNot(beNil()) + + cocoaAction = nil expect(weakAction).to(beNil()) } } From 90582dc054825f46ce2598f3ffedb5c12e772226 Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 7 Oct 2016 12:48:47 +0200 Subject: [PATCH 0421/1028] Fixed the watchOS target settings. --- ReactiveCocoa.xcodeproj/project.pbxproj | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index fcf9bc975c..40a6c49c8e 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -79,7 +79,6 @@ 9A1D06591D93EA7E00ACF44C /* UIViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06351D93EA7E00ACF44C /* UIViewTests.swift */; }; 9A1D065B1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; 9A1D065C1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; - 9A1D065D1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; 9A1D065E1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; 9A1D06771D9415FF00ACF44C /* RACObjCRuntimeUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06761D9415FB00ACF44C /* RACObjCRuntimeUtilities.m */; }; 9A1D06781D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06761D9415FB00ACF44C /* RACObjCRuntimeUtilities.m */; }; @@ -88,6 +87,7 @@ 9A1E72BA1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; 9A1E72BB1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; 9A1E72BC1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; + 9A9DFEE51DA7B5500039EE1B /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; 9AD0F06B1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; 9AD0F06C1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; @@ -961,11 +961,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9A9DFEE51DA7B5500039EE1B /* NSObject+Intercepting.swift in Sources */, 9A1D06791D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9ADE4A931DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, - 9A1D065D1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, 9ADE4A7E1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, - 9A1D065D1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, CD0C45E01CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9AF0EA771D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, 9A1D05E21D93E99100ACF44C /* Association.swift in Sources */, From ceaa858971b1d2c79c8c12a82e4b76ea59e56779 Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 7 Oct 2016 15:40:21 +0200 Subject: [PATCH 0422/1028] Hid `UIControl.setAction`. --- ReactiveCocoa/UIKit/UIControl.swift | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index 4ba0571709..5b2cf7c21f 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -29,15 +29,7 @@ extension Reactive where Base: UIControl { initial: { _ in Atomic(nil) }) } - public var action: CocoaAction? { - return associatedAction.value?.action - } - - public var controlEventsForAction: UIControlEvents? { - return associatedAction.value?.controlEvents - } - - public func setAction(_ action: CocoaAction?, for controlEvents: UIControlEvents) { + internal func setAction(_ action: CocoaAction?, for controlEvents: UIControlEvents) { associatedAction.modify { associatedAction in if let old = associatedAction { old.disposable.dispose() From db270e060234da24c76d35f871b6c4862e089ade Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 7 Oct 2016 15:42:31 +0200 Subject: [PATCH 0423/1028] Renamed `Control` of `CocoaAction` to `Sender`. --- ReactiveCocoa/CocoaAction.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ReactiveCocoa/CocoaAction.swift b/ReactiveCocoa/CocoaAction.swift index 4134401d9c..82b6c13424 100644 --- a/ReactiveCocoa/CocoaAction.swift +++ b/ReactiveCocoa/CocoaAction.swift @@ -6,10 +6,10 @@ import enum Result.NoError /// `UIControl`), with KVO, or with Cocoa Bindings. /// /// - important: The `Action` is weakly referenced. -public final class CocoaAction: NSObject { +public final class CocoaAction: NSObject { /// The selector for message senders. public static var selector: Selector { - return #selector(CocoaAction.execute(_:)) + return #selector(CocoaAction.execute(_:)) } /// Whether the action is enabled. @@ -37,9 +37,9 @@ public final class CocoaAction: NSObject { /// action and returns a value (e.g. /// `(UISwitch) -> (Bool)` to reflect whether a provided /// switch is currently on. - public init(_ action: Action, _ inputTransform: @escaping (Control) -> Input) { + public init(_ action: Action, _ inputTransform: @escaping (Sender) -> Input) { _execute = { input in - let control = input as! Control + let control = input as! Sender let producer = action.apply(inputTransform(control)) producer.start() } From 42244cf04cd5bf84e7348f07d8c0257237259618 Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 7 Oct 2016 15:55:47 +0200 Subject: [PATCH 0424/1028] Renamed `*texts` to `*textValues`. --- ReactiveCocoa/UIKit/UITextField.swift | 4 ++-- ReactiveCocoa/UIKit/UITextView.swift | 4 ++-- ReactiveCocoaTests/UIKit/UITextFieldTests.swift | 4 ++-- ReactiveCocoaTests/UIKit/UITextViewTests.swift | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ReactiveCocoa/UIKit/UITextField.swift b/ReactiveCocoa/UIKit/UITextField.swift index f76c0f1d01..691d88abc7 100644 --- a/ReactiveCocoa/UIKit/UITextField.swift +++ b/ReactiveCocoa/UIKit/UITextField.swift @@ -15,11 +15,11 @@ extension Reactive where Base: UITextField { return makeBindingTarget { $0.text = $1 } } - public var texts: Signal { + public var textValues: Signal { return trigger(for: .editingDidEnd).map { [unowned base] in base.text } } - public var continuousTexts: Signal { + public var continuousTextValues: Signal { return trigger(for: .editingChanged).map { [unowned base] in base.text } } } diff --git a/ReactiveCocoa/UIKit/UITextView.swift b/ReactiveCocoa/UIKit/UITextView.swift index 52260e5d99..4aaee0a563 100644 --- a/ReactiveCocoa/UIKit/UITextView.swift +++ b/ReactiveCocoa/UIKit/UITextView.swift @@ -16,7 +16,7 @@ extension Reactive where Base: UITextView { return makeBindingTarget { $0.text = $1 } } - public var texts: Signal { + public var textValues: Signal { var signal: Signal! NotificationCenter.default @@ -29,7 +29,7 @@ extension Reactive where Base: UITextView { return signal } - public var continuousTexts: Signal { + public var continuousTextValues: Signal { var signal: Signal! NotificationCenter.default diff --git a/ReactiveCocoaTests/UIKit/UITextFieldTests.swift b/ReactiveCocoaTests/UIKit/UITextFieldTests.swift index a22e391919..c395f20452 100644 --- a/ReactiveCocoaTests/UIKit/UITextFieldTests.swift +++ b/ReactiveCocoaTests/UIKit/UITextFieldTests.swift @@ -19,7 +19,7 @@ class UITextFieldTests: XCTestCase { let textField = UITextField(frame: CGRect.zero) textField.text = "Test" - textField.reactive.texts.observeValues { text in + textField.reactive.textValues.observeValues { text in XCTAssertEqual(text, textField.text) expectation.fulfill() } @@ -34,7 +34,7 @@ class UITextFieldTests: XCTestCase { let textField = UITextField(frame: CGRect.zero) textField.text = "Test" - textField.reactive.continuousTexts.observeValues { text in + textField.reactive.continuousTextValues.observeValues { text in XCTAssertEqual(text, textField.text) expectation.fulfill() } diff --git a/ReactiveCocoaTests/UIKit/UITextViewTests.swift b/ReactiveCocoaTests/UIKit/UITextViewTests.swift index f256f79b35..3bf26c0c12 100644 --- a/ReactiveCocoaTests/UIKit/UITextViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UITextViewTests.swift @@ -19,7 +19,7 @@ class UITextViewTests: XCTestCase { let textView = UITextView(frame: CGRect.zero) textView.text = "Test" - textView.reactive.texts.observeValues { text in + textView.reactive.textValues.observeValues { text in XCTAssertEqual(text, textView.text) expectation.fulfill() } @@ -34,7 +34,7 @@ class UITextViewTests: XCTestCase { let textView = UITextView(frame: CGRect.zero) textView.text = "Test" - textView.reactive.continuousTexts.observeValues { text in + textView.reactive.continuousTextValues.observeValues { text in XCTAssertEqual(text, textView.text) expectation.fulfill() } From 8201da2f29f8026eccc268bd9231353365b0cab4 Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 7 Oct 2016 16:00:30 +0200 Subject: [PATCH 0425/1028] Added `NSControl.reactive.isEnabled`. --- ReactiveCocoa/AppKit/NSControl.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ReactiveCocoa/AppKit/NSControl.swift b/ReactiveCocoa/AppKit/NSControl.swift index 8b8f7446f2..a530018628 100644 --- a/ReactiveCocoa/AppKit/NSControl.swift +++ b/ReactiveCocoa/AppKit/NSControl.swift @@ -11,6 +11,10 @@ import enum Result.NoError import AppKit extension Reactive where Base: NSControl { + public var isEnabled: BindingTarget { + return makeBindingTarget { $0.isEnabled = $1 } + } + public var attributedString: BindingTarget { return makeBindingTarget { $0.attributedStringValue = $1 } } From 0093b650427485566323bd036a47fd2ebb510cd1 Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 7 Oct 2016 16:50:54 +0200 Subject: [PATCH 0426/1028] Reused `associatedObject(_:key:initial:) in `signal(for:setup:)`. --- ReactiveCocoa/NSObject+Intercepting.swift | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 175160dcd2..eccdb46f7a 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -14,15 +14,7 @@ extension Reactive where Base: NSObject { objc_sync_enter(self) defer { objc_sync_exit(self) } - let map: NSMutableDictionary = { - if let map = objc_getAssociatedObject(self, &mapKey) as? NSMutableDictionary { - return map - } else { - let map = NSMutableDictionary() - objc_setAssociatedObject(self, &mapKey, map, .OBJC_ASSOCIATION_RETAIN) - return map - } - }() + let map = associatedObject(base, key: &mapKey) { _ in NSMutableDictionary() } let selectorName = String(describing: selector) as NSString if let signal = map.object(forKey: selectorName) as? Signal { From 7e369e5d19bff97080a08bdc4dbfbc1cc628b456 Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 7 Oct 2016 17:24:34 +0200 Subject: [PATCH 0427/1028] Cleaned up the association API. --- ReactiveCocoa.xcodeproj/project.pbxproj | 28 +++++--- ReactiveCocoa/AppKit/NSControl.swift | 4 +- ReactiveCocoa/Association.swift | 76 -------------------- ReactiveCocoa/NSObject+Association.swift | 30 ++++++++ ReactiveCocoa/NSObject+BindingTarget.swift | 4 +- ReactiveCocoa/NSObject+Intercepting.swift | 2 +- ReactiveCocoa/UIKit/UIBarButtonItem.swift | 6 +- ReactiveCocoa/UIKit/UIControl.swift | 6 +- ReactiveCocoaTests/AssociationSpec.swift | 80 ++++++++++++++++++++++ 9 files changed, 133 insertions(+), 103 deletions(-) delete mode 100644 ReactiveCocoa/Association.swift create mode 100644 ReactiveCocoa/NSObject+Association.swift create mode 100644 ReactiveCocoaTests/AssociationSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 40a6c49c8e..b1fad36a6b 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -21,10 +21,10 @@ 7DFBED211CDB8D8300EE435B /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 7DFBED281CDB8DE300EE435B /* DynamicPropertySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */; }; 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */; }; - 9A1D05E01D93E99100ACF44C /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* Association.swift */; }; - 9A1D05E11D93E99100ACF44C /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* Association.swift */; }; - 9A1D05E21D93E99100ACF44C /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* Association.swift */; }; - 9A1D05E31D93E99100ACF44C /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* Association.swift */; }; + 9A1D05E01D93E99100ACF44C /* NSObject+Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */; }; + 9A1D05E11D93E99100ACF44C /* NSObject+Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */; }; + 9A1D05E21D93E99100ACF44C /* NSObject+Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */; }; + 9A1D05E31D93E99100ACF44C /* NSObject+Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */; }; 9A1D05FF1D93EA0000ACF44C /* UIActivityIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05EC1D93E9F100ACF44C /* UIActivityIndicatorView.swift */; }; 9A1D06001D93EA0000ACF44C /* UIBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05ED1D93E9F100ACF44C /* UIBarButtonItem.swift */; }; 9A1D06011D93EA0000ACF44C /* UIBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05EE1D93E9F100ACF44C /* UIBarItem.swift */; }; @@ -88,6 +88,9 @@ 9A1E72BB1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; 9A1E72BC1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; 9A9DFEE51DA7B5500039EE1B /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; + 9A9DFEE91DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; + 9A9DFEEA1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; + 9A9DFEEB1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; 9AD0F06B1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; 9AD0F06C1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; @@ -214,7 +217,7 @@ 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Framework.xcconfig"; sourceTree = ""; }; 57A4D2471BA13F9700F7D4B1 /* tvOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-StaticLibrary.xcconfig"; sourceTree = ""; }; 7DFBED031CDB8C9500EE435B /* ReactiveCocoaTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveCocoaTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 9A1D05DF1D93E99100ACF44C /* Association.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Association.swift; sourceTree = ""; }; + 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Association.swift"; sourceTree = ""; }; 9A1D05EC1D93E9F100ACF44C /* UIActivityIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorView.swift; sourceTree = ""; }; 9A1D05ED1D93E9F100ACF44C /* UIBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIBarButtonItem.swift; sourceTree = ""; }; 9A1D05EE1D93E9F100ACF44C /* UIBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIBarItem.swift; sourceTree = ""; }; @@ -247,6 +250,7 @@ 9A1D06751D9415FB00ACF44C /* RACObjCRuntimeUtilities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RACObjCRuntimeUtilities.h; sourceTree = ""; }; 9A1D06761D9415FB00ACF44C /* RACObjCRuntimeUtilities.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RACObjCRuntimeUtilities.m; sourceTree = ""; }; 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyValueObservingSpec.swift; sourceTree = ""; }; + 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssociationSpec.swift; sourceTree = ""; }; 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+KeyValueObserving.swift"; sourceTree = ""; }; 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaAction.swift; sourceTree = ""; }; 9ADE4A881DA6D206005C2AC8 /* NSControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSControl.swift; sourceTree = ""; }; @@ -510,7 +514,7 @@ 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */, 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */, 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */, - 9A1D05DF1D93E99100ACF44C /* Association.swift */, + 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */, 9A1D05E91D93E9F100ACF44C /* AppKit */, 9A1D05EB1D93E9F100ACF44C /* UIKit */, D04725ED19E49ED7006002AA /* Supporting Files */, @@ -532,6 +536,7 @@ children = ( 9ADE4A8C1DA6D94C005C2AC8 /* AppKit */, 9A1D06231D93EA7E00ACF44C /* UIKit */, + 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */, CD8401821CEE8ED7009F0ABF /* CocoaActionSpec.swift */, D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */, B696FB801A7640C00075236D /* TestError.swift */, @@ -918,7 +923,7 @@ 9A1D06211D93EA0100ACF44C /* UIView.swift in Sources */, 9A1D067A1D94160100ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9ADE4A941DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, - 9A1D05E31D93E99100ACF44C /* Association.swift in Sources */, + 9A1D05E31D93E99100ACF44C /* NSObject+Association.swift in Sources */, 9A1D06161D93EA0100ACF44C /* UIControl.swift in Sources */, 9A1D06141D93EA0100ACF44C /* UIButton.swift in Sources */, 9A1D065E1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, @@ -939,6 +944,7 @@ files = ( 9A1D06591D93EA7E00ACF44C /* UIViewTests.swift in Sources */, 7DFBED281CDB8DE300EE435B /* DynamicPropertySpec.swift in Sources */, + 9A9DFEEB1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */, CD8401851CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, 9A1E72BC1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, 9A1D06451D93EA7E00ACF44C /* UIImageViewTests.swift in Sources */, @@ -967,7 +973,7 @@ 9ADE4A7E1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, CD0C45E01CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9AF0EA771D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, - 9A1D05E21D93E99100ACF44C /* Association.swift in Sources */, + 9A1D05E21D93E99100ACF44C /* NSObject+Association.swift in Sources */, 4A0E11011D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, 9AD0F06C1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, ); @@ -980,7 +986,7 @@ CD0C45DE1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9A1D06771D9415FF00ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, - 9A1D05E01D93E99100ACF44C /* Association.swift in Sources */, + 9A1D05E01D93E99100ACF44C /* NSObject+Association.swift in Sources */, 9ADE4A7C1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, 9ADE4A961DA6F018005C2AC8 /* NSTextField.swift in Sources */, 9ADE4A891DA6D206005C2AC8 /* NSControl.swift in Sources */, @@ -1000,6 +1006,7 @@ B696FB811A7640C00075236D /* TestError.swift in Sources */, BFA6B94D1A7604D400C846D1 /* SignalProducerNimbleMatchers.swift in Sources */, CD8401831CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, + 9A9DFEE91DA7EFB60039EE1B /* AssociationSpec.swift in Sources */, 9A1E72BA1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1017,7 +1024,7 @@ 9A1D06781D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9A1D060F1D93EA0000ACF44C /* UIView.swift in Sources */, 9A1D06051D93EA0000ACF44C /* UIDatePicker.swift in Sources */, - 9A1D05E11D93E99100ACF44C /* Association.swift in Sources */, + 9A1D05E11D93E99100ACF44C /* NSObject+Association.swift in Sources */, 9A1D06041D93EA0000ACF44C /* UIControl.swift in Sources */, 9A1D06021D93EA0000ACF44C /* UIButton.swift in Sources */, 9A1D060A1D93EA0000ACF44C /* UISwitch.swift in Sources */, @@ -1050,6 +1057,7 @@ 9A1D063E1D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift in Sources */, 9A1D06361D93EA7E00ACF44C /* UIActivityIndicatorViewTests.swift in Sources */, 9A1D06461D93EA7E00ACF44C /* UILabelTests.swift in Sources */, + 9A9DFEEA1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */, 9A1D06421D93EA7E00ACF44C /* UIDatePickerTests.swift in Sources */, 9A1D06381D93EA7E00ACF44C /* UIBarButtonItemTests.swift in Sources */, 9A1E72BB1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, diff --git a/ReactiveCocoa/AppKit/NSControl.swift b/ReactiveCocoa/AppKit/NSControl.swift index a530018628..a56027acf9 100644 --- a/ReactiveCocoa/AppKit/NSControl.swift +++ b/ReactiveCocoa/AppKit/NSControl.swift @@ -80,7 +80,7 @@ extension Reactive where Base: NSControl { } private var trigger: Signal<(), NoError> { - let receiver: ActionMessageReceiver = associatedObject(base, key: &controlTriggerKey) { base in + let receiver: ActionMessageReceiver = associatedValue { base in let receiver = ActionMessageReceiver() base.target = receiver base.action = #selector(ActionMessageReceiver.receive) @@ -109,5 +109,3 @@ private class ActionMessageReceiver: NSObject { observer.send(value: ()) } } - -var controlTriggerKey = 0 diff --git a/ReactiveCocoa/Association.swift b/ReactiveCocoa/Association.swift deleted file mode 100644 index 2aad38ee39..0000000000 --- a/ReactiveCocoa/Association.swift +++ /dev/null @@ -1,76 +0,0 @@ -// -// Association.swift -// Rex -// -// Created by Neil Pankey on 6/19/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - -import ReactiveSwift - -/// Attaches a `MutableProperty` value to the `host` object using KVC to get the initial -/// value and write subsequent updates from the property's producer. Note that `keyPath` -/// is a `StaticString` because it's pointer value is used as key value when associating -/// the property. -/// -/// This can be used as an alternative to `DynamicProperty` for creating strongly typed -/// bindings on Cocoa objects. -internal 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: String(describing: keyPath)) - } - return associatedProperty(host, key: keyPath.utf8Start, initial: initial, setter: setter) -} - -/// Attaches a `MutableProperty` value to the `host` object using KVC to get the initial -/// value and write subsequent updates from the property's producer. Note that `keyPath` -/// is a `StaticString` because it's pointer value is used as key value when associating -/// the property. -/// -/// This can be used as an alternative to `DynamicProperty` for creating strongly typed -/// bindings on Cocoa objects. -internal func associatedProperty(_ host: AnyObject, keyPath: StaticString, placeholder: () -> T) -> MutableProperty { - let setter: (AnyObject, T) -> () = { host, newValue in - host.setValue(newValue, forKeyPath: String(describing: keyPath)) - } - return associatedProperty(host, key: keyPath.utf8Start, initial: { host in - host.value(forKeyPath: String(describing: keyPath)) as? T ?? placeholder() - }, setter: setter) -} - -/// Attaches a `MutableProperty` value to the `host` object under `key`. The property is -/// initialized with the result of `initial`. Changes on the property's producer are -/// monitored and written to `setter`. -/// -/// This can be used as an alternative to `DynamicProperty` for creating strongly typed -/// bindings on Cocoa objects. -internal 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)) - - setUp(property) - - property.producer.startWithValues { [weak host] next in - if let host = host { - setter(host, next) - } - } - - return property - } -} - -/// On first use attaches the object returned from `initial` to the `host` object using -/// `key` via `objc_setAssociatedObject`. On subsequent usage, returns said object via -/// `objc_getAssociatedObject`. -internal func associatedObject(_ host: Host, key: UnsafeRawPointer, initial: (Host) -> T) -> T { - var value = objc_getAssociatedObject(host, key) as? T - if value == nil { - value = initial(host) - objc_setAssociatedObject(host, key, value, .OBJC_ASSOCIATION_RETAIN) - } - return value! -} diff --git a/ReactiveCocoa/NSObject+Association.swift b/ReactiveCocoa/NSObject+Association.swift new file mode 100644 index 0000000000..51444c6936 --- /dev/null +++ b/ReactiveCocoa/NSObject+Association.swift @@ -0,0 +1,30 @@ +// +// Association.swift +// ReactiveCocoa +// +// Created by Neil Pankey on 6/19/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import ReactiveSwift + +extension Reactive where Base: NSObject { + /// Retrieve the associated value for the specified key. If the value does not + /// exist, `initial` would be called and the returned value would be + /// associated subsequently. + /// + /// - parameters: + /// - key: An optional key to differentiate different values. + /// - initial: The action that supples an initial value. + /// + /// - returns: + /// The associated value for the specified key. + internal func associatedValue(forKey key: StaticString = #function, initial: (Base) -> T) -> T { + var value = objc_getAssociatedObject(base, key.utf8Start) as? T + if value == nil { + value = initial(base) + objc_setAssociatedObject(base, key.utf8Start, value, .OBJC_ASSOCIATION_RETAIN) + } + return value! + } +} diff --git a/ReactiveCocoa/NSObject+BindingTarget.swift b/ReactiveCocoa/NSObject+BindingTarget.swift index b1ac07a7a3..d80db04960 100644 --- a/ReactiveCocoa/NSObject+BindingTarget.swift +++ b/ReactiveCocoa/NSObject+BindingTarget.swift @@ -14,7 +14,7 @@ extension Reactive where Base: NSObject { /// - returns: /// A binding target that holds no strong references to `base`. internal func makeBindingTarget(action: @escaping (Base, U) -> Void) -> BindingTarget { - let scheduler = associatedObject(base, key: &schedulerKey, initial: { _ in UIScheduler() }) + let scheduler = associatedValue { _ in UIScheduler() } return BindingTarget(on: scheduler, lifetime: lifetime) { [weak base] value in if let base = base { @@ -23,5 +23,3 @@ extension Reactive where Base: NSObject { } } } - -private var schedulerKey = 0 diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index eccdb46f7a..1710019a90 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -14,7 +14,7 @@ extension Reactive where Base: NSObject { objc_sync_enter(self) defer { objc_sync_exit(self) } - let map = associatedObject(base, key: &mapKey) { _ in NSMutableDictionary() } + let map = associatedValue { _ in NSMutableDictionary() } let selectorName = String(describing: selector) as NSString if let signal = map.object(forKey: selectorName) as? Signal { diff --git a/ReactiveCocoa/UIKit/UIBarButtonItem.swift b/ReactiveCocoa/UIKit/UIBarButtonItem.swift index 143f644962..478a4ed6db 100644 --- a/ReactiveCocoa/UIKit/UIBarButtonItem.swift +++ b/ReactiveCocoa/UIKit/UIBarButtonItem.swift @@ -11,9 +11,7 @@ import UIKit extension Reactive where Base: UIBarButtonItem { private var associatedAction: Atomic?> { - return associatedObject(base, - key: &associatedActionKey, - initial: { _ in Atomic(nil) }) + return associatedValue { _ in Atomic(nil) } } public var pressed: CocoaAction? { @@ -28,5 +26,3 @@ extension Reactive where Base: UIBarButtonItem { } } } - -private var associatedActionKey = 0 diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index 5b2cf7c21f..69068f5fd6 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -24,9 +24,7 @@ private class UnsafeControlReceiver: NSObject { extension Reactive where Base: UIControl { internal var associatedAction: Atomic<(action: CocoaAction, controlEvents: UIControlEvents, disposable: Disposable)?> { - return associatedObject(base, - key: &associatedActionKey, - initial: { _ in Atomic(nil) }) + return associatedValue { _ in Atomic(nil) } } internal func setAction(_ action: CocoaAction?, for controlEvents: UIControlEvents) { @@ -85,5 +83,3 @@ extension Reactive where Base: UIControl { return makeBindingTarget { $0.isHighlighted = $1 } } } - -private var associatedActionKey = 0 diff --git a/ReactiveCocoaTests/AssociationSpec.swift b/ReactiveCocoaTests/AssociationSpec.swift new file mode 100644 index 0000000000..54a1350db7 --- /dev/null +++ b/ReactiveCocoaTests/AssociationSpec.swift @@ -0,0 +1,80 @@ +import ReactiveSwift +import Result +import Nimble +import Quick +@testable import ReactiveCocoa + +class AssociationSpec: QuickSpec { + override func spec() { + it("should create and retrieve the same object") { + let object = NSObject() + let token = NSObject() + + var counter = 0 + + func retrieve() -> NSObject { + return object.reactive.associatedValue { _ in + counter += 1 + return token + } + } + + expect(counter) == 0 + + let firstResult = retrieve() + expect(counter) == 1 + + let secondResult = retrieve() + expect(counter) == 1 + + expect(firstResult).to(beIdenticalTo(secondResult)) + expect(firstResult).to(beIdenticalTo(token)) + } + + it("should support multiple keys per object") { + let object = NSObject() + let token = NSObject() + let token2 = NSObject() + + var counter = 0 + var counter2 = 0 + + + func retrieve() -> NSObject { + return object.reactive.associatedValue { _ in + counter += 1 + return token + } + } + + func retrieve2() -> NSObject { + return object.reactive.associatedValue(forKey: "customKey") { _ in + counter2 += 1 + return token2 + } + } + + expect(counter) == 0 + + let firstResult = retrieve() + expect(counter) == 1 + + let secondResult = retrieve() + expect(counter) == 1 + + expect(firstResult).to(beIdenticalTo(secondResult)) + expect(firstResult).to(beIdenticalTo(token)) + + expect(counter2) == 0 + + let thirdResult = retrieve2() + expect(counter2) == 1 + + let forthResult = retrieve2() + expect(counter2) == 1 + + expect(thirdResult).to(beIdenticalTo(forthResult)) + expect(thirdResult).to(beIdenticalTo(token2)) + } + } +} From 02f7670fea17eafd0c99e5c4eb7c08e500531e95 Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 7 Oct 2016 17:26:51 +0200 Subject: [PATCH 0428/1028] Fixed a typo of the property name in `Reactive`. --- ReactiveCocoa/AppKit/NSControl.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa/AppKit/NSControl.swift b/ReactiveCocoa/AppKit/NSControl.swift index a56027acf9..6dbc3f752d 100644 --- a/ReactiveCocoa/AppKit/NSControl.swift +++ b/ReactiveCocoa/AppKit/NSControl.swift @@ -15,7 +15,7 @@ extension Reactive where Base: NSControl { return makeBindingTarget { $0.isEnabled = $1 } } - public var attributedString: BindingTarget { + public var attributedStringValue: BindingTarget { return makeBindingTarget { $0.attributedStringValue = $1 } } From ec69f254865db3563d024264291318864596c424 Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 7 Oct 2016 17:58:06 +0200 Subject: [PATCH 0429/1028] Enabled state binding in `Reactive.pressed`. --- ReactiveCocoa/UIKit/UIBarButtonItem.swift | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/ReactiveCocoa/UIKit/UIBarButtonItem.swift b/ReactiveCocoa/UIKit/UIBarButtonItem.swift index 478a4ed6db..f113212a34 100644 --- a/ReactiveCocoa/UIKit/UIBarButtonItem.swift +++ b/ReactiveCocoa/UIKit/UIBarButtonItem.swift @@ -10,19 +10,25 @@ import ReactiveSwift import UIKit extension Reactive where Base: UIBarButtonItem { - private var associatedAction: Atomic?> { + private var associatedAction: Atomic<(action: CocoaAction, disposable: Disposable)?> { return associatedValue { _ in Atomic(nil) } } public var pressed: CocoaAction? { get { - return associatedAction.value + return associatedAction.value?.action } nonmutating set { - associatedAction.value = newValue base.target = newValue base.action = newValue.map { _ in CocoaAction.selector } + + associatedAction + .swap(newValue.map { action in + let disposable = isEnabled <~ action.isEnabled + return (action, disposable) + })? + .disposable.dispose() } } } From 76efe5a31e4e034d9ece5e6d600d21b47867b755 Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 7 Oct 2016 18:03:02 +0200 Subject: [PATCH 0430/1028] Added `Reactive.title(for:)`. --- ReactiveCocoa/UIKit/UIButton.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ReactiveCocoa/UIKit/UIButton.swift b/ReactiveCocoa/UIKit/UIButton.swift index 21804669f8..9c96f2dbd1 100644 --- a/ReactiveCocoa/UIKit/UIButton.swift +++ b/ReactiveCocoa/UIKit/UIButton.swift @@ -29,6 +29,11 @@ extension Reactive where Base: UIButton { public var title: BindingTarget { return makeBindingTarget { $0.setTitle($1, for: .normal) } } + + /// Sets the title of the button for the specified state. + public func title(for state: UIControlState) -> BindingTarget { + return makeBindingTarget { $0.setTitle($1, for: state) } + } } private var pressedKey: UInt8 = 0 From 3bdad2f511e4e8d6077e76eb64b5e98907e0740f Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 7 Oct 2016 18:14:07 +0200 Subject: [PATCH 0431/1028] Updated the API documentation. --- ReactiveCocoa/AppKit/NSControl.swift | 19 ++++++++++++++++ ReactiveCocoa/AppKit/NSTextField.swift | 1 + ReactiveCocoa/NSObject+BindingTarget.swift | 8 +++---- ReactiveCocoa/NSObject+Intercepting.swift | 22 +++++++++++++++++-- .../NSObject+KeyValueObserving.swift | 2 +- ReactiveCocoa/NSObject+Lifetime.swift | 2 +- .../UIKit/UIActivityIndicatorView.swift | 13 ++--------- ReactiveCocoa/UIKit/UIBarButtonItem.swift | 3 +++ ReactiveCocoa/UIKit/UIBarItem.swift | 2 +- ReactiveCocoa/UIKit/UIButton.swift | 5 +++-- ReactiveCocoa/UIKit/UIControl.swift | 22 ++++++++++++++++--- ReactiveCocoa/UIKit/UIDatePicker.swift | 3 ++- ReactiveCocoa/UIKit/UIImageView.swift | 4 ++-- ReactiveCocoa/UIKit/UILabel.swift | 6 ++--- ReactiveCocoa/UIKit/UIProgressView.swift | 2 +- ReactiveCocoa/UIKit/UISegmentedControl.swift | 3 ++- ReactiveCocoa/UIKit/UISwitch.swift | 3 ++- ReactiveCocoa/UIKit/UITextField.swift | 3 +++ ReactiveCocoa/UIKit/UITextView.swift | 4 +++- ReactiveCocoa/UIKit/UIView.swift | 7 +++--- 20 files changed, 95 insertions(+), 39 deletions(-) diff --git a/ReactiveCocoa/AppKit/NSControl.swift b/ReactiveCocoa/AppKit/NSControl.swift index 6dbc3f752d..a33aed35ec 100644 --- a/ReactiveCocoa/AppKit/NSControl.swift +++ b/ReactiveCocoa/AppKit/NSControl.swift @@ -11,74 +11,93 @@ import enum Result.NoError import AppKit extension Reactive where Base: NSControl { + /// Sets whether the control is enabled. public var isEnabled: BindingTarget { return makeBindingTarget { $0.isEnabled = $1 } } + /// Sets the value of the control with an `NSAttributedString`. public var attributedStringValue: BindingTarget { return makeBindingTarget { $0.attributedStringValue = $1 } } + /// A signal of values in `NSAttributedString`, emitted by the control. public var attributedStringValues: Signal { return trigger.map { [unowned base] in base.attributedStringValue } } + /// Sets the value of the control with a `Bool`. public var boolValue: BindingTarget { return makeBindingTarget { $0.integerValue = $1 ? NSOnState : NSOffState } } + /// A signal of values in `Bool`, emitted by the control. public var boolValues: Signal { return trigger.map { [unowned base] in base.integerValue == NSOffState ? false : true } } + /// Sets the value of the control with a `Double`. public var doubleValue: BindingTarget { return makeBindingTarget { $0.doubleValue = $1 } } + /// A signal of values in `Double`, emitted by the control. public var doubleValues: Signal { return trigger.map { [unowned base] in base.doubleValue } } + /// Sets the value of the control with a `Float`. public var floatValue: BindingTarget { return makeBindingTarget { $0.floatValue = $1 } } + /// A signal of values in `Float`, emitted by the control. public var floatValues: Signal { return trigger.map { [unowned base] in base.floatValue } } + /// Sets the value of the control with an `Int32`. public var intValue: BindingTarget { return makeBindingTarget { $0.intValue = $1 } } + /// A signal of values in `Int32`, emitted by the control. public var intValues: Signal { return trigger.map { [unowned base] in base.intValue } } + /// Sets the value of the control with an `Int`. public var integerValue: BindingTarget { return makeBindingTarget { $0.integerValue = $1 } } + /// A signal of values in `Int`, emitted by the control. public var integerValues: Signal { return trigger.map { [unowned base] in base.integerValue } } + /// Sets the value of the control. public var objectValue: BindingTarget { return makeBindingTarget { $0.objectValue = $1 } } + /// A signal of values in `Any?`, emitted by the control. public var objectValues: Signal { return trigger.map { [unowned base] in base.objectValue } } + /// Sets the value of the control with a `String`. public var stringValue: BindingTarget { return makeBindingTarget { $0.stringValue = $1 } } + /// A signal of values in `String`, emitted by the control. public var stringValues: Signal { return trigger.map { [unowned base] in base.stringValue } } + /// A trigger signal that sends a `next` event for every action messages + /// received from the control, and completes when the control deinitializes. private var trigger: Signal<(), NoError> { let receiver: ActionMessageReceiver = associatedValue { base in let receiver = ActionMessageReceiver() diff --git a/ReactiveCocoa/AppKit/NSTextField.swift b/ReactiveCocoa/AppKit/NSTextField.swift index 0588f00316..cd4b9dd3eb 100644 --- a/ReactiveCocoa/AppKit/NSTextField.swift +++ b/ReactiveCocoa/AppKit/NSTextField.swift @@ -11,6 +11,7 @@ import AppKit import enum Result.NoError extension Reactive where Base: NSTextField { + /// A signal of values in `String` from the text field upon any changes. public var continuousStringValues: Signal { var signal: Signal! diff --git a/ReactiveCocoa/NSObject+BindingTarget.swift b/ReactiveCocoa/NSObject+BindingTarget.swift index d80db04960..8a820832d8 100644 --- a/ReactiveCocoa/NSObject+BindingTarget.swift +++ b/ReactiveCocoa/NSObject+BindingTarget.swift @@ -2,9 +2,9 @@ import Foundation import ReactiveSwift extension Reactive where Base: NSObject { - /// Creates a binding target which uses the lifetime of `base`, and weakly - /// references `base` so that the supplied `action` is triggered only if - /// `base` has not deinitialized. + /// Creates a binding target which uses the lifetime of the object, and weakly + /// references the object so that the supplied `action` is triggered only if + /// the object has not deinitialized. /// /// - important: The binding target is bound to the main queue. /// @@ -12,7 +12,7 @@ extension Reactive where Base: NSObject { /// - action: The action to consume values from the bindings. /// /// - returns: - /// A binding target that holds no strong references to `base`. + /// A binding target that holds no strong references to the object. internal func makeBindingTarget(action: @escaping (Base, U) -> Void) -> BindingTarget { let scheduler = associatedValue { _ in UIScheduler() } diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 1710019a90..ffc668509f 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -4,13 +4,31 @@ import enum Result.NoError import ReactiveCocoaPrivate extension Reactive where Base: NSObject { + /// Create a signal which sends a `next` event at the end of every invocation + /// of `selector` on the object. + /// + /// - parameters: + /// - selector: The selector to observe. + /// + /// - returns: + /// A trigger signal. public func trigger(for selector: Selector) -> Signal<(), NoError> { return signal(for: selector) { observer in return { _ in observer.send(value: ()) } } } - private func signal(for selector: Selector, action: (Observer) -> rac_receiver_t) -> Signal { + /// Create a signal which sends a `next` event at the end of every invocation + /// of `selector` on the object. + /// + /// - parameters: + /// - selector: The selector to observe. + /// - setup: The setup closure of how received events in the runtime are + /// piped to the returned signal. + /// + /// - returns: + /// A trigger signal. + private func signal(for selector: Selector, setup: (Observer) -> rac_receiver_t) -> Signal { objc_sync_enter(self) defer { objc_sync_exit(self) } @@ -22,7 +40,7 @@ extension Reactive where Base: NSObject { } let (signal, observer) = Signal.pipe() - let action = action(observer) + let action = setup(observer) let isSuccessful = RACRegisterBlockForSelector(base, selector, nil, action) assert(isSuccessful) diff --git a/ReactiveCocoa/NSObject+KeyValueObserving.swift b/ReactiveCocoa/NSObject+KeyValueObserving.swift index 9707fcb598..911707afd0 100644 --- a/ReactiveCocoa/NSObject+KeyValueObserving.swift +++ b/ReactiveCocoa/NSObject+KeyValueObserving.swift @@ -6,7 +6,7 @@ extension Reactive where Base: NSObject { /// Create a producer which sends the current value and all the subsequent /// changes of the property specified by the key path. /// - /// The producer completes when `self` deinitializes. + /// The producer completes when the object deinitializes. /// /// - parameters: /// - keyPath: The key path of the property to be observed. diff --git a/ReactiveCocoa/NSObject+Lifetime.swift b/ReactiveCocoa/NSObject+Lifetime.swift index c5d4e52342..d4c2a07362 100644 --- a/ReactiveCocoa/NSObject+Lifetime.swift +++ b/ReactiveCocoa/NSObject+Lifetime.swift @@ -5,7 +5,7 @@ private var lifetimeKey: UInt8 = 0 private var lifetimeTokenKey: UInt8 = 0 extension Reactive where Base: NSObject { - /// Returns a lifetime that ends when the receiver is deallocated. + /// Returns a lifetime that ends when the object is deallocated. @nonobjc public var lifetime: Lifetime { objc_sync_enter(base) defer { objc_sync_exit(base) } diff --git a/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift b/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift index a314928827..f6dd5c5533 100644 --- a/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift +++ b/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift @@ -10,17 +10,8 @@ import ReactiveSwift import UIKit extension Reactive where Base: UIActivityIndicatorView { - - /// Wraps an indicator's `isAnimating()` state in a bindable property. - /// Setting a new value to the property would call `startAnimating()` or - /// `stopAnimating()` depending on the value. + /// Sets whether the activity indicator should be animating. public var isAnimating: BindingTarget { - return makeBindingTarget { _self, isAnimating in - if isAnimating { - _self.startAnimating() - } else { - _self.stopAnimating() - } - } + return makeBindingTarget { $1 ? $0.startAnimating() : $0.stopAnimating() } } } diff --git a/ReactiveCocoa/UIKit/UIBarButtonItem.swift b/ReactiveCocoa/UIKit/UIBarButtonItem.swift index f113212a34..0c34817ee8 100644 --- a/ReactiveCocoa/UIKit/UIBarButtonItem.swift +++ b/ReactiveCocoa/UIKit/UIBarButtonItem.swift @@ -10,10 +10,13 @@ import ReactiveSwift import UIKit extension Reactive where Base: UIBarButtonItem { + /// The current associated action of `self`. private var associatedAction: Atomic<(action: CocoaAction, disposable: Disposable)?> { return associatedValue { _ in Atomic(nil) } } + /// The action to be triggered when the button is pressed. It also controls + /// the enabled state of the button. public var pressed: CocoaAction? { get { return associatedAction.value?.action diff --git a/ReactiveCocoa/UIKit/UIBarItem.swift b/ReactiveCocoa/UIKit/UIBarItem.swift index 1442887a34..22a965522e 100644 --- a/ReactiveCocoa/UIKit/UIBarItem.swift +++ b/ReactiveCocoa/UIKit/UIBarItem.swift @@ -10,7 +10,7 @@ import ReactiveSwift import UIKit extension Reactive where Base: UIBarItem { - /// Wraps a UIBarItem's `enabled` state in a bindable property. + /// Sets whether the bar item is enabled. public var isEnabled: BindingTarget { return makeBindingTarget { $0.isEnabled = $1 } } diff --git a/ReactiveCocoa/UIKit/UIButton.swift b/ReactiveCocoa/UIKit/UIButton.swift index 9c96f2dbd1..492e163989 100644 --- a/ReactiveCocoa/UIKit/UIButton.swift +++ b/ReactiveCocoa/UIKit/UIButton.swift @@ -10,6 +10,8 @@ import ReactiveSwift import UIKit extension Reactive where Base: UIButton { + /// The action to be triggered when the button is pressed. It also controls + /// the enabled state of the button. public var pressed: CocoaAction? { get { return associatedAction.withValue { info in @@ -24,8 +26,7 @@ extension Reactive where Base: UIButton { } } - /// Wraps a button's `title` text in a bindable property. Note that this only applies - /// to `UIControlState.Normal`. + /// Sets the title of the button for its normal state. public var title: BindingTarget { return makeBindingTarget { $0.setTitle($1, for: .normal) } } diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index 69068f5fd6..c5eb37db49 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -23,10 +23,18 @@ private class UnsafeControlReceiver: NSObject { } extension Reactive where Base: UIControl { + /// The current associated action of `self`, with its registered event mask + /// and its disposable. internal var associatedAction: Atomic<(action: CocoaAction, controlEvents: UIControlEvents, disposable: Disposable)?> { return associatedValue { _ in Atomic(nil) } } + /// Set the associated action of `self` to `action`, and register it for the + /// control events specified by `controlEvents`. + /// + /// - parameters: + /// - action: The action to be associated. + /// - controlEvents: The control event mask. internal func setAction(_ action: CocoaAction?, for controlEvents: UIControlEvents) { associatedAction.modify { associatedAction in if let old = associatedAction { @@ -49,6 +57,14 @@ extension Reactive where Base: UIControl { } } + /// Create a signal which sends a `next` event for each of the specified + /// control events. + /// + /// - parameters: + /// - controlEvents: The control event mask. + /// + /// - returns: + /// A trigger signal. public func trigger(for controlEvents: UIControlEvents) -> Signal<(), NoError> { return Signal { observer in let receiver = UnsafeControlReceiver(observer: observer) @@ -68,17 +84,17 @@ extension Reactive where Base: UIControl { } } - /// Wraps a control's `enabled` state in a bindable property. + /// Sets whether the control is enabled. public var isEnabled: BindingTarget { return makeBindingTarget { $0.isEnabled = $1 } } - /// Wraps a control's `selected` state in a bindable property. + /// Sets whether the control is selected. public var isSelected: BindingTarget { return makeBindingTarget { $0.isSelected = $1 } } - /// Wraps a control's `highlighted` state in a bindable property. + /// Sets whether the control is highlighted. public var isHighlighted: BindingTarget { return makeBindingTarget { $0.isHighlighted = $1 } } diff --git a/ReactiveCocoa/UIKit/UIDatePicker.swift b/ReactiveCocoa/UIKit/UIDatePicker.swift index af0e8953bb..885af7d311 100644 --- a/ReactiveCocoa/UIKit/UIDatePicker.swift +++ b/ReactiveCocoa/UIKit/UIDatePicker.swift @@ -11,11 +11,12 @@ import enum Result.NoError import UIKit extension Reactive where Base: UIDatePicker { - // Wraps a datePicker's `date` value in a bindable property. + /// Sets the date of the date picker. public var date: BindingTarget { return makeBindingTarget { $0.date = $1 } } + /// A signal of dates emitted by the date picker. public var dates: Signal { return trigger(for: .valueChanged).map { [unowned base] in base.date } } diff --git a/ReactiveCocoa/UIKit/UIImageView.swift b/ReactiveCocoa/UIKit/UIImageView.swift index 3c214581a3..2ab49b742f 100644 --- a/ReactiveCocoa/UIKit/UIImageView.swift +++ b/ReactiveCocoa/UIKit/UIImageView.swift @@ -10,12 +10,12 @@ import ReactiveSwift import UIKit extension Reactive where Base: UIImageView { - /// Wraps a imageView's `image` value in a bindable property. + /// Sets the image of the image view. public var image: BindingTarget { return makeBindingTarget { $0.image = $1 } } - /// Wraps a imageView's `highlightedImage` value in a bindable property. + /// Sets the image of the image view for its highlighted state. public var highlightedImage: BindingTarget { return makeBindingTarget { $0.highlightedImage = $1 } } diff --git a/ReactiveCocoa/UIKit/UILabel.swift b/ReactiveCocoa/UIKit/UILabel.swift index 4a6c2f8918..f00087e34c 100644 --- a/ReactiveCocoa/UIKit/UILabel.swift +++ b/ReactiveCocoa/UIKit/UILabel.swift @@ -10,17 +10,17 @@ import ReactiveSwift import UIKit extension Reactive where Base: UILabel { - /// Wraps a label's `text` value in a bindable property. + /// Sets the text of the label. public var text: BindingTarget { return makeBindingTarget { $0.text = $1 } } - /// Wraps a label's `attributedText` value in a bindable property. + /// Sets the attributed text of the label. public var attributedText: BindingTarget { return makeBindingTarget { $0.attributedText = $1 } } - /// Wraps a label's `textColor` value in a bindable property. + /// Sets the color of the text of the label. public var textColor: BindingTarget { return makeBindingTarget { $0.textColor = $1 } } diff --git a/ReactiveCocoa/UIKit/UIProgressView.swift b/ReactiveCocoa/UIKit/UIProgressView.swift index 06dd847132..456891949b 100644 --- a/ReactiveCocoa/UIKit/UIProgressView.swift +++ b/ReactiveCocoa/UIKit/UIProgressView.swift @@ -10,7 +10,7 @@ import ReactiveSwift import UIKit extension Reactive where Base: UIProgressView { - /// Wraps a progressView's `progress` value in a bindable property. + /// Sets the reative progress to be reflected by the progress view. public var progress: BindingTarget { return makeBindingTarget { $0.progress = $1 } } diff --git a/ReactiveCocoa/UIKit/UISegmentedControl.swift b/ReactiveCocoa/UIKit/UISegmentedControl.swift index 687c4beb83..b4cb8cadc3 100644 --- a/ReactiveCocoa/UIKit/UISegmentedControl.swift +++ b/ReactiveCocoa/UIKit/UISegmentedControl.swift @@ -11,11 +11,12 @@ import enum Result.NoError import UIKit extension Reactive where Base: UISegmentedControl { - /// Wraps a segmentedControls `selectedSegmentIndex` state in a bindable property. + /// Changes the selected segment of the segmented control. public var selectedSegmentIndex: BindingTarget { return makeBindingTarget { $0.selectedSegmentIndex = $1 } } + /// A signal of indexes of selections emitted by the segmented control. public var selectedSegmentIndexes: Signal { return trigger(for: .valueChanged).map { [unowned base] in base.selectedSegmentIndex } } diff --git a/ReactiveCocoa/UIKit/UISwitch.swift b/ReactiveCocoa/UIKit/UISwitch.swift index 1b3a8bf099..f07fb44d09 100644 --- a/ReactiveCocoa/UIKit/UISwitch.swift +++ b/ReactiveCocoa/UIKit/UISwitch.swift @@ -11,11 +11,12 @@ import enum Result.NoError import UIKit extension Reactive where Base: UISwitch { - /// Wraps a switch's `on` value in a bindable property. + /// Sets the on-off state of the switch. public var isOn: BindingTarget { return makeBindingTarget { $0.isOn = $1 } } + /// A signal of on-off states in `Bool` emitted by the switch. public var isOnValues: Signal { return trigger(for: .valueChanged).map { [unowned base] in base.isOn } } diff --git a/ReactiveCocoa/UIKit/UITextField.swift b/ReactiveCocoa/UIKit/UITextField.swift index 691d88abc7..49b6c853b1 100644 --- a/ReactiveCocoa/UIKit/UITextField.swift +++ b/ReactiveCocoa/UIKit/UITextField.swift @@ -11,14 +11,17 @@ import enum Result.NoError import UIKit extension Reactive where Base: UITextField { + /// Sets the text of the text field. public var text: BindingTarget { return makeBindingTarget { $0.text = $1 } } + /// A signal of text values emitted by the text field upon end of editing. public var textValues: Signal { return trigger(for: .editingDidEnd).map { [unowned base] in base.text } } + /// A signal of text values emitted by the text field upon any changes. public var continuousTextValues: Signal { return trigger(for: .editingChanged).map { [unowned base] in base.text } } diff --git a/ReactiveCocoa/UIKit/UITextView.swift b/ReactiveCocoa/UIKit/UITextView.swift index 4aaee0a563..9c185e6e36 100644 --- a/ReactiveCocoa/UIKit/UITextView.swift +++ b/ReactiveCocoa/UIKit/UITextView.swift @@ -11,11 +11,12 @@ import UIKit import enum Result.NoError extension Reactive where Base: UITextView { - /// Sends the textView's string value whenever it changes. + /// Sets the text of the text view. public var text: BindingTarget { return makeBindingTarget { $0.text = $1 } } + /// A signal of text values emitted by the text view upon end of editing. public var textValues: Signal { var signal: Signal! @@ -29,6 +30,7 @@ extension Reactive where Base: UITextView { return signal } + /// A signal of text values emitted by the text view upon any changes. public var continuousTextValues: Signal { var signal: Signal! diff --git a/ReactiveCocoa/UIKit/UIView.swift b/ReactiveCocoa/UIKit/UIView.swift index d7a80d5cf0..d531131493 100644 --- a/ReactiveCocoa/UIKit/UIView.swift +++ b/ReactiveCocoa/UIKit/UIView.swift @@ -10,18 +10,17 @@ import ReactiveSwift import UIKit extension Reactive where Base: UIView { - /// Wraps a view's `alpha` value in a bindable property. + /// Sets the alpha value of the view. public var alpha: BindingTarget { return makeBindingTarget { $0.alpha = $1 } } - /// Wraps a view's `hidden` state in a bindable property. + /// Sets whether the view is hidden. public var isHidden: BindingTarget { return makeBindingTarget { $0.isHidden = $1 } } - - /// Wraps a view's `userInteractionEnabled` state in a bindable property. + /// Sets whether the view accepts user interactions. public var isUserInteractionEnabled: BindingTarget { return makeBindingTarget { $0.isUserInteractionEnabled = $1 } } From 4d710585d0b3119c265b7baef44e1fc449e61f94 Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 7 Oct 2016 18:17:58 +0200 Subject: [PATCH 0432/1028] Removed all file headers. --- ReactiveCocoa/AppKit/NSControl.swift | 8 -------- ReactiveCocoa/AppKit/NSTextField.swift | 8 -------- ReactiveCocoa/NSObject+Association.swift | 8 -------- ReactiveCocoa/RACObjCRuntimeUtilities.m | 8 -------- ReactiveCocoa/ReactiveCocoa.h | 8 -------- ReactiveCocoa/UIKit/UIActivityIndicatorView.swift | 8 -------- ReactiveCocoa/UIKit/UIBarButtonItem.swift | 8 -------- ReactiveCocoa/UIKit/UIBarItem.swift | 8 -------- ReactiveCocoa/UIKit/UIButton.swift | 8 -------- ReactiveCocoa/UIKit/UIControl.swift | 8 -------- ReactiveCocoa/UIKit/UIDatePicker.swift | 8 -------- ReactiveCocoa/UIKit/UIImageView.swift | 8 -------- ReactiveCocoa/UIKit/UILabel.swift | 8 -------- ReactiveCocoa/UIKit/UIProgressView.swift | 8 -------- ReactiveCocoa/UIKit/UISegmentedControl.swift | 8 -------- ReactiveCocoa/UIKit/UISwitch.swift | 8 -------- ReactiveCocoa/UIKit/UITextField.swift | 8 -------- ReactiveCocoa/UIKit/UITextView.swift | 8 -------- ReactiveCocoa/UIKit/UIView.swift | 8 -------- ReactiveCocoaTests/DynamicPropertySpec.swift | 8 -------- ReactiveCocoaTests/SignalProducerNimbleMatchers.swift | 9 --------- ReactiveCocoaTests/TestError.swift | 8 -------- .../UIKit/UIActivityIndicatorViewTests.swift | 8 -------- ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift | 8 -------- ReactiveCocoaTests/UIKit/UIButtonTests.swift | 8 -------- .../UIControl+EnableSendActionsForControlEvents.swift | 8 -------- ReactiveCocoaTests/UIKit/UIControlTests.swift | 8 -------- ReactiveCocoaTests/UIKit/UIDatePickerTests.swift | 8 -------- ReactiveCocoaTests/UIKit/UIImageViewTests.swift | 8 -------- ReactiveCocoaTests/UIKit/UILabelTests.swift | 8 -------- ReactiveCocoaTests/UIKit/UIProgressViewTests.swift | 8 -------- ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift | 8 -------- ReactiveCocoaTests/UIKit/UISwitchTests.swift | 8 -------- ReactiveCocoaTests/UIKit/UITextFieldTests.swift | 8 -------- ReactiveCocoaTests/UIKit/UITextViewTests.swift | 8 -------- ReactiveCocoaTests/UIKit/UIViewTests.swift | 8 -------- 36 files changed, 289 deletions(-) diff --git a/ReactiveCocoa/AppKit/NSControl.swift b/ReactiveCocoa/AppKit/NSControl.swift index a33aed35ec..f52cf7675d 100644 --- a/ReactiveCocoa/AppKit/NSControl.swift +++ b/ReactiveCocoa/AppKit/NSControl.swift @@ -1,11 +1,3 @@ -// -// NSControl.swift -// ReactiveCocoa -// -// Created by Yury Lapitsky on 7/8/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - import ReactiveSwift import enum Result.NoError import AppKit diff --git a/ReactiveCocoa/AppKit/NSTextField.swift b/ReactiveCocoa/AppKit/NSTextField.swift index cd4b9dd3eb..18166adbc5 100644 --- a/ReactiveCocoa/AppKit/NSTextField.swift +++ b/ReactiveCocoa/AppKit/NSTextField.swift @@ -1,11 +1,3 @@ -// -// NSTextField.swift -// Rex -// -// Created by Yury Lapitsky on 7/8/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - import ReactiveSwift import AppKit import enum Result.NoError diff --git a/ReactiveCocoa/NSObject+Association.swift b/ReactiveCocoa/NSObject+Association.swift index 51444c6936..b81df2ac3d 100644 --- a/ReactiveCocoa/NSObject+Association.swift +++ b/ReactiveCocoa/NSObject+Association.swift @@ -1,11 +1,3 @@ -// -// Association.swift -// ReactiveCocoa -// -// Created by Neil Pankey on 6/19/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - import ReactiveSwift extension Reactive where Base: NSObject { diff --git a/ReactiveCocoa/RACObjCRuntimeUtilities.m b/ReactiveCocoa/RACObjCRuntimeUtilities.m index 8da3347d48..2c0220eb82 100644 --- a/ReactiveCocoa/RACObjCRuntimeUtilities.m +++ b/ReactiveCocoa/RACObjCRuntimeUtilities.m @@ -1,11 +1,3 @@ -// -// RACObjCRuntimeUtilities.m -// ReactiveCocoa -// -// Created by Josh Abernathy on 3/18/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - #import "RACObjCRuntimeUtilities.h" #import #import diff --git a/ReactiveCocoa/ReactiveCocoa.h b/ReactiveCocoa/ReactiveCocoa.h index 733fd418ab..841a3109d1 100644 --- a/ReactiveCocoa/ReactiveCocoa.h +++ b/ReactiveCocoa/ReactiveCocoa.h @@ -1,11 +1,3 @@ -// -// ReactiveCocoa.h -// ReactiveCocoa -// -// Created by Josh Abernathy on 3/5/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - #import //! Project version number for ReactiveCocoa. diff --git a/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift b/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift index f6dd5c5533..6855318683 100644 --- a/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift +++ b/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift @@ -1,11 +1,3 @@ -// -// UIActivityIndicatorView.swift -// Rex -// -// Created by Evgeny Kazakov on 02/07/16. -// Copyright © 2016 Neil Pankey. All rights reserved. -// - import ReactiveSwift import UIKit diff --git a/ReactiveCocoa/UIKit/UIBarButtonItem.swift b/ReactiveCocoa/UIKit/UIBarButtonItem.swift index 0c34817ee8..04e199ae75 100644 --- a/ReactiveCocoa/UIKit/UIBarButtonItem.swift +++ b/ReactiveCocoa/UIKit/UIBarButtonItem.swift @@ -1,11 +1,3 @@ -// -// UIBarButtonItem.swift -// Rex -// -// Created by Bjarke Hesthaven Søndergaard on 24/07/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - import ReactiveSwift import UIKit diff --git a/ReactiveCocoa/UIKit/UIBarItem.swift b/ReactiveCocoa/UIKit/UIBarItem.swift index 22a965522e..a1ca833efe 100644 --- a/ReactiveCocoa/UIKit/UIBarItem.swift +++ b/ReactiveCocoa/UIKit/UIBarItem.swift @@ -1,11 +1,3 @@ -// -// UIBarItem.swift -// Rex -// -// Created by Bjarke Hesthaven Søndergaard on 24/07/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - import ReactiveSwift import UIKit diff --git a/ReactiveCocoa/UIKit/UIButton.swift b/ReactiveCocoa/UIKit/UIButton.swift index 492e163989..dcbebbb16c 100644 --- a/ReactiveCocoa/UIKit/UIButton.swift +++ b/ReactiveCocoa/UIKit/UIButton.swift @@ -1,11 +1,3 @@ -// -// UIButton.swift -// Rex -// -// Created by Neil Pankey on 6/19/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - import ReactiveSwift import UIKit diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index c5eb37db49..f5fa28967a 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -1,11 +1,3 @@ -// -// UIView.swift -// Rex -// -// Created by Neil Pankey on 6/19/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - import ReactiveSwift import UIKit import enum Result.NoError diff --git a/ReactiveCocoa/UIKit/UIDatePicker.swift b/ReactiveCocoa/UIKit/UIDatePicker.swift index 885af7d311..c17cb235bd 100644 --- a/ReactiveCocoa/UIKit/UIDatePicker.swift +++ b/ReactiveCocoa/UIKit/UIDatePicker.swift @@ -1,11 +1,3 @@ -// -// UIDatePicker.swift -// Rex -// -// Created by Guido Marucci Blas on 3/25/16. -// Copyright © 2016 Neil Pankey. All rights reserved. -// - import ReactiveSwift import enum Result.NoError import UIKit diff --git a/ReactiveCocoa/UIKit/UIImageView.swift b/ReactiveCocoa/UIKit/UIImageView.swift index 2ab49b742f..4beedf6a1b 100644 --- a/ReactiveCocoa/UIKit/UIImageView.swift +++ b/ReactiveCocoa/UIKit/UIImageView.swift @@ -1,11 +1,3 @@ -// -// UIImageView.swift -// Rex -// -// Created by Andy Jacobs on 21/10/15. -// Copyright © 2015 Neil Pankey. All rights reserved. -// - import ReactiveSwift import UIKit diff --git a/ReactiveCocoa/UIKit/UILabel.swift b/ReactiveCocoa/UIKit/UILabel.swift index f00087e34c..5e21d19340 100644 --- a/ReactiveCocoa/UIKit/UILabel.swift +++ b/ReactiveCocoa/UIKit/UILabel.swift @@ -1,11 +1,3 @@ -// -// UILabel.swift -// Rex -// -// Created by Neil Pankey on 6/19/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - import ReactiveSwift import UIKit diff --git a/ReactiveCocoa/UIKit/UIProgressView.swift b/ReactiveCocoa/UIKit/UIProgressView.swift index 456891949b..6397ad4bc6 100644 --- a/ReactiveCocoa/UIKit/UIProgressView.swift +++ b/ReactiveCocoa/UIKit/UIProgressView.swift @@ -1,11 +1,3 @@ -// -// UIProgressView.swift -// Rex -// -// Created by Tomi Pajunen on 04/05/16. -// Copyright © 2016 Neil Pankey. All rights reserved. -// - import ReactiveSwift import UIKit diff --git a/ReactiveCocoa/UIKit/UISegmentedControl.swift b/ReactiveCocoa/UIKit/UISegmentedControl.swift index b4cb8cadc3..f2a0b33662 100644 --- a/ReactiveCocoa/UIKit/UISegmentedControl.swift +++ b/ReactiveCocoa/UIKit/UISegmentedControl.swift @@ -1,11 +1,3 @@ -// -// UISegmentedControl.swift -// Rex -// -// Created by Markus Chmelar on 07/06/16. -// Copyright © 2016 Neil Pankey. All rights reserved. -// - import ReactiveSwift import enum Result.NoError import UIKit diff --git a/ReactiveCocoa/UIKit/UISwitch.swift b/ReactiveCocoa/UIKit/UISwitch.swift index f07fb44d09..4d7b887fc2 100644 --- a/ReactiveCocoa/UIKit/UISwitch.swift +++ b/ReactiveCocoa/UIKit/UISwitch.swift @@ -1,11 +1,3 @@ -// -// UISwitch.swift -// Rex -// -// Created by David Rodrigues on 07/04/16. -// Copyright © 2016 Neil Pankey. All rights reserved. -// - import ReactiveSwift import enum Result.NoError import UIKit diff --git a/ReactiveCocoa/UIKit/UITextField.swift b/ReactiveCocoa/UIKit/UITextField.swift index 49b6c853b1..e27047456c 100644 --- a/ReactiveCocoa/UIKit/UITextField.swift +++ b/ReactiveCocoa/UIKit/UITextField.swift @@ -1,11 +1,3 @@ -// -// UITextField.swift -// Rex -// -// Created by Rui Peres on 17/01/2016. -// Copyright © 2016 Neil Pankey. All rights reserved. -// - import ReactiveSwift import enum Result.NoError import UIKit diff --git a/ReactiveCocoa/UIKit/UITextView.swift b/ReactiveCocoa/UIKit/UITextView.swift index 9c185e6e36..f82304eee0 100644 --- a/ReactiveCocoa/UIKit/UITextView.swift +++ b/ReactiveCocoa/UIKit/UITextView.swift @@ -1,11 +1,3 @@ -// -// UITextView.swift -// Rex -// -// Created by Rui Peres on 05/04/2016. -// Copyright © 2016 Neil Pankey. All rights reserved. -// - import ReactiveSwift import UIKit import enum Result.NoError diff --git a/ReactiveCocoa/UIKit/UIView.swift b/ReactiveCocoa/UIKit/UIView.swift index d531131493..27728bdd08 100644 --- a/ReactiveCocoa/UIKit/UIView.swift +++ b/ReactiveCocoa/UIKit/UIView.swift @@ -1,11 +1,3 @@ -// -// UIView.swift -// Rex -// -// Created by Andy Jacobs on 21/10/15. -// Copyright © 2015 Neil Pankey. All rights reserved. -// - import ReactiveSwift import UIKit diff --git a/ReactiveCocoaTests/DynamicPropertySpec.swift b/ReactiveCocoaTests/DynamicPropertySpec.swift index 95851f6611..3db87f17e9 100644 --- a/ReactiveCocoaTests/DynamicPropertySpec.swift +++ b/ReactiveCocoaTests/DynamicPropertySpec.swift @@ -1,11 +1,3 @@ -// -// DynamicPropertySpec.swift -// ReactiveCocoa -// -// Created by Justin Spahr-Summers on 2015-01-23. -// Copyright (c) 2015 GitHub. All rights reserved. -// - import ReactiveSwift import Result import Nimble diff --git a/ReactiveCocoaTests/SignalProducerNimbleMatchers.swift b/ReactiveCocoaTests/SignalProducerNimbleMatchers.swift index e7548b436c..28f8316fa0 100644 --- a/ReactiveCocoaTests/SignalProducerNimbleMatchers.swift +++ b/ReactiveCocoaTests/SignalProducerNimbleMatchers.swift @@ -1,13 +1,4 @@ -// -// SignalProducerNimbleMatchers.swift -// ReactiveCocoa -// -// Created by Javier Soto on 1/25/15. -// Copyright (c) 2015 GitHub. All rights reserved. -// - import Foundation - import ReactiveCocoa import ReactiveSwift import Nimble diff --git a/ReactiveCocoaTests/TestError.swift b/ReactiveCocoaTests/TestError.swift index 6a9313c76b..36067c76fc 100644 --- a/ReactiveCocoaTests/TestError.swift +++ b/ReactiveCocoaTests/TestError.swift @@ -1,11 +1,3 @@ -// -// TestError.swift -// ReactiveCocoa -// -// Created by Almas Sapargali on 1/26/15. -// Copyright (c) 2015 GitHub. All rights reserved. -// - import ReactiveCocoa import ReactiveSwift import Result diff --git a/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift b/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift index 05309d65bd..bb4605730a 100644 --- a/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift @@ -1,11 +1,3 @@ -// -// UIActivityIndicatorViewTests.swift -// Rex -// -// Created by Evgeny Kazakov on 02/07/16. -// Copyright © 2016 Neil Pankey. All rights reserved. -// - import XCTest import ReactiveSwift import ReactiveCocoa diff --git a/ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift b/ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift index 5e491c368a..0613dfbbac 100644 --- a/ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift +++ b/ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift @@ -1,11 +1,3 @@ -// -// UIBarButtonItemTests.swift -// Rex -// -// Created by Andy Jacobs on 21/08/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/ReactiveCocoaTests/UIKit/UIButtonTests.swift b/ReactiveCocoaTests/UIKit/UIButtonTests.swift index f5ed97924e..ee185e4d39 100644 --- a/ReactiveCocoaTests/UIKit/UIButtonTests.swift +++ b/ReactiveCocoaTests/UIKit/UIButtonTests.swift @@ -1,11 +1,3 @@ -// -// UIButtonTests.swift -// Rex -// -// Created by Andy Jacobs on 21/08/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift b/ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift index 8a9d974c3f..0cac9de691 100644 --- a/ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift +++ b/ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift @@ -1,11 +1,3 @@ -// -// UIControl+EnableSendActionsForControlEvents.swift -// Rex -// -// Created by David Rodrigues on 24/04/16. -// Copyright © 2016 Neil Pankey. All rights reserved. -// - import UIKit private let rac_swizzleToken: Void = { diff --git a/ReactiveCocoaTests/UIKit/UIControlTests.swift b/ReactiveCocoaTests/UIKit/UIControlTests.swift index ecb4cbc4b8..04f6e10102 100644 --- a/ReactiveCocoaTests/UIKit/UIControlTests.swift +++ b/ReactiveCocoaTests/UIKit/UIControlTests.swift @@ -1,11 +1,3 @@ -// -// UIControlTests.swift -// Rex -// -// Created by Andy Jacobs on 21/08/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/ReactiveCocoaTests/UIKit/UIDatePickerTests.swift b/ReactiveCocoaTests/UIKit/UIDatePickerTests.swift index 74d26260a4..cf1a49cdf6 100644 --- a/ReactiveCocoaTests/UIKit/UIDatePickerTests.swift +++ b/ReactiveCocoaTests/UIKit/UIDatePickerTests.swift @@ -1,11 +1,3 @@ -// -// UIDatePickerTests.swift -// Rex -// -// Created by Guido Marucci Blas on 3/25/16. -// Copyright © 2016 Neil Pankey. All rights reserved. -// - import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/ReactiveCocoaTests/UIKit/UIImageViewTests.swift b/ReactiveCocoaTests/UIKit/UIImageViewTests.swift index 57de8ca2ae..b7873f5a27 100644 --- a/ReactiveCocoaTests/UIKit/UIImageViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UIImageViewTests.swift @@ -1,11 +1,3 @@ -// -// UIImageViewTests.swift -// Rex -// -// Created by Andy Jacobs on 21/10/15. -// Copyright © 2015 Neil Pankey. All rights reserved. -// - import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/ReactiveCocoaTests/UIKit/UILabelTests.swift b/ReactiveCocoaTests/UIKit/UILabelTests.swift index ab96396921..e45cfc0b0f 100644 --- a/ReactiveCocoaTests/UIKit/UILabelTests.swift +++ b/ReactiveCocoaTests/UIKit/UILabelTests.swift @@ -1,11 +1,3 @@ -// -// UILabelTests.swift -// Rex -// -// Created by Neil Pankey on 8/20/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/ReactiveCocoaTests/UIKit/UIProgressViewTests.swift b/ReactiveCocoaTests/UIKit/UIProgressViewTests.swift index 9ed7b52e76..d3a637768b 100644 --- a/ReactiveCocoaTests/UIKit/UIProgressViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UIProgressViewTests.swift @@ -1,11 +1,3 @@ -// -// UIProgressViewTests.swift -// Rex -// -// Created by Tomi Pajunen on 04/05/16. -// Copyright © 2016 Neil Pankey. All rights reserved. -// - import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift b/ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift index 37f0af603d..780aa9de36 100644 --- a/ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift +++ b/ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift @@ -1,11 +1,3 @@ -// -// UISegmentedControlTests.swift -// Rex -// -// Created by Markus Chmelar on 07/06/16. -// Copyright © 2016 Neil Pankey. All rights reserved. -// - import XCTest import ReactiveSwift import ReactiveCocoa diff --git a/ReactiveCocoaTests/UIKit/UISwitchTests.swift b/ReactiveCocoaTests/UIKit/UISwitchTests.swift index fdcdaa1c86..bcb4899b1f 100644 --- a/ReactiveCocoaTests/UIKit/UISwitchTests.swift +++ b/ReactiveCocoaTests/UIKit/UISwitchTests.swift @@ -1,11 +1,3 @@ -// -// UISwitchTests.swift -// Rex -// -// Created by David Rodrigues on 07/04/16. -// Copyright © 2016 Neil Pankey. All rights reserved. -// - import XCTest import ReactiveSwift import ReactiveCocoa diff --git a/ReactiveCocoaTests/UIKit/UITextFieldTests.swift b/ReactiveCocoaTests/UIKit/UITextFieldTests.swift index c395f20452..b33f101e6c 100644 --- a/ReactiveCocoaTests/UIKit/UITextFieldTests.swift +++ b/ReactiveCocoaTests/UIKit/UITextFieldTests.swift @@ -1,11 +1,3 @@ -// -// UITextFieldTests.swift -// Rex -// -// Created by Rui Peres on 17/01/2016. -// Copyright © 2016 Neil Pankey. All rights reserved. -// - import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/ReactiveCocoaTests/UIKit/UITextViewTests.swift b/ReactiveCocoaTests/UIKit/UITextViewTests.swift index 3bf26c0c12..c732f7e224 100644 --- a/ReactiveCocoaTests/UIKit/UITextViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UITextViewTests.swift @@ -1,11 +1,3 @@ -// -// UITextViewTests.swift -// Rex -// -// Created by Rui Peres on 05/04/2016. -// Copyright © 2016 Neil Pankey. All rights reserved. -// - import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/ReactiveCocoaTests/UIKit/UIViewTests.swift b/ReactiveCocoaTests/UIKit/UIViewTests.swift index 8e75790255..cdb3325484 100644 --- a/ReactiveCocoaTests/UIKit/UIViewTests.swift +++ b/ReactiveCocoaTests/UIKit/UIViewTests.swift @@ -1,11 +1,3 @@ -// -// UIViewTests.swift -// Rex -// -// Created by Andy Jacobs on 21/10/15. -// Copyright © 2015 Neil Pankey. All rights reserved. -// - import ReactiveSwift import ReactiveCocoa import UIKit From d3b62b3099f85782abf214f0776a930330a6daf1 Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 7 Oct 2016 18:57:18 +0200 Subject: [PATCH 0433/1028] Updated the documentation of `CocoaAction` and tweaked its implementation. --- ReactiveCocoa/CocoaAction.swift | 45 ++++++++++++++------------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/ReactiveCocoa/CocoaAction.swift b/ReactiveCocoa/CocoaAction.swift index 82b6c13424..6d649b6c59 100644 --- a/ReactiveCocoa/CocoaAction.swift +++ b/ReactiveCocoa/CocoaAction.swift @@ -2,10 +2,8 @@ import Foundation import ReactiveSwift import enum Result.NoError -/// Wraps an Action for use by a GUI control (such as `NSControl` or +/// CocoaAction wraps an Action for use by a GUI control (such as `NSControl` or /// `UIControl`), with KVO, or with Cocoa Bindings. -/// -/// - important: The `Action` is weakly referenced. public final class CocoaAction: NSObject { /// The selector for message senders. public static var selector: Selector { @@ -24,23 +22,18 @@ public final class CocoaAction: NSObject { /// KVO notification for every change. public let isExecuting: Property - private let _execute: (AnyObject?) -> Void + private let _execute: (Sender) -> Void - /// Initializes a Cocoa action that will invoke the given Action by - /// transforming the object given to execute(). - /// - /// - important: The `Action` is weakly referenced. + /// Initialize a CocoaAction that invokes the given Action by mapping the + /// sender to the input type of the Action. /// /// - parameters: - /// - action: Executable action. - /// - inputTransform: Closure that accepts the UI control performing the - /// action and returns a value (e.g. - /// `(UISwitch) -> (Bool)` to reflect whether a provided - /// switch is currently on. + /// - action: The Action. + /// - inputTransform: A closure that maps Sender to the input type of the + /// Action. public init(_ action: Action, _ inputTransform: @escaping (Sender) -> Input) { - _execute = { input in - let control = input as! Sender - let producer = action.apply(inputTransform(control)) + _execute = { sender in + let producer = action.apply(inputTransform(sender)) producer.start() } @@ -50,30 +43,30 @@ public final class CocoaAction: NSObject { super.init() } - /// Initializes a Cocoa action that will invoke the given Action. + /// Initialize a CocoaAction that invokes the given Action. /// /// - parameters: - /// - action: Executable action. + /// - action: The Action. public convenience init(_ action: Action<(), Output, Error>) { self.init(action, { _ in }) } - /// Initializes a Cocoa action that will invoke the given Action by always - /// providing the given input. + /// Initialize a CocoaAction that invokes the given Action with the given + /// constant. /// /// - parameters: - /// - action: Executable action. - /// - input: A value given as input to the action. + /// - action: The Action. + /// - input: The constant value as the input to the action. public convenience init(_ action: Action, input: Input) { self.init(action, { _ in input }) } - /// Attempts to execute the underlying action with the given input, subject + /// Attempt to execute the underlying action with the given input, subject /// to the behavior described by the initializer that was used. /// /// - parameters: - /// - input: A value for the action passed during initialization. - @IBAction public func execute(_ input: AnyObject?) { - _execute(input) + /// - sender: The sender which initiates the attempt. + @IBAction public func execute(_ sender: Any?) { + _execute(sender as! Sender) } } From c4c667078083fe57781f73e729c056284086dff7 Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 7 Oct 2016 22:30:59 +0200 Subject: [PATCH 0434/1028] Tweaked the capture lists and formatting of some closures. --- ReactiveCocoa/AppKit/NSControl.swift | 18 ++++++++++-------- ReactiveCocoa/NSObject+BindingTarget.swift | 2 +- ReactiveCocoa/UIKit/UIControl.swift | 4 ++-- ReactiveCocoa/UIKit/UIDatePicker.swift | 3 ++- ReactiveCocoa/UIKit/UISegmentedControl.swift | 3 ++- ReactiveCocoa/UIKit/UISwitch.swift | 3 ++- ReactiveCocoa/UIKit/UITextField.swift | 6 ++++-- 7 files changed, 23 insertions(+), 16 deletions(-) diff --git a/ReactiveCocoa/AppKit/NSControl.swift b/ReactiveCocoa/AppKit/NSControl.swift index f52cf7675d..dcdfb6419c 100644 --- a/ReactiveCocoa/AppKit/NSControl.swift +++ b/ReactiveCocoa/AppKit/NSControl.swift @@ -15,7 +15,7 @@ extension Reactive where Base: NSControl { /// A signal of values in `NSAttributedString`, emitted by the control. public var attributedStringValues: Signal { - return trigger.map { [unowned base] in base.attributedStringValue } + return trigger.map { [unowned base = self.base] in base.attributedStringValue } } /// Sets the value of the control with a `Bool`. @@ -25,7 +25,9 @@ extension Reactive where Base: NSControl { /// A signal of values in `Bool`, emitted by the control. public var boolValues: Signal { - return trigger.map { [unowned base] in base.integerValue == NSOffState ? false : true } + return trigger.map { [unowned base = self.base] in + return base.integerValue == NSOffState ? false : true + } } /// Sets the value of the control with a `Double`. @@ -35,7 +37,7 @@ extension Reactive where Base: NSControl { /// A signal of values in `Double`, emitted by the control. public var doubleValues: Signal { - return trigger.map { [unowned base] in base.doubleValue } + return trigger.map { [unowned base = self.base] in base.doubleValue } } /// Sets the value of the control with a `Float`. @@ -45,7 +47,7 @@ extension Reactive where Base: NSControl { /// A signal of values in `Float`, emitted by the control. public var floatValues: Signal { - return trigger.map { [unowned base] in base.floatValue } + return trigger.map { [unowned base = self.base] in base.floatValue } } /// Sets the value of the control with an `Int32`. @@ -55,7 +57,7 @@ extension Reactive where Base: NSControl { /// A signal of values in `Int32`, emitted by the control. public var intValues: Signal { - return trigger.map { [unowned base] in base.intValue } + return trigger.map { [unowned base = self.base] in base.intValue } } /// Sets the value of the control with an `Int`. @@ -65,7 +67,7 @@ extension Reactive where Base: NSControl { /// A signal of values in `Int`, emitted by the control. public var integerValues: Signal { - return trigger.map { [unowned base] in base.integerValue } + return trigger.map { [unowned base = self.base] in base.integerValue } } /// Sets the value of the control. @@ -75,7 +77,7 @@ extension Reactive where Base: NSControl { /// A signal of values in `Any?`, emitted by the control. public var objectValues: Signal { - return trigger.map { [unowned base] in base.objectValue } + return trigger.map { [unowned base = self.base] in base.objectValue } } /// Sets the value of the control with a `String`. @@ -85,7 +87,7 @@ extension Reactive where Base: NSControl { /// A signal of values in `String`, emitted by the control. public var stringValues: Signal { - return trigger.map { [unowned base] in base.stringValue } + return trigger.map { [unowned base = self.base] in base.stringValue } } /// A trigger signal that sends a `next` event for every action messages diff --git a/ReactiveCocoa/NSObject+BindingTarget.swift b/ReactiveCocoa/NSObject+BindingTarget.swift index 8a820832d8..be3933ac27 100644 --- a/ReactiveCocoa/NSObject+BindingTarget.swift +++ b/ReactiveCocoa/NSObject+BindingTarget.swift @@ -16,7 +16,7 @@ extension Reactive where Base: NSObject { internal func makeBindingTarget(action: @escaping (Base, U) -> Void) -> BindingTarget { let scheduler = associatedValue { _ in UIScheduler() } - return BindingTarget(on: scheduler, lifetime: lifetime) { [weak base] value in + return BindingTarget(on: scheduler, lifetime: lifetime) { [weak base = self.base] value in if let base = base { action(base, value) } diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index f5fa28967a..710edb7f7f 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -38,7 +38,7 @@ extension Reactive where Base: UIControl { let disposable = CompositeDisposable() disposable += isEnabled <~ action.isEnabled - disposable += { [weak base] in + disposable += { [weak base = self.base] in base?.removeTarget(action, action: CocoaAction.selector, for: controlEvents) } @@ -66,7 +66,7 @@ extension Reactive where Base: UIControl { let disposable = lifetime.ended.observeCompleted(observer.sendCompleted) - return ActionDisposable { [weak base] in + return ActionDisposable { [weak base = self.base] in disposable?.dispose() base?.removeTarget(receiver, diff --git a/ReactiveCocoa/UIKit/UIDatePicker.swift b/ReactiveCocoa/UIKit/UIDatePicker.swift index c17cb235bd..4d00c65c72 100644 --- a/ReactiveCocoa/UIKit/UIDatePicker.swift +++ b/ReactiveCocoa/UIKit/UIDatePicker.swift @@ -10,6 +10,7 @@ extension Reactive where Base: UIDatePicker { /// A signal of dates emitted by the date picker. public var dates: Signal { - return trigger(for: .valueChanged).map { [unowned base] in base.date } + return trigger(for: .valueChanged) + .map { [unowned base = self.base] in base.date } } } diff --git a/ReactiveCocoa/UIKit/UISegmentedControl.swift b/ReactiveCocoa/UIKit/UISegmentedControl.swift index f2a0b33662..b4b275a939 100644 --- a/ReactiveCocoa/UIKit/UISegmentedControl.swift +++ b/ReactiveCocoa/UIKit/UISegmentedControl.swift @@ -10,6 +10,7 @@ extension Reactive where Base: UISegmentedControl { /// A signal of indexes of selections emitted by the segmented control. public var selectedSegmentIndexes: Signal { - return trigger(for: .valueChanged).map { [unowned base] in base.selectedSegmentIndex } + return trigger(for: .valueChanged) + .map { [unowned base = self.base] in base.selectedSegmentIndex } } } diff --git a/ReactiveCocoa/UIKit/UISwitch.swift b/ReactiveCocoa/UIKit/UISwitch.swift index 4d7b887fc2..42e13588ea 100644 --- a/ReactiveCocoa/UIKit/UISwitch.swift +++ b/ReactiveCocoa/UIKit/UISwitch.swift @@ -10,6 +10,7 @@ extension Reactive where Base: UISwitch { /// A signal of on-off states in `Bool` emitted by the switch. public var isOnValues: Signal { - return trigger(for: .valueChanged).map { [unowned base] in base.isOn } + return trigger(for: .valueChanged) + .map { [unowned base = self.base] in base.isOn } } } diff --git a/ReactiveCocoa/UIKit/UITextField.swift b/ReactiveCocoa/UIKit/UITextField.swift index e27047456c..953fd867eb 100644 --- a/ReactiveCocoa/UIKit/UITextField.swift +++ b/ReactiveCocoa/UIKit/UITextField.swift @@ -10,11 +10,13 @@ extension Reactive where Base: UITextField { /// A signal of text values emitted by the text field upon end of editing. public var textValues: Signal { - return trigger(for: .editingDidEnd).map { [unowned base] in base.text } + return trigger(for: .editingDidEnd) + .map { [unowned base = self.base] in base.text } } /// A signal of text values emitted by the text field upon any changes. public var continuousTextValues: Signal { - return trigger(for: .editingChanged).map { [unowned base] in base.text } + return trigger(for: .editingChanged) + .map { [unowned base = self.base] in base.text } } } From bf87ed3572208dc0f45480c0032b0e7399a874b8 Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 7 Oct 2016 23:20:24 +0200 Subject: [PATCH 0435/1028] Updated dependency: ReactiveSwift 1.0.0-alpha.2 --- Cartfile | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cartfile b/Cartfile index 1a7b7f1c06..a1e62fec3b 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" "reactivity" +github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.2" diff --git a/Cartfile.resolved b/Cartfile.resolved index 4a61170718..fd4898dc4f 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -2,4 +2,4 @@ github "Quick/Nimble" "v5.0.0" github "Quick/Quick" "v0.10.0" github "antitypical/Result" "3.0.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" -github "ReactiveCocoa/ReactiveSwift" "1f24443560cc767e5706b7b48c0dc5e169479a00" +github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.2" diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index 1f24443560..dfb2f9ae68 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit 1f24443560cc767e5706b7b48c0dc5e169479a00 +Subproject commit dfb2f9ae68c04c38c768e8a25fffa0d72f7fa5b5 From 9bcec14015506873f10c9dfb8d36eae2c12f5275 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Sat, 8 Oct 2016 15:40:23 +0900 Subject: [PATCH 0436/1028] Update ReactiveSwift to 1.0.0-alpha.2 --- Cartfile | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cartfile b/Cartfile index a27dbdbda7..a1e62fec3b 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.1" +github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.2" diff --git a/Cartfile.resolved b/Cartfile.resolved index e9e3c050e8..fd4898dc4f 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -2,4 +2,4 @@ github "Quick/Nimble" "v5.0.0" github "Quick/Quick" "v0.10.0" github "antitypical/Result" "3.0.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" -github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.1" +github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.2" diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index bc409d30ff..dfb2f9ae68 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit bc409d30ff4daa059c8904fb9b505492cc48cfbe +Subproject commit dfb2f9ae68c04c38c768e8a25fffa0d72f7fa5b5 From d0b953d85129ed2f63dcf71631ae97eefadf0faf Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Sun, 9 Oct 2016 13:11:51 +0200 Subject: [PATCH 0437/1028] Update README with regards to Swift, ObjC and bridging between those --- README.md | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 59af26479d..8e5224c5dd 100644 --- a/README.md +++ b/README.md @@ -51,17 +51,13 @@ For more information about the concepts in ReactiveCocoa, see [ReactiveSwift][]. ## Objective-C and Swift -Although ReactiveCocoa was started as an Objective-C framework, as of [version -3.0][CHANGELOG], all major feature development is concentrated on the [Swift API][]. +After announced Swift, ReactiveCocoa was rewritten in Swift. As of [version +5.0][CHANGELOG], the Objective-C API and the Swift API are entirely separated into different projects([ReactiveObjC][] and [ReactiveSwift][]). -RAC’s [Objective-C API][] and Swift API are entirely separate, but there is -a [bridge][Objective-C Bridging] to convert between the two. This -is mostly meant as a compatibility layer for older ReactiveCocoa projects, or to -use Cocoa extensions which haven’t been added to the Swift API yet. +There is a bridge to convert between those APIs ([ReactiveObjCBridge][]) which is mostly meant as a compatibility layer for older ReactiveCocoa projects. The Objective-C API will continue to exist and be supported for the foreseeable -future, but it won’t receive many improvements. For more information about using -this API, please consult our [legacy documentation][]. +future, but it won’t receive many improvements. For more information about using this API, please consult our [legacy documentation][]. **We highly recommend that all new projects use the Swift API.** @@ -102,6 +98,8 @@ a tour of ReactiveCocoa’s concepts, and the [Basic Operators][] for some introductory examples of using it. [ReactiveSwift]: https://github.com/ReactiveCocoa/ReactiveSwift +[ReactiveObjC]: https://github.com/ReactiveCocoa/ReactiveObjC +[ReactiveObjCBridge]: https://github.com/ReactiveCocoa/ReactiveObjCBridge [Actions]: Documentation/FrameworkOverview.md#actions [Basic Operators]: Documentation/BasicOperators.md [CHANGELOG]: CHANGELOG.md @@ -109,8 +107,6 @@ introductory examples of using it. [Documentation]: Documentation [Framework Overview]: Documentation/FrameworkOverview.md [Legacy Documentation]: https://github.com/ReactiveCocoa/ReactiveObjC/blob/master/Documentation/ -[Objective-C API]: ReactiveCocoa/Objective-C -[Objective-C Bridging]: Documentation/ObjectiveCBridging.md [Signal producers]: Documentation/FrameworkOverview.md#signal-producers [Signals]: Documentation/FrameworkOverview.md#signals [Swift API]: ReactiveCocoa/Swift From b7a398f88c66e64ae7a0fe7ff190e63664e7de3f Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Sun, 9 Oct 2016 13:12:26 +0200 Subject: [PATCH 0438/1028] Remove ObjectiveCBridging documentation because now lives in the ReactiveObjCBridging project --- Documentation/ObjectiveCBridging.md | 99 ----------------------------- 1 file changed, 99 deletions(-) delete mode 100644 Documentation/ObjectiveCBridging.md diff --git a/Documentation/ObjectiveCBridging.md b/Documentation/ObjectiveCBridging.md deleted file mode 100644 index f5016999a2..0000000000 --- a/Documentation/ObjectiveCBridging.md +++ /dev/null @@ -1,99 +0,0 @@ -# Objective-C Bridging - -While ReactiveCocoa 3.0 introduces an entirely new design, it also aims for maximum compatibility with RAC 2, to ease the pain of migration. To interoperate with RAC 2’s Objective-C APIs, RAC 3 offers bridging functions that can convert Objective-C types to Swift types and vice-versa. - -Because the APIs are based on fundamentally different designs, the conversion is not always one-to-one; however, every attempt has been made to faithfully translate the concepts between the two APIs (and languages). - -The bridged types include: - - 1. [`RACSignal` and `SignalProducer` or `Signal`](#racsignal-and-signalproducer-or-signal) - 1. [`RACCommand` and `Action`](#raccommand-and-action) - 1. [`RACScheduler` and `SchedulerType`](#racscheduler-and-schedulertype) - 1. [`RACDisposable` and `Disposable`](#racdisposable-and-disposable) - -For the complete bridging API, including documentation, see [`ObjectiveCBridging.swift`][ObjectiveCBridging]. To learn more about how to migrate between ReactiveCocoa 2 and 3, see the [CHANGELOG][]. - -## `RACSignal` and `SignalProducer` or `Signal` - -In RAC 3, “cold” signals are represented by the `SignalProducer` type, and “hot” signals are represented by the `Signal` type. - -“Cold” `RACSignal`s can be converted into `SignalProducer`s using the new `toSignalProducer` method: - -```swift -extension RACSignal { - func toSignalProducer() -> SignalProducer -} -``` - -“Hot” `RACSignal`s cannot be directly converted into `Signal`s, because _any_ `RACSignal` subscription could potentially involve side effects. To obtain a `Signal`, use `RACSignal.toSignalProducer` followed by `SignalProducer.start`, which will make those potential side effects explicit. - -For the other direction, use the `toRACSignal()` function. - -When called with a `SignalProducer`, these functions will create a `RACSignal` to `start()` the producer once for each subscription: - -```swift -func toRACSignal(producer: SignalProducer) -> RACSignal -func toRACSignal(producer: SignalProducer) -> RACSignal -``` - -When called with a `Signal`, these functions will create a `RACSignal` that simply observes it: - -```swift -func toRACSignal(signal: Signal) -> RACSignal -func toRACSignal(signal: Signal) -> RACSignal -``` - -## `RACCommand` and `Action` - -To convert `RACCommand`s into the new `Action` type, use the `toAction()` extension method: - -```swift -extension RACCommand { - func toAction() -> Action -} -``` - -To convert `Action`s into `RACCommand`s, use the `toRACCommand()` function: - -```swift -func toRACCommand(action: Action) -> RACCommand -func toRACCommand(action: Action) -> RACCommand -``` - -**NOTE:** The `executing` properties of actions and commands are not synchronized across the API bridge. To ensure consistency, only observe the `executing` property from the base object (the one passed _into_ the bridge, not retrieved from it), so updates occur no matter which object is used for execution. - -## `RACScheduler` and `SchedulerType` - -Any `RACScheduler` instance is automatically a `DateSchedulerType` (and therefore a `SchedulerType`), and can be passed directly into any function or method that expects one. - -Some (but not all) `SchedulerType`s from RAC 3 can be converted into `RACScheduler` instances, using the `toRACScheduler()` method: - -```swift -extension ImmediateScheduler { - func toRACScheduler() -> RACScheduler -} - -extension UIScheduler { - func toRACScheduler() -> RACScheduler -} - -extension QueueScheduler { - func toRACScheduler() -> RACScheduler -} -``` - -## `RACDisposable` and `Disposable` - -Any `RACDisposable` instance is automatically a `Disposable`, and can be used directly anywhere a type conforming to `Disposable` is expected. - -Although there is no direct conversion from `Disposable` into `RACDisposable`, it is easy to do manually: - -```swift -let swiftDisposable: Disposable -let objcDisposable = RACDisposable { - swiftDisposable.dispose() -} -``` - -[CHANGELOG]: ../CHANGELOG.md -[ObjectiveCBridging]: ../ReactiveCocoa/Swift/ObjectiveCBridging.swift From a30b54d9b2aa296a97dd9da8b1a905523a2797ab Mon Sep 17 00:00:00 2001 From: Anders Date: Sat, 8 Oct 2016 13:10:29 +0200 Subject: [PATCH 0439/1028] Removed an unused global variable. --- ReactiveCocoa/NSObject+Intercepting.swift | 2 -- ReactiveCocoa/UIKit/UIButton.swift | 2 -- 2 files changed, 4 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index ffc668509f..cbcd165460 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -50,5 +50,3 @@ extension Reactive where Base: NSObject { return signal } } - -private var mapKey = 0 diff --git a/ReactiveCocoa/UIKit/UIButton.swift b/ReactiveCocoa/UIKit/UIButton.swift index dcbebbb16c..4f4a418742 100644 --- a/ReactiveCocoa/UIKit/UIButton.swift +++ b/ReactiveCocoa/UIKit/UIButton.swift @@ -28,5 +28,3 @@ extension Reactive where Base: UIButton { return makeBindingTarget { $0.setTitle($1, for: state) } } } - -private var pressedKey: UInt8 = 0 From 39329d8c76715bb19cd5b03934563734654bed7e Mon Sep 17 00:00:00 2001 From: Anders Date: Sat, 8 Oct 2016 13:16:32 +0200 Subject: [PATCH 0440/1028] Create one `UIScheduler` for each instance of binding target. --- ReactiveCocoa/NSObject+BindingTarget.swift | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ReactiveCocoa/NSObject+BindingTarget.swift b/ReactiveCocoa/NSObject+BindingTarget.swift index be3933ac27..12b018c8f1 100644 --- a/ReactiveCocoa/NSObject+BindingTarget.swift +++ b/ReactiveCocoa/NSObject+BindingTarget.swift @@ -13,10 +13,8 @@ extension Reactive where Base: NSObject { /// /// - returns: /// A binding target that holds no strong references to the object. - internal func makeBindingTarget(action: @escaping (Base, U) -> Void) -> BindingTarget { - let scheduler = associatedValue { _ in UIScheduler() } - - return BindingTarget(on: scheduler, lifetime: lifetime) { [weak base = self.base] value in + internal func makeBindingTarget(_ key: StaticString = #function, action: @escaping (Base, U) -> Void) -> BindingTarget { + return BindingTarget(on: UIScheduler(), lifetime: lifetime) { [weak base = self.base] value in if let base = base { action(base, value) } From 7caec8bd7990b9bf0618d75bcaac09ec24c8b0cd Mon Sep 17 00:00:00 2001 From: Anders Date: Sun, 9 Oct 2016 14:01:01 +0200 Subject: [PATCH 0441/1028] Shared `CocoaTrigger` for all uses of the target-action pattern. --- ReactiveCocoa.xcodeproj/project.pbxproj | 10 +++++++ ReactiveCocoa/AppKit/NSControl.swift | 35 ++++++++----------------- ReactiveCocoa/CocoaTrigger.swift | 15 +++++++++++ ReactiveCocoa/UIKit/UIControl.swift | 18 +++---------- 4 files changed, 39 insertions(+), 39 deletions(-) create mode 100644 ReactiveCocoa/CocoaTrigger.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index b1fad36a6b..97692e5a22 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -87,6 +87,10 @@ 9A1E72BA1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; 9A1E72BB1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; 9A1E72BC1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; + 9A2E425E1DAA6737006D909F /* CocoaTrigger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2E425D1DAA6737006D909F /* CocoaTrigger.swift */; }; + 9A2E425F1DAA6737006D909F /* CocoaTrigger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2E425D1DAA6737006D909F /* CocoaTrigger.swift */; }; + 9A2E42601DAA6737006D909F /* CocoaTrigger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2E425D1DAA6737006D909F /* CocoaTrigger.swift */; }; + 9A2E42611DAA6737006D909F /* CocoaTrigger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2E425D1DAA6737006D909F /* CocoaTrigger.swift */; }; 9A9DFEE51DA7B5500039EE1B /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; 9A9DFEE91DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; 9A9DFEEA1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; @@ -250,6 +254,7 @@ 9A1D06751D9415FB00ACF44C /* RACObjCRuntimeUtilities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RACObjCRuntimeUtilities.h; sourceTree = ""; }; 9A1D06761D9415FB00ACF44C /* RACObjCRuntimeUtilities.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RACObjCRuntimeUtilities.m; sourceTree = ""; }; 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyValueObservingSpec.swift; sourceTree = ""; }; + 9A2E425D1DAA6737006D909F /* CocoaTrigger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaTrigger.swift; sourceTree = ""; }; 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssociationSpec.swift; sourceTree = ""; }; 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+KeyValueObserving.swift"; sourceTree = ""; }; 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaAction.swift; sourceTree = ""; }; @@ -515,6 +520,7 @@ 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */, 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */, 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */, + 9A2E425D1DAA6737006D909F /* CocoaTrigger.swift */, 9A1D05E91D93E9F100ACF44C /* AppKit */, 9A1D05EB1D93E9F100ACF44C /* UIKit */, D04725ED19E49ED7006002AA /* Supporting Files */, @@ -932,6 +938,7 @@ 4A0E11021D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, CD0C45E11CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9A1D061A1D93EA0100ACF44C /* UIProgressView.swift in Sources */, + 9A2E42611DAA6737006D909F /* CocoaTrigger.swift in Sources */, 9A1D06121D93EA0100ACF44C /* UIBarButtonItem.swift in Sources */, 9A1D06111D93EA0100ACF44C /* UIActivityIndicatorView.swift in Sources */, 9A1D061B1D93EA0100ACF44C /* UISegmentedControl.swift in Sources */, @@ -967,6 +974,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9A2E42601DAA6737006D909F /* CocoaTrigger.swift in Sources */, 9A9DFEE51DA7B5500039EE1B /* NSObject+Intercepting.swift in Sources */, 9A1D06791D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9ADE4A931DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, @@ -989,6 +997,7 @@ 9A1D05E01D93E99100ACF44C /* NSObject+Association.swift in Sources */, 9ADE4A7C1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, 9ADE4A961DA6F018005C2AC8 /* NSTextField.swift in Sources */, + 9A2E425E1DAA6737006D909F /* CocoaTrigger.swift in Sources */, 9ADE4A891DA6D206005C2AC8 /* NSControl.swift in Sources */, 9AF0EA751D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, 9ADE4A911DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, @@ -1021,6 +1030,7 @@ 9A1D06061D93EA0000ACF44C /* UIImageView.swift in Sources */, 9AF0EA761D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, CD0C45DF1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, + 9A2E425F1DAA6737006D909F /* CocoaTrigger.swift in Sources */, 9A1D06781D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9A1D060F1D93EA0000ACF44C /* UIView.swift in Sources */, 9A1D06051D93EA0000ACF44C /* UIDatePicker.swift in Sources */, diff --git a/ReactiveCocoa/AppKit/NSControl.swift b/ReactiveCocoa/AppKit/NSControl.swift index dcdfb6419c..915c5bebbb 100644 --- a/ReactiveCocoa/AppKit/NSControl.swift +++ b/ReactiveCocoa/AppKit/NSControl.swift @@ -93,32 +93,19 @@ extension Reactive where Base: NSControl { /// A trigger signal that sends a `next` event for every action messages /// received from the control, and completes when the control deinitializes. private var trigger: Signal<(), NoError> { - let receiver: ActionMessageReceiver = associatedValue { base in - let receiver = ActionMessageReceiver() - base.target = receiver - base.action = #selector(ActionMessageReceiver.receive) - - return receiver - } - - return receiver.trigger - } -} + return associatedValue { base in + let (signal, observer) = Signal<(), NoError>.pipe() -private class ActionMessageReceiver: NSObject { - let trigger: Signal<(), NoError> - private let observer: Signal<(), NoError>.Observer + let receiver = CocoaTrigger(observer) + base.target = receiver + base.action = #selector(CocoaTrigger.sendNext) - override init() { - (trigger, observer) = Signal<(), NoError>.pipe() - super.init() - } + lifetime.ended.observeCompleted { + _ = receiver + observer.sendCompleted() + } - deinit { - observer.sendCompleted() - } - - @objc func receive(_ sender: Any?) { - observer.send(value: ()) + return signal + } } } diff --git a/ReactiveCocoa/CocoaTrigger.swift b/ReactiveCocoa/CocoaTrigger.swift new file mode 100644 index 0000000000..a315a7c2eb --- /dev/null +++ b/ReactiveCocoa/CocoaTrigger.swift @@ -0,0 +1,15 @@ +import Foundation +import ReactiveSwift +import enum Result.NoError + +internal class CocoaTrigger: NSObject { + let observer: Observer<(), NoError> + + init(_ observer: Observer<(), NoError>) { + self.observer = observer + } + + @objc func sendNext(_ receiver: Any?) { + observer.send(value: ()) + } +} diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index 710edb7f7f..33d9f0006b 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -2,18 +2,6 @@ import ReactiveSwift import UIKit import enum Result.NoError -private class UnsafeControlReceiver: NSObject { - private let observer: Observer<(), NoError> - - fileprivate init(observer: Observer<(), NoError>) { - self.observer = observer - } - - @objc fileprivate func sendNext(_ receiver: Any?) { - observer.send(value: ()) - } -} - extension Reactive where Base: UIControl { /// The current associated action of `self`, with its registered event mask /// and its disposable. @@ -59,9 +47,9 @@ extension Reactive where Base: UIControl { /// A trigger signal. public func trigger(for controlEvents: UIControlEvents) -> Signal<(), NoError> { return Signal { observer in - let receiver = UnsafeControlReceiver(observer: observer) + let receiver = CocoaTrigger(observer) base.addTarget(receiver, - action: #selector(UnsafeControlReceiver.sendNext), + action: #selector(CocoaTrigger.sendNext), for: controlEvents) let disposable = lifetime.ended.observeCompleted(observer.sendCompleted) @@ -70,7 +58,7 @@ extension Reactive where Base: UIControl { disposable?.dispose() base?.removeTarget(receiver, - action: #selector(UnsafeControlReceiver.sendNext), + action: #selector(CocoaTrigger.sendNext), for: controlEvents) } } From b75b3d6cf6e6a700ecc40576bfc90a0426a98651 Mon Sep 17 00:00:00 2001 From: Anders Date: Sun, 9 Oct 2016 14:02:32 +0200 Subject: [PATCH 0442/1028] Fixed a typo in the doc of `UIProgressView.reactive`. --- ReactiveCocoa/UIKit/UIProgressView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa/UIKit/UIProgressView.swift b/ReactiveCocoa/UIKit/UIProgressView.swift index 6397ad4bc6..2d3bad01ab 100644 --- a/ReactiveCocoa/UIKit/UIProgressView.swift +++ b/ReactiveCocoa/UIKit/UIProgressView.swift @@ -2,7 +2,7 @@ import ReactiveSwift import UIKit extension Reactive where Base: UIProgressView { - /// Sets the reative progress to be reflected by the progress view. + /// Sets the relative progress to be reflected by the progress view. public var progress: BindingTarget { return makeBindingTarget { $0.progress = $1 } } From 7395e163ca4888e308fbb806d11bf80a95d81711 Mon Sep 17 00:00:00 2001 From: Anders Date: Sun, 9 Oct 2016 14:45:32 +0200 Subject: [PATCH 0443/1028] Tweaked the implementation of `NSObject.reactive.trigger(for:)`. --- ReactiveCocoa/NSObject+Intercepting.swift | 25 +++++------------------ ReactiveCocoa/RACObjCRuntimeUtilities.h | 6 ++++-- ReactiveCocoa/RACObjCRuntimeUtilities.m | 8 ++++++-- 3 files changed, 15 insertions(+), 24 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index cbcd165460..30682b30c8 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -13,35 +13,20 @@ extension Reactive where Base: NSObject { /// - returns: /// A trigger signal. public func trigger(for selector: Selector) -> Signal<(), NoError> { - return signal(for: selector) { observer in - return { _ in observer.send(value: ()) } - } - } - - /// Create a signal which sends a `next` event at the end of every invocation - /// of `selector` on the object. - /// - /// - parameters: - /// - selector: The selector to observe. - /// - setup: The setup closure of how received events in the runtime are - /// piped to the returned signal. - /// - /// - returns: - /// A trigger signal. - private func signal(for selector: Selector, setup: (Observer) -> rac_receiver_t) -> Signal { objc_sync_enter(self) defer { objc_sync_exit(self) } let map = associatedValue { _ in NSMutableDictionary() } let selectorName = String(describing: selector) as NSString - if let signal = map.object(forKey: selectorName) as? Signal { + if let signal = map.object(forKey: selectorName) as? Signal<(), NoError> { return signal } - let (signal, observer) = Signal.pipe() - let action = setup(observer) - let isSuccessful = RACRegisterBlockForSelector(base, selector, nil, action) + let (signal, observer) = Signal<(), NoError>.pipe() + let isSuccessful = base.rac_setupInvocationObservation(for: selector, + protocol: nil, + receiver: observer.send(value:)) assert(isSuccessful) lifetime.ended.observeCompleted(observer.sendCompleted) diff --git a/ReactiveCocoa/RACObjCRuntimeUtilities.h b/ReactiveCocoa/RACObjCRuntimeUtilities.h index 4f1d78808b..0d15dd41f8 100644 --- a/ReactiveCocoa/RACObjCRuntimeUtilities.h +++ b/ReactiveCocoa/RACObjCRuntimeUtilities.h @@ -1,10 +1,12 @@ #import NS_ASSUME_NONNULL_BEGIN -typedef void (^rac_receiver_t)(void); +@interface NSObject (RACObjCRuntimeUtilities) /// Register a block which would be triggered when `selector` is called. /// /// Warning: The callee is responsible for synchronization. -BOOL RACRegisterBlockForSelector(NSObject *self, SEL selector, Protocol * _Nullable protocol, rac_receiver_t receiver); +-(BOOL) rac_setupInvocationObservationForSelector:(SEL)selector protocol:(nullable Protocol *)protocol receiver:(void (^)(void)) receiver; + +@end NS_ASSUME_NONNULL_END diff --git a/ReactiveCocoa/RACObjCRuntimeUtilities.m b/ReactiveCocoa/RACObjCRuntimeUtilities.m index 2c0220eb82..b93d6ecdfb 100644 --- a/ReactiveCocoa/RACObjCRuntimeUtilities.m +++ b/ReactiveCocoa/RACObjCRuntimeUtilities.m @@ -29,7 +29,7 @@ static SEL RACAliasForSelector(SEL originalSelector) { static BOOL RACForwardInvocation(id self, NSInvocation *invocation) { SEL aliasSelector = RACAliasForSelector(invocation.selector); - __block rac_receiver_t receiver = objc_getAssociatedObject(self, aliasSelector); + __block void(^receiver)(void) = objc_getAssociatedObject(self, aliasSelector); Class class = object_getClass(invocation.target); BOOL respondsToAlias = [class instancesRespondToSelector:aliasSelector]; @@ -249,7 +249,9 @@ static Class RACSwizzleClass(NSObject *self) { return subclass; } -BOOL RACRegisterBlockForSelector(NSObject *self, SEL selector, Protocol *protocol, rac_receiver_t receiver) { +@implementation NSObject (RACObjCRuntimeUtilities) + +-(BOOL) rac_setupInvocationObservationForSelector:(SEL)selector protocol:(Protocol *)protocol receiver:(void (^)(void))receiver { SEL aliasSelector = RACAliasForSelector(selector); __block void (^existingReceiver)(void) = objc_getAssociatedObject(self, aliasSelector); @@ -300,3 +302,5 @@ BOOL RACRegisterBlockForSelector(NSObject *self, SEL selector, Protocol *protoco return YES; } + +@end From 0bcd4b638859940ac722583038473373271fe0c2 Mon Sep 17 00:00:00 2001 From: Anders Date: Sun, 9 Oct 2016 14:54:25 +0200 Subject: [PATCH 0444/1028] Use private modulemap. --- ReactiveCocoa.xcodeproj/project.pbxproj | 8 ++++---- ReactiveCocoa/NSObject+Intercepting.swift | 2 +- ReactiveCocoa/ReactiveCocoaPrivate/module.modulemap | 5 +---- .../ReactiveCocoaPrivate/module.private.modulemap | 3 +++ 4 files changed, 9 insertions(+), 9 deletions(-) create mode 100644 ReactiveCocoa/ReactiveCocoaPrivate/module.private.modulemap diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 97692e5a22..c050171f54 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -1331,7 +1331,7 @@ DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate/"; }; name = Debug; }; @@ -1343,7 +1343,7 @@ DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate/"; }; name = Release; }; @@ -1450,7 +1450,7 @@ DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate/"; }; name = Profile; }; @@ -1519,7 +1519,7 @@ DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate/"; }; name = Test; }; diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 30682b30c8..e3b2a85296 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -1,7 +1,7 @@ import Foundation import ReactiveSwift import enum Result.NoError -import ReactiveCocoaPrivate +import ReactiveCocoaUtils.Private extension Reactive where Base: NSObject { /// Create a signal which sends a `next` event at the end of every invocation diff --git a/ReactiveCocoa/ReactiveCocoaPrivate/module.modulemap b/ReactiveCocoa/ReactiveCocoaPrivate/module.modulemap index ceeae718d0..0fe7caf5cd 100644 --- a/ReactiveCocoa/ReactiveCocoaPrivate/module.modulemap +++ b/ReactiveCocoa/ReactiveCocoaPrivate/module.modulemap @@ -1,4 +1 @@ -module ReactiveCocoaPrivate { - header "../RACObjCRuntimeUtilities.h" - export * -} +module ReactiveCocoaUtils {} diff --git a/ReactiveCocoa/ReactiveCocoaPrivate/module.private.modulemap b/ReactiveCocoa/ReactiveCocoaPrivate/module.private.modulemap new file mode 100644 index 0000000000..e696289373 --- /dev/null +++ b/ReactiveCocoa/ReactiveCocoaPrivate/module.private.modulemap @@ -0,0 +1,3 @@ +explicit module ReactiveCocoaUtils.Private { + header "../RACObjCRuntimeUtilities.h" +} From 180a1fde3f2dacc6559f7a14458b221100e6acf4 Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Sun, 9 Oct 2016 19:18:43 +0200 Subject: [PATCH 0445/1028] Fix missing space --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8e5224c5dd..3c7c17f661 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ For more information about the concepts in ReactiveCocoa, see [ReactiveSwift][]. ## Objective-C and Swift After announced Swift, ReactiveCocoa was rewritten in Swift. As of [version -5.0][CHANGELOG], the Objective-C API and the Swift API are entirely separated into different projects([ReactiveObjC][] and [ReactiveSwift][]). +5.0][CHANGELOG], the Objective-C API and the Swift API are entirely separated into different projects ([ReactiveObjC][] and [ReactiveSwift][]). There is a bridge to convert between those APIs ([ReactiveObjCBridge][]) which is mostly meant as a compatibility layer for older ReactiveCocoa projects. From 698d023a1a61fff15a448ba6129ae379a11893df Mon Sep 17 00:00:00 2001 From: Anders Date: Mon, 10 Oct 2016 19:02:57 +0200 Subject: [PATCH 0446/1028] Renamed `CocoaTrigger` to `CocoaTarget`. --- ReactiveCocoa.xcodeproj/project.pbxproj | 20 +++++++++---------- ReactiveCocoa/AppKit/NSControl.swift | 4 ++-- .../{CocoaTrigger.swift => CocoaTarget.swift} | 3 ++- ReactiveCocoa/UIKit/UIControl.swift | 6 +++--- 4 files changed, 17 insertions(+), 16 deletions(-) rename ReactiveCocoa/{CocoaTrigger.swift => CocoaTarget.swift} (75%) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index c050171f54..14fb8db82d 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -87,10 +87,10 @@ 9A1E72BA1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; 9A1E72BB1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; 9A1E72BC1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; - 9A2E425E1DAA6737006D909F /* CocoaTrigger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2E425D1DAA6737006D909F /* CocoaTrigger.swift */; }; - 9A2E425F1DAA6737006D909F /* CocoaTrigger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2E425D1DAA6737006D909F /* CocoaTrigger.swift */; }; - 9A2E42601DAA6737006D909F /* CocoaTrigger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2E425D1DAA6737006D909F /* CocoaTrigger.swift */; }; - 9A2E42611DAA6737006D909F /* CocoaTrigger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2E425D1DAA6737006D909F /* CocoaTrigger.swift */; }; + 9A2E425E1DAA6737006D909F /* CocoaTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */; }; + 9A2E425F1DAA6737006D909F /* CocoaTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */; }; + 9A2E42601DAA6737006D909F /* CocoaTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */; }; + 9A2E42611DAA6737006D909F /* CocoaTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */; }; 9A9DFEE51DA7B5500039EE1B /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; 9A9DFEE91DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; 9A9DFEEA1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; @@ -254,7 +254,7 @@ 9A1D06751D9415FB00ACF44C /* RACObjCRuntimeUtilities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RACObjCRuntimeUtilities.h; sourceTree = ""; }; 9A1D06761D9415FB00ACF44C /* RACObjCRuntimeUtilities.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RACObjCRuntimeUtilities.m; sourceTree = ""; }; 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyValueObservingSpec.swift; sourceTree = ""; }; - 9A2E425D1DAA6737006D909F /* CocoaTrigger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaTrigger.swift; sourceTree = ""; }; + 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaTarget.swift; sourceTree = ""; }; 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssociationSpec.swift; sourceTree = ""; }; 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+KeyValueObserving.swift"; sourceTree = ""; }; 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaAction.swift; sourceTree = ""; }; @@ -520,7 +520,7 @@ 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */, 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */, 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */, - 9A2E425D1DAA6737006D909F /* CocoaTrigger.swift */, + 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */, 9A1D05E91D93E9F100ACF44C /* AppKit */, 9A1D05EB1D93E9F100ACF44C /* UIKit */, D04725ED19E49ED7006002AA /* Supporting Files */, @@ -938,7 +938,7 @@ 4A0E11021D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, CD0C45E11CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9A1D061A1D93EA0100ACF44C /* UIProgressView.swift in Sources */, - 9A2E42611DAA6737006D909F /* CocoaTrigger.swift in Sources */, + 9A2E42611DAA6737006D909F /* CocoaTarget.swift in Sources */, 9A1D06121D93EA0100ACF44C /* UIBarButtonItem.swift in Sources */, 9A1D06111D93EA0100ACF44C /* UIActivityIndicatorView.swift in Sources */, 9A1D061B1D93EA0100ACF44C /* UISegmentedControl.swift in Sources */, @@ -974,7 +974,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 9A2E42601DAA6737006D909F /* CocoaTrigger.swift in Sources */, + 9A2E42601DAA6737006D909F /* CocoaTarget.swift in Sources */, 9A9DFEE51DA7B5500039EE1B /* NSObject+Intercepting.swift in Sources */, 9A1D06791D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9ADE4A931DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, @@ -997,7 +997,7 @@ 9A1D05E01D93E99100ACF44C /* NSObject+Association.swift in Sources */, 9ADE4A7C1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, 9ADE4A961DA6F018005C2AC8 /* NSTextField.swift in Sources */, - 9A2E425E1DAA6737006D909F /* CocoaTrigger.swift in Sources */, + 9A2E425E1DAA6737006D909F /* CocoaTarget.swift in Sources */, 9ADE4A891DA6D206005C2AC8 /* NSControl.swift in Sources */, 9AF0EA751D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, 9ADE4A911DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, @@ -1030,7 +1030,7 @@ 9A1D06061D93EA0000ACF44C /* UIImageView.swift in Sources */, 9AF0EA761D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, CD0C45DF1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, - 9A2E425F1DAA6737006D909F /* CocoaTrigger.swift in Sources */, + 9A2E425F1DAA6737006D909F /* CocoaTarget.swift in Sources */, 9A1D06781D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9A1D060F1D93EA0000ACF44C /* UIView.swift in Sources */, 9A1D06051D93EA0000ACF44C /* UIDatePicker.swift in Sources */, diff --git a/ReactiveCocoa/AppKit/NSControl.swift b/ReactiveCocoa/AppKit/NSControl.swift index 915c5bebbb..f6122a5426 100644 --- a/ReactiveCocoa/AppKit/NSControl.swift +++ b/ReactiveCocoa/AppKit/NSControl.swift @@ -96,9 +96,9 @@ extension Reactive where Base: NSControl { return associatedValue { base in let (signal, observer) = Signal<(), NoError>.pipe() - let receiver = CocoaTrigger(observer) + let receiver = CocoaTarget(observer) base.target = receiver - base.action = #selector(CocoaTrigger.sendNext) + base.action = #selector(CocoaTarget.sendNext) lifetime.ended.observeCompleted { _ = receiver diff --git a/ReactiveCocoa/CocoaTrigger.swift b/ReactiveCocoa/CocoaTarget.swift similarity index 75% rename from ReactiveCocoa/CocoaTrigger.swift rename to ReactiveCocoa/CocoaTarget.swift index a315a7c2eb..dbe58ba4e7 100644 --- a/ReactiveCocoa/CocoaTrigger.swift +++ b/ReactiveCocoa/CocoaTarget.swift @@ -2,7 +2,8 @@ import Foundation import ReactiveSwift import enum Result.NoError -internal class CocoaTrigger: NSObject { +/// A target that accepts action messages. +internal class CocoaTarget: NSObject { let observer: Observer<(), NoError> init(_ observer: Observer<(), NoError>) { diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index 33d9f0006b..474fbc90cb 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -47,9 +47,9 @@ extension Reactive where Base: UIControl { /// A trigger signal. public func trigger(for controlEvents: UIControlEvents) -> Signal<(), NoError> { return Signal { observer in - let receiver = CocoaTrigger(observer) + let receiver = CocoaTarget(observer) base.addTarget(receiver, - action: #selector(CocoaTrigger.sendNext), + action: #selector(CocoaTarget.sendNext), for: controlEvents) let disposable = lifetime.ended.observeCompleted(observer.sendCompleted) @@ -58,7 +58,7 @@ extension Reactive where Base: UIControl { disposable?.dispose() base?.removeTarget(receiver, - action: #selector(CocoaTrigger.sendNext), + action: #selector(CocoaTarget.sendNext), for: controlEvents) } } From 0558476f99defbec58be6a15e6bb0c87538b40f5 Mon Sep 17 00:00:00 2001 From: Anders Date: Mon, 10 Oct 2016 19:30:06 +0200 Subject: [PATCH 0447/1028] Underscored the internal API. --- ReactiveCocoa/NSObject+Intercepting.swift | 2 +- ReactiveCocoa/RACObjCRuntimeUtilities.h | 2 +- ReactiveCocoa/RACObjCRuntimeUtilities.m | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index e3b2a85296..51ab14dc90 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -24,7 +24,7 @@ extension Reactive where Base: NSObject { } let (signal, observer) = Signal<(), NoError>.pipe() - let isSuccessful = base.rac_setupInvocationObservation(for: selector, + let isSuccessful = base._rac_setupInvocationObservation(for: selector, protocol: nil, receiver: observer.send(value:)) assert(isSuccessful) diff --git a/ReactiveCocoa/RACObjCRuntimeUtilities.h b/ReactiveCocoa/RACObjCRuntimeUtilities.h index 0d15dd41f8..d08364d8a4 100644 --- a/ReactiveCocoa/RACObjCRuntimeUtilities.h +++ b/ReactiveCocoa/RACObjCRuntimeUtilities.h @@ -6,7 +6,7 @@ NS_ASSUME_NONNULL_BEGIN /// Register a block which would be triggered when `selector` is called. /// /// Warning: The callee is responsible for synchronization. --(BOOL) rac_setupInvocationObservationForSelector:(SEL)selector protocol:(nullable Protocol *)protocol receiver:(void (^)(void)) receiver; +-(BOOL) _rac_setupInvocationObservationForSelector:(SEL)selector protocol:(nullable Protocol *)protocol receiver:(void (^)(void)) receiver; @end NS_ASSUME_NONNULL_END diff --git a/ReactiveCocoa/RACObjCRuntimeUtilities.m b/ReactiveCocoa/RACObjCRuntimeUtilities.m index b93d6ecdfb..7801768937 100644 --- a/ReactiveCocoa/RACObjCRuntimeUtilities.m +++ b/ReactiveCocoa/RACObjCRuntimeUtilities.m @@ -251,7 +251,7 @@ static Class RACSwizzleClass(NSObject *self) { @implementation NSObject (RACObjCRuntimeUtilities) --(BOOL) rac_setupInvocationObservationForSelector:(SEL)selector protocol:(Protocol *)protocol receiver:(void (^)(void))receiver { +-(BOOL) _rac_setupInvocationObservationForSelector:(SEL)selector protocol:(Protocol *)protocol receiver:(void (^)(void))receiver { SEL aliasSelector = RACAliasForSelector(selector); __block void (^existingReceiver)(void) = objc_getAssociatedObject(self, aliasSelector); From 24cd6230d200b70bddcb049cd21bb5296a1a5f01 Mon Sep 17 00:00:00 2001 From: Anders Date: Sat, 15 Oct 2016 02:50:32 +0200 Subject: [PATCH 0448/1028] Added `NSTextField.reactive.continuousAttributedStringValues`. --- ReactiveCocoa/AppKit/NSTextField.swift | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/ReactiveCocoa/AppKit/NSTextField.swift b/ReactiveCocoa/AppKit/NSTextField.swift index 18166adbc5..a0ced97dcd 100644 --- a/ReactiveCocoa/AppKit/NSTextField.swift +++ b/ReactiveCocoa/AppKit/NSTextField.swift @@ -16,4 +16,19 @@ extension Reactive where Base: NSTextField { return signal } + + /// A signal of values in `NSAttributedString` from the text field upon any + /// changes. + public var continuousAttributedStringValues: Signal { + var signal: Signal! + + NotificationCenter.default + .reactive + .notifications(forName: .NSControlTextDidChange, object: base) + .take(during: lifetime) + .map { ($0.object as! NSTextField).attributedStringValue } + .startWithSignal { innerSignal, _ in signal = innerSignal } + + return signal + } } From e48c43694c212939997f9c382c1886cd99d846e6 Mon Sep 17 00:00:00 2001 From: Anders Date: Sun, 16 Oct 2016 20:14:32 +0200 Subject: [PATCH 0449/1028] Reintroduced `reactive.prepareForReuse` for reusable components. --- ReactiveCocoa/AppKit/ReusableComponents.swift | 4 ++ ReactiveCocoa/Reusable.swift | 10 ++++ ReactiveCocoa/UIKit/ReusableComponents.swift | 5 ++ .../AppKit/ReusableComponentsSpec.swift | 38 ++++++++++++++ .../UIKit/ReusableComponentsSpec.swift | 52 +++++++++++++++++++ 5 files changed, 109 insertions(+) create mode 100644 ReactiveCocoa/AppKit/ReusableComponents.swift create mode 100644 ReactiveCocoa/Reusable.swift create mode 100644 ReactiveCocoa/UIKit/ReusableComponents.swift create mode 100644 ReactiveCocoaTests/AppKit/ReusableComponentsSpec.swift create mode 100644 ReactiveCocoaTests/UIKit/ReusableComponentsSpec.swift diff --git a/ReactiveCocoa/AppKit/ReusableComponents.swift b/ReactiveCocoa/AppKit/ReusableComponents.swift new file mode 100644 index 0000000000..8c94c88451 --- /dev/null +++ b/ReactiveCocoa/AppKit/ReusableComponents.swift @@ -0,0 +1,4 @@ +import AppKit + +extension NSTableCellView: Reusable {} +extension NSCollectionViewItem: Reusable {} diff --git a/ReactiveCocoa/Reusable.swift b/ReactiveCocoa/Reusable.swift new file mode 100644 index 0000000000..c57e1d95bd --- /dev/null +++ b/ReactiveCocoa/Reusable.swift @@ -0,0 +1,10 @@ +import ReactiveSwift +import enum Result.NoError + +public protocol Reusable: class {} + +extension Reactive where Base: NSObject, Base: Reusable { + public var prepareForReuse: Signal<(), NoError> { + return trigger(for: Selector(("prepareForReuse"))) + } +} diff --git a/ReactiveCocoa/UIKit/ReusableComponents.swift b/ReactiveCocoa/UIKit/ReusableComponents.swift new file mode 100644 index 0000000000..8d36b20ae7 --- /dev/null +++ b/ReactiveCocoa/UIKit/ReusableComponents.swift @@ -0,0 +1,5 @@ +import UIKit + +extension UITableViewCell: Reusable {} +extension UITableViewHeaderFooterView: Reusable {} +extension UICollectionReusableView: Reusable {} diff --git a/ReactiveCocoaTests/AppKit/ReusableComponentsSpec.swift b/ReactiveCocoaTests/AppKit/ReusableComponentsSpec.swift new file mode 100644 index 0000000000..29dea1277d --- /dev/null +++ b/ReactiveCocoaTests/AppKit/ReusableComponentsSpec.swift @@ -0,0 +1,38 @@ +import Quick +import Nimble +import Result +import ReactiveSwift +import ReactiveCocoa +import AppKit + +class ReusableComponentsSpec: QuickSpec { + override func spec() { + describe("NSTableCellView") { + let cell = NSTableCellView() + + var isTriggered = false + cell.reactive.prepareForReuse.observeValues { + isTriggered = true + } + + expect(isTriggered) == false + + cell.prepareForReuse() + expect(isTriggered) == true + } + + describe("NSCollectionViewItem") { + let cell = NSCollectionViewItem() + + var isTriggered = false + cell.reactive.prepareForReuse.observeValues { + isTriggered = true + } + + expect(isTriggered) == false + + cell.prepareForReuse() + expect(isTriggered) == true + } + } +} diff --git a/ReactiveCocoaTests/UIKit/ReusableComponentsSpec.swift b/ReactiveCocoaTests/UIKit/ReusableComponentsSpec.swift new file mode 100644 index 0000000000..f1c53d4848 --- /dev/null +++ b/ReactiveCocoaTests/UIKit/ReusableComponentsSpec.swift @@ -0,0 +1,52 @@ +import Quick +import Nimble +import Result +import ReactiveSwift +import ReactiveCocoa +import UIKit + +class ReusableComponentsSpec: QuickSpec { + override func spec() { + describe("UITableViewCell") { + let cell = UITableViewCell() + + var isTriggered = false + cell.reactive.prepareForReuse.observeValues { + isTriggered = true + } + + expect(isTriggered) == false + + cell.prepareForReuse() + expect(isTriggered) == true + } + + describe("UITableViewHeaderFooterView") { + let cell = UITableViewHeaderFooterView() + + var isTriggered = false + cell.reactive.prepareForReuse.observeValues { + isTriggered = true + } + + expect(isTriggered) == false + + cell.prepareForReuse() + expect(isTriggered) == true + } + + describe("UICollectionReusableView") { + let cell = UICollectionReusableView() + + var isTriggered = false + cell.reactive.prepareForReuse.observeValues { + isTriggered = true + } + + expect(isTriggered) == false + + cell.prepareForReuse() + expect(isTriggered) == true + } + } +} From 27d716a4629acb082da36e79c6b861fc551da1ee Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 18 Oct 2016 00:02:15 +0200 Subject: [PATCH 0450/1028] Revert "Reintroduced `reactive.prepareForReuse` for reusable components." This reverts commit e48c43694c212939997f9c382c1886cd99d846e6. --- ReactiveCocoa/AppKit/ReusableComponents.swift | 4 -- ReactiveCocoa/Reusable.swift | 10 ---- ReactiveCocoa/UIKit/ReusableComponents.swift | 5 -- .../AppKit/ReusableComponentsSpec.swift | 38 -------------- .../UIKit/ReusableComponentsSpec.swift | 52 ------------------- 5 files changed, 109 deletions(-) delete mode 100644 ReactiveCocoa/AppKit/ReusableComponents.swift delete mode 100644 ReactiveCocoa/Reusable.swift delete mode 100644 ReactiveCocoa/UIKit/ReusableComponents.swift delete mode 100644 ReactiveCocoaTests/AppKit/ReusableComponentsSpec.swift delete mode 100644 ReactiveCocoaTests/UIKit/ReusableComponentsSpec.swift diff --git a/ReactiveCocoa/AppKit/ReusableComponents.swift b/ReactiveCocoa/AppKit/ReusableComponents.swift deleted file mode 100644 index 8c94c88451..0000000000 --- a/ReactiveCocoa/AppKit/ReusableComponents.swift +++ /dev/null @@ -1,4 +0,0 @@ -import AppKit - -extension NSTableCellView: Reusable {} -extension NSCollectionViewItem: Reusable {} diff --git a/ReactiveCocoa/Reusable.swift b/ReactiveCocoa/Reusable.swift deleted file mode 100644 index c57e1d95bd..0000000000 --- a/ReactiveCocoa/Reusable.swift +++ /dev/null @@ -1,10 +0,0 @@ -import ReactiveSwift -import enum Result.NoError - -public protocol Reusable: class {} - -extension Reactive where Base: NSObject, Base: Reusable { - public var prepareForReuse: Signal<(), NoError> { - return trigger(for: Selector(("prepareForReuse"))) - } -} diff --git a/ReactiveCocoa/UIKit/ReusableComponents.swift b/ReactiveCocoa/UIKit/ReusableComponents.swift deleted file mode 100644 index 8d36b20ae7..0000000000 --- a/ReactiveCocoa/UIKit/ReusableComponents.swift +++ /dev/null @@ -1,5 +0,0 @@ -import UIKit - -extension UITableViewCell: Reusable {} -extension UITableViewHeaderFooterView: Reusable {} -extension UICollectionReusableView: Reusable {} diff --git a/ReactiveCocoaTests/AppKit/ReusableComponentsSpec.swift b/ReactiveCocoaTests/AppKit/ReusableComponentsSpec.swift deleted file mode 100644 index 29dea1277d..0000000000 --- a/ReactiveCocoaTests/AppKit/ReusableComponentsSpec.swift +++ /dev/null @@ -1,38 +0,0 @@ -import Quick -import Nimble -import Result -import ReactiveSwift -import ReactiveCocoa -import AppKit - -class ReusableComponentsSpec: QuickSpec { - override func spec() { - describe("NSTableCellView") { - let cell = NSTableCellView() - - var isTriggered = false - cell.reactive.prepareForReuse.observeValues { - isTriggered = true - } - - expect(isTriggered) == false - - cell.prepareForReuse() - expect(isTriggered) == true - } - - describe("NSCollectionViewItem") { - let cell = NSCollectionViewItem() - - var isTriggered = false - cell.reactive.prepareForReuse.observeValues { - isTriggered = true - } - - expect(isTriggered) == false - - cell.prepareForReuse() - expect(isTriggered) == true - } - } -} diff --git a/ReactiveCocoaTests/UIKit/ReusableComponentsSpec.swift b/ReactiveCocoaTests/UIKit/ReusableComponentsSpec.swift deleted file mode 100644 index f1c53d4848..0000000000 --- a/ReactiveCocoaTests/UIKit/ReusableComponentsSpec.swift +++ /dev/null @@ -1,52 +0,0 @@ -import Quick -import Nimble -import Result -import ReactiveSwift -import ReactiveCocoa -import UIKit - -class ReusableComponentsSpec: QuickSpec { - override func spec() { - describe("UITableViewCell") { - let cell = UITableViewCell() - - var isTriggered = false - cell.reactive.prepareForReuse.observeValues { - isTriggered = true - } - - expect(isTriggered) == false - - cell.prepareForReuse() - expect(isTriggered) == true - } - - describe("UITableViewHeaderFooterView") { - let cell = UITableViewHeaderFooterView() - - var isTriggered = false - cell.reactive.prepareForReuse.observeValues { - isTriggered = true - } - - expect(isTriggered) == false - - cell.prepareForReuse() - expect(isTriggered) == true - } - - describe("UICollectionReusableView") { - let cell = UICollectionReusableView() - - var isTriggered = false - cell.reactive.prepareForReuse.observeValues { - isTriggered = true - } - - expect(isTriggered) == false - - cell.prepareForReuse() - expect(isTriggered) == true - } - } -} From 8392e0df665a1799e2631e6f591f63501d5aa9ae Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 18 Oct 2016 00:14:32 +0200 Subject: [PATCH 0451/1028] Mark `CocoaTarget` as final. --- ReactiveCocoa/CocoaTarget.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa/CocoaTarget.swift b/ReactiveCocoa/CocoaTarget.swift index dbe58ba4e7..9c3ac2e983 100644 --- a/ReactiveCocoa/CocoaTarget.swift +++ b/ReactiveCocoa/CocoaTarget.swift @@ -3,7 +3,7 @@ import ReactiveSwift import enum Result.NoError /// A target that accepts action messages. -internal class CocoaTarget: NSObject { +internal final class CocoaTarget: NSObject { let observer: Observer<(), NoError> init(_ observer: Observer<(), NoError>) { From bc3fe39d24848c5667174109b431a2e73dd64771 Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 18 Oct 2016 00:17:06 +0200 Subject: [PATCH 0452/1028] Query selectors from instances instead of the expected type. --- ReactiveCocoa/AppKit/NSControl.swift | 2 +- ReactiveCocoa/UIKit/UIControl.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ReactiveCocoa/AppKit/NSControl.swift b/ReactiveCocoa/AppKit/NSControl.swift index f6122a5426..3769360336 100644 --- a/ReactiveCocoa/AppKit/NSControl.swift +++ b/ReactiveCocoa/AppKit/NSControl.swift @@ -98,7 +98,7 @@ extension Reactive where Base: NSControl { let receiver = CocoaTarget(observer) base.target = receiver - base.action = #selector(CocoaTarget.sendNext) + base.action = #selector(receiver.sendNext) lifetime.ended.observeCompleted { _ = receiver diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index 474fbc90cb..2161b9cdf4 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -49,7 +49,7 @@ extension Reactive where Base: UIControl { return Signal { observer in let receiver = CocoaTarget(observer) base.addTarget(receiver, - action: #selector(CocoaTarget.sendNext), + action: #selector(receiver.sendNext), for: controlEvents) let disposable = lifetime.ended.observeCompleted(observer.sendCompleted) @@ -58,7 +58,7 @@ extension Reactive where Base: UIControl { disposable?.dispose() base?.removeTarget(receiver, - action: #selector(CocoaTarget.sendNext), + action: #selector(receiver.sendNext), for: controlEvents) } } From 808377c514c655f1f70fa83ea892857435da5a98 Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 18 Oct 2016 00:17:46 +0200 Subject: [PATCH 0453/1028] Use optional chaining in `UIControl.reactive.setAction(_:for:)`. --- ReactiveCocoa/UIKit/UIControl.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index 2161b9cdf4..ef586a2128 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -17,9 +17,7 @@ extension Reactive where Base: UIControl { /// - controlEvents: The control event mask. internal func setAction(_ action: CocoaAction?, for controlEvents: UIControlEvents) { associatedAction.modify { associatedAction in - if let old = associatedAction { - old.disposable.dispose() - } + associatedAction?.disposable.dispose() if let action = action { base.addTarget(action, action: CocoaAction.selector, for: controlEvents) From d8149088a3e5edb91f010f6720f300a4b2cb6873 Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 18 Oct 2016 00:21:28 +0200 Subject: [PATCH 0454/1028] Corrected the type coercions in NSObject reactive extensions. --- ReactiveCocoa/NSObject+Association.swift | 2 +- ReactiveCocoa/NSObject+Intercepting.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoa/NSObject+Association.swift b/ReactiveCocoa/NSObject+Association.swift index b81df2ac3d..24e148f8bd 100644 --- a/ReactiveCocoa/NSObject+Association.swift +++ b/ReactiveCocoa/NSObject+Association.swift @@ -12,7 +12,7 @@ extension Reactive where Base: NSObject { /// - returns: /// The associated value for the specified key. internal func associatedValue(forKey key: StaticString = #function, initial: (Base) -> T) -> T { - var value = objc_getAssociatedObject(base, key.utf8Start) as? T + var value = objc_getAssociatedObject(base, key.utf8Start) as! T? if value == nil { value = initial(base) objc_setAssociatedObject(base, key.utf8Start, value, .OBJC_ASSOCIATION_RETAIN) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 51ab14dc90..8c03345368 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -19,7 +19,7 @@ extension Reactive where Base: NSObject { let map = associatedValue { _ in NSMutableDictionary() } let selectorName = String(describing: selector) as NSString - if let signal = map.object(forKey: selectorName) as? Signal<(), NoError> { + if let signal = map.object(forKey: selectorName) as! Signal<(), NoError>? { return signal } From 5b416fcf25b8837577a96a2789723e8303e661da Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 18 Oct 2016 00:23:12 +0200 Subject: [PATCH 0455/1028] Precondition on invocation swizzling setup. --- ReactiveCocoa/NSObject+Intercepting.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 8c03345368..f9d34c6c76 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -27,7 +27,7 @@ extension Reactive where Base: NSObject { let isSuccessful = base._rac_setupInvocationObservation(for: selector, protocol: nil, receiver: observer.send(value:)) - assert(isSuccessful) + precondition(isSuccessful) lifetime.ended.observeCompleted(observer.sendCompleted) map.setObject(signal, forKey: selectorName) From d8b2b1e0efcc0fda74bf569ce3ddfbbd7e392160 Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 18 Oct 2016 17:03:53 +0200 Subject: [PATCH 0456/1028] Converted UIKit extension tests into Quick specs. --- ReactiveCocoa.xcodeproj/project.pbxproj | 148 +++++++++--------- .../UIKit/UIActivityIndicatorViewSpec.swift | 33 ++++ .../UIKit/UIActivityIndicatorViewTests.swift | 26 --- .../UIKit/UIBarButtonItemSpec.swift | 39 +++++ .../UIKit/UIBarButtonItemTests.swift | 38 ----- ReactiveCocoaTests/UIKit/UIButtonSpec.swift | 61 ++++++++ ReactiveCocoaTests/UIKit/UIButtonTests.swift | 88 ----------- ...ol+EnableSendActionsForControlEvents.swift | 2 +- ReactiveCocoaTests/UIKit/UIControlSpec.swift | 84 ++++++++++ ReactiveCocoaTests/UIKit/UIControlTests.swift | 99 ------------ .../UIKit/UIDatePickerSpec.swift | 47 ++++++ .../UIKit/UIDatePickerTests.swift | 38 ----- .../UIKit/UIImageViewSpec.swift | 51 ++++++ .../UIKit/UIImageViewTests.swift | 64 -------- ReactiveCocoaTests/UIKit/UILabelSpec.swift | 75 +++++++++ ReactiveCocoaTests/UIKit/UILabelTests.swift | 80 ---------- .../UIKit/UIProgressViewSpec.swift | 39 +++++ .../UIKit/UIProgressViewTests.swift | 38 ----- .../UIKit/UISegmentedControlSpec.swift | 26 +++ .../UIKit/UISegmentedControlTests.swift | 21 --- ReactiveCocoaTests/UIKit/UISwitchSpec.swift | 44 ++++++ ReactiveCocoaTests/UIKit/UISwitchTests.swift | 26 --- .../UIKit/UITextFieldSpec.swift | 46 ++++++ .../UIKit/UITextFieldTests.swift | 36 ----- ReactiveCocoaTests/UIKit/UITextViewSpec.swift | 49 ++++++ .../UIKit/UITextViewTests.swift | 36 ----- ReactiveCocoaTests/UIKit/UIViewSpec.swift | 65 ++++++++ ReactiveCocoaTests/UIKit/UIViewTests.swift | 72 --------- 28 files changed, 734 insertions(+), 737 deletions(-) create mode 100644 ReactiveCocoaTests/UIKit/UIActivityIndicatorViewSpec.swift delete mode 100644 ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift create mode 100644 ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift delete mode 100644 ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift create mode 100644 ReactiveCocoaTests/UIKit/UIButtonSpec.swift delete mode 100644 ReactiveCocoaTests/UIKit/UIButtonTests.swift create mode 100644 ReactiveCocoaTests/UIKit/UIControlSpec.swift delete mode 100644 ReactiveCocoaTests/UIKit/UIControlTests.swift create mode 100644 ReactiveCocoaTests/UIKit/UIDatePickerSpec.swift delete mode 100644 ReactiveCocoaTests/UIKit/UIDatePickerTests.swift create mode 100644 ReactiveCocoaTests/UIKit/UIImageViewSpec.swift delete mode 100644 ReactiveCocoaTests/UIKit/UIImageViewTests.swift create mode 100644 ReactiveCocoaTests/UIKit/UILabelSpec.swift delete mode 100644 ReactiveCocoaTests/UIKit/UILabelTests.swift create mode 100644 ReactiveCocoaTests/UIKit/UIProgressViewSpec.swift delete mode 100644 ReactiveCocoaTests/UIKit/UIProgressViewTests.swift create mode 100644 ReactiveCocoaTests/UIKit/UISegmentedControlSpec.swift delete mode 100644 ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift create mode 100644 ReactiveCocoaTests/UIKit/UISwitchSpec.swift delete mode 100644 ReactiveCocoaTests/UIKit/UISwitchTests.swift create mode 100644 ReactiveCocoaTests/UIKit/UITextFieldSpec.swift delete mode 100644 ReactiveCocoaTests/UIKit/UITextFieldTests.swift create mode 100644 ReactiveCocoaTests/UIKit/UITextViewSpec.swift delete mode 100644 ReactiveCocoaTests/UIKit/UITextViewTests.swift create mode 100644 ReactiveCocoaTests/UIKit/UIViewSpec.swift delete mode 100644 ReactiveCocoaTests/UIKit/UIViewTests.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 14fb8db82d..cac92d8c4c 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -51,32 +51,32 @@ 9A1D061F1D93EA0100ACF44C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05FA1D93E9F100ACF44C /* UITextField.swift */; }; 9A1D06201D93EA0100ACF44C /* UITextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05FB1D93E9F100ACF44C /* UITextView.swift */; }; 9A1D06211D93EA0100ACF44C /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05FC1D93E9F100ACF44C /* UIView.swift */; }; - 9A1D06361D93EA7E00ACF44C /* UIActivityIndicatorViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06241D93EA7E00ACF44C /* UIActivityIndicatorViewTests.swift */; }; - 9A1D06371D93EA7E00ACF44C /* UIActivityIndicatorViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06241D93EA7E00ACF44C /* UIActivityIndicatorViewTests.swift */; }; - 9A1D06381D93EA7E00ACF44C /* UIBarButtonItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06251D93EA7E00ACF44C /* UIBarButtonItemTests.swift */; }; - 9A1D06391D93EA7E00ACF44C /* UIBarButtonItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06251D93EA7E00ACF44C /* UIBarButtonItemTests.swift */; }; - 9A1D063A1D93EA7E00ACF44C /* UIButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06261D93EA7E00ACF44C /* UIButtonTests.swift */; }; - 9A1D063B1D93EA7E00ACF44C /* UIButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06261D93EA7E00ACF44C /* UIButtonTests.swift */; }; + 9A1D06361D93EA7E00ACF44C /* UIActivityIndicatorViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06241D93EA7E00ACF44C /* UIActivityIndicatorViewSpec.swift */; }; + 9A1D06371D93EA7E00ACF44C /* UIActivityIndicatorViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06241D93EA7E00ACF44C /* UIActivityIndicatorViewSpec.swift */; }; + 9A1D06381D93EA7E00ACF44C /* UIBarButtonItemSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06251D93EA7E00ACF44C /* UIBarButtonItemSpec.swift */; }; + 9A1D06391D93EA7E00ACF44C /* UIBarButtonItemSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06251D93EA7E00ACF44C /* UIBarButtonItemSpec.swift */; }; + 9A1D063A1D93EA7E00ACF44C /* UIButtonSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06261D93EA7E00ACF44C /* UIButtonSpec.swift */; }; + 9A1D063B1D93EA7E00ACF44C /* UIButtonSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06261D93EA7E00ACF44C /* UIButtonSpec.swift */; }; 9A1D063E1D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06281D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift */; }; 9A1D063F1D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06281D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift */; }; - 9A1D06401D93EA7E00ACF44C /* UIControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06291D93EA7E00ACF44C /* UIControlTests.swift */; }; - 9A1D06411D93EA7E00ACF44C /* UIControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06291D93EA7E00ACF44C /* UIControlTests.swift */; }; - 9A1D06421D93EA7E00ACF44C /* UIDatePickerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062A1D93EA7E00ACF44C /* UIDatePickerTests.swift */; }; - 9A1D06441D93EA7E00ACF44C /* UIImageViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062B1D93EA7E00ACF44C /* UIImageViewTests.swift */; }; - 9A1D06451D93EA7E00ACF44C /* UIImageViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062B1D93EA7E00ACF44C /* UIImageViewTests.swift */; }; - 9A1D06461D93EA7E00ACF44C /* UILabelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062C1D93EA7E00ACF44C /* UILabelTests.swift */; }; - 9A1D06471D93EA7E00ACF44C /* UILabelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062C1D93EA7E00ACF44C /* UILabelTests.swift */; }; - 9A1D06481D93EA7E00ACF44C /* UIProgressViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062D1D93EA7E00ACF44C /* UIProgressViewTests.swift */; }; - 9A1D06491D93EA7E00ACF44C /* UIProgressViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062D1D93EA7E00ACF44C /* UIProgressViewTests.swift */; }; - 9A1D064A1D93EA7E00ACF44C /* UISegmentedControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062E1D93EA7E00ACF44C /* UISegmentedControlTests.swift */; }; - 9A1D064B1D93EA7E00ACF44C /* UISegmentedControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062E1D93EA7E00ACF44C /* UISegmentedControlTests.swift */; }; - 9A1D064C1D93EA7E00ACF44C /* UISwitchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062F1D93EA7E00ACF44C /* UISwitchTests.swift */; }; - 9A1D06521D93EA7E00ACF44C /* UITextFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06321D93EA7E00ACF44C /* UITextFieldTests.swift */; }; - 9A1D06531D93EA7E00ACF44C /* UITextFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06321D93EA7E00ACF44C /* UITextFieldTests.swift */; }; - 9A1D06541D93EA7E00ACF44C /* UITextViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06331D93EA7E00ACF44C /* UITextViewTests.swift */; }; - 9A1D06551D93EA7E00ACF44C /* UITextViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06331D93EA7E00ACF44C /* UITextViewTests.swift */; }; - 9A1D06581D93EA7E00ACF44C /* UIViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06351D93EA7E00ACF44C /* UIViewTests.swift */; }; - 9A1D06591D93EA7E00ACF44C /* UIViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06351D93EA7E00ACF44C /* UIViewTests.swift */; }; + 9A1D06401D93EA7E00ACF44C /* UIControlSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06291D93EA7E00ACF44C /* UIControlSpec.swift */; }; + 9A1D06411D93EA7E00ACF44C /* UIControlSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06291D93EA7E00ACF44C /* UIControlSpec.swift */; }; + 9A1D06421D93EA7E00ACF44C /* UIDatePickerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062A1D93EA7E00ACF44C /* UIDatePickerSpec.swift */; }; + 9A1D06441D93EA7E00ACF44C /* UIImageViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062B1D93EA7E00ACF44C /* UIImageViewSpec.swift */; }; + 9A1D06451D93EA7E00ACF44C /* UIImageViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062B1D93EA7E00ACF44C /* UIImageViewSpec.swift */; }; + 9A1D06461D93EA7E00ACF44C /* UILabelSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062C1D93EA7E00ACF44C /* UILabelSpec.swift */; }; + 9A1D06471D93EA7E00ACF44C /* UILabelSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062C1D93EA7E00ACF44C /* UILabelSpec.swift */; }; + 9A1D06481D93EA7E00ACF44C /* UIProgressViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062D1D93EA7E00ACF44C /* UIProgressViewSpec.swift */; }; + 9A1D06491D93EA7E00ACF44C /* UIProgressViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062D1D93EA7E00ACF44C /* UIProgressViewSpec.swift */; }; + 9A1D064A1D93EA7E00ACF44C /* UISegmentedControlSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062E1D93EA7E00ACF44C /* UISegmentedControlSpec.swift */; }; + 9A1D064B1D93EA7E00ACF44C /* UISegmentedControlSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062E1D93EA7E00ACF44C /* UISegmentedControlSpec.swift */; }; + 9A1D064C1D93EA7E00ACF44C /* UISwitchSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D062F1D93EA7E00ACF44C /* UISwitchSpec.swift */; }; + 9A1D06521D93EA7E00ACF44C /* UITextFieldSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06321D93EA7E00ACF44C /* UITextFieldSpec.swift */; }; + 9A1D06531D93EA7E00ACF44C /* UITextFieldSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06321D93EA7E00ACF44C /* UITextFieldSpec.swift */; }; + 9A1D06541D93EA7E00ACF44C /* UITextViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06331D93EA7E00ACF44C /* UITextViewSpec.swift */; }; + 9A1D06551D93EA7E00ACF44C /* UITextViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06331D93EA7E00ACF44C /* UITextViewSpec.swift */; }; + 9A1D06581D93EA7E00ACF44C /* UIViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06351D93EA7E00ACF44C /* UIViewSpec.swift */; }; + 9A1D06591D93EA7E00ACF44C /* UIViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06351D93EA7E00ACF44C /* UIViewSpec.swift */; }; 9A1D065B1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; 9A1D065C1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; 9A1D065E1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; @@ -236,20 +236,20 @@ 9A1D05FA1D93E9F100ACF44C /* UITextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITextField.swift; sourceTree = ""; }; 9A1D05FB1D93E9F100ACF44C /* UITextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITextView.swift; sourceTree = ""; }; 9A1D05FC1D93E9F100ACF44C /* UIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = ""; }; - 9A1D06241D93EA7E00ACF44C /* UIActivityIndicatorViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorViewTests.swift; sourceTree = ""; }; - 9A1D06251D93EA7E00ACF44C /* UIBarButtonItemTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBarButtonItemTests.swift; sourceTree = ""; }; - 9A1D06261D93EA7E00ACF44C /* UIButtonTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIButtonTests.swift; sourceTree = ""; }; + 9A1D06241D93EA7E00ACF44C /* UIActivityIndicatorViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIActivityIndicatorViewSpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 9A1D06251D93EA7E00ACF44C /* UIBarButtonItemSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIBarButtonItemSpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 9A1D06261D93EA7E00ACF44C /* UIButtonSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIButtonSpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 9A1D06281D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIControl+EnableSendActionsForControlEvents.swift"; sourceTree = ""; }; - 9A1D06291D93EA7E00ACF44C /* UIControlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIControlTests.swift; sourceTree = ""; }; - 9A1D062A1D93EA7E00ACF44C /* UIDatePickerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIDatePickerTests.swift; sourceTree = ""; }; - 9A1D062B1D93EA7E00ACF44C /* UIImageViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageViewTests.swift; sourceTree = ""; }; - 9A1D062C1D93EA7E00ACF44C /* UILabelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabelTests.swift; sourceTree = ""; }; - 9A1D062D1D93EA7E00ACF44C /* UIProgressViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIProgressViewTests.swift; sourceTree = ""; }; - 9A1D062E1D93EA7E00ACF44C /* UISegmentedControlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISegmentedControlTests.swift; sourceTree = ""; }; - 9A1D062F1D93EA7E00ACF44C /* UISwitchTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISwitchTests.swift; sourceTree = ""; }; - 9A1D06321D93EA7E00ACF44C /* UITextFieldTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextFieldTests.swift; sourceTree = ""; }; - 9A1D06331D93EA7E00ACF44C /* UITextViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextViewTests.swift; sourceTree = ""; }; - 9A1D06351D93EA7E00ACF44C /* UIViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewTests.swift; sourceTree = ""; }; + 9A1D06291D93EA7E00ACF44C /* UIControlSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIControlSpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 9A1D062A1D93EA7E00ACF44C /* UIDatePickerSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIDatePickerSpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 9A1D062B1D93EA7E00ACF44C /* UIImageViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIImageViewSpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 9A1D062C1D93EA7E00ACF44C /* UILabelSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UILabelSpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 9A1D062D1D93EA7E00ACF44C /* UIProgressViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIProgressViewSpec.swift; sourceTree = ""; }; + 9A1D062E1D93EA7E00ACF44C /* UISegmentedControlSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISegmentedControlSpec.swift; sourceTree = ""; }; + 9A1D062F1D93EA7E00ACF44C /* UISwitchSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISwitchSpec.swift; sourceTree = ""; }; + 9A1D06321D93EA7E00ACF44C /* UITextFieldSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextFieldSpec.swift; sourceTree = ""; }; + 9A1D06331D93EA7E00ACF44C /* UITextViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextViewSpec.swift; sourceTree = ""; }; + 9A1D06351D93EA7E00ACF44C /* UIViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewSpec.swift; sourceTree = ""; }; 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Intercepting.swift"; sourceTree = ""; }; 9A1D06751D9415FB00ACF44C /* RACObjCRuntimeUtilities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RACObjCRuntimeUtilities.h; sourceTree = ""; }; 9A1D06761D9415FB00ACF44C /* RACObjCRuntimeUtilities.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RACObjCRuntimeUtilities.m; sourceTree = ""; }; @@ -429,20 +429,20 @@ 9A1D06231D93EA7E00ACF44C /* UIKit */ = { isa = PBXGroup; children = ( - 9A1D06241D93EA7E00ACF44C /* UIActivityIndicatorViewTests.swift */, - 9A1D06251D93EA7E00ACF44C /* UIBarButtonItemTests.swift */, - 9A1D06261D93EA7E00ACF44C /* UIButtonTests.swift */, + 9A1D06241D93EA7E00ACF44C /* UIActivityIndicatorViewSpec.swift */, + 9A1D06251D93EA7E00ACF44C /* UIBarButtonItemSpec.swift */, + 9A1D06261D93EA7E00ACF44C /* UIButtonSpec.swift */, 9A1D06281D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift */, - 9A1D06291D93EA7E00ACF44C /* UIControlTests.swift */, - 9A1D062A1D93EA7E00ACF44C /* UIDatePickerTests.swift */, - 9A1D062B1D93EA7E00ACF44C /* UIImageViewTests.swift */, - 9A1D062C1D93EA7E00ACF44C /* UILabelTests.swift */, - 9A1D062D1D93EA7E00ACF44C /* UIProgressViewTests.swift */, - 9A1D062E1D93EA7E00ACF44C /* UISegmentedControlTests.swift */, - 9A1D062F1D93EA7E00ACF44C /* UISwitchTests.swift */, - 9A1D06321D93EA7E00ACF44C /* UITextFieldTests.swift */, - 9A1D06331D93EA7E00ACF44C /* UITextViewTests.swift */, - 9A1D06351D93EA7E00ACF44C /* UIViewTests.swift */, + 9A1D06291D93EA7E00ACF44C /* UIControlSpec.swift */, + 9A1D062A1D93EA7E00ACF44C /* UIDatePickerSpec.swift */, + 9A1D062B1D93EA7E00ACF44C /* UIImageViewSpec.swift */, + 9A1D062C1D93EA7E00ACF44C /* UILabelSpec.swift */, + 9A1D062D1D93EA7E00ACF44C /* UIProgressViewSpec.swift */, + 9A1D062E1D93EA7E00ACF44C /* UISegmentedControlSpec.swift */, + 9A1D062F1D93EA7E00ACF44C /* UISwitchSpec.swift */, + 9A1D06321D93EA7E00ACF44C /* UITextFieldSpec.swift */, + 9A1D06331D93EA7E00ACF44C /* UITextViewSpec.swift */, + 9A1D06351D93EA7E00ACF44C /* UIViewSpec.swift */, ); path = UIKit; sourceTree = ""; @@ -949,24 +949,24 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 9A1D06591D93EA7E00ACF44C /* UIViewTests.swift in Sources */, + 9A1D06591D93EA7E00ACF44C /* UIViewSpec.swift in Sources */, 7DFBED281CDB8DE300EE435B /* DynamicPropertySpec.swift in Sources */, 9A9DFEEB1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */, CD8401851CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, 9A1E72BC1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, - 9A1D06451D93EA7E00ACF44C /* UIImageViewTests.swift in Sources */, - 9A1D06551D93EA7E00ACF44C /* UITextViewTests.swift in Sources */, - 9A1D063B1D93EA7E00ACF44C /* UIButtonTests.swift in Sources */, - 9A1D064B1D93EA7E00ACF44C /* UISegmentedControlTests.swift in Sources */, + 9A1D06451D93EA7E00ACF44C /* UIImageViewSpec.swift in Sources */, + 9A1D06551D93EA7E00ACF44C /* UITextViewSpec.swift in Sources */, + 9A1D063B1D93EA7E00ACF44C /* UIButtonSpec.swift in Sources */, + 9A1D064B1D93EA7E00ACF44C /* UISegmentedControlSpec.swift in Sources */, 9A1D063F1D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift in Sources */, - 9A1D06371D93EA7E00ACF44C /* UIActivityIndicatorViewTests.swift in Sources */, - 9A1D06471D93EA7E00ACF44C /* UILabelTests.swift in Sources */, - 9A1D06391D93EA7E00ACF44C /* UIBarButtonItemTests.swift in Sources */, + 9A1D06371D93EA7E00ACF44C /* UIActivityIndicatorViewSpec.swift in Sources */, + 9A1D06471D93EA7E00ACF44C /* UILabelSpec.swift in Sources */, + 9A1D06391D93EA7E00ACF44C /* UIBarButtonItemSpec.swift in Sources */, 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */, - 9A1D06411D93EA7E00ACF44C /* UIControlTests.swift in Sources */, - 9A1D06491D93EA7E00ACF44C /* UIProgressViewTests.swift in Sources */, + 9A1D06411D93EA7E00ACF44C /* UIControlSpec.swift in Sources */, + 9A1D06491D93EA7E00ACF44C /* UIProgressViewSpec.swift in Sources */, BEE020661D637B0000DF261F /* TestError.swift in Sources */, - 9A1D06531D93EA7E00ACF44C /* UITextFieldTests.swift in Sources */, + 9A1D06531D93EA7E00ACF44C /* UITextFieldSpec.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1055,26 +1055,26 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 9A1D06581D93EA7E00ACF44C /* UIViewTests.swift in Sources */, + 9A1D06581D93EA7E00ACF44C /* UIViewSpec.swift in Sources */, D0A2260F1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */, BFA6B94E1A7604D500C846D1 /* SignalProducerNimbleMatchers.swift in Sources */, B696FB821A7640C00075236D /* TestError.swift in Sources */, - 9A1D064C1D93EA7E00ACF44C /* UISwitchTests.swift in Sources */, - 9A1D06441D93EA7E00ACF44C /* UIImageViewTests.swift in Sources */, - 9A1D06541D93EA7E00ACF44C /* UITextViewTests.swift in Sources */, - 9A1D063A1D93EA7E00ACF44C /* UIButtonTests.swift in Sources */, - 9A1D064A1D93EA7E00ACF44C /* UISegmentedControlTests.swift in Sources */, + 9A1D064C1D93EA7E00ACF44C /* UISwitchSpec.swift in Sources */, + 9A1D06441D93EA7E00ACF44C /* UIImageViewSpec.swift in Sources */, + 9A1D06541D93EA7E00ACF44C /* UITextViewSpec.swift in Sources */, + 9A1D063A1D93EA7E00ACF44C /* UIButtonSpec.swift in Sources */, + 9A1D064A1D93EA7E00ACF44C /* UISegmentedControlSpec.swift in Sources */, 9A1D063E1D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift in Sources */, - 9A1D06361D93EA7E00ACF44C /* UIActivityIndicatorViewTests.swift in Sources */, - 9A1D06461D93EA7E00ACF44C /* UILabelTests.swift in Sources */, + 9A1D06361D93EA7E00ACF44C /* UIActivityIndicatorViewSpec.swift in Sources */, + 9A1D06461D93EA7E00ACF44C /* UILabelSpec.swift in Sources */, 9A9DFEEA1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */, - 9A1D06421D93EA7E00ACF44C /* UIDatePickerTests.swift in Sources */, - 9A1D06381D93EA7E00ACF44C /* UIBarButtonItemTests.swift in Sources */, + 9A1D06421D93EA7E00ACF44C /* UIDatePickerSpec.swift in Sources */, + 9A1D06381D93EA7E00ACF44C /* UIBarButtonItemSpec.swift in Sources */, 9A1E72BB1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, - 9A1D06401D93EA7E00ACF44C /* UIControlTests.swift in Sources */, - 9A1D06481D93EA7E00ACF44C /* UIProgressViewTests.swift in Sources */, + 9A1D06401D93EA7E00ACF44C /* UIControlSpec.swift in Sources */, + 9A1D06481D93EA7E00ACF44C /* UIProgressViewSpec.swift in Sources */, CD8401841CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, - 9A1D06521D93EA7E00ACF44C /* UITextFieldTests.swift in Sources */, + 9A1D06521D93EA7E00ACF44C /* UITextFieldSpec.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewSpec.swift b/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewSpec.swift new file mode 100644 index 0000000000..187089a593 --- /dev/null +++ b/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewSpec.swift @@ -0,0 +1,33 @@ +import Quick +import Nimble +import ReactiveSwift +import ReactiveCocoa +import Result + +class UIActivityIndicatorSpec: QuickSpec { + override func spec() { + var activityIndicatorView: UIActivityIndicatorView! + weak var _activityIndicatorView: UIActivityIndicatorView? + + beforeEach { + activityIndicatorView = UIActivityIndicatorView(frame: .zero) + _activityIndicatorView = activityIndicatorView + } + + afterEach { + activityIndicatorView = nil + expect(_activityIndicatorView).to(beNil()) + } + + it("should accept changes from bindings to its animating state") { + let (pipeSignal, observer) = Signal.pipe() + activityIndicatorView.reactive.isAnimating <~ SignalProducer(signal: pipeSignal) + + observer.send(value: true) + expect(activityIndicatorView.isAnimating) == true + + observer.send(value: false) + expect(activityIndicatorView.isAnimating) == false + } + } +} diff --git a/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift b/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift deleted file mode 100644 index bb4605730a..0000000000 --- a/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift +++ /dev/null @@ -1,26 +0,0 @@ -import XCTest -import ReactiveSwift -import ReactiveCocoa -import Result - -class UIActivityIndicatorTests: XCTestCase { - weak var _activityIndicatorView: UIActivityIndicatorView? - - override func tearDown() { - XCTAssert(_activityIndicatorView == nil, "Retain cycle detected in UIActivityIndicatorView properties") - super.tearDown() - } - - func testAnimatingProperty() { - let indicatorView = UIActivityIndicatorView(frame: CGRect.zero) - _activityIndicatorView = indicatorView - - let (pipeSignal, observer) = Signal.pipe() - indicatorView.reactive.isAnimating <~ SignalProducer(signal: pipeSignal) - - observer.send(value: true) - XCTAssertTrue(indicatorView.isAnimating) - observer.send(value: false) - XCTAssertFalse(indicatorView.isAnimating) - } -} diff --git a/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift b/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift new file mode 100644 index 0000000000..49b8e0df0b --- /dev/null +++ b/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift @@ -0,0 +1,39 @@ +import Quick +import Nimble +import ReactiveSwift +import ReactiveCocoa +import UIKit +import enum Result.NoError + +class UIBarButtonItemSpec: QuickSpec { + override func spec() { + var barButtonItem: UIBarButtonItem! + weak var _barButtonItem: UIBarButtonItem? + + beforeEach { + barButtonItem = UIBarButtonItem() + _barButtonItem = barButtonItem + } + + afterEach { + barButtonItem = nil + expect(_barButtonItem).to(beNil()) + } + + it("should not be retained with the presence of a `pressed` action") { + let action = Action<(),(),NoError> { SignalProducer(value: ()) } + barButtonItem.reactive.pressed = CocoaAction(action) + } + + it("should accept changes from bindings to its enabling state") { + let (pipeSignal, observer) = Signal.pipe() + barButtonItem.reactive.isEnabled <~ SignalProducer(signal: pipeSignal) + + observer.send(value: false) + expect(barButtonItem.isEnabled) == false + + observer.send(value: true) + expect(barButtonItem.isEnabled) == true + } + } +} diff --git a/ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift b/ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift deleted file mode 100644 index 0613dfbbac..0000000000 --- a/ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift +++ /dev/null @@ -1,38 +0,0 @@ -import ReactiveSwift -import ReactiveCocoa -import UIKit -import XCTest -import enum Result.NoError - -class UIBarButtonItemTests: XCTestCase { - weak var _barButtonItem: UIBarButtonItem? - - override func tearDown() { - XCTAssert(_barButtonItem == nil, "Retain cycle detected in UIBarButtonItem properties") - super.tearDown() - } - - func testActionPropertyDoesntCreateRetainCycle() { - let barButtonItem = UIBarButtonItem() - _barButtonItem = barButtonItem - - let action = Action<(),(),NoError> { - SignalProducer(value: ()) - } - - barButtonItem.reactive.pressed = CocoaAction(action) - } - - func testEnabledProperty() { - let barButtonItem = UIBarButtonItem() - barButtonItem.isEnabled = true - - let (pipeSignal, observer) = Signal.pipe() - barButtonItem.reactive.isEnabled <~ SignalProducer(signal: pipeSignal) - - observer.send(value: false) - XCTAssertFalse(barButtonItem.isEnabled) - observer.send(value: true) - XCTAssertTrue(barButtonItem.isEnabled) - } -} diff --git a/ReactiveCocoaTests/UIKit/UIButtonSpec.swift b/ReactiveCocoaTests/UIKit/UIButtonSpec.swift new file mode 100644 index 0000000000..5f4c571c11 --- /dev/null +++ b/ReactiveCocoaTests/UIKit/UIButtonSpec.swift @@ -0,0 +1,61 @@ +import Quick +import Nimble +import ReactiveSwift +import ReactiveCocoa +import UIKit +import enum Result.NoError + +class UIButtonSpec: QuickSpec { + override func spec() { + var button: UIButton! + weak var _button: UIButton? + + beforeEach { + button = UIButton(frame: .zero) + _button = button + } + + afterEach { + button = nil + expect(_button).to(beNil()) + } + + it("should accept changes from bindings to its titles under different states") { + let firstTitle = "First title" + let secondTitle = "Second title" + + let (pipeSignal, observer) = Signal.pipe() + button.reactive.title <~ SignalProducer(signal: pipeSignal) + button.setTitle("", for: .selected) + button.setTitle("", for: .highlighted) + + observer.send(value: firstTitle) + expect(button.title(for: UIControlState())) == firstTitle + expect(button.title(for: .highlighted)) == "" + expect(button.title(for: .selected)) == "" + + observer.send(value: secondTitle) + expect(button.title(for: UIControlState())) == secondTitle + expect(button.title(for: .highlighted)) == "" + expect(button.title(for: .selected)) == "" + } + + it("should execute the `pressed` action upon receiving a `touchUpInside` action message.") { + button.isEnabled = true + button.isUserInteractionEnabled = true + + let pressed = MutableProperty(false) + let action = Action<(), Bool, NoError> { _ in + SignalProducer(value: true) + } + + pressed <~ SignalProducer(signal: action.values) + + button.reactive.pressed = CocoaAction(action) + expect(pressed.value) == false + + button.sendActions(for: .touchUpInside) + expect(pressed.value) == true + } + } +} diff --git a/ReactiveCocoaTests/UIKit/UIButtonTests.swift b/ReactiveCocoaTests/UIKit/UIButtonTests.swift deleted file mode 100644 index ee185e4d39..0000000000 --- a/ReactiveCocoaTests/UIKit/UIButtonTests.swift +++ /dev/null @@ -1,88 +0,0 @@ -import ReactiveSwift -import ReactiveCocoa -import UIKit -import XCTest -import enum Result.NoError - -extension UIButton { - static func button() -> UIButton { - let button = UIButton(type: UIButtonType.custom) - return button - } -} - -class UIButtonTests: XCTestCase { - weak var _button: UIButton? - - override func tearDown() { - XCTAssert(_button == nil, "Retain cycle detected in UIButton properties") - super.tearDown() - } - - func testEnabledPropertyDoesntCreateRetainCycle() { - let button = UIButton(frame: CGRect.zero) - _button = button - - button.reactive.isEnabled <~ SignalProducer(value: false) - XCTAssert(_button?.isEnabled == false) - } - - func testPressedPropertyDoesntCreateRetainCycle() { - let button = UIButton(frame: CGRect.zero) - _button = button - - let action = Action<(),(),NoError> { - SignalProducer(value: ()) - } - - button.reactive.pressed = CocoaAction(action) - } - - func testTitlePropertyDoesntCreateRetainCycle() { - let button = UIButton(frame: CGRect.zero) - _button = button - - button.reactive.title <~ SignalProducer(value: "button") - XCTAssert(_button?.title(for: UIControlState()) == "button") - } - - func testTitleProperty() { - let firstTitle = "First title" - let secondTitle = "Second title" - let button = UIButton(frame: CGRect.zero) - let (pipeSignal, observer) = Signal.pipe() - button.reactive.title <~ SignalProducer(signal: pipeSignal) - button.setTitle("", for: .selected) - button.setTitle("", for: .highlighted) - - observer.send(value: firstTitle) - XCTAssertEqual(button.title(for: UIControlState()), firstTitle) - XCTAssertEqual(button.title(for: .highlighted), "") - XCTAssertEqual(button.title(for: .selected), "") - - observer.send(value: secondTitle) - XCTAssertEqual(button.title(for: UIControlState()), secondTitle) - XCTAssertEqual(button.title(for: .highlighted), "") - XCTAssertEqual(button.title(for: .selected), "") - } - - func testPressedProperty() { - let button = UIButton(frame: CGRect.zero) - button.isEnabled = true - button.isUserInteractionEnabled = true - - let pressed = MutableProperty(false) - let action = Action<(), Bool, NoError> { _ in - SignalProducer(value: true) - } - - pressed <~ SignalProducer(signal: action.values) - button.reactive.pressed = CocoaAction(action) - - XCTAssertFalse(pressed.value) - - button.sendActions(for: .touchUpInside) - - XCTAssertTrue(pressed.value) - } -} diff --git a/ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift b/ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift index 0cac9de691..886b336d55 100644 --- a/ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift +++ b/ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift @@ -31,7 +31,7 @@ private let rac_swizzleToken: Void = { /// the pair target+action. extension UIControl { override open class func initialize() { - if self !== UIControl.self { + guard self === UIControl.self else { return } diff --git a/ReactiveCocoaTests/UIKit/UIControlSpec.swift b/ReactiveCocoaTests/UIKit/UIControlSpec.swift new file mode 100644 index 0000000000..7a5ad0a107 --- /dev/null +++ b/ReactiveCocoaTests/UIKit/UIControlSpec.swift @@ -0,0 +1,84 @@ +import ReactiveSwift +import ReactiveCocoa +import UIKit +import Quick +import Nimble +import enum Result.NoError + +class UIControlSpec: QuickSpec { + override func spec() { + var control: UIControl! + weak var _control: UIControl? + + beforeEach { + control = UIControl(frame: .zero) + _control = control + } + afterEach { + control = nil + expect(_control).to(beNil()) + } + + it("should accept changes from bindings to its enabling state") { + control.isEnabled = false + + let (pipeSignal, observer) = Signal.pipe() + control.reactive.isEnabled <~ SignalProducer(signal: pipeSignal) + + observer.send(value: true) + expect(control.isEnabled) == true + + observer.send(value: false) + expect(control.isEnabled) == false + } + + it("should accept changes from bindings to its selecting state") { + control.isSelected = false + + let (pipeSignal, observer) = Signal.pipe() + control.reactive.isSelected <~ SignalProducer(signal: pipeSignal) + + observer.send(value: true) + expect(control.isSelected) == true + + observer.send(value: false) + expect(control.isSelected) == false + } + + it("should accept changes from bindings to its highlighting state") { + control.isHighlighted = false + + let (pipeSignal, observer) = Signal.pipe() + control.reactive.isHighlighted <~ SignalProducer(signal: pipeSignal) + + observer.send(value: true) + expect(control.isHighlighted) == true + + observer.send(value: false) + expect(control.isHighlighted) == false + } + + it("should accept changes from mutliple bindings to its states") { + control.isSelected = false + control.isEnabled = false + + let (pipeSignalSelected, observerSelected) = Signal.pipe() + let (pipeSignalEnabled, observerEnabled) = Signal.pipe() + control.reactive.isSelected <~ SignalProducer(signal: pipeSignalSelected) + control.reactive.isEnabled <~ SignalProducer(signal: pipeSignalEnabled) + + observerSelected.send(value: true) + observerEnabled.send(value: true) + expect(control.isEnabled) == true + expect(control.isSelected) == true + + observerSelected.send(value: false) + expect(control.isEnabled) == true + expect(control.isSelected) == false + + observerEnabled.send(value: false) + expect(control.isEnabled) == false + expect(control.isSelected) == false + } + } +} diff --git a/ReactiveCocoaTests/UIKit/UIControlTests.swift b/ReactiveCocoaTests/UIKit/UIControlTests.swift deleted file mode 100644 index 04f6e10102..0000000000 --- a/ReactiveCocoaTests/UIKit/UIControlTests.swift +++ /dev/null @@ -1,99 +0,0 @@ -import ReactiveSwift -import ReactiveCocoa -import UIKit -import XCTest -import enum Result.NoError - -class UIControlTests: XCTestCase { - weak var _control: UIControl? - - override func tearDown() { - XCTAssert(_control == nil, "Retain cycle detected in UIControl properties") - super.tearDown() - } - - func testEnabledPropertyDoesntCreateRetainCycle() { - let control = UIControl(frame: CGRect.zero) - _control = control - - control.reactive.isEnabled <~ SignalProducer(value: false) - XCTAssert(_control?.isEnabled == false) - } - - func testSelectedPropertyDoesntCreateRetainCycle() { - let control = UIControl(frame: CGRect.zero) - _control = control - - control.reactive.isSelected <~ SignalProducer(value: true) - XCTAssert(_control?.isSelected == true) - } - - func testHighlightedPropertyDoesntCreateRetainCycle() { - let control = UIControl(frame: CGRect.zero) - _control = control - - control.reactive.isHighlighted <~ SignalProducer(value: true) - XCTAssert(_control?.isHighlighted == true) - } - - func testEnabledProperty () { - let control = UIControl(frame: CGRect.zero) - control.isEnabled = false - - let (pipeSignal, observer) = Signal.pipe() - control.reactive.isEnabled <~ SignalProducer(signal: pipeSignal) - - observer.send(value: true) - XCTAssertTrue(control.isEnabled) - observer.send(value: false) - XCTAssertFalse(control.isEnabled) - } - - func testSelectedProperty() { - let control = UIControl(frame: CGRect.zero) - control.isSelected = false - - let (pipeSignal, observer) = Signal.pipe() - control.reactive.isSelected <~ SignalProducer(signal: pipeSignal) - - observer.send(value: true) - XCTAssertTrue(control.isSelected) - observer.send(value: false) - XCTAssertFalse(control.isSelected) - } - - func testHighlightedProperty() { - let control = UIControl(frame: CGRect.zero) - control.isHighlighted = false - - let (pipeSignal, observer) = Signal.pipe() - control.reactive.isHighlighted <~ SignalProducer(signal: pipeSignal) - - observer.send(value: true) - XCTAssertTrue(control.isHighlighted) - observer.send(value: false) - XCTAssertFalse(control.isHighlighted) - } - - func testEnabledAndSelectedProperty() { - let control = UIControl(frame: CGRect.zero) - control.isSelected = false - control.isEnabled = false - - let (pipeSignalSelected, observerSelected) = Signal.pipe() - let (pipeSignalEnabled, observerEnabled) = Signal.pipe() - control.reactive.isSelected <~ SignalProducer(signal: pipeSignalSelected) - control.reactive.isEnabled <~ SignalProducer(signal: pipeSignalEnabled) - - observerSelected.send(value: true) - observerEnabled.send(value: true) - XCTAssertTrue(control.isEnabled) - XCTAssertTrue(control.isSelected) - observerSelected.send(value: false) - XCTAssertTrue(control.isEnabled) - XCTAssertFalse(control.isSelected) - observerEnabled.send(value: false) - XCTAssertFalse(control.isEnabled) - XCTAssertFalse(control.isSelected) - } -} diff --git a/ReactiveCocoaTests/UIKit/UIDatePickerSpec.swift b/ReactiveCocoaTests/UIKit/UIDatePickerSpec.swift new file mode 100644 index 0000000000..99f43de451 --- /dev/null +++ b/ReactiveCocoaTests/UIKit/UIDatePickerSpec.swift @@ -0,0 +1,47 @@ +import ReactiveSwift +import ReactiveCocoa +import UIKit +import Quick +import Nimble + +class UIDatePickerSpec: QuickSpec { + override func spec() { + var date: Date! + var picker: UIDatePicker! + weak var _picker: UIDatePicker? + + beforeEach { + let formatter = DateFormatter() + formatter.dateFormat = "MM/dd/YYYY" + date = formatter.date(from: "11/29/1988")! + + picker = UIDatePicker(frame: .zero) + _picker = picker + } + + afterEach { + picker = nil + expect(_picker).to(beNil()) + } + + it("should accept changes from bindings to its date value") { + picker.reactive.date.consume(date) + expect(picker.date) == date + } + + it("should emit user initiated changes to its date value") { + let expectation = self.expectation(description: "Expected rac_date to send an event when picker's date value is changed by a UI event") + defer { self.waitForExpectations(timeout: 2, handler: nil) } + + picker.reactive.dates.observeValues { changedDate in + expect(changedDate) == date + expectation.fulfill() + } + + picker.date = date + picker.isEnabled = true + picker.isUserInteractionEnabled = true + picker.sendActions(for: .valueChanged) + } + } +} diff --git a/ReactiveCocoaTests/UIKit/UIDatePickerTests.swift b/ReactiveCocoaTests/UIKit/UIDatePickerTests.swift deleted file mode 100644 index cf1a49cdf6..0000000000 --- a/ReactiveCocoaTests/UIKit/UIDatePickerTests.swift +++ /dev/null @@ -1,38 +0,0 @@ -import ReactiveSwift -import ReactiveCocoa -import UIKit -import XCTest - -class UIDatePickerTests: XCTestCase { - var date: Date! - var picker: UIDatePicker! - - override func setUp() { - let formatter = DateFormatter() - formatter.dateFormat = "MM/dd/YYYY" - date = formatter.date(from: "11/29/1988")! - - picker = UIDatePicker(frame: CGRect.zero) - } - - func testUpdatePickerFromProperty() { - picker.reactive.date.consume(date) - - XCTAssertEqual(picker.date, date) - } - - func testUpdatePropertyFromPicker() { - let expectation = self.expectation(description: "Expected rac_date to send an event when picker's date value is changed by a UI event") - defer { self.waitForExpectations(timeout: 2, handler: nil) } - - picker.reactive.dates.observeValues { changedDate in - XCTAssertEqual(changedDate, self.date) - expectation.fulfill() - } - - picker.date = date - picker.isEnabled = true - picker.isUserInteractionEnabled = true - picker.sendActions(for: .valueChanged) - } -} diff --git a/ReactiveCocoaTests/UIKit/UIImageViewSpec.swift b/ReactiveCocoaTests/UIKit/UIImageViewSpec.swift new file mode 100644 index 0000000000..03df3c4c26 --- /dev/null +++ b/ReactiveCocoaTests/UIKit/UIImageViewSpec.swift @@ -0,0 +1,51 @@ +import ReactiveSwift +import ReactiveCocoa +import UIKit +import Quick +import Nimble +import enum Result.NoError + +class UIImageViewSpec: QuickSpec { + override func spec() { + var imageView: UIImageView! + weak var _imageView: UIImageView? + + beforeEach { + imageView = UIImageView(frame: .zero) + _imageView = imageView + } + + afterEach { + imageView = nil + expect(_imageView).to(beNil()) + } + + it("should accept changes from bindings to its displaying image") { + let firstChange = UIImage() + let secondChange = UIImage() + + let (pipeSignal, observer) = Signal.pipe() + imageView.reactive.image <~ SignalProducer(signal: pipeSignal) + + observer.send(value: firstChange) + expect(imageView.image) == firstChange + + observer.send(value: secondChange) + expect(imageView.image) == secondChange + } + + it("should accept changes from bindings to its displaying image when highlighted") { + let firstChange = UIImage() + let secondChange = UIImage() + + let (pipeSignal, observer) = Signal.pipe() + imageView.reactive.highlightedImage <~ SignalProducer(signal: pipeSignal) + + observer.send(value: firstChange) + expect(imageView.highlightedImage) == firstChange + + observer.send(value: secondChange) + expect(imageView.highlightedImage) == secondChange + } + } +} diff --git a/ReactiveCocoaTests/UIKit/UIImageViewTests.swift b/ReactiveCocoaTests/UIKit/UIImageViewTests.swift deleted file mode 100644 index b7873f5a27..0000000000 --- a/ReactiveCocoaTests/UIKit/UIImageViewTests.swift +++ /dev/null @@ -1,64 +0,0 @@ -import ReactiveSwift -import ReactiveCocoa -import UIKit -import XCTest -import enum Result.NoError - -class UIImageViewTests: XCTestCase { - weak var _imageView: UIImageView? - - override func tearDown() { - XCTAssert(_imageView == nil, "Retain cycle detected in UIImageView properties") - super.tearDown() - } - - func testImagePropertyDoesntCreateRetainCycle() { - let imageView = UIImageView(frame: CGRect.zero) - _imageView = imageView - - let image = UIImage() - - imageView.reactive.image <~ SignalProducer(value: image) - XCTAssert(_imageView?.image == image) - } - - func testHighlightedImagePropertyDoesntCreateRetainCycle() { - let imageView = UIImageView(frame: CGRect.zero) - _imageView = imageView - - let image = UIImage() - - imageView.reactive.highlightedImage <~ SignalProducer(value: image) - XCTAssert(_imageView?.highlightedImage == image) - } - - func testImageProperty() { - let imageView = UIImageView(frame: CGRect.zero) - - let firstChange = UIImage() - let secondChange = UIImage() - - let (pipeSignal, observer) = Signal.pipe() - imageView.reactive.image <~ SignalProducer(signal: pipeSignal) - - observer.send(value: firstChange) - XCTAssertEqual(imageView.image, firstChange) - observer.send(value: secondChange) - XCTAssertEqual(imageView.image, secondChange) - } - - func testHighlightedImageProperty() { - let imageView = UIImageView(frame: CGRect.zero) - - let firstChange = UIImage() - let secondChange = UIImage() - - let (pipeSignal, observer) = Signal.pipe() - imageView.reactive.highlightedImage <~ SignalProducer(signal: pipeSignal) - - observer.send(value: firstChange) - XCTAssertEqual(imageView.highlightedImage, firstChange) - observer.send(value: secondChange) - XCTAssertEqual(imageView.highlightedImage, secondChange) - } -} diff --git a/ReactiveCocoaTests/UIKit/UILabelSpec.swift b/ReactiveCocoaTests/UIKit/UILabelSpec.swift new file mode 100644 index 0000000000..382c0d7e7f --- /dev/null +++ b/ReactiveCocoaTests/UIKit/UILabelSpec.swift @@ -0,0 +1,75 @@ +import ReactiveSwift +import ReactiveCocoa +import UIKit +import Quick +import Nimble +import enum Result.NoError + +class UILabelSpec: QuickSpec { + override func spec() { + var label: UILabel! + weak var _label: UILabel? + + beforeEach { + label = UILabel(frame: .zero) + _label = label + } + + afterEach { + label = nil + expect(_label).to(beNil()) + } + + it("should accept changes from bindings to its text value") { + let firstChange = "first" + let secondChange = "second" + + label.text = "" + + let (pipeSignal, observer) = Signal.pipe() + label.reactive.text <~ SignalProducer(signal: pipeSignal) + + observer.send(value: firstChange) + expect(label.text) == firstChange + + observer.send(value: secondChange) + expect(label.text) == secondChange + + observer.send(value: nil) + expect(label.text).to(beNil()) + } + + it("should accept changes from bindings to its attributed text value") { + let firstChange = NSAttributedString(string: "first") + let secondChange = NSAttributedString(string: "second") + + label.attributedText = NSAttributedString(string: "") + + let (pipeSignal, observer) = Signal.pipe() + label.reactive.attributedText <~ SignalProducer(signal: pipeSignal) + + observer.send(value: firstChange) + expect(label.attributedText) == firstChange + + observer.send(value: secondChange) + expect(label.attributedText) == secondChange + } + + it("should accept changes from bindings to its text color value") { + let firstChange = UIColor.red + let secondChange = UIColor.black + + let label = UILabel(frame: .zero) + + let (pipeSignal, observer) = Signal.pipe() + label.textColor = UIColor.black + label.reactive.textColor <~ SignalProducer(signal: pipeSignal) + + observer.send(value: firstChange) + expect(label.textColor) == firstChange + + observer.send(value: secondChange) + expect(label.textColor) == secondChange + } + } +} diff --git a/ReactiveCocoaTests/UIKit/UILabelTests.swift b/ReactiveCocoaTests/UIKit/UILabelTests.swift deleted file mode 100644 index e45cfc0b0f..0000000000 --- a/ReactiveCocoaTests/UIKit/UILabelTests.swift +++ /dev/null @@ -1,80 +0,0 @@ -import ReactiveSwift -import ReactiveCocoa -import UIKit -import XCTest -import enum Result.NoError - -class UILabelTests: XCTestCase { - weak var _label: UILabel? - - override func tearDown() { - XCTAssert(_label == nil, "Retain cycle detected in UILabel properties") - super.tearDown() - } - - func testTextPropertyDoesntCreateRetainCycle() { - let label = UILabel(frame: CGRect.zero) - _label = label - - label.reactive.text <~ SignalProducer(value: "Test") - XCTAssert(_label?.text == "Test") - } - - func testTextProperty() { - let firstChange = "first" - let secondChange = "second" - - let label = UILabel(frame: CGRect.zero) - label.text = "" - - let (pipeSignal, observer) = Signal.pipe() - label.reactive.text <~ SignalProducer(signal: pipeSignal) - - observer.send(value: firstChange) - XCTAssertEqual(label.text, firstChange) - observer.send(value: secondChange) - XCTAssertEqual(label.text, secondChange) - observer.send(value: nil) - XCTAssertNil(label.text) - } - - func testAttributedTextPropertyDoesntCreateRetainCycle() { - let label = UILabel(frame: CGRect.zero) - _label = label - - label.reactive.attributedText <~ SignalProducer(value: NSAttributedString(string: "Test")) - XCTAssert(_label?.attributedText?.string == "Test") - } - - func testAttributedTextProperty() { - let firstChange = NSAttributedString(string: "first") - let secondChange = NSAttributedString(string: "second") - - let label = UILabel(frame: CGRect.zero) - label.attributedText = NSAttributedString(string: "") - - let (pipeSignal, observer) = Signal.pipe() - label.reactive.attributedText <~ SignalProducer(signal: pipeSignal) - - observer.send(value: firstChange) - XCTAssertEqual(label.attributedText, firstChange) - observer.send(value: secondChange) - XCTAssertEqual(label.attributedText, secondChange) - } - - func testTextColorProperty() { - let firstChange = UIColor.red - let secondChange = UIColor.black - - let label = UILabel(frame: CGRect.zero) - - let (pipeSignal, observer) = Signal.pipe() - label.textColor = UIColor.black - label.reactive.textColor <~ SignalProducer(signal: pipeSignal) - - observer.send(value: firstChange) - XCTAssertEqual(label.textColor, firstChange) - observer.send(value: secondChange) - XCTAssertEqual(label.textColor, secondChange) - } -} diff --git a/ReactiveCocoaTests/UIKit/UIProgressViewSpec.swift b/ReactiveCocoaTests/UIKit/UIProgressViewSpec.swift new file mode 100644 index 0000000000..040f5cf94e --- /dev/null +++ b/ReactiveCocoaTests/UIKit/UIProgressViewSpec.swift @@ -0,0 +1,39 @@ +import ReactiveSwift +import ReactiveCocoa +import UIKit +import Quick +import Nimble +import enum Result.NoError + +class UIProgressViewSpec: QuickSpec { + override func spec() { + var progressView: UIProgressView! + weak var _progressView: UIProgressView? + + beforeEach { + progressView = UIProgressView(frame: .zero) + _progressView = progressView + } + + afterEach { + progressView = nil + expect(_progressView).to(beNil()) + } + + it("should accept changes from bindings to its progress value") { + let firstChange: Float = 0.5 + let secondChange: Float = 0.0 + + progressView.progress = 1.0 + + let (pipeSignal, observer) = Signal.pipe() + progressView.reactive.progress <~ SignalProducer(signal: pipeSignal) + + observer.send(value: firstChange) + expect(progressView.progress) ≈ firstChange + + observer.send(value: secondChange) + expect(progressView.progress) ≈ secondChange + } + } +} diff --git a/ReactiveCocoaTests/UIKit/UIProgressViewTests.swift b/ReactiveCocoaTests/UIKit/UIProgressViewTests.swift deleted file mode 100644 index d3a637768b..0000000000 --- a/ReactiveCocoaTests/UIKit/UIProgressViewTests.swift +++ /dev/null @@ -1,38 +0,0 @@ -import ReactiveSwift -import ReactiveCocoa -import UIKit -import XCTest -import enum Result.NoError - -class UIProgressViewTests: XCTestCase { - weak var _progressView: UIProgressView? - - override func tearDown() { - XCTAssert(_progressView == nil, "Retain cycle detected in UIProgressView properties") - super.tearDown() - } - - func testProgressPropertyDoesntCreateRetainCycle() { - let progressView = UIProgressView(frame: CGRect.zero) - _progressView = progressView - - progressView.reactive.progress <~ SignalProducer(value: 0.5) - XCTAssert(_progressView?.progress == 0.5) - } - - func testProgressProperty() { - let firstChange: Float = 0.5 - let secondChange: Float = 0.0 - - let progressView = UIProgressView(frame: CGRect.zero) - progressView.progress = 1.0 - - let (pipeSignal, observer) = Signal.pipe() - progressView.reactive.progress <~ SignalProducer(signal: pipeSignal) - - observer.send(value: firstChange) - XCTAssertEqual(progressView.progress, firstChange) - observer.send(value: secondChange) - XCTAssertEqual(progressView.progress, secondChange) - } -} diff --git a/ReactiveCocoaTests/UIKit/UISegmentedControlSpec.swift b/ReactiveCocoaTests/UIKit/UISegmentedControlSpec.swift new file mode 100644 index 0000000000..fad7680ed7 --- /dev/null +++ b/ReactiveCocoaTests/UIKit/UISegmentedControlSpec.swift @@ -0,0 +1,26 @@ +import Quick +import Nimble +import ReactiveSwift +import ReactiveCocoa +import Result + +class UISegmentedControlSpec: QuickSpec { + override func spec() { + it("should accept changes from bindings to its selected segment index") { + let s = UISegmentedControl(items: ["0", "1", "2"]) + s.selectedSegmentIndex = UISegmentedControlNoSegment + expect(s.numberOfSegments) == 3 + + let (pipeSignal, observer) = Signal.pipe() + s.reactive.selectedSegmentIndex <~ SignalProducer(signal: pipeSignal) + + expect(s.selectedSegmentIndex) == UISegmentedControlNoSegment + + observer.send(value: 1) + expect(s.selectedSegmentIndex) == 1 + + observer.send(value: 2) + expect(s.selectedSegmentIndex) == 2 + } + } +} diff --git a/ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift b/ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift deleted file mode 100644 index 780aa9de36..0000000000 --- a/ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift +++ /dev/null @@ -1,21 +0,0 @@ -import XCTest -import ReactiveSwift -import ReactiveCocoa -import Result - -class UISegmentedControlTests: XCTestCase { - func testSelectedSegmentIndexProperty() { - let s = UISegmentedControl(items: ["0", "1", "2"]) - s.selectedSegmentIndex = UISegmentedControlNoSegment - XCTAssertEqual(s.numberOfSegments, 3) - - let (pipeSignal, observer) = Signal.pipe() - s.reactive.selectedSegmentIndex <~ SignalProducer(signal: pipeSignal) - - XCTAssertEqual(s.selectedSegmentIndex, UISegmentedControlNoSegment) - observer.send(value: 1) - XCTAssertEqual(s.selectedSegmentIndex, 1) - observer.send(value: 2) - XCTAssertEqual(s.selectedSegmentIndex, 2) - } -} diff --git a/ReactiveCocoaTests/UIKit/UISwitchSpec.swift b/ReactiveCocoaTests/UIKit/UISwitchSpec.swift new file mode 100644 index 0000000000..c82cfaf0ff --- /dev/null +++ b/ReactiveCocoaTests/UIKit/UISwitchSpec.swift @@ -0,0 +1,44 @@ +import Quick +import Nimble +import ReactiveSwift +import ReactiveCocoa +import Result + +class UISwitchSpec: QuickSpec { + override func spec() { + var toggle: UISwitch! + weak var _toggle: UISwitch? + + beforeEach { + toggle = UISwitch(frame: .zero) + _toggle = toggle + } + + afterEach { + toggle = nil + expect(_toggle).to(beNil()) + } + + it("should accept changes from bindings to its `isOn` state") { + toggle.isOn = false + + let (pipeSignal, observer) = Signal.pipe() + toggle.reactive.isOn <~ SignalProducer(signal: pipeSignal) + + observer.send(value: true) + expect(toggle.isOn) == true + + observer.send(value: false) + expect(toggle.isOn) == false + } + + it("should emit user initiated changes to its `isOn` state") { + var latestValue: Bool? + toggle.reactive.isOnValues.observeValues { latestValue = $0 } + + toggle.isOn = true + toggle.sendActions(for: .valueChanged) + expect(latestValue!) == true + } + } +} diff --git a/ReactiveCocoaTests/UIKit/UISwitchTests.swift b/ReactiveCocoaTests/UIKit/UISwitchTests.swift deleted file mode 100644 index bcb4899b1f..0000000000 --- a/ReactiveCocoaTests/UIKit/UISwitchTests.swift +++ /dev/null @@ -1,26 +0,0 @@ -import XCTest -import ReactiveSwift -import ReactiveCocoa -import Result - -class UISwitchTests: XCTestCase { - func testOnProperty() { - let toggle = UISwitch(frame: CGRect.zero) - toggle.isOn = false - - let (pipeSignal, observer) = Signal.pipe() - toggle.reactive.isOn <~ SignalProducer(signal: pipeSignal) - - observer.send(value: true) - XCTAssertTrue(toggle.isOn) - observer.send(value: false) - XCTAssertFalse(toggle.isOn) - - var latestValue: Bool? - toggle.reactive.isOnValues.observeValues { latestValue = $0 } - - toggle.isOn = true - toggle.sendActions(for: .valueChanged) - XCTAssertTrue(latestValue!) - } -} diff --git a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift new file mode 100644 index 0000000000..4e44304bdb --- /dev/null +++ b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift @@ -0,0 +1,46 @@ +import ReactiveSwift +import ReactiveCocoa +import UIKit +import Quick +import Nimble + +class UITextFieldSpec: QuickSpec { + override func spec() { + var textField: UITextField! + weak var _textField: UITextField? + + beforeEach { + textField = UITextField(frame: .zero) + _textField = textField + } + + afterEach { + textField = nil + expect(_textField).to(beNil()) + } + + it("should emit user initiated changes to its text value when the editing ends") { + textField.text = "Test" + + var latestValue: String? + textField.reactive.textValues.observeValues { text in + latestValue = text + } + + textField.sendActions(for: .editingDidEnd) + expect(latestValue) == textField.text + } + + it("should emit user initiated changes to its text value continuously") { + textField.text = "Test" + + var latestValue: String? + textField.reactive.continuousTextValues.observeValues { text in + latestValue = text + } + + textField.sendActions(for: .editingChanged) + expect(latestValue) == textField.text + } + } +} diff --git a/ReactiveCocoaTests/UIKit/UITextFieldTests.swift b/ReactiveCocoaTests/UIKit/UITextFieldTests.swift deleted file mode 100644 index b33f101e6c..0000000000 --- a/ReactiveCocoaTests/UIKit/UITextFieldTests.swift +++ /dev/null @@ -1,36 +0,0 @@ -import ReactiveSwift -import ReactiveCocoa -import UIKit -import XCTest - -class UITextFieldTests: XCTestCase { - func testTexts() { - let expectation = self.expectation(description: "Expected `texts`'s value to equal to the textField's text") - defer { self.waitForExpectations(timeout: 2, handler: nil) } - - let textField = UITextField(frame: CGRect.zero) - textField.text = "Test" - - textField.reactive.textValues.observeValues { text in - XCTAssertEqual(text, textField.text) - expectation.fulfill() - } - - textField.sendActions(for: .editingDidEnd) - } - - func testContinuousTexts() { - let expectation = self.expectation(description: "Expected `continuousTexts`'s value to equal to the textField's text") - defer { self.waitForExpectations(timeout: 2, handler: nil) } - - let textField = UITextField(frame: CGRect.zero) - textField.text = "Test" - - textField.reactive.continuousTextValues.observeValues { text in - XCTAssertEqual(text, textField.text) - expectation.fulfill() - } - - textField.sendActions(for: .editingChanged) - } -} diff --git a/ReactiveCocoaTests/UIKit/UITextViewSpec.swift b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift new file mode 100644 index 0000000000..0e803dd538 --- /dev/null +++ b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift @@ -0,0 +1,49 @@ +import ReactiveSwift +import ReactiveCocoa +import UIKit +import Quick +import Nimble + +class UITextViewSpec: QuickSpec { + override func spec() { + var textView: UITextView! + weak var _textView: UITextView? + + beforeEach { + textView = UITextView(frame: .zero) + _textView = textView + } + + afterEach { + textView = nil + expect(_textView).to(beNil()) + } + + it("should emit user initiated changes to its text value when the editing ends") { + textView.text = "Test" + + var latestValue: String? + textView.reactive.textValues.observeValues { text in + latestValue = text + } + + NotificationCenter.default.post(name: .UITextViewTextDidEndEditing, + object: textView) + expect(latestValue) == textView.text + } + + it("should emit user initiated changes to its text value continuously") { + let textView = UITextView(frame: .zero) + textView.text = "Test" + + var latestValue: String? + textView.reactive.continuousTextValues.observeValues { text in + latestValue = text + } + + NotificationCenter.default.post(name: .UITextViewTextDidChange, + object: textView) + expect(latestValue) == textView.text + } + } +} diff --git a/ReactiveCocoaTests/UIKit/UITextViewTests.swift b/ReactiveCocoaTests/UIKit/UITextViewTests.swift deleted file mode 100644 index c732f7e224..0000000000 --- a/ReactiveCocoaTests/UIKit/UITextViewTests.swift +++ /dev/null @@ -1,36 +0,0 @@ -import ReactiveSwift -import ReactiveCocoa -import UIKit -import XCTest - -class UITextViewTests: XCTestCase { - func testTexts() { - let expectation = self.expectation(description: "Expected `texts`'s value to equal to the textViews's text") - defer { self.waitForExpectations(timeout: 2, handler: nil) } - - let textView = UITextView(frame: CGRect.zero) - textView.text = "Test" - - textView.reactive.textValues.observeValues { text in - XCTAssertEqual(text, textView.text) - expectation.fulfill() - } - - NotificationCenter.default.post(name: NSNotification.Name.UITextViewTextDidEndEditing, object: textView) - } - - func testContinuousTexts() { - let expectation = self.expectation(description: "Expected `continuousTexts`'s value to equal to the textViews's text") - defer { self.waitForExpectations(timeout: 2, handler: nil) } - - let textView = UITextView(frame: CGRect.zero) - textView.text = "Test" - - textView.reactive.continuousTextValues.observeValues { text in - XCTAssertEqual(text, textView.text) - expectation.fulfill() - } - - NotificationCenter.default.post(name: NSNotification.Name.UITextViewTextDidChange, object: textView) - } -} diff --git a/ReactiveCocoaTests/UIKit/UIViewSpec.swift b/ReactiveCocoaTests/UIKit/UIViewSpec.swift new file mode 100644 index 0000000000..0dd5a06d31 --- /dev/null +++ b/ReactiveCocoaTests/UIKit/UIViewSpec.swift @@ -0,0 +1,65 @@ +import ReactiveSwift +import ReactiveCocoa +import UIKit +import Quick +import Nimble +import enum Result.NoError + +class UIViewSpec: QuickSpec { + override func spec() { + var view: UIView! + weak var _view: UIView? + + beforeEach { + view = UIView(frame: .zero) + _view = view + } + + afterEach { + view = nil + expect(_view).to(beNil()) + } + + it("should accept changes from bindings to its hiding state") { + view.isHidden = true + + let (pipeSignal, observer) = Signal.pipe() + view.reactive.isHidden <~ SignalProducer(signal: pipeSignal) + + observer.send(value: true) + expect(view.isHidden) == true + + observer.send(value: false) + expect(view.isHidden) == false + } + + it("should accept changes from bindings to its alpha value") { + view.alpha = 0.0 + + let firstChange = CGFloat(0.5) + let secondChange = CGFloat(0.7) + + let (pipeSignal, observer) = Signal.pipe() + view.reactive.alpha <~ SignalProducer(signal: pipeSignal) + + observer.send(value: firstChange) + expect(view.alpha) ≈ firstChange + + observer.send(value: secondChange) + expect(view.alpha) ≈ secondChange + } + + it("should accept changes from bindings to its user interaction enabling state") { + view.isUserInteractionEnabled = true + + let (pipeSignal, observer) = Signal.pipe() + view.reactive.isUserInteractionEnabled <~ SignalProducer(signal: pipeSignal) + + observer.send(value: true) + expect(view.isUserInteractionEnabled) == true + + observer.send(value: false) + expect(view.isUserInteractionEnabled) == false + } + } +} diff --git a/ReactiveCocoaTests/UIKit/UIViewTests.swift b/ReactiveCocoaTests/UIKit/UIViewTests.swift deleted file mode 100644 index cdb3325484..0000000000 --- a/ReactiveCocoaTests/UIKit/UIViewTests.swift +++ /dev/null @@ -1,72 +0,0 @@ -import ReactiveSwift -import ReactiveCocoa -import UIKit -import XCTest -import enum Result.NoError - -class UIViewTests: XCTestCase { - weak var _view: UIView? - - override func tearDown() { - XCTAssert(_view == nil, "Retain cycle detected in UIView properties") - super.tearDown() - } - - func testAlphaPropertyDoesntCreateRetainCycle() { - let view = UIView(frame: CGRect.zero) - _view = view - - view.reactive.alpha <~ SignalProducer(value: 0.5) - XCTAssertEqualWithAccuracy(_view!.alpha, 0.5, accuracy: 0.01) - } - - func testHiddenPropertyDoesntCreateRetainCycle() { - let view = UIView(frame: CGRect.zero) - _view = view - - view.reactive.isHidden <~ SignalProducer(value: true) - XCTAssert(_view?.isHidden == true) - } - - func testHiddenProperty() { - let view = UIView(frame: CGRect.zero) - view.isHidden = true - - let (pipeSignal, observer) = Signal.pipe() - view.reactive.isHidden <~ SignalProducer(signal: pipeSignal) - - observer.send(value: true) - XCTAssertTrue(view.isHidden) - observer.send(value: false) - XCTAssertFalse(view.isHidden) - } - - func testAlphaProperty() { - let view = UIView(frame: CGRect.zero) - view.alpha = 0.0 - - let firstChange = CGFloat(0.5) - let secondChange = CGFloat(0.7) - - let (pipeSignal, observer) = Signal.pipe() - view.reactive.alpha <~ SignalProducer(signal: pipeSignal) - - observer.send(value: firstChange) - XCTAssertEqualWithAccuracy(view.alpha, firstChange, accuracy: 0.01) - observer.send(value: secondChange) - XCTAssertEqualWithAccuracy(view.alpha, secondChange, accuracy: 0.01) - } - - func testUserInteractionEnabledProperty() { - let view = UIView(frame: CGRect.zero) - view.isUserInteractionEnabled = true - - let (pipeSignal, observer) = Signal.pipe() - view.reactive.isUserInteractionEnabled <~ SignalProducer(signal: pipeSignal) - - observer.send(value: true) - XCTAssertTrue(view.isUserInteractionEnabled) - observer.send(value: false) - XCTAssertFalse(view.isUserInteractionEnabled) - } -} From 903be1ebfa32ae88b6bf2b3204e883efda76330f Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 18 Oct 2016 17:41:25 +0200 Subject: [PATCH 0457/1028] Test if controls are released properly in `NSControlSpec`. --- ReactiveCocoaTests/AppKit/NSControlSpec.swift | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ReactiveCocoaTests/AppKit/NSControlSpec.swift b/ReactiveCocoaTests/AppKit/NSControlSpec.swift index 7fbabc403d..1135d510d4 100644 --- a/ReactiveCocoaTests/AppKit/NSControlSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSControlSpec.swift @@ -10,6 +10,7 @@ class NSControlSpec: QuickSpec { describe("NSControl") { var window: NSWindow! var control: NSButton! + weak var _control: NSControl? beforeEach { window = NSWindow() @@ -18,9 +19,18 @@ class NSControlSpec: QuickSpec { control.setButtonType(.onOff) control.state = NSOffState + _control = control + window.contentView!.addSubview(control) } + afterEach { + control.removeFromSuperview() + control = nil + + expect(_control).to(beNil()) + } + it("should emit changes in Int") { var values = [Int]() control.reactive.integerValues.observeValues { values.append($0) } From 210ff3d2b9e1837ec8e6611ba31b84347173f4cf Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 18 Oct 2016 18:59:41 +0200 Subject: [PATCH 0458/1028] Fixed the failing `NSControlSpec` test cases. --- ReactiveCocoaTests/AppKit/NSControlSpec.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoaTests/AppKit/NSControlSpec.swift b/ReactiveCocoaTests/AppKit/NSControlSpec.swift index 1135d510d4..526bc9a979 100644 --- a/ReactiveCocoaTests/AppKit/NSControlSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSControlSpec.swift @@ -25,8 +25,10 @@ class NSControlSpec: QuickSpec { } afterEach { - control.removeFromSuperview() - control = nil + autoreleasepool { + control.removeFromSuperview() + control = nil + } expect(_control).to(beNil()) } From 99bb5683f859878a2a7bd666f9e8698d855b460c Mon Sep 17 00:00:00 2001 From: NachoSoto Date: Mon, 17 Oct 2016 12:49:24 -0700 Subject: [PATCH 0459/1028] DynamicProperty: don't expose signal setter --- ReactiveCocoa/DynamicProperty.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa/DynamicProperty.swift b/ReactiveCocoa/DynamicProperty.swift index 20c85d89e8..7dfa65dc2b 100644 --- a/ReactiveCocoa/DynamicProperty.swift +++ b/ReactiveCocoa/DynamicProperty.swift @@ -42,7 +42,7 @@ public final class DynamicProperty: MutablePropertyProtocol { .map { $0 as! Value } } - public lazy var signal: Signal = { [unowned self] in + public private(set) lazy var signal: Signal = { var signal: Signal! self.producer.startWithSignal { innerSignal, _ in signal = innerSignal } return signal From 59b55aa96f5e93cc116c3047037789e09331446d Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 18 Oct 2016 20:38:34 +0200 Subject: [PATCH 0460/1028] Refactored `KeyValueObservingSpec`. --- .../KeyValueObservingSpec.swift | 51 ++++++++++++------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/ReactiveCocoaTests/KeyValueObservingSpec.swift b/ReactiveCocoaTests/KeyValueObservingSpec.swift index fbde95cc87..257d201a9b 100644 --- a/ReactiveCocoaTests/KeyValueObservingSpec.swift +++ b/ReactiveCocoaTests/KeyValueObservingSpec.swift @@ -11,10 +11,13 @@ class KeyValueObservingSpec: QuickSpec { it("should sends the current value and then the changes for the key path") { let object = ObservableObject() var values: [Int] = [] - object.reactive.values(forKeyPath: "rac_value").startWithValues { value in - expect(value).notTo(beNil()) - values.append(value as! Int) - } + + object.reactive + .values(forKeyPath: #keyPath(ObservableObject.rac_value)) + .startWithValues { value in + expect(value).notTo(beNil()) + values.append(value as! Int) + } expect(values) == [ 0 ] @@ -28,10 +31,13 @@ class KeyValueObservingSpec: QuickSpec { it("should sends the current value and then the changes for the key path, even if the value actually remains unchanged") { let object = ObservableObject() var values: [Int] = [] - object.reactive.values(forKeyPath: "rac_value").startWithValues { value in - expect(value).notTo(beNil()) - values.append(value as! Int) - } + + object.reactive + .values(forKeyPath: #keyPath(ObservableObject.rac_value)) + .startWithValues { value in + expect(value).notTo(beNil()) + values.append(value as! Int) + } expect(values) == [ 0 ] @@ -49,9 +55,9 @@ class KeyValueObservingSpec: QuickSpec { // Use a closure so this object has a shorter lifetime. let object = ObservableObject() - object.reactive.values(forKeyPath: "rac_value").startWithCompleted { - completed = true - } + object.reactive + .values(forKeyPath: #keyPath(ObservableObject.rac_value)) + .startWithCompleted { completed = true } expect(completed) == false }() @@ -63,7 +69,8 @@ class KeyValueObservingSpec: QuickSpec { var interrupted = false let object = ObservableObject() - let disposable = object.reactive.values(forKeyPath: "rac_value") + let disposable = object.reactive + .values(forKeyPath: #keyPath(ObservableObject.rac_value)) .startWithInterrupted { interrupted = true } expect(interrupted) == false @@ -78,7 +85,7 @@ class KeyValueObservingSpec: QuickSpec { parentObject .reactive - .values(forKeyPath: "rac_object.rac_value") + .values(forKeyPath: #keyPath(NestedObservableObject.rac_object.rac_value)) .map { $0 as! NSNumber } .map { $0.intValue } .startWithValues { @@ -106,6 +113,8 @@ class KeyValueObservingSpec: QuickSpec { var innerObject = Optional(ObservableObject()) parentObject.rac_weakObject = innerObject + // `#keyPath` does not work with weak relationships. + var values: [Int] = [] parentObject .reactive @@ -160,7 +169,7 @@ class KeyValueObservingSpec: QuickSpec { testObject .reactive - .values(forKeyPath: "rac_value") + .values(forKeyPath: #keyPath(ObservableObject.rac_value)) .skip(first: 1) .take(first: 1) .map { $0 as! NSNumber } @@ -182,7 +191,7 @@ class KeyValueObservingSpec: QuickSpec { testObject .reactive - .values(forKeyPath: "rac_value") + .values(forKeyPath: #keyPath(ObservableObject.rac_value)) .skip(first: 1) .take(first: 1) .observe(on: UIScheduler()) @@ -205,7 +214,7 @@ class KeyValueObservingSpec: QuickSpec { testObject .reactive - .values(forKeyPath: "rac_value") + .values(forKeyPath: #keyPath(ObservableObject.rac_value)) .observe(on: UIScheduler()) .map { $0 as! NSNumber } .map { $0.intValue } @@ -251,7 +260,7 @@ class KeyValueObservingSpec: QuickSpec { DispatchQueue.concurrentPerform(iterations: numIterations) { index in testObject .reactive - .values(forKeyPath: "rac_value") + .values(forKeyPath: #keyPath(ObservableObject.rac_value)) .skip(first: 1) .observe(on: deliveringObserver) .map { $0 as! NSNumber } @@ -272,7 +281,10 @@ class KeyValueObservingSpec: QuickSpec { iterationQueue.async { DispatchQueue.concurrentPerform(iterations: numIterations) { index in - let disposable = testObject.reactive.values(forKeyPath: "rac_value").startWithCompleted {} + let disposable = testObject.reactive + .values(forKeyPath: #keyPath(ObservableObject.rac_value)) + .startWithCompleted {} + serialDisposable.innerDisposable = disposable concurrentQueue.async { @@ -295,7 +307,8 @@ class KeyValueObservingSpec: QuickSpec { otherScheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) } - let replayProducer = testObject.reactive.values(forKeyPath: "rac_value") + let replayProducer = testObject.reactive + .values(forKeyPath: #keyPath(ObservableObject.rac_value)) .map { $0 as! NSNumber } .map { $0.intValue } .map { $0 % 2 == 0 } From c767383013ba0459421ef47d510237417a134b4f Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 18 Oct 2016 20:52:15 +0200 Subject: [PATCH 0461/1028] Added `InterceptingSpec`. --- ReactiveCocoa.xcodeproj/project.pbxproj | 8 +++ ReactiveCocoaTests/InterceptingSpec.swift | 84 +++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 ReactiveCocoaTests/InterceptingSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index cac92d8c4c..87864c8f31 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -91,6 +91,9 @@ 9A2E425F1DAA6737006D909F /* CocoaTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */; }; 9A2E42601DAA6737006D909F /* CocoaTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */; }; 9A2E42611DAA6737006D909F /* CocoaTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */; }; + 9A6AAA0E1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; + 9A6AAA0F1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; + 9A6AAA101DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; 9A9DFEE51DA7B5500039EE1B /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; 9A9DFEE91DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; 9A9DFEEA1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; @@ -255,6 +258,7 @@ 9A1D06761D9415FB00ACF44C /* RACObjCRuntimeUtilities.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RACObjCRuntimeUtilities.m; sourceTree = ""; }; 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyValueObservingSpec.swift; sourceTree = ""; }; 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaTarget.swift; sourceTree = ""; }; + 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterceptingSpec.swift; sourceTree = ""; }; 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssociationSpec.swift; sourceTree = ""; }; 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+KeyValueObserving.swift"; sourceTree = ""; }; 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaAction.swift; sourceTree = ""; }; @@ -547,6 +551,7 @@ D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */, B696FB801A7640C00075236D /* TestError.swift */, 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */, + 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */, D04725FA19E49ED7006002AA /* Supporting Files */, ); path = ReactiveCocoaTests; @@ -964,6 +969,7 @@ 9A1D06391D93EA7E00ACF44C /* UIBarButtonItemSpec.swift in Sources */, 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */, 9A1D06411D93EA7E00ACF44C /* UIControlSpec.swift in Sources */, + 9A6AAA101DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */, 9A1D06491D93EA7E00ACF44C /* UIProgressViewSpec.swift in Sources */, BEE020661D637B0000DF261F /* TestError.swift in Sources */, 9A1D06531D93EA7E00ACF44C /* UITextFieldSpec.swift in Sources */, @@ -1010,6 +1016,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9A6AAA0E1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */, 9ADE4A8F1DA6DA20005C2AC8 /* NSControlSpec.swift in Sources */, D0A2260E1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */, B696FB811A7640C00075236D /* TestError.swift in Sources */, @@ -1069,6 +1076,7 @@ 9A1D06461D93EA7E00ACF44C /* UILabelSpec.swift in Sources */, 9A9DFEEA1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */, 9A1D06421D93EA7E00ACF44C /* UIDatePickerSpec.swift in Sources */, + 9A6AAA0F1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */, 9A1D06381D93EA7E00ACF44C /* UIBarButtonItemSpec.swift in Sources */, 9A1E72BB1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, 9A1D06401D93EA7E00ACF44C /* UIControlSpec.swift in Sources */, diff --git a/ReactiveCocoaTests/InterceptingSpec.swift b/ReactiveCocoaTests/InterceptingSpec.swift new file mode 100644 index 0000000000..324ec89e0d --- /dev/null +++ b/ReactiveCocoaTests/InterceptingSpec.swift @@ -0,0 +1,84 @@ +import Foundation +import ReactiveCocoa +import ReactiveSwift +import enum Result.NoError +import Quick +import Nimble + +class InterceptingSpec: QuickSpec { + override func spec() { + describe("trigger(for:)") { + var object: InterceptedObject! + weak var _object: InterceptedObject? + + beforeEach { + object = InterceptedObject() + _object = object + } + + afterEach { + object = nil + expect(_object).to(beNil()) + } + + it("should send a value when the selector is invoked") { + let signal = object.reactive.trigger(for: #selector(object.increment)) + + var counter = 0 + signal.observeValues { counter += 1 } + + expect(counter) == 0 + expect(object.counter) == 0 + + object.increment() + expect(counter) == 1 + expect(object.counter) == 1 + + object.increment() + expect(counter) == 2 + expect(object.counter) == 2 + } + + it("should complete when the object deinitializes") { + let signal = object.reactive.trigger(for: #selector(object.increment)) + + var isCompleted = false + signal.observeCompleted { isCompleted = true } + expect(isCompleted) == false + + object = nil + expect(_object).to(beNil()) + expect(isCompleted) == true + } + + it("should multicast") { + let signal1 = object.reactive.trigger(for: #selector(object.increment)) + let signal2 = object.reactive.trigger(for: #selector(object.increment)) + + var counter1 = 0 + var counter2 = 0 + signal1.observeValues { counter1 += 1 } + signal2.observeValues { counter2 += 1 } + + expect(counter1) == 0 + expect(counter2) == 0 + + object.increment() + expect(counter1) == 1 + expect(counter2) == 1 + + object.increment() + expect(counter1) == 2 + expect(counter2) == 2 + } + } + } +} + +class InterceptedObject: NSObject { + var counter = 0 + + dynamic func increment() { + counter += 1 + } +} From 1c3a3a0dd0ace8123fded783db269d96ec60c74b Mon Sep 17 00:00:00 2001 From: Anders Date: Wed, 19 Oct 2016 00:58:33 +0200 Subject: [PATCH 0462/1028] ReactiveSwift 1.0.0-alpha.3 --- Cartfile | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- ReactiveCocoa/AppKit/NSTextField.swift | 14 ++------------ ReactiveCocoa/UIKit/UITextView.swift | 14 ++------------ 5 files changed, 7 insertions(+), 27 deletions(-) diff --git a/Cartfile b/Cartfile index a1e62fec3b..db644ec958 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.2" +github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.3" diff --git a/Cartfile.resolved b/Cartfile.resolved index fd4898dc4f..08a40e7bde 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -2,4 +2,4 @@ github "Quick/Nimble" "v5.0.0" github "Quick/Quick" "v0.10.0" github "antitypical/Result" "3.0.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" -github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.2" +github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.3" diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index dfb2f9ae68..55345ebd4e 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit dfb2f9ae68c04c38c768e8a25fffa0d72f7fa5b5 +Subproject commit 55345ebd4ec28baeacb4041a99d56839428bcaff diff --git a/ReactiveCocoa/AppKit/NSTextField.swift b/ReactiveCocoa/AppKit/NSTextField.swift index a0ced97dcd..e5d7c63a46 100644 --- a/ReactiveCocoa/AppKit/NSTextField.swift +++ b/ReactiveCocoa/AppKit/NSTextField.swift @@ -5,30 +5,20 @@ import enum Result.NoError extension Reactive where Base: NSTextField { /// A signal of values in `String` from the text field upon any changes. public var continuousStringValues: Signal { - var signal: Signal! - - NotificationCenter.default + return NotificationCenter.default .reactive .notifications(forName: .NSControlTextDidChange, object: base) .take(during: lifetime) .map { ($0.object as! NSTextField).stringValue } - .startWithSignal { innerSignal, _ in signal = innerSignal } - - return signal } /// A signal of values in `NSAttributedString` from the text field upon any /// changes. public var continuousAttributedStringValues: Signal { - var signal: Signal! - - NotificationCenter.default + return NotificationCenter.default .reactive .notifications(forName: .NSControlTextDidChange, object: base) .take(during: lifetime) .map { ($0.object as! NSTextField).attributedStringValue } - .startWithSignal { innerSignal, _ in signal = innerSignal } - - return signal } } diff --git a/ReactiveCocoa/UIKit/UITextView.swift b/ReactiveCocoa/UIKit/UITextView.swift index f82304eee0..6fd6f109bd 100644 --- a/ReactiveCocoa/UIKit/UITextView.swift +++ b/ReactiveCocoa/UIKit/UITextView.swift @@ -10,29 +10,19 @@ extension Reactive where Base: UITextView { /// A signal of text values emitted by the text view upon end of editing. public var textValues: Signal { - var signal: Signal! - - NotificationCenter.default + return NotificationCenter.default .reactive .notifications(forName: .UITextViewTextDidEndEditing, object: base) .take(during: lifetime) .map { ($0.object as! UITextView).text! } - .startWithSignal { innerSignal, _ in signal = innerSignal } - - return signal } /// A signal of text values emitted by the text view upon any changes. public var continuousTextValues: Signal { - var signal: Signal! - - NotificationCenter.default + return NotificationCenter.default .reactive .notifications(forName: .UITextViewTextDidChange, object: base) .take(during: lifetime) .map { ($0.object as! UITextView).text! } - .startWithSignal { innerSignal, _ in signal = innerSignal } - - return signal } } From ae10a1ee12b3730afbeadba7a38b06c7ab860e94 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Wed, 19 Oct 2016 15:25:28 +0900 Subject: [PATCH 0463/1028] Update ReactiveSwift to 1.0.0-alpha.3 --- Cartfile | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cartfile b/Cartfile index a1e62fec3b..db644ec958 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.2" +github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.3" diff --git a/Cartfile.resolved b/Cartfile.resolved index fd4898dc4f..08a40e7bde 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -2,4 +2,4 @@ github "Quick/Nimble" "v5.0.0" github "Quick/Quick" "v0.10.0" github "antitypical/Result" "3.0.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" -github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.2" +github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.3" diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index dfb2f9ae68..55345ebd4e 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit dfb2f9ae68c04c38c768e8a25fffa0d72f7fa5b5 +Subproject commit 55345ebd4ec28baeacb4041a99d56839428bcaff From 5e7534c99b3db5049d63891f32ac4f8c3da91e8e Mon Sep 17 00:00:00 2001 From: Anders Date: Wed, 19 Oct 2016 17:27:08 +0200 Subject: [PATCH 0464/1028] Disabled the dealloc check for leaking UIKit components. --- ReactiveCocoaTests/UIKit/UISwitchSpec.swift | 7 ++++++- ReactiveCocoaTests/UIKit/UITextFieldSpec.swift | 7 ++++++- ReactiveCocoaTests/UIKit/UITextViewSpec.swift | 7 ++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/ReactiveCocoaTests/UIKit/UISwitchSpec.swift b/ReactiveCocoaTests/UIKit/UISwitchSpec.swift index c82cfaf0ff..7534ea98a8 100644 --- a/ReactiveCocoaTests/UIKit/UISwitchSpec.swift +++ b/ReactiveCocoaTests/UIKit/UISwitchSpec.swift @@ -16,7 +16,12 @@ class UISwitchSpec: QuickSpec { afterEach { toggle = nil - expect(_toggle).to(beNil()) + + // Disabled due to an issue of the iOS SDK. + // Please refer to https://github.com/ReactiveCocoa/ReactiveCocoa/issues/3251 + // for more information. + // + // expect(_toggle).to(beNil()) } it("should accept changes from bindings to its `isOn` state") { diff --git a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift index 4e44304bdb..cfaf8d3b89 100644 --- a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift @@ -16,7 +16,12 @@ class UITextFieldSpec: QuickSpec { afterEach { textField = nil - expect(_textField).to(beNil()) + + // Disabled due to an issue of the iOS SDK. + // Please refer to https://github.com/ReactiveCocoa/ReactiveCocoa/issues/3251 + // for more information. + // + //expect(_textField).to(beNil()) } it("should emit user initiated changes to its text value when the editing ends") { diff --git a/ReactiveCocoaTests/UIKit/UITextViewSpec.swift b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift index 0e803dd538..495362f970 100644 --- a/ReactiveCocoaTests/UIKit/UITextViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift @@ -16,7 +16,12 @@ class UITextViewSpec: QuickSpec { afterEach { textView = nil - expect(_textView).to(beNil()) + + // Disabled due to an issue of the iOS SDK. + // Please refer to https://github.com/ReactiveCocoa/ReactiveCocoa/issues/3251 + // for more information. + // + // expect(_textView).to(beNil()) } it("should emit user initiated changes to its text value when the editing ends") { From 01230f4ec900c35adf08334df302454f3d41d2b6 Mon Sep 17 00:00:00 2001 From: Anders Date: Wed, 19 Oct 2016 21:36:46 +0200 Subject: [PATCH 0465/1028] Reenabled dealloc checks in UITextField/UITextView specs. --- .../UIKit/UITextFieldSpec.swift | 17 +++-- ReactiveCocoaTests/UIKit/UITextViewSpec.swift | 68 +++++++++---------- 2 files changed, 41 insertions(+), 44 deletions(-) diff --git a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift index cfaf8d3b89..0c63644a14 100644 --- a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift @@ -10,18 +10,17 @@ class UITextFieldSpec: QuickSpec { weak var _textField: UITextField? beforeEach { - textField = UITextField(frame: .zero) - _textField = textField + autoreleasepool { + textField = UITextField(frame: .zero) + _textField = textField + } } afterEach { - textField = nil - - // Disabled due to an issue of the iOS SDK. - // Please refer to https://github.com/ReactiveCocoa/ReactiveCocoa/issues/3251 - // for more information. - // - //expect(_textField).to(beNil()) + autoreleasepool { + textField = nil + } + expect(_textField).to(beNil()) } it("should emit user initiated changes to its text value when the editing ends") { diff --git a/ReactiveCocoaTests/UIKit/UITextViewSpec.swift b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift index 495362f970..c201b1a682 100644 --- a/ReactiveCocoaTests/UIKit/UITextViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift @@ -6,49 +6,47 @@ import Nimble class UITextViewSpec: QuickSpec { override func spec() { - var textView: UITextView! - weak var _textView: UITextView? - - beforeEach { - textView = UITextView(frame: .zero) - _textView = textView - } - - afterEach { - textView = nil + var textView: UITextView! + weak var _textView: UITextView? + + beforeEach { + autoreleasepool { + textView = UITextView(frame: .zero) + _textView = textView + } + } - // Disabled due to an issue of the iOS SDK. - // Please refer to https://github.com/ReactiveCocoa/ReactiveCocoa/issues/3251 - // for more information. - // - // expect(_textView).to(beNil()) - } + afterEach { + autoreleasepool { + textView = nil + } + expect(_textView).toEventually(beNil()) + } - it("should emit user initiated changes to its text value when the editing ends") { - textView.text = "Test" + it("should emit user initiated changes to its text value when the editing ends") { + textView.text = "Test" - var latestValue: String? - textView.reactive.textValues.observeValues { text in + var latestValue: String? + textView.reactive.textValues.observeValues { text in latestValue = text - } + } - NotificationCenter.default.post(name: .UITextViewTextDidEndEditing, - object: textView) - expect(latestValue) == textView.text - } + NotificationCenter.default.post(name: .UITextViewTextDidEndEditing, + object: textView) + expect(latestValue) == textView.text + } - it("should emit user initiated changes to its text value continuously") { - let textView = UITextView(frame: .zero) - textView.text = "Test" + it("should emit user initiated changes to its text value continuously") { + textView.text = "Test" - var latestValue: String? - textView.reactive.continuousTextValues.observeValues { text in - latestValue = text - } + var latestValue: String? + textView.reactive.continuousTextValues.observeValues { text in + latestValue = text + } - NotificationCenter.default.post(name: .UITextViewTextDidChange, - object: textView) - expect(latestValue) == textView.text + NotificationCenter.default.post(name: .UITextViewTextDidChange, + object: textView) + expect(latestValue) == textView.text } } } From bdf59d778924bb60df450290df9019ad7ec4aff8 Mon Sep 17 00:00:00 2001 From: Anders Date: Wed, 19 Oct 2016 22:03:28 +0200 Subject: [PATCH 0466/1028] Use custom module map for the framework. Removed the Utils submodule. --- ReactiveCocoa.xcodeproj/project.pbxproj | 40 +++++++++++-------- ReactiveCocoa/NSObject+Intercepting.swift | 1 - .../ReactiveCocoaPrivate/module.modulemap | 1 - .../module.private.modulemap | 3 -- ReactiveCocoa/module.modulemap | 7 ++++ 5 files changed, 31 insertions(+), 21 deletions(-) delete mode 100644 ReactiveCocoa/ReactiveCocoaPrivate/module.modulemap delete mode 100644 ReactiveCocoa/ReactiveCocoaPrivate/module.private.modulemap create mode 100644 ReactiveCocoa/module.modulemap diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 87864c8f31..4fb1b4175c 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -94,6 +94,10 @@ 9A6AAA0E1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; 9A6AAA0F1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; 9A6AAA101DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; + 9A6AAA191DB808A80013AAEA /* RACObjCRuntimeUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A1D06751D9415FB00ACF44C /* RACObjCRuntimeUtilities.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 9A6AAA1A1DB808A80013AAEA /* RACObjCRuntimeUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A1D06751D9415FB00ACF44C /* RACObjCRuntimeUtilities.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 9A6AAA1B1DB808A90013AAEA /* RACObjCRuntimeUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A1D06751D9415FB00ACF44C /* RACObjCRuntimeUtilities.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 9A6AAA1C1DB808AA0013AAEA /* RACObjCRuntimeUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A1D06751D9415FB00ACF44C /* RACObjCRuntimeUtilities.h */; settings = {ATTRIBUTES = (Private, ); }; }; 9A9DFEE51DA7B5500039EE1B /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; 9A9DFEE91DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; 9A9DFEEA1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; @@ -645,6 +649,7 @@ buildActionMask = 2147483647; files = ( 57A4D20A1BA13D7A00F7D4B1 /* ReactiveCocoa.h in Headers */, + 9A6AAA1C1DB808AA0013AAEA /* RACObjCRuntimeUtilities.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -653,6 +658,7 @@ buildActionMask = 2147483647; files = ( A9B315CA1B3940AB0001CB9C /* ReactiveCocoa.h in Headers */, + 9A6AAA1B1DB808A90013AAEA /* RACObjCRuntimeUtilities.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -661,6 +667,7 @@ buildActionMask = 2147483647; files = ( D04725F019E49ED7006002AA /* ReactiveCocoa.h in Headers */, + 9A6AAA191DB808A80013AAEA /* RACObjCRuntimeUtilities.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -669,6 +676,7 @@ buildActionMask = 2147483647; files = ( D037666419EDA43C00A782A9 /* ReactiveCocoa.h in Headers */, + 9A6AAA1A1DB808A80013AAEA /* RACObjCRuntimeUtilities.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1119,7 +1127,7 @@ "DTRACE_PROBES_DISABLED=1", ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; - SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; }; name = Debug; }; @@ -1135,7 +1143,7 @@ "DTRACE_PROBES_DISABLED=1", ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; - SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; }; name = Test; }; @@ -1151,7 +1159,7 @@ "DTRACE_PROBES_DISABLED=1", ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; - SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; }; name = Release; }; @@ -1167,7 +1175,7 @@ "DTRACE_PROBES_DISABLED=1", ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; - SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; }; name = Profile; }; @@ -1239,7 +1247,7 @@ "DTRACE_PROBES_DISABLED=1", ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; - SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; }; name = Debug; }; @@ -1255,7 +1263,7 @@ "DTRACE_PROBES_DISABLED=1", ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; - SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; }; name = Test; }; @@ -1271,7 +1279,7 @@ "DTRACE_PROBES_DISABLED=1", ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; - SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; }; name = Release; }; @@ -1287,7 +1295,7 @@ "DTRACE_PROBES_DISABLED=1", ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; - SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; }; name = Profile; }; @@ -1339,7 +1347,7 @@ DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate/"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; }; name = Debug; }; @@ -1351,7 +1359,7 @@ DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate/"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; }; name = Release; }; @@ -1389,7 +1397,7 @@ DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; }; name = Debug; }; @@ -1401,7 +1409,7 @@ DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; }; name = Release; }; @@ -1458,7 +1466,7 @@ DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate/"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; }; name = Profile; }; @@ -1483,7 +1491,7 @@ DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; }; name = Profile; }; @@ -1527,7 +1535,7 @@ DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate/"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; }; name = Test; }; @@ -1552,7 +1560,7 @@ DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - SWIFT_INCLUDE_PATHS = "$(SRCROOT)/ReactiveCocoa/ReactiveCocoaPrivate"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; }; name = Test; }; diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index f9d34c6c76..2959551bc3 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -1,7 +1,6 @@ import Foundation import ReactiveSwift import enum Result.NoError -import ReactiveCocoaUtils.Private extension Reactive where Base: NSObject { /// Create a signal which sends a `next` event at the end of every invocation diff --git a/ReactiveCocoa/ReactiveCocoaPrivate/module.modulemap b/ReactiveCocoa/ReactiveCocoaPrivate/module.modulemap deleted file mode 100644 index 0fe7caf5cd..0000000000 --- a/ReactiveCocoa/ReactiveCocoaPrivate/module.modulemap +++ /dev/null @@ -1 +0,0 @@ -module ReactiveCocoaUtils {} diff --git a/ReactiveCocoa/ReactiveCocoaPrivate/module.private.modulemap b/ReactiveCocoa/ReactiveCocoaPrivate/module.private.modulemap deleted file mode 100644 index e696289373..0000000000 --- a/ReactiveCocoa/ReactiveCocoaPrivate/module.private.modulemap +++ /dev/null @@ -1,3 +0,0 @@ -explicit module ReactiveCocoaUtils.Private { - header "../RACObjCRuntimeUtilities.h" -} diff --git a/ReactiveCocoa/module.modulemap b/ReactiveCocoa/module.modulemap new file mode 100644 index 0000000000..ca356bb8e5 --- /dev/null +++ b/ReactiveCocoa/module.modulemap @@ -0,0 +1,7 @@ +framework module ReactiveCocoa { + umbrella header "ReactiveCocoa.h" + private header "RACObjCRuntimeUtilities.h" + + export * + module * { export * } +} From 38afa631ca0694338c31eb785c7bb42fc7d7adba Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Mon, 26 Sep 2016 18:49:47 +0900 Subject: [PATCH 0467/1028] Add official podspec --- .travis.yml | 6 ++++++ ReactiveCocoa.podspec | 29 +++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 ReactiveCocoa.podspec diff --git a/.travis.yml b/.travis.yml index 5056f2bf63..ee29542168 100644 --- a/.travis.yml +++ b/.travis.yml @@ -56,6 +56,12 @@ matrix: - carthage build --no-skip-current --platform watchOS env: - JOB=CARTHAGE-watchOS + - script: + - gem install cocoapods + - pod repo update --silent + - pod lib lint --allow-warnings + env: + - JOB=PODSPEC notifications: email: false slack: diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec new file mode 100644 index 0000000000..04de50e4a1 --- /dev/null +++ b/ReactiveCocoa.podspec @@ -0,0 +1,29 @@ +Pod::Spec.new do |s| + s.name = "ReactiveCocoa" + s.version = "5.0.0-alpha.1" + s.summary = "Streams of values over time" + s.description = <<-DESC + ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. + DESC + s.homepage = "https://github.com/ReactiveCocoa/ReactiveCocoa" + s.license = { :type => "MIT", :file => "LICENSE.md" } + s.author = "ReactiveCocoa" + + s.osx.deployment_target = "10.9" + s.ios.deployment_target = "8.0" + s.tvos.deployment_target = "9.0" + s.watchos.deployment_target = "2.0" + + # Right now this points to a commit, but eventually it will be a git tag instead. That tag will be something like `:tag => "#{s.version}"`, generating 5.0.0-alpha.1 for example. + s.source = { :git => "https://github.com/ReactiveCocoa/ReactiveCocoa.git", :commit => "2bee28d" } + s.source_files = "ReactiveCocoa/*.{swift,h,m}" + s.private_header_files = "ReactiveCocoa/RACObjCRuntimeUtilities.h" + s.osx.source_files = "ReactiveCocoa/AppKit/*.{swift}" + s.ios.source_files = "ReactiveCocoa/UIKit/*.{swift}" + s.tvos.source_files = "ReactiveCocoa/UIKit/*.{swift}" + s.tvos.exclude_files = "ReactiveCocoa/UIKit/*{UIDatePicker,UISwitch}*" + # The following line should be uncommented out once ReactiveCocoa/ReactiveCocoa#3252 is merged. + # s.module_map = "ReactiveCocoa/module.modulemap" + + s.dependency 'ReactiveSwift', '~> 1.0.0-alpha.3' +end From bcfbf2985b8ebf957aef1e6bdbb82c5e09f9e689 Mon Sep 17 00:00:00 2001 From: David Collado Sela Date: Thu, 20 Oct 2016 16:50:33 +0200 Subject: [PATCH 0468/1028] Trigger method for UIGestureRecognizers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Based on UIControl triggers I’ve added the old rac_gestureSignal as a trigger. --- ReactiveCocoa.xcodeproj/project.pbxproj | 6 +++++ ReactiveCocoa/UIKit/UIGestureRecognizer.swift | 25 +++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 ReactiveCocoa/UIKit/UIGestureRecognizer.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 87864c8f31..9ce5f968ac 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 419139461DB910570043C9D1 /* UIGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 419139431DB910570043C9D1 /* UIGestureRecognizer.swift */; }; + 419139491DB910570043C9D1 /* UIGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 419139431DB910570043C9D1 /* UIGestureRecognizer.swift */; }; 4A0E10FF1D2A92720065D310 /* NSObject+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */; }; 4A0E11001D2A92720065D310 /* NSObject+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */; }; 4A0E11011D2A92720065D310 /* NSObject+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */; }; @@ -217,6 +219,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 419139431DB910570043C9D1 /* UIGestureRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIGestureRecognizer.swift; sourceTree = ""; }; 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Lifetime.swift"; sourceTree = ""; }; 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Application.xcconfig"; sourceTree = ""; }; @@ -426,6 +429,7 @@ 9A1D05FA1D93E9F100ACF44C /* UITextField.swift */, 9A1D05FB1D93E9F100ACF44C /* UITextView.swift */, 9A1D05FC1D93E9F100ACF44C /* UIView.swift */, + 419139431DB910570043C9D1 /* UIGestureRecognizer.swift */, ); path = UIKit; sourceTree = ""; @@ -935,6 +939,7 @@ 9A1D067A1D94160100ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9ADE4A941DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, 9A1D05E31D93E99100ACF44C /* NSObject+Association.swift in Sources */, + 419139491DB910570043C9D1 /* UIGestureRecognizer.swift in Sources */, 9A1D06161D93EA0100ACF44C /* UIControl.swift in Sources */, 9A1D06141D93EA0100ACF44C /* UIButton.swift in Sources */, 9A1D065E1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, @@ -1044,6 +1049,7 @@ 9A1D05E11D93E99100ACF44C /* NSObject+Association.swift in Sources */, 9A1D06041D93EA0000ACF44C /* UIControl.swift in Sources */, 9A1D06021D93EA0000ACF44C /* UIButton.swift in Sources */, + 419139461DB910570043C9D1 /* UIGestureRecognizer.swift in Sources */, 9A1D060A1D93EA0000ACF44C /* UISwitch.swift in Sources */, 9A1D065C1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, 9A1D06071D93EA0000ACF44C /* UILabel.swift in Sources */, diff --git a/ReactiveCocoa/UIKit/UIGestureRecognizer.swift b/ReactiveCocoa/UIKit/UIGestureRecognizer.swift new file mode 100644 index 0000000000..72a0e793e5 --- /dev/null +++ b/ReactiveCocoa/UIKit/UIGestureRecognizer.swift @@ -0,0 +1,25 @@ +import ReactiveSwift +import UIKit +import enum Result.NoError + +extension Reactive where Base: UIGestureRecognizer { + + /// Create a signal which sends a `next` event for each gesture event + /// + /// - returns: + /// A trigger signal. + + public func trigger() -> Signal<(), NoError> { + return Signal { observer in + let receiver = CocoaTarget(observer) + base.addTarget(receiver, action: #selector(receiver.sendNext)) + + let disposable = lifetime.ended.observeCompleted(observer.sendCompleted) + + return ActionDisposable { [weak base = self.base] in + disposable?.dispose() + base?.removeTarget(receiver, action: #selector(receiver.sendNext)) + } + } + } +} From 88b9e8469f3ffcfebed1b36a5d619707acbb30af Mon Sep 17 00:00:00 2001 From: David Collado Sela Date: Thu, 20 Oct 2016 20:16:10 +0200 Subject: [PATCH 0469/1028] Changed name to recognised and moved to computed property --- ReactiveCocoa/UIKit/UIGestureRecognizer.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa/UIKit/UIGestureRecognizer.swift b/ReactiveCocoa/UIKit/UIGestureRecognizer.swift index 72a0e793e5..eb0ebec6f9 100644 --- a/ReactiveCocoa/UIKit/UIGestureRecognizer.swift +++ b/ReactiveCocoa/UIKit/UIGestureRecognizer.swift @@ -9,7 +9,7 @@ extension Reactive where Base: UIGestureRecognizer { /// - returns: /// A trigger signal. - public func trigger() -> Signal<(), NoError> { + public var recognised: Signal<(), NoError> { return Signal { observer in let receiver = CocoaTarget(observer) base.addTarget(receiver, action: #selector(receiver.sendNext)) From 1677895de4ffbe3d5152dcd92a4269db68f9485d Mon Sep 17 00:00:00 2001 From: Christopher Liscio Date: Thu, 20 Oct 2016 16:34:49 -0400 Subject: [PATCH 0470/1028] Add some extra control bindings --- ReactiveCocoa.xcodeproj/project.pbxproj | 4 ++++ ReactiveCocoa/AppKit/NSImageView.swift | 10 ++++++++++ ReactiveCocoa/AppKit/NSTextField.swift | 5 +++++ 3 files changed, 19 insertions(+) create mode 100644 ReactiveCocoa/AppKit/NSImageView.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 87864c8f31..40c77fda88 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -21,6 +21,7 @@ 7DFBED211CDB8D8300EE435B /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 7DFBED281CDB8DE300EE435B /* DynamicPropertySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */; }; 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */; }; + 8392D8FD1DB93E5E00504ED4 /* NSImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8392D8FC1DB93E5E00504ED4 /* NSImageView.swift */; }; 9A1D05E01D93E99100ACF44C /* NSObject+Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */; }; 9A1D05E11D93E99100ACF44C /* NSObject+Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */; }; 9A1D05E21D93E99100ACF44C /* NSObject+Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */; }; @@ -224,6 +225,7 @@ 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Framework.xcconfig"; sourceTree = ""; }; 57A4D2471BA13F9700F7D4B1 /* tvOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-StaticLibrary.xcconfig"; sourceTree = ""; }; 7DFBED031CDB8C9500EE435B /* ReactiveCocoaTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveCocoaTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 8392D8FC1DB93E5E00504ED4 /* NSImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSImageView.swift; sourceTree = ""; }; 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Association.swift"; sourceTree = ""; }; 9A1D05EC1D93E9F100ACF44C /* UIActivityIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorView.swift; sourceTree = ""; }; 9A1D05ED1D93E9F100ACF44C /* UIBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIBarButtonItem.swift; sourceTree = ""; }; @@ -405,6 +407,7 @@ children = ( 9ADE4A951DA6F018005C2AC8 /* NSTextField.swift */, 9ADE4A881DA6D206005C2AC8 /* NSControl.swift */, + 8392D8FC1DB93E5E00504ED4 /* NSImageView.swift */, ); path = AppKit; sourceTree = ""; @@ -1004,6 +1007,7 @@ 9ADE4A7C1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, 9ADE4A961DA6F018005C2AC8 /* NSTextField.swift in Sources */, 9A2E425E1DAA6737006D909F /* CocoaTarget.swift in Sources */, + 8392D8FD1DB93E5E00504ED4 /* NSImageView.swift in Sources */, 9ADE4A891DA6D206005C2AC8 /* NSControl.swift in Sources */, 9AF0EA751D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, 9ADE4A911DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, diff --git a/ReactiveCocoa/AppKit/NSImageView.swift b/ReactiveCocoa/AppKit/NSImageView.swift new file mode 100644 index 0000000000..f538720a6b --- /dev/null +++ b/ReactiveCocoa/AppKit/NSImageView.swift @@ -0,0 +1,10 @@ +import ReactiveSwift +import enum Result.NoError +import AppKit + +extension Reactive where Base: NSImageView { + /// Sets the currently displayed image + public var image: BindingTarget { + return makeBindingTarget { $0.image = $1 } + } +} diff --git a/ReactiveCocoa/AppKit/NSTextField.swift b/ReactiveCocoa/AppKit/NSTextField.swift index e5d7c63a46..c35a1e90a6 100644 --- a/ReactiveCocoa/AppKit/NSTextField.swift +++ b/ReactiveCocoa/AppKit/NSTextField.swift @@ -3,6 +3,11 @@ import AppKit import enum Result.NoError extension Reactive where Base: NSTextField { + /// Wraps the `stringValue` binding target for cross-platform compatibility + public var text: BindingTarget { + return stringValue + } + /// A signal of values in `String` from the text field upon any changes. public var continuousStringValues: Signal { return NotificationCenter.default From f11efbef87b2f492f7366aa991305fbf9fac4a47 Mon Sep 17 00:00:00 2001 From: David Collado Sela Date: Fri, 21 Oct 2016 10:34:00 +0200 Subject: [PATCH 0471/1028] Added UIGestureRecognizer test Added unit test for UIGestureRecognizer recognized reactive signal --- ReactiveCocoa.xcodeproj/project.pbxproj | 4 ++ .../UIKit/UIGestureRecognizerSpec.swift | 69 +++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 9ce5f968ac..2a81561008 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 419139461DB910570043C9D1 /* UIGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 419139431DB910570043C9D1 /* UIGestureRecognizer.swift */; }; 419139491DB910570043C9D1 /* UIGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 419139431DB910570043C9D1 /* UIGestureRecognizer.swift */; }; + 4191394E1DBA01A00043C9D1 /* UIGestureRecognizerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4191394B1DBA002C0043C9D1 /* UIGestureRecognizerSpec.swift */; }; 4A0E10FF1D2A92720065D310 /* NSObject+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */; }; 4A0E11001D2A92720065D310 /* NSObject+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */; }; 4A0E11011D2A92720065D310 /* NSObject+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */; }; @@ -220,6 +221,7 @@ /* Begin PBXFileReference section */ 419139431DB910570043C9D1 /* UIGestureRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIGestureRecognizer.swift; sourceTree = ""; }; + 4191394B1DBA002C0043C9D1 /* UIGestureRecognizerSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIGestureRecognizerSpec.swift; sourceTree = ""; }; 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Lifetime.swift"; sourceTree = ""; }; 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Application.xcconfig"; sourceTree = ""; }; @@ -451,6 +453,7 @@ 9A1D06321D93EA7E00ACF44C /* UITextFieldSpec.swift */, 9A1D06331D93EA7E00ACF44C /* UITextViewSpec.swift */, 9A1D06351D93EA7E00ACF44C /* UIViewSpec.swift */, + 4191394B1DBA002C0043C9D1 /* UIGestureRecognizerSpec.swift */, ); path = UIKit; sourceTree = ""; @@ -1078,6 +1081,7 @@ 9A1D063A1D93EA7E00ACF44C /* UIButtonSpec.swift in Sources */, 9A1D064A1D93EA7E00ACF44C /* UISegmentedControlSpec.swift in Sources */, 9A1D063E1D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift in Sources */, + 4191394E1DBA01A00043C9D1 /* UIGestureRecognizerSpec.swift in Sources */, 9A1D06361D93EA7E00ACF44C /* UIActivityIndicatorViewSpec.swift in Sources */, 9A1D06461D93EA7E00ACF44C /* UILabelSpec.swift in Sources */, 9A9DFEEA1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */, diff --git a/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift b/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift new file mode 100644 index 0000000000..9c5d762272 --- /dev/null +++ b/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift @@ -0,0 +1,69 @@ +import Quick +import Nimble +import ReactiveSwift +import ReactiveCocoa +import UIKit +import enum Result.NoError + +class UIGestureRecognizerSpec: QuickSpec { + override func spec() { + var gestureRecognizer: TestTapGestureRecognizer! + weak var _gestureRecognizer: TestTapGestureRecognizer? + + beforeEach { + gestureRecognizer = TestTapGestureRecognizer() + _gestureRecognizer = gestureRecognizer + } + + afterEach { + gestureRecognizer = nil + expect(_gestureRecognizer).to(beNil()) + } + + it("should send a value when the gesture is recognised") { + let signal = gestureRecognizer.reactive.recognised + + var counter = 0 + signal.observeValues { counter += 1 } + + expect(counter) == 0 + gestureRecognizer.fireGestureEvent(.possible) + + expect(counter) == 1 + + gestureRecognizer.fireGestureEvent( .began) + expect(counter) == 2 + + gestureRecognizer.fireGestureEvent(.changed) + expect(counter) == 3 + + gestureRecognizer.fireGestureEvent(.ended) + expect(counter) == 4 + + } + } +} + +private struct TargetActionPair { + let target: AnyObject + let action: Selector +} + +private class TestTapGestureRecognizer: UITapGestureRecognizer { + var targetActionPair: TargetActionPair? + var forceState: UIGestureRecognizerState = .ended + override var state: UIGestureRecognizerState { + get { return forceState } + set { self.state = newValue } + } + + override func addTarget(_ target: Any, action: Selector) { + targetActionPair = TargetActionPair(target: target as AnyObject, action: action) + } + + func fireGestureEvent(_ state: UIGestureRecognizerState) { + guard let targetAction = self.targetActionPair else { return } + forceState = state + _ = targetAction.target.perform(targetAction.action, with: self) + } +} From 550132fae34367bc661c782a484d25b3fa2eee45 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Sat, 22 Oct 2016 09:57:01 +0900 Subject: [PATCH 0472/1028] Update podspec --- ReactiveCocoa.podspec | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 04de50e4a1..c121ac63c5 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "5.0.0-alpha.1" + s.version = "5.0.0-alpha.2" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. @@ -14,7 +14,7 @@ Pod::Spec.new do |s| s.tvos.deployment_target = "9.0" s.watchos.deployment_target = "2.0" - # Right now this points to a commit, but eventually it will be a git tag instead. That tag will be something like `:tag => "#{s.version}"`, generating 5.0.0-alpha.1 for example. + # Right now this points to a commit, but eventually it will be a git tag instead. That tag will be something like `:tag => "#{s.version}"`, generating 5.0.0-alpha.2 for example. s.source = { :git => "https://github.com/ReactiveCocoa/ReactiveCocoa.git", :commit => "2bee28d" } s.source_files = "ReactiveCocoa/*.{swift,h,m}" s.private_header_files = "ReactiveCocoa/RACObjCRuntimeUtilities.h" @@ -22,8 +22,7 @@ Pod::Spec.new do |s| s.ios.source_files = "ReactiveCocoa/UIKit/*.{swift}" s.tvos.source_files = "ReactiveCocoa/UIKit/*.{swift}" s.tvos.exclude_files = "ReactiveCocoa/UIKit/*{UIDatePicker,UISwitch}*" - # The following line should be uncommented out once ReactiveCocoa/ReactiveCocoa#3252 is merged. - # s.module_map = "ReactiveCocoa/module.modulemap" + s.module_map = "ReactiveCocoa/module.modulemap" s.dependency 'ReactiveSwift', '~> 1.0.0-alpha.3' end From 7fdbe1964d09741c3d4c37d9b9d594f16ea6eb04 Mon Sep 17 00:00:00 2001 From: Anders Date: Sun, 16 Oct 2016 20:14:32 +0200 Subject: [PATCH 0473/1028] Reintroduced `reactive.prepareForReuse` for reusable components. --- ReactiveCocoa.xcodeproj/project.pbxproj | 20 +++++++ ReactiveCocoa/AppKit/ReusableComponents.swift | 12 +++++ ReactiveCocoa/Reusable.swift | 12 +++++ ReactiveCocoa/UIKit/ReusableComponents.swift | 5 ++ .../AppKit/ReusableComponentsSpec.swift | 38 ++++++++++++++ .../UIKit/ReusableComponentsSpec.swift | 52 +++++++++++++++++++ 6 files changed, 139 insertions(+) create mode 100644 ReactiveCocoa/AppKit/ReusableComponents.swift create mode 100644 ReactiveCocoa/Reusable.swift create mode 100644 ReactiveCocoa/UIKit/ReusableComponents.swift create mode 100644 ReactiveCocoaTests/AppKit/ReusableComponentsSpec.swift create mode 100644 ReactiveCocoaTests/UIKit/ReusableComponentsSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 87864c8f31..c78ca3125a 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -94,6 +94,13 @@ 9A6AAA0E1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; 9A6AAA0F1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; 9A6AAA101DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; + 9A6AAA1E1DB8F50D0013AAEA /* Reusable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA1D1DB8F50D0013AAEA /* Reusable.swift */; }; + 9A6AAA1F1DB8F50D0013AAEA /* Reusable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA1D1DB8F50D0013AAEA /* Reusable.swift */; }; + 9A6AAA201DB8F50D0013AAEA /* Reusable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA1D1DB8F50D0013AAEA /* Reusable.swift */; }; + 9A6AAA211DB8F50D0013AAEA /* Reusable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA1D1DB8F50D0013AAEA /* Reusable.swift */; }; + 9A6AAA231DB8F51C0013AAEA /* ReusableComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA221DB8F51C0013AAEA /* ReusableComponents.swift */; }; + 9A6AAA241DB8F51C0013AAEA /* ReusableComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA221DB8F51C0013AAEA /* ReusableComponents.swift */; }; + 9A6AAA261DB8F5280013AAEA /* ReusableComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA251DB8F5280013AAEA /* ReusableComponents.swift */; }; 9A9DFEE51DA7B5500039EE1B /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; 9A9DFEE91DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; 9A9DFEEA1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; @@ -259,6 +266,9 @@ 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyValueObservingSpec.swift; sourceTree = ""; }; 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaTarget.swift; sourceTree = ""; }; 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterceptingSpec.swift; sourceTree = ""; }; + 9A6AAA1D1DB8F50D0013AAEA /* Reusable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reusable.swift; sourceTree = ""; }; + 9A6AAA221DB8F51C0013AAEA /* ReusableComponents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponents.swift; sourceTree = ""; }; + 9A6AAA251DB8F5280013AAEA /* ReusableComponents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponents.swift; sourceTree = ""; }; 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssociationSpec.swift; sourceTree = ""; }; 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+KeyValueObserving.swift"; sourceTree = ""; }; 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaAction.swift; sourceTree = ""; }; @@ -403,6 +413,7 @@ 9A1D05E91D93E9F100ACF44C /* AppKit */ = { isa = PBXGroup; children = ( + 9A6AAA251DB8F5280013AAEA /* ReusableComponents.swift */, 9ADE4A951DA6F018005C2AC8 /* NSTextField.swift */, 9ADE4A881DA6D206005C2AC8 /* NSControl.swift */, ); @@ -412,6 +423,7 @@ 9A1D05EB1D93E9F100ACF44C /* UIKit */ = { isa = PBXGroup; children = ( + 9A6AAA221DB8F51C0013AAEA /* ReusableComponents.swift */, 9A1D05EC1D93E9F100ACF44C /* UIActivityIndicatorView.swift */, 9A1D05ED1D93E9F100ACF44C /* UIBarButtonItem.swift */, 9A1D05EE1D93E9F100ACF44C /* UIBarItem.swift */, @@ -525,6 +537,7 @@ 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */, 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */, 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */, + 9A6AAA1D1DB8F50D0013AAEA /* Reusable.swift */, 9A1D05E91D93E9F100ACF44C /* AppKit */, 9A1D05EB1D93E9F100ACF44C /* UIKit */, D04725ED19E49ED7006002AA /* Supporting Files */, @@ -929,6 +942,7 @@ 9A1D061F1D93EA0100ACF44C /* UITextField.swift in Sources */, 9ADE4A7F1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, 9A1D06201D93EA0100ACF44C /* UITextView.swift in Sources */, + 9A6AAA241DB8F51C0013AAEA /* ReusableComponents.swift in Sources */, 9A1D06181D93EA0100ACF44C /* UIImageView.swift in Sources */, 9AD0F06D1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, 9A1D06211D93EA0100ACF44C /* UIView.swift in Sources */, @@ -939,6 +953,7 @@ 9A1D06141D93EA0100ACF44C /* UIButton.swift in Sources */, 9A1D065E1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, 9AF0EA781D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, + 9A6AAA211DB8F50D0013AAEA /* Reusable.swift in Sources */, 9A1D06191D93EA0100ACF44C /* UILabel.swift in Sources */, 4A0E11021D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, CD0C45E11CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, @@ -981,6 +996,7 @@ buildActionMask = 2147483647; files = ( 9A2E42601DAA6737006D909F /* CocoaTarget.swift in Sources */, + 9A6AAA201DB8F50D0013AAEA /* Reusable.swift in Sources */, 9A9DFEE51DA7B5500039EE1B /* NSObject+Intercepting.swift in Sources */, 9A1D06791D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9ADE4A931DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, @@ -1001,8 +1017,10 @@ 9A1D06771D9415FF00ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, 9A1D05E01D93E99100ACF44C /* NSObject+Association.swift in Sources */, + 9A6AAA261DB8F5280013AAEA /* ReusableComponents.swift in Sources */, 9ADE4A7C1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, 9ADE4A961DA6F018005C2AC8 /* NSTextField.swift in Sources */, + 9A6AAA1E1DB8F50D0013AAEA /* Reusable.swift in Sources */, 9A2E425E1DAA6737006D909F /* CocoaTarget.swift in Sources */, 9ADE4A891DA6D206005C2AC8 /* NSControl.swift in Sources */, 9AF0EA751D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, @@ -1033,8 +1051,10 @@ files = ( 9A1D06011D93EA0000ACF44C /* UIBarItem.swift in Sources */, 9A1D060D1D93EA0000ACF44C /* UITextField.swift in Sources */, + 9A6AAA231DB8F51C0013AAEA /* ReusableComponents.swift in Sources */, 9A1D060E1D93EA0000ACF44C /* UITextView.swift in Sources */, 9A1D06061D93EA0000ACF44C /* UIImageView.swift in Sources */, + 9A6AAA1F1DB8F50D0013AAEA /* Reusable.swift in Sources */, 9AF0EA761D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, CD0C45DF1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9A2E425F1DAA6737006D909F /* CocoaTarget.swift in Sources */, diff --git a/ReactiveCocoa/AppKit/ReusableComponents.swift b/ReactiveCocoa/AppKit/ReusableComponents.swift new file mode 100644 index 0000000000..0505615ca3 --- /dev/null +++ b/ReactiveCocoa/AppKit/ReusableComponents.swift @@ -0,0 +1,12 @@ +import AppKit + +extension NSTableCellView: Reusable {} +extension NSCollectionViewItem: Reusable { + @nonobjc public func prepareForReuse() { + // Workaround: The compiler complains about `prepareForReuse` not being + // available for "OSXApplicationExtension 10.9", regardless of + // any `@available` annotation to the extension and to the + // protocol. + (self as AnyObject).prepareForReuse() + } +} diff --git a/ReactiveCocoa/Reusable.swift b/ReactiveCocoa/Reusable.swift new file mode 100644 index 0000000000..9e48e370d1 --- /dev/null +++ b/ReactiveCocoa/Reusable.swift @@ -0,0 +1,12 @@ +import ReactiveSwift +import enum Result.NoError + +public protocol Reusable: class { + func prepareForReuse() +} + +extension Reactive where Base: NSObject, Base: Reusable { + public var prepareForReuse: Signal<(), NoError> { + return trigger(for: Selector(("prepareForReuse"))) + } +} diff --git a/ReactiveCocoa/UIKit/ReusableComponents.swift b/ReactiveCocoa/UIKit/ReusableComponents.swift new file mode 100644 index 0000000000..8d36b20ae7 --- /dev/null +++ b/ReactiveCocoa/UIKit/ReusableComponents.swift @@ -0,0 +1,5 @@ +import UIKit + +extension UITableViewCell: Reusable {} +extension UITableViewHeaderFooterView: Reusable {} +extension UICollectionReusableView: Reusable {} diff --git a/ReactiveCocoaTests/AppKit/ReusableComponentsSpec.swift b/ReactiveCocoaTests/AppKit/ReusableComponentsSpec.swift new file mode 100644 index 0000000000..29dea1277d --- /dev/null +++ b/ReactiveCocoaTests/AppKit/ReusableComponentsSpec.swift @@ -0,0 +1,38 @@ +import Quick +import Nimble +import Result +import ReactiveSwift +import ReactiveCocoa +import AppKit + +class ReusableComponentsSpec: QuickSpec { + override func spec() { + describe("NSTableCellView") { + let cell = NSTableCellView() + + var isTriggered = false + cell.reactive.prepareForReuse.observeValues { + isTriggered = true + } + + expect(isTriggered) == false + + cell.prepareForReuse() + expect(isTriggered) == true + } + + describe("NSCollectionViewItem") { + let cell = NSCollectionViewItem() + + var isTriggered = false + cell.reactive.prepareForReuse.observeValues { + isTriggered = true + } + + expect(isTriggered) == false + + cell.prepareForReuse() + expect(isTriggered) == true + } + } +} diff --git a/ReactiveCocoaTests/UIKit/ReusableComponentsSpec.swift b/ReactiveCocoaTests/UIKit/ReusableComponentsSpec.swift new file mode 100644 index 0000000000..f1c53d4848 --- /dev/null +++ b/ReactiveCocoaTests/UIKit/ReusableComponentsSpec.swift @@ -0,0 +1,52 @@ +import Quick +import Nimble +import Result +import ReactiveSwift +import ReactiveCocoa +import UIKit + +class ReusableComponentsSpec: QuickSpec { + override func spec() { + describe("UITableViewCell") { + let cell = UITableViewCell() + + var isTriggered = false + cell.reactive.prepareForReuse.observeValues { + isTriggered = true + } + + expect(isTriggered) == false + + cell.prepareForReuse() + expect(isTriggered) == true + } + + describe("UITableViewHeaderFooterView") { + let cell = UITableViewHeaderFooterView() + + var isTriggered = false + cell.reactive.prepareForReuse.observeValues { + isTriggered = true + } + + expect(isTriggered) == false + + cell.prepareForReuse() + expect(isTriggered) == true + } + + describe("UICollectionReusableView") { + let cell = UICollectionReusableView() + + var isTriggered = false + cell.reactive.prepareForReuse.observeValues { + isTriggered = true + } + + expect(isTriggered) == false + + cell.prepareForReuse() + expect(isTriggered) == true + } + } +} From c594080c92d6672099c15e59744a6c21de519fc1 Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 20 Oct 2016 15:05:32 +0200 Subject: [PATCH 0474/1028] Removed the false workaround. Updated the project settings. --- ReactiveCocoa.xcodeproj/project.pbxproj | 10 ++++++++++ ReactiveCocoa/AppKit/ReusableComponents.swift | 10 +--------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index c78ca3125a..fbaabfa2db 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -101,6 +101,9 @@ 9A6AAA231DB8F51C0013AAEA /* ReusableComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA221DB8F51C0013AAEA /* ReusableComponents.swift */; }; 9A6AAA241DB8F51C0013AAEA /* ReusableComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA221DB8F51C0013AAEA /* ReusableComponents.swift */; }; 9A6AAA261DB8F5280013AAEA /* ReusableComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA251DB8F5280013AAEA /* ReusableComponents.swift */; }; + 9A6AAA2B1DB8F85C0013AAEA /* ReusableComponentsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA271DB8F7EB0013AAEA /* ReusableComponentsSpec.swift */; }; + 9A6AAA2C1DB8F8640013AAEA /* ReusableComponentsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA291DB8F7F10013AAEA /* ReusableComponentsSpec.swift */; }; + 9A6AAA2D1DB8F8650013AAEA /* ReusableComponentsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA291DB8F7F10013AAEA /* ReusableComponentsSpec.swift */; }; 9A9DFEE51DA7B5500039EE1B /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; 9A9DFEE91DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; 9A9DFEEA1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; @@ -269,6 +272,8 @@ 9A6AAA1D1DB8F50D0013AAEA /* Reusable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reusable.swift; sourceTree = ""; }; 9A6AAA221DB8F51C0013AAEA /* ReusableComponents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponents.swift; sourceTree = ""; }; 9A6AAA251DB8F5280013AAEA /* ReusableComponents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponents.swift; sourceTree = ""; }; + 9A6AAA271DB8F7EB0013AAEA /* ReusableComponentsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ReusableComponentsSpec.swift; path = AppKit/ReusableComponentsSpec.swift; sourceTree = ""; }; + 9A6AAA291DB8F7F10013AAEA /* ReusableComponentsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponentsSpec.swift; sourceTree = ""; }; 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssociationSpec.swift; sourceTree = ""; }; 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+KeyValueObserving.swift"; sourceTree = ""; }; 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaAction.swift; sourceTree = ""; }; @@ -445,6 +450,7 @@ 9A1D06231D93EA7E00ACF44C /* UIKit */ = { isa = PBXGroup; children = ( + 9A6AAA291DB8F7F10013AAEA /* ReusableComponentsSpec.swift */, 9A1D06241D93EA7E00ACF44C /* UIActivityIndicatorViewSpec.swift */, 9A1D06251D93EA7E00ACF44C /* UIBarButtonItemSpec.swift */, 9A1D06261D93EA7E00ACF44C /* UIButtonSpec.swift */, @@ -467,6 +473,7 @@ isa = PBXGroup; children = ( 9ADE4A8D1DA6D965005C2AC8 /* NSControlSpec.swift */, + 9A6AAA271DB8F7EB0013AAEA /* ReusableComponentsSpec.swift */, ); name = AppKit; sourceTree = ""; @@ -944,6 +951,7 @@ 9A1D06201D93EA0100ACF44C /* UITextView.swift in Sources */, 9A6AAA241DB8F51C0013AAEA /* ReusableComponents.swift in Sources */, 9A1D06181D93EA0100ACF44C /* UIImageView.swift in Sources */, + 9A6AAA2D1DB8F8650013AAEA /* ReusableComponentsSpec.swift in Sources */, 9AD0F06D1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, 9A1D06211D93EA0100ACF44C /* UIView.swift in Sources */, 9A1D067A1D94160100ACF44C /* RACObjCRuntimeUtilities.m in Sources */, @@ -1039,6 +1047,7 @@ D0A2260E1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */, B696FB811A7640C00075236D /* TestError.swift in Sources */, BFA6B94D1A7604D400C846D1 /* SignalProducerNimbleMatchers.swift in Sources */, + 9A6AAA2B1DB8F85C0013AAEA /* ReusableComponentsSpec.swift in Sources */, CD8401831CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, 9A9DFEE91DA7EFB60039EE1B /* AssociationSpec.swift in Sources */, 9A1E72BA1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, @@ -1054,6 +1063,7 @@ 9A6AAA231DB8F51C0013AAEA /* ReusableComponents.swift in Sources */, 9A1D060E1D93EA0000ACF44C /* UITextView.swift in Sources */, 9A1D06061D93EA0000ACF44C /* UIImageView.swift in Sources */, + 9A6AAA2C1DB8F8640013AAEA /* ReusableComponentsSpec.swift in Sources */, 9A6AAA1F1DB8F50D0013AAEA /* Reusable.swift in Sources */, 9AF0EA761D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, CD0C45DF1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, diff --git a/ReactiveCocoa/AppKit/ReusableComponents.swift b/ReactiveCocoa/AppKit/ReusableComponents.swift index 0505615ca3..8c94c88451 100644 --- a/ReactiveCocoa/AppKit/ReusableComponents.swift +++ b/ReactiveCocoa/AppKit/ReusableComponents.swift @@ -1,12 +1,4 @@ import AppKit extension NSTableCellView: Reusable {} -extension NSCollectionViewItem: Reusable { - @nonobjc public func prepareForReuse() { - // Workaround: The compiler complains about `prepareForReuse` not being - // available for "OSXApplicationExtension 10.9", regardless of - // any `@available` annotation to the extension and to the - // protocol. - (self as AnyObject).prepareForReuse() - } -} +extension NSCollectionViewItem: Reusable {} From a3c3dc214251b48c732de2a85c32680b509e5063 Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 20 Oct 2016 15:53:09 +0200 Subject: [PATCH 0475/1028] Adjusted the `prepareForReuse` extensions to account for platform API differences. --- ReactiveCocoa.xcodeproj/project.pbxproj | 18 +++---------- ReactiveCocoa/AppKit/ReusableComponents.swift | 16 ++++++++++-- ReactiveCocoa/Reusable.swift | 12 --------- ReactiveCocoa/UIKit/ReusableComponents.swift | 12 +++++++++ .../AppKit/ReusableComponentsSpec.swift | 26 ++++++++++++------- 5 files changed, 47 insertions(+), 37 deletions(-) delete mode 100644 ReactiveCocoa/Reusable.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index fbaabfa2db..0f10c5b174 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -94,16 +94,12 @@ 9A6AAA0E1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; 9A6AAA0F1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; 9A6AAA101DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; - 9A6AAA1E1DB8F50D0013AAEA /* Reusable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA1D1DB8F50D0013AAEA /* Reusable.swift */; }; - 9A6AAA1F1DB8F50D0013AAEA /* Reusable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA1D1DB8F50D0013AAEA /* Reusable.swift */; }; - 9A6AAA201DB8F50D0013AAEA /* Reusable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA1D1DB8F50D0013AAEA /* Reusable.swift */; }; - 9A6AAA211DB8F50D0013AAEA /* Reusable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA1D1DB8F50D0013AAEA /* Reusable.swift */; }; 9A6AAA231DB8F51C0013AAEA /* ReusableComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA221DB8F51C0013AAEA /* ReusableComponents.swift */; }; 9A6AAA241DB8F51C0013AAEA /* ReusableComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA221DB8F51C0013AAEA /* ReusableComponents.swift */; }; 9A6AAA261DB8F5280013AAEA /* ReusableComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA251DB8F5280013AAEA /* ReusableComponents.swift */; }; 9A6AAA2B1DB8F85C0013AAEA /* ReusableComponentsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA271DB8F7EB0013AAEA /* ReusableComponentsSpec.swift */; }; - 9A6AAA2C1DB8F8640013AAEA /* ReusableComponentsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA291DB8F7F10013AAEA /* ReusableComponentsSpec.swift */; }; - 9A6AAA2D1DB8F8650013AAEA /* ReusableComponentsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA291DB8F7F10013AAEA /* ReusableComponentsSpec.swift */; }; + 9A6AAA2E1DB903A20013AAEA /* ReusableComponentsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA291DB8F7F10013AAEA /* ReusableComponentsSpec.swift */; }; + 9A6AAA2F1DB903A40013AAEA /* ReusableComponentsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA291DB8F7F10013AAEA /* ReusableComponentsSpec.swift */; }; 9A9DFEE51DA7B5500039EE1B /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; 9A9DFEE91DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; 9A9DFEEA1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; @@ -269,7 +265,6 @@ 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyValueObservingSpec.swift; sourceTree = ""; }; 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaTarget.swift; sourceTree = ""; }; 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterceptingSpec.swift; sourceTree = ""; }; - 9A6AAA1D1DB8F50D0013AAEA /* Reusable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reusable.swift; sourceTree = ""; }; 9A6AAA221DB8F51C0013AAEA /* ReusableComponents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponents.swift; sourceTree = ""; }; 9A6AAA251DB8F5280013AAEA /* ReusableComponents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponents.swift; sourceTree = ""; }; 9A6AAA271DB8F7EB0013AAEA /* ReusableComponentsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ReusableComponentsSpec.swift; path = AppKit/ReusableComponentsSpec.swift; sourceTree = ""; }; @@ -544,7 +539,6 @@ 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */, 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */, 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */, - 9A6AAA1D1DB8F50D0013AAEA /* Reusable.swift */, 9A1D05E91D93E9F100ACF44C /* AppKit */, 9A1D05EB1D93E9F100ACF44C /* UIKit */, D04725ED19E49ED7006002AA /* Supporting Files */, @@ -951,7 +945,6 @@ 9A1D06201D93EA0100ACF44C /* UITextView.swift in Sources */, 9A6AAA241DB8F51C0013AAEA /* ReusableComponents.swift in Sources */, 9A1D06181D93EA0100ACF44C /* UIImageView.swift in Sources */, - 9A6AAA2D1DB8F8650013AAEA /* ReusableComponentsSpec.swift in Sources */, 9AD0F06D1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, 9A1D06211D93EA0100ACF44C /* UIView.swift in Sources */, 9A1D067A1D94160100ACF44C /* RACObjCRuntimeUtilities.m in Sources */, @@ -961,7 +954,6 @@ 9A1D06141D93EA0100ACF44C /* UIButton.swift in Sources */, 9A1D065E1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, 9AF0EA781D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, - 9A6AAA211DB8F50D0013AAEA /* Reusable.swift in Sources */, 9A1D06191D93EA0100ACF44C /* UILabel.swift in Sources */, 4A0E11021D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, CD0C45E11CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, @@ -988,6 +980,7 @@ 9A1D064B1D93EA7E00ACF44C /* UISegmentedControlSpec.swift in Sources */, 9A1D063F1D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift in Sources */, 9A1D06371D93EA7E00ACF44C /* UIActivityIndicatorViewSpec.swift in Sources */, + 9A6AAA2F1DB903A40013AAEA /* ReusableComponentsSpec.swift in Sources */, 9A1D06471D93EA7E00ACF44C /* UILabelSpec.swift in Sources */, 9A1D06391D93EA7E00ACF44C /* UIBarButtonItemSpec.swift in Sources */, 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */, @@ -1004,7 +997,6 @@ buildActionMask = 2147483647; files = ( 9A2E42601DAA6737006D909F /* CocoaTarget.swift in Sources */, - 9A6AAA201DB8F50D0013AAEA /* Reusable.swift in Sources */, 9A9DFEE51DA7B5500039EE1B /* NSObject+Intercepting.swift in Sources */, 9A1D06791D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9ADE4A931DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, @@ -1028,7 +1020,6 @@ 9A6AAA261DB8F5280013AAEA /* ReusableComponents.swift in Sources */, 9ADE4A7C1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, 9ADE4A961DA6F018005C2AC8 /* NSTextField.swift in Sources */, - 9A6AAA1E1DB8F50D0013AAEA /* Reusable.swift in Sources */, 9A2E425E1DAA6737006D909F /* CocoaTarget.swift in Sources */, 9ADE4A891DA6D206005C2AC8 /* NSControl.swift in Sources */, 9AF0EA751D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, @@ -1063,8 +1054,6 @@ 9A6AAA231DB8F51C0013AAEA /* ReusableComponents.swift in Sources */, 9A1D060E1D93EA0000ACF44C /* UITextView.swift in Sources */, 9A1D06061D93EA0000ACF44C /* UIImageView.swift in Sources */, - 9A6AAA2C1DB8F8640013AAEA /* ReusableComponentsSpec.swift in Sources */, - 9A6AAA1F1DB8F50D0013AAEA /* Reusable.swift in Sources */, 9AF0EA761D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, CD0C45DF1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9A2E425F1DAA6737006D909F /* CocoaTarget.swift in Sources */, @@ -1109,6 +1098,7 @@ 9A6AAA0F1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */, 9A1D06381D93EA7E00ACF44C /* UIBarButtonItemSpec.swift in Sources */, 9A1E72BB1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, + 9A6AAA2E1DB903A20013AAEA /* ReusableComponentsSpec.swift in Sources */, 9A1D06401D93EA7E00ACF44C /* UIControlSpec.swift in Sources */, 9A1D06481D93EA7E00ACF44C /* UIProgressViewSpec.swift in Sources */, CD8401841CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, diff --git a/ReactiveCocoa/AppKit/ReusableComponents.swift b/ReactiveCocoa/AppKit/ReusableComponents.swift index 8c94c88451..db2e5ec908 100644 --- a/ReactiveCocoa/AppKit/ReusableComponents.swift +++ b/ReactiveCocoa/AppKit/ReusableComponents.swift @@ -1,4 +1,16 @@ import AppKit +import ReactiveSwift +import enum Result.NoError -extension NSTableCellView: Reusable {} -extension NSCollectionViewItem: Reusable {} +extension Reactive where Base: NSObject, Base: NSView { + public var prepareForReuse: Signal<(), NoError> { + return trigger(for: #selector(base.prepareForReuse)) + } +} + +extension Reactive where Base: NSObject, Base: NSCollectionViewElement { + @available(macOS 10.11, *) + public var prepareForReuse: Signal<(), NoError> { + return trigger(for: #selector(base.prepareForReuse)) + } +} diff --git a/ReactiveCocoa/Reusable.swift b/ReactiveCocoa/Reusable.swift deleted file mode 100644 index 9e48e370d1..0000000000 --- a/ReactiveCocoa/Reusable.swift +++ /dev/null @@ -1,12 +0,0 @@ -import ReactiveSwift -import enum Result.NoError - -public protocol Reusable: class { - func prepareForReuse() -} - -extension Reactive where Base: NSObject, Base: Reusable { - public var prepareForReuse: Signal<(), NoError> { - return trigger(for: Selector(("prepareForReuse"))) - } -} diff --git a/ReactiveCocoa/UIKit/ReusableComponents.swift b/ReactiveCocoa/UIKit/ReusableComponents.swift index 8d36b20ae7..3beafd23cb 100644 --- a/ReactiveCocoa/UIKit/ReusableComponents.swift +++ b/ReactiveCocoa/UIKit/ReusableComponents.swift @@ -1,4 +1,16 @@ import UIKit +import ReactiveSwift +import enum Result.NoError + +@objc public protocol Reusable: class { + func prepareForReuse() +} + +extension Reactive where Base: NSObject, Base: Reusable { + public var prepareForReuse: Signal<(), NoError> { + return trigger(for: #selector(base.prepareForReuse)) + } +} extension UITableViewCell: Reusable {} extension UITableViewHeaderFooterView: Reusable {} diff --git a/ReactiveCocoaTests/AppKit/ReusableComponentsSpec.swift b/ReactiveCocoaTests/AppKit/ReusableComponentsSpec.swift index 29dea1277d..5cdf422897 100644 --- a/ReactiveCocoaTests/AppKit/ReusableComponentsSpec.swift +++ b/ReactiveCocoaTests/AppKit/ReusableComponentsSpec.swift @@ -21,18 +21,26 @@ class ReusableComponentsSpec: QuickSpec { expect(isTriggered) == true } - describe("NSCollectionViewItem") { - let cell = NSCollectionViewItem() + if #available(macOS 10.11, *) { + describe("NSCollectionViewItem") { + let item = TestViewItem() - var isTriggered = false - cell.reactive.prepareForReuse.observeValues { - isTriggered = true - } + var isTriggered = false + item.reactive.prepareForReuse.observeValues { + isTriggered = true + } - expect(isTriggered) == false + expect(isTriggered) == false - cell.prepareForReuse() - expect(isTriggered) == true + item.prepareForReuse() + expect(isTriggered) == true + } } } } + +private class TestViewItem: NSCollectionViewItem { + override func loadView() { + view = NSView() + } +} From 43431a20cc83e9e41f31c3f9e886bc8bd6319860 Mon Sep 17 00:00:00 2001 From: Anders Date: Sat, 22 Oct 2016 01:58:37 +0200 Subject: [PATCH 0476/1028] Fixed the ReusableComponentsSpecs. --- .../AppKit/ReusableComponentsSpec.swift | 36 +++++++------ .../UIKit/ReusableComponentsSpec.swift | 54 ++++++++++--------- 2 files changed, 50 insertions(+), 40 deletions(-) diff --git a/ReactiveCocoaTests/AppKit/ReusableComponentsSpec.swift b/ReactiveCocoaTests/AppKit/ReusableComponentsSpec.swift index 5cdf422897..359b395009 100644 --- a/ReactiveCocoaTests/AppKit/ReusableComponentsSpec.swift +++ b/ReactiveCocoaTests/AppKit/ReusableComponentsSpec.swift @@ -8,32 +8,36 @@ import AppKit class ReusableComponentsSpec: QuickSpec { override func spec() { describe("NSTableCellView") { - let cell = NSTableCellView() + it("should send a `value` event when `prepareForReuse` is triggered") { + let cell = NSTableCellView() - var isTriggered = false - cell.reactive.prepareForReuse.observeValues { - isTriggered = true - } + var isTriggered = false + cell.reactive.prepareForReuse.observeValues { + isTriggered = true + } - expect(isTriggered) == false + expect(isTriggered) == false - cell.prepareForReuse() - expect(isTriggered) == true + cell.prepareForReuse() + expect(isTriggered) == true + } } if #available(macOS 10.11, *) { describe("NSCollectionViewItem") { - let item = TestViewItem() + it("should send a `value` event when `prepareForReuse` is triggered") { + let item = TestViewItem() - var isTriggered = false - item.reactive.prepareForReuse.observeValues { - isTriggered = true - } + var isTriggered = false + item.reactive.prepareForReuse.observeValues { + isTriggered = true + } - expect(isTriggered) == false + expect(isTriggered) == false - item.prepareForReuse() - expect(isTriggered) == true + item.prepareForReuse() + expect(isTriggered) == true + } } } } diff --git a/ReactiveCocoaTests/UIKit/ReusableComponentsSpec.swift b/ReactiveCocoaTests/UIKit/ReusableComponentsSpec.swift index f1c53d4848..31c94a0827 100644 --- a/ReactiveCocoaTests/UIKit/ReusableComponentsSpec.swift +++ b/ReactiveCocoaTests/UIKit/ReusableComponentsSpec.swift @@ -8,45 +8,51 @@ import UIKit class ReusableComponentsSpec: QuickSpec { override func spec() { describe("UITableViewCell") { - let cell = UITableViewCell() + it("should send a `value` event when `prepareForReuse` is triggered") { + let cell = UITableViewCell() - var isTriggered = false - cell.reactive.prepareForReuse.observeValues { - isTriggered = true - } + var isTriggered = false + cell.reactive.prepareForReuse.observeValues { + isTriggered = true + } - expect(isTriggered) == false + expect(isTriggered) == false - cell.prepareForReuse() - expect(isTriggered) == true + cell.prepareForReuse() + expect(isTriggered) == true + } } describe("UITableViewHeaderFooterView") { - let cell = UITableViewHeaderFooterView() + it("should send a `value` event when `prepareForReuse` is triggered") { + let cell = UITableViewHeaderFooterView() - var isTriggered = false - cell.reactive.prepareForReuse.observeValues { - isTriggered = true - } + var isTriggered = false + cell.reactive.prepareForReuse.observeValues { + isTriggered = true + } - expect(isTriggered) == false + expect(isTriggered) == false - cell.prepareForReuse() - expect(isTriggered) == true + cell.prepareForReuse() + expect(isTriggered) == true + } } describe("UICollectionReusableView") { - let cell = UICollectionReusableView() + it("should send a `value` event when `prepareForReuse` is triggered") { + let cell = UICollectionReusableView() - var isTriggered = false - cell.reactive.prepareForReuse.observeValues { - isTriggered = true - } + var isTriggered = false + cell.reactive.prepareForReuse.observeValues { + isTriggered = true + } - expect(isTriggered) == false + expect(isTriggered) == false - cell.prepareForReuse() - expect(isTriggered) == true + cell.prepareForReuse() + expect(isTriggered) == true + } } } } From b0bf2987753049831ea252f79b53b2e9ef718961 Mon Sep 17 00:00:00 2001 From: Anders Date: Sat, 22 Oct 2016 18:39:47 +0200 Subject: [PATCH 0477/1028] Update podspec. --- ReactiveCocoa.podspec | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index c121ac63c5..924f633f72 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -14,8 +14,7 @@ Pod::Spec.new do |s| s.tvos.deployment_target = "9.0" s.watchos.deployment_target = "2.0" - # Right now this points to a commit, but eventually it will be a git tag instead. That tag will be something like `:tag => "#{s.version}"`, generating 5.0.0-alpha.2 for example. - s.source = { :git => "https://github.com/ReactiveCocoa/ReactiveCocoa.git", :commit => "2bee28d" } + s.source = { :git => "https://github.com/ReactiveCocoa/ReactiveCocoa.git", :tag => "#{s.version}" } s.source_files = "ReactiveCocoa/*.{swift,h,m}" s.private_header_files = "ReactiveCocoa/RACObjCRuntimeUtilities.h" s.osx.source_files = "ReactiveCocoa/AppKit/*.{swift}" From 438bfb2c1bec914de70990e2f157271c27f65ba2 Mon Sep 17 00:00:00 2001 From: Anders Date: Sat, 22 Oct 2016 18:45:01 +0200 Subject: [PATCH 0478/1028] Added a CocoaPods badge to README.md. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3c7c17f661..3d78260679 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ![](Logo/header.png) -[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![GitHub release](https://img.shields.io/github/release/ReactiveCocoa/ReactiveCocoa.svg)](https://github.com/ReactiveCocoa/ReactiveCocoa/releases) ![Swift 3.0.x](https://img.shields.io/badge/Swift-3.0.x-orange.svg) ![platforms](https://img.shields.io/badge/platforms-iOS%20%7C%20OS%20X%20%7C%20watchOS%20%7C%20tvOS%20-lightgrey.svg) +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![CocoaPods compatible](https://img.shields.io/cocoapods/v/ReactiveCocoa.svg)](https://cocoapods.org/pods/ReactiveCocoa) [![GitHub release](https://img.shields.io/github/release/ReactiveCocoa/ReactiveCocoa.svg)](https://github.com/ReactiveCocoa/ReactiveCocoa/releases) ![Swift 3.0.x](https://img.shields.io/badge/Swift-3.0.x-orange.svg) ![platforms](https://img.shields.io/badge/platforms-iOS%20%7C%20OS%20X%20%7C%20watchOS%20%7C%20tvOS%20-lightgrey.svg) ReactiveCocoa (RAC) is a Cocoa framework built on top of [ReactiveSwift][]. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. From 348008253fc338e8ff63fccebb0fc550b8002dca Mon Sep 17 00:00:00 2001 From: Anders Date: Sat, 22 Oct 2016 22:04:25 +0200 Subject: [PATCH 0479/1028] Rewrote the readme for ReactiveCocoa 5.0. --- Logo/header.png | Bin 43393 -> 53148 bytes README.md | 151 ++++++++++++++++++++++++++++++------------------ 2 files changed, 94 insertions(+), 57 deletions(-) diff --git a/Logo/header.png b/Logo/header.png index 95b4dece8faea25b18a1d2e7cd9e4020adf07485..5c03daeae875ed98bb56148c04c46b0596ea3939 100644 GIT binary patch literal 53148 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ͱG9jy&7E~ni!AQA5b0=lLF>xGbHEs_s}{#)2#6g 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%0AJUi*4yQp_&AEPSBWS<2TrSsNA=m8 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 'https://github.com/ReactiveCocoa/ReactiveCocoa.git' +``` + +#### Git submodule 1. Add the ReactiveCocoa repository as a [submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules) of your @@ -81,21 +111,28 @@ To add RAC to your application: 1. If your application target does not contain Swift code at all, you should also set the `EMBEDDED_CONTENT_CONTAINS_SWIFT` build setting to “Yes”. -Or, if you’re using [Carthage](https://github.com/Carthage/Carthage), simply add -ReactiveCocoa to your `Cartfile`: -``` -github "ReactiveCocoa/ReactiveCocoa" -``` -Make sure to add `ReactiveCocoa.framework`, `ReactiveSwift`, and `Result.framework` to "Linked Frameworks and Libraries" and "copy-frameworks" Build Phases. +## Objective-C, Swift and the ReactiveCocoa family + +Starting with [version 5.0][CHANGELOG], ReactiveCocoa primarily focuses on providing reactive extensions to Cocoa frameworks in Swift. In other words: + +1. The core Swift API - which provides FRP inspired primitives - had been spun off as [ReactiveSwift][]. + +2. The legacy Objective-C API had been spun off as [ReactiveObjC][]. + +3. The bridge between the core Swift API and the legacy Objective-C API is now hosted in ([ReactiveObjCBridge][]), which is mostly meant as a compatibility layer for older ReactiveCocoa projects. + +The Objective-C API will continue to be maintained and supported in the foreseeable future. But given the maturity of the framework and the language, non-trivial changes are not expected. Please visit [ReactiveObjC][] for more information. + +**We highly recommend all Swift projects using the Swift API.** + +## Have a question? +If you need any help, please visit our [GitHub issues](https://github.com/ReactiveCocoa/ReactiveCocoa/issues?q=is%3Aissue+label%3Aquestion+) or [Stack Overflow](http://stackoverflow.com/questions/tagged/reactive-cocoa). Feel free to file an issue if you do not manage to find any solution from the archives. -If you would prefer to use [CocoaPods](https://cocoapods.org), there are some -[unofficial podspecs](https://github.com/CocoaPods/Specs/tree/master/Specs/ReactiveCocoa) -that have been generously contributed by third parties. +## Using Swift 2.x? +See [ReactiveCocoa +4.x](https://github.com/ReactiveCocoa/ReactiveCocoa/tree/v4.0.0) for legacy Swift support. -Once you’ve set up your project, check out the [Framework Overview][] for -a tour of ReactiveCocoa’s concepts, and the [Basic Operators][] for some -introductory examples of using it. [ReactiveSwift]: https://github.com/ReactiveCocoa/ReactiveSwift [ReactiveObjC]: https://github.com/ReactiveCocoa/ReactiveObjC From 35cba381f415059ad308da8204ecdc86bfcb5660 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 22 Oct 2016 22:26:27 +0200 Subject: [PATCH 0480/1028] Fixed a typo. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fe8ac638ed..0662f00f2b 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ __ReactiveCocoa__ wraps various aspects of Cocoa frameworks with the declarative 1. **Expressive, safe key path observation** You may easily obtain a stream of values for a certain key path, without the - need to deal with the obscured KVO API. + need to deal with the obscure KVO API. ```swift let producer = object.reactive.values(forKeyPath: #keyPath(key)) ``` From cdc1b7dbe35aa92e3ef2d370428b4821e697e408 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 22 Oct 2016 22:30:57 +0200 Subject: [PATCH 0481/1028] Fixed another typo in a code snippet. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0662f00f2b..c29faded33 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ __ReactiveCocoa__ wraps various aspects of Cocoa frameworks with the declarative ```swift NotificationCenter.default.reactive .notifications(forName: .MyNotification) - .take(until: self.reactive.lifetime) + .take(during: self.reactive.lifetime) ``` 1. **Method call interception** From f342a53a632cf0854e0d5b012b3d3823fb84dd2f Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sun, 23 Oct 2016 05:25:06 +0200 Subject: [PATCH 0482/1028] Improved the writing. --- README.md | 71 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index c29faded33..11c3a6a62e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ![](Logo/header.png) -##### Reactive extensions to Cocoa frameworks, built on top of [ReactiveSwift][]. +#### Reactive extensions to Cocoa frameworks, built on top of [ReactiveSwift][]. [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](#carthage) [![CocoaPods compatible](https://img.shields.io/cocoapods/v/ReactiveCocoa.svg)](#cocoapods) [![GitHub release](https://img.shields.io/github/release/ReactiveCocoa/ReactiveCocoa.svg)](https://github.com/ReactiveCocoa/ReactiveCocoa/releases) ![Swift 3.0.x](https://img.shields.io/badge/Swift-3.0.x-orange.svg) ![platforms](https://img.shields.io/badge/platforms-iOS%20%7C%20OS%20X%20%7C%20watchOS%20%7C%20tvOS%20-lightgrey.svg) @@ -9,48 +9,67 @@ __ReactiveCocoa__ wraps various aspects of Cocoa frameworks with the declarative primitives from [ReactiveSwift](). Let's go through a few core aspects of ReactiveCocoa: -1. **Unidirectional UI bindings** +1. **UI Bindings** - You may establish bindings from any streams of values to the `BindingTarget`s - exposed by the UI components. + UI components exposes `BindingTarget`s, which accept bindings from any + kind of streams of values via the `<~` operator. ```swift + // Bind the `name` property of `person` to the text value of an `UILabel`. nameLabel.text <~ person.name ``` 1. **Controls and User Interactions** - You may observe many kinds of user interactions as streams of values. For - example, UI controls expose signals that send user initiated - changes in its state. + Interactive UI components expose `Signal`s for control events + and updates in the control value upon user interactions. + + A selected set of controls provide a convenience, expressive binding + API for `Action`s. + + ```swift - perferences.allowsCookies <~ cookieMonsterView.toggle.isOnValues + // Update `allowsCookies` whenever the toggle is flipped. + perferences.allowsCookies <~ toggle.reactive.isOnValues + + // Compute live character counts from the continuous stream of user initiated + // changes in the text. + textField.reactive.continuousTextValues.map { $0.characters.count } + + // Trigger `commit` whenever the button is pressed. + button.reactive.pressed = CocoaAction(viewModel.commit) ``` + +1. **Declarative Objective-C Dynamism** -1. **Expressive, safe key path observation** - - You may easily obtain a stream of values for a certain key path, without the - need to deal with the obscure KVO API. + Create signals that are sourced by intercepting Objective-C objects, + e.g. method call interception and object deinitialization. + ```swift - let producer = object.reactive.values(forKeyPath: #keyPath(key)) + // Notify after every time `viewWillAppear(_:)` is called. + let appearing = object.reactive.trigger(for: #selector(viewWillAppear(_:))) + + // Observe the lifetime of `object`. + object.reactive.lifetime.ended.observeCompleted(doCleanup) ``` -1. **Object deinitialization** +1. **Expressive, Safe Key Path Observation** - You may couple resources to or compose signal with the `Lifetime` of every - `NSObject`. + Establish key-value observations in the form of `SignalProducer`s and + `DynamicProperty`s, and enjoy the inherited composability. + ```swift - NotificationCenter.default.reactive - .notifications(forName: .MyNotification) + // A producer that sends the current value of `keyPath`, followed by + // subsequent changes. + // + // Terminate the KVO observation if the lifetime of `self` ends. + let producer = object.reactive.values(forKeyPath: #keyPath(key)) .take(during: self.reactive.lifetime) - ``` - -1. **Method call interception** - - You may ask to intercept a particular method, and get notified after it is - called. - ```swift - let appearing = object.reactive.trigger(for: #selector(viewWillAppear(_:))) + + // A parameterized property that represents the supplied key path of the + // wrapped object. It holds a weak reference to the wrapped object. + let property = DynamicProperty(object: person, + keyPath: #keyPath(person.name)) ``` But there are still more to be discovered, and more to be introduced. Read our in-code documentations and release notes to From 5f1693c0ac59e01a6cd42aade8e99c086de7a27b Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Mon, 24 Oct 2016 18:24:17 +0200 Subject: [PATCH 0483/1028] Improved the writing (again). --- README.md | 53 ++++++++++++----------------------------------------- 1 file changed, 12 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 11c3a6a62e..2d39c24d2d 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,17 @@ [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](#carthage) [![CocoaPods compatible](https://img.shields.io/cocoapods/v/ReactiveCocoa.svg)](#cocoapods) [![GitHub release](https://img.shields.io/github/release/ReactiveCocoa/ReactiveCocoa.svg)](https://github.com/ReactiveCocoa/ReactiveCocoa/releases) ![Swift 3.0.x](https://img.shields.io/badge/Swift-3.0.x-orange.svg) ![platforms](https://img.shields.io/badge/platforms-iOS%20%7C%20OS%20X%20%7C%20watchOS%20%7C%20tvOS%20-lightgrey.svg) -[» Looking for the Objective-C API?](#objective-c-swift-and-the-reactivecocoa-family) +[» Looking for the Objective-C API?](https://github.com/ReactiveCocoa/ReactiveObjC) +[» Still using Swift 2.x?](https://github.com/ReactiveCocoa/ReactiveCocoa/tree/v4.0.0) -## Introduction +### What is ReactiveSwift? +__ReactiveSwift__ builds around the grand concept of ___stream of values over time___, and offers composable, declaractive and flexible primitives. These primitives can be used to uniformly represent common Cocoa and generic programming patterns that are fundementally an act of observation. -__ReactiveCocoa__ wraps various aspects of Cocoa frameworks with the declarative primitives from [ReactiveSwift](). Let's go through a few core aspects of ReactiveCocoa: +For more information about the core primitives, see [ReactiveSwift](). + +### What is ReactiveCocoa? + +__ReactiveCocoa__ wraps various aspects of Cocoa frameworks with the declarative [ReactiveSwift]() primitives. 1. **UI Bindings** @@ -47,7 +53,7 @@ __ReactiveCocoa__ wraps various aspects of Cocoa frameworks with the declarative ```swift // Notify after every time `viewWillAppear(_:)` is called. - let appearing = object.reactive.trigger(for: #selector(viewWillAppear(_:))) + let appearing = view.reactive.trigger(for: #selector(viewWillAppear(_:))) // Observe the lifetime of `object`. object.reactive.lifetime.ended.observeCompleted(doCleanup) @@ -75,22 +81,7 @@ __ReactiveCocoa__ wraps various aspects of Cocoa frameworks with the declarative But there are still more to be discovered, and more to be introduced. Read our in-code documentations and release notes to find out more. - -#### On ReactiveSwift -ReactiveSwift is inspired by [functional reactive - programming](https://joshaber.github.io/2013/02/11/input-and-output/). - - -Rather than using mutable variables which are replaced and modified in-place, -ReactiveSwift offers two core primitives, [`Signal`][Signals] and [`SignalProducer`][Signal producers], that derive from the grand concept of ___stream of values over time___. - -It is flexible enough to uniformly represent generic patterns and common Cocoa patterns that are fundementally an act of observation. e.g. _Callbacks_, _Futures_, _Promises_, _Notifications_, _Target-Action_, _Key Value Observing_ (KVO), etc. - -With states all being represented reactively, it is easy to declaratively chain and compose the streams of values together, with less spaghetti code and state to bridge the gap. - -For more information about the core primitives, see [ReactiveSwift](). - -## Getting started +### Getting started ReactiveCocoa supports macOS 10.9+, iOS 8.0+, watchOS 2.0+, and tvOS 9.0+. @@ -130,29 +121,9 @@ pod 'ReactiveCocoa', :git => 'https://github.com/ReactiveCocoa/ReactiveCocoa.git 1. If your application target does not contain Swift code at all, you should also set the `EMBEDDED_CONTENT_CONTAINS_SWIFT` build setting to “Yes”. - -## Objective-C, Swift and the ReactiveCocoa family - -Starting with [version 5.0][CHANGELOG], ReactiveCocoa primarily focuses on providing reactive extensions to Cocoa frameworks in Swift. In other words: - -1. The core Swift API - which provides FRP inspired primitives - had been spun off as [ReactiveSwift][]. - -2. The legacy Objective-C API had been spun off as [ReactiveObjC][]. - -3. The bridge between the core Swift API and the legacy Objective-C API is now hosted in ([ReactiveObjCBridge][]), which is mostly meant as a compatibility layer for older ReactiveCocoa projects. - -The Objective-C API will continue to be maintained and supported in the foreseeable future. But given the maturity of the framework and the language, non-trivial changes are not expected. Please visit [ReactiveObjC][] for more information. - -**We highly recommend all Swift projects using the Swift API.** - -## Have a question? +### Have a question? If you need any help, please visit our [GitHub issues](https://github.com/ReactiveCocoa/ReactiveCocoa/issues?q=is%3Aissue+label%3Aquestion+) or [Stack Overflow](http://stackoverflow.com/questions/tagged/reactive-cocoa). Feel free to file an issue if you do not manage to find any solution from the archives. -## Using Swift 2.x? -See [ReactiveCocoa -4.x](https://github.com/ReactiveCocoa/ReactiveCocoa/tree/v4.0.0) for legacy Swift support. - - [ReactiveSwift]: https://github.com/ReactiveCocoa/ReactiveSwift [ReactiveObjC]: https://github.com/ReactiveCocoa/ReactiveObjC [ReactiveObjCBridge]: https://github.com/ReactiveCocoa/ReactiveObjCBridge From 116655f36272536739b4a9e9fee914d4e5e4d8cc Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Mon, 24 Oct 2016 18:28:41 +0200 Subject: [PATCH 0484/1028] A tiny bit of rewording in the readme. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2d39c24d2d..2656dc58ea 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [» Still using Swift 2.x?](https://github.com/ReactiveCocoa/ReactiveCocoa/tree/v4.0.0) ### What is ReactiveSwift? -__ReactiveSwift__ builds around the grand concept of ___stream of values over time___, and offers composable, declaractive and flexible primitives. These primitives can be used to uniformly represent common Cocoa and generic programming patterns that are fundementally an act of observation. +__ReactiveSwift__ offers composable, declaractive and flexible primitives that are built around the grand concept of ___streams of values over time___. These primitives can be used to uniformly represent common Cocoa and generic programming patterns that are fundementally an act of observation. For more information about the core primitives, see [ReactiveSwift](). From 2771306e8ce269317700e0462718507827be0ae5 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Mon, 24 Oct 2016 18:50:28 +0200 Subject: [PATCH 0485/1028] Reorganized the links. Promoted all h3 to h2. --- README.md | 54 +++++++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 2656dc58ea..a44f09061e 100644 --- a/README.md +++ b/README.md @@ -3,21 +3,20 @@ [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](#carthage) [![CocoaPods compatible](https://img.shields.io/cocoapods/v/ReactiveCocoa.svg)](#cocoapods) [![GitHub release](https://img.shields.io/github/release/ReactiveCocoa/ReactiveCocoa.svg)](https://github.com/ReactiveCocoa/ReactiveCocoa/releases) ![Swift 3.0.x](https://img.shields.io/badge/Swift-3.0.x-orange.svg) ![platforms](https://img.shields.io/badge/platforms-iOS%20%7C%20OS%20X%20%7C%20watchOS%20%7C%20tvOS%20-lightgrey.svg) -[» Looking for the Objective-C API?](https://github.com/ReactiveCocoa/ReactiveObjC) -[» Still using Swift 2.x?](https://github.com/ReactiveCocoa/ReactiveCocoa/tree/v4.0.0) +[» Looking for the Objective-C API?][] [» Still using Swift 2.x?][] -### What is ReactiveSwift? +## What is ReactiveSwift? __ReactiveSwift__ offers composable, declaractive and flexible primitives that are built around the grand concept of ___streams of values over time___. These primitives can be used to uniformly represent common Cocoa and generic programming patterns that are fundementally an act of observation. -For more information about the core primitives, see [ReactiveSwift](). +For more information about the core primitives, see [ReactiveSwift][]. -### What is ReactiveCocoa? +## What is ReactiveCocoa? -__ReactiveCocoa__ wraps various aspects of Cocoa frameworks with the declarative [ReactiveSwift]() primitives. +__ReactiveCocoa__ wraps various aspects of Cocoa frameworks with the declarative [ReactiveSwift][] primitives. 1. **UI Bindings** - UI components exposes `BindingTarget`s, which accept bindings from any + UI components exposes [`BindingTarget`][]s, which accept bindings from any kind of streams of values via the `<~` operator. ```swift @@ -27,11 +26,11 @@ __ReactiveCocoa__ wraps various aspects of Cocoa frameworks with the declarative 1. **Controls and User Interactions** - Interactive UI components expose `Signal`s for control events + Interactive UI components expose [`Signal`][]s for control events and updates in the control value upon user interactions. A selected set of controls provide a convenience, expressive binding - API for `Action`s. + API for [`Action`][]s. ```swift @@ -61,7 +60,7 @@ __ReactiveCocoa__ wraps various aspects of Cocoa frameworks with the declarative 1. **Expressive, Safe Key Path Observation** - Establish key-value observations in the form of `SignalProducer`s and + Establish key-value observations in the form of [`SignalProducer`][]s and `DynamicProperty`s, and enjoy the inherited composability. ```swift @@ -81,13 +80,13 @@ __ReactiveCocoa__ wraps various aspects of Cocoa frameworks with the declarative But there are still more to be discovered, and more to be introduced. Read our in-code documentations and release notes to find out more. -### Getting started +## Getting started ReactiveCocoa supports macOS 10.9+, iOS 8.0+, watchOS 2.0+, and tvOS 9.0+. #### Carthage -If you are using [Carthage](https://github.com/Carthage/Carthage) to manage your dependency, simply add +If you are using [Carthage][] to manage your dependency, simply add ReactiveCocoa to your `Cartfile`: ``` @@ -98,7 +97,7 @@ If you use Carthage to build your dependencies, make sure you have added `Reacti #### CocoaPods -If you are using [CocoaPods](https://cocoapods.org/) to manage your dependency, simply add +If you are using [CocoaPods][] to manage your dependency, simply add ReactiveCocoa to your `Podfile`: ``` @@ -107,8 +106,7 @@ pod 'ReactiveCocoa', :git => 'https://github.com/ReactiveCocoa/ReactiveCocoa.git #### Git submodule - 1. Add the ReactiveCocoa repository as a - [submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules) of your + 1. Add the ReactiveCocoa repository as a [submodule][] of your application’s repository. 1. Run `git submodule update --init --recursive` from within the ReactiveCocoa folder. 1. Drag and drop `ReactiveCocoa.xcodeproj`, @@ -121,19 +119,21 @@ pod 'ReactiveCocoa', :git => 'https://github.com/ReactiveCocoa/ReactiveCocoa.git 1. If your application target does not contain Swift code at all, you should also set the `EMBEDDED_CONTENT_CONTAINS_SWIFT` build setting to “Yes”. -### Have a question? -If you need any help, please visit our [GitHub issues](https://github.com/ReactiveCocoa/ReactiveCocoa/issues?q=is%3Aissue+label%3Aquestion+) or [Stack Overflow](http://stackoverflow.com/questions/tagged/reactive-cocoa). Feel free to file an issue if you do not manage to find any solution from the archives. +## Have a question? +If you need any help, please visit our [GitHub issues][] or [Stack Overflow][]. Feel free to file an issue if you do not manage to find any solution from the archives. + [ReactiveSwift]: https://github.com/ReactiveCocoa/ReactiveSwift [ReactiveObjC]: https://github.com/ReactiveCocoa/ReactiveObjC -[ReactiveObjCBridge]: https://github.com/ReactiveCocoa/ReactiveObjCBridge -[Actions]: Documentation/FrameworkOverview.md#actions -[Basic Operators]: Documentation/BasicOperators.md +[GitHub issues]: https://github.com/ReactiveCocoa/ReactiveCocoa/issues?q=is%3Aissue+label%3Aquestion+ +[Stack Overflow]: http://stackoverflow.com/questions/tagged/reactive-cocoa [CHANGELOG]: CHANGELOG.md -[Code]: ReactiveCocoa -[Documentation]: Documentation -[Framework Overview]: Documentation/FrameworkOverview.md -[Legacy Documentation]: https://github.com/ReactiveCocoa/ReactiveObjC/blob/master/Documentation/ -[Signal producers]: Documentation/FrameworkOverview.md#signal-producers -[Signals]: Documentation/FrameworkOverview.md#signals -[Swift API]: ReactiveCocoa/Swift +[Carthage]: https://github.com/Carthage/Carthage +[CocoaPods]: https://cocoapods.org/ +[submodule]: https://git-scm.com/book/en/v2/Git-Tools-Submodules +[» Looking for the Objective-C API?]: https://github.com/ReactiveCocoa/ReactiveObjC +[» Still using Swift 2.x?]: https://github.com/ReactiveCocoa/ReactiveCocoa/tree/v4.0.0 +[`Signal`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#signals +[`SignalProducer`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#signal-producers +[`Action`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#actions +[`BindingTarget`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#binding-target From 47cb9665ce22b6266672de8e5c0d0b99cf6a2f9b Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Mon, 24 Oct 2016 18:55:36 +0200 Subject: [PATCH 0486/1028] =?UTF-8?q?Replace=20»=20with=20=E2=9A=A0?= =?UTF-8?q?=EF=B8=8F.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a44f09061e..a0530ffd93 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](#carthage) [![CocoaPods compatible](https://img.shields.io/cocoapods/v/ReactiveCocoa.svg)](#cocoapods) [![GitHub release](https://img.shields.io/github/release/ReactiveCocoa/ReactiveCocoa.svg)](https://github.com/ReactiveCocoa/ReactiveCocoa/releases) ![Swift 3.0.x](https://img.shields.io/badge/Swift-3.0.x-orange.svg) ![platforms](https://img.shields.io/badge/platforms-iOS%20%7C%20OS%20X%20%7C%20watchOS%20%7C%20tvOS%20-lightgrey.svg) -[» Looking for the Objective-C API?][] [» Still using Swift 2.x?][] +⚠️ [Looking for the Objective-C API?][] ⚠️ [Still using Swift 2.x?][] ## What is ReactiveSwift? __ReactiveSwift__ offers composable, declaractive and flexible primitives that are built around the grand concept of ___streams of values over time___. These primitives can be used to uniformly represent common Cocoa and generic programming patterns that are fundementally an act of observation. @@ -131,8 +131,8 @@ If you need any help, please visit our [GitHub issues][] or [Stack Overflow][]. [Carthage]: https://github.com/Carthage/Carthage [CocoaPods]: https://cocoapods.org/ [submodule]: https://git-scm.com/book/en/v2/Git-Tools-Submodules -[» Looking for the Objective-C API?]: https://github.com/ReactiveCocoa/ReactiveObjC -[» Still using Swift 2.x?]: https://github.com/ReactiveCocoa/ReactiveCocoa/tree/v4.0.0 +[Looking for the Objective-C API?]: https://github.com/ReactiveCocoa/ReactiveObjC +[Still using Swift 2.x?]: https://github.com/ReactiveCocoa/ReactiveCocoa/tree/v4.0.0 [`Signal`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#signals [`SignalProducer`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#signal-producers [`Action`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#actions From 6bd7dd86bb32f3cf27c1e895f55baedc06cdcdca Mon Sep 17 00:00:00 2001 From: Christopher Liscio Date: Tue, 25 Oct 2016 13:21:37 -0400 Subject: [PATCH 0487/1028] Fixed up some language, added a tiny bit more --- Documentation/RAC5MigrationGuide.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/Documentation/RAC5MigrationGuide.md b/Documentation/RAC5MigrationGuide.md index 739305aea9..0cd724b230 100644 --- a/Documentation/RAC5MigrationGuide.md +++ b/Documentation/RAC5MigrationGuide.md @@ -1,4 +1,4 @@ -# RAC 5.0 Migration Guide +# ReactiveCocoa 5.0 Migration Guide * [Repository Split](#repository-split) * [ReactiveCocoa](#reactivecocoa) @@ -13,13 +13,13 @@ * [Atomic](#atomic) ## Repository Split -As part of RAC 5.0, ReactiveCocoa has been split into multiple repositories. _The rationale for this change is explained below._ +In version 5.0, we split ReactiveCocoa into multiple repositories for reasons explained in the sections below. The following should help you get started with choosing the repositories you require: **If you’re using only the Swift APIs**, you can continue to include ReactiveCocoa. You will also need to link against [ReactiveSwift][], which is now a dependency of ReactiveCocoa. **If you’re using only the Objective-C APIs**, you can switch to using [ReactiveObjC][]. It has all the Obj-C code from RAC 2. -**If you’re using both the Swift and Objective-C APIs**, you will most likely want to use both ReactiveCocoa and [ReactiveObjCBridge][], which depend on [ReactiveSwift][] and [ReactiveObjC][]. +**If you’re using both the Swift and Objective-C APIs**, you likely require both ReactiveCocoa and [ReactiveObjCBridge][], which depend on [ReactiveSwift][] and [ReactiveObjC][]. ### ReactiveCocoa The ReactiveCocoa library is newly focused on Swift and the UI layers of Apple’s platforms, building on the work of [Rex](https://github.com/neilpa/Rex). @@ -34,7 +34,7 @@ As Swift continues to grow as a language and a platform, we hope that it will ex [ReactiveSwift]: https://github.com/ReactiveCocoa/ReactiveSwift ### ReactiveObjC -RAC 3/4 included the Objective-C code from RAC 2. That code has been moved to [ReactiveObjC][] because: +The 3.x and 4.x releases of ReactiveCocoa included the Objective-C code from ReactiveCocoa 2.x. That code has been moved to [ReactiveObjC][] because: 1. It’s independent of the Swift code 2. It has a separate user base @@ -45,15 +45,22 @@ We hope that this move will enable continued support of ReactiveObjC. [ReactiveObjC]: https://github.com/ReactiveCocoa/ReactiveObjC ### ReactiveObjCBridge -Moving the Swift and Objective-C APIs to separate repositories meant that a new home was need for the bridging layer between the two. +Moving the Swift and Objective-C APIs to separate repositories meant that a new home was needed for the bridging layer between the two. -This value will remain an important tool for anyone who’s heavily invested in the Objective-C RAC APIs as they (hopefully) move gradually to Swift. +This bridge is an important tool for users that are working in mixed-language code bases. Whether you are slowly adding Swift to a mature product built with the ReactiveCocoa Objective-C APIs, or looking to adopt ReactiveCocoa in a mixed code base, the bridge is required to communicate between Swift and Objective-C code. [ReactiveObjCBridge]: https://github.com/ReactiveCocoa/ReactiveObjCBridge ## API Names +We mostly adjusted the ReactiveCocoa API to follow the [Swift 3 API Design Guidelines](https://swift.org/blog/swift-3-api-design/), or to match the Cocoa and Foundation API changes that came with Swift 3 and the latest platform SDKs. + +Lots has changed, but if you're already migrating to Swift 3 then that should not come as a surprise. Fortunately for you, we've provided annotations in the source that should help you while using the Swift 3 migration tool that ships with Xcode 8. When changes aren't picked up by the migrator, they are often provided for you as Fix-Its. + +**Tip:** You can apply all the suggested fix-its in the current scope by choosing Editor > Fix All In Scope from the main menu in Xcode, or by using the associated keyboard shortcut. + ## Signal + ### Lifetime Semantics ## SignalProducer From 5a91a19088002afb92466240d73defc5ec40e922 Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 25 Oct 2016 22:33:26 +0200 Subject: [PATCH 0488/1028] Fixed a bug which caused a deadlock in `trigger(for:)`. --- ReactiveCocoa/NSObject+Intercepting.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 2959551bc3..26aa072a5c 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -12,8 +12,8 @@ extension Reactive where Base: NSObject { /// - returns: /// A trigger signal. public func trigger(for selector: Selector) -> Signal<(), NoError> { - objc_sync_enter(self) - defer { objc_sync_exit(self) } + objc_sync_enter(base) + defer { objc_sync_exit(base) } let map = associatedValue { _ in NSMutableDictionary() } From 320c6daf5e6a0c6c7c4d5eb47d35022cd41632d3 Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 25 Oct 2016 23:02:50 +0200 Subject: [PATCH 0489/1028] Added test cases for deadlocking in InterceptingSpec and LifetimeSpec. --- ReactiveCocoa.xcodeproj/project.pbxproj | 8 +++++ ReactiveCocoaTests/InterceptingSpec.swift | 18 ++++++++++ ReactiveCocoaTests/LifetimeSpec.swift | 43 +++++++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 ReactiveCocoaTests/LifetimeSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 3e62580322..fd34631fc2 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -123,6 +123,9 @@ 9ADE4A931DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADE4A901DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift */; }; 9ADE4A941DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADE4A901DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift */; }; 9ADE4A961DA6F018005C2AC8 /* NSTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADE4A951DA6F018005C2AC8 /* NSTextField.swift */; }; + 9ADFE5A11DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */; }; + 9ADFE5A21DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */; }; + 9ADFE5A31DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */; }; 9AF0EA751D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; 9AF0EA761D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; 9AF0EA771D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; @@ -280,6 +283,7 @@ 9ADE4A8D1DA6D965005C2AC8 /* NSControlSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NSControlSpec.swift; path = AppKit/NSControlSpec.swift; sourceTree = ""; }; 9ADE4A901DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+ReactiveExtensionsProvider.swift"; sourceTree = ""; }; 9ADE4A951DA6F018005C2AC8 /* NSTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSTextField.swift; sourceTree = ""; }; + 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LifetimeSpec.swift; sourceTree = ""; }; 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+BindingTarget.swift"; sourceTree = ""; }; A97451331B3A935E00F48E55 /* watchOS-Application.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Application.xcconfig"; sourceTree = ""; }; A97451341B3A935E00F48E55 /* watchOS-Base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Base.xcconfig"; sourceTree = ""; }; @@ -570,6 +574,7 @@ B696FB801A7640C00075236D /* TestError.swift */, 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */, 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */, + 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */, D04725FA19E49ED7006002AA /* Supporting Files */, ); path = ReactiveCocoaTests; @@ -992,6 +997,7 @@ 9A1D06471D93EA7E00ACF44C /* UILabelSpec.swift in Sources */, 9A1D06391D93EA7E00ACF44C /* UIBarButtonItemSpec.swift in Sources */, 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */, + 9ADFE5A31DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */, 9A1D06411D93EA7E00ACF44C /* UIControlSpec.swift in Sources */, 9A6AAA101DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */, 9A1D06491D93EA7E00ACF44C /* UIProgressViewSpec.swift in Sources */, @@ -1044,6 +1050,7 @@ 9A6AAA0E1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */, 9ADE4A8F1DA6DA20005C2AC8 /* NSControlSpec.swift in Sources */, D0A2260E1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */, + 9ADFE5A11DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */, B696FB811A7640C00075236D /* TestError.swift in Sources */, BFA6B94D1A7604D400C846D1 /* SignalProducerNimbleMatchers.swift in Sources */, 9A6AAA2B1DB8F85C0013AAEA /* ReusableComponentsSpec.swift in Sources */, @@ -1102,6 +1109,7 @@ 9A1D06361D93EA7E00ACF44C /* UIActivityIndicatorViewSpec.swift in Sources */, 9A1D06461D93EA7E00ACF44C /* UILabelSpec.swift in Sources */, 9A9DFEEA1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */, + 9ADFE5A21DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */, 9A1D06421D93EA7E00ACF44C /* UIDatePickerSpec.swift in Sources */, 9A6AAA0F1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */, 9A1D06381D93EA7E00ACF44C /* UIBarButtonItemSpec.swift in Sources */, diff --git a/ReactiveCocoaTests/InterceptingSpec.swift b/ReactiveCocoaTests/InterceptingSpec.swift index 324ec89e0d..22af27dfc8 100644 --- a/ReactiveCocoaTests/InterceptingSpec.swift +++ b/ReactiveCocoaTests/InterceptingSpec.swift @@ -71,6 +71,24 @@ class InterceptingSpec: QuickSpec { expect(counter1) == 2 expect(counter2) == 2 } + + it("should not deadlock") { + for _ in 1 ... 10 { + var isDeadlock = true + + DispatchQueue.global(priority: .high).async { + _ = object.reactive.trigger(for: #selector(object.increment)) + + DispatchQueue.global(priority: .high).async { + _ = object.reactive.trigger(for: #selector(object.increment)) + + isDeadlock = false + } + } + + expect(isDeadlock).toEventually(beFalsy()) + } + } } } } diff --git a/ReactiveCocoaTests/LifetimeSpec.swift b/ReactiveCocoaTests/LifetimeSpec.swift new file mode 100644 index 0000000000..b085bd2472 --- /dev/null +++ b/ReactiveCocoaTests/LifetimeSpec.swift @@ -0,0 +1,43 @@ +import Foundation +import ReactiveCocoa +import ReactiveSwift +import enum Result.NoError +import Quick +import Nimble + +class LifetimeSpec: QuickSpec { + override func spec() { + describe("NSObject.reactive.lifetime") { + var object: NSObject! + weak var _object: NSObject? + + beforeEach { + object = NSObject() + _object = object + } + + afterEach { + object = nil + expect(_object).to(beNil()) + } + + it("should not deadlock") { + for _ in 1 ... 10 { + var isDeadlock = true + + DispatchQueue.global(priority: .high).async { + _ = object.reactive.lifetime + + DispatchQueue.global(priority: .high).async { + _ = object.reactive.lifetime + + isDeadlock = false + } + } + + expect(isDeadlock).toEventually(beFalsy()) + } + } + } + } +} From acf6b7052950add4dc5655ce95122950b0f2ce43 Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 25 Oct 2016 23:07:39 +0200 Subject: [PATCH 0490/1028] Introduced a constrained, internal wrapper of `objc_sync_*`. --- ReactiveCocoa.xcodeproj/project.pbxproj | 10 ++++++++ ReactiveCocoa/NSObject+Intercepting.swift | 31 +++++++++++------------ ReactiveCocoa/NSObject+Lifetime.swift | 21 ++++++++------- ReactiveCocoa/NSObject+Synchronize.swift | 7 +++++ 4 files changed, 42 insertions(+), 27 deletions(-) create mode 100644 ReactiveCocoa/NSObject+Synchronize.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index fd34631fc2..a3eb1db1bf 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -126,6 +126,10 @@ 9ADFE5A11DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */; }; 9ADFE5A21DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */; }; 9ADFE5A31DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */; }; + 9ADFE5A51DC0001C001E11F7 /* NSObject+Synchronize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronize.swift */; }; + 9ADFE5A61DC0001C001E11F7 /* NSObject+Synchronize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronize.swift */; }; + 9ADFE5A71DC0001C001E11F7 /* NSObject+Synchronize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronize.swift */; }; + 9ADFE5A81DC0001C001E11F7 /* NSObject+Synchronize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronize.swift */; }; 9AF0EA751D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; 9AF0EA761D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; 9AF0EA771D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; @@ -284,6 +288,7 @@ 9ADE4A901DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+ReactiveExtensionsProvider.swift"; sourceTree = ""; }; 9ADE4A951DA6F018005C2AC8 /* NSTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSTextField.swift; sourceTree = ""; }; 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LifetimeSpec.swift; sourceTree = ""; }; + 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Synchronize.swift"; sourceTree = ""; }; 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+BindingTarget.swift"; sourceTree = ""; }; A97451331B3A935E00F48E55 /* watchOS-Application.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Application.xcconfig"; sourceTree = ""; }; A97451341B3A935E00F48E55 /* watchOS-Base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Base.xcconfig"; sourceTree = ""; }; @@ -546,6 +551,7 @@ 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */, 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */, 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */, + 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronize.swift */, 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */, 9A1D05E91D93E9F100ACF44C /* AppKit */, 9A1D05EB1D93E9F100ACF44C /* UIKit */, @@ -969,6 +975,7 @@ 9AF0EA781D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, 9A1D06191D93EA0100ACF44C /* UILabel.swift in Sources */, 4A0E11021D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, + 9ADFE5A81DC0001C001E11F7 /* NSObject+Synchronize.swift in Sources */, CD0C45E11CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9A1D061A1D93EA0100ACF44C /* UIProgressView.swift in Sources */, 9A2E42611DAA6737006D909F /* CocoaTarget.swift in Sources */, @@ -1014,6 +1021,7 @@ 9A9DFEE51DA7B5500039EE1B /* NSObject+Intercepting.swift in Sources */, 9A1D06791D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9ADE4A931DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, + 9ADFE5A71DC0001C001E11F7 /* NSObject+Synchronize.swift in Sources */, 9ADE4A7E1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, CD0C45E01CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9AF0EA771D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, @@ -1032,6 +1040,7 @@ 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, 9A1D05E01D93E99100ACF44C /* NSObject+Association.swift in Sources */, 9A6AAA261DB8F5280013AAEA /* ReusableComponents.swift in Sources */, + 9ADFE5A51DC0001C001E11F7 /* NSObject+Synchronize.swift in Sources */, 9ADE4A7C1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, 9ADE4A961DA6F018005C2AC8 /* NSTextField.swift in Sources */, 9A2E425E1DAA6737006D909F /* CocoaTarget.swift in Sources */, @@ -1083,6 +1092,7 @@ 9A1D06071D93EA0000ACF44C /* UILabel.swift in Sources */, 4A0E11001D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, 9ADE4A921DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, + 9ADFE5A61DC0001C001E11F7 /* NSObject+Synchronize.swift in Sources */, 9AD0F06B1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, 9A1D06081D93EA0000ACF44C /* UIProgressView.swift in Sources */, 9A1D06001D93EA0000ACF44C /* UIBarButtonItem.swift in Sources */, diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 26aa072a5c..c3b0464e7a 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -12,25 +12,24 @@ extension Reactive where Base: NSObject { /// - returns: /// A trigger signal. public func trigger(for selector: Selector) -> Signal<(), NoError> { - objc_sync_enter(base) - defer { objc_sync_exit(base) } + return base.synchronize { + let map = associatedValue { _ in NSMutableDictionary() } - let map = associatedValue { _ in NSMutableDictionary() } + let selectorName = String(describing: selector) as NSString + if let signal = map.object(forKey: selectorName) as! Signal<(), NoError>? { + return signal + } - let selectorName = String(describing: selector) as NSString - if let signal = map.object(forKey: selectorName) as! Signal<(), NoError>? { - return signal - } - - let (signal, observer) = Signal<(), NoError>.pipe() - let isSuccessful = base._rac_setupInvocationObservation(for: selector, - protocol: nil, - receiver: observer.send(value:)) - precondition(isSuccessful) + let (signal, observer) = Signal<(), NoError>.pipe() + let isSuccessful = base._rac_setupInvocationObservation(for: selector, + protocol: nil, + receiver: observer.send(value:)) + precondition(isSuccessful) - lifetime.ended.observeCompleted(observer.sendCompleted) - map.setObject(signal, forKey: selectorName) + lifetime.ended.observeCompleted(observer.sendCompleted) + map.setObject(signal, forKey: selectorName) - return signal + return signal + } } } diff --git a/ReactiveCocoa/NSObject+Lifetime.swift b/ReactiveCocoa/NSObject+Lifetime.swift index d4c2a07362..a88c7bbfc6 100644 --- a/ReactiveCocoa/NSObject+Lifetime.swift +++ b/ReactiveCocoa/NSObject+Lifetime.swift @@ -7,19 +7,18 @@ private var lifetimeTokenKey: UInt8 = 0 extension Reactive where Base: NSObject { /// Returns a lifetime that ends when the object is deallocated. @nonobjc public var lifetime: Lifetime { - objc_sync_enter(base) - defer { objc_sync_exit(base) } + return base.synchronize { + if let lifetime = objc_getAssociatedObject(base, &lifetimeKey) as! Lifetime? { + return lifetime + } - if let lifetime = objc_getAssociatedObject(base, &lifetimeKey) as! Lifetime? { - return lifetime - } - - let token = Lifetime.Token() - let lifetime = Lifetime(token) + let token = Lifetime.Token() + let lifetime = Lifetime(token) - objc_setAssociatedObject(base, &lifetimeTokenKey, token, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - objc_setAssociatedObject(base, &lifetimeKey, lifetime, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + objc_setAssociatedObject(base, &lifetimeTokenKey, token, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + objc_setAssociatedObject(base, &lifetimeKey, lifetime, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - return lifetime + return lifetime + } } } diff --git a/ReactiveCocoa/NSObject+Synchronize.swift b/ReactiveCocoa/NSObject+Synchronize.swift new file mode 100644 index 0000000000..af36d55be4 --- /dev/null +++ b/ReactiveCocoa/NSObject+Synchronize.swift @@ -0,0 +1,7 @@ +extension NSObject { + internal func synchronize(execute: () throws -> Result) rethrows -> Result { + objc_sync_enter(self) + defer { objc_sync_exit(self) } + return try execute() + } +} From 30577883d0675d2ca36080656358f9db7b513a75 Mon Sep 17 00:00:00 2001 From: Anders Date: Wed, 26 Oct 2016 00:59:54 +0200 Subject: [PATCH 0491/1028] Appended the `d` at the end of `NSObject.synchronized`. --- ReactiveCocoa/NSObject+Intercepting.swift | 2 +- ReactiveCocoa/NSObject+Lifetime.swift | 2 +- ReactiveCocoa/NSObject+Synchronize.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index c3b0464e7a..dd63bb8f48 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -12,7 +12,7 @@ extension Reactive where Base: NSObject { /// - returns: /// A trigger signal. public func trigger(for selector: Selector) -> Signal<(), NoError> { - return base.synchronize { + return base.synchronized { let map = associatedValue { _ in NSMutableDictionary() } let selectorName = String(describing: selector) as NSString diff --git a/ReactiveCocoa/NSObject+Lifetime.swift b/ReactiveCocoa/NSObject+Lifetime.swift index a88c7bbfc6..b9f739a4c4 100644 --- a/ReactiveCocoa/NSObject+Lifetime.swift +++ b/ReactiveCocoa/NSObject+Lifetime.swift @@ -7,7 +7,7 @@ private var lifetimeTokenKey: UInt8 = 0 extension Reactive where Base: NSObject { /// Returns a lifetime that ends when the object is deallocated. @nonobjc public var lifetime: Lifetime { - return base.synchronize { + return base.synchronized { if let lifetime = objc_getAssociatedObject(base, &lifetimeKey) as! Lifetime? { return lifetime } diff --git a/ReactiveCocoa/NSObject+Synchronize.swift b/ReactiveCocoa/NSObject+Synchronize.swift index af36d55be4..97c8fde417 100644 --- a/ReactiveCocoa/NSObject+Synchronize.swift +++ b/ReactiveCocoa/NSObject+Synchronize.swift @@ -1,5 +1,5 @@ extension NSObject { - internal func synchronize(execute: () throws -> Result) rethrows -> Result { + internal func synchronized(execute: () throws -> Result) rethrows -> Result { objc_sync_enter(self) defer { objc_sync_exit(self) } return try execute() From 7c1c5d033dd365d2c6412283034b165fd6c1167a Mon Sep 17 00:00:00 2001 From: Anders Date: Wed, 26 Oct 2016 13:19:41 +0200 Subject: [PATCH 0492/1028] Marked `NSObject.synchronized` as final and non-ObjC. Renamed the containing file. --- ReactiveCocoa.xcodeproj/project.pbxproj | 20 +++++++++---------- ...ize.swift => NSObject+Synchronizing.swift} | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) rename ReactiveCocoa/{NSObject+Synchronize.swift => NSObject+Synchronizing.swift} (50%) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index a3eb1db1bf..c32060296c 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -126,10 +126,10 @@ 9ADFE5A11DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */; }; 9ADFE5A21DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */; }; 9ADFE5A31DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */; }; - 9ADFE5A51DC0001C001E11F7 /* NSObject+Synchronize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronize.swift */; }; - 9ADFE5A61DC0001C001E11F7 /* NSObject+Synchronize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronize.swift */; }; - 9ADFE5A71DC0001C001E11F7 /* NSObject+Synchronize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronize.swift */; }; - 9ADFE5A81DC0001C001E11F7 /* NSObject+Synchronize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronize.swift */; }; + 9ADFE5A51DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronizing.swift */; }; + 9ADFE5A61DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronizing.swift */; }; + 9ADFE5A71DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronizing.swift */; }; + 9ADFE5A81DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronizing.swift */; }; 9AF0EA751D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; 9AF0EA761D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; 9AF0EA771D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; @@ -288,7 +288,7 @@ 9ADE4A901DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+ReactiveExtensionsProvider.swift"; sourceTree = ""; }; 9ADE4A951DA6F018005C2AC8 /* NSTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSTextField.swift; sourceTree = ""; }; 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LifetimeSpec.swift; sourceTree = ""; }; - 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Synchronize.swift"; sourceTree = ""; }; + 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronizing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Synchronizing.swift"; sourceTree = ""; }; 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+BindingTarget.swift"; sourceTree = ""; }; A97451331B3A935E00F48E55 /* watchOS-Application.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Application.xcconfig"; sourceTree = ""; }; A97451341B3A935E00F48E55 /* watchOS-Base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Base.xcconfig"; sourceTree = ""; }; @@ -551,7 +551,7 @@ 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */, 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */, 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */, - 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronize.swift */, + 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronizing.swift */, 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */, 9A1D05E91D93E9F100ACF44C /* AppKit */, 9A1D05EB1D93E9F100ACF44C /* UIKit */, @@ -975,7 +975,7 @@ 9AF0EA781D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, 9A1D06191D93EA0100ACF44C /* UILabel.swift in Sources */, 4A0E11021D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, - 9ADFE5A81DC0001C001E11F7 /* NSObject+Synchronize.swift in Sources */, + 9ADFE5A81DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */, CD0C45E11CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9A1D061A1D93EA0100ACF44C /* UIProgressView.swift in Sources */, 9A2E42611DAA6737006D909F /* CocoaTarget.swift in Sources */, @@ -1021,7 +1021,7 @@ 9A9DFEE51DA7B5500039EE1B /* NSObject+Intercepting.swift in Sources */, 9A1D06791D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9ADE4A931DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, - 9ADFE5A71DC0001C001E11F7 /* NSObject+Synchronize.swift in Sources */, + 9ADFE5A71DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */, 9ADE4A7E1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, CD0C45E01CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9AF0EA771D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, @@ -1040,7 +1040,7 @@ 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, 9A1D05E01D93E99100ACF44C /* NSObject+Association.swift in Sources */, 9A6AAA261DB8F5280013AAEA /* ReusableComponents.swift in Sources */, - 9ADFE5A51DC0001C001E11F7 /* NSObject+Synchronize.swift in Sources */, + 9ADFE5A51DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */, 9ADE4A7C1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, 9ADE4A961DA6F018005C2AC8 /* NSTextField.swift in Sources */, 9A2E425E1DAA6737006D909F /* CocoaTarget.swift in Sources */, @@ -1092,7 +1092,7 @@ 9A1D06071D93EA0000ACF44C /* UILabel.swift in Sources */, 4A0E11001D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, 9ADE4A921DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, - 9ADFE5A61DC0001C001E11F7 /* NSObject+Synchronize.swift in Sources */, + 9ADFE5A61DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */, 9AD0F06B1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, 9A1D06081D93EA0000ACF44C /* UIProgressView.swift in Sources */, 9A1D06001D93EA0000ACF44C /* UIBarButtonItem.swift in Sources */, diff --git a/ReactiveCocoa/NSObject+Synchronize.swift b/ReactiveCocoa/NSObject+Synchronizing.swift similarity index 50% rename from ReactiveCocoa/NSObject+Synchronize.swift rename to ReactiveCocoa/NSObject+Synchronizing.swift index 97c8fde417..2b5d5a6036 100644 --- a/ReactiveCocoa/NSObject+Synchronize.swift +++ b/ReactiveCocoa/NSObject+Synchronizing.swift @@ -1,5 +1,5 @@ extension NSObject { - internal func synchronized(execute: () throws -> Result) rethrows -> Result { + @nonobjc internal final func synchronized(execute: () throws -> Result) rethrows -> Result { objc_sync_enter(self) defer { objc_sync_exit(self) } return try execute() From a09b14f7259ae81ef84d052cdfb31ec036a418dd Mon Sep 17 00:00:00 2001 From: Anders Date: Wed, 26 Oct 2016 13:21:13 +0200 Subject: [PATCH 0493/1028] Fixed the typos in the new test cases for deadlocking. --- ReactiveCocoaTests/InterceptingSpec.swift | 6 +++--- ReactiveCocoaTests/LifetimeSpec.swift | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ReactiveCocoaTests/InterceptingSpec.swift b/ReactiveCocoaTests/InterceptingSpec.swift index 22af27dfc8..fb917fbd36 100644 --- a/ReactiveCocoaTests/InterceptingSpec.swift +++ b/ReactiveCocoaTests/InterceptingSpec.swift @@ -74,7 +74,7 @@ class InterceptingSpec: QuickSpec { it("should not deadlock") { for _ in 1 ... 10 { - var isDeadlock = true + var isDeadlocked = true DispatchQueue.global(priority: .high).async { _ = object.reactive.trigger(for: #selector(object.increment)) @@ -82,11 +82,11 @@ class InterceptingSpec: QuickSpec { DispatchQueue.global(priority: .high).async { _ = object.reactive.trigger(for: #selector(object.increment)) - isDeadlock = false + isDeadlocked = false } } - expect(isDeadlock).toEventually(beFalsy()) + expect(isDeadlocked).toEventually(beFalsy()) } } } diff --git a/ReactiveCocoaTests/LifetimeSpec.swift b/ReactiveCocoaTests/LifetimeSpec.swift index b085bd2472..0d17d366ae 100644 --- a/ReactiveCocoaTests/LifetimeSpec.swift +++ b/ReactiveCocoaTests/LifetimeSpec.swift @@ -23,7 +23,7 @@ class LifetimeSpec: QuickSpec { it("should not deadlock") { for _ in 1 ... 10 { - var isDeadlock = true + var isDeadlocked = true DispatchQueue.global(priority: .high).async { _ = object.reactive.lifetime @@ -31,11 +31,11 @@ class LifetimeSpec: QuickSpec { DispatchQueue.global(priority: .high).async { _ = object.reactive.lifetime - isDeadlock = false + isDeadlocked = false } } - expect(isDeadlock).toEventually(beFalsy()) + expect(isDeadlocked).toEventually(beFalsy()) } } } From 1961d6fe357830075396af2f289cdfe3e2127e9c Mon Sep 17 00:00:00 2001 From: Anders Date: Wed, 26 Oct 2016 13:48:19 +0200 Subject: [PATCH 0494/1028] ReactiveCocoa 5.0.0-alpha.3. --- ReactiveCocoa.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 924f633f72..236da9f74e 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "5.0.0-alpha.2" + s.version = "5.0.0-alpha.3" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. From 37135c2e44269fbf59d80695783827629f5d6a91 Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Thu, 27 Oct 2016 09:40:13 -0400 Subject: [PATCH 0495/1028] Remove unused private variable in DynamicProperty --- ReactiveCocoa/DynamicProperty.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/ReactiveCocoa/DynamicProperty.swift b/ReactiveCocoa/DynamicProperty.swift index 3de02b3588..442a6b3c61 100644 --- a/ReactiveCocoa/DynamicProperty.swift +++ b/ReactiveCocoa/DynamicProperty.swift @@ -12,8 +12,6 @@ public final class DynamicProperty: MutablePropertyProtocol { private weak var object: NSObject? private let keyPath: String - private var property: MutableProperty? - /// The current value of the property, as read and written using Key-Value /// Coding. public var value: Value? { From 7e3d4131765fe51178e5bfe8622cc15aea465ab3 Mon Sep 17 00:00:00 2001 From: David Collado Sela Date: Thu, 27 Oct 2016 17:35:51 +0200 Subject: [PATCH 0496/1028] New CocoaTargetProtocol Added CocoaTargetProtocol allowing a Generic CocoaTarget implementation. Valid when Observer value is Void and when it has another value. --- ReactiveCocoa/AppKit/NSControl.swift | 2 +- ReactiveCocoa/CocoaTarget.swift | 25 +++++++++++++++---- ReactiveCocoa/UIKit/UIControl.swift | 2 +- ReactiveCocoa/UIKit/UIGestureRecognizer.swift | 8 ++++-- .../UIKit/UIGestureRecognizerSpec.swift | 2 +- 5 files changed, 29 insertions(+), 10 deletions(-) diff --git a/ReactiveCocoa/AppKit/NSControl.swift b/ReactiveCocoa/AppKit/NSControl.swift index 3769360336..1267ab03fa 100644 --- a/ReactiveCocoa/AppKit/NSControl.swift +++ b/ReactiveCocoa/AppKit/NSControl.swift @@ -96,7 +96,7 @@ extension Reactive where Base: NSControl { return associatedValue { base in let (signal, observer) = Signal<(), NoError>.pipe() - let receiver = CocoaTarget(observer) + let receiver = CocoaTarget<()>(observer) base.target = receiver base.action = #selector(receiver.sendNext) diff --git a/ReactiveCocoa/CocoaTarget.swift b/ReactiveCocoa/CocoaTarget.swift index 9c3ac2e983..c82b135908 100644 --- a/ReactiveCocoa/CocoaTarget.swift +++ b/ReactiveCocoa/CocoaTarget.swift @@ -3,14 +3,29 @@ import ReactiveSwift import enum Result.NoError /// A target that accepts action messages. -internal final class CocoaTarget: NSObject { - let observer: Observer<(), NoError> - - init(_ observer: Observer<(), NoError>) { +internal final class CocoaTarget: NSObject { + let observer: Observer + let transform: (Any?) -> Value + + init(_ observer: Observer, transform: @escaping (Any?) -> Value) { self.observer = observer + self.transform = transform } + @objc func sendNext(_ receiver: Any?) { - observer.send(value: ()) + observer.send(value: transform(receiver)) + } +} + +protocol CocoaTargetProtocol: class { + associatedtype Value + init(_ observer: Observer, transform: @escaping (Any?) -> Value) +} +extension CocoaTarget:CocoaTargetProtocol{} + +extension CocoaTargetProtocol where Value == Void { + init(_ observer: Observer<(), NoError>) { + self.init(observer, transform: { _ in }) } } diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index ef586a2128..0f819c4ba8 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -45,7 +45,7 @@ extension Reactive where Base: UIControl { /// A trigger signal. public func trigger(for controlEvents: UIControlEvents) -> Signal<(), NoError> { return Signal { observer in - let receiver = CocoaTarget(observer) + let receiver = CocoaTarget<()>(observer) base.addTarget(receiver, action: #selector(receiver.sendNext), for: controlEvents) diff --git a/ReactiveCocoa/UIKit/UIGestureRecognizer.swift b/ReactiveCocoa/UIKit/UIGestureRecognizer.swift index eb0ebec6f9..b56022d4d1 100644 --- a/ReactiveCocoa/UIKit/UIGestureRecognizer.swift +++ b/ReactiveCocoa/UIKit/UIGestureRecognizer.swift @@ -9,9 +9,11 @@ extension Reactive where Base: UIGestureRecognizer { /// - returns: /// A trigger signal. - public var recognised: Signal<(), NoError> { + public var recognised: Signal { return Signal { observer in - let receiver = CocoaTarget(observer) + let receiver = CocoaTarget(observer, transform: { gestureRecognizer in + return gestureRecognizer as! Base + }) base.addTarget(receiver, action: #selector(receiver.sendNext)) let disposable = lifetime.ended.observeCompleted(observer.sendCompleted) @@ -23,3 +25,5 @@ extension Reactive where Base: UIGestureRecognizer { } } } + + diff --git a/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift b/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift index 9c5d762272..fac4258d18 100644 --- a/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift @@ -24,7 +24,7 @@ class UIGestureRecognizerSpec: QuickSpec { let signal = gestureRecognizer.reactive.recognised var counter = 0 - signal.observeValues { counter += 1 } + signal.observeValues { _ in counter += 1 } expect(counter) == 0 gestureRecognizer.fireGestureEvent(.possible) From 332cfca6c704a94b669335757b41e5fa1604a3c0 Mon Sep 17 00:00:00 2001 From: David Collado Sela Date: Thu, 27 Oct 2016 17:39:15 +0200 Subject: [PATCH 0497/1028] Changed gesture signal name to stateChanged --- ReactiveCocoa/UIKit/UIGestureRecognizer.swift | 2 +- ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ReactiveCocoa/UIKit/UIGestureRecognizer.swift b/ReactiveCocoa/UIKit/UIGestureRecognizer.swift index b56022d4d1..2452770f86 100644 --- a/ReactiveCocoa/UIKit/UIGestureRecognizer.swift +++ b/ReactiveCocoa/UIKit/UIGestureRecognizer.swift @@ -9,7 +9,7 @@ extension Reactive where Base: UIGestureRecognizer { /// - returns: /// A trigger signal. - public var recognised: Signal { + public var stateChanged: Signal { return Signal { observer in let receiver = CocoaTarget(observer, transform: { gestureRecognizer in return gestureRecognizer as! Base diff --git a/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift b/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift index fac4258d18..085e0126a6 100644 --- a/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift @@ -20,8 +20,8 @@ class UIGestureRecognizerSpec: QuickSpec { expect(_gestureRecognizer).to(beNil()) } - it("should send a value when the gesture is recognised") { - let signal = gestureRecognizer.reactive.recognised + it("should send a value when the gesture state changes") { + let signal = gestureRecognizer.reactive.stateChanged var counter = 0 signal.observeValues { _ in counter += 1 } From 73287a07e4f3586d31c4f89a4d22ba6274e8d991 Mon Sep 17 00:00:00 2001 From: David Collado Sela Date: Thu, 27 Oct 2016 17:39:56 +0200 Subject: [PATCH 0498/1028] Made test class private --- ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift b/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift index 085e0126a6..22625c8957 100644 --- a/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift @@ -49,7 +49,7 @@ private struct TargetActionPair { let action: Selector } -private class TestTapGestureRecognizer: UITapGestureRecognizer { +final private class TestTapGestureRecognizer: UITapGestureRecognizer { var targetActionPair: TargetActionPair? var forceState: UIGestureRecognizerState = .ended override var state: UIGestureRecognizerState { From 0c6e94ea209cbb75c750195bf37e7389813c12dd Mon Sep 17 00:00:00 2001 From: David Collado Sela Date: Thu, 27 Oct 2016 17:40:46 +0200 Subject: [PATCH 0499/1028] Removed extra space --- ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift b/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift index 22625c8957..f77f4bf035 100644 --- a/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift @@ -39,7 +39,6 @@ class UIGestureRecognizerSpec: QuickSpec { gestureRecognizer.fireGestureEvent(.ended) expect(counter) == 4 - } } } From 5031cb2e5781743b7eff4b37222ae66e49aac4fc Mon Sep 17 00:00:00 2001 From: David Collado Sela Date: Thu, 27 Oct 2016 17:51:52 +0200 Subject: [PATCH 0500/1028] Added UIGestureRecognizer tests Added two additional tests for events and gesture recognizer passed in signal --- .../UIKit/UIGestureRecognizerSpec.swift | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift b/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift index f77f4bf035..d3a83f57d3 100644 --- a/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift @@ -40,6 +40,38 @@ class UIGestureRecognizerSpec: QuickSpec { gestureRecognizer.fireGestureEvent(.ended) expect(counter) == 4 } + + it("should send it's gesture recognizer in signal") { + let signal = gestureRecognizer.reactive.stateChanged + var counter = 0 + signal.observeValues{ signalGestureRecognizer in + if signalGestureRecognizer === gestureRecognizer{ + counter += 1 + } + } + gestureRecognizer.fireGestureEvent( .began) + expect(counter) == 1 + } + + it("should send it's gesture recognizer with the fired state") { + let signal = gestureRecognizer.reactive.stateChanged + weak var signalGestureRecognizer:TestTapGestureRecognizer? + signal.observeValues{ recognizer in + signalGestureRecognizer = recognizer + } + + gestureRecognizer.fireGestureEvent(.possible) + expect(signalGestureRecognizer?.state) == .possible + + gestureRecognizer.fireGestureEvent( .began) + expect(signalGestureRecognizer?.state) == .began + + gestureRecognizer.fireGestureEvent(.changed) + expect(signalGestureRecognizer?.state) == .changed + + gestureRecognizer.fireGestureEvent(.ended) + expect(signalGestureRecognizer?.state) == .ended + } } } From 6ff71e8bd7d6c1010083cc83b05261783aa7e49e Mon Sep 17 00:00:00 2001 From: David Collado Sela Date: Fri, 28 Oct 2016 09:44:29 +0200 Subject: [PATCH 0501/1028] Removed empty lines --- ReactiveCocoa/UIKit/UIGestureRecognizer.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/ReactiveCocoa/UIKit/UIGestureRecognizer.swift b/ReactiveCocoa/UIKit/UIGestureRecognizer.swift index 2452770f86..5c72c8ea31 100644 --- a/ReactiveCocoa/UIKit/UIGestureRecognizer.swift +++ b/ReactiveCocoa/UIKit/UIGestureRecognizer.swift @@ -3,12 +3,10 @@ import UIKit import enum Result.NoError extension Reactive where Base: UIGestureRecognizer { - /// Create a signal which sends a `next` event for each gesture event /// /// - returns: /// A trigger signal. - public var stateChanged: Signal { return Signal { observer in let receiver = CocoaTarget(observer, transform: { gestureRecognizer in From da866d7ced92ef48cb4c8cf7a0db40691fbba61b Mon Sep 17 00:00:00 2001 From: David Collado Sela Date: Fri, 28 Oct 2016 09:44:57 +0200 Subject: [PATCH 0502/1028] Added space --- ReactiveCocoa/CocoaTarget.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/ReactiveCocoa/CocoaTarget.swift b/ReactiveCocoa/CocoaTarget.swift index c82b135908..a133f62676 100644 --- a/ReactiveCocoa/CocoaTarget.swift +++ b/ReactiveCocoa/CocoaTarget.swift @@ -22,6 +22,7 @@ protocol CocoaTargetProtocol: class { associatedtype Value init(_ observer: Observer, transform: @escaping (Any?) -> Value) } + extension CocoaTarget:CocoaTargetProtocol{} extension CocoaTargetProtocol where Value == Void { From cea167e2d6e712351b8369687ac99964c3337a43 Mon Sep 17 00:00:00 2001 From: David Collado Sela Date: Fri, 28 Oct 2016 09:45:18 +0200 Subject: [PATCH 0503/1028] Changed protocol to internal --- ReactiveCocoa/CocoaTarget.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa/CocoaTarget.swift b/ReactiveCocoa/CocoaTarget.swift index a133f62676..e7f9f1bd27 100644 --- a/ReactiveCocoa/CocoaTarget.swift +++ b/ReactiveCocoa/CocoaTarget.swift @@ -18,7 +18,7 @@ internal final class CocoaTarget: NSObject { } } -protocol CocoaTargetProtocol: class { +internal protocol CocoaTargetProtocol: class { associatedtype Value init(_ observer: Observer, transform: @escaping (Any?) -> Value) } From 9434297744fb96a2ae621989086c307730e85d1b Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Sat, 29 Oct 2016 11:41:56 -0400 Subject: [PATCH 0504/1028] Add cross reference notes to UIKit text observation methods --- ReactiveCocoa/UIKit/UITextField.swift | 5 +++++ ReactiveCocoa/UIKit/UITextView.swift | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/ReactiveCocoa/UIKit/UITextField.swift b/ReactiveCocoa/UIKit/UITextField.swift index 953fd867eb..31ec54f50b 100644 --- a/ReactiveCocoa/UIKit/UITextField.swift +++ b/ReactiveCocoa/UIKit/UITextField.swift @@ -9,12 +9,17 @@ extension Reactive where Base: UITextField { } /// A signal of text values emitted by the text field upon end of editing. + /// + /// - note: To observe text values that change on all editing events, + /// see `continuousTextValues`. public var textValues: Signal { return trigger(for: .editingDidEnd) .map { [unowned base = self.base] in base.text } } /// A signal of text values emitted by the text field upon any changes. + /// + /// - note: To observe text values only when editing ends, see `textValues`. public var continuousTextValues: Signal { return trigger(for: .editingChanged) .map { [unowned base = self.base] in base.text } diff --git a/ReactiveCocoa/UIKit/UITextView.swift b/ReactiveCocoa/UIKit/UITextView.swift index 6fd6f109bd..e4d62f306d 100644 --- a/ReactiveCocoa/UIKit/UITextView.swift +++ b/ReactiveCocoa/UIKit/UITextView.swift @@ -9,6 +9,9 @@ extension Reactive where Base: UITextView { } /// A signal of text values emitted by the text view upon end of editing. + /// + /// - note: To observe text values that change on all editing events, + /// see `continuousTextValues`. public var textValues: Signal { return NotificationCenter.default .reactive @@ -18,6 +21,8 @@ extension Reactive where Base: UITextView { } /// A signal of text values emitted by the text view upon any changes. + /// + /// - note: To observe text values only when editing ends, see `textValues`. public var continuousTextValues: Signal { return NotificationCenter.default .reactive From f6fd187a11d34e02c2eb3cbbaa2f350fd40b5e0c Mon Sep 17 00:00:00 2001 From: David Collado Date: Mon, 31 Oct 2016 14:39:30 +0100 Subject: [PATCH 0505/1028] Andersio suggestions --- ReactiveCocoa/CocoaTarget.swift | 13 ++++++------- ReactiveCocoa/UIKit/UIGestureRecognizer.swift | 4 ++-- .../UIKit/UIGestureRecognizerSpec.swift | 4 ++-- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/ReactiveCocoa/CocoaTarget.swift b/ReactiveCocoa/CocoaTarget.swift index e7f9f1bd27..492d42e0bc 100644 --- a/ReactiveCocoa/CocoaTarget.swift +++ b/ReactiveCocoa/CocoaTarget.swift @@ -4,16 +4,15 @@ import enum Result.NoError /// A target that accepts action messages. internal final class CocoaTarget: NSObject { - let observer: Observer - let transform: (Any?) -> Value + private let observer: Observer + private let transform: (Any?) -> Value - init(_ observer: Observer, transform: @escaping (Any?) -> Value) { + internal init(_ observer: Observer, transform: @escaping (Any?) -> Value) { self.observer = observer self.transform = transform } - - @objc func sendNext(_ receiver: Any?) { + @objc internal func sendNext(_ receiver: Any?) { observer.send(value: transform(receiver)) } } @@ -23,10 +22,10 @@ internal protocol CocoaTargetProtocol: class { init(_ observer: Observer, transform: @escaping (Any?) -> Value) } -extension CocoaTarget:CocoaTargetProtocol{} +extension CocoaTarget: CocoaTargetProtocol{} extension CocoaTargetProtocol where Value == Void { - init(_ observer: Observer<(), NoError>) { + internal init(_ observer: Observer<(), NoError>) { self.init(observer, transform: { _ in }) } } diff --git a/ReactiveCocoa/UIKit/UIGestureRecognizer.swift b/ReactiveCocoa/UIKit/UIGestureRecognizer.swift index 5c72c8ea31..11b4d2e7c0 100644 --- a/ReactiveCocoa/UIKit/UIGestureRecognizer.swift +++ b/ReactiveCocoa/UIKit/UIGestureRecognizer.swift @@ -9,9 +9,9 @@ extension Reactive where Base: UIGestureRecognizer { /// A trigger signal. public var stateChanged: Signal { return Signal { observer in - let receiver = CocoaTarget(observer, transform: { gestureRecognizer in + let receiver = CocoaTarget(observer){ gestureRecognizer in return gestureRecognizer as! Base - }) + } base.addTarget(receiver, action: #selector(receiver.sendNext)) let disposable = lifetime.ended.observeCompleted(observer.sendCompleted) diff --git a/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift b/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift index d3a83f57d3..ea0d847123 100644 --- a/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift @@ -75,12 +75,12 @@ class UIGestureRecognizerSpec: QuickSpec { } } -private struct TargetActionPair { +fileprivate struct TargetActionPair { let target: AnyObject let action: Selector } -final private class TestTapGestureRecognizer: UITapGestureRecognizer { +final fileprivate class TestTapGestureRecognizer: UITapGestureRecognizer { var targetActionPair: TargetActionPair? var forceState: UIGestureRecognizerState = .ended override var state: UIGestureRecognizerState { From ac27f34753613549c3f9c11c58c4cc73bfa2568e Mon Sep 17 00:00:00 2001 From: Anders Date: Mon, 31 Oct 2016 17:32:12 +0100 Subject: [PATCH 0506/1028] Formatting & access modifiers. Included the spec in tvOS test. --- ReactiveCocoa.xcodeproj/project.pbxproj | 2 ++ ReactiveCocoa/CocoaTarget.swift | 2 +- ReactiveCocoa/UIKit/UIGestureRecognizer.swift | 2 -- .../UIKit/UIGestureRecognizerSpec.swift | 21 ++++++++++--------- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 2a81561008..9261110b0f 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -97,6 +97,7 @@ 9A6AAA0E1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; 9A6AAA0F1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; 9A6AAA101DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; + 9A9A129A1DC7A97100D10223 /* UIGestureRecognizerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4191394B1DBA002C0043C9D1 /* UIGestureRecognizerSpec.swift */; }; 9A9DFEE51DA7B5500039EE1B /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; 9A9DFEE91DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; 9A9DFEEA1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; @@ -962,6 +963,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9A9A129A1DC7A97100D10223 /* UIGestureRecognizerSpec.swift in Sources */, 9A1D06591D93EA7E00ACF44C /* UIViewSpec.swift in Sources */, 7DFBED281CDB8DE300EE435B /* DynamicPropertySpec.swift in Sources */, 9A9DFEEB1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */, diff --git a/ReactiveCocoa/CocoaTarget.swift b/ReactiveCocoa/CocoaTarget.swift index 492d42e0bc..f37b2f5620 100644 --- a/ReactiveCocoa/CocoaTarget.swift +++ b/ReactiveCocoa/CocoaTarget.swift @@ -22,7 +22,7 @@ internal protocol CocoaTargetProtocol: class { init(_ observer: Observer, transform: @escaping (Any?) -> Value) } -extension CocoaTarget: CocoaTargetProtocol{} +extension CocoaTarget: CocoaTargetProtocol {} extension CocoaTargetProtocol where Value == Void { internal init(_ observer: Observer<(), NoError>) { diff --git a/ReactiveCocoa/UIKit/UIGestureRecognizer.swift b/ReactiveCocoa/UIKit/UIGestureRecognizer.swift index 11b4d2e7c0..98daf58854 100644 --- a/ReactiveCocoa/UIKit/UIGestureRecognizer.swift +++ b/ReactiveCocoa/UIKit/UIGestureRecognizer.swift @@ -23,5 +23,3 @@ extension Reactive where Base: UIGestureRecognizer { } } } - - diff --git a/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift b/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift index ea0d847123..bd0d172bfa 100644 --- a/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift @@ -75,24 +75,25 @@ class UIGestureRecognizerSpec: QuickSpec { } } -fileprivate struct TargetActionPair { - let target: AnyObject - let action: Selector -} +private final class TestTapGestureRecognizer: UITapGestureRecognizer { + private struct TargetActionPair { + let target: AnyObject + let action: Selector + } + + private var targetActionPair: TargetActionPair? + private var forceState: UIGestureRecognizerState = .ended -final fileprivate class TestTapGestureRecognizer: UITapGestureRecognizer { - var targetActionPair: TargetActionPair? - var forceState: UIGestureRecognizerState = .ended - override var state: UIGestureRecognizerState { + fileprivate override var state: UIGestureRecognizerState { get { return forceState } set { self.state = newValue } } - override func addTarget(_ target: Any, action: Selector) { + fileprivate override func addTarget(_ target: Any, action: Selector) { targetActionPair = TargetActionPair(target: target as AnyObject, action: action) } - func fireGestureEvent(_ state: UIGestureRecognizerState) { + fileprivate func fireGestureEvent(_ state: UIGestureRecognizerState) { guard let targetAction = self.targetActionPair else { return } forceState = state _ = targetAction.target.perform(targetAction.action, with: self) From 97af74d330bcb6f5e7a22fd2f8479e46ea67e9e1 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Mon, 31 Oct 2016 17:37:28 +0100 Subject: [PATCH 0507/1028] Fixed a typo in the readme. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a0530ffd93..7b4c5972a1 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ __ReactiveCocoa__ wraps various aspects of Cocoa frameworks with the declarative ```swift // Update `allowsCookies` whenever the toggle is flipped. - perferences.allowsCookies <~ toggle.reactive.isOnValues + preferences.allowsCookies <~ toggle.reactive.isOnValues // Compute live character counts from the continuous stream of user initiated // changes in the text. From cf4a20af33cacc0dc1a7083e43a582495fbc1f03 Mon Sep 17 00:00:00 2001 From: Anders Date: Mon, 31 Oct 2016 17:40:59 +0100 Subject: [PATCH 0508/1028] Changed the method prefix and class suffix used for swizzling. --- ReactiveCocoa/RACObjCRuntimeUtilities.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoa/RACObjCRuntimeUtilities.m b/ReactiveCocoa/RACObjCRuntimeUtilities.m index 7801768937..f3c33dae34 100644 --- a/ReactiveCocoa/RACObjCRuntimeUtilities.m +++ b/ReactiveCocoa/RACObjCRuntimeUtilities.m @@ -7,8 +7,8 @@ const NSInteger RACSelectorSignalErrorMethodSwizzlingRace = 1; const NSExceptionName RACSwizzleException = @"RACSwizzleException"; -static NSString * const RACSignalForSelectorAliasPrefix = @"rac_alias_"; -static NSString * const RACSubclassSuffix = @"_RACSelectorSignal"; +static NSString * const RACSignalForSelectorAliasPrefix = @"rac_swift_"; +static NSString * const RACSubclassSuffix = @"_RACIntercepting"; static void *RACSubclassAssociationKey = &RACSubclassAssociationKey; static NSMutableSet *swizzledClasses() { From d217e24eb794004f9f0d59143d0cc24a4dccdf00 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Tue, 1 Nov 2016 16:28:29 +0100 Subject: [PATCH 0509/1028] Fixed a few typos in the readme. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7b4c5972a1..2f2e79f9d9 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ⚠️ [Looking for the Objective-C API?][] ⚠️ [Still using Swift 2.x?][] ## What is ReactiveSwift? -__ReactiveSwift__ offers composable, declaractive and flexible primitives that are built around the grand concept of ___streams of values over time___. These primitives can be used to uniformly represent common Cocoa and generic programming patterns that are fundementally an act of observation. +__ReactiveSwift__ offers composable, declarative and flexible primitives that are built around the grand concept of ___streams of values over time___. These primitives can be used to uniformly represent common Cocoa and generic programming patterns that are fundamentally an act of observation. For more information about the core primitives, see [ReactiveSwift][]. @@ -86,7 +86,7 @@ ReactiveCocoa supports macOS 10.9+, iOS 8.0+, watchOS 2.0+, and tvOS 9.0+. #### Carthage -If you are using [Carthage][] to manage your dependency, simply add +If you are using [Carthage][] to manage your dependencies, simply add ReactiveCocoa to your `Cartfile`: ``` @@ -97,7 +97,7 @@ If you use Carthage to build your dependencies, make sure you have added `Reacti #### CocoaPods -If you are using [CocoaPods][] to manage your dependency, simply add +If you are using [CocoaPods][] to manage your dependencies, simply add ReactiveCocoa to your `Podfile`: ``` From a01e7d260b93f6f9e2b4238838ee77e98dd9a07d Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Tue, 1 Nov 2016 16:36:08 +0100 Subject: [PATCH 0510/1028] Adjusted the wording in the readme. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2f2e79f9d9..293584ad59 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ __ReactiveCocoa__ wraps various aspects of Cocoa frameworks with the declarative keyPath: #keyPath(person.name)) ``` -But there are still more to be discovered, and more to be introduced. Read our in-code documentations and release notes to +But there are still more to be discovered and introduced. Read our in-code documentations and release notes to find out more. ## Getting started @@ -86,7 +86,7 @@ ReactiveCocoa supports macOS 10.9+, iOS 8.0+, watchOS 2.0+, and tvOS 9.0+. #### Carthage -If you are using [Carthage][] to manage your dependencies, simply add +If you use [Carthage][] to manage your dependencies, simply add ReactiveCocoa to your `Cartfile`: ``` @@ -97,7 +97,7 @@ If you use Carthage to build your dependencies, make sure you have added `Reacti #### CocoaPods -If you are using [CocoaPods][] to manage your dependencies, simply add +If you use [CocoaPods][] to manage your dependencies, simply add ReactiveCocoa to your `Podfile`: ``` From 592f0ddff023a8b4716f2808502f1ea1b2a130bb Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Wed, 2 Nov 2016 13:52:27 +0900 Subject: [PATCH 0511/1028] DynamicProperty.init should not accept nil value for `object` That does not make sense. --- ReactiveCocoa/DynamicProperty.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoa/DynamicProperty.swift b/ReactiveCocoa/DynamicProperty.swift index 442a6b3c61..ed79540e8a 100644 --- a/ReactiveCocoa/DynamicProperty.swift +++ b/ReactiveCocoa/DynamicProperty.swift @@ -55,12 +55,12 @@ public final class DynamicProperty: MutablePropertyProtocol { /// - parameters: /// - object: An object to be observed. /// - keyPath: Key path to observe on the object. - public init(object: NSObject?, keyPath: String) { + public init(object: NSObject, keyPath: String) { self.object = object self.keyPath = keyPath /// A DynamicProperty will stay alive as long as its object is alive. /// This is made possible by strong reference cycles. - _ = object?.reactive.lifetime.ended.observeCompleted { _ = self } + _ = object.reactive.lifetime.ended.observeCompleted { _ = self } } } From 56b518d250c7bf267307ae1f7504e19d1e199495 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Wed, 2 Nov 2016 13:56:04 +0900 Subject: [PATCH 0512/1028] Fix indent --- ReactiveCocoa/NSObject+Intercepting.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index dd63bb8f48..6fc575b1f3 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -22,8 +22,8 @@ extension Reactive where Base: NSObject { let (signal, observer) = Signal<(), NoError>.pipe() let isSuccessful = base._rac_setupInvocationObservation(for: selector, - protocol: nil, - receiver: observer.send(value:)) + protocol: nil, + receiver: observer.send(value:)) precondition(isSuccessful) lifetime.ended.observeCompleted(observer.sendCompleted) From f9b675dec123f88fd6a7c068b3d1af1d8cacfa3a Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Wed, 2 Nov 2016 14:18:00 +0900 Subject: [PATCH 0513/1028] Refactor NSTextField and UITextView extensions --- ReactiveCocoa/AppKit/NSTextField.swift | 13 +++++++------ ReactiveCocoa/UIKit/UITextView.swift | 20 ++++++++++---------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/ReactiveCocoa/AppKit/NSTextField.swift b/ReactiveCocoa/AppKit/NSTextField.swift index e5d7c63a46..746d0792df 100644 --- a/ReactiveCocoa/AppKit/NSTextField.swift +++ b/ReactiveCocoa/AppKit/NSTextField.swift @@ -3,22 +3,23 @@ import AppKit import enum Result.NoError extension Reactive where Base: NSTextField { - /// A signal of values in `String` from the text field upon any changes. - public var continuousStringValues: Signal { + private var notifications: Signal { return NotificationCenter.default .reactive .notifications(forName: .NSControlTextDidChange, object: base) .take(during: lifetime) + } + + /// A signal of values in `String` from the text field upon any changes. + public var continuousStringValues: Signal { + return notifications .map { ($0.object as! NSTextField).stringValue } } /// A signal of values in `NSAttributedString` from the text field upon any /// changes. public var continuousAttributedStringValues: Signal { - return NotificationCenter.default - .reactive - .notifications(forName: .NSControlTextDidChange, object: base) - .take(during: lifetime) + return notifications .map { ($0.object as! NSTextField).attributedStringValue } } } diff --git a/ReactiveCocoa/UIKit/UITextView.swift b/ReactiveCocoa/UIKit/UITextView.swift index e4d62f306d..e5fdcf0245 100644 --- a/ReactiveCocoa/UIKit/UITextView.swift +++ b/ReactiveCocoa/UIKit/UITextView.swift @@ -8,26 +8,26 @@ extension Reactive where Base: UITextView { return makeBindingTarget { $0.text = $1 } } + private func textValues(forName name: NSNotification.Name) -> Signal { + return NotificationCenter.default + .reactive + .notifications(forName: name, object: base) + .take(during: lifetime) + .map { ($0.object as! UITextView).text! } + } + /// A signal of text values emitted by the text view upon end of editing. /// /// - note: To observe text values that change on all editing events, /// see `continuousTextValues`. public var textValues: Signal { - return NotificationCenter.default - .reactive - .notifications(forName: .UITextViewTextDidEndEditing, object: base) - .take(during: lifetime) - .map { ($0.object as! UITextView).text! } + return textValues(forName: .UITextViewTextDidEndEditing) } /// A signal of text values emitted by the text view upon any changes. /// /// - note: To observe text values only when editing ends, see `textValues`. public var continuousTextValues: Signal { - return NotificationCenter.default - .reactive - .notifications(forName: .UITextViewTextDidChange, object: base) - .take(during: lifetime) - .map { ($0.object as! UITextView).text! } + return textValues(forName: .UITextViewTextDidChange) } } From 3b64c5505fa84b1991c150ab150349a4b4d565e7 Mon Sep 17 00:00:00 2001 From: Eimantas Vaiciunas Date: Wed, 2 Nov 2016 18:58:04 +0200 Subject: [PATCH 0514/1028] Make reactive `backgroundColor` for `UIView` --- ReactiveCocoa/UIKit/UIView.swift | 5 +++++ ReactiveCocoaTests/UIKit/UIViewSpec.swift | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/ReactiveCocoa/UIKit/UIView.swift b/ReactiveCocoa/UIKit/UIView.swift index 27728bdd08..d9094369f7 100644 --- a/ReactiveCocoa/UIKit/UIView.swift +++ b/ReactiveCocoa/UIKit/UIView.swift @@ -16,4 +16,9 @@ extension Reactive where Base: UIView { public var isUserInteractionEnabled: BindingTarget { return makeBindingTarget { $0.isUserInteractionEnabled = $1 } } + + /// Sets the background color of the view. + public var backgroundColor: BindingTarget { + return makeBindingTarget { $0.backgroundColor = $1 } + } } diff --git a/ReactiveCocoaTests/UIKit/UIViewSpec.swift b/ReactiveCocoaTests/UIKit/UIViewSpec.swift index 0dd5a06d31..de884913e4 100644 --- a/ReactiveCocoaTests/UIKit/UIViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIViewSpec.swift @@ -61,5 +61,21 @@ class UIViewSpec: QuickSpec { observer.send(value: false) expect(view.isUserInteractionEnabled) == false } + + it("should accept changes from bindings to its background color") { + view.backgroundColor = .white + + let (pipeSignal, observer) = Signal.pipe() + view.reactive.backgroundColor <~ SignalProducer(signal: pipeSignal) + + observer.send(value: .yellow) + expect(view.backgroundColor).to(equal(UIColor.yellow)) + + observer.send(value: .green) + expect(view.backgroundColor).to(equal(UIColor.green)) + + observer.send(value: .red) + expect(view.backgroundColor).to(equal(UIColor.red)) + } } } From a00438418b566517f8a9066064f2ddaf26ae0edd Mon Sep 17 00:00:00 2001 From: David Collado Date: Thu, 3 Nov 2016 09:17:08 +0100 Subject: [PATCH 0515/1028] Style issues fixed --- ReactiveCocoa/UIKit/UIGestureRecognizer.swift | 2 +- ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ReactiveCocoa/UIKit/UIGestureRecognizer.swift b/ReactiveCocoa/UIKit/UIGestureRecognizer.swift index 98daf58854..6ffc5a89f1 100644 --- a/ReactiveCocoa/UIKit/UIGestureRecognizer.swift +++ b/ReactiveCocoa/UIKit/UIGestureRecognizer.swift @@ -9,7 +9,7 @@ extension Reactive where Base: UIGestureRecognizer { /// A trigger signal. public var stateChanged: Signal { return Signal { observer in - let receiver = CocoaTarget(observer){ gestureRecognizer in + let receiver = CocoaTarget(observer) { gestureRecognizer in return gestureRecognizer as! Base } base.addTarget(receiver, action: #selector(receiver.sendNext)) diff --git a/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift b/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift index bd0d172bfa..62acba2ae4 100644 --- a/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift @@ -44,7 +44,7 @@ class UIGestureRecognizerSpec: QuickSpec { it("should send it's gesture recognizer in signal") { let signal = gestureRecognizer.reactive.stateChanged var counter = 0 - signal.observeValues{ signalGestureRecognizer in + signal.observeValues { signalGestureRecognizer in if signalGestureRecognizer === gestureRecognizer{ counter += 1 } @@ -55,8 +55,8 @@ class UIGestureRecognizerSpec: QuickSpec { it("should send it's gesture recognizer with the fired state") { let signal = gestureRecognizer.reactive.stateChanged - weak var signalGestureRecognizer:TestTapGestureRecognizer? - signal.observeValues{ recognizer in + weak var signalGestureRecognizer: TestTapGestureRecognizer? + signal.observeValues { recognizer in signalGestureRecognizer = recognizer } From 872823c1f4d51a05bd3398adae435c38ea8ccab2 Mon Sep 17 00:00:00 2001 From: David Collado Date: Thu, 3 Nov 2016 09:20:34 +0100 Subject: [PATCH 0516/1028] Removed generic CocoaTarget type when it's void --- ReactiveCocoa/AppKit/NSControl.swift | 2 +- ReactiveCocoa/UIKit/UIControl.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoa/AppKit/NSControl.swift b/ReactiveCocoa/AppKit/NSControl.swift index 1267ab03fa..3769360336 100644 --- a/ReactiveCocoa/AppKit/NSControl.swift +++ b/ReactiveCocoa/AppKit/NSControl.swift @@ -96,7 +96,7 @@ extension Reactive where Base: NSControl { return associatedValue { base in let (signal, observer) = Signal<(), NoError>.pipe() - let receiver = CocoaTarget<()>(observer) + let receiver = CocoaTarget(observer) base.target = receiver base.action = #selector(receiver.sendNext) diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index 0f819c4ba8..ef586a2128 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -45,7 +45,7 @@ extension Reactive where Base: UIControl { /// A trigger signal. public func trigger(for controlEvents: UIControlEvents) -> Signal<(), NoError> { return Signal { observer in - let receiver = CocoaTarget<()>(observer) + let receiver = CocoaTarget(observer) base.addTarget(receiver, action: #selector(receiver.sendNext), for: controlEvents) From 61ac5f7c6506cd642ebef2b8f1c1ba6b2f9fdc33 Mon Sep 17 00:00:00 2001 From: Eimantas Vaiciunas Date: Wed, 2 Nov 2016 20:01:19 +0200 Subject: [PATCH 0517/1028] Add reactive `constant` to `NSLayoutConstraint` --- ReactiveCocoa.xcodeproj/project.pbxproj | 32 +++++++++++++++++ ReactiveCocoa/Shared/NSLayoutConstraint.swift | 15 ++++++++ .../Shared/NSLayoutConstraintSpec.swift | 36 +++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 ReactiveCocoa/Shared/NSLayoutConstraint.swift create mode 100644 ReactiveCocoaTests/Shared/NSLayoutConstraintSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index c32060296c..0435e66f89 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -11,6 +11,12 @@ 4A0E11001D2A92720065D310 /* NSObject+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */; }; 4A0E11011D2A92720065D310 /* NSObject+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */; }; 4A0E11021D2A92720065D310 /* NSObject+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */; }; + 538DCB791DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */; }; + 538DCB7A1DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */; }; + 538DCB7B1DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */; }; + 538DCB7D1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB7C1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift */; }; + 538DCB7E1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB7C1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift */; }; + 538DCB7F1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB7C1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift */; }; 57A4D2081BA13D7A00F7D4B1 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; 57A4D20A1BA13D7A00F7D4B1 /* ReactiveCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7DFBED081CDB8C9500EE435B /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */; }; @@ -235,6 +241,8 @@ /* Begin PBXFileReference section */ 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Lifetime.swift"; sourceTree = ""; }; + 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSLayoutConstraint.swift; sourceTree = ""; }; + 538DCB7C1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSLayoutConstraintSpec.swift; sourceTree = ""; }; 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Application.xcconfig"; sourceTree = ""; }; 57A4D2451BA13F9700F7D4B1 /* tvOS-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Base.xcconfig"; sourceTree = ""; }; @@ -412,6 +420,22 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 538DCB761DCA5E1600332880 /* Shared */ = { + isa = PBXGroup; + children = ( + 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */, + ); + path = Shared; + sourceTree = ""; + }; + 538DCB771DCA5E3200332880 /* Shared */ = { + isa = PBXGroup; + children = ( + 538DCB7C1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift */, + ); + path = Shared; + sourceTree = ""; + }; 57A4D2431BA13F9700F7D4B1 /* tvOS */ = { isa = PBXGroup; children = ( @@ -555,6 +579,7 @@ 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */, 9A1D05E91D93E9F100ACF44C /* AppKit */, 9A1D05EB1D93E9F100ACF44C /* UIKit */, + 538DCB761DCA5E1600332880 /* Shared */, D04725ED19E49ED7006002AA /* Supporting Files */, ); path = ReactiveCocoa; @@ -574,6 +599,7 @@ children = ( 9ADE4A8C1DA6D94C005C2AC8 /* AppKit */, 9A1D06231D93EA7E00ACF44C /* UIKit */, + 538DCB771DCA5E3200332880 /* Shared */, 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */, CD8401821CEE8ED7009F0ABF /* CocoaActionSpec.swift */, D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */, @@ -967,6 +993,7 @@ 9AD0F06D1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, 9A1D06211D93EA0100ACF44C /* UIView.swift in Sources */, 9A1D067A1D94160100ACF44C /* RACObjCRuntimeUtilities.m in Sources */, + 538DCB7B1DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */, 9ADE4A941DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, 9A1D05E31D93E99100ACF44C /* NSObject+Association.swift in Sources */, 9A1D06161D93EA0100ACF44C /* UIControl.swift in Sources */, @@ -1001,6 +1028,7 @@ 9A1D063F1D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift in Sources */, 9A1D06371D93EA7E00ACF44C /* UIActivityIndicatorViewSpec.swift in Sources */, 9A6AAA2F1DB903A40013AAEA /* ReusableComponentsSpec.swift in Sources */, + 538DCB7F1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift in Sources */, 9A1D06471D93EA7E00ACF44C /* UILabelSpec.swift in Sources */, 9A1D06391D93EA7E00ACF44C /* UIBarButtonItemSpec.swift in Sources */, 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */, @@ -1039,6 +1067,7 @@ 9A1D06771D9415FF00ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, 9A1D05E01D93E99100ACF44C /* NSObject+Association.swift in Sources */, + 538DCB791DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */, 9A6AAA261DB8F5280013AAEA /* ReusableComponents.swift in Sources */, 9ADFE5A51DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */, 9ADE4A7C1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, @@ -1057,6 +1086,7 @@ buildActionMask = 2147483647; files = ( 9A6AAA0E1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */, + 538DCB7D1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift in Sources */, 9ADE4A8F1DA6DA20005C2AC8 /* NSControlSpec.swift in Sources */, D0A2260E1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */, 9ADFE5A11DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */, @@ -1095,6 +1125,7 @@ 9ADFE5A61DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */, 9AD0F06B1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, 9A1D06081D93EA0000ACF44C /* UIProgressView.swift in Sources */, + 538DCB7A1DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */, 9A1D06001D93EA0000ACF44C /* UIBarButtonItem.swift in Sources */, 9A1D05FF1D93EA0000ACF44C /* UIActivityIndicatorView.swift in Sources */, 9A1D06091D93EA0000ACF44C /* UISegmentedControl.swift in Sources */, @@ -1128,6 +1159,7 @@ 9A1D06401D93EA7E00ACF44C /* UIControlSpec.swift in Sources */, 9A1D06481D93EA7E00ACF44C /* UIProgressViewSpec.swift in Sources */, CD8401841CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, + 538DCB7E1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift in Sources */, 9A1D06521D93EA7E00ACF44C /* UITextFieldSpec.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/ReactiveCocoa/Shared/NSLayoutConstraint.swift b/ReactiveCocoa/Shared/NSLayoutConstraint.swift new file mode 100644 index 0000000000..506a3457e3 --- /dev/null +++ b/ReactiveCocoa/Shared/NSLayoutConstraint.swift @@ -0,0 +1,15 @@ +import ReactiveSwift + +#if os(macOS) +import AppKit +#else +import UIKit +#endif + +extension Reactive where Base: NSLayoutConstraint { + + public var constant: BindingTarget { + return makeBindingTarget(action: { $0.constant = $1 }) + } + +} diff --git a/ReactiveCocoaTests/Shared/NSLayoutConstraintSpec.swift b/ReactiveCocoaTests/Shared/NSLayoutConstraintSpec.swift new file mode 100644 index 0000000000..ea1159cc44 --- /dev/null +++ b/ReactiveCocoaTests/Shared/NSLayoutConstraintSpec.swift @@ -0,0 +1,36 @@ +import ReactiveSwift +import ReactiveCocoa +import Quick +import Nimble +import enum Result.NoError + +class NSLayoutConstraintSpec: QuickSpec { + override func spec() { + var constraint: NSLayoutConstraint! + weak var _constraint: NSLayoutConstraint? + + beforeEach { + constraint = NSLayoutConstraint() + _constraint = constraint + } + + afterEach { + constraint = nil + expect(_constraint).to(beNil()) + } + + it("should accept changes from bindings to its constant") { + expect(constraint.constant).to(equal(0.0)) + + let (pipeSignal, observer) = Signal.pipe() + + constraint.reactive.constant <~ pipeSignal + + observer.send(value: 5.0) + expect(constraint.constant).to(equal(5.0)) + + observer.send(value: -3.0) + expect(constraint.constant).to(equal(-3.0)) + } + } +} From 857ee63f4f16cb4dcdf77bc739328a160f28f401 Mon Sep 17 00:00:00 2001 From: Eimantas Vaiciunas Date: Wed, 2 Nov 2016 20:03:29 +0200 Subject: [PATCH 0518/1028] Document the reactive `constant` --- ReactiveCocoa/Shared/NSLayoutConstraint.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/ReactiveCocoa/Shared/NSLayoutConstraint.swift b/ReactiveCocoa/Shared/NSLayoutConstraint.swift index 506a3457e3..5476c865b3 100644 --- a/ReactiveCocoa/Shared/NSLayoutConstraint.swift +++ b/ReactiveCocoa/Shared/NSLayoutConstraint.swift @@ -8,6 +8,7 @@ import UIKit extension Reactive where Base: NSLayoutConstraint { + /// Sets the constant. public var constant: BindingTarget { return makeBindingTarget(action: { $0.constant = $1 }) } From 79c05a4caa83ec9ec70102e5f0e749346819b25f Mon Sep 17 00:00:00 2001 From: Eimantas Vaiciunas Date: Thu, 3 Nov 2016 15:50:46 +0200 Subject: [PATCH 0519/1028] Add reactive `image` and `title` to `UIBarItem` --- ReactiveCocoa/UIKit/UIBarItem.swift | 10 ++++++++ .../UIKit/UIBarButtonItemSpec.swift | 25 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/ReactiveCocoa/UIKit/UIBarItem.swift b/ReactiveCocoa/UIKit/UIBarItem.swift index a1ca833efe..54a3f93e51 100644 --- a/ReactiveCocoa/UIKit/UIBarItem.swift +++ b/ReactiveCocoa/UIKit/UIBarItem.swift @@ -6,4 +6,14 @@ extension Reactive where Base: UIBarItem { public var isEnabled: BindingTarget { return makeBindingTarget { $0.isEnabled = $1 } } + + /// Sets image of bar item. + public var image: BindingTarget { + return makeBindingTarget { $0.image = $1 } + } + + /// Sets the title of bar item. + public var title: BindingTarget { + return makeBindingTarget { $0.title = $1 } + } } diff --git a/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift b/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift index 49b8e0df0b..50fd147a95 100644 --- a/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift @@ -35,5 +35,30 @@ class UIBarButtonItemSpec: QuickSpec { observer.send(value: true) expect(barButtonItem.isEnabled) == true } + + it("should accept changes from bindings to its title") { + let (pipeSignal, observer) = Signal.pipe() + barButtonItem.reactive.title <~ SignalProducer(signal: pipeSignal) + + observer.send(value: "title") + expect(barButtonItem.title).to(equal("title")) + + observer.send(value: nil) + expect(barButtonItem.title).to(beNil()) + } + + it("should accept changes from bindings to its image") { + let (pipeSignal, observer) = Signal.pipe() + barButtonItem.reactive.image <~ SignalProducer(signal: pipeSignal) + + let image = UIImage() + expect(image).notTo(beNil()) + + observer.send(value: image) + expect(barButtonItem.image).to(equal(image)) + + observer.send(value: nil) + expect(barButtonItem.image).to(beNil()) + } } } From eebe70c1a697ba65c9bd531dc0e1755c2185907d Mon Sep 17 00:00:00 2001 From: Eimantas Vaiciunas Date: Thu, 3 Nov 2016 17:15:26 +0200 Subject: [PATCH 0520/1028] Use `==` for equality checks in `UIBarItem` specs --- ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift b/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift index 50fd147a95..db79250ea9 100644 --- a/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift @@ -41,7 +41,7 @@ class UIBarButtonItemSpec: QuickSpec { barButtonItem.reactive.title <~ SignalProducer(signal: pipeSignal) observer.send(value: "title") - expect(barButtonItem.title).to(equal("title")) + expect(barButtonItem.title) == "title" observer.send(value: nil) expect(barButtonItem.title).to(beNil()) @@ -55,7 +55,7 @@ class UIBarButtonItemSpec: QuickSpec { expect(image).notTo(beNil()) observer.send(value: image) - expect(barButtonItem.image).to(equal(image)) + expect(barButtonItem.image) == image observer.send(value: nil) expect(barButtonItem.image).to(beNil()) From cbd88a2bc469dbc28a16112724a131d98b4a7002 Mon Sep 17 00:00:00 2001 From: Eimantas Vaiciunas Date: Thu, 3 Nov 2016 19:34:22 +0200 Subject: [PATCH 0521/1028] =?UTF-8?q?Change=20equality=20check=20to=20`?= =?UTF-8?q?=E2=89=88`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ReactiveCocoaTests/Shared/NSLayoutConstraintSpec.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoaTests/Shared/NSLayoutConstraintSpec.swift b/ReactiveCocoaTests/Shared/NSLayoutConstraintSpec.swift index ea1159cc44..4c4bac5f7d 100644 --- a/ReactiveCocoaTests/Shared/NSLayoutConstraintSpec.swift +++ b/ReactiveCocoaTests/Shared/NSLayoutConstraintSpec.swift @@ -27,10 +27,10 @@ class NSLayoutConstraintSpec: QuickSpec { constraint.reactive.constant <~ pipeSignal observer.send(value: 5.0) - expect(constraint.constant).to(equal(5.0)) + expect(constraint.constant) ≈ 5.0 observer.send(value: -3.0) - expect(constraint.constant).to(equal(-3.0)) + expect(constraint.constant) ≈ -3.0 } } } From 6d6c8386e7a9955bebea73345f0cf1b3409506c7 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Thu, 3 Nov 2016 22:21:44 -0400 Subject: [PATCH 0522/1028] Move contents into the CHANGELOG --- CHANGELOG.md | 67 +++++++++++++++++++++++++-- Documentation/RAC5MigrationGuide.md | 70 ----------------------------- 2 files changed, 63 insertions(+), 74 deletions(-) delete mode 100644 Documentation/RAC5MigrationGuide.md diff --git a/CHANGELOG.md b/CHANGELOG.md index bd49c774c7..abb439c25c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,65 @@ +# 5.0 + +## Repository Split +In version 5.0, we split ReactiveCocoa into multiple repositories for reasons explained in the sections below. The following should help you get started with choosing the repositories you require: + +**If you’re using only the Swift APIs**, you can continue to include ReactiveCocoa. You will also need to link against [ReactiveSwift][], which is now a dependency of ReactiveCocoa. + +**If you’re using only the Objective-C APIs**, you can switch to using [ReactiveObjC][]. It has all the Obj-C code from RAC 2. + +**If you’re using both the Swift and Objective-C APIs**, you likely require both ReactiveCocoa and [ReactiveObjCBridge][], which depend on [ReactiveSwift][] and [ReactiveObjC][]. + +### ReactiveCocoa +The ReactiveCocoa library is newly focused on Swift and the UI layers of Apple’s platforms, building on the work of [Rex](https://github.com/neilpa/Rex). + +Reactive programming provides significant benefit in UI programming. RAC 3 and 4 focused on building out the new core Swift API. But we feel that those APIs have matured and it’s time for RAC-friendly extensions to AppKit and UIKit. + +### ReactiveSwift +The core, platform-independent Swift APIs have been extracted to a new framework, [ReactiveSwift][]. + +As Swift continues to grow as a language and a platform, we hope that it will expand beyond Cocoa and Apple’s platforms. Separating the Swift code makes it possible to use the reactive paradigm on other platforms. + +[ReactiveSwift]: https://github.com/ReactiveCocoa/ReactiveSwift + +### ReactiveObjC +The 3.x and 4.x releases of ReactiveCocoa included the Objective-C code from ReactiveCocoa 2.x. That code has been moved to [ReactiveObjC][] because: + + 1. It’s independent of the Swift code + 2. It has a separate user base + 3. It has a separate group of maintainers + +We hope that this move will enable continued support of ReactiveObjC. + +[ReactiveObjC]: https://github.com/ReactiveCocoa/ReactiveObjC + +### ReactiveObjCBridge +Moving the Swift and Objective-C APIs to separate repositories meant that a new home was needed for the bridging layer between the two. + +This bridge is an important tool for users that are working in mixed-language code bases. Whether you are slowly adding Swift to a mature product built with the ReactiveCocoa Objective-C APIs, or looking to adopt ReactiveCocoa in a mixed code base, the bridge is required to communicate between Swift and Objective-C code. + +[ReactiveObjCBridge]: https://github.com/ReactiveCocoa/ReactiveObjCBridge + +## API Names + +We mostly adjusted the ReactiveCocoa API to follow the [Swift 3 API Design Guidelines](https://swift.org/blog/swift-3-api-design/), or to match the Cocoa and Foundation API changes that came with Swift 3 and the latest platform SDKs. + +Lots has changed, but if you're already migrating to Swift 3 then that should not come as a surprise. Fortunately for you, we've provided annotations in the source that should help you while using the Swift 3 migration tool that ships with Xcode 8. When changes aren't picked up by the migrator, they are often provided for you as Fix-Its. + +**Tip:** You can apply all the suggested fix-its in the current scope by choosing Editor > Fix All In Scope from the main menu in Xcode, or by using the associated keyboard shortcut. + +## Signal + +### Lifetime Semantics + +## SignalProducer + +## Properties + +## Atomic + # 4.0 -If you’re new to the Swift API and migrating from RAC 2, start with the [3.0 changes](#30). This section only covers the differences between `3.0` and `4.0`. +If you’re new to the Swift API and migrating from RAC 2, start with the [3.0 changes](#30). This section only covers the differences between `3.0` and `4.0`. Just like in `RAC 3`, because Objective-C is still in widespread use, 99% of `RAC 2.x` code will continue to work under `RAC 4.0` without any changes. That is, `RAC 2.x` primitives are still available in `RAC 4.0`. @@ -17,9 +76,9 @@ Previously the custom `|>` was required to enable chaining global functions with ```swift /// RAC 3 -signal - |> filter { $0 % 2 == 0 } - |> map { $0 * $0 } +signal + |> filter { $0 % 2 == 0 } + |> map { $0 * $0 } |> observe { print($0) } /// RAC 4 diff --git a/Documentation/RAC5MigrationGuide.md b/Documentation/RAC5MigrationGuide.md deleted file mode 100644 index 0cd724b230..0000000000 --- a/Documentation/RAC5MigrationGuide.md +++ /dev/null @@ -1,70 +0,0 @@ -# ReactiveCocoa 5.0 Migration Guide - -* [Repository Split](#repository-split) - * [ReactiveCocoa](#reactivecocoa) - * [ReactiveSwift](#reactiveswift) - * [ReactiveObjC](#reactiveobjc) - * [ReactiveObjCBridge](#reactiveobjcbridge) -* [API Names](#api-names) -* [Signal](#signal) - * [Lifetime Semantics](#lifetime-semantics) -* [SignalProducer](#signalproducer) -* [Properties](#properties) -* [Atomic](#atomic) - -## Repository Split -In version 5.0, we split ReactiveCocoa into multiple repositories for reasons explained in the sections below. The following should help you get started with choosing the repositories you require: - -**If you’re using only the Swift APIs**, you can continue to include ReactiveCocoa. You will also need to link against [ReactiveSwift][], which is now a dependency of ReactiveCocoa. - -**If you’re using only the Objective-C APIs**, you can switch to using [ReactiveObjC][]. It has all the Obj-C code from RAC 2. - -**If you’re using both the Swift and Objective-C APIs**, you likely require both ReactiveCocoa and [ReactiveObjCBridge][], which depend on [ReactiveSwift][] and [ReactiveObjC][]. - -### ReactiveCocoa -The ReactiveCocoa library is newly focused on Swift and the UI layers of Apple’s platforms, building on the work of [Rex](https://github.com/neilpa/Rex). - -Reactive programming provides significant benefit in UI programming. RAC 3 and 4 focused on building out the new core Swift API. But we feel that those APIs have matured and it’s time for RAC-friendly extensions to AppKit and UIKit. - -### ReactiveSwift -The core, platform-independent Swift APIs have been extracted to a new framework, [ReactiveSwift][]. - -As Swift continues to grow as a language and a platform, we hope that it will expand beyond Cocoa and Apple’s platforms. Separating the Swift code makes it possible to use the reactive paradigm on other platforms. - -[ReactiveSwift]: https://github.com/ReactiveCocoa/ReactiveSwift - -### ReactiveObjC -The 3.x and 4.x releases of ReactiveCocoa included the Objective-C code from ReactiveCocoa 2.x. That code has been moved to [ReactiveObjC][] because: - - 1. It’s independent of the Swift code - 2. It has a separate user base - 3. It has a separate group of maintainers - -We hope that this move will enable continued support of ReactiveObjC. - -[ReactiveObjC]: https://github.com/ReactiveCocoa/ReactiveObjC - -### ReactiveObjCBridge -Moving the Swift and Objective-C APIs to separate repositories meant that a new home was needed for the bridging layer between the two. - -This bridge is an important tool for users that are working in mixed-language code bases. Whether you are slowly adding Swift to a mature product built with the ReactiveCocoa Objective-C APIs, or looking to adopt ReactiveCocoa in a mixed code base, the bridge is required to communicate between Swift and Objective-C code. - -[ReactiveObjCBridge]: https://github.com/ReactiveCocoa/ReactiveObjCBridge - -## API Names - -We mostly adjusted the ReactiveCocoa API to follow the [Swift 3 API Design Guidelines](https://swift.org/blog/swift-3-api-design/), or to match the Cocoa and Foundation API changes that came with Swift 3 and the latest platform SDKs. - -Lots has changed, but if you're already migrating to Swift 3 then that should not come as a surprise. Fortunately for you, we've provided annotations in the source that should help you while using the Swift 3 migration tool that ships with Xcode 8. When changes aren't picked up by the migrator, they are often provided for you as Fix-Its. - -**Tip:** You can apply all the suggested fix-its in the current scope by choosing Editor > Fix All In Scope from the main menu in Xcode, or by using the associated keyboard shortcut. - -## Signal - -### Lifetime Semantics - -## SignalProducer - -## Properties - -## Atomic From 1c8974e0ee2833f16620fdd292e5672fbf0025ce Mon Sep 17 00:00:00 2001 From: Eimantas Vaiciunas Date: Fri, 4 Nov 2016 07:59:54 +0200 Subject: [PATCH 0523/1028] Use trailing closure syntax --- ReactiveCocoa/Shared/NSLayoutConstraint.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa/Shared/NSLayoutConstraint.swift b/ReactiveCocoa/Shared/NSLayoutConstraint.swift index 5476c865b3..5fa78fb1d3 100644 --- a/ReactiveCocoa/Shared/NSLayoutConstraint.swift +++ b/ReactiveCocoa/Shared/NSLayoutConstraint.swift @@ -10,7 +10,7 @@ extension Reactive where Base: NSLayoutConstraint { /// Sets the constant. public var constant: BindingTarget { - return makeBindingTarget(action: { $0.constant = $1 }) + return makeBindingTarget { $0.constant = $1 } } } From 80e6d01306fd9f11eaddee6e5ce664d68bb49fc5 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Fri, 4 Nov 2016 22:02:34 +0900 Subject: [PATCH 0524/1028] [UIViewSpec] Consistently use `==` operator over `to(equal(...))` --- ReactiveCocoaTests/UIKit/UIViewSpec.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ReactiveCocoaTests/UIKit/UIViewSpec.swift b/ReactiveCocoaTests/UIKit/UIViewSpec.swift index de884913e4..20d876646a 100644 --- a/ReactiveCocoaTests/UIKit/UIViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIViewSpec.swift @@ -69,13 +69,13 @@ class UIViewSpec: QuickSpec { view.reactive.backgroundColor <~ SignalProducer(signal: pipeSignal) observer.send(value: .yellow) - expect(view.backgroundColor).to(equal(UIColor.yellow)) + expect(view.backgroundColor) == .yellow observer.send(value: .green) - expect(view.backgroundColor).to(equal(UIColor.green)) + expect(view.backgroundColor) == .green observer.send(value: .red) - expect(view.backgroundColor).to(equal(UIColor.red)) + expect(view.backgroundColor) == .red } } } From 0e0027723a69633430a3392b394601181b54dee8 Mon Sep 17 00:00:00 2001 From: Eimantas Vaiciunas Date: Fri, 4 Nov 2016 15:29:33 +0200 Subject: [PATCH 0525/1028] Update podspec file --- ReactiveCocoa.podspec | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 236da9f74e..9f27caa863 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -17,10 +17,10 @@ Pod::Spec.new do |s| s.source = { :git => "https://github.com/ReactiveCocoa/ReactiveCocoa.git", :tag => "#{s.version}" } s.source_files = "ReactiveCocoa/*.{swift,h,m}" s.private_header_files = "ReactiveCocoa/RACObjCRuntimeUtilities.h" - s.osx.source_files = "ReactiveCocoa/AppKit/*.{swift}" - s.ios.source_files = "ReactiveCocoa/UIKit/*.{swift}" + s.osx.source_files = "ReactiveCocoa/AppKit/*.{swift}", "ReactiveCocoa/Shared/*.{swift}" + s.ios.source_files = "ReactiveCocoa/UIKit/*.{swift}", "ReactiveCocoa/Shared/*.{swift}" s.tvos.source_files = "ReactiveCocoa/UIKit/*.{swift}" - s.tvos.exclude_files = "ReactiveCocoa/UIKit/*{UIDatePicker,UISwitch}*" + s.tvos.exclude_files = "ReactiveCocoa/UIKit/*{UIDatePicker,UISwitch}*", "ReactiveCocoa/Shared/*.{swift}" s.module_map = "ReactiveCocoa/module.modulemap" s.dependency 'ReactiveSwift', '~> 1.0.0-alpha.3' From 64043322b8d6f997245b9071424f5fbb7f3c76c2 Mon Sep 17 00:00:00 2001 From: Eimantas Vaiciunas Date: Fri, 4 Nov 2016 15:41:23 +0200 Subject: [PATCH 0526/1028] Update podspec for tvos and watchos platforms --- ReactiveCocoa.podspec | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 9f27caa863..b997325028 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -19,8 +19,9 @@ Pod::Spec.new do |s| s.private_header_files = "ReactiveCocoa/RACObjCRuntimeUtilities.h" s.osx.source_files = "ReactiveCocoa/AppKit/*.{swift}", "ReactiveCocoa/Shared/*.{swift}" s.ios.source_files = "ReactiveCocoa/UIKit/*.{swift}", "ReactiveCocoa/Shared/*.{swift}" - s.tvos.source_files = "ReactiveCocoa/UIKit/*.{swift}" - s.tvos.exclude_files = "ReactiveCocoa/UIKit/*{UIDatePicker,UISwitch}*", "ReactiveCocoa/Shared/*.{swift}" + s.tvos.source_files = "ReactiveCocoa/UIKit/*.{swift}", "ReactiveCocoa/Shared/*.{swift}" + s.tvos.exclude_files = "ReactiveCocoa/UIKit/*{UIDatePicker,UISwitch}*" + s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_map = "ReactiveCocoa/module.modulemap" s.dependency 'ReactiveSwift', '~> 1.0.0-alpha.3' From db2421117791e38a9e7749f0cec157461b312d14 Mon Sep 17 00:00:00 2001 From: Eimantas Vaiciunas Date: Fri, 4 Nov 2016 15:45:55 +0200 Subject: [PATCH 0527/1028] Add `Shared` files everywhere Exclude for watchos. --- ReactiveCocoa.podspec | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index b997325028..e516cd1a15 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -15,11 +15,11 @@ Pod::Spec.new do |s| s.watchos.deployment_target = "2.0" s.source = { :git => "https://github.com/ReactiveCocoa/ReactiveCocoa.git", :tag => "#{s.version}" } - s.source_files = "ReactiveCocoa/*.{swift,h,m}" + s.source_files = "ReactiveCocoa/*.{swift,h,m}", "ReactiveCocoa/Shared/*.{swift}" s.private_header_files = "ReactiveCocoa/RACObjCRuntimeUtilities.h" - s.osx.source_files = "ReactiveCocoa/AppKit/*.{swift}", "ReactiveCocoa/Shared/*.{swift}" - s.ios.source_files = "ReactiveCocoa/UIKit/*.{swift}", "ReactiveCocoa/Shared/*.{swift}" - s.tvos.source_files = "ReactiveCocoa/UIKit/*.{swift}", "ReactiveCocoa/Shared/*.{swift}" + s.osx.source_files = "ReactiveCocoa/AppKit/*.{swift}" + s.ios.source_files = "ReactiveCocoa/UIKit/*.{swift}" + s.tvos.source_files = "ReactiveCocoa/UIKit/*.{swift}" s.tvos.exclude_files = "ReactiveCocoa/UIKit/*{UIDatePicker,UISwitch}*" s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_map = "ReactiveCocoa/module.modulemap" From 7c06126f6dc16d0181e0ee2185d6cfcd8d97c710 Mon Sep 17 00:00:00 2001 From: Anders Date: Sat, 5 Nov 2016 01:53:11 +0100 Subject: [PATCH 0528/1028] Updated Nimble to 5.1.1. --- Cartfile.private | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/Nimble | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cartfile.private b/Cartfile.private index 36e1c8b92d..c7331aaa2a 100644 --- a/Cartfile.private +++ b/Cartfile.private @@ -1,3 +1,3 @@ github "jspahrsummers/xcconfigs" "3d9d996" github "Quick/Quick" ~> 0.10 -github "Quick/Nimble" ~> 5.0 +github "Quick/Nimble" ~> 5.1 diff --git a/Cartfile.resolved b/Cartfile.resolved index 08a40e7bde..718852967c 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,4 +1,4 @@ -github "Quick/Nimble" "v5.0.0" +github "Quick/Nimble" "v5.1.1" github "Quick/Quick" "v0.10.0" github "antitypical/Result" "3.0.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" diff --git a/Carthage/Checkouts/Nimble b/Carthage/Checkouts/Nimble index 0209419661..3720e6b0f6 160000 --- a/Carthage/Checkouts/Nimble +++ b/Carthage/Checkouts/Nimble @@ -1 +1 @@ -Subproject commit 0209419661b6acceff45cd63984596f2e9eea517 +Subproject commit 3720e6b0f6de6d6435f79f8a174fb4bb92df5dc4 From 138a3695d5b2316d1529b11c9312de3c058741d0 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Sun, 6 Nov 2016 11:31:34 -0500 Subject: [PATCH 0529/1028] Write about Signal lifetime semantics --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index abb439c25c..890a62fd1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,8 @@ Lots has changed, but if you're already migrating to Swift 3 then that should no ### Lifetime Semantics +Prior to RAC 5.0, `Signal`s lived and continued to emit values (and side effects) until they completed. This was very confusing, even for RAC veterans. So [changes have been made](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/2959) to the lifetime semantics. `Signal`s now live and continue to emit events while either (a) they have observers or (b) they are retained. This clears up a number of unexpected cases and makes `Signal`s much less dangerous. + ## SignalProducer ## Properties From 240a34b6021f99bf8c5c00a4b19cfd5a9e712ca1 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Sun, 6 Nov 2016 11:34:59 -0500 Subject: [PATCH 0530/1028] Add a note about properties --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 890a62fd1f..04e6bdc9d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,6 +57,8 @@ Prior to RAC 5.0, `Signal`s lived and continued to emit values (and side effects ## Properties +Properties are now composable! They have many of the same operators as `Signal` and `SignalProducer`: `map`, `filter`, `combineLatest`, `zip`, `flatten`, etc. + ## Atomic # 4.0 From b5a69cbe8724ae725d6772c6e2fad8fbc0d0e328 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Sun, 6 Nov 2016 11:38:17 -0500 Subject: [PATCH 0531/1028] Add a note about Atomic --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04e6bdc9d0..75f997fc7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,8 @@ Properties are now composable! They have many of the same operators as `Signal` ## Atomic +The `Atomic.modify` closure now takes an `inout` instead of relying on a return value. This provides a minor speed boost, but also makes it possible to return a separate value from the closure to be used after the lock is released. + # 4.0 If you’re new to the Swift API and migrating from RAC 2, start with the [3.0 changes](#30). This section only covers the differences between `3.0` and `4.0`. From a8e73535cf7ddb9837bc47cfdf5bfde220ba525b Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Sat, 5 Nov 2016 12:46:03 -0400 Subject: [PATCH 0532/1028] Add UITableView.reactive.reloadData binding target --- ReactiveCocoa.xcodeproj/project.pbxproj | 18 +++++- ReactiveCocoa/UIKit/UITableView.swift | 8 +++ .../UIKit/UITableViewSpec.swift | 63 +++++++++++++++++++ 3 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 ReactiveCocoa/UIKit/UITableView.swift create mode 100644 ReactiveCocoaTests/UIKit/UITableViewSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 7f02d514fb..3e663738d1 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -14,6 +14,10 @@ 4A0E11001D2A92720065D310 /* NSObject+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */; }; 4A0E11011D2A92720065D310 /* NSObject+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */; }; 4A0E11021D2A92720065D310 /* NSObject+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */; }; + 4ABEFE1F1DCFCEF60066A8C2 /* UITableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFDE11DCFC8560066A8C2 /* UITableView.swift */; }; + 4ABEFE201DCFCEF80066A8C2 /* UITableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFDE11DCFC8560066A8C2 /* UITableView.swift */; }; + 4ABEFE211DCFCF090066A8C2 /* UITableViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFDE31DCFCCD70066A8C2 /* UITableViewSpec.swift */; }; + 4ABEFE221DCFCF0A0066A8C2 /* UITableViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFDE31DCFCCD70066A8C2 /* UITableViewSpec.swift */; }; 538DCB791DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */; }; 538DCB7A1DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */; }; 538DCB7B1DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */; }; @@ -103,7 +107,6 @@ 9A6AAA0E1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; 9A6AAA0F1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; 9A6AAA101DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; - 9A9A129A1DC7A97100D10223 /* UIGestureRecognizerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4191394B1DBA002C0043C9D1 /* UIGestureRecognizerSpec.swift */; }; 9A6AAA191DB808A80013AAEA /* RACObjCRuntimeUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A1D06751D9415FB00ACF44C /* RACObjCRuntimeUtilities.h */; settings = {ATTRIBUTES = (Private, ); }; }; 9A6AAA1A1DB808A80013AAEA /* RACObjCRuntimeUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A1D06751D9415FB00ACF44C /* RACObjCRuntimeUtilities.h */; settings = {ATTRIBUTES = (Private, ); }; }; 9A6AAA1B1DB808A90013AAEA /* RACObjCRuntimeUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A1D06751D9415FB00ACF44C /* RACObjCRuntimeUtilities.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -114,6 +117,7 @@ 9A6AAA2B1DB8F85C0013AAEA /* ReusableComponentsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA271DB8F7EB0013AAEA /* ReusableComponentsSpec.swift */; }; 9A6AAA2E1DB903A20013AAEA /* ReusableComponentsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA291DB8F7F10013AAEA /* ReusableComponentsSpec.swift */; }; 9A6AAA2F1DB903A40013AAEA /* ReusableComponentsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA291DB8F7F10013AAEA /* ReusableComponentsSpec.swift */; }; + 9A9A129A1DC7A97100D10223 /* UIGestureRecognizerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4191394B1DBA002C0043C9D1 /* UIGestureRecognizerSpec.swift */; }; 9A9DFEE51DA7B5500039EE1B /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; 9A9DFEE91DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; 9A9DFEEA1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; @@ -247,6 +251,8 @@ 419139431DB910570043C9D1 /* UIGestureRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIGestureRecognizer.swift; sourceTree = ""; }; 4191394B1DBA002C0043C9D1 /* UIGestureRecognizerSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIGestureRecognizerSpec.swift; sourceTree = ""; }; 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Lifetime.swift"; sourceTree = ""; }; + 4ABEFDE11DCFC8560066A8C2 /* UITableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableView.swift; sourceTree = ""; }; + 4ABEFDE31DCFCCD70066A8C2 /* UITableViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewSpec.swift; sourceTree = ""; }; 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSLayoutConstraint.swift; sourceTree = ""; }; 538DCB7C1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSLayoutConstraintSpec.swift; sourceTree = ""; }; 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -473,15 +479,16 @@ 9A1D05EF1D93E9F100ACF44C /* UIButton.swift */, 9A1D05F11D93E9F100ACF44C /* UIControl.swift */, 9A1D05F21D93E9F100ACF44C /* UIDatePicker.swift */, + 419139431DB910570043C9D1 /* UIGestureRecognizer.swift */, 9A1D05F31D93E9F100ACF44C /* UIImageView.swift */, 9A1D05F41D93E9F100ACF44C /* UILabel.swift */, 9A1D05F51D93E9F100ACF44C /* UIProgressView.swift */, 9A1D05F61D93E9F100ACF44C /* UISegmentedControl.swift */, 9A1D05F71D93E9F100ACF44C /* UISwitch.swift */, + 4ABEFDE11DCFC8560066A8C2 /* UITableView.swift */, 9A1D05FA1D93E9F100ACF44C /* UITextField.swift */, 9A1D05FB1D93E9F100ACF44C /* UITextView.swift */, 9A1D05FC1D93E9F100ACF44C /* UIView.swift */, - 419139431DB910570043C9D1 /* UIGestureRecognizer.swift */, ); path = UIKit; sourceTree = ""; @@ -496,15 +503,16 @@ 9A1D06281D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift */, 9A1D06291D93EA7E00ACF44C /* UIControlSpec.swift */, 9A1D062A1D93EA7E00ACF44C /* UIDatePickerSpec.swift */, + 4191394B1DBA002C0043C9D1 /* UIGestureRecognizerSpec.swift */, 9A1D062B1D93EA7E00ACF44C /* UIImageViewSpec.swift */, 9A1D062C1D93EA7E00ACF44C /* UILabelSpec.swift */, 9A1D062D1D93EA7E00ACF44C /* UIProgressViewSpec.swift */, 9A1D062E1D93EA7E00ACF44C /* UISegmentedControlSpec.swift */, 9A1D062F1D93EA7E00ACF44C /* UISwitchSpec.swift */, + 4ABEFDE31DCFCCD70066A8C2 /* UITableViewSpec.swift */, 9A1D06321D93EA7E00ACF44C /* UITextFieldSpec.swift */, 9A1D06331D93EA7E00ACF44C /* UITextViewSpec.swift */, 9A1D06351D93EA7E00ACF44C /* UIViewSpec.swift */, - 4191394B1DBA002C0043C9D1 /* UIGestureRecognizerSpec.swift */, ); path = UIKit; sourceTree = ""; @@ -997,6 +1005,7 @@ 9ADE4A7F1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, 9A1D06201D93EA0100ACF44C /* UITextView.swift in Sources */, 9A6AAA241DB8F51C0013AAEA /* ReusableComponents.swift in Sources */, + 4ABEFE201DCFCEF80066A8C2 /* UITableView.swift in Sources */, 9A1D06181D93EA0100ACF44C /* UIImageView.swift in Sources */, 9AD0F06D1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, 9A1D06211D93EA0100ACF44C /* UIView.swift in Sources */, @@ -1045,6 +1054,7 @@ 9ADFE5A31DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */, 9A1D06411D93EA7E00ACF44C /* UIControlSpec.swift in Sources */, 9A6AAA101DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */, + 4ABEFE221DCFCF0A0066A8C2 /* UITableViewSpec.swift in Sources */, 9A1D06491D93EA7E00ACF44C /* UIProgressViewSpec.swift in Sources */, BEE020661D637B0000DF261F /* TestError.swift in Sources */, 9A1D06531D93EA7E00ACF44C /* UITextFieldSpec.swift in Sources */, @@ -1135,6 +1145,7 @@ 9ADE4A921DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, 9ADFE5A61DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */, 9AD0F06B1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, + 4ABEFE1F1DCFCEF60066A8C2 /* UITableView.swift in Sources */, 9A1D06081D93EA0000ACF44C /* UIProgressView.swift in Sources */, 538DCB7A1DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */, 9A1D06001D93EA0000ACF44C /* UIBarButtonItem.swift in Sources */, @@ -1168,6 +1179,7 @@ 9A1D06381D93EA7E00ACF44C /* UIBarButtonItemSpec.swift in Sources */, 9A1E72BB1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, 9A6AAA2E1DB903A20013AAEA /* ReusableComponentsSpec.swift in Sources */, + 4ABEFE211DCFCF090066A8C2 /* UITableViewSpec.swift in Sources */, 9A1D06401D93EA7E00ACF44C /* UIControlSpec.swift in Sources */, 9A1D06481D93EA7E00ACF44C /* UIProgressViewSpec.swift in Sources */, CD8401841CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, diff --git a/ReactiveCocoa/UIKit/UITableView.swift b/ReactiveCocoa/UIKit/UITableView.swift new file mode 100644 index 0000000000..96f2bb952a --- /dev/null +++ b/ReactiveCocoa/UIKit/UITableView.swift @@ -0,0 +1,8 @@ +import ReactiveSwift +import UIKit + +extension Reactive where Base: UITableView { + public var reloadData: BindingTarget<()> { + return makeBindingTarget { base, _ in base.reloadData() } + } +} diff --git a/ReactiveCocoaTests/UIKit/UITableViewSpec.swift b/ReactiveCocoaTests/UIKit/UITableViewSpec.swift new file mode 100644 index 0000000000..c7c0203b4b --- /dev/null +++ b/ReactiveCocoaTests/UIKit/UITableViewSpec.swift @@ -0,0 +1,63 @@ +import Quick +import Nimble +import ReactiveCocoa +import ReactiveSwift +import Result +import UIKit + +final class UITableViewSpec: QuickSpec { + override func spec() { + var tableView: TestTableView! + + beforeEach { + tableView = TestTableView() + } + + describe("reloadData") { + var bindingSignal: Signal<(), NoError>! + var bindingObserver: Signal<(), NoError>.Observer! + + var reloadDataCount = 0 + + beforeEach { + let (signal, observer) = Signal<(), NoError>.pipe() + (bindingSignal, bindingObserver) = (signal, observer) + + reloadDataCount = 0 + + tableView.reloadDataSignal.observeValues { + reloadDataCount += 1 + } + } + + it("invokes reloadData whenever the bound signal sends a value") { + tableView.reactive.reloadData <~ bindingSignal + + bindingObserver.send(value: ()) + bindingObserver.send(value: ()) + + expect(reloadDataCount) == 2 + } + } + } +} + +private final class TestTableView: UITableView { + let reloadDataSignal: Signal<(), NoError> + private let reloadDataObserver: Signal<(), NoError>.Observer + + override init(frame: CGRect, style: UITableViewStyle) { + (reloadDataSignal, reloadDataObserver) = Signal.pipe() + super.init(frame: frame, style: style) + } + + required init?(coder aDecoder: NSCoder) { + (reloadDataSignal, reloadDataObserver) = Signal.pipe() + super.init(coder: aDecoder) + } + + override func reloadData() { + super.reloadData() + reloadDataObserver.send(value: ()) + } +} From 39e1c89ea53f609b2ce5d6cf1e572d632d67d500 Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Sat, 5 Nov 2016 12:54:41 -0400 Subject: [PATCH 0533/1028] Add UICollectionView.reactive.reloadData binding target --- ReactiveCocoa.xcodeproj/project.pbxproj | 12 ++++ ReactiveCocoa/UIKit/UICollectionView.swift | 8 +++ .../UIKit/UICollectionViewSpec.swift | 63 +++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 ReactiveCocoa/UIKit/UICollectionView.swift create mode 100644 ReactiveCocoaTests/UIKit/UICollectionViewSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 3e663738d1..3b10b5062b 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -18,6 +18,10 @@ 4ABEFE201DCFCEF80066A8C2 /* UITableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFDE11DCFC8560066A8C2 /* UITableView.swift */; }; 4ABEFE211DCFCF090066A8C2 /* UITableViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFDE31DCFCCD70066A8C2 /* UITableViewSpec.swift */; }; 4ABEFE221DCFCF0A0066A8C2 /* UITableViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFDE31DCFCCD70066A8C2 /* UITableViewSpec.swift */; }; + 4ABEFE251DCFCF630066A8C2 /* UICollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE231DCFCF5C0066A8C2 /* UICollectionView.swift */; }; + 4ABEFE261DCFCF640066A8C2 /* UICollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE231DCFCF5C0066A8C2 /* UICollectionView.swift */; }; + 4ABEFE281DCFCFA90066A8C2 /* UICollectionViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE271DCFCFA90066A8C2 /* UICollectionViewSpec.swift */; }; + 4ABEFE291DCFCFA90066A8C2 /* UICollectionViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE271DCFCFA90066A8C2 /* UICollectionViewSpec.swift */; }; 538DCB791DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */; }; 538DCB7A1DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */; }; 538DCB7B1DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */; }; @@ -253,6 +257,8 @@ 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Lifetime.swift"; sourceTree = ""; }; 4ABEFDE11DCFC8560066A8C2 /* UITableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableView.swift; sourceTree = ""; }; 4ABEFDE31DCFCCD70066A8C2 /* UITableViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewSpec.swift; sourceTree = ""; }; + 4ABEFE231DCFCF5C0066A8C2 /* UICollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UICollectionView.swift; sourceTree = ""; }; + 4ABEFE271DCFCFA90066A8C2 /* UICollectionViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UICollectionViewSpec.swift; sourceTree = ""; }; 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSLayoutConstraint.swift; sourceTree = ""; }; 538DCB7C1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSLayoutConstraintSpec.swift; sourceTree = ""; }; 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -477,6 +483,7 @@ 9A1D05ED1D93E9F100ACF44C /* UIBarButtonItem.swift */, 9A1D05EE1D93E9F100ACF44C /* UIBarItem.swift */, 9A1D05EF1D93E9F100ACF44C /* UIButton.swift */, + 4ABEFE231DCFCF5C0066A8C2 /* UICollectionView.swift */, 9A1D05F11D93E9F100ACF44C /* UIControl.swift */, 9A1D05F21D93E9F100ACF44C /* UIDatePicker.swift */, 419139431DB910570043C9D1 /* UIGestureRecognizer.swift */, @@ -500,6 +507,7 @@ 9A1D06241D93EA7E00ACF44C /* UIActivityIndicatorViewSpec.swift */, 9A1D06251D93EA7E00ACF44C /* UIBarButtonItemSpec.swift */, 9A1D06261D93EA7E00ACF44C /* UIButtonSpec.swift */, + 4ABEFE271DCFCFA90066A8C2 /* UICollectionViewSpec.swift */, 9A1D06281D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift */, 9A1D06291D93EA7E00ACF44C /* UIControlSpec.swift */, 9A1D062A1D93EA7E00ACF44C /* UIDatePickerSpec.swift */, @@ -1002,6 +1010,7 @@ files = ( 9A1D06131D93EA0100ACF44C /* UIBarItem.swift in Sources */, 9A1D061F1D93EA0100ACF44C /* UITextField.swift in Sources */, + 4ABEFE261DCFCF640066A8C2 /* UICollectionView.swift in Sources */, 9ADE4A7F1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, 9A1D06201D93EA0100ACF44C /* UITextView.swift in Sources */, 9A6AAA241DB8F51C0013AAEA /* ReusableComponents.swift in Sources */, @@ -1056,6 +1065,7 @@ 9A6AAA101DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */, 4ABEFE221DCFCF0A0066A8C2 /* UITableViewSpec.swift in Sources */, 9A1D06491D93EA7E00ACF44C /* UIProgressViewSpec.swift in Sources */, + 4ABEFE291DCFCFA90066A8C2 /* UICollectionViewSpec.swift in Sources */, BEE020661D637B0000DF261F /* TestError.swift in Sources */, 9A1D06531D93EA7E00ACF44C /* UITextFieldSpec.swift in Sources */, ); @@ -1123,6 +1133,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 4ABEFE251DCFCF630066A8C2 /* UICollectionView.swift in Sources */, 9A1D06011D93EA0000ACF44C /* UIBarItem.swift in Sources */, 9A1D060D1D93EA0000ACF44C /* UITextField.swift in Sources */, 9A6AAA231DB8F51C0013AAEA /* ReusableComponents.swift in Sources */, @@ -1164,6 +1175,7 @@ BFA6B94E1A7604D500C846D1 /* SignalProducerNimbleMatchers.swift in Sources */, B696FB821A7640C00075236D /* TestError.swift in Sources */, 9A1D064C1D93EA7E00ACF44C /* UISwitchSpec.swift in Sources */, + 4ABEFE281DCFCFA90066A8C2 /* UICollectionViewSpec.swift in Sources */, 9A1D06441D93EA7E00ACF44C /* UIImageViewSpec.swift in Sources */, 9A1D06541D93EA7E00ACF44C /* UITextViewSpec.swift in Sources */, 9A1D063A1D93EA7E00ACF44C /* UIButtonSpec.swift in Sources */, diff --git a/ReactiveCocoa/UIKit/UICollectionView.swift b/ReactiveCocoa/UIKit/UICollectionView.swift new file mode 100644 index 0000000000..67ea780693 --- /dev/null +++ b/ReactiveCocoa/UIKit/UICollectionView.swift @@ -0,0 +1,8 @@ +import ReactiveSwift +import UIKit + +extension Reactive where Base: UICollectionView { + public var reloadData: BindingTarget<()> { + return makeBindingTarget { base, _ in base.reloadData() } + } +} diff --git a/ReactiveCocoaTests/UIKit/UICollectionViewSpec.swift b/ReactiveCocoaTests/UIKit/UICollectionViewSpec.swift new file mode 100644 index 0000000000..9b5d50e468 --- /dev/null +++ b/ReactiveCocoaTests/UIKit/UICollectionViewSpec.swift @@ -0,0 +1,63 @@ +import Quick +import Nimble +import ReactiveCocoa +import ReactiveSwift +import Result +import UIKit + +final class UICollectionViewSpec: QuickSpec { + override func spec() { + var collectionView: TestCollectionView! + + beforeEach { + collectionView = TestCollectionView() + } + + describe("reloadData") { + var bindingSignal: Signal<(), NoError>! + var bindingObserver: Signal<(), NoError>.Observer! + + var reloadDataCount = 0 + + beforeEach { + let (signal, observer) = Signal<(), NoError>.pipe() + (bindingSignal, bindingObserver) = (signal, observer) + + reloadDataCount = 0 + + collectionView.reloadDataSignal.observeValues { + reloadDataCount += 1 + } + } + + it("invokes reloadData whenever the bound signal sends a value") { + collectionView.reactive.reloadData <~ bindingSignal + + bindingObserver.send(value: ()) + bindingObserver.send(value: ()) + + expect(reloadDataCount) == 2 + } + } + } +} + +private final class TestCollectionView: UICollectionView { + let reloadDataSignal: Signal<(), NoError> + private let reloadDataObserver: Signal<(), NoError>.Observer + + init() { + (reloadDataSignal, reloadDataObserver) = Signal.pipe() + super.init(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) + } + + required init?(coder aDecoder: NSCoder) { + (reloadDataSignal, reloadDataObserver) = Signal.pipe() + super.init(coder: aDecoder) + } + + override func reloadData() { + super.reloadData() + reloadDataObserver.send(value: ()) + } +} From 37386e20b4a587202bc6181d7bedc2266a8da9b4 Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Sat, 5 Nov 2016 12:59:09 -0400 Subject: [PATCH 0534/1028] Fix up path of AppKit group in ReactiveCocoaTests --- ReactiveCocoa.xcodeproj/project.pbxproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 3b10b5062b..33c6fd87c7 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -304,13 +304,13 @@ 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterceptingSpec.swift; sourceTree = ""; }; 9A6AAA221DB8F51C0013AAEA /* ReusableComponents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponents.swift; sourceTree = ""; }; 9A6AAA251DB8F5280013AAEA /* ReusableComponents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponents.swift; sourceTree = ""; }; - 9A6AAA271DB8F7EB0013AAEA /* ReusableComponentsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ReusableComponentsSpec.swift; path = AppKit/ReusableComponentsSpec.swift; sourceTree = ""; }; + 9A6AAA271DB8F7EB0013AAEA /* ReusableComponentsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponentsSpec.swift; sourceTree = ""; }; 9A6AAA291DB8F7F10013AAEA /* ReusableComponentsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponentsSpec.swift; sourceTree = ""; }; 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssociationSpec.swift; sourceTree = ""; }; 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+KeyValueObserving.swift"; sourceTree = ""; }; 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaAction.swift; sourceTree = ""; }; 9ADE4A881DA6D206005C2AC8 /* NSControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSControl.swift; sourceTree = ""; }; - 9ADE4A8D1DA6D965005C2AC8 /* NSControlSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NSControlSpec.swift; path = AppKit/NSControlSpec.swift; sourceTree = ""; }; + 9ADE4A8D1DA6D965005C2AC8 /* NSControlSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSControlSpec.swift; sourceTree = ""; }; 9ADE4A901DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+ReactiveExtensionsProvider.swift"; sourceTree = ""; }; 9ADE4A951DA6F018005C2AC8 /* NSTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSTextField.swift; sourceTree = ""; }; 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LifetimeSpec.swift; sourceTree = ""; }; @@ -531,7 +531,7 @@ 9ADE4A8D1DA6D965005C2AC8 /* NSControlSpec.swift */, 9A6AAA271DB8F7EB0013AAEA /* ReusableComponentsSpec.swift */, ); - name = AppKit; + path = AppKit; sourceTree = ""; }; A97451321B3A935E00F48E55 /* watchOS */ = { From 2871ed4b9aafe329b2b66dc3bf333febe1aac6aa Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Sat, 5 Nov 2016 13:02:14 -0400 Subject: [PATCH 0535/1028] Add NSTableView.reactive.reloadData binding target --- ReactiveCocoa.xcodeproj/project.pbxproj | 12 +++- ReactiveCocoa/AppKit/NSTableView.swift | 8 +++ .../AppKit/NSTableViewSpec.swift | 63 +++++++++++++++++++ 3 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 ReactiveCocoa/AppKit/NSTableView.swift create mode 100644 ReactiveCocoaTests/AppKit/NSTableViewSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 33c6fd87c7..7955abb596 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -22,6 +22,8 @@ 4ABEFE261DCFCF640066A8C2 /* UICollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE231DCFCF5C0066A8C2 /* UICollectionView.swift */; }; 4ABEFE281DCFCFA90066A8C2 /* UICollectionViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE271DCFCFA90066A8C2 /* UICollectionViewSpec.swift */; }; 4ABEFE291DCFCFA90066A8C2 /* UICollectionViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE271DCFCFA90066A8C2 /* UICollectionViewSpec.swift */; }; + 4ABEFE2B1DCFD0030066A8C2 /* NSTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE2A1DCFD0030066A8C2 /* NSTableView.swift */; }; + 4ABEFE2E1DCFD01F0066A8C2 /* NSTableViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE2C1DCFD0180066A8C2 /* NSTableViewSpec.swift */; }; 538DCB791DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */; }; 538DCB7A1DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */; }; 538DCB7B1DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */; }; @@ -259,6 +261,8 @@ 4ABEFDE31DCFCCD70066A8C2 /* UITableViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewSpec.swift; sourceTree = ""; }; 4ABEFE231DCFCF5C0066A8C2 /* UICollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UICollectionView.swift; sourceTree = ""; }; 4ABEFE271DCFCFA90066A8C2 /* UICollectionViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UICollectionViewSpec.swift; sourceTree = ""; }; + 4ABEFE2A1DCFD0030066A8C2 /* NSTableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSTableView.swift; sourceTree = ""; }; + 4ABEFE2C1DCFD0180066A8C2 /* NSTableViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSTableViewSpec.swift; sourceTree = ""; }; 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSLayoutConstraint.swift; sourceTree = ""; }; 538DCB7C1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSLayoutConstraintSpec.swift; sourceTree = ""; }; 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -468,9 +472,10 @@ 9A1D05E91D93E9F100ACF44C /* AppKit */ = { isa = PBXGroup; children = ( - 9A6AAA251DB8F5280013AAEA /* ReusableComponents.swift */, - 9ADE4A951DA6F018005C2AC8 /* NSTextField.swift */, 9ADE4A881DA6D206005C2AC8 /* NSControl.swift */, + 4ABEFE2A1DCFD0030066A8C2 /* NSTableView.swift */, + 9ADE4A951DA6F018005C2AC8 /* NSTextField.swift */, + 9A6AAA251DB8F5280013AAEA /* ReusableComponents.swift */, ); path = AppKit; sourceTree = ""; @@ -529,6 +534,7 @@ isa = PBXGroup; children = ( 9ADE4A8D1DA6D965005C2AC8 /* NSControlSpec.swift */, + 4ABEFE2C1DCFD0180066A8C2 /* NSTableViewSpec.swift */, 9A6AAA271DB8F7EB0013AAEA /* ReusableComponentsSpec.swift */, ); path = AppKit; @@ -1093,6 +1099,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 4ABEFE2B1DCFD0030066A8C2 /* NSTableView.swift in Sources */, CD0C45DE1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9A1D06771D9415FF00ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, @@ -1126,6 +1133,7 @@ CD8401831CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, 9A9DFEE91DA7EFB60039EE1B /* AssociationSpec.swift in Sources */, 9A1E72BA1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, + 4ABEFE2E1DCFD01F0066A8C2 /* NSTableViewSpec.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ReactiveCocoa/AppKit/NSTableView.swift b/ReactiveCocoa/AppKit/NSTableView.swift new file mode 100644 index 0000000000..6cdc867a70 --- /dev/null +++ b/ReactiveCocoa/AppKit/NSTableView.swift @@ -0,0 +1,8 @@ +import AppKit +import ReactiveSwift + +extension Reactive where Base: NSTableView { + public var reloadData: BindingTarget<()> { + return makeBindingTarget { base, _ in base.reloadData() } + } +} diff --git a/ReactiveCocoaTests/AppKit/NSTableViewSpec.swift b/ReactiveCocoaTests/AppKit/NSTableViewSpec.swift new file mode 100644 index 0000000000..8fbb46fa95 --- /dev/null +++ b/ReactiveCocoaTests/AppKit/NSTableViewSpec.swift @@ -0,0 +1,63 @@ +import Quick +import Nimble +import ReactiveCocoa +import ReactiveSwift +import Result +import AppKit + +final class NSTableViewSpec: QuickSpec { + override func spec() { + var tableView: TestTableView! + + beforeEach { + tableView = TestTableView() + } + + describe("reloadData") { + var bindingSignal: Signal<(), NoError>! + var bindingObserver: Signal<(), NoError>.Observer! + + var reloadDataCount = 0 + + beforeEach { + let (signal, observer) = Signal<(), NoError>.pipe() + (bindingSignal, bindingObserver) = (signal, observer) + + reloadDataCount = 0 + + tableView.reloadDataSignal.observeValues { + reloadDataCount += 1 + } + } + + it("invokes reloadData whenever the bound signal sends a value") { + tableView.reactive.reloadData <~ bindingSignal + + bindingObserver.send(value: ()) + bindingObserver.send(value: ()) + + expect(reloadDataCount) == 2 + } + } + } +} + +private final class TestTableView: NSTableView { + let reloadDataSignal: Signal<(), NoError> + private let reloadDataObserver: Signal<(), NoError>.Observer + + override init(frame: CGRect) { + (reloadDataSignal, reloadDataObserver) = Signal.pipe() + super.init(frame: frame) + } + + required init?(coder aDecoder: NSCoder) { + (reloadDataSignal, reloadDataObserver) = Signal.pipe() + super.init(coder: aDecoder) + } + + override func reloadData() { + super.reloadData() + reloadDataObserver.send(value: ()) + } +} From 986ee0f706b42051504a8a27ac00ed477e4dc3c4 Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Sat, 5 Nov 2016 13:06:53 -0400 Subject: [PATCH 0536/1028] Add NSCollectionView.reactive.reloadData binding target --- ReactiveCocoa.xcodeproj/project.pbxproj | 8 +++ ReactiveCocoa/AppKit/NSCollectionView.swift | 9 +++ .../AppKit/NSCollectionViewSpec.swift | 65 +++++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 ReactiveCocoa/AppKit/NSCollectionView.swift create mode 100644 ReactiveCocoaTests/AppKit/NSCollectionViewSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 7955abb596..462391cb15 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -24,6 +24,8 @@ 4ABEFE291DCFCFA90066A8C2 /* UICollectionViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE271DCFCFA90066A8C2 /* UICollectionViewSpec.swift */; }; 4ABEFE2B1DCFD0030066A8C2 /* NSTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE2A1DCFD0030066A8C2 /* NSTableView.swift */; }; 4ABEFE2E1DCFD01F0066A8C2 /* NSTableViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE2C1DCFD0180066A8C2 /* NSTableViewSpec.swift */; }; + 4ABEFE301DCFD0530066A8C2 /* NSCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE2F1DCFD0530066A8C2 /* NSCollectionView.swift */; }; + 4ABEFE331DCFD0630066A8C2 /* NSCollectionViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE311DCFD05F0066A8C2 /* NSCollectionViewSpec.swift */; }; 538DCB791DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */; }; 538DCB7A1DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */; }; 538DCB7B1DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */; }; @@ -263,6 +265,8 @@ 4ABEFE271DCFCFA90066A8C2 /* UICollectionViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UICollectionViewSpec.swift; sourceTree = ""; }; 4ABEFE2A1DCFD0030066A8C2 /* NSTableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSTableView.swift; sourceTree = ""; }; 4ABEFE2C1DCFD0180066A8C2 /* NSTableViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSTableViewSpec.swift; sourceTree = ""; }; + 4ABEFE2F1DCFD0530066A8C2 /* NSCollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSCollectionView.swift; sourceTree = ""; }; + 4ABEFE311DCFD05F0066A8C2 /* NSCollectionViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSCollectionViewSpec.swift; sourceTree = ""; }; 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSLayoutConstraint.swift; sourceTree = ""; }; 538DCB7C1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSLayoutConstraintSpec.swift; sourceTree = ""; }; 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -472,6 +476,7 @@ 9A1D05E91D93E9F100ACF44C /* AppKit */ = { isa = PBXGroup; children = ( + 4ABEFE2F1DCFD0530066A8C2 /* NSCollectionView.swift */, 9ADE4A881DA6D206005C2AC8 /* NSControl.swift */, 4ABEFE2A1DCFD0030066A8C2 /* NSTableView.swift */, 9ADE4A951DA6F018005C2AC8 /* NSTextField.swift */, @@ -533,6 +538,7 @@ 9ADE4A8C1DA6D94C005C2AC8 /* AppKit */ = { isa = PBXGroup; children = ( + 4ABEFE311DCFD05F0066A8C2 /* NSCollectionViewSpec.swift */, 9ADE4A8D1DA6D965005C2AC8 /* NSControlSpec.swift */, 4ABEFE2C1DCFD0180066A8C2 /* NSTableViewSpec.swift */, 9A6AAA271DB8F7EB0013AAEA /* ReusableComponentsSpec.swift */, @@ -1101,6 +1107,7 @@ files = ( 4ABEFE2B1DCFD0030066A8C2 /* NSTableView.swift in Sources */, CD0C45DE1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, + 4ABEFE301DCFD0530066A8C2 /* NSCollectionView.swift in Sources */, 9A1D06771D9415FF00ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, 9A1D05E01D93E99100ACF44C /* NSObject+Association.swift in Sources */, @@ -1127,6 +1134,7 @@ 9ADE4A8F1DA6DA20005C2AC8 /* NSControlSpec.swift in Sources */, D0A2260E1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */, 9ADFE5A11DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */, + 4ABEFE331DCFD0630066A8C2 /* NSCollectionViewSpec.swift in Sources */, B696FB811A7640C00075236D /* TestError.swift in Sources */, BFA6B94D1A7604D400C846D1 /* SignalProducerNimbleMatchers.swift in Sources */, 9A6AAA2B1DB8F85C0013AAEA /* ReusableComponentsSpec.swift in Sources */, diff --git a/ReactiveCocoa/AppKit/NSCollectionView.swift b/ReactiveCocoa/AppKit/NSCollectionView.swift new file mode 100644 index 0000000000..74741200e3 --- /dev/null +++ b/ReactiveCocoa/AppKit/NSCollectionView.swift @@ -0,0 +1,9 @@ +import AppKit +import ReactiveSwift + +extension Reactive where Base: NSCollectionView { + @available(macOS 10.11, *) + public var reloadData: BindingTarget<()> { + return makeBindingTarget { base, _ in base.reloadData() } + } +} diff --git a/ReactiveCocoaTests/AppKit/NSCollectionViewSpec.swift b/ReactiveCocoaTests/AppKit/NSCollectionViewSpec.swift new file mode 100644 index 0000000000..b24313da68 --- /dev/null +++ b/ReactiveCocoaTests/AppKit/NSCollectionViewSpec.swift @@ -0,0 +1,65 @@ +import Quick +import Nimble +import ReactiveCocoa +import ReactiveSwift +import Result +import AppKit + +@available(macOS 10.11, *) +final class NSCollectionViewSpec: QuickSpec { + override func spec() { + var collectionView: TestCollectionView! + + beforeEach { + collectionView = TestCollectionView() + } + + describe("reloadData") { + var bindingSignal: Signal<(), NoError>! + var bindingObserver: Signal<(), NoError>.Observer! + + var reloadDataCount = 0 + + beforeEach { + let (signal, observer) = Signal<(), NoError>.pipe() + (bindingSignal, bindingObserver) = (signal, observer) + + reloadDataCount = 0 + + collectionView.reloadDataSignal.observeValues { + reloadDataCount += 1 + } + } + + it("invokes reloadData whenever the bound signal sends a value") { + collectionView.reactive.reloadData <~ bindingSignal + + bindingObserver.send(value: ()) + bindingObserver.send(value: ()) + + expect(reloadDataCount) == 2 + } + } + } +} + +@available(macOS 10.11, *) +private final class TestCollectionView: NSCollectionView { + let reloadDataSignal: Signal<(), NoError> + private let reloadDataObserver: Signal<(), NoError>.Observer + + override init(frame: CGRect) { + (reloadDataSignal, reloadDataObserver) = Signal.pipe() + super.init(frame: frame) + } + + required init?(coder aDecoder: NSCoder) { + (reloadDataSignal, reloadDataObserver) = Signal.pipe() + super.init(coder: aDecoder) + } + + override func reloadData() { + super.reloadData() + reloadDataObserver.send(value: ()) + } +} From c19280bbcc310987397f77e331eb98fc8187e16e Mon Sep 17 00:00:00 2001 From: Anders Date: Sun, 6 Nov 2016 23:53:58 +0100 Subject: [PATCH 0537/1028] Trigger signal for optional, unbacked protocol requirements. --- ReactiveCocoa/NSObject+Intercepting.swift | 15 ++++++++++++-- ReactiveCocoaTests/InterceptingSpec.swift | 25 ++++++++++++++++++++++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 6fc575b1f3..afd6bf8c16 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -6,12 +6,23 @@ extension Reactive where Base: NSObject { /// Create a signal which sends a `next` event at the end of every invocation /// of `selector` on the object. /// + /// `trigger(for:from:)` can be used to intercept optional protocol + /// requirements by supplying the protocol as `protocol`. The instance need + /// not have a concrete implementation of the requirement. + /// + /// However, as Cocoa classes usually cache information about delegate + /// conformances, trigger signals for optional, unbacked protocol requirements + /// should be set up before the instance is assigned as the corresponding + /// delegate. + /// /// - parameters: /// - selector: The selector to observe. + /// - protocol: The protocol of the selector, or `nil` if the selector does + /// not belong to any protocol. /// /// - returns: /// A trigger signal. - public func trigger(for selector: Selector) -> Signal<(), NoError> { + public func trigger(for selector: Selector, from protocol: Protocol? = nil) -> Signal<(), NoError> { return base.synchronized { let map = associatedValue { _ in NSMutableDictionary() } @@ -22,7 +33,7 @@ extension Reactive where Base: NSObject { let (signal, observer) = Signal<(), NoError>.pipe() let isSuccessful = base._rac_setupInvocationObservation(for: selector, - protocol: nil, + protocol: `protocol`, receiver: observer.send(value:)) precondition(isSuccessful) diff --git a/ReactiveCocoaTests/InterceptingSpec.swift b/ReactiveCocoaTests/InterceptingSpec.swift index fb917fbd36..0e754b943a 100644 --- a/ReactiveCocoaTests/InterceptingSpec.swift +++ b/ReactiveCocoaTests/InterceptingSpec.swift @@ -39,6 +39,24 @@ class InterceptingSpec: QuickSpec { expect(object.counter) == 2 } + it("should send a value when the selector is invoked without implementation") { + let selector = #selector(TestProtocol.optionalMethod) + + let signal = object.reactive.trigger(for: selector, + from: TestProtocol.self) + + var counter = 0 + signal.observeValues { counter += 1 } + + expect(counter) == 0 + + (object as TestProtocol).optionalMethod!() + expect(counter) == 1 + + (object as TestProtocol).optionalMethod!() + expect(counter) == 2 + } + it("should complete when the object deinitializes") { let signal = object.reactive.trigger(for: #selector(object.increment)) @@ -93,8 +111,13 @@ class InterceptingSpec: QuickSpec { } } -class InterceptedObject: NSObject { +@objc protocol TestProtocol { + @objc optional func optionalMethod() +} + +class InterceptedObject: NSObject, TestProtocol { var counter = 0 + var testProtocolCounter = 0 dynamic func increment() { counter += 1 From b344d84b5f862d60b3b11e298fed5e49799c3427 Mon Sep 17 00:00:00 2001 From: Anders Date: Mon, 7 Nov 2016 00:03:51 +0100 Subject: [PATCH 0538/1028] `response(to:)` now handles selectors added at runtime correctly. --- ReactiveCocoa/RACObjCRuntimeUtilities.m | 2 +- ReactiveCocoaTests/InterceptingSpec.swift | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ReactiveCocoa/RACObjCRuntimeUtilities.m b/ReactiveCocoa/RACObjCRuntimeUtilities.m index f3c33dae34..25427118c2 100644 --- a/ReactiveCocoa/RACObjCRuntimeUtilities.m +++ b/ReactiveCocoa/RACObjCRuntimeUtilities.m @@ -111,7 +111,7 @@ static void RACSwizzleRespondsToSelector(Class class) { Method method = rac_getImmediateInstanceMethod(class, selector); if (method != NULL && method_getImplementation(method) == _objc_msgForward) { - SEL aliasSelector = (selector); + SEL aliasSelector = RACAliasForSelector(selector); if (objc_getAssociatedObject(self, aliasSelector) != nil) return YES; } diff --git a/ReactiveCocoaTests/InterceptingSpec.swift b/ReactiveCocoaTests/InterceptingSpec.swift index 0e754b943a..afc6717f3f 100644 --- a/ReactiveCocoaTests/InterceptingSpec.swift +++ b/ReactiveCocoaTests/InterceptingSpec.swift @@ -44,6 +44,7 @@ class InterceptingSpec: QuickSpec { let signal = object.reactive.trigger(for: selector, from: TestProtocol.self) + expect(object.responds(to: selector)) == true var counter = 0 signal.observeValues { counter += 1 } @@ -55,6 +56,7 @@ class InterceptingSpec: QuickSpec { (object as TestProtocol).optionalMethod!() expect(counter) == 2 + } it("should complete when the object deinitializes") { From bb180b95e007af9ba5aff1d0783f501a63005601 Mon Sep 17 00:00:00 2001 From: Anders Date: Mon, 7 Nov 2016 00:39:59 +0100 Subject: [PATCH 0539/1028] Added an internal NSInvocation and NSMethodSignature wrapper. --- ReactiveCocoa/NSObject+Intercepting.swift | 2 +- ReactiveCocoa/RACObjCRuntimeUtilities.h | 11 ++++++- ReactiveCocoa/RACObjCRuntimeUtilities.m | 36 +++++++++++++++++++++-- 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index afd6bf8c16..5a4480621a 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -34,7 +34,7 @@ extension Reactive where Base: NSObject { let (signal, observer) = Signal<(), NoError>.pipe() let isSuccessful = base._rac_setupInvocationObservation(for: selector, protocol: `protocol`, - receiver: observer.send(value:)) + receiver: { _ in observer.send(value: ()) }) precondition(isSuccessful) lifetime.ended.observeCompleted(observer.sendCompleted) diff --git a/ReactiveCocoa/RACObjCRuntimeUtilities.h b/ReactiveCocoa/RACObjCRuntimeUtilities.h index d08364d8a4..2497452417 100644 --- a/ReactiveCocoa/RACObjCRuntimeUtilities.h +++ b/ReactiveCocoa/RACObjCRuntimeUtilities.h @@ -1,12 +1,21 @@ #import NS_ASSUME_NONNULL_BEGIN +@interface RACSwiftInvocationArguments : NSObject + +@property(readonly, nonatomic) NSInteger count; + +-(const char *)argumentTypeAt:(NSInteger)position; +-(void)copyArgumentAt:(NSInteger)position to:(void *)buffer; + +@end + @interface NSObject (RACObjCRuntimeUtilities) /// Register a block which would be triggered when `selector` is called. /// /// Warning: The callee is responsible for synchronization. --(BOOL) _rac_setupInvocationObservationForSelector:(SEL)selector protocol:(nullable Protocol *)protocol receiver:(void (^)(void)) receiver; +-(BOOL) _rac_setupInvocationObservationForSelector:(SEL)selector protocol:(nullable Protocol *)protocol receiver:(void (^)(RACSwiftInvocationArguments*)) receiver; @end NS_ASSUME_NONNULL_END diff --git a/ReactiveCocoa/RACObjCRuntimeUtilities.m b/ReactiveCocoa/RACObjCRuntimeUtilities.m index 25427118c2..f01965e746 100644 --- a/ReactiveCocoa/RACObjCRuntimeUtilities.m +++ b/ReactiveCocoa/RACObjCRuntimeUtilities.m @@ -22,6 +22,12 @@ return set; } +@interface RACSwiftInvocationArguments (Private) + +-(instancetype) initWithInvocation:(NSInvocation *)invocation; + +@end + static SEL RACAliasForSelector(SEL originalSelector) { NSString *selectorName = NSStringFromSelector(originalSelector); return NSSelectorFromString([RACSignalForSelectorAliasPrefix stringByAppendingString:selectorName]); @@ -29,7 +35,7 @@ static SEL RACAliasForSelector(SEL originalSelector) { static BOOL RACForwardInvocation(id self, NSInvocation *invocation) { SEL aliasSelector = RACAliasForSelector(invocation.selector); - __block void(^receiver)(void) = objc_getAssociatedObject(self, aliasSelector); + __block void(^receiver)(RACSwiftInvocationArguments*) = objc_getAssociatedObject(self, aliasSelector); Class class = object_getClass(invocation.target); BOOL respondsToAlias = [class instancesRespondToSelector:aliasSelector]; @@ -40,7 +46,7 @@ static BOOL RACForwardInvocation(id self, NSInvocation *invocation) { if (receiver == nil) return respondsToAlias; - receiver(); + receiver([[RACSwiftInvocationArguments alloc] initWithInvocation:invocation]); return YES; } @@ -251,7 +257,7 @@ static Class RACSwizzleClass(NSObject *self) { @implementation NSObject (RACObjCRuntimeUtilities) --(BOOL) _rac_setupInvocationObservationForSelector:(SEL)selector protocol:(Protocol *)protocol receiver:(void (^)(void))receiver { +-(BOOL) _rac_setupInvocationObservationForSelector:(SEL)selector protocol:(Protocol *)protocol receiver:(void (^)(RACSwiftInvocationArguments*))receiver { SEL aliasSelector = RACAliasForSelector(selector); __block void (^existingReceiver)(void) = objc_getAssociatedObject(self, aliasSelector); @@ -302,5 +308,29 @@ -(BOOL) _rac_setupInvocationObservationForSelector:(SEL)selector protocol:(Proto return YES; } +@end + +@implementation RACSwiftInvocationArguments +NSInvocation* invocation; + +-(instancetype) initWithInvocation:(NSInvocation *)inv { + self = [super init]; + if (self) { + invocation = inv; + } + return self; +} + +-(NSInteger)getCount { + return [[invocation methodSignature] numberOfArguments]; +} + +-(const char *)argumentTypeAt:(NSInteger)position { + return [[invocation methodSignature] getArgumentTypeAtIndex:position]; +} + +-(void)copyArgumentAt:(NSInteger)position to:(void *)buffer { + [invocation getArgument:buffer atIndex:position]; +} @end From c514f76497acca9640e53d424d2af0fb6fe913e1 Mon Sep 17 00:00:00 2001 From: Anders Date: Mon, 7 Nov 2016 00:40:31 +0100 Subject: [PATCH 0540/1028] Added an private enum for ObjC type encoding. --- ReactiveCocoa/NSObject+Intercepting.swift | 31 +++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 5a4480621a..075d461934 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -44,3 +44,34 @@ extension Reactive where Base: NSObject { } } } + +private enum TypeEncoding: Int8 { + // Integer + case char = 99 + case int = 105 + case short = 115 + case long = 108 + case longLong = 113 + + // Unsigned Integer + case unsignedChar = 67 + case unsignedInt = 73 + case unsignedShort = 83 + case unsignedLong = 76 + case unsignedLongLong = 81 + + // FP + case float = 102 + case double = 100 + + case bool = 66 + case void = 118 + case cString = 42 + case object = 64 + case type = 35 + case selector = 58 + case array = 91 + case bitfield = 98 + case pointer = 94 + // Note: Structure `{` and union `(` are not supported. +} From 0e89e6a5e27a48bf47ba9542feba7d92227c4481 Mon Sep 17 00:00:00 2001 From: Anders Date: Mon, 7 Nov 2016 01:39:02 +0100 Subject: [PATCH 0541/1028] Implement the argument bridging logic. --- ReactiveCocoa/NSObject+Intercepting.swift | 129 +++++++++++++++++++++- ReactiveCocoa/RACObjCRuntimeUtilities.h | 1 + ReactiveCocoa/RACObjCRuntimeUtilities.m | 8 +- ReactiveCocoaTests/InterceptingSpec.swift | 116 ++++++++++++++++++- 4 files changed, 250 insertions(+), 4 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 075d461934..c35aba0b7b 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -43,8 +43,134 @@ extension Reactive where Base: NSObject { return signal } } + + /// Create a signal which sends a `next` event, containing an array of bridged + /// arguments, at the end of every invocation of `selector` on the object. + /// + /// `trigger(for:from:)` can be used to intercept optional protocol + /// requirements by supplying the protocol as `protocol`. The instance need + /// not have a concrete implementation of the requirement. + /// + /// However, as Cocoa classes usually cache information about delegate + /// conformances, trigger signals for optional, unbacked protocol requirements + /// should be set up before the instance is assigned as the corresponding + /// delegate. + /// + /// - parameters: + /// - selector: The selector to observe. + /// - protocol: The protocol of the selector, or `nil` if the selector does + /// not belong to any protocol. + /// + /// - returns: + /// A signal that sends an array of bridged arguments. + public func signal(for selector: Selector, from protocol: Protocol? = nil) -> Signal<[Any?], NoError> { + return base.synchronized { + let map = associatedValue { _ in NSMutableDictionary() } + + let selectorName = String(describing: selector) as NSString + if let signal = map.object(forKey: selectorName) as! Signal<[Any?], NoError>? { + return signal + } + + let (signal, observer) = Signal<[Any?], NoError>.pipe() + let isSuccessful = base._rac_setupInvocationObservation(for: selector, + protocol: `protocol`, + receiver: bridge(observer)) + precondition(isSuccessful) + + lifetime.ended.observeCompleted(observer.sendCompleted) + map.setObject(signal, forKey: selectorName) + + return signal + } + } +} + +private func bridge(_ observer: Observer<[Any?], NoError>) -> (RACSwiftInvocationArguments) -> Void { + return { arguments in + let count = arguments.count + + var bridged = [Any?]() + bridged.reserveCapacity(count - 2) + + // Ignore `self` and `_cmd`. + for position in 2 ..< count { + let encoding = TypeEncoding(rawValue: arguments.argumentType(at: position).pointee) ?? .undefined + + func extract(_ type: U.Type) -> U { + let pointer = UnsafeMutableRawPointer.allocate(bytes: MemoryLayout.size, + alignedTo: MemoryLayout.alignment) + defer { + pointer.deallocate(bytes: MemoryLayout.size, + alignedTo: MemoryLayout.alignment) + } + + arguments.copyArgument(at: position, to: pointer) + return pointer.assumingMemoryBound(to: type).pointee + } + + switch encoding { + case .char: + bridged.append(extract(CChar.self)) + case .int: + bridged.append(extract(CInt.self)) + case .short: + bridged.append(extract(CShort.self)) + case .long: + bridged.append(extract(CLong.self)) + case .longLong: + bridged.append(extract(CLongLong.self)) + + case .unsignedChar: + bridged.append(extract(CUnsignedChar.self)) + case .unsignedInt: + bridged.append(extract(CUnsignedInt.self)) + case .unsignedShort: + bridged.append(extract(CUnsignedShort.self)) + case .unsignedLong: + bridged.append(extract(CUnsignedLong.self)) + + case .bitfield: + fallthrough + case .unsignedLongLong: + bridged.append(extract(CUnsignedLongLong.self)) + + case .float: + bridged.append(extract(CFloat.self)) + case .double: + bridged.append(extract(CDouble.self)) + + case .bool: + bridged.append(extract(CBool.self)) + case .void: + bridged.append(()) + + case .cString: + var pointer: UnsafePointer? + arguments.copyArgument(at: position, to: &pointer) + bridged.append(pointer.map(String.init(cString:))) + + case .object: + bridged.append(extract((AnyObject?).self)) + case .type: + bridged.append(extract((AnyClass?).self)) + + case .selector: + bridged.append(arguments.selectorString(at: position)) + + case .array: + bridged.append(extract(OpaquePointer.self)) + + case .undefined: + bridged.append(nil) + } + } + + observer.send(value: bridged) + } } + private enum TypeEncoding: Int8 { // Integer case char = 99 @@ -72,6 +198,7 @@ private enum TypeEncoding: Int8 { case selector = 58 case array = 91 case bitfield = 98 - case pointer = 94 // Note: Structure `{` and union `(` are not supported. + + case undefined = -1 } diff --git a/ReactiveCocoa/RACObjCRuntimeUtilities.h b/ReactiveCocoa/RACObjCRuntimeUtilities.h index 2497452417..cbad7acebb 100644 --- a/ReactiveCocoa/RACObjCRuntimeUtilities.h +++ b/ReactiveCocoa/RACObjCRuntimeUtilities.h @@ -7,6 +7,7 @@ NS_ASSUME_NONNULL_BEGIN -(const char *)argumentTypeAt:(NSInteger)position; -(void)copyArgumentAt:(NSInteger)position to:(void *)buffer; +-(NSString*)selectorStringAt:(NSInteger)position; @end diff --git a/ReactiveCocoa/RACObjCRuntimeUtilities.m b/ReactiveCocoa/RACObjCRuntimeUtilities.m index f01965e746..00c8570099 100644 --- a/ReactiveCocoa/RACObjCRuntimeUtilities.m +++ b/ReactiveCocoa/RACObjCRuntimeUtilities.m @@ -321,7 +321,7 @@ -(instancetype) initWithInvocation:(NSInvocation *)inv { return self; } --(NSInteger)getCount { +-(NSInteger)count { return [[invocation methodSignature] numberOfArguments]; } @@ -333,4 +333,10 @@ -(void)copyArgumentAt:(NSInteger)position to:(void *)buffer { [invocation getArgument:buffer atIndex:position]; } +-(NSString*)selectorStringAt:(NSInteger)position { + SEL selector; + [invocation getArgument:&selector atIndex:position]; + return NSStringFromSelector(selector); +} + @end diff --git a/ReactiveCocoaTests/InterceptingSpec.swift b/ReactiveCocoaTests/InterceptingSpec.swift index afc6717f3f..ba77d25652 100644 --- a/ReactiveCocoaTests/InterceptingSpec.swift +++ b/ReactiveCocoaTests/InterceptingSpec.swift @@ -56,7 +56,6 @@ class InterceptingSpec: QuickSpec { (object as TestProtocol).optionalMethod!() expect(counter) == 2 - } it("should complete when the object deinitializes") { @@ -110,6 +109,118 @@ class InterceptingSpec: QuickSpec { } } } + + describe("signal(for:)") { + var object: InterceptedObject! + weak var _object: InterceptedObject? + + beforeEach { + object = InterceptedObject() + _object = object + } + + afterEach { + object = nil + expect(_object).to(beNil()) + } + + it("should send a value with bridged arguments") { + let signal = object.reactive.signal(for: #selector(object.test)) + + var arguments = [[Any?]]() + signal.observeValues { arguments.append($0) } + + expect(arguments.count) == 0 + + let firstObject = NSObject() + object.test(a: 1, b: 10.0, c: firstObject, d: nil) + expect(arguments.count) == 1 + + expect(arguments[0][0] as? Int64) == 1 + expect(arguments[0][1] as? Double) == 10.0 + expect(arguments[0][2] as? NSObject) == firstObject + expect(arguments[0][3] as! NSObject?).to(beNil()) + + let secondObject = NSObject() + object.test(a: 2, b: 20.0, c: secondObject, d: nil) + expect(arguments.count) == 2 + + expect(arguments[1][0] as? Int64) == 2 + expect(arguments[1][1] as? Double) == 20.0 + expect(arguments[1][2] as? NSObject) == secondObject + expect(arguments[1][3] as! NSObject?).to(beNil()) + } + + it("should send a value when the selector is invoked without implementation") { + let selector = #selector(TestProtocol.optionalMethod) + + let signal = object.reactive.signal(for: selector, + from: TestProtocol.self) + expect(object.responds(to: selector)) == true + + var counter = 0 + signal.observeValues { _ in counter += 1 } + + expect(counter) == 0 + + (object as TestProtocol).optionalMethod!() + expect(counter) == 1 + + (object as TestProtocol).optionalMethod!() + expect(counter) == 2 + } + + it("should complete when the object deinitializes") { + let signal = object.reactive.signal(for: #selector(object.increment)) + + var isCompleted = false + signal.observeCompleted { isCompleted = true } + expect(isCompleted) == false + + object = nil + expect(_object).to(beNil()) + expect(isCompleted) == true + } + + it("should multicast") { + let signal1 = object.reactive.signal(for: #selector(object.increment)) + let signal2 = object.reactive.signal(for: #selector(object.increment)) + + var counter1 = 0 + var counter2 = 0 + signal1.observeValues { _ in counter1 += 1 } + signal2.observeValues { _ in counter2 += 1 } + + expect(counter1) == 0 + expect(counter2) == 0 + + object.increment() + expect(counter1) == 1 + expect(counter2) == 1 + + object.increment() + expect(counter1) == 2 + expect(counter2) == 2 + } + + it("should not deadlock") { + for _ in 1 ... 10 { + var isDeadlocked = true + + DispatchQueue.global(priority: .high).async { + _ = object.reactive.signal(for: #selector(object.increment)) + + DispatchQueue.global(priority: .high).async { + _ = object.reactive.signal(for: #selector(object.increment)) + + isDeadlocked = false + } + } + + expect(isDeadlocked).toEventually(beFalsy()) + } + } + } } } @@ -119,9 +230,10 @@ class InterceptingSpec: QuickSpec { class InterceptedObject: NSObject, TestProtocol { var counter = 0 - var testProtocolCounter = 0 dynamic func increment() { counter += 1 } + + dynamic func test(a: Int, b: Double, c: NSObject, d: NSObject?) {} } From 06483b76cd2f0271e3bd68d89903534bc013acb8 Mon Sep 17 00:00:00 2001 From: Anders Date: Mon, 7 Nov 2016 15:24:28 +0100 Subject: [PATCH 0542/1028] Bridge scalar and struct arguments as `NSValue` and `NSNumber`. --- ReactiveCocoa/NSObject+Intercepting.swift | 63 ++++------ ReactiveCocoaTests/InterceptingSpec.swift | 143 +++++++++++++++++++--- 2 files changed, 151 insertions(+), 55 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index c35aba0b7b..765f7fbf0b 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -95,7 +95,8 @@ private func bridge(_ observer: Observer<[Any?], NoError>) -> (RACSwiftInvocatio // Ignore `self` and `_cmd`. for position in 2 ..< count { - let encoding = TypeEncoding(rawValue: arguments.argumentType(at: position).pointee) ?? .undefined + let rawEncoding = arguments.argumentType(at: position) + let encoding = TypeEncoding(rawValue: rawEncoding.pointee) ?? .undefined func extract(_ type: U.Type) -> U { let pointer = UnsafeMutableRawPointer.allocate(bytes: MemoryLayout.size, @@ -111,58 +112,49 @@ private func bridge(_ observer: Observer<[Any?], NoError>) -> (RACSwiftInvocatio switch encoding { case .char: - bridged.append(extract(CChar.self)) + bridged.append(NSNumber(value: extract(CChar.self))) case .int: - bridged.append(extract(CInt.self)) + bridged.append(NSNumber(value: extract(CInt.self))) case .short: - bridged.append(extract(CShort.self)) + bridged.append(NSNumber(value: extract(CShort.self))) case .long: - bridged.append(extract(CLong.self)) + bridged.append(NSNumber(value: extract(CLong.self))) case .longLong: - bridged.append(extract(CLongLong.self)) - + bridged.append(NSNumber(value: extract(CLongLong.self))) case .unsignedChar: - bridged.append(extract(CUnsignedChar.self)) + bridged.append(NSNumber(value: extract(CUnsignedChar.self))) case .unsignedInt: - bridged.append(extract(CUnsignedInt.self)) + bridged.append(NSNumber(value: extract(CUnsignedInt.self))) case .unsignedShort: - bridged.append(extract(CUnsignedShort.self)) + bridged.append(NSNumber(value: extract(CUnsignedShort.self))) case .unsignedLong: - bridged.append(extract(CUnsignedLong.self)) - - case .bitfield: - fallthrough + bridged.append(NSNumber(value: extract(CUnsignedLong.self))) case .unsignedLongLong: - bridged.append(extract(CUnsignedLongLong.self)) - + bridged.append(NSNumber(value: extract(CUnsignedLongLong.self))) case .float: - bridged.append(extract(CFloat.self)) + bridged.append(NSNumber(value: extract(CFloat.self))) case .double: - bridged.append(extract(CDouble.self)) - + bridged.append(NSNumber(value: extract(CDouble.self))) case .bool: - bridged.append(extract(CBool.self)) - case .void: - bridged.append(()) - - case .cString: - var pointer: UnsafePointer? - arguments.copyArgument(at: position, to: &pointer) - bridged.append(pointer.map(String.init(cString:))) - + bridged.append(NSNumber(value: extract(CBool.self))) case .object: bridged.append(extract((AnyObject?).self)) case .type: bridged.append(extract((AnyClass?).self)) - case .selector: bridged.append(arguments.selectorString(at: position)) - - case .array: - bridged.append(extract(OpaquePointer.self)) - + case .cString: + var pointer: UnsafePointer? + arguments.copyArgument(at: position, to: &pointer) + bridged.append(pointer) case .undefined: - bridged.append(nil) + var size = 0, alignment = 0 + NSGetSizeAndAlignment(rawEncoding, &size, &alignment) + let buffer = UnsafeMutableRawPointer.allocate(bytes: size, alignedTo: alignment) + defer { buffer.deallocate(bytes: size, alignedTo: alignment) } + + arguments.copyArgument(at: position, to: buffer) + bridged.append(NSValue(bytes: buffer, objCType: rawEncoding)) } } @@ -191,13 +183,10 @@ private enum TypeEncoding: Int8 { case double = 100 case bool = 66 - case void = 118 case cString = 42 case object = 64 case type = 35 case selector = 58 - case array = 91 - case bitfield = 98 // Note: Structure `{` and union `(` are not supported. case undefined = -1 diff --git a/ReactiveCocoaTests/InterceptingSpec.swift b/ReactiveCocoaTests/InterceptingSpec.swift index ba77d25652..286ecb70d9 100644 --- a/ReactiveCocoaTests/InterceptingSpec.swift +++ b/ReactiveCocoaTests/InterceptingSpec.swift @@ -4,6 +4,7 @@ import ReactiveSwift import enum Result.NoError import Quick import Nimble +import CoreGraphics class InterceptingSpec: QuickSpec { override func spec() { @@ -124,38 +125,142 @@ class InterceptingSpec: QuickSpec { expect(_object).to(beNil()) } - it("should send a value with bridged arguments") { - let signal = object.reactive.signal(for: #selector(object.test)) + it("should send a value with bridged numeric arguments") { + let signal = object.reactive.signal(for: #selector(object.testNumericValues(c:s:i:l:ll:uc:us:ui:ul:ull:f:d:b:))) var arguments = [[Any?]]() signal.observeValues { arguments.append($0) } expect(arguments.count) == 0 - let firstObject = NSObject() - object.test(a: 1, b: 10.0, c: firstObject, d: nil) + func call(offset: UInt) { + object.testNumericValues(c: CChar.max - CChar(offset), + s: CShort.max - CShort(offset), + i: CInt.max - CInt(offset), + l: CLong.max - CLong(offset), + ll: CLongLong.max - CLongLong(offset), + uc: CUnsignedChar.max - CUnsignedChar(offset), + us: CUnsignedShort.max - CUnsignedShort(offset), + ui: CUnsignedInt.max - CUnsignedInt(offset), + ul: CUnsignedLong.max - CUnsignedLong(offset), + ull: CUnsignedLongLong.max - CUnsignedLongLong(offset), + f: CFloat.greatestFiniteMagnitude - CFloat(offset), + d: CDouble.greatestFiniteMagnitude - CDouble(offset), + b: offset % 2 == 0 ? true : false) + } + + func validate(arguments: [Any?], offset: UInt) { + expect(arguments[0] as? CChar) == CChar.max - CChar(offset) + expect(arguments[1] as? CShort) == CShort.max - CShort(offset) + expect(arguments[2] as? CInt) == CInt.max - CInt(offset) + expect(arguments[3] as? CLong) == CLong.max - CLong(offset) + expect(arguments[4] as? CLongLong) == CLongLong.max - CLongLong(offset) + expect(arguments[5] as? CUnsignedChar) == CUnsignedChar.max - CUnsignedChar(offset) + expect(arguments[6] as? CUnsignedShort) == CUnsignedShort.max - CUnsignedShort(offset) + expect(arguments[7] as? CUnsignedInt) == CUnsignedInt.max - CUnsignedInt(offset) + expect(arguments[8] as? CUnsignedLong) == CUnsignedLong.max - CUnsignedLong(offset) + expect(arguments[9] as? CUnsignedLongLong) == CUnsignedLongLong.max - CUnsignedLongLong(offset) + expect(arguments[10] as? CFloat) == CFloat.greatestFiniteMagnitude - CFloat(offset) + expect(arguments[11] as? CDouble) == CDouble.greatestFiniteMagnitude - CDouble(offset) + expect(arguments[12] as? CBool) == (offset % 2 == 0 ? true : false) + } + + call(offset: 0) expect(arguments.count) == 1 + validate(arguments: arguments[0], offset: 0) + + call(offset: 1) + expect(arguments.count) == 2 + validate(arguments: arguments[1], offset: 1) + + call(offset: 2) + expect(arguments.count) == 3 + validate(arguments: arguments[2], offset: 2) + } + + it("should send a value with bridged reference arguments") { + let signal = object.reactive.signal(for: #selector(object.testReferences(nonnull:nullable:iuo:class:nullableClass:iuoClass:))) + + var arguments = [[Any?]]() + signal.observeValues { arguments.append($0) } + + expect(arguments.count) == 0 + + let token = NSObject() + + object.testReferences(nonnull: token, + nullable: token, + iuo: token, + class: InterceptingSpec.self, + nullableClass: InterceptingSpec.self, + iuoClass: InterceptingSpec.self) + + expect(arguments.count) == 1 + + expect((arguments[0][0] as! NSObject)) == token + expect(arguments[0][1] as! NSObject?) == token + expect(arguments[0][2] as! NSObject?) == token + expect(arguments[0][3] as! AnyClass is InterceptingSpec.Type) == true + expect(arguments[0][4] as! AnyClass? is InterceptingSpec.Type) == true + expect(arguments[0][5] as! AnyClass? is InterceptingSpec.Type) == true - expect(arguments[0][0] as? Int64) == 1 - expect(arguments[0][1] as? Double) == 10.0 - expect(arguments[0][2] as? NSObject) == firstObject - expect(arguments[0][3] as! NSObject?).to(beNil()) + object.testReferences(nonnull: token, + nullable: nil, + iuo: nil, + class: InterceptingSpec.self, + nullableClass: nil, + iuoClass: nil) - let secondObject = NSObject() - object.test(a: 2, b: 20.0, c: secondObject, d: nil) expect(arguments.count) == 2 - expect(arguments[1][0] as? Int64) == 2 - expect(arguments[1][1] as? Double) == 20.0 - expect(arguments[1][2] as? NSObject) == secondObject - expect(arguments[1][3] as! NSObject?).to(beNil()) + expect((arguments[1][0] as! NSObject)) == token + expect(arguments[1][1] as! NSObject?).to(beNil()) + expect(arguments[1][2] as! NSObject?).to(beNil()) + expect(arguments[1][3] as! AnyClass is InterceptingSpec.Type) == true + expect(arguments[1][4] as! AnyClass?).to(beNil()) + expect(arguments[1][5] as! AnyClass?).to(beNil()) + } + + it("should send a value with bridged struct arguments") { + let signal = object.reactive.signal(for: #selector(object.testBridgedStructs(p:s:r:a:))) + + var arguments = [[Any?]]() + signal.observeValues { arguments.append($0) } + + expect(arguments.count) == 0 + + func call(offset: CGFloat) { + object.testBridgedStructs(p: CGPoint(x: offset, y: offset), + s: CGSize(width: offset, height: offset), + r: CGRect(x: offset, y: offset, width: offset, height: offset), + a: CGAffineTransform(translationX: offset, y: offset)) + } + + func validate(arguments: [Any?], offset: CGFloat) { + expect((arguments[0] as! CGPoint)) == CGPoint(x: offset, y: offset) + expect((arguments[1] as! CGSize)) == CGSize(width: offset, height: offset) + expect((arguments[2] as! CGRect)) == CGRect(x: offset, y: offset, width: offset, height: offset) + expect((arguments[3] as! CGAffineTransform)) == CGAffineTransform(translationX: offset, y: offset) + } + + call(offset: 0) + expect(arguments.count) == 1 + validate(arguments: arguments[0], offset: 0) + + call(offset: 1) + expect(arguments.count) == 2 + validate(arguments: arguments[1], offset: 1) + + call(offset: 2) + expect(arguments.count) == 3 + validate(arguments: arguments[2], offset: 2) } it("should send a value when the selector is invoked without implementation") { let selector = #selector(TestProtocol.optionalMethod) let signal = object.reactive.signal(for: selector, - from: TestProtocol.self) + from: TestProtocol.self) expect(object.responds(to: selector)) == true var counter = 0 @@ -216,7 +321,7 @@ class InterceptingSpec: QuickSpec { isDeadlocked = false } } - + expect(isDeadlocked).toEventually(beFalsy()) } } @@ -234,6 +339,8 @@ class InterceptedObject: NSObject, TestProtocol { dynamic func increment() { counter += 1 } - - dynamic func test(a: Int, b: Double, c: NSObject, d: NSObject?) {} + + dynamic func testNumericValues(c: CChar, s: CShort, i: CInt, l: CLong, ll: CLongLong, uc: CUnsignedChar, us: CUnsignedShort, ui: CUnsignedInt, ul: CUnsignedLong, ull: CUnsignedLongLong, f: CFloat, d: CDouble, b: CBool) {} + dynamic func testReferences(nonnull: NSObject, nullable: NSObject?, iuo: NSObject!, class: AnyClass, nullableClass: AnyClass?, iuoClass: AnyClass!) {} + dynamic func testBridgedStructs(p: CGPoint, s: CGSize, r: CGRect, a: CGAffineTransform) {} } From 9d816f51242d1c99215944c294f1092af1120e9c Mon Sep 17 00:00:00 2001 From: Anders Date: Mon, 7 Nov 2016 15:59:20 +0100 Subject: [PATCH 0543/1028] Refactored the internal ObjC interception setup API. --- ReactiveCocoa/NSObject+Intercepting.swift | 4 +- ReactiveCocoa/RACObjCRuntimeUtilities.h | 7 +++- ReactiveCocoa/RACObjCRuntimeUtilities.m | 46 +++++++++++++++++++++-- 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 765f7fbf0b..992ac7fd5d 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -34,7 +34,7 @@ extension Reactive where Base: NSObject { let (signal, observer) = Signal<(), NoError>.pipe() let isSuccessful = base._rac_setupInvocationObservation(for: selector, protocol: `protocol`, - receiver: { _ in observer.send(value: ()) }) + receiver: observer.send(value:)) precondition(isSuccessful) lifetime.ended.observeCompleted(observer.sendCompleted) @@ -75,7 +75,7 @@ extension Reactive where Base: NSObject { let (signal, observer) = Signal<[Any?], NoError>.pipe() let isSuccessful = base._rac_setupInvocationObservation(for: selector, protocol: `protocol`, - receiver: bridge(observer)) + argsReceiver: bridge(observer)) precondition(isSuccessful) lifetime.ended.observeCompleted(observer.sendCompleted) diff --git a/ReactiveCocoa/RACObjCRuntimeUtilities.h b/ReactiveCocoa/RACObjCRuntimeUtilities.h index cbad7acebb..79770eb5f1 100644 --- a/ReactiveCocoa/RACObjCRuntimeUtilities.h +++ b/ReactiveCocoa/RACObjCRuntimeUtilities.h @@ -16,7 +16,12 @@ NS_ASSUME_NONNULL_BEGIN /// Register a block which would be triggered when `selector` is called. /// /// Warning: The callee is responsible for synchronization. --(BOOL) _rac_setupInvocationObservationForSelector:(SEL)selector protocol:(nullable Protocol *)protocol receiver:(void (^)(RACSwiftInvocationArguments*)) receiver; +-(BOOL) _rac_setupInvocationObservationForSelector:(SEL)selector protocol:(nullable Protocol *)protocol argsReceiver:(void (^)(RACSwiftInvocationArguments*)) receiverBlock; + +/// Register a block which would be triggered when `selector` is called. +/// +/// Warning: The callee is responsible for synchronization. +-(BOOL) _rac_setupInvocationObservationForSelector:(SEL)selector protocol:(nullable Protocol *)protocol receiver:(void (^)(void)) receiverBlock; @end NS_ASSUME_NONNULL_END diff --git a/ReactiveCocoa/RACObjCRuntimeUtilities.m b/ReactiveCocoa/RACObjCRuntimeUtilities.m index 00c8570099..20ee8cd93a 100644 --- a/ReactiveCocoa/RACObjCRuntimeUtilities.m +++ b/ReactiveCocoa/RACObjCRuntimeUtilities.m @@ -22,6 +22,15 @@ return set; } +@interface RACForwardingInfo : NSObject + +@property (readonly, nonatomic) BOOL isTrigger; +@property (readonly, nonatomic, retain) id block; + +-(instancetype) initWithBlock:(id)block isTrigger:(BOOL)isTrigger; + +@end + @interface RACSwiftInvocationArguments (Private) -(instancetype) initWithInvocation:(NSInvocation *)invocation; @@ -35,7 +44,7 @@ static SEL RACAliasForSelector(SEL originalSelector) { static BOOL RACForwardInvocation(id self, NSInvocation *invocation) { SEL aliasSelector = RACAliasForSelector(invocation.selector); - __block void(^receiver)(RACSwiftInvocationArguments*) = objc_getAssociatedObject(self, aliasSelector); + RACForwardingInfo* receiver = objc_getAssociatedObject(self, aliasSelector); Class class = object_getClass(invocation.target); BOOL respondsToAlias = [class instancesRespondToSelector:aliasSelector]; @@ -46,7 +55,14 @@ static BOOL RACForwardInvocation(id self, NSInvocation *invocation) { if (receiver == nil) return respondsToAlias; - receiver([[RACSwiftInvocationArguments alloc] initWithInvocation:invocation]); + if (receiver.isTrigger) { + __block void(^block)(void) = receiver.block; + block(); + } else { + __block void(^block)(RACSwiftInvocationArguments*) = receiver.block; + block([[RACSwiftInvocationArguments alloc] initWithInvocation:invocation]); + + } return YES; } @@ -257,15 +273,24 @@ static Class RACSwizzleClass(NSObject *self) { @implementation NSObject (RACObjCRuntimeUtilities) --(BOOL) _rac_setupInvocationObservationForSelector:(SEL)selector protocol:(Protocol *)protocol receiver:(void (^)(RACSwiftInvocationArguments*))receiver { +-(BOOL) _rac_setupInvocationObservationForSelector:(SEL)selector protocol:(Protocol *)protocol argsReceiver:(void (^)(RACSwiftInvocationArguments*))receiverBlock { + return [self _rac_setupInvocationObservationForSelector:selector protocol:protocol isTrigger:false receiver:receiverBlock]; +} + +-(BOOL) _rac_setupInvocationObservationForSelector:(SEL)selector protocol:(Protocol *)protocol receiver:(void (^)(void))receiverBlock { + return [self _rac_setupInvocationObservationForSelector:selector protocol:protocol isTrigger:true receiver:receiverBlock]; +} + +-(BOOL) _rac_setupInvocationObservationForSelector:(SEL)selector protocol:(Protocol *)protocol isTrigger:(BOOL)isTrigger receiver:(id)receiverBlock { SEL aliasSelector = RACAliasForSelector(selector); - __block void (^existingReceiver)(void) = objc_getAssociatedObject(self, aliasSelector); + RACForwardingInfo* existingReceiver = objc_getAssociatedObject(self, aliasSelector); if (existingReceiver != nil) return NO; Class class = RACSwizzleClass(self); NSCAssert(class != nil, @"Could not swizzle class of %@", self); + RACForwardingInfo* receiver = [[RACForwardingInfo alloc] initWithBlock:receiverBlock isTrigger:isTrigger]; objc_setAssociatedObject(self, aliasSelector, receiver, OBJC_ASSOCIATION_RETAIN); Method targetMethod = class_getInstanceMethod(class, selector); @@ -340,3 +365,16 @@ -(NSString*)selectorStringAt:(NSInteger)position { } @end + +@implementation RACForwardingInfo + +-(instancetype) initWithBlock:(id)block isTrigger:(BOOL)isTrigger { + self = [super init]; + if (self) { + _block = block; + _isTrigger = isTrigger; + } + return self; +} + +@end From ce979075accbf5b2966bfc21837f94770a44a406 Mon Sep 17 00:00:00 2001 From: Anders Date: Mon, 7 Nov 2016 18:50:19 +0100 Subject: [PATCH 0544/1028] Bridge C string as `NSValue`. --- ReactiveCocoa/NSObject+Intercepting.swift | 10 +--------- ReactiveCocoa/RACObjCRuntimeUtilities.m | 2 +- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 992ac7fd5d..519c3fac65 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -143,10 +143,6 @@ private func bridge(_ observer: Observer<[Any?], NoError>) -> (RACSwiftInvocatio bridged.append(extract((AnyClass?).self)) case .selector: bridged.append(arguments.selectorString(at: position)) - case .cString: - var pointer: UnsafePointer? - arguments.copyArgument(at: position, to: &pointer) - bridged.append(pointer) case .undefined: var size = 0, alignment = 0 NSGetSizeAndAlignment(rawEncoding, &size, &alignment) @@ -164,30 +160,26 @@ private func bridge(_ observer: Observer<[Any?], NoError>) -> (RACSwiftInvocatio private enum TypeEncoding: Int8 { - // Integer case char = 99 case int = 105 case short = 115 case long = 108 case longLong = 113 - // Unsigned Integer case unsignedChar = 67 case unsignedInt = 73 case unsignedShort = 83 case unsignedLong = 76 case unsignedLongLong = 81 - // FP case float = 102 case double = 100 case bool = 66 - case cString = 42 + case object = 64 case type = 35 case selector = 58 - // Note: Structure `{` and union `(` are not supported. case undefined = -1 } diff --git a/ReactiveCocoa/RACObjCRuntimeUtilities.m b/ReactiveCocoa/RACObjCRuntimeUtilities.m index 20ee8cd93a..3d05d28e32 100644 --- a/ReactiveCocoa/RACObjCRuntimeUtilities.m +++ b/ReactiveCocoa/RACObjCRuntimeUtilities.m @@ -61,8 +61,8 @@ static BOOL RACForwardInvocation(id self, NSInvocation *invocation) { } else { __block void(^block)(RACSwiftInvocationArguments*) = receiver.block; block([[RACSwiftInvocationArguments alloc] initWithInvocation:invocation]); - } + return YES; } From 5ba1496706187f67e8ab81de2a090f18b606ef68 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Wed, 9 Nov 2016 07:47:25 -0500 Subject: [PATCH 0545/1028] Adjust based on feedback --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75f997fc7b..531d951c10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,7 +51,7 @@ Lots has changed, but if you're already migrating to Swift 3 then that should no ### Lifetime Semantics -Prior to RAC 5.0, `Signal`s lived and continued to emit values (and side effects) until they completed. This was very confusing, even for RAC veterans. So [changes have been made](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/2959) to the lifetime semantics. `Signal`s now live and continue to emit events while either (a) they have observers or (b) they are retained. This clears up a number of unexpected cases and makes `Signal`s much less dangerous. +Prior to RAC 5.0, `Signal`s lived and continued to emit values (and side effects) until they completed. This was very confusing, even for RAC veterans. So [changes have been made](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/2959) to the lifetime semantics. `Signal`s now live and continue to emit events only while either (a) they have observers or (b) they are retained. This clears up a number of unexpected cases and makes `Signal`s much less dangerous. ## SignalProducer @@ -61,7 +61,7 @@ Properties are now composable! They have many of the same operators as `Signal` ## Atomic -The `Atomic.modify` closure now takes an `inout` instead of relying on a return value. This provides a minor speed boost, but also makes it possible to return a separate value from the closure to be used after the lock is released. +The `Atomic.modify` closure now takes an `inout` instead of relying on a return value. This provides a minor speed boost, because the compiler can avoid a copy in some cases, but also makes it possible to return a separate value from the closure to be used after the lock is released. # 4.0 From 0903b24af036f042569430693e7113b4e8f205e4 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Thu, 10 Nov 2016 15:51:52 +0100 Subject: [PATCH 0546/1028] Replace h2 with h3 for layout consistency. --- CHANGELOG.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 531d951c10..4af92a8c8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # 5.0 -## Repository Split +### Repository Split In version 5.0, we split ReactiveCocoa into multiple repositories for reasons explained in the sections below. The following should help you get started with choosing the repositories you require: **If you’re using only the Swift APIs**, you can continue to include ReactiveCocoa. You will also need to link against [ReactiveSwift][], which is now a dependency of ReactiveCocoa. @@ -9,19 +9,19 @@ In version 5.0, we split ReactiveCocoa into multiple repositories for reasons ex **If you’re using both the Swift and Objective-C APIs**, you likely require both ReactiveCocoa and [ReactiveObjCBridge][], which depend on [ReactiveSwift][] and [ReactiveObjC][]. -### ReactiveCocoa +##### ReactiveCocoa The ReactiveCocoa library is newly focused on Swift and the UI layers of Apple’s platforms, building on the work of [Rex](https://github.com/neilpa/Rex). Reactive programming provides significant benefit in UI programming. RAC 3 and 4 focused on building out the new core Swift API. But we feel that those APIs have matured and it’s time for RAC-friendly extensions to AppKit and UIKit. -### ReactiveSwift +##### ReactiveSwift The core, platform-independent Swift APIs have been extracted to a new framework, [ReactiveSwift][]. As Swift continues to grow as a language and a platform, we hope that it will expand beyond Cocoa and Apple’s platforms. Separating the Swift code makes it possible to use the reactive paradigm on other platforms. [ReactiveSwift]: https://github.com/ReactiveCocoa/ReactiveSwift -### ReactiveObjC +##### ReactiveObjC The 3.x and 4.x releases of ReactiveCocoa included the Objective-C code from ReactiveCocoa 2.x. That code has been moved to [ReactiveObjC][] because: 1. It’s independent of the Swift code @@ -32,14 +32,14 @@ We hope that this move will enable continued support of ReactiveObjC. [ReactiveObjC]: https://github.com/ReactiveCocoa/ReactiveObjC -### ReactiveObjCBridge +##### ReactiveObjCBridge Moving the Swift and Objective-C APIs to separate repositories meant that a new home was needed for the bridging layer between the two. This bridge is an important tool for users that are working in mixed-language code bases. Whether you are slowly adding Swift to a mature product built with the ReactiveCocoa Objective-C APIs, or looking to adopt ReactiveCocoa in a mixed code base, the bridge is required to communicate between Swift and Objective-C code. [ReactiveObjCBridge]: https://github.com/ReactiveCocoa/ReactiveObjCBridge -## API Names +### API Names We mostly adjusted the ReactiveCocoa API to follow the [Swift 3 API Design Guidelines](https://swift.org/blog/swift-3-api-design/), or to match the Cocoa and Foundation API changes that came with Swift 3 and the latest platform SDKs. @@ -47,19 +47,19 @@ Lots has changed, but if you're already migrating to Swift 3 then that should no **Tip:** You can apply all the suggested fix-its in the current scope by choosing Editor > Fix All In Scope from the main menu in Xcode, or by using the associated keyboard shortcut. -## Signal +### Signal -### Lifetime Semantics +##### Lifetime Semantics Prior to RAC 5.0, `Signal`s lived and continued to emit values (and side effects) until they completed. This was very confusing, even for RAC veterans. So [changes have been made](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/2959) to the lifetime semantics. `Signal`s now live and continue to emit events only while either (a) they have observers or (b) they are retained. This clears up a number of unexpected cases and makes `Signal`s much less dangerous. -## SignalProducer +### SignalProducer -## Properties +### Properties Properties are now composable! They have many of the same operators as `Signal` and `SignalProducer`: `map`, `filter`, `combineLatest`, `zip`, `flatten`, etc. -## Atomic +### Atomic The `Atomic.modify` closure now takes an `inout` instead of relying on a return value. This provides a minor speed boost, because the compiler can avoid a copy in some cases, but also makes it possible to return a separate value from the closure to be used after the lock is released. From 8640afb83bf304eb285be18606a2a91961b80127 Mon Sep 17 00:00:00 2001 From: Eimantas Vaiciunas Date: Thu, 10 Nov 2016 17:01:07 +0200 Subject: [PATCH 0547/1028] Make MKMapView's type reactive Make the extension on tvOS available only from version 9.2. --- ReactiveCocoa.xcodeproj/project.pbxproj | 16 ++++++++ ReactiveCocoa/Shared/MKMapView.swift | 12 ++++++ ReactiveCocoaTests/Shared/MKMapViewSpec.swift | 40 +++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 ReactiveCocoa/Shared/MKMapView.swift create mode 100644 ReactiveCocoaTests/Shared/MKMapViewSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 462391cb15..9586132056 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -32,6 +32,12 @@ 538DCB7D1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB7C1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift */; }; 538DCB7E1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB7C1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift */; }; 538DCB7F1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB7C1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift */; }; + 53A6BED21DD4BCA90016C058 /* MKMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED11DD4BCA90016C058 /* MKMapView.swift */; }; + 53A6BED31DD4BCA90016C058 /* MKMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED11DD4BCA90016C058 /* MKMapView.swift */; }; + 53A6BED41DD4BCA90016C058 /* MKMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED11DD4BCA90016C058 /* MKMapView.swift */; }; + 53A6BED61DD4BD2C0016C058 /* MKMapViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED51DD4BD2C0016C058 /* MKMapViewSpec.swift */; }; + 53A6BED71DD4BD2C0016C058 /* MKMapViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED51DD4BD2C0016C058 /* MKMapViewSpec.swift */; }; + 53A6BED81DD4BD2C0016C058 /* MKMapViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED51DD4BD2C0016C058 /* MKMapViewSpec.swift */; }; 57A4D2081BA13D7A00F7D4B1 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; 57A4D20A1BA13D7A00F7D4B1 /* ReactiveCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7DFBED081CDB8C9500EE435B /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */; }; @@ -269,6 +275,8 @@ 4ABEFE311DCFD05F0066A8C2 /* NSCollectionViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSCollectionViewSpec.swift; sourceTree = ""; }; 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSLayoutConstraint.swift; sourceTree = ""; }; 538DCB7C1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSLayoutConstraintSpec.swift; sourceTree = ""; }; + 53A6BED11DD4BCA90016C058 /* MKMapView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MKMapView.swift; sourceTree = ""; }; + 53A6BED51DD4BD2C0016C058 /* MKMapViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MKMapViewSpec.swift; sourceTree = ""; }; 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Application.xcconfig"; sourceTree = ""; }; 57A4D2451BA13F9700F7D4B1 /* tvOS-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Base.xcconfig"; sourceTree = ""; }; @@ -450,6 +458,7 @@ isa = PBXGroup; children = ( 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */, + 53A6BED11DD4BCA90016C058 /* MKMapView.swift */, ); path = Shared; sourceTree = ""; @@ -458,6 +467,7 @@ isa = PBXGroup; children = ( 538DCB7C1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift */, + 53A6BED51DD4BD2C0016C058 /* MKMapViewSpec.swift */, ); path = Shared; sourceTree = ""; @@ -1045,6 +1055,7 @@ CD0C45E11CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9A1D061A1D93EA0100ACF44C /* UIProgressView.swift in Sources */, 9A2E42611DAA6737006D909F /* CocoaTarget.swift in Sources */, + 53A6BED41DD4BCA90016C058 /* MKMapView.swift in Sources */, 9A1D06121D93EA0100ACF44C /* UIBarButtonItem.swift in Sources */, 9A1D06111D93EA0100ACF44C /* UIActivityIndicatorView.swift in Sources */, 9A1D061B1D93EA0100ACF44C /* UISegmentedControl.swift in Sources */, @@ -1057,6 +1068,7 @@ files = ( 9A9A129A1DC7A97100D10223 /* UIGestureRecognizerSpec.swift in Sources */, 9A1D06591D93EA7E00ACF44C /* UIViewSpec.swift in Sources */, + 53A6BED81DD4BD2C0016C058 /* MKMapViewSpec.swift in Sources */, 7DFBED281CDB8DE300EE435B /* DynamicPropertySpec.swift in Sources */, 9A9DFEEB1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */, CD8401851CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, @@ -1120,6 +1132,7 @@ 9ADE4A891DA6D206005C2AC8 /* NSControl.swift in Sources */, 9AF0EA751D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, 9ADE4A911DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, + 53A6BED21DD4BCA90016C058 /* MKMapView.swift in Sources */, 9A1D065B1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, 4A0E10FF1D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, ); @@ -1133,6 +1146,7 @@ 538DCB7D1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift in Sources */, 9ADE4A8F1DA6DA20005C2AC8 /* NSControlSpec.swift in Sources */, D0A2260E1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */, + 53A6BED61DD4BD2C0016C058 /* MKMapViewSpec.swift in Sources */, 9ADFE5A11DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */, 4ABEFE331DCFD0630066A8C2 /* NSCollectionViewSpec.swift in Sources */, B696FB811A7640C00075236D /* TestError.swift in Sources */, @@ -1168,6 +1182,7 @@ 9A1D060A1D93EA0000ACF44C /* UISwitch.swift in Sources */, 9A1D065C1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, 9A1D06071D93EA0000ACF44C /* UILabel.swift in Sources */, + 53A6BED31DD4BCA90016C058 /* MKMapView.swift in Sources */, 4A0E11001D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, 9ADE4A921DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, 9ADFE5A61DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */, @@ -1204,6 +1219,7 @@ 9ADFE5A21DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */, 9A1D06421D93EA7E00ACF44C /* UIDatePickerSpec.swift in Sources */, 9A6AAA0F1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */, + 53A6BED71DD4BD2C0016C058 /* MKMapViewSpec.swift in Sources */, 9A1D06381D93EA7E00ACF44C /* UIBarButtonItemSpec.swift in Sources */, 9A1E72BB1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, 9A6AAA2E1DB903A20013AAEA /* ReusableComponentsSpec.swift in Sources */, diff --git a/ReactiveCocoa/Shared/MKMapView.swift b/ReactiveCocoa/Shared/MKMapView.swift new file mode 100644 index 0000000000..a524a55aaf --- /dev/null +++ b/ReactiveCocoa/Shared/MKMapView.swift @@ -0,0 +1,12 @@ +import ReactiveSwift +import MapKit + +@available(tvOS 9.2, *) +extension Reactive where Base: MKMapView { + + /// Sets the map type. + public var mapType: BindingTarget { + return makeBindingTarget { $0.mapType = $1 } + } + +} diff --git a/ReactiveCocoaTests/Shared/MKMapViewSpec.swift b/ReactiveCocoaTests/Shared/MKMapViewSpec.swift new file mode 100644 index 0000000000..82fec00422 --- /dev/null +++ b/ReactiveCocoaTests/Shared/MKMapViewSpec.swift @@ -0,0 +1,40 @@ +import ReactiveSwift +import ReactiveCocoa +import Quick +import Nimble +import enum Result.NoError +import MapKit + +@available(tvOS 9.2, *) +class MKMapViewSpec: QuickSpec { + override func spec() { + var mapView: MKMapView! + weak var _mapView: MKMapView? + + beforeEach { + mapView = MKMapView(frame: .zero) + _mapView = mapView + } + + afterEach { + mapView = nil + // using toEventually(beNil()) here + // since it takes time to release MKMapView + expect(_mapView).toEventually(beNil()) + } + + it("should accept changes from bindings to its map type") { + expect(mapView.mapType) == MKMapType.standard + + let (pipeSignal, observer) = Signal.pipe() + + mapView.reactive.mapType <~ pipeSignal + + observer.send(value: MKMapType.satellite) + expect(mapView.mapType) == MKMapType.satellite + + observer.send(value: MKMapType.hybrid) + expect(mapView.mapType) == MKMapType.hybrid + } + } +} From 35bd5ab3d8ed4284ac1352cdb18b3c27024790e5 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Thu, 10 Nov 2016 16:12:08 +0100 Subject: [PATCH 0548/1028] Added a table of RAC5 counterparts to commonly used ObjC APIs. --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4af92a8c8b..a16b2011ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,6 +63,17 @@ Properties are now composable! They have many of the same operators as `Signal` The `Atomic.modify` closure now takes an `inout` instead of relying on a return value. This provides a minor speed boost, because the compiler can avoid a copy in some cases, but also makes it possible to return a separate value from the closure to be used after the lock is released. +### Migrating from the ReactiveObjC APIs + +| ReactiveObjC | ReactiveCocoa | +| ------------------ | ------------- | +| `rac_liftSelector:withSignals:` | Apply `combineLatest` to your signals, and pass the method as the action to `observeValues`. | +| `rac_signalForSelector:fromProtocol:` | `NSObject.reactive.trigger(for:from:)` and `NSObject.reactive.signal(for:from:)` | +| `rac_willDeallocSignal` | `NSObject.reactive.lifetime`, in conjunction with the `take(during:)` operator. | +| `RAC(label, text)` | UI components expose binding targets via `NSObject.reactive`, e.g. `UILabel.reactive.text`, that can be used with the `<~` unidirectional binding operator. | +| `RACObserve(object, keyPath)` | `NSObject.reactive.values(forKeyPath:)` | +| Control value changes, e.g. `textField.rac_text` | UI components expose signals via `NSObject.reactive`, e.g. `UITextField.reactive.continuousTextValues`. | + # 4.0 If you’re new to the Swift API and migrating from RAC 2, start with the [3.0 changes](#30). This section only covers the differences between `3.0` and `4.0`. From 1f216fd8957c03c9ac4adfca9a9d7a5f37e502b1 Mon Sep 17 00:00:00 2001 From: Eimantas Vaiciunas Date: Thu, 10 Nov 2016 18:31:18 +0200 Subject: [PATCH 0549/1028] Add a few `MKMapView` properties to reactive extension - `isZoomEnabled` - `isScrollEnabled` - `isPitchEnabled` -- **not** on `tvOS` - `isRotateAvailable` -- **not** on `tvOS` --- ReactiveCocoa/Shared/MKMapView.swift | 23 ++++++++++ ReactiveCocoaTests/Shared/MKMapViewSpec.swift | 46 +++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/ReactiveCocoa/Shared/MKMapView.swift b/ReactiveCocoa/Shared/MKMapView.swift index a524a55aaf..65e295e10a 100644 --- a/ReactiveCocoa/Shared/MKMapView.swift +++ b/ReactiveCocoa/Shared/MKMapView.swift @@ -9,4 +9,27 @@ extension Reactive where Base: MKMapView { return makeBindingTarget { $0.mapType = $1 } } + /// Sets if zoom is enabled for map. + public var isZoomEnabled: BindingTarget { + return makeBindingTarget { $0.isZoomEnabled = $1 } + } + + /// Sets if scroll is enabled for map. + public var isScrollEnabled: BindingTarget { + return makeBindingTarget { $0.isScrollEnabled = $1 } + } + + #if !os(tvOS) + /// Sets if pitch is enabled for map. + @available(tvOS, unavailable) + public var isPitchEnabled: BindingTarget { + return makeBindingTarget { $0.isPitchEnabled = $1 } + } + + /// Sets if rotation is enabled for map. + @available(tvOS, unavailable) + public var isRotateEnabled: BindingTarget { + return makeBindingTarget { $0.isRotateEnabled = $1 } + } + #endif } diff --git a/ReactiveCocoaTests/Shared/MKMapViewSpec.swift b/ReactiveCocoaTests/Shared/MKMapViewSpec.swift index 82fec00422..73117a8d07 100644 --- a/ReactiveCocoaTests/Shared/MKMapViewSpec.swift +++ b/ReactiveCocoaTests/Shared/MKMapViewSpec.swift @@ -36,5 +36,51 @@ class MKMapViewSpec: QuickSpec { observer.send(value: MKMapType.hybrid) expect(mapView.mapType) == MKMapType.hybrid } + + it("should accept changes from bindings to its zoom enabled state") { + expect(mapView.isZoomEnabled) == true + + let (pipeSignal, observer) = Signal.pipe() + + mapView.reactive.isZoomEnabled <~ pipeSignal + + observer.send(value: false) + expect(mapView.isZoomEnabled) == false + } + + it("should accept changes from bindings to its scroll enabled state") { + expect(mapView.isScrollEnabled) == true + + let (pipeSignal, observer) = Signal.pipe() + + mapView.reactive.isScrollEnabled <~ pipeSignal + + observer.send(value: false) + expect(mapView.isScrollEnabled) == false + } + + #if !os(tvOS) + it("should accept changes from bindings to its pitch enabled state") { + expect(mapView.isPitchEnabled) == true + + let (pipeSignal, observer) = Signal.pipe() + + mapView.reactive.isPitchEnabled <~ pipeSignal + + observer.send(value: false) + expect(mapView.isPitchEnabled) == false + } + + it("should accept changes from bindings to its rotate enabled state") { + expect(mapView.isRotateEnabled) == true + + let (pipeSignal, observer) = Signal.pipe() + + mapView.reactive.isRotateEnabled <~ pipeSignal + + observer.send(value: false) + expect(mapView.isRotateEnabled) == false + } + #endif } } From db9279a4a9a29da71a7b13223db9ba6d1415b47d Mon Sep 17 00:00:00 2001 From: Eimantas Date: Thu, 10 Nov 2016 19:55:26 +0200 Subject: [PATCH 0550/1028] Remove redundant availability API calls. --- ReactiveCocoa/Shared/MKMapView.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/ReactiveCocoa/Shared/MKMapView.swift b/ReactiveCocoa/Shared/MKMapView.swift index 65e295e10a..3e206bc388 100644 --- a/ReactiveCocoa/Shared/MKMapView.swift +++ b/ReactiveCocoa/Shared/MKMapView.swift @@ -21,13 +21,11 @@ extension Reactive where Base: MKMapView { #if !os(tvOS) /// Sets if pitch is enabled for map. - @available(tvOS, unavailable) public var isPitchEnabled: BindingTarget { return makeBindingTarget { $0.isPitchEnabled = $1 } } /// Sets if rotation is enabled for map. - @available(tvOS, unavailable) public var isRotateEnabled: BindingTarget { return makeBindingTarget { $0.isRotateEnabled = $1 } } From c030f624fddc59cb61219901497cc2ca014ef0a4 Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 11 Nov 2016 07:27:01 +0100 Subject: [PATCH 0551/1028] Revert "Trigger signal for optional, unbacked protocol requirements." This reverts commit c19280bbcc310987397f77e331eb98fc8187e16e. --- ReactiveCocoa/NSObject+Intercepting.swift | 30 +++------------- ReactiveCocoaTests/InterceptingSpec.swift | 44 +---------------------- 2 files changed, 5 insertions(+), 69 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 519c3fac65..f735a4af42 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -6,23 +6,12 @@ extension Reactive where Base: NSObject { /// Create a signal which sends a `next` event at the end of every invocation /// of `selector` on the object. /// - /// `trigger(for:from:)` can be used to intercept optional protocol - /// requirements by supplying the protocol as `protocol`. The instance need - /// not have a concrete implementation of the requirement. - /// - /// However, as Cocoa classes usually cache information about delegate - /// conformances, trigger signals for optional, unbacked protocol requirements - /// should be set up before the instance is assigned as the corresponding - /// delegate. - /// /// - parameters: /// - selector: The selector to observe. - /// - protocol: The protocol of the selector, or `nil` if the selector does - /// not belong to any protocol. /// /// - returns: /// A trigger signal. - public func trigger(for selector: Selector, from protocol: Protocol? = nil) -> Signal<(), NoError> { + public func trigger(for selector: Selector) -> Signal<(), NoError> { return base.synchronized { let map = associatedValue { _ in NSMutableDictionary() } @@ -33,7 +22,7 @@ extension Reactive where Base: NSObject { let (signal, observer) = Signal<(), NoError>.pipe() let isSuccessful = base._rac_setupInvocationObservation(for: selector, - protocol: `protocol`, + protocol: nil, receiver: observer.send(value:)) precondition(isSuccessful) @@ -47,23 +36,12 @@ extension Reactive where Base: NSObject { /// Create a signal which sends a `next` event, containing an array of bridged /// arguments, at the end of every invocation of `selector` on the object. /// - /// `trigger(for:from:)` can be used to intercept optional protocol - /// requirements by supplying the protocol as `protocol`. The instance need - /// not have a concrete implementation of the requirement. - /// - /// However, as Cocoa classes usually cache information about delegate - /// conformances, trigger signals for optional, unbacked protocol requirements - /// should be set up before the instance is assigned as the corresponding - /// delegate. - /// /// - parameters: /// - selector: The selector to observe. - /// - protocol: The protocol of the selector, or `nil` if the selector does - /// not belong to any protocol. /// /// - returns: /// A signal that sends an array of bridged arguments. - public func signal(for selector: Selector, from protocol: Protocol? = nil) -> Signal<[Any?], NoError> { + public func signal(for selector: Selector) -> Signal<[Any?], NoError> { return base.synchronized { let map = associatedValue { _ in NSMutableDictionary() } @@ -74,7 +52,7 @@ extension Reactive where Base: NSObject { let (signal, observer) = Signal<[Any?], NoError>.pipe() let isSuccessful = base._rac_setupInvocationObservation(for: selector, - protocol: `protocol`, + protocol: nil, argsReceiver: bridge(observer)) precondition(isSuccessful) diff --git a/ReactiveCocoaTests/InterceptingSpec.swift b/ReactiveCocoaTests/InterceptingSpec.swift index 286ecb70d9..a7be3de794 100644 --- a/ReactiveCocoaTests/InterceptingSpec.swift +++ b/ReactiveCocoaTests/InterceptingSpec.swift @@ -40,25 +40,6 @@ class InterceptingSpec: QuickSpec { expect(object.counter) == 2 } - it("should send a value when the selector is invoked without implementation") { - let selector = #selector(TestProtocol.optionalMethod) - - let signal = object.reactive.trigger(for: selector, - from: TestProtocol.self) - expect(object.responds(to: selector)) == true - - var counter = 0 - signal.observeValues { counter += 1 } - - expect(counter) == 0 - - (object as TestProtocol).optionalMethod!() - expect(counter) == 1 - - (object as TestProtocol).optionalMethod!() - expect(counter) == 2 - } - it("should complete when the object deinitializes") { let signal = object.reactive.trigger(for: #selector(object.increment)) @@ -256,25 +237,6 @@ class InterceptingSpec: QuickSpec { validate(arguments: arguments[2], offset: 2) } - it("should send a value when the selector is invoked without implementation") { - let selector = #selector(TestProtocol.optionalMethod) - - let signal = object.reactive.signal(for: selector, - from: TestProtocol.self) - expect(object.responds(to: selector)) == true - - var counter = 0 - signal.observeValues { _ in counter += 1 } - - expect(counter) == 0 - - (object as TestProtocol).optionalMethod!() - expect(counter) == 1 - - (object as TestProtocol).optionalMethod!() - expect(counter) == 2 - } - it("should complete when the object deinitializes") { let signal = object.reactive.signal(for: #selector(object.increment)) @@ -329,11 +291,7 @@ class InterceptingSpec: QuickSpec { } } -@objc protocol TestProtocol { - @objc optional func optionalMethod() -} - -class InterceptedObject: NSObject, TestProtocol { +class InterceptedObject: NSObject { var counter = 0 dynamic func increment() { From d0485950f9a4cc83e5689be8da148f091b7e5403 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Fri, 11 Nov 2016 07:35:29 +0100 Subject: [PATCH 0552/1028] Updated the ReactiveObjC API migration table. --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a16b2011ed..ac52e4fd32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,8 +67,9 @@ The `Atomic.modify` closure now takes an `inout` instead of relying on a return | ReactiveObjC | ReactiveCocoa | | ------------------ | ------------- | -| `rac_liftSelector:withSignals:` | Apply `combineLatest` to your signals, and pass the method as the action to `observeValues`. | -| `rac_signalForSelector:fromProtocol:` | `NSObject.reactive.trigger(for:from:)` and `NSObject.reactive.signal(for:from:)` | +| `rac_liftSelector:withSignals:` | Apply `combineLatest` to your signals, and pass the method as the action to `observeValues`.

    Signal.combineLatest([signal1, signal2])
    .observeValues(self.perform(first:second:))
    | +| `rac_signalForSelector:` | `NSObject.reactive.trigger(for:)` and `NSObject.reactive.signal(for:)` | +| `rac_signalForSelector:fromProtocol:` | Currently no counterpart. | | `rac_willDeallocSignal` | `NSObject.reactive.lifetime`, in conjunction with the `take(during:)` operator. | | `RAC(label, text)` | UI components expose binding targets via `NSObject.reactive`, e.g. `UILabel.reactive.text`, that can be used with the `<~` unidirectional binding operator. | | `RACObserve(object, keyPath)` | `NSObject.reactive.values(forKeyPath:)` | From 338c08fc3096945815ddaad96b24ba98dc639d85 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Fri, 11 Nov 2016 22:06:21 +0100 Subject: [PATCH 0553/1028] Update changelog. --- CHANGELOG.md | 150 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 129 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac52e4fd32..21127f8797 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # 5.0 +### Table of Contents +1. [Repository Split](#repository-split) +1. [API Renaming](#api-renaming) +1. [Changes in ReactiveSwift 1.0](#changes-in-reactiveswift-10) +1. [Migrating from the ReactiveObjC API](#migrating-from-the-reactiveobjc-api) + ### Repository Split In version 5.0, we split ReactiveCocoa into multiple repositories for reasons explained in the sections below. The following should help you get started with choosing the repositories you require: @@ -9,19 +15,19 @@ In version 5.0, we split ReactiveCocoa into multiple repositories for reasons ex **If you’re using both the Swift and Objective-C APIs**, you likely require both ReactiveCocoa and [ReactiveObjCBridge][], which depend on [ReactiveSwift][] and [ReactiveObjC][]. -##### ReactiveCocoa +#### ReactiveCocoa The ReactiveCocoa library is newly focused on Swift and the UI layers of Apple’s platforms, building on the work of [Rex](https://github.com/neilpa/Rex). Reactive programming provides significant benefit in UI programming. RAC 3 and 4 focused on building out the new core Swift API. But we feel that those APIs have matured and it’s time for RAC-friendly extensions to AppKit and UIKit. -##### ReactiveSwift +#### ReactiveSwift The core, platform-independent Swift APIs have been extracted to a new framework, [ReactiveSwift][]. As Swift continues to grow as a language and a platform, we hope that it will expand beyond Cocoa and Apple’s platforms. Separating the Swift code makes it possible to use the reactive paradigm on other platforms. [ReactiveSwift]: https://github.com/ReactiveCocoa/ReactiveSwift -##### ReactiveObjC +#### ReactiveObjC The 3.x and 4.x releases of ReactiveCocoa included the Objective-C code from ReactiveCocoa 2.x. That code has been moved to [ReactiveObjC][] because: 1. It’s independent of the Swift code @@ -39,7 +45,7 @@ This bridge is an important tool for users that are working in mixed-language co [ReactiveObjCBridge]: https://github.com/ReactiveCocoa/ReactiveObjCBridge -### API Names +### API Renaming We mostly adjusted the ReactiveCocoa API to follow the [Swift 3 API Design Guidelines](https://swift.org/blog/swift-3-api-design/), or to match the Cocoa and Foundation API changes that came with Swift 3 and the latest platform SDKs. @@ -47,34 +53,136 @@ Lots has changed, but if you're already migrating to Swift 3 then that should no **Tip:** You can apply all the suggested fix-its in the current scope by choosing Editor > Fix All In Scope from the main menu in Xcode, or by using the associated keyboard shortcut. -### Signal +### Changes in ReactiveSwift 1.0 -##### Lifetime Semantics +#### Signal: Lifetime Semantics Prior to RAC 5.0, `Signal`s lived and continued to emit values (and side effects) until they completed. This was very confusing, even for RAC veterans. So [changes have been made](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/2959) to the lifetime semantics. `Signal`s now live and continue to emit events only while either (a) they have observers or (b) they are retained. This clears up a number of unexpected cases and makes `Signal`s much less dangerous. -### SignalProducer - -### Properties +#### SignalProducer: `buffer` has been removed. +Consider using `Signal.pipe` for `buffer(0)`, `MutableProperty` for `buffer(1)` or `replayLazily(upTo: 1)` for `buffer(n)`. +#### Properties: Composition Properties are now composable! They have many of the same operators as `Signal` and `SignalProducer`: `map`, `filter`, `combineLatest`, `zip`, `flatten`, etc. -### Atomic +#### Properties: Lifetime Semantics +Composed properties, including those created via `Property(initial:then:)`, are semantically a view to their ultimate sources. In other words, the lifetime, the signal and the producer would respect the ultimate sources, and deinitialization of an instance of composed property would not have an effect on these. + +```swift +let property = MutableProperty(1) +var composed: Property = property.map { $0 + 10 } +composed.startWithValues { print("\($0)") } +composed = nil + +property.value = 2 +// The produced signal is still alive, printing `12` to the output stream. +``` + +#### Atomic: A more efficient `modify` -The `Atomic.modify` closure now takes an `inout` instead of relying on a return value. This provides a minor speed boost, because the compiler can avoid a copy in some cases, but also makes it possible to return a separate value from the closure to be used after the lock is released. +`Atomic.modify` now passes its value to the supplied action as an `inout`. This enables the compiler to optimize it as an in-place mutation, which benefits collections, large `struct`s and `struct`s with considerable amount of references. -### Migrating from the ReactiveObjC APIs +Moreover, `Atomic.modify` now returns the returned value from the supplied action, instead of the old value as in RAC 4.x, so as to reduce unnecessary copying. -| ReactiveObjC | ReactiveCocoa | -| ------------------ | ------------- | -| `rac_liftSelector:withSignals:` | Apply `combineLatest` to your signals, and pass the method as the action to `observeValues`.
    Signal.combineLatest([signal1, signal2])
    .observeValues(self.perform(first:second:))
    | -| `rac_signalForSelector:` | `NSObject.reactive.trigger(for:)` and `NSObject.reactive.signal(for:)` | -| `rac_signalForSelector:fromProtocol:` | Currently no counterpart. | -| `rac_willDeallocSignal` | `NSObject.reactive.lifetime`, in conjunction with the `take(during:)` operator. | -| `RAC(label, text)` | UI components expose binding targets via `NSObject.reactive`, e.g. `UILabel.reactive.text`, that can be used with the `<~` unidirectional binding operator. | -| `RACObserve(object, keyPath)` | `NSObject.reactive.values(forKeyPath:)` | -| Control value changes, e.g. `textField.rac_text` | UI components expose signals via `NSObject.reactive`, e.g. `UITextField.reactive.continuousTextValues`. | +```swift +// ReactiveCocoa 4.0 +let old = atomicCount.modify { $0 + 1 } + +// ReactiveSwift 1.0 +let old = atomicCount.modify { value in + let old = value + value += 1 + return old +} +``` +### Migrating from the ReactiveObjC API + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ReactiveObjCReactiveCocoa 5.0
    Primitives
    Cold `RACSignal``SignalProducer`
    Hot `RACSignal``Signal`
    Serial `RACCommand``Action`
    Concurrent `RACCommand`Currently no counterpart.
    Macros
    `RAC(label, text)`Discover binding targets via `.reactive` on UI components. +

    label.reactive.text \<~ viewModel.name

    +
    `RACObserve(object, keyPath)``NSObject.reactive.values(forKeyPath:)`
    NSObject interception
    `rac_willDeallocSignal``NSObject.reactive.lifetime`, in conjunction with the `take(during:)` operator. +

    signal.take(during: object.reactive.lifetime)

    +
    `rac_liftSelector:withSignals:`Apply `combineLatest` to your signals, and pass the method as the action to `observeValues`. +

    +

    +Signal.combineLatest([signal1, signal2])
    +	.observeValues(self.perform(first:second:))
    +				
    +

    +
    `rac_signalForSelector:``NSObject.reactive.trigger(for:)` and `NSObject.reactive.signal(for:)`
    `rac_signalForSelector:fromProtocol:`Currently no counterpart.
    Control bindings and observations
    Control value changes, e.g. `textField.rac_text`Discover control value signals via `.reactive` on UI components. +

    viewModel.searchString \<~ textField.reactive.textValues

    +
    `rac_signalForControlEvents:``UIControl.reactive.trigger(for:)`
    `rac_command`Discover action binding APIs via `.reactive` on UI components. +

    button.pressed = CocoaAction(viewModel.submitAction)

    +
    # 4.0 If you’re new to the Swift API and migrating from RAC 2, start with the [3.0 changes](#30). This section only covers the differences between `3.0` and `4.0`. From e16b1e71c378925aa373c2be8dd937efbaec8209 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Fri, 11 Nov 2016 22:20:55 +0100 Subject: [PATCH 0554/1028] Split the giant table. --- CHANGELOG.md | 91 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 58 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21127f8797..5a731d7e2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -98,90 +98,115 @@ let old = atomicCount.modify { value in ### Migrating from the ReactiveObjC API +#### Primitives + + + - + + - - + + - - + + - - - - - + + +
    ReactiveObjC ReactiveCocoa 5.0
    PrimitivesCold RACSignalSignalProducer
    Cold `RACSignal``SignalProducer`Hot RACSignalSignal
    Hot `RACSignal``Signal`Serial RACCommandAction
    Serial `RACCommand``Action`
    Concurrent `RACCommand`Concurrent RACCommand Currently no counterpart.
    + +#### Macros + + - + + + + - - + - + + +
    MacrosReactiveObjCReactiveCocoa 5.0
    `RAC(label, text)`Discover binding targets via `.reactive` on UI components. + RAC(label, text)Discover binding targets via .reactive on UI components.

    label.reactive.text \<~ viewModel.name

    `RACObserve(object, keyPath)`RACObserve(object, keyPath) `NSObject.reactive.values(forKeyPath:)`
    +#### NSObject interception + + - + + + + - - + - - + - - + + - + + +
    NSObject interceptionReactiveObjCReactiveCocoa 5.0
    `rac_willDeallocSignal``NSObject.reactive.lifetime`, in conjunction with the `take(during:)` operator. + rac_willDeallocSignalNSObject.reactive.lifetime, in conjunction with the take(during:) operator.

    signal.take(during: object.reactive.lifetime)

    `rac_liftSelector:withSignals:`Apply `combineLatest` to your signals, and pass the method as the action to `observeValues`. + rac_liftSelector:withSignals:Apply combineLatest to your signals, and pass the method as the action to observeValues.

    -

    -Signal.combineLatest([signal1, signal2])
    -	.observeValues(self.perform(first:second:))
    -				
    +
    Signal.combineLatest([signal1, signal2])
    +	.observeValues(self.perform(first:second:))

    `rac_signalForSelector:``NSObject.reactive.trigger(for:)` and `NSObject.reactive.signal(for:)`rac_signalForSelector:NSObject.reactive.trigger(for:) and NSObject.reactive.signal(for:)
    `rac_signalForSelector:fromProtocol:`rac_signalForSelector:fromProtocol: Currently no counterpart.
    +#### Control bindings and observations + + - + + + + - - + - - + + - - + +
    Control bindings and observationsReactiveObjCReactiveCocoa 5.0
    Control value changes, e.g. `textField.rac_text`Discover control value signals via `.reactive` on UI components. + Control value changes, e.g. textField.rac_textDiscover control value signals via .reactive on UI components.

    viewModel.searchString \<~ textField.reactive.textValues

    `rac_signalForControlEvents:``UIControl.reactive.trigger(for:)`rac_signalForControlEvents:UIControl.reactive.trigger(for:)
    `rac_command`Discover action binding APIs via `.reactive` on UI components. + rac_command`Discover action binding APIs via .reactive on UI components.

    button.pressed = CocoaAction(viewModel.submitAction)

    # 4.0 From 1839fff290f0a2deda61ebfbe2a97e01b2c17e58 Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 11 Nov 2016 22:24:11 +0100 Subject: [PATCH 0555/1028] Use the Xcode 8.1 sneak peek image for CI test. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ee29542168..6d0a4ba566 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: objective-c -osx_image: xcode8 +osx_image: xcode8.1sneakpeek before_install: true install: true git: From a16323db40439dd1935b0a0fdddc2b9130af4d6a Mon Sep 17 00:00:00 2001 From: Eimantas Vaiciunas Date: Sat, 12 Nov 2016 19:14:18 +0200 Subject: [PATCH 0556/1028] Make `UISlider` great again - Make `value` reactive; - Make `minimumValue` reactive; - Make `maximumValue` reactive; --- ReactiveCocoa.xcodeproj/project.pbxproj | 8 +++ ReactiveCocoa/UIKit/UISlider.swift | 21 ++++++++ ReactiveCocoaTests/UIKit/UISliderSpec.swift | 56 +++++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 ReactiveCocoa/UIKit/UISlider.swift create mode 100644 ReactiveCocoaTests/UIKit/UISliderSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 9586132056..713c1a6f3f 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -38,6 +38,8 @@ 53A6BED61DD4BD2C0016C058 /* MKMapViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED51DD4BD2C0016C058 /* MKMapViewSpec.swift */; }; 53A6BED71DD4BD2C0016C058 /* MKMapViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED51DD4BD2C0016C058 /* MKMapViewSpec.swift */; }; 53A6BED81DD4BD2C0016C058 /* MKMapViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED51DD4BD2C0016C058 /* MKMapViewSpec.swift */; }; + 53AC46CC1DD6F97400C799E1 /* UISlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53AC46CB1DD6F97400C799E1 /* UISlider.swift */; }; + 53AC46CF1DD6FC0000C799E1 /* UISliderSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53AC46CE1DD6FC0000C799E1 /* UISliderSpec.swift */; }; 57A4D2081BA13D7A00F7D4B1 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; 57A4D20A1BA13D7A00F7D4B1 /* ReactiveCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7DFBED081CDB8C9500EE435B /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */; }; @@ -277,6 +279,8 @@ 538DCB7C1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSLayoutConstraintSpec.swift; sourceTree = ""; }; 53A6BED11DD4BCA90016C058 /* MKMapView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MKMapView.swift; sourceTree = ""; }; 53A6BED51DD4BD2C0016C058 /* MKMapViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MKMapViewSpec.swift; sourceTree = ""; }; + 53AC46CB1DD6F97400C799E1 /* UISlider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISlider.swift; sourceTree = ""; }; + 53AC46CE1DD6FC0000C799E1 /* UISliderSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISliderSpec.swift; sourceTree = ""; }; 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Application.xcconfig"; sourceTree = ""; }; 57A4D2451BA13F9700F7D4B1 /* tvOS-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Base.xcconfig"; sourceTree = ""; }; @@ -511,6 +515,7 @@ 9A1D05F41D93E9F100ACF44C /* UILabel.swift */, 9A1D05F51D93E9F100ACF44C /* UIProgressView.swift */, 9A1D05F61D93E9F100ACF44C /* UISegmentedControl.swift */, + 53AC46CB1DD6F97400C799E1 /* UISlider.swift */, 9A1D05F71D93E9F100ACF44C /* UISwitch.swift */, 4ABEFDE11DCFC8560066A8C2 /* UITableView.swift */, 9A1D05FA1D93E9F100ACF44C /* UITextField.swift */, @@ -541,6 +546,7 @@ 9A1D06321D93EA7E00ACF44C /* UITextFieldSpec.swift */, 9A1D06331D93EA7E00ACF44C /* UITextViewSpec.swift */, 9A1D06351D93EA7E00ACF44C /* UIViewSpec.swift */, + 53AC46CE1DD6FC0000C799E1 /* UISliderSpec.swift */, ); path = UIKit; sourceTree = ""; @@ -1173,6 +1179,7 @@ CD0C45DF1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9A2E425F1DAA6737006D909F /* CocoaTarget.swift in Sources */, 9A1D06781D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */, + 53AC46CC1DD6F97400C799E1 /* UISlider.swift in Sources */, 9A1D060F1D93EA0000ACF44C /* UIView.swift in Sources */, 9A1D06051D93EA0000ACF44C /* UIDatePicker.swift in Sources */, 9A1D05E11D93E99100ACF44C /* NSObject+Association.swift in Sources */, @@ -1218,6 +1225,7 @@ 9A9DFEEA1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */, 9ADFE5A21DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */, 9A1D06421D93EA7E00ACF44C /* UIDatePickerSpec.swift in Sources */, + 53AC46CF1DD6FC0000C799E1 /* UISliderSpec.swift in Sources */, 9A6AAA0F1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */, 53A6BED71DD4BD2C0016C058 /* MKMapViewSpec.swift in Sources */, 9A1D06381D93EA7E00ACF44C /* UIBarButtonItemSpec.swift in Sources */, diff --git a/ReactiveCocoa/UIKit/UISlider.swift b/ReactiveCocoa/UIKit/UISlider.swift new file mode 100644 index 0000000000..e304d55947 --- /dev/null +++ b/ReactiveCocoa/UIKit/UISlider.swift @@ -0,0 +1,21 @@ +import UIKit +import ReactiveSwift + +extension Reactive where Base: UISlider { + + /// Sets slider's value. + public var value: BindingTarget { + return makeBindingTarget { $0.value = $1 } + } + + /// Sets slider's minimum value. + public var minimumValue: BindingTarget { + return makeBindingTarget { $0.minimumValue = $1 } + } + + /// Sets slider's maximum value. + public var maximumValue: BindingTarget { + return makeBindingTarget { $0.maximumValue = $1 } + } + +} diff --git a/ReactiveCocoaTests/UIKit/UISliderSpec.swift b/ReactiveCocoaTests/UIKit/UISliderSpec.swift new file mode 100644 index 0000000000..3faff6124f --- /dev/null +++ b/ReactiveCocoaTests/UIKit/UISliderSpec.swift @@ -0,0 +1,56 @@ +import Quick +import Nimble +import ReactiveCocoa +import ReactiveSwift +import enum Result.NoError +import UIKit + +class UISliderSpec: QuickSpec { + override func spec() { + var slider: UISlider! + weak var _slider: UISlider? + + beforeEach { + slider = UISlider() + _slider = slider + } + + afterEach { + slider = nil + expect(_slider).to(beNil()) + } + + it("should accept changes from bindings to its value") { + expect(slider.value) == 0.0 + + let (pipeSignal, observer) = Signal.pipe() + + slider.reactive.value <~ pipeSignal + + observer.send(value: 0.5) + expect(slider.value) ≈ 0.5 + } + + it("should accept changes from bindings to its minimum value") { + expect(slider.minimumValue) == 0.0 + + let (pipeSignal, observer) = Signal.pipe() + + slider.reactive.minimumValue <~ pipeSignal + + observer.send(value: 0.3) + expect(slider.minimumValue) ≈ 0.3 + } + + it("should accept changes from bindings to its maximum value") { + expect(slider.maximumValue) == 1.0 + + let (pipeSignal, observer) = Signal.pipe() + + slider.reactive.maximumValue <~ pipeSignal + + observer.send(value: 0.7) + expect(slider.maximumValue) ≈ 0.7 + } + } +} From aeb4f157f95ac47c331c0f891032ae270149fce4 Mon Sep 17 00:00:00 2001 From: Eimantas Vaiciunas Date: Sat, 12 Nov 2016 20:07:07 +0200 Subject: [PATCH 0557/1028] Sort spec files by name --- ReactiveCocoa.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 713c1a6f3f..3f376614d1 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -541,12 +541,12 @@ 9A1D062C1D93EA7E00ACF44C /* UILabelSpec.swift */, 9A1D062D1D93EA7E00ACF44C /* UIProgressViewSpec.swift */, 9A1D062E1D93EA7E00ACF44C /* UISegmentedControlSpec.swift */, + 53AC46CE1DD6FC0000C799E1 /* UISliderSpec.swift */, 9A1D062F1D93EA7E00ACF44C /* UISwitchSpec.swift */, 4ABEFDE31DCFCCD70066A8C2 /* UITableViewSpec.swift */, 9A1D06321D93EA7E00ACF44C /* UITextFieldSpec.swift */, 9A1D06331D93EA7E00ACF44C /* UITextViewSpec.swift */, 9A1D06351D93EA7E00ACF44C /* UIViewSpec.swift */, - 53AC46CE1DD6FC0000C799E1 /* UISliderSpec.swift */, ); path = UIKit; sourceTree = ""; From 348863cc8b872e4756b4902b895b590455212bbc Mon Sep 17 00:00:00 2001 From: Eimantas Vaiciunas Date: Sat, 12 Nov 2016 20:29:12 +0200 Subject: [PATCH 0558/1028] Exclude UIStepper from tvOS builds --- ReactiveCocoa.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index e516cd1a15..ff715c4bf5 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -20,7 +20,7 @@ Pod::Spec.new do |s| s.osx.source_files = "ReactiveCocoa/AppKit/*.{swift}" s.ios.source_files = "ReactiveCocoa/UIKit/*.{swift}" s.tvos.source_files = "ReactiveCocoa/UIKit/*.{swift}" - s.tvos.exclude_files = "ReactiveCocoa/UIKit/*{UIDatePicker,UISwitch}*" + s.tvos.exclude_files = "ReactiveCocoa/UIKit/*{UIDatePicker,UISwitch,UISlider}*" s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_map = "ReactiveCocoa/module.modulemap" From 7ce99a2b34724d279a6cb9b8237fc84abd80f23d Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 12 Nov 2016 23:59:48 +0100 Subject: [PATCH 0559/1028] Fixed a typo. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a731d7e2a..f33686792b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,7 +60,7 @@ Lots has changed, but if you're already migrating to Swift 3 then that should no Prior to RAC 5.0, `Signal`s lived and continued to emit values (and side effects) until they completed. This was very confusing, even for RAC veterans. So [changes have been made](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/2959) to the lifetime semantics. `Signal`s now live and continue to emit events only while either (a) they have observers or (b) they are retained. This clears up a number of unexpected cases and makes `Signal`s much less dangerous. #### SignalProducer: `buffer` has been removed. -Consider using `Signal.pipe` for `buffer(0)`, `MutableProperty` for `buffer(1)` or `replayLazily(upTo: 1)` for `buffer(n)`. +Consider using `Signal.pipe` for `buffer(0)`, `MutableProperty` for `buffer(1)` or `replayLazily(upTo: n)` for `buffer(n)`. #### Properties: Composition Properties are now composable! They have many of the same operators as `Signal` and `SignalProducer`: `map`, `filter`, `combineLatest`, `zip`, `flatten`, etc. From 39043dd5f68dd0179475a7ebae141a55e13fe2a2 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sun, 13 Nov 2016 03:48:55 +0100 Subject: [PATCH 0560/1028] Added a link in the readme to the changelog. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 293584ad59..d9b2cc906d 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ ⚠️ [Looking for the Objective-C API?][] ⚠️ [Still using Swift 2.x?][] +🎉 [Migrating from RAC 4.x?][CHANGELOG] + ## What is ReactiveSwift? __ReactiveSwift__ offers composable, declarative and flexible primitives that are built around the grand concept of ___streams of values over time___. These primitives can be used to uniformly represent common Cocoa and generic programming patterns that are fundamentally an act of observation. From a0c25c1a92c7abc5a3f42ab63ae86e836c5b7028 Mon Sep 17 00:00:00 2001 From: Eimantas Vaiciunas Date: Sun, 13 Nov 2016 08:20:47 +0200 Subject: [PATCH 0561/1028] Add signal for `continuousValues` --- ReactiveCocoa/UIKit/UISlider.swift | 10 ++++++++++ ReactiveCocoaTests/UIKit/UISliderSpec.swift | 12 ++++++++++++ 2 files changed, 22 insertions(+) diff --git a/ReactiveCocoa/UIKit/UISlider.swift b/ReactiveCocoa/UIKit/UISlider.swift index e304d55947..5ad5a97c99 100644 --- a/ReactiveCocoa/UIKit/UISlider.swift +++ b/ReactiveCocoa/UIKit/UISlider.swift @@ -1,5 +1,6 @@ import UIKit import ReactiveSwift +import enum Result.NoError extension Reactive where Base: UISlider { @@ -18,4 +19,13 @@ extension Reactive where Base: UISlider { return makeBindingTarget { $0.maximumValue = $1 } } + /// A signal of float values emitted by the slider while being dragged by + /// the user. + /// + /// - note: If slider's `isContinuous` property is `true` then values are + /// sent only when user releases the slider. + public var continuousValues: Signal { + return trigger(for: .valueChanged) + .map { [unowned base = self.base] in base.value } + } } diff --git a/ReactiveCocoaTests/UIKit/UISliderSpec.swift b/ReactiveCocoaTests/UIKit/UISliderSpec.swift index 3faff6124f..4fa8e36c3e 100644 --- a/ReactiveCocoaTests/UIKit/UISliderSpec.swift +++ b/ReactiveCocoaTests/UIKit/UISliderSpec.swift @@ -52,5 +52,17 @@ class UISliderSpec: QuickSpec { observer.send(value: 0.7) expect(slider.maximumValue) ≈ 0.7 } + + it("should emit user's changes for its value") { + slider.value = 0.25 + + var updatedValue: Float? + slider.reactive.continuousValues.observeValues { value in + updatedValue = value + } + + slider.sendActions(for: .valueChanged) + expect(updatedValue) ≈ 0.25 + } } } From 2fda531cb20e39ef4b845a2e564bf2147ef31f45 Mon Sep 17 00:00:00 2001 From: Eimantas Vaiciunas Date: Sun, 13 Nov 2016 15:32:31 +0200 Subject: [PATCH 0562/1028] Made false note true --- ReactiveCocoa/UIKit/UISlider.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa/UIKit/UISlider.swift b/ReactiveCocoa/UIKit/UISlider.swift index 5ad5a97c99..a40e1655b7 100644 --- a/ReactiveCocoa/UIKit/UISlider.swift +++ b/ReactiveCocoa/UIKit/UISlider.swift @@ -22,7 +22,7 @@ extension Reactive where Base: UISlider { /// A signal of float values emitted by the slider while being dragged by /// the user. /// - /// - note: If slider's `isContinuous` property is `true` then values are + /// - note: If slider's `isContinuous` property is `false` then values are /// sent only when user releases the slider. public var continuousValues: Signal { return trigger(for: .valueChanged) From 0a5d796980dbb7a8d351a76269ec128fca67d799 Mon Sep 17 00:00:00 2001 From: Eimantas Vaiciunas Date: Sun, 13 Nov 2016 16:52:22 +0200 Subject: [PATCH 0563/1028] Rename `continuousValues` to `values` --- ReactiveCocoa/UIKit/UISlider.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa/UIKit/UISlider.swift b/ReactiveCocoa/UIKit/UISlider.swift index a40e1655b7..03f9638a90 100644 --- a/ReactiveCocoa/UIKit/UISlider.swift +++ b/ReactiveCocoa/UIKit/UISlider.swift @@ -24,7 +24,7 @@ extension Reactive where Base: UISlider { /// /// - note: If slider's `isContinuous` property is `false` then values are /// sent only when user releases the slider. - public var continuousValues: Signal { + public var values: Signal { return trigger(for: .valueChanged) .map { [unowned base = self.base] in base.value } } From 4f02661c440a7c2562075e5c9ae4f851d4de925b Mon Sep 17 00:00:00 2001 From: Eimantas Vaiciunas Date: Sun, 13 Nov 2016 16:53:25 +0200 Subject: [PATCH 0564/1028] Fix tests for `values` signal --- ReactiveCocoaTests/UIKit/UISliderSpec.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoaTests/UIKit/UISliderSpec.swift b/ReactiveCocoaTests/UIKit/UISliderSpec.swift index 4fa8e36c3e..c7f15c2637 100644 --- a/ReactiveCocoaTests/UIKit/UISliderSpec.swift +++ b/ReactiveCocoaTests/UIKit/UISliderSpec.swift @@ -57,7 +57,7 @@ class UISliderSpec: QuickSpec { slider.value = 0.25 var updatedValue: Float? - slider.reactive.continuousValues.observeValues { value in + slider.reactive.values.observeValues { value in updatedValue = value } From 4e4e219904bf93fb9a21b85e0d2a51e6a25f9143 Mon Sep 17 00:00:00 2001 From: Eimantas Vaiciunas Date: Sun, 13 Nov 2016 18:09:42 +0200 Subject: [PATCH 0565/1028] Assert `nil` before triggering the action --- ReactiveCocoaTests/UIKit/UISliderSpec.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/ReactiveCocoaTests/UIKit/UISliderSpec.swift b/ReactiveCocoaTests/UIKit/UISliderSpec.swift index c7f15c2637..d3b572ea94 100644 --- a/ReactiveCocoaTests/UIKit/UISliderSpec.swift +++ b/ReactiveCocoaTests/UIKit/UISliderSpec.swift @@ -61,6 +61,7 @@ class UISliderSpec: QuickSpec { updatedValue = value } + expect(updatedValue).to(beNil()) slider.sendActions(for: .valueChanged) expect(updatedValue) ≈ 0.25 } From 44dde9c714827ca46bca3624b1778402def5589e Mon Sep 17 00:00:00 2001 From: Eimantas Vaiciunas Date: Sat, 12 Nov 2016 20:17:26 +0200 Subject: [PATCH 0566/1028] Make `UIStepper` value reactive --- ReactiveCocoa.xcodeproj/project.pbxproj | 8 +++++ ReactiveCocoa/UIKit/UIStepper.swift | 10 ++++++ ReactiveCocoaTests/UIKit/UIStepperSpec.swift | 34 ++++++++++++++++++++ 3 files changed, 52 insertions(+) create mode 100644 ReactiveCocoa/UIKit/UIStepper.swift create mode 100644 ReactiveCocoaTests/UIKit/UIStepperSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 3f376614d1..441e46a689 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -26,6 +26,8 @@ 4ABEFE2E1DCFD01F0066A8C2 /* NSTableViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE2C1DCFD0180066A8C2 /* NSTableViewSpec.swift */; }; 4ABEFE301DCFD0530066A8C2 /* NSCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE2F1DCFD0530066A8C2 /* NSCollectionView.swift */; }; 4ABEFE331DCFD0630066A8C2 /* NSCollectionViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE311DCFD05F0066A8C2 /* NSCollectionViewSpec.swift */; }; + 531866F81DD7920400D1285F /* UIStepper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531866F71DD7920400D1285F /* UIStepper.swift */; }; + 531866FA1DD7925600D1285F /* UIStepperSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531866F91DD7925600D1285F /* UIStepperSpec.swift */; }; 538DCB791DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */; }; 538DCB7A1DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */; }; 538DCB7B1DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */; }; @@ -275,6 +277,8 @@ 4ABEFE2C1DCFD0180066A8C2 /* NSTableViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSTableViewSpec.swift; sourceTree = ""; }; 4ABEFE2F1DCFD0530066A8C2 /* NSCollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSCollectionView.swift; sourceTree = ""; }; 4ABEFE311DCFD05F0066A8C2 /* NSCollectionViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSCollectionViewSpec.swift; sourceTree = ""; }; + 531866F71DD7920400D1285F /* UIStepper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIStepper.swift; sourceTree = ""; }; + 531866F91DD7925600D1285F /* UIStepperSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIStepperSpec.swift; sourceTree = ""; }; 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSLayoutConstraint.swift; sourceTree = ""; }; 538DCB7C1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSLayoutConstraintSpec.swift; sourceTree = ""; }; 53A6BED11DD4BCA90016C058 /* MKMapView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MKMapView.swift; sourceTree = ""; }; @@ -516,6 +520,7 @@ 9A1D05F51D93E9F100ACF44C /* UIProgressView.swift */, 9A1D05F61D93E9F100ACF44C /* UISegmentedControl.swift */, 53AC46CB1DD6F97400C799E1 /* UISlider.swift */, + 531866F71DD7920400D1285F /* UIStepper.swift */, 9A1D05F71D93E9F100ACF44C /* UISwitch.swift */, 4ABEFDE11DCFC8560066A8C2 /* UITableView.swift */, 9A1D05FA1D93E9F100ACF44C /* UITextField.swift */, @@ -547,6 +552,7 @@ 9A1D06321D93EA7E00ACF44C /* UITextFieldSpec.swift */, 9A1D06331D93EA7E00ACF44C /* UITextViewSpec.swift */, 9A1D06351D93EA7E00ACF44C /* UIViewSpec.swift */, + 531866F91DD7925600D1285F /* UIStepperSpec.swift */, ); path = UIKit; sourceTree = ""; @@ -1196,6 +1202,7 @@ 9AD0F06B1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, 4ABEFE1F1DCFCEF60066A8C2 /* UITableView.swift in Sources */, 9A1D06081D93EA0000ACF44C /* UIProgressView.swift in Sources */, + 531866F81DD7920400D1285F /* UIStepper.swift in Sources */, 538DCB7A1DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */, 9A1D06001D93EA0000ACF44C /* UIBarButtonItem.swift in Sources */, 9A1D05FF1D93EA0000ACF44C /* UIActivityIndicatorView.swift in Sources */, @@ -1220,6 +1227,7 @@ 9A1D064A1D93EA7E00ACF44C /* UISegmentedControlSpec.swift in Sources */, 9A1D063E1D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift in Sources */, 4191394E1DBA01A00043C9D1 /* UIGestureRecognizerSpec.swift in Sources */, + 531866FA1DD7925600D1285F /* UIStepperSpec.swift in Sources */, 9A1D06361D93EA7E00ACF44C /* UIActivityIndicatorViewSpec.swift in Sources */, 9A1D06461D93EA7E00ACF44C /* UILabelSpec.swift in Sources */, 9A9DFEEA1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */, diff --git a/ReactiveCocoa/UIKit/UIStepper.swift b/ReactiveCocoa/UIKit/UIStepper.swift new file mode 100644 index 0000000000..04e6a425db --- /dev/null +++ b/ReactiveCocoa/UIKit/UIStepper.swift @@ -0,0 +1,10 @@ +import UIKit +import ReactiveSwift + +extension Reactive where Base: UIStepper { + + /// Sets the stepper's value. + public var value: BindingTarget { + return makeBindingTarget { $0.value = $1 } + } +} diff --git a/ReactiveCocoaTests/UIKit/UIStepperSpec.swift b/ReactiveCocoaTests/UIKit/UIStepperSpec.swift new file mode 100644 index 0000000000..ffff0bddf1 --- /dev/null +++ b/ReactiveCocoaTests/UIKit/UIStepperSpec.swift @@ -0,0 +1,34 @@ +import Quick +import Nimble +import ReactiveCocoa +import ReactiveSwift +import enum Result.NoError +import UIKit + +class UIStepperSpec: QuickSpec { + override func spec() { + var stepper: UIStepper! + weak var _stepper: UIStepper? + + beforeEach { + stepper = UIStepper() + _stepper = stepper + } + + afterEach { + stepper = nil + expect(_stepper).to(beNil()) + } + + it("should accept changes from bindings to its value") { + expect(stepper.value) == 0.0 + + let (pipeSignal, observer) = Signal.pipe() + + stepper.reactive.value <~ pipeSignal + + observer.send(value: 0.5) + expect(stepper.value) ≈ 0.5 + } + } +} From 413eecb03398058ebdfa9472a7a665d7c4bd7ba4 Mon Sep 17 00:00:00 2001 From: Eimantas Vaiciunas Date: Mon, 14 Nov 2016 08:44:47 +0200 Subject: [PATCH 0567/1028] Make more `UIStepper` properties reactive - add `minimumValue`; - add `maximumValue`; - create `values` signal; - update podspec file; --- ReactiveCocoa.podspec | 2 +- ReactiveCocoa/UIKit/UIStepper.swift | 18 ++++++++++ ReactiveCocoaTests/UIKit/UIStepperSpec.swift | 35 ++++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index ff715c4bf5..ac8678a65c 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -20,7 +20,7 @@ Pod::Spec.new do |s| s.osx.source_files = "ReactiveCocoa/AppKit/*.{swift}" s.ios.source_files = "ReactiveCocoa/UIKit/*.{swift}" s.tvos.source_files = "ReactiveCocoa/UIKit/*.{swift}" - s.tvos.exclude_files = "ReactiveCocoa/UIKit/*{UIDatePicker,UISwitch,UISlider}*" + s.tvos.exclude_files = "ReactiveCocoa/UIKit/*{UIDatePicker,UISwitch,UISlider,UIStepper}*" s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_map = "ReactiveCocoa/module.modulemap" diff --git a/ReactiveCocoa/UIKit/UIStepper.swift b/ReactiveCocoa/UIKit/UIStepper.swift index 04e6a425db..12c4823dad 100644 --- a/ReactiveCocoa/UIKit/UIStepper.swift +++ b/ReactiveCocoa/UIKit/UIStepper.swift @@ -1,5 +1,6 @@ import UIKit import ReactiveSwift +import enum Result.NoError extension Reactive where Base: UIStepper { @@ -7,4 +8,21 @@ extension Reactive where Base: UIStepper { public var value: BindingTarget { return makeBindingTarget { $0.value = $1 } } + + /// Sets stepper's minimum value. + public var minimumValue: BindingTarget { + return makeBindingTarget { $0.minimumValue = $1 } + } + + /// Sets stepper's maximum value. + public var maximumValue: BindingTarget { + return makeBindingTarget { $0.maximumValue = $1 } + } + + /// A signal of double values emitted by the stepper upon each user's + /// interaction. + public var values: Signal { + return trigger(for: .valueChanged) + .map { [unowned base = self.base] in base.value } + } } diff --git a/ReactiveCocoaTests/UIKit/UIStepperSpec.swift b/ReactiveCocoaTests/UIKit/UIStepperSpec.swift index ffff0bddf1..ac2f841d88 100644 --- a/ReactiveCocoaTests/UIKit/UIStepperSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIStepperSpec.swift @@ -30,5 +30,40 @@ class UIStepperSpec: QuickSpec { observer.send(value: 0.5) expect(stepper.value) ≈ 0.5 } + + it("should accept changes from bindings to its minimum value") { + expect(stepper.minimumValue) == 0.0 + + let (pipeSignal, observer) = Signal.pipe() + + stepper.reactive.minimumValue <~ pipeSignal + + observer.send(value: 0.3) + expect(stepper.minimumValue) ≈ 0.3 + } + + it("should accept changes from bindings to its maximum value") { + expect(stepper.maximumValue) == 100.0 + + let (pipeSignal, observer) = Signal.pipe() + + stepper.reactive.maximumValue <~ pipeSignal + + observer.send(value: 33.0) + expect(stepper.maximumValue) ≈ 33.0 + } + + it("should emit user's changes for its value") { + stepper.value = 0.25 + + var updatedValue: Double? + stepper.reactive.values.observeValues { value in + updatedValue = value + } + + expect(updatedValue).to(beNil()) + stepper.sendActions(for: .valueChanged) + expect(updatedValue) ≈ 0.25 + } } } From 6742596b12d70f72c9e7726c582e289eea69bb29 Mon Sep 17 00:00:00 2001 From: Eimantas Vaiciunas Date: Mon, 14 Nov 2016 09:01:58 +0200 Subject: [PATCH 0568/1028] Sort UIKit specs by name --- ReactiveCocoa.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 441e46a689..28108e95dc 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -547,12 +547,12 @@ 9A1D062D1D93EA7E00ACF44C /* UIProgressViewSpec.swift */, 9A1D062E1D93EA7E00ACF44C /* UISegmentedControlSpec.swift */, 53AC46CE1DD6FC0000C799E1 /* UISliderSpec.swift */, + 531866F91DD7925600D1285F /* UIStepperSpec.swift */, 9A1D062F1D93EA7E00ACF44C /* UISwitchSpec.swift */, 4ABEFDE31DCFCCD70066A8C2 /* UITableViewSpec.swift */, 9A1D06321D93EA7E00ACF44C /* UITextFieldSpec.swift */, 9A1D06331D93EA7E00ACF44C /* UITextViewSpec.swift */, 9A1D06351D93EA7E00ACF44C /* UIViewSpec.swift */, - 531866F91DD7925600D1285F /* UIStepperSpec.swift */, ); path = UIKit; sourceTree = ""; From 94821eddbf86f092a92bc880e7fc8e2fcf694c0b Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Tue, 15 Nov 2016 13:34:27 +0900 Subject: [PATCH 0569/1028] Separate iOS-specific UIKit files into its own directory / group --- ReactiveCocoa.podspec | 3 +-- ReactiveCocoa.xcodeproj/project.pbxproj | 16 ++++++++++++---- ReactiveCocoa/UIKit/{ => iOS}/UIDatePicker.swift | 0 ReactiveCocoa/UIKit/{ => iOS}/UISlider.swift | 0 ReactiveCocoa/UIKit/{ => iOS}/UIStepper.swift | 0 ReactiveCocoa/UIKit/{ => iOS}/UISwitch.swift | 0 6 files changed, 13 insertions(+), 6 deletions(-) rename ReactiveCocoa/UIKit/{ => iOS}/UIDatePicker.swift (100%) rename ReactiveCocoa/UIKit/{ => iOS}/UISlider.swift (100%) rename ReactiveCocoa/UIKit/{ => iOS}/UIStepper.swift (100%) rename ReactiveCocoa/UIKit/{ => iOS}/UISwitch.swift (100%) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index ac8678a65c..14e5d7a6d8 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -18,9 +18,8 @@ Pod::Spec.new do |s| s.source_files = "ReactiveCocoa/*.{swift,h,m}", "ReactiveCocoa/Shared/*.{swift}" s.private_header_files = "ReactiveCocoa/RACObjCRuntimeUtilities.h" s.osx.source_files = "ReactiveCocoa/AppKit/*.{swift}" - s.ios.source_files = "ReactiveCocoa/UIKit/*.{swift}" + s.ios.source_files = "ReactiveCocoa/UIKit/*.{swift}", "ReactiveCocoa/UIKit/iOS/*.{swift}" s.tvos.source_files = "ReactiveCocoa/UIKit/*.{swift}" - s.tvos.exclude_files = "ReactiveCocoa/UIKit/*{UIDatePicker,UISwitch,UISlider,UIStepper}*" s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_map = "ReactiveCocoa/module.modulemap" diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 28108e95dc..642cd938b9 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -506,6 +506,7 @@ 9A1D05EB1D93E9F100ACF44C /* UIKit */ = { isa = PBXGroup; children = ( + CD91E3D41DDAC67700FA70D0 /* iOS */, 9A6AAA221DB8F51C0013AAEA /* ReusableComponents.swift */, 9A1D05EC1D93E9F100ACF44C /* UIActivityIndicatorView.swift */, 9A1D05ED1D93E9F100ACF44C /* UIBarButtonItem.swift */, @@ -513,15 +514,11 @@ 9A1D05EF1D93E9F100ACF44C /* UIButton.swift */, 4ABEFE231DCFCF5C0066A8C2 /* UICollectionView.swift */, 9A1D05F11D93E9F100ACF44C /* UIControl.swift */, - 9A1D05F21D93E9F100ACF44C /* UIDatePicker.swift */, 419139431DB910570043C9D1 /* UIGestureRecognizer.swift */, 9A1D05F31D93E9F100ACF44C /* UIImageView.swift */, 9A1D05F41D93E9F100ACF44C /* UILabel.swift */, 9A1D05F51D93E9F100ACF44C /* UIProgressView.swift */, 9A1D05F61D93E9F100ACF44C /* UISegmentedControl.swift */, - 53AC46CB1DD6F97400C799E1 /* UISlider.swift */, - 531866F71DD7920400D1285F /* UIStepper.swift */, - 9A1D05F71D93E9F100ACF44C /* UISwitch.swift */, 4ABEFDE11DCFC8560066A8C2 /* UITableView.swift */, 9A1D05FA1D93E9F100ACF44C /* UITextField.swift */, 9A1D05FB1D93E9F100ACF44C /* UITextView.swift */, @@ -593,6 +590,17 @@ name = Frameworks; sourceTree = ""; }; + CD91E3D41DDAC67700FA70D0 /* iOS */ = { + isa = PBXGroup; + children = ( + 9A1D05F21D93E9F100ACF44C /* UIDatePicker.swift */, + 53AC46CB1DD6F97400C799E1 /* UISlider.swift */, + 531866F71DD7920400D1285F /* UIStepper.swift */, + 9A1D05F71D93E9F100ACF44C /* UISwitch.swift */, + ); + path = iOS; + sourceTree = ""; + }; D04725E019E49ED7006002AA = { isa = PBXGroup; children = ( diff --git a/ReactiveCocoa/UIKit/UIDatePicker.swift b/ReactiveCocoa/UIKit/iOS/UIDatePicker.swift similarity index 100% rename from ReactiveCocoa/UIKit/UIDatePicker.swift rename to ReactiveCocoa/UIKit/iOS/UIDatePicker.swift diff --git a/ReactiveCocoa/UIKit/UISlider.swift b/ReactiveCocoa/UIKit/iOS/UISlider.swift similarity index 100% rename from ReactiveCocoa/UIKit/UISlider.swift rename to ReactiveCocoa/UIKit/iOS/UISlider.swift diff --git a/ReactiveCocoa/UIKit/UIStepper.swift b/ReactiveCocoa/UIKit/iOS/UIStepper.swift similarity index 100% rename from ReactiveCocoa/UIKit/UIStepper.swift rename to ReactiveCocoa/UIKit/iOS/UIStepper.swift diff --git a/ReactiveCocoa/UIKit/UISwitch.swift b/ReactiveCocoa/UIKit/iOS/UISwitch.swift similarity index 100% rename from ReactiveCocoa/UIKit/UISwitch.swift rename to ReactiveCocoa/UIKit/iOS/UISwitch.swift From fe2f27c7207f398b474e6f4cefe2dea2060db1fe Mon Sep 17 00:00:00 2001 From: Anders Date: Wed, 16 Nov 2016 04:09:07 +0100 Subject: [PATCH 0570/1028] Ported a few object interception test cases from ReactiveObjC. --- ReactiveCocoa/RACObjCRuntimeUtilities.h | 2 + ReactiveCocoa/RACObjCRuntimeUtilities.m | 4 + ReactiveCocoaTests/InterceptingSpec.swift | 620 +++++++++++++++++++++- 3 files changed, 624 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoa/RACObjCRuntimeUtilities.h b/ReactiveCocoa/RACObjCRuntimeUtilities.h index 79770eb5f1..968f3f2d88 100644 --- a/ReactiveCocoa/RACObjCRuntimeUtilities.h +++ b/ReactiveCocoa/RACObjCRuntimeUtilities.h @@ -1,6 +1,8 @@ #import NS_ASSUME_NONNULL_BEGIN +IMP _rac_objc_msgForward(); + @interface RACSwiftInvocationArguments : NSObject @property(readonly, nonatomic) NSInteger count; diff --git a/ReactiveCocoa/RACObjCRuntimeUtilities.m b/ReactiveCocoa/RACObjCRuntimeUtilities.m index 3d05d28e32..9bb9102922 100644 --- a/ReactiveCocoa/RACObjCRuntimeUtilities.m +++ b/ReactiveCocoa/RACObjCRuntimeUtilities.m @@ -22,6 +22,10 @@ return set; } +IMP _rac_objc_msgForward() { + return _objc_msgForward; +} + @interface RACForwardingInfo : NSObject @property (readonly, nonatomic) BOOL isTrigger; diff --git a/ReactiveCocoaTests/InterceptingSpec.swift b/ReactiveCocoaTests/InterceptingSpec.swift index a7be3de794..fcbcd472d6 100644 --- a/ReactiveCocoaTests/InterceptingSpec.swift +++ b/ReactiveCocoaTests/InterceptingSpec.swift @@ -90,6 +90,538 @@ class InterceptingSpec: QuickSpec { expect(isDeadlocked).toEventually(beFalsy()) } } + + it("should send completed on deallocation") { + var completed = false + var deallocated = false + + autoreleasepool { + let object = InterceptedObject() + + object.reactive.lifetime.ended.observeCompleted { + deallocated = true + } + + object.reactive.trigger(for: #selector(object.lifeIsGood)).observeCompleted { + completed = true + } + + expect(deallocated) == false + expect(completed) == false + } + + expect(deallocated) == true + expect(completed) == true + } + + it("should send arguments for invocation and invoke the original method on previously KVO'd receiver") { + var latestValue: Bool? + + object.reactive + .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) + .startWithValues { objectValue in + latestValue = objectValue as! Bool? + } + + expect(latestValue).to(beNil()) + + var firstValue: Bool? + var secondValue: String? + + object.reactive + .signal(for: #selector(object.set(first:second:))) + .observeValues { x in + firstValue = x[0] as! Bool? + secondValue = x[1] as! String? + } + + object.set(first: true, second: "Winner") + + expect(object.hasInvokedSetObjectValueAndSecondObjectValue) == true + expect(object.objectValue as! Bool?) == true + expect(object.secondObjectValue as! String?) == "Winner" + + expect(latestValue) == true + + expect(firstValue) == true + expect(secondValue) == "Winner" + } +/** + it("should send arguments for invocation and invoke the a KVO-swizzled then RAC-swizzled setter") { + var latestValue: Bool? + + object.reactive + .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) + .startWithValues { objectValue in + latestValue = objectValue as! Bool? + } + + expect(latestValue).to(beNil()) + + var value: Bool? + object.reactive.signal(for: #selector(setter: object.objectValue)).observeValues { x in + value = x[0] as! Bool? + } + + object.objectValue = true + + expect(object.objectValue as! Bool?) == true + expect(latestValue) == true + expect(value) == true + } +**/ + it("should send arguments for invocation and invoke the a RAC-swizzled then KVO-swizzled setter") { + let object = InterceptedObject() + + var value: Bool? + object.reactive.signal(for: #selector(setter: object.objectValue)).observeValues { x in + value = x[0] as! Bool? + } + + var latestValue: Bool? + + object.reactive + .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) + .startWithValues { objectValue in + latestValue = objectValue as! Bool? + } + + expect(latestValue).to(beNil()) + + object.objectValue = true + + expect(object.objectValue as! Bool?) == true + expect(latestValue) == true + expect(value) == true + } + + it("should send arguments for invocation and invoke the original method when receiver is subsequently KVO'd") { + let object = InterceptedObject() + + var firstValue: Bool? + var secondValue: String? + + object.reactive.signal(for: #selector(object.set(first:second:))).observeValues { x in + firstValue = x[0] as! Bool? + secondValue = x[1] as! String? + } + + var latestValue: Bool? + + object.reactive + .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) + .startWithValues { objectValue in + latestValue = objectValue as! Bool? + } + + expect(latestValue).to(beNil()) + + object.set(first: true, second: "Winner") + + expect(object.hasInvokedSetObjectValueAndSecondObjectValue) == true + expect(object.objectValue as! Bool?) == true + expect(object.secondObjectValue as! String?) == "Winner" + + expect(latestValue) == true + + expect(firstValue) == true + expect(secondValue) == "Winner" + } + + it("should properly implement -respondsToSelector: when called on KVO'd receiver") { + let object = InterceptedObject() + + // First, setup KVO on `object`, which gives us the desired side-effect + // of `object` taking on a KVO-custom subclass. + object.reactive + .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) + .start() + + let selector = NSSelectorFromString("anyOldSelector:") + + // With the KVO subclass in place, call -rac_signalForSelector: to + // implement -anyOldSelector: directly on the KVO subclass. + _ = object.reactive.trigger(for: selector) + + expect(object.responds(to: selector)) == true + } + + it("should properly implement -respondsToSelector: when called on signalForSelector'd receiver that has subsequently been KVO'd") { + let object = InterceptedObject() + + let selector = NSSelectorFromString("anyOldSelector:") + + // Implement -anyOldSelector: on the object first + _ = object.reactive.trigger(for: selector) + + expect(object.responds(to: selector)) == true + + // Then KVO the object + object.reactive + .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) + .start() + + expect(object.responds(to: selector)) == true + } + + it("should properly implement -respondsToSelector: when called on signalForSelector'd receiver that has subsequently been KVO'd, then signalForSelector'd again") { + let object = InterceptedObject() + + let selector = NSSelectorFromString("anyOldSelector:") + + // Implement -anyOldSelector: on the object first + _ = object.reactive.trigger(for: selector) + + expect(object.responds(to: selector)) == true + + // Then KVO the object + object.reactive + .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) + .start() + + expect(object.responds(to: selector)) == true + + let selector2 = NSSelectorFromString("anotherSelector:") + + // Then implement -anotherSelector: on the object + _ = object.reactive.trigger(for: selector2) + + expect(object.responds(to: selector2)) == true + } + + it("should call the right signal for two instances of the same class, adding signals for the same selector") { + let object1 = InterceptedObject() + let object2 = InterceptedObject() + + let selector = NSSelectorFromString("lifeIsGood:") + + var value1: Int? + object1.reactive.signal(for: selector).observeValues { x in + value1 = x[0] as! Int? + } + + var value2: Int? + object2.reactive.signal(for: selector).observeValues { x in + value2 = x[0] as! Int? + } + + object1.lifeIsGood(42) + expect(value1) == 42 + expect(value2).to(beNil()) + + object2.lifeIsGood(420) + expect(value1) == 42 + expect(value2) == 420 + } + + it("should send on signal after the original method is invoked") { + let object = InterceptedObject() + + var invokedMethodBefore = false + object.reactive.trigger(for: #selector(object.set(first:second:))).observeValues { + invokedMethodBefore = object.hasInvokedSetObjectValueAndSecondObjectValue + } + + object.set(first: true, second: "Winner") + expect(invokedMethodBefore) == true + } + } +/** + describe("interoperability") { + var invoked: Bool! + var object: InterceptedObject! + var originalClass: AnyClass! + + beforeEach { + invoked = false + object = InterceptedObject() + originalClass = InterceptedObject.self + } + + it("should invoke the swizzled `forwardInvocation:` on an instance isa-swizzled by both RAC and KVO.") { + object.reactive + .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) + .start() + + _ = object.reactive.trigger(for: #selector(object.lifeIsGood)) + + let swizzledSelector = #selector(object.lifeIsGood) + + // Redirect `swizzledSelector` to the forwarding machinery. + let method = class_getInstanceMethod(originalClass, swizzledSelector) + let typeDescription = method_getTypeEncoding(method) + let originalImp = class_replaceMethod(originalClass, swizzledSelector, _rac_objc_msgForward(), typeDescription) + + defer { + class_replaceMethod(originalClass, swizzledSelector, originalImp, typeDescription) + } + + // Swizzle `forwardInvocation:` to intercept `swizzledSelector`. + let patchForwardInvocationBlock: @convention(block) (AnyObject?, AnyObject?) -> Void = { _, invocation in + if ((invocation as! NSInvocationProtocol).selector == swizzledSelector) { + expect(invoked) == false + invoked = true + } + } + + let newForwardInvocation = imp_implementationWithBlock(patchForwardInvocationBlock as Any) + let oldForwardInvocation = class_replaceMethod(originalClass, NSObject.forwardInvocationSelector, newForwardInvocation, "v@:@") + + defer { + class_replaceMethod(originalClass, NSObject.forwardInvocationSelector, oldForwardInvocation, "v@:@") + } + + object.lifeIsGood(nil) + expect(invoked) == true + } + + it("should invoke the swizzled `forwardInvocation:` on an instance isa-swizzled by RAC.") { + _ = object.reactive.trigger(for: #selector(object.lifeIsGood)) + + let swizzledSelector = #selector(object.lifeIsGood) + + // Redirect `swizzledSelector` to the forwarding machinery. + let method = class_getInstanceMethod(originalClass, swizzledSelector) + let typeEncoding = method_getTypeEncoding(method) + let originalImp = class_replaceMethod(originalClass, swizzledSelector, _rac_objc_msgForward(), typeEncoding) + + defer { + class_replaceMethod(originalClass, swizzledSelector, originalImp, typeEncoding) + } + + // Swizzle `forwardInvocation:` to intercept `swizzledSelector`. + let patchForwardInvocationBlock: @convention(block) (AnyObject?, AnyObject?) -> Void = { _, invocation in + if ((invocation as! NSInvocationProtocol).selector == swizzledSelector) { + expect(invoked) == false + invoked = true + } + } + + let newForwardInvocation = imp_implementationWithBlock(patchForwardInvocationBlock as Any) + let oldForwardInvocation = class_replaceMethod(originalClass, NSObject.forwardInvocationSelector, newForwardInvocation, "v@:@") + + defer { + class_replaceMethod(originalClass, NSObject.forwardInvocationSelector, oldForwardInvocation, "v@:@") + } + + object.lifeIsGood(nil) + expect(invoked) == true + } + + it("should invoke the swizzled selector on an instance isa-swizzled by RAC.") { + _ = object.reactive.trigger(for: #selector(object.lifeIsGood)) + + let swizzledSelector = #selector(object.lifeIsGood) + + let method = class_getInstanceMethod(originalClass, swizzledSelector) + let typeEncoding = method_getTypeEncoding(method) + + let methodSwizzlingBlock: @convention(block) (AnyObject?, AnyObject?) -> Void = { _ in + expect(invoked) == false + invoked = true + } + + let newImplementation = imp_implementationWithBlock(methodSwizzlingBlock as Any) + let oldImplementation = class_replaceMethod(originalClass, swizzledSelector, newImplementation, typeEncoding) + + defer { + class_replaceMethod(originalClass, swizzledSelector, oldImplementation, typeEncoding) + } + + object.lifeIsGood(nil) + expect(invoked) == true + } + + it("should invoke the swizzled setter on an instance isa-swizzled by RAC.") { + _ = object.reactive.trigger(for: #selector(setter: object.objectValue)) + + let swizzledSelector = #selector(object.lifeIsGood) + + let method = class_getInstanceMethod(originalClass, swizzledSelector) + let typeEncoding = method_getTypeEncoding(method) + + let methodSwizzlingBlock: @convention(block) (AnyObject?, AnyObject?) -> Void = { _ in + expect(invoked) == false + invoked = true + } + + let newImplementation = imp_implementationWithBlock(methodSwizzlingBlock as Any) + let oldImplementation = class_replaceMethod(originalClass, swizzledSelector, newImplementation, typeEncoding) + + defer { + class_replaceMethod(originalClass, swizzledSelector, oldImplementation, typeEncoding) + } + + object.lifeIsGood(nil) + expect(invoked) == true + } + } +**/ + it("should swizzle an NSObject method") { + let object = NSObject() + + var value: [Any?]? + + object.reactive + .signal(for: #selector(getter: object.description)) + .observeValues { x in + value = x + } + + expect(value) == nil + + expect(object.description).notTo(beNil()) + expect(value).toNot(beNil()) + } + + describe("a class that already overrides -forwardInvocation:") { + it("should invoke the superclass' implementation") { + let object = ForwardInvocationTestObject() + + var value: Int? + object.reactive + .signal(for: #selector(object.lifeIsGood)) + .observeValues { x in + value = x[0] as! Int? + } + + object.lifeIsGood(42) + expect(value) == 42 + + expect(object.forwardedCount) == 0 + + object.perform(ForwardInvocationTestObject.forwardedSelector) + + expect(object.forwardedCount) == 1 + expect(object.forwardedSelector) == ForwardInvocationTestObject.forwardedSelector + } + + it("should not infinite recurse when KVO'd after RAC swizzled") { + let object = ForwardInvocationTestObject() + + var value: Int? + + object.reactive + .signal(for: #selector(object.lifeIsGood)) + .observeValues { x in + value = x[0] as! Int? + } + + object.reactive + .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) + .start() + + object.lifeIsGood(42) + expect(value) == 42 + + expect(object.forwardedCount) == 0 + + object.perform(ForwardInvocationTestObject.forwardedSelector) + + expect(object.forwardedCount) == 1 + expect(object.forwardedSelector) == ForwardInvocationTestObject.forwardedSelector + } + } + + describe("two classes in the same hierarchy") { + var superclassObj: InterceptedObject! + var superclassTuple: [Any?]? + + var subclassObj: InterceptedObjectSubclass! + var subclassTuple: [Any?]? + + beforeEach { + superclassObj = InterceptedObject() + expect(superclassObj).notTo(beNil()) + + subclassObj = InterceptedObjectSubclass() + expect(subclassObj).notTo(beNil()) + } + + it("should not collide") { + superclassObj.reactive + .signal(for: #selector(InterceptedObject.foo)) + .observeValues { args in + superclassTuple = args + } + + subclassObj + .reactive + .signal(for: #selector(InterceptedObject.foo)) + .observeValues { args in + subclassTuple = args + } + + expect(superclassObj.foo(40, "foo")) == "Not Subclass 40 foo" + + let expectedValues = [40, "foo"] as NSArray + expect(superclassTuple as NSArray?) == expectedValues + + expect(subclassObj.foo(40, "foo")) == "Subclass 40 foo" + + expect(subclassTuple as NSArray?) == expectedValues + } + + it("should not collide when the superclass is invoked asynchronously") { + superclassObj.reactive + .signal(for: #selector(InterceptedObject.set(first:second:))) + .observeValues { args in + superclassTuple = args + } + + subclassObj + .reactive + .signal(for: #selector(InterceptedObject.set(first:second:))) + .observeValues { args in + subclassTuple = args + } + + superclassObj.set(first: "foo", second:"42") + expect(superclassObj.hasInvokedSetObjectValueAndSecondObjectValue) == true + + let expectedValues = ["foo", "42"] as NSArray + expect(superclassTuple as NSArray?) == expectedValues + + subclassObj.set(first: "foo", second:"42") + expect(subclassObj.hasInvokedSetObjectValueAndSecondObjectValue) == false + expect(subclassObj.hasInvokedSetObjectValueAndSecondObjectValue).toEventually(beTruthy()) + + expect(subclassTuple as NSArray?) == expectedValues + } + } + + describe("class reporting") { + var object: InterceptedObject! + var originalClass: AnyClass! + + beforeEach { + object = InterceptedObject() + originalClass = InterceptedObject.self + } + + it("should report the original class") { + _ = object.reactive.trigger(for: #selector(object.lifeIsGood)) + expect(object._class).to(beIdenticalTo(originalClass)) + } + + it("should report the original class when it's KVO'd after dynamically subclassing") { + _ = object.reactive.trigger(for: #selector(object.lifeIsGood)) + + object.reactive + .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) + .start() + + expect(object._class).to(beIdenticalTo(originalClass)) + } + + it("should report the original class when it's KVO'd before dynamically subclassing") { + object.reactive + .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) + .start() + + _ = object.reactive.trigger(for: #selector(object.lifeIsGood)) + expect(object._class).to(beIdenticalTo(originalClass)) + } } describe("signal(for:)") { @@ -291,13 +823,97 @@ class InterceptingSpec: QuickSpec { } } -class InterceptedObject: NSObject { +extension NSObject { + fileprivate static var forwardInvocationSelector: Selector { + return Selector((("forwardInvocation:"))) + } + + /// Get the runtime class through the Objective-C `-class` instance method. + /// + /// Swift's `type(of:)` does not respect NSObject's `-class`, and returns the + /// runtime subclasses. + public var _class: AnyClass { + typealias ClassMethod = @convention(c) (AnyObject?, Selector?) -> AnyClass + + let classSelector = Selector((("class"))) + let method = class_getInstanceMethod(object_getClass(self), classSelector) + let impl = unsafeBitCast(method_getImplementation(method), to: ClassMethod.self) + return impl(self, classSelector) + } +} + +private class ForwardInvocationTestObject: InterceptedObject { + static let forwardedSelector = Selector((("forwarded"))) + + var forwardedCount = 0 + var forwardedSelector: Selector? + + override open class func initialize() { + struct Static { + static var token: Int = { + let impl: @convention(block) (AnyObject?, AnyObject?) -> Void = { object, invocation in + let object = object as! ForwardInvocationTestObject + object.forwardedCount += 1 + object.forwardedSelector = invocation!.selector + } + + let isSuccessful = class_addMethod(ForwardInvocationTestObject.self, NSObject.forwardInvocationSelector, imp_implementationWithBlock(impl as Any), UnsafeRawPointer(("v@:@" as StaticString).utf8Start).assumingMemoryBound(to: Int8.self)) + assert(isSuccessful) + assert(ForwardInvocationTestObject.instancesRespond(to: NSObject.forwardInvocationSelector)) + + let isSuccessful2 = class_addMethod(ForwardInvocationTestObject.self, ForwardInvocationTestObject.forwardedSelector, _rac_objc_msgForward(), UnsafeRawPointer(("v@:" as StaticString).utf8Start).assumingMemoryBound(to: Int8.self)) + assert(isSuccessful) + assert(ForwardInvocationTestObject.instancesRespond(to: ForwardInvocationTestObject.forwardedSelector)) + + return 0 + }() + } + + _ = Static.token + } +} + +@objc private protocol NSInvocationProtocol { + var target: NSObject { get } + var selector: Selector { get } + + func invoke() +} + +private class InterceptedObjectSubclass: InterceptedObject { + dynamic override func foo(_ number: Int, _ string: String) -> String { + return "Subclass \(number) \(string)" + } + + dynamic override func set(first: Any?, second: Any?) { + DispatchQueue.main.async { + super.set(first: first, second: second) + } + } +} + +private class InterceptedObject: NSObject { var counter = 0 + dynamic var hasInvokedSetObjectValueAndSecondObjectValue = false + dynamic var objectValue: Any? + dynamic var secondObjectValue: Any? dynamic func increment() { counter += 1 } - + + dynamic func foo(_ number: Int, _ string: String) -> String { + return "Not Subclass \(number) \(string)" + } + + dynamic func lifeIsGood(_ value: Any?) {} + dynamic func set(first: Any?, second: Any?) { + objectValue = first + secondObjectValue = second + + hasInvokedSetObjectValueAndSecondObjectValue = true + } + dynamic func testNumericValues(c: CChar, s: CShort, i: CInt, l: CLong, ll: CLongLong, uc: CUnsignedChar, us: CUnsignedShort, ui: CUnsignedInt, ul: CUnsignedLong, ull: CUnsignedLongLong, f: CFloat, d: CDouble, b: CBool) {} dynamic func testReferences(nonnull: NSObject, nullable: NSObject?, iuo: NSObject!, class: AnyClass, nullableClass: AnyClass?, iuoClass: AnyClass!) {} dynamic func testBridgedStructs(p: CGPoint, s: CGSize, r: CGRect, a: CGAffineTransform) {} From 82b595beaa6a03c696dbe710febc3a0ce3794f38 Mon Sep 17 00:00:00 2001 From: Anders Date: Wed, 16 Nov 2016 05:06:19 +0100 Subject: [PATCH 0571/1028] Update Travis CI config to use the Xcode 8.1 image. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ee29542168..aa4511bbc1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: objective-c -osx_image: xcode8 +osx_image: xcode8.1 before_install: true install: true git: From 4edaabed23e4df6098a197790a480779f36698fa Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Wed, 16 Nov 2016 06:44:27 +0100 Subject: [PATCH 0572/1028] Update changelog. --- CHANGELOG.md | 100 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 98 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f33686792b..b13f2896b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,8 @@ ### Table of Contents 1. [Repository Split](#repository-split) -1. [API Renaming](#api-renaming) +1. [Swift 3.0 API Renaming](#swift-30-api-renaming) +1. [New in 5.0: Cocoa Extensions](#new-in-50-cocoa-extensions) 1. [Changes in ReactiveSwift 1.0](#changes-in-reactiveswift-10) 1. [Migrating from the ReactiveObjC API](#migrating-from-the-reactiveobjc-api) @@ -45,7 +46,7 @@ This bridge is an important tool for users that are working in mixed-language co [ReactiveObjCBridge]: https://github.com/ReactiveCocoa/ReactiveObjCBridge -### API Renaming +### Swift 3.0 API Renaming We mostly adjusted the ReactiveCocoa API to follow the [Swift 3 API Design Guidelines](https://swift.org/blog/swift-3-api-design/), or to match the Cocoa and Foundation API changes that came with Swift 3 and the latest platform SDKs. @@ -53,6 +54,88 @@ Lots has changed, but if you're already migrating to Swift 3 then that should no **Tip:** You can apply all the suggested fix-its in the current scope by choosing Editor > Fix All In Scope from the main menu in Xcode, or by using the associated keyboard shortcut. +### New in 5.0: Cocoa Extensions + +#### Foundation: Object Interception + +RAC 5.0 includes a few object interception tools from ReactiveObjC, remastered for ReactiveSwift. + +1. **Method Call Interception** + + Create signals that are sourced by intercepting Objective-C objects. + + ```swift + // Notify after every time `viewWillAppear(_:)` is called. + let appearing = view.reactive.trigger(for: #selector(viewWillAppear(_:))) + ``` + +1. **Object Lifetime** + + Obtain a `Lifetime` token for any `NSObject` to observe their deinitialization. + + ```swift + // Observe the lifetime of `object`. + object.reactive.lifetime.ended.observeCompleted(doCleanup) + ``` + +1. **Expressive, Safe Key Path Observation** + + Establish key-value observations in the form of [`SignalProducer`][]s and + strong-typed `DynamicProperty`s, and enjoy the inherited composability. + + ```swift + // A producer that sends the current value of `keyPath`, followed by + // subsequent changes. + // + // Terminate the KVO observation if the lifetime of `self` ends. + let producer = object.reactive.values(forKeyPath: #keyPath(key)) + .take(during: self.reactive.lifetime) + + // A parameterized property that represents the supplied key path of the + // wrapped object. It holds a weak reference to the wrapped object. + let property = DynamicProperty(object: person, + keyPath: #keyPath(person.name)) + ``` + +These are accessible via the `reactive` magic property that is available on any ObjC objects. + +#### AppKit & UIKit: UI bindings + +UI components now expose a collection of binding targets to which can be bound from any arbitrary streams of values. + +1. **UI Bindings** + + UI components exposes [`BindingTarget`][]s, which accept bindings from any + kind of streams of values via the `<~` operator. + + ```swift + // Bind the `name` property of `person` to the text value of an `UILabel`. + nameLabel.text <~ person.name + ``` + +1. **Controls and User Interactions** + + Interactive UI components expose [`Signal`][]s for control events + and updates in the control value upon user interactions. + + A selected set of controls provide a convenience, expressive binding + API for [`Action`][]s. + + + ```swift + // Update `allowsCookies` whenever the toggle is flipped. + preferences.allowsCookies <~ toggle.reactive.isOnValues + + // Compute live character counts from the continuous stream of user initiated + // changes in the text. + textField.reactive.continuousTextValues.map { $0.characters.count } + + // Trigger `commit` whenever the button is pressed. + button.reactive.pressed = CocoaAction(viewModel.commit) + ``` + +These are accessible via the `reactive` magic property that is available on any ObjC objects. + ### Changes in ReactiveSwift 1.0 #### Signal: Lifetime Semantics @@ -96,6 +179,14 @@ let old = atomicCount.modify { value in } ``` +#### BindingTarget + +The new `BindingTargetProtocol` protocol has been formally introduced to represent an entity to which can form a unidirectional binding via the `<~` operator. A new type `BindingTarget` has also been introduced to represent non-observable targets that are expected to only be written to. + +#### Lifetime + +`Lifetime` is introduced to represent the lifetime of any arbitrary reference types. It works by completing the signal when its wrapping `Lifetime.Token` deinitializes with the associated reference type. + ### Migrating from the ReactiveObjC API #### Primitives @@ -663,3 +754,8 @@ soon as possible on the main thread—even synchronously (if possible), thereby replacing RAC 2’s `-performOnMainThread` operator—while `QueueScheduler.mainQueueScheduler` will always enqueue work after the current run loop iteration, and can be used to schedule work at a future date. + +[`Signal`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#signals +[`SignalProducer`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#signal-producers +[`Action`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#actions +[`BindingTarget`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#binding-target From 803b0a5e1150cb8bd0a6bb26764d4f09956311f9 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Wed, 16 Nov 2016 06:54:04 +0100 Subject: [PATCH 0573/1028] Added a few bits about `BindingTarget` and `Lifetime`. --- CHANGELOG.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b13f2896b2..20c26b23d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -181,11 +181,27 @@ let old = atomicCount.modify { value in #### BindingTarget -The new `BindingTargetProtocol` protocol has been formally introduced to represent an entity to which can form a unidirectional binding via the `<~` operator. A new type `BindingTarget` has also been introduced to represent non-observable targets that are expected to only be written to. +The new `BindingTargetProtocol` protocol has been formally introduced to represent an entity to which can form a unidirectional binding using the `<~` operator. A new type `BindingTarget` has also been introduced to represent non-observable targets that are expected to only be written to. + +```swift +// The `UIControl` exposes a `isEnabled` binding target. +control.isEnabled <~ viewModel.isEnabled +``` #### Lifetime -`Lifetime` is introduced to represent the lifetime of any arbitrary reference types. It works by completing the signal when its wrapping `Lifetime.Token` deinitializes with the associated reference type. +`Lifetime` is introduced to represent the lifetime of any arbitrary reference types. It works by completing the signal when its wrapping `Lifetime.Token` deinitializes with the associated reference type. While it is provided as `NSObject.reactive.lifetime` on Objective-C objects, it can also be associated manually with Swift classes to provide the same semantics. + +```swift +public final class MyController { + private let token = Lifetime.Token() + public let lifetime: Lifetime + + public init() { + lifetime = Lifetime(token) + } +} +``` ### Migrating from the ReactiveObjC API From 876157923ef03201213fb0cfd61084a3da3160fd Mon Sep 17 00:00:00 2001 From: Anders Date: Wed, 16 Nov 2016 18:43:43 +0100 Subject: [PATCH 0574/1028] Migrated test cases to use latest Dispatch APIs when appropriate. --- ReactiveCocoaTests/InterceptingSpec.swift | 18 +++++++++++++++--- ReactiveCocoaTests/LifetimeSpec.swift | 18 +++++++++++++++--- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/ReactiveCocoaTests/InterceptingSpec.swift b/ReactiveCocoaTests/InterceptingSpec.swift index fb917fbd36..2cb907ba24 100644 --- a/ReactiveCocoaTests/InterceptingSpec.swift +++ b/ReactiveCocoaTests/InterceptingSpec.swift @@ -76,13 +76,25 @@ class InterceptingSpec: QuickSpec { for _ in 1 ... 10 { var isDeadlocked = true - DispatchQueue.global(priority: .high).async { - _ = object.reactive.trigger(for: #selector(object.increment)) + if #available(*, macOS 10.10) { + DispatchQueue.global(qos: .userInitiated).async { + _ = object.reactive.trigger(for: #selector(object.increment)) + + DispatchQueue.global(qos: .userInitiated).async { + _ = object.reactive.trigger(for: #selector(object.increment)) + isDeadlocked = false + } + } + } else { DispatchQueue.global(priority: .high).async { _ = object.reactive.trigger(for: #selector(object.increment)) - isDeadlocked = false + DispatchQueue.global(priority: .high).async { + _ = object.reactive.trigger(for: #selector(object.increment)) + + isDeadlocked = false + } } } diff --git a/ReactiveCocoaTests/LifetimeSpec.swift b/ReactiveCocoaTests/LifetimeSpec.swift index 0d17d366ae..0605e5c003 100644 --- a/ReactiveCocoaTests/LifetimeSpec.swift +++ b/ReactiveCocoaTests/LifetimeSpec.swift @@ -25,13 +25,25 @@ class LifetimeSpec: QuickSpec { for _ in 1 ... 10 { var isDeadlocked = true - DispatchQueue.global(priority: .high).async { - _ = object.reactive.lifetime + if #available(*, macOS 10.10) { + DispatchQueue.global(qos: .userInitiated).async { + _ = object.reactive.lifetime + + DispatchQueue.global(qos: .userInitiated).async { + _ = object.reactive.lifetime + isDeadlocked = false + } + } + } else { DispatchQueue.global(priority: .high).async { _ = object.reactive.lifetime - isDeadlocked = false + DispatchQueue.global(priority: .high).async { + _ = object.reactive.lifetime + + isDeadlocked = false + } } } From 1f3a1eccdacfaeff7a8e23451ffb60949343deb9 Mon Sep 17 00:00:00 2001 From: Anders Date: Wed, 16 Nov 2016 18:44:19 +0100 Subject: [PATCH 0575/1028] Rewrote an occasionally failing async KVO test case. --- .../KeyValueObservingSpec.swift | 52 +++++++++++++------ 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/ReactiveCocoaTests/KeyValueObservingSpec.swift b/ReactiveCocoaTests/KeyValueObservingSpec.swift index 257d201a9b..7a3a701e00 100644 --- a/ReactiveCocoaTests/KeyValueObservingSpec.swift +++ b/ReactiveCocoaTests/KeyValueObservingSpec.swift @@ -299,37 +299,55 @@ class KeyValueObservingSpec: QuickSpec { } it("async disposal of signal with in-flight changes") { - let (teardown, teardownObserver) = Signal<(), NoError>.pipe() let otherScheduler: QueueScheduler + + var token = Optional(Lifetime.Token()) + let lifetime = Lifetime(token!) + if #available(*, OSX 10.10) { otherScheduler = QueueScheduler(name: "\(#file):\(#line)") } else { otherScheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) } - let replayProducer = testObject.reactive - .values(forKeyPath: #keyPath(ObservableObject.rac_value)) - .map { $0 as! NSNumber } - .map { $0.intValue } - .map { $0 % 2 == 0 } - .observe(on: otherScheduler) - .take(until: teardown) - .replayLazily(upTo: 1) + // Create a scope to limit the lifetime `values(forKeyPath:)`. + var replayProducer: SignalProducer? = { + return testObject.reactive + .values(forKeyPath: #keyPath(ObservableObject.rac_value)) + .map { $0 as! NSNumber } + .map { $0.intValue } + .map { $0 % 2 == 0 } + .observe(on: otherScheduler) + .take(during: lifetime) + .replayLazily(upTo: 1) + }() - replayProducer.start { _ in } + // `replayProducer` ceases its reference to the values producer after + // it starts. + replayProducer!.start() - iterationQueue.async { - DispatchQueue.concurrentPerform(iterations: numIterations) { index in + iterationQueue.suspend() + + let half = numIterations / 2 + + for index in 0 ..< numIterations { + iterationQueue.async { [testObject = testObject!] in testObject.rac_value = index } - } - iterationQueue.async(flags: .barrier) { - teardownObserver.send(value: ()) + if index == half { + iterationQueue.async(flags: .barrier) { [replayProducer = replayProducer!] in + token = nil + expect(replayProducer.last()).toNot(beNil()) + } + } } - let event = replayProducer.last() - expect(event).toNot(beNil()) + testObject = nil + replayProducer = nil + iterationQueue.resume() + + iterationQueue.sync(flags: .barrier, execute: {}) } } } From a94ec1f3836c5f896642cbc9754f54a26ccbcc5d Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Wed, 16 Nov 2016 22:06:30 +0100 Subject: [PATCH 0576/1028] Include prerelease tags in Carthage/Pods tutorial. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d9b2cc906d..2659a71aa7 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ If you use [Carthage][] to manage your dependencies, simply add ReactiveCocoa to your `Cartfile`: ``` -github "ReactiveCocoa/ReactiveCocoa" +github "ReactiveCocoa/ReactiveCocoa" "5.0.0-alpha.3" ``` If you use Carthage to build your dependencies, make sure you have added `ReactiveCocoa.framework`, `ReactiveSwift.framework`, and `Result.framework` to the "_Linked Frameworks and Libraries_" section of your target, and have included them in your Carthage framework copying build phase. @@ -103,7 +103,7 @@ If you use [CocoaPods][] to manage your dependencies, simply add ReactiveCocoa to your `Podfile`: ``` -pod 'ReactiveCocoa', :git => 'https://github.com/ReactiveCocoa/ReactiveCocoa.git' +pod 'ReactiveCocoa', '5.0.0-alpha.3' ``` #### Git submodule From db25d569b0d81e6cf68fdf5732a6e7a8c82d7a71 Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 17 Nov 2016 03:52:17 +0100 Subject: [PATCH 0577/1028] Streamlined 3 test cases from KVO, Lifetime and Intercepting spec. --- ReactiveCocoaTests/InterceptingSpec.swift | 26 ++++++++----------- .../KeyValueObservingSpec.swift | 16 +++--------- ReactiveCocoaTests/LifetimeSpec.swift | 26 ++++++++----------- 3 files changed, 26 insertions(+), 42 deletions(-) diff --git a/ReactiveCocoaTests/InterceptingSpec.swift b/ReactiveCocoaTests/InterceptingSpec.swift index 2cb907ba24..7844b4fe0a 100644 --- a/ReactiveCocoaTests/InterceptingSpec.swift +++ b/ReactiveCocoaTests/InterceptingSpec.swift @@ -76,25 +76,21 @@ class InterceptingSpec: QuickSpec { for _ in 1 ... 10 { var isDeadlocked = true - if #available(*, macOS 10.10) { - DispatchQueue.global(qos: .userInitiated).async { - _ = object.reactive.trigger(for: #selector(object.increment)) + func createQueue() -> DispatchQueue { + if #available(*, macOS 10.10) { + return .global(qos: .userInitiated) + } else { + return .global(priority: .high) + } + } - DispatchQueue.global(qos: .userInitiated).async { - _ = object.reactive.trigger(for: #selector(object.increment)) + createQueue().async { + _ = object.reactive.trigger(for: #selector(object.increment)) - isDeadlocked = false - } - } - } else { - DispatchQueue.global(priority: .high).async { + createQueue().async { _ = object.reactive.trigger(for: #selector(object.increment)) - DispatchQueue.global(priority: .high).async { - _ = object.reactive.trigger(for: #selector(object.increment)) - - isDeadlocked = false - } + isDeadlocked = false } } diff --git a/ReactiveCocoaTests/KeyValueObservingSpec.swift b/ReactiveCocoaTests/KeyValueObservingSpec.swift index 7a3a701e00..93d48db4dc 100644 --- a/ReactiveCocoaTests/KeyValueObservingSpec.swift +++ b/ReactiveCocoaTests/KeyValueObservingSpec.swift @@ -310,9 +310,7 @@ class KeyValueObservingSpec: QuickSpec { otherScheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) } - // Create a scope to limit the lifetime `values(forKeyPath:)`. - var replayProducer: SignalProducer? = { - return testObject.reactive + let replayProducer = testObject.reactive .values(forKeyPath: #keyPath(ObservableObject.rac_value)) .map { $0 as! NSNumber } .map { $0.intValue } @@ -320,33 +318,27 @@ class KeyValueObservingSpec: QuickSpec { .observe(on: otherScheduler) .take(during: lifetime) .replayLazily(upTo: 1) - }() - // `replayProducer` ceases its reference to the values producer after - // it starts. - replayProducer!.start() + replayProducer.start() iterationQueue.suspend() let half = numIterations / 2 for index in 0 ..< numIterations { - iterationQueue.async { [testObject = testObject!] in + iterationQueue.async { testObject.rac_value = index } if index == half { - iterationQueue.async(flags: .barrier) { [replayProducer = replayProducer!] in + iterationQueue.async(flags: .barrier) { token = nil expect(replayProducer.last()).toNot(beNil()) } } } - testObject = nil - replayProducer = nil iterationQueue.resume() - iterationQueue.sync(flags: .barrier, execute: {}) } } diff --git a/ReactiveCocoaTests/LifetimeSpec.swift b/ReactiveCocoaTests/LifetimeSpec.swift index 0605e5c003..d3781839d1 100644 --- a/ReactiveCocoaTests/LifetimeSpec.swift +++ b/ReactiveCocoaTests/LifetimeSpec.swift @@ -25,25 +25,21 @@ class LifetimeSpec: QuickSpec { for _ in 1 ... 10 { var isDeadlocked = true - if #available(*, macOS 10.10) { - DispatchQueue.global(qos: .userInitiated).async { - _ = object.reactive.lifetime + func createQueue() -> DispatchQueue { + if #available(*, macOS 10.10) { + return .global(qos: .userInitiated) + } else { + return .global(priority: .high) + } + } - DispatchQueue.global(qos: .userInitiated).async { - _ = object.reactive.lifetime + createQueue().async { + _ = object.reactive.lifetime - isDeadlocked = false - } - } - } else { - DispatchQueue.global(priority: .high).async { + createQueue().async { _ = object.reactive.lifetime - DispatchQueue.global(priority: .high).async { - _ = object.reactive.lifetime - - isDeadlocked = false - } + isDeadlocked = false } } From 4c701ded6ce50a42944f0a6e26e261b857a0a33e Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 17 Nov 2016 06:46:25 +0100 Subject: [PATCH 0578/1028] Fixed a `nil` expectation in InterceptingSpec. --- ReactiveCocoaTests/InterceptingSpec.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoaTests/InterceptingSpec.swift b/ReactiveCocoaTests/InterceptingSpec.swift index fcbcd472d6..9c96a4a8e9 100644 --- a/ReactiveCocoaTests/InterceptingSpec.swift +++ b/ReactiveCocoaTests/InterceptingSpec.swift @@ -468,7 +468,7 @@ class InterceptingSpec: QuickSpec { value = x } - expect(value) == nil + expect(value).to(beNil()) expect(object.description).notTo(beNil()) expect(value).toNot(beNil()) From d6071594236180c9aef4ea3a228953c62849d9b5 Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 17 Nov 2016 06:46:51 +0100 Subject: [PATCH 0579/1028] Removed the `RACSwiftInvocationArguments` wrapper class. --- ReactiveCocoa.xcodeproj/project.pbxproj | 2 ++ ReactiveCocoa/NSObject+Intercepting.swift | 35 ++++++++++++++---- ReactiveCocoa/RACObjCRuntimeUtilities.h | 12 +------ ReactiveCocoa/RACObjCRuntimeUtilities.m | 43 ++--------------------- 4 files changed, 34 insertions(+), 58 deletions(-) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 642cd938b9..4b71d353ac 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -339,6 +339,7 @@ 9ADE4A951DA6F018005C2AC8 /* NSTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSTextField.swift; sourceTree = ""; }; 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LifetimeSpec.swift; sourceTree = ""; }; 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronizing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Synchronizing.swift"; sourceTree = ""; }; + 9AE7C2A21DDD768500F7534C /* module.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = ""; }; 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+BindingTarget.swift"; sourceTree = ""; }; A97451331B3A935E00F48E55 /* watchOS-Application.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Application.xcconfig"; sourceTree = ""; }; A97451341B3A935E00F48E55 /* watchOS-Base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Base.xcconfig"; sourceTree = ""; }; @@ -654,6 +655,7 @@ D04725ED19E49ED7006002AA /* Supporting Files */ = { isa = PBXGroup; children = ( + 9AE7C2A21DDD768500F7534C /* module.modulemap */, CDC42E2E1AE7AB8B00965373 /* Result.framework */, D04725EE19E49ED7006002AA /* Info.plist */, ); diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index f735a4af42..d1b1ac6a84 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -64,16 +64,18 @@ extension Reactive where Base: NSObject { } } -private func bridge(_ observer: Observer<[Any?], NoError>) -> (RACSwiftInvocationArguments) -> Void { +private func bridge(_ observer: Observer<[Any?], NoError>) -> (Any) -> Void { return { arguments in - let count = arguments.count + let arguments = arguments as AnyObject + let methodSignature = arguments.objcMethodSignature! + let count = UInt(methodSignature.numberOfArguments!) var bridged = [Any?]() - bridged.reserveCapacity(count - 2) + bridged.reserveCapacity(Int(count - 2)) // Ignore `self` and `_cmd`. for position in 2 ..< count { - let rawEncoding = arguments.argumentType(at: position) + let rawEncoding = methodSignature.argumentType(at: position) let encoding = TypeEncoding(rawValue: rawEncoding.pointee) ?? .undefined func extract(_ type: U.Type) -> U { @@ -84,7 +86,7 @@ private func bridge(_ observer: Observer<[Any?], NoError>) -> (RACSwiftInvocatio alignedTo: MemoryLayout.alignment) } - arguments.copyArgument(at: position, to: pointer) + arguments.copy(to: pointer, forArgumentAt: Int(position)) return pointer.assumingMemoryBound(to: type).pointee } @@ -120,14 +122,14 @@ private func bridge(_ observer: Observer<[Any?], NoError>) -> (RACSwiftInvocatio case .type: bridged.append(extract((AnyClass?).self)) case .selector: - bridged.append(arguments.selectorString(at: position)) + bridged.append(extract((Selector?).self)) case .undefined: var size = 0, alignment = 0 NSGetSizeAndAlignment(rawEncoding, &size, &alignment) let buffer = UnsafeMutableRawPointer.allocate(bytes: size, alignedTo: alignment) defer { buffer.deallocate(bytes: size, alignedTo: alignment) } - arguments.copyArgument(at: position, to: buffer) + arguments.copy(to: buffer, forArgumentAt: Int(position)) bridged.append(NSValue(bytes: buffer, objCType: rawEncoding)) } } @@ -136,6 +138,25 @@ private func bridge(_ observer: Observer<[Any?], NoError>) -> (RACSwiftInvocatio } } +@objc private protocol ObjCInvocation { + var target: NSObject? { get set } + var selector: Selector? { get set } + + @objc(methodSignature) + var objcMethodSignature: AnyObject { get } + + @objc(getArgument:atIndex:) + func copy(to buffer: UnsafeMutableRawPointer?, forArgumentAt index: Int) + + func invoke() +} + +@objc private protocol ObjCMethodSignature { + var numberOfArguments: UInt { get } + + @objc(getArgumentTypeAtIndex:) + func argumentType(at index: UInt) -> UnsafePointer +} private enum TypeEncoding: Int8 { case char = 99 diff --git a/ReactiveCocoa/RACObjCRuntimeUtilities.h b/ReactiveCocoa/RACObjCRuntimeUtilities.h index 968f3f2d88..27e5c9508b 100644 --- a/ReactiveCocoa/RACObjCRuntimeUtilities.h +++ b/ReactiveCocoa/RACObjCRuntimeUtilities.h @@ -3,22 +3,12 @@ NS_ASSUME_NONNULL_BEGIN IMP _rac_objc_msgForward(); -@interface RACSwiftInvocationArguments : NSObject - -@property(readonly, nonatomic) NSInteger count; - --(const char *)argumentTypeAt:(NSInteger)position; --(void)copyArgumentAt:(NSInteger)position to:(void *)buffer; --(NSString*)selectorStringAt:(NSInteger)position; - -@end - @interface NSObject (RACObjCRuntimeUtilities) /// Register a block which would be triggered when `selector` is called. /// /// Warning: The callee is responsible for synchronization. --(BOOL) _rac_setupInvocationObservationForSelector:(SEL)selector protocol:(nullable Protocol *)protocol argsReceiver:(void (^)(RACSwiftInvocationArguments*)) receiverBlock; +-(BOOL) _rac_setupInvocationObservationForSelector:(SEL)selector protocol:(nullable Protocol *)protocol argsReceiver:(void (^)(id)) receiverBlock; /// Register a block which would be triggered when `selector` is called. /// diff --git a/ReactiveCocoa/RACObjCRuntimeUtilities.m b/ReactiveCocoa/RACObjCRuntimeUtilities.m index 9bb9102922..1ace128fc1 100644 --- a/ReactiveCocoa/RACObjCRuntimeUtilities.m +++ b/ReactiveCocoa/RACObjCRuntimeUtilities.m @@ -35,12 +35,6 @@ -(instancetype) initWithBlock:(id)block isTrigger:(BOOL)isTrigger; @end -@interface RACSwiftInvocationArguments (Private) - --(instancetype) initWithInvocation:(NSInvocation *)invocation; - -@end - static SEL RACAliasForSelector(SEL originalSelector) { NSString *selectorName = NSStringFromSelector(originalSelector); return NSSelectorFromString([RACSignalForSelectorAliasPrefix stringByAppendingString:selectorName]); @@ -63,8 +57,8 @@ static BOOL RACForwardInvocation(id self, NSInvocation *invocation) { __block void(^block)(void) = receiver.block; block(); } else { - __block void(^block)(RACSwiftInvocationArguments*) = receiver.block; - block([[RACSwiftInvocationArguments alloc] initWithInvocation:invocation]); + __block void(^block)(id) = receiver.block; + block(invocation); } return YES; @@ -277,7 +271,7 @@ static Class RACSwizzleClass(NSObject *self) { @implementation NSObject (RACObjCRuntimeUtilities) --(BOOL) _rac_setupInvocationObservationForSelector:(SEL)selector protocol:(Protocol *)protocol argsReceiver:(void (^)(RACSwiftInvocationArguments*))receiverBlock { +-(BOOL) _rac_setupInvocationObservationForSelector:(SEL)selector protocol:(Protocol *)protocol argsReceiver:(void (^)(id))receiverBlock { return [self _rac_setupInvocationObservationForSelector:selector protocol:protocol isTrigger:false receiver:receiverBlock]; } @@ -339,37 +333,6 @@ -(BOOL) _rac_setupInvocationObservationForSelector:(SEL)selector protocol:(Proto } @end -@implementation RACSwiftInvocationArguments -NSInvocation* invocation; - --(instancetype) initWithInvocation:(NSInvocation *)inv { - self = [super init]; - if (self) { - invocation = inv; - } - return self; -} - --(NSInteger)count { - return [[invocation methodSignature] numberOfArguments]; -} - --(const char *)argumentTypeAt:(NSInteger)position { - return [[invocation methodSignature] getArgumentTypeAtIndex:position]; -} - --(void)copyArgumentAt:(NSInteger)position to:(void *)buffer { - [invocation getArgument:buffer atIndex:position]; -} - --(NSString*)selectorStringAt:(NSInteger)position { - SEL selector; - [invocation getArgument:&selector atIndex:position]; - return NSStringFromSelector(selector); -} - -@end - @implementation RACForwardingInfo -(instancetype) initWithBlock:(id)block isTrigger:(BOOL)isTrigger { From c074e74e66fe6ed1497aa04642d021142821117b Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 17 Nov 2016 14:13:32 +0100 Subject: [PATCH 0580/1028] Association API. --- ReactiveCocoa/NSObject+Association.swift | 28 ++++++++++++++++++++++-- ReactiveCocoa/NSObject+Lifetime.swift | 6 ++--- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/ReactiveCocoa/NSObject+Association.swift b/ReactiveCocoa/NSObject+Association.swift index 24e148f8bd..01fcf3047f 100644 --- a/ReactiveCocoa/NSObject+Association.swift +++ b/ReactiveCocoa/NSObject+Association.swift @@ -12,11 +12,35 @@ extension Reactive where Base: NSObject { /// - returns: /// The associated value for the specified key. internal func associatedValue(forKey key: StaticString = #function, initial: (Base) -> T) -> T { - var value = objc_getAssociatedObject(base, key.utf8Start) as! T? + var value = base.value(forAssociatedKey: key.utf8Start) as! T? if value == nil { value = initial(base) - objc_setAssociatedObject(base, key.utf8Start, value, .OBJC_ASSOCIATION_RETAIN) + base.setValue(value, forAssociatedKey: key.utf8Start) } return value! } } + +extension NSObject { + /// Retrieve the associated value for the specified key. + /// + /// - parameters: + /// - key: The key. + /// + /// - returns: + /// The associated value, or `nil` if no value is associated with the key. + internal func value(forAssociatedKey key: UnsafeRawPointer) -> Any? { + return objc_getAssociatedObject(self, key) + } + + /// Set the associated value for the specified key. + /// + /// - parameters: + /// - value: The value to be associated. + /// - key: The key. + /// - weak: `true` if the value should be weakly referenced. `false` + /// otherwise. + internal func setValue(_ value: Any?, forAssociatedKey key: UnsafeRawPointer, weak: Bool = false) { + objc_setAssociatedObject(self, key, value, weak ? .OBJC_ASSOCIATION_ASSIGN : .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } +} diff --git a/ReactiveCocoa/NSObject+Lifetime.swift b/ReactiveCocoa/NSObject+Lifetime.swift index b9f739a4c4..387c4ebc63 100644 --- a/ReactiveCocoa/NSObject+Lifetime.swift +++ b/ReactiveCocoa/NSObject+Lifetime.swift @@ -8,15 +8,15 @@ extension Reactive where Base: NSObject { /// Returns a lifetime that ends when the object is deallocated. @nonobjc public var lifetime: Lifetime { return base.synchronized { - if let lifetime = objc_getAssociatedObject(base, &lifetimeKey) as! Lifetime? { + if let lifetime = base.value(forAssociatedKey: &lifetimeKey) as! Lifetime? { return lifetime } let token = Lifetime.Token() let lifetime = Lifetime(token) - objc_setAssociatedObject(base, &lifetimeTokenKey, token, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - objc_setAssociatedObject(base, &lifetimeKey, lifetime, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + base.setValue(token, forAssociatedKey: &lifetimeTokenKey) + base.setValue(lifetime, forAssociatedKey: &lifetimeKey) return lifetime } From c13803929784d1880ccf813faa29db936ffffe73 Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 17 Nov 2016 16:26:57 +0100 Subject: [PATCH 0581/1028] Moved the NSInvocation related protocols into a separate file. --- ReactiveCocoa.xcodeproj/project.pbxproj | 12 +++++++- ReactiveCocoa/NSObject+Intercepting.swift | 20 ------------- ReactiveCocoa/ObjC+Selectors.swift | 35 +++++++++++++++++++++++ 3 files changed, 46 insertions(+), 21 deletions(-) create mode 100644 ReactiveCocoa/ObjC+Selectors.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 4b71d353ac..27376d825b 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -162,6 +162,10 @@ 9ADFE5A61DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronizing.swift */; }; 9ADFE5A71DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronizing.swift */; }; 9ADFE5A81DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronizing.swift */; }; + 9AE7C2A41DDD7F5100F7534C /* ObjC+Selectors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE7C2A31DDD7F5100F7534C /* ObjC+Selectors.swift */; }; + 9AE7C2A51DDD7F5100F7534C /* ObjC+Selectors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE7C2A31DDD7F5100F7534C /* ObjC+Selectors.swift */; }; + 9AE7C2A61DDD7F5100F7534C /* ObjC+Selectors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE7C2A31DDD7F5100F7534C /* ObjC+Selectors.swift */; }; + 9AE7C2A71DDD7F5100F7534C /* ObjC+Selectors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE7C2A31DDD7F5100F7534C /* ObjC+Selectors.swift */; }; 9AF0EA751D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; 9AF0EA761D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; 9AF0EA771D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; @@ -340,6 +344,7 @@ 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LifetimeSpec.swift; sourceTree = ""; }; 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronizing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Synchronizing.swift"; sourceTree = ""; }; 9AE7C2A21DDD768500F7534C /* module.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = ""; }; + 9AE7C2A31DDD7F5100F7534C /* ObjC+Selectors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObjC+Selectors.swift"; sourceTree = ""; }; 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+BindingTarget.swift"; sourceTree = ""; }; A97451331B3A935E00F48E55 /* watchOS-Application.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Application.xcconfig"; sourceTree = ""; }; A97451341B3A935E00F48E55 /* watchOS-Base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Base.xcconfig"; sourceTree = ""; }; @@ -635,6 +640,7 @@ 9A1D06751D9415FB00ACF44C /* RACObjCRuntimeUtilities.h */, 9A1D06761D9415FB00ACF44C /* RACObjCRuntimeUtilities.m */, 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */, + 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */, CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */, 9ADE4A901DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift */, 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */, @@ -643,7 +649,7 @@ 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */, 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */, 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronizing.swift */, - 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */, + 9AE7C2A31DDD7F5100F7534C /* ObjC+Selectors.swift */, 9A1D05E91D93E9F100ACF44C /* AppKit */, 9A1D05EB1D93E9F100ACF44C /* UIKit */, 538DCB761DCA5E1600332880 /* Shared */, @@ -1056,6 +1062,7 @@ 9A1D061F1D93EA0100ACF44C /* UITextField.swift in Sources */, 4ABEFE261DCFCF640066A8C2 /* UICollectionView.swift in Sources */, 9ADE4A7F1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, + 9AE7C2A71DDD7F5100F7534C /* ObjC+Selectors.swift in Sources */, 9A1D06201D93EA0100ACF44C /* UITextView.swift in Sources */, 9A6AAA241DB8F51C0013AAEA /* ReusableComponents.swift in Sources */, 4ABEFE201DCFCEF80066A8C2 /* UITableView.swift in Sources */, @@ -1131,6 +1138,7 @@ 9AF0EA771D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, 9A1D05E21D93E99100ACF44C /* NSObject+Association.swift in Sources */, 4A0E11011D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, + 9AE7C2A61DDD7F5100F7534C /* ObjC+Selectors.swift in Sources */, 9AD0F06C1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1146,6 +1154,7 @@ 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, 9A1D05E01D93E99100ACF44C /* NSObject+Association.swift in Sources */, 538DCB791DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */, + 9AE7C2A41DDD7F5100F7534C /* ObjC+Selectors.swift in Sources */, 9A6AAA261DB8F5280013AAEA /* ReusableComponents.swift in Sources */, 9ADFE5A51DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */, 9ADE4A7C1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, @@ -1193,6 +1202,7 @@ 9A1D06061D93EA0000ACF44C /* UIImageView.swift in Sources */, 9AF0EA761D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, CD0C45DF1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, + 9AE7C2A51DDD7F5100F7534C /* ObjC+Selectors.swift in Sources */, 9A2E425F1DAA6737006D909F /* CocoaTarget.swift in Sources */, 9A1D06781D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 53AC46CC1DD6F97400C799E1 /* UISlider.swift in Sources */, diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index d1b1ac6a84..6a4272b456 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -138,26 +138,6 @@ private func bridge(_ observer: Observer<[Any?], NoError>) -> (Any) -> Void { } } -@objc private protocol ObjCInvocation { - var target: NSObject? { get set } - var selector: Selector? { get set } - - @objc(methodSignature) - var objcMethodSignature: AnyObject { get } - - @objc(getArgument:atIndex:) - func copy(to buffer: UnsafeMutableRawPointer?, forArgumentAt index: Int) - - func invoke() -} - -@objc private protocol ObjCMethodSignature { - var numberOfArguments: UInt { get } - - @objc(getArgumentTypeAtIndex:) - func argumentType(at index: UInt) -> UnsafePointer -} - private enum TypeEncoding: Int8 { case char = 99 case int = 105 diff --git a/ReactiveCocoa/ObjC+Selectors.swift b/ReactiveCocoa/ObjC+Selectors.swift new file mode 100644 index 0000000000..5dd780424b --- /dev/null +++ b/ReactiveCocoa/ObjC+Selectors.swift @@ -0,0 +1,35 @@ +internal enum ObjCSelector { + static let forwardInvocation = Selector((("forwardInvocation:"))) + static let methodSignatureForSelector = Selector((("methodSignatureForSelector:"))) + static let getClass = Selector((("class"))) +} + +internal enum ObjCMethodEncoding { + static let forwardInvocation = extract("v@:@") + static let methodSignatureForSelector = extract("v@::") + static let getClass = extract("#@:") + + private static func extract(_ string: StaticString) -> UnsafePointer { + return UnsafeRawPointer(string.utf8Start).assumingMemoryBound(to: CChar.self) + } +} + +@objc internal protocol ObjCInvocation { + var target: NSObject? { get set } + var selector: Selector? { get set } + + @objc(methodSignature) + var objcMethodSignature: AnyObject { get } + + @objc(getArgument:atIndex:) + func copy(to buffer: UnsafeMutableRawPointer?, forArgumentAt index: Int) + + func invoke() +} + +@objc internal protocol ObjCMethodSignature { + var numberOfArguments: UInt { get } + + @objc(getArgumentTypeAtIndex:) + func argumentType(at index: UInt) -> UnsafePointer +} From 54def52dbc68e56a649860d58b7515fcf3554909 Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 17 Nov 2016 16:30:17 +0100 Subject: [PATCH 0582/1028] Runtime subclassing and ObjC runtime wrapper. --- ReactiveCocoa.xcodeproj/project.pbxproj | 68 +++++++-- ReactiveCocoa/NSObject+Association.swift | 4 +- ReactiveCocoa/NSObject+ObjCRuntime.swift | 9 ++ ...C+Selectors.swift => ObjC+Constants.swift} | 20 --- ReactiveCocoa/ObjC+Messages.swift | 24 ++++ ReactiveCocoa/ObjC+Runtime.swift | 131 ++++++++++++++++++ ReactiveCocoa/ObjC+RuntimeSubclassing.swift | 65 +++++++++ ReactiveCocoaTests/SwizzlingSpec.swift | 43 ++++++ 8 files changed, 332 insertions(+), 32 deletions(-) create mode 100644 ReactiveCocoa/NSObject+ObjCRuntime.swift rename ReactiveCocoa/{ObjC+Selectors.swift => ObjC+Constants.swift} (54%) create mode 100644 ReactiveCocoa/ObjC+Messages.swift create mode 100644 ReactiveCocoa/ObjC+Runtime.swift create mode 100644 ReactiveCocoa/ObjC+RuntimeSubclassing.swift create mode 100644 ReactiveCocoaTests/SwizzlingSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 27376d825b..793db7ee82 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -140,6 +140,25 @@ 9A9DFEE91DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; 9A9DFEEA1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; 9A9DFEEB1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; + 9AA0BD7C1DDE03DE00531FCF /* ObjC+Runtime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD771DDE03DE00531FCF /* ObjC+Runtime.swift */; }; + 9AA0BD7D1DDE03DE00531FCF /* ObjC+Runtime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD771DDE03DE00531FCF /* ObjC+Runtime.swift */; }; + 9AA0BD7E1DDE03DE00531FCF /* ObjC+Runtime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD771DDE03DE00531FCF /* ObjC+Runtime.swift */; }; + 9AA0BD7F1DDE03DE00531FCF /* ObjC+Runtime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD771DDE03DE00531FCF /* ObjC+Runtime.swift */; }; + 9AA0BD811DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD801DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift */; }; + 9AA0BD821DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD801DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift */; }; + 9AA0BD831DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD801DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift */; }; + 9AA0BD841DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD801DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift */; }; + 9AA0BD861DDE0A8B00531FCF /* SwizzlingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD851DDE0A8B00531FCF /* SwizzlingSpec.swift */; }; + 9AA0BD871DDE0A8B00531FCF /* SwizzlingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD851DDE0A8B00531FCF /* SwizzlingSpec.swift */; }; + 9AA0BD881DDE0A8B00531FCF /* SwizzlingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD851DDE0A8B00531FCF /* SwizzlingSpec.swift */; }; + 9AA0BD8A1DDE153A00531FCF /* ObjC+Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD891DDE153A00531FCF /* ObjC+Constants.swift */; }; + 9AA0BD8B1DDE153A00531FCF /* ObjC+Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD891DDE153A00531FCF /* ObjC+Constants.swift */; }; + 9AA0BD8C1DDE153A00531FCF /* ObjC+Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD891DDE153A00531FCF /* ObjC+Constants.swift */; }; + 9AA0BD8D1DDE153A00531FCF /* ObjC+Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD891DDE153A00531FCF /* ObjC+Constants.swift */; }; + 9AA0BD8F1DDE29F800531FCF /* NSObject+ObjCRuntime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD8E1DDE29F800531FCF /* NSObject+ObjCRuntime.swift */; }; + 9AA0BD901DDE29F800531FCF /* NSObject+ObjCRuntime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD8E1DDE29F800531FCF /* NSObject+ObjCRuntime.swift */; }; + 9AA0BD911DDE29F800531FCF /* NSObject+ObjCRuntime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD8E1DDE29F800531FCF /* NSObject+ObjCRuntime.swift */; }; + 9AA0BD921DDE29F800531FCF /* NSObject+ObjCRuntime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD8E1DDE29F800531FCF /* NSObject+ObjCRuntime.swift */; }; 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; 9AD0F06B1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; 9AD0F06C1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; @@ -162,10 +181,10 @@ 9ADFE5A61DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronizing.swift */; }; 9ADFE5A71DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronizing.swift */; }; 9ADFE5A81DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronizing.swift */; }; - 9AE7C2A41DDD7F5100F7534C /* ObjC+Selectors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE7C2A31DDD7F5100F7534C /* ObjC+Selectors.swift */; }; - 9AE7C2A51DDD7F5100F7534C /* ObjC+Selectors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE7C2A31DDD7F5100F7534C /* ObjC+Selectors.swift */; }; - 9AE7C2A61DDD7F5100F7534C /* ObjC+Selectors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE7C2A31DDD7F5100F7534C /* ObjC+Selectors.swift */; }; - 9AE7C2A71DDD7F5100F7534C /* ObjC+Selectors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE7C2A31DDD7F5100F7534C /* ObjC+Selectors.swift */; }; + 9AE7C2A41DDD7F5100F7534C /* ObjC+Messages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE7C2A31DDD7F5100F7534C /* ObjC+Messages.swift */; }; + 9AE7C2A51DDD7F5100F7534C /* ObjC+Messages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE7C2A31DDD7F5100F7534C /* ObjC+Messages.swift */; }; + 9AE7C2A61DDD7F5100F7534C /* ObjC+Messages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE7C2A31DDD7F5100F7534C /* ObjC+Messages.swift */; }; + 9AE7C2A71DDD7F5100F7534C /* ObjC+Messages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE7C2A31DDD7F5100F7534C /* ObjC+Messages.swift */; }; 9AF0EA751D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; 9AF0EA761D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; 9AF0EA771D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; @@ -335,6 +354,11 @@ 9A6AAA271DB8F7EB0013AAEA /* ReusableComponentsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponentsSpec.swift; sourceTree = ""; }; 9A6AAA291DB8F7F10013AAEA /* ReusableComponentsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponentsSpec.swift; sourceTree = ""; }; 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssociationSpec.swift; sourceTree = ""; }; + 9AA0BD771DDE03DE00531FCF /* ObjC+Runtime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObjC+Runtime.swift"; sourceTree = ""; }; + 9AA0BD801DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObjC+RuntimeSubclassing.swift"; sourceTree = ""; }; + 9AA0BD851DDE0A8B00531FCF /* SwizzlingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwizzlingSpec.swift; sourceTree = ""; }; + 9AA0BD891DDE153A00531FCF /* ObjC+Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObjC+Constants.swift"; sourceTree = ""; }; + 9AA0BD8E1DDE29F800531FCF /* NSObject+ObjCRuntime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+ObjCRuntime.swift"; sourceTree = ""; }; 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+KeyValueObserving.swift"; sourceTree = ""; }; 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaAction.swift; sourceTree = ""; }; 9ADE4A881DA6D206005C2AC8 /* NSControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSControl.swift; sourceTree = ""; }; @@ -344,7 +368,7 @@ 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LifetimeSpec.swift; sourceTree = ""; }; 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronizing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Synchronizing.swift"; sourceTree = ""; }; 9AE7C2A21DDD768500F7534C /* module.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = ""; }; - 9AE7C2A31DDD7F5100F7534C /* ObjC+Selectors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObjC+Selectors.swift"; sourceTree = ""; }; + 9AE7C2A31DDD7F5100F7534C /* ObjC+Messages.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObjC+Messages.swift"; sourceTree = ""; }; 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+BindingTarget.swift"; sourceTree = ""; }; A97451331B3A935E00F48E55 /* watchOS-Application.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Application.xcconfig"; sourceTree = ""; }; A97451341B3A935E00F48E55 /* watchOS-Base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Base.xcconfig"; sourceTree = ""; }; @@ -649,7 +673,11 @@ 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */, 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */, 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronizing.swift */, - 9AE7C2A31DDD7F5100F7534C /* ObjC+Selectors.swift */, + 9AA0BD8E1DDE29F800531FCF /* NSObject+ObjCRuntime.swift */, + 9AE7C2A31DDD7F5100F7534C /* ObjC+Messages.swift */, + 9AA0BD891DDE153A00531FCF /* ObjC+Constants.swift */, + 9AA0BD801DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift */, + 9AA0BD771DDE03DE00531FCF /* ObjC+Runtime.swift */, 9A1D05E91D93E9F100ACF44C /* AppKit */, 9A1D05EB1D93E9F100ACF44C /* UIKit */, 538DCB761DCA5E1600332880 /* Shared */, @@ -681,6 +709,7 @@ 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */, 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */, 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */, + 9AA0BD851DDE0A8B00531FCF /* SwizzlingSpec.swift */, D04725FA19E49ED7006002AA /* Supporting Files */, ); path = ReactiveCocoaTests; @@ -1062,11 +1091,12 @@ 9A1D061F1D93EA0100ACF44C /* UITextField.swift in Sources */, 4ABEFE261DCFCF640066A8C2 /* UICollectionView.swift in Sources */, 9ADE4A7F1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, - 9AE7C2A71DDD7F5100F7534C /* ObjC+Selectors.swift in Sources */, + 9AE7C2A71DDD7F5100F7534C /* ObjC+Messages.swift in Sources */, 9A1D06201D93EA0100ACF44C /* UITextView.swift in Sources */, 9A6AAA241DB8F51C0013AAEA /* ReusableComponents.swift in Sources */, 4ABEFE201DCFCEF80066A8C2 /* UITableView.swift in Sources */, 9A1D06181D93EA0100ACF44C /* UIImageView.swift in Sources */, + 9AA0BD841DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift in Sources */, 9AD0F06D1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, 9A1D06211D93EA0100ACF44C /* UIView.swift in Sources */, 9A1D067A1D94160100ACF44C /* RACObjCRuntimeUtilities.m in Sources */, @@ -1077,12 +1107,15 @@ 9A1D06161D93EA0100ACF44C /* UIControl.swift in Sources */, 9A1D06141D93EA0100ACF44C /* UIButton.swift in Sources */, 9A1D065E1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, + 9AA0BD8D1DDE153A00531FCF /* ObjC+Constants.swift in Sources */, 9AF0EA781D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, 9A1D06191D93EA0100ACF44C /* UILabel.swift in Sources */, 4A0E11021D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, 9ADFE5A81DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */, CD0C45E11CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, + 9AA0BD921DDE29F800531FCF /* NSObject+ObjCRuntime.swift in Sources */, 9A1D061A1D93EA0100ACF44C /* UIProgressView.swift in Sources */, + 9AA0BD7F1DDE03DE00531FCF /* ObjC+Runtime.swift in Sources */, 9A2E42611DAA6737006D909F /* CocoaTarget.swift in Sources */, 53A6BED41DD4BCA90016C058 /* MKMapView.swift in Sources */, 9A1D06121D93EA0100ACF44C /* UIBarButtonItem.swift in Sources */, @@ -1119,6 +1152,7 @@ 4ABEFE221DCFCF0A0066A8C2 /* UITableViewSpec.swift in Sources */, 9A1D06491D93EA7E00ACF44C /* UIProgressViewSpec.swift in Sources */, 4ABEFE291DCFCFA90066A8C2 /* UICollectionViewSpec.swift in Sources */, + 9AA0BD881DDE0A8B00531FCF /* SwizzlingSpec.swift in Sources */, BEE020661D637B0000DF261F /* TestError.swift in Sources */, 9A1D06531D93EA7E00ACF44C /* UITextFieldSpec.swift in Sources */, ); @@ -1130,15 +1164,19 @@ files = ( 9A2E42601DAA6737006D909F /* CocoaTarget.swift in Sources */, 9A9DFEE51DA7B5500039EE1B /* NSObject+Intercepting.swift in Sources */, + 9AA0BD7E1DDE03DE00531FCF /* ObjC+Runtime.swift in Sources */, 9A1D06791D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9ADE4A931DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, + 9AA0BD8C1DDE153A00531FCF /* ObjC+Constants.swift in Sources */, 9ADFE5A71DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */, 9ADE4A7E1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, + 9AA0BD831DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift in Sources */, CD0C45E01CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, + 9AA0BD911DDE29F800531FCF /* NSObject+ObjCRuntime.swift in Sources */, 9AF0EA771D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, 9A1D05E21D93E99100ACF44C /* NSObject+Association.swift in Sources */, 4A0E11011D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, - 9AE7C2A61DDD7F5100F7534C /* ObjC+Selectors.swift in Sources */, + 9AE7C2A61DDD7F5100F7534C /* ObjC+Messages.swift in Sources */, 9AD0F06C1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1152,10 +1190,13 @@ 4ABEFE301DCFD0530066A8C2 /* NSCollectionView.swift in Sources */, 9A1D06771D9415FF00ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, + 9AA0BD8F1DDE29F800531FCF /* NSObject+ObjCRuntime.swift in Sources */, 9A1D05E01D93E99100ACF44C /* NSObject+Association.swift in Sources */, 538DCB791DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */, - 9AE7C2A41DDD7F5100F7534C /* ObjC+Selectors.swift in Sources */, + 9AE7C2A41DDD7F5100F7534C /* ObjC+Messages.swift in Sources */, + 9AA0BD8A1DDE153A00531FCF /* ObjC+Constants.swift in Sources */, 9A6AAA261DB8F5280013AAEA /* ReusableComponents.swift in Sources */, + 9AA0BD7C1DDE03DE00531FCF /* ObjC+Runtime.swift in Sources */, 9ADFE5A51DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */, 9ADE4A7C1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, 9ADE4A961DA6F018005C2AC8 /* NSTextField.swift in Sources */, @@ -1166,6 +1207,7 @@ 53A6BED21DD4BCA90016C058 /* MKMapView.swift in Sources */, 9A1D065B1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, 4A0E10FF1D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, + 9AA0BD811DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1176,6 +1218,7 @@ 9A6AAA0E1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */, 538DCB7D1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift in Sources */, 9ADE4A8F1DA6DA20005C2AC8 /* NSControlSpec.swift in Sources */, + 9AA0BD861DDE0A8B00531FCF /* SwizzlingSpec.swift in Sources */, D0A2260E1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */, 53A6BED61DD4BD2C0016C058 /* MKMapViewSpec.swift in Sources */, 9ADFE5A11DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */, @@ -1202,16 +1245,19 @@ 9A1D06061D93EA0000ACF44C /* UIImageView.swift in Sources */, 9AF0EA761D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, CD0C45DF1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, - 9AE7C2A51DDD7F5100F7534C /* ObjC+Selectors.swift in Sources */, + 9AE7C2A51DDD7F5100F7534C /* ObjC+Messages.swift in Sources */, 9A2E425F1DAA6737006D909F /* CocoaTarget.swift in Sources */, 9A1D06781D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 53AC46CC1DD6F97400C799E1 /* UISlider.swift in Sources */, + 9AA0BD821DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift in Sources */, 9A1D060F1D93EA0000ACF44C /* UIView.swift in Sources */, 9A1D06051D93EA0000ACF44C /* UIDatePicker.swift in Sources */, 9A1D05E11D93E99100ACF44C /* NSObject+Association.swift in Sources */, 9A1D06041D93EA0000ACF44C /* UIControl.swift in Sources */, 9A1D06021D93EA0000ACF44C /* UIButton.swift in Sources */, + 9AA0BD901DDE29F800531FCF /* NSObject+ObjCRuntime.swift in Sources */, 419139461DB910570043C9D1 /* UIGestureRecognizer.swift in Sources */, + 9AA0BD7D1DDE03DE00531FCF /* ObjC+Runtime.swift in Sources */, 9A1D060A1D93EA0000ACF44C /* UISwitch.swift in Sources */, 9A1D065C1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, 9A1D06071D93EA0000ACF44C /* UILabel.swift in Sources */, @@ -1226,6 +1272,7 @@ 538DCB7A1DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */, 9A1D06001D93EA0000ACF44C /* UIBarButtonItem.swift in Sources */, 9A1D05FF1D93EA0000ACF44C /* UIActivityIndicatorView.swift in Sources */, + 9AA0BD8B1DDE153A00531FCF /* ObjC+Constants.swift in Sources */, 9A1D06091D93EA0000ACF44C /* UISegmentedControl.swift in Sources */, 9ADE4A7D1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, ); @@ -1240,6 +1287,7 @@ BFA6B94E1A7604D500C846D1 /* SignalProducerNimbleMatchers.swift in Sources */, B696FB821A7640C00075236D /* TestError.swift in Sources */, 9A1D064C1D93EA7E00ACF44C /* UISwitchSpec.swift in Sources */, + 9AA0BD871DDE0A8B00531FCF /* SwizzlingSpec.swift in Sources */, 4ABEFE281DCFCFA90066A8C2 /* UICollectionViewSpec.swift in Sources */, 9A1D06441D93EA7E00ACF44C /* UIImageViewSpec.swift in Sources */, 9A1D06541D93EA7E00ACF44C /* UITextViewSpec.swift in Sources */, diff --git a/ReactiveCocoa/NSObject+Association.swift b/ReactiveCocoa/NSObject+Association.swift index 01fcf3047f..2488a2f0d4 100644 --- a/ReactiveCocoa/NSObject+Association.swift +++ b/ReactiveCocoa/NSObject+Association.swift @@ -29,7 +29,7 @@ extension NSObject { /// /// - returns: /// The associated value, or `nil` if no value is associated with the key. - internal func value(forAssociatedKey key: UnsafeRawPointer) -> Any? { + @nonobjc internal func value(forAssociatedKey key: UnsafeRawPointer) -> Any? { return objc_getAssociatedObject(self, key) } @@ -40,7 +40,7 @@ extension NSObject { /// - key: The key. /// - weak: `true` if the value should be weakly referenced. `false` /// otherwise. - internal func setValue(_ value: Any?, forAssociatedKey key: UnsafeRawPointer, weak: Bool = false) { + @nonobjc internal func setValue(_ value: Any?, forAssociatedKey key: UnsafeRawPointer, weak: Bool = false) { objc_setAssociatedObject(self, key, value, weak ? .OBJC_ASSOCIATION_ASSIGN : .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } diff --git a/ReactiveCocoa/NSObject+ObjCRuntime.swift b/ReactiveCocoa/NSObject+ObjCRuntime.swift new file mode 100644 index 0000000000..126c2c6f5d --- /dev/null +++ b/ReactiveCocoa/NSObject+ObjCRuntime.swift @@ -0,0 +1,9 @@ +extension NSObject { + @nonobjc internal var objcClass: AnyClass { + return (self as AnyObject).objcClass + } + + @nonobjc internal func setClass(_ class: ObjCClass) { + object_setClass(self, `class`.reference) + } +} diff --git a/ReactiveCocoa/ObjC+Selectors.swift b/ReactiveCocoa/ObjC+Constants.swift similarity index 54% rename from ReactiveCocoa/ObjC+Selectors.swift rename to ReactiveCocoa/ObjC+Constants.swift index 5dd780424b..e317acf3f4 100644 --- a/ReactiveCocoa/ObjC+Selectors.swift +++ b/ReactiveCocoa/ObjC+Constants.swift @@ -13,23 +13,3 @@ internal enum ObjCMethodEncoding { return UnsafeRawPointer(string.utf8Start).assumingMemoryBound(to: CChar.self) } } - -@objc internal protocol ObjCInvocation { - var target: NSObject? { get set } - var selector: Selector? { get set } - - @objc(methodSignature) - var objcMethodSignature: AnyObject { get } - - @objc(getArgument:atIndex:) - func copy(to buffer: UnsafeMutableRawPointer?, forArgumentAt index: Int) - - func invoke() -} - -@objc internal protocol ObjCMethodSignature { - var numberOfArguments: UInt { get } - - @objc(getArgumentTypeAtIndex:) - func argumentType(at index: UInt) -> UnsafePointer -} diff --git a/ReactiveCocoa/ObjC+Messages.swift b/ReactiveCocoa/ObjC+Messages.swift new file mode 100644 index 0000000000..8a9dee71f8 --- /dev/null +++ b/ReactiveCocoa/ObjC+Messages.swift @@ -0,0 +1,24 @@ +@objc internal protocol ObjCClassReporting { + @objc(class) + var objcClass: AnyClass! { get } +} + +@objc internal protocol ObjCInvocation { + var target: NSObject? { get set } + var selector: Selector? { get set } + + @objc(methodSignature) + var objcMethodSignature: AnyObject { get } + + @objc(getArgument:atIndex:) + func copy(to buffer: UnsafeMutableRawPointer?, forArgumentAt index: Int) + + func invoke() +} + +@objc internal protocol ObjCMethodSignature { + var numberOfArguments: UInt { get } + + @objc(getArgumentTypeAtIndex:) + func argumentType(at index: UInt) -> UnsafePointer +} diff --git a/ReactiveCocoa/ObjC+Runtime.swift b/ReactiveCocoa/ObjC+Runtime.swift new file mode 100644 index 0000000000..f3089e531b --- /dev/null +++ b/ReactiveCocoa/ObjC+Runtime.swift @@ -0,0 +1,131 @@ +internal struct ObjCClass { + let reference: AnyClass + + var name: String { + return String(cString: class_getName(reference)) + } + + var metaclass: ObjCClass { + return ObjCClass(object_getClass(reference)!) + } + + var superclass: ObjCClass? { + if let superclass = class_getSuperclass(reference) { + return ObjCClass(superclass) + } + + return nil + } + + init(_ reference: AnyClass) { + self.reference = reference + } + + init?(name: String) { + if let classRef = name.withCString(objc_getClass) { + self.reference = classRef as! AnyClass + } else { + return nil + } + } + + func addMethod(with implementation: CFunction, for selector: Selector, types: UnsafePointer) { + class_addMethod(reference, selector, implementation.reference, types) + } + + func replaceMethod(with implementation: CFunction, for selector: Selector, types: UnsafePointer) -> CFunction? { + let imp = class_replaceMethod(reference, selector, implementation.reference, types) + return imp.map(CFunction.init) + } + + func method(for selector: Selector, searchesAncestors: Bool = true) -> ObjCMethod? { + if searchesAncestors { + if let method = class_getInstanceMethod(reference, selector) { + return ObjCMethod(method, in: self) + } + } else { + var count: UInt32 = 0 + let buffer = class_copyMethodList(reference, &count) + let methods = UnsafeBufferPointer(start: buffer, count: Int(count)) + + defer { free(buffer) } + + for method in methods { + if method_getName(method!) == selector { + return ObjCMethod(method!, in: self) + } + } + } + + return nil + } +} + +extension ObjCClass: Hashable { + static func ==(left: ObjCClass, right: ObjCClass) -> Bool { + return left.reference === right.reference + } + + var hashValue: Int { + return ObjectIdentifier(reference).hashValue + } +} + +extension ObjCClass { + static func type(of object: NSObject) -> ObjCClass { + return ObjCClass(object_getClass(object)) + } + + static func allocate(name: String, superclass: ObjCClass, setup: (ObjCClass) throws -> Void) rethrows -> ObjCClass { + let subclass = name.withCString { name in + return ObjCClass(objc_allocateClassPair(superclass.reference, name, 0)!) + } + + try setup(subclass) + objc_registerClassPair(subclass.reference) + + return subclass + } +} + +internal struct ObjCMethod { + fileprivate let parent: ObjCClass + fileprivate let reference: Method + + var function: CFunction { + return CFunction(method_getImplementation(reference)) + } + + var typeEncoding: UnsafePointer { + return method_getTypeEncoding(reference) + } + + init(_ reference: Method, in class: ObjCClass) { + self.reference = reference + self.parent = `class` + } + + func replaceImplementation(_ implementation: CFunction) -> CFunction? { + return parent.replaceMethod(with: function, + for: method_getName(reference), + types: method_getTypeEncoding(reference)) + } +} + +internal struct CFunction { + static let forwarding = CFunction(_rac_objc_msgForward()) + + fileprivate let reference: IMP + + init(assuming block: U) { + self.reference = unsafeBitCast(block, to: IMP.self) + } + + fileprivate init(_ reference: IMP) { + self.reference = reference + } + + init(block: Any) { + self.reference = imp_implementationWithBlock(block) + } +} diff --git a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift new file mode 100644 index 0000000000..cf53067150 --- /dev/null +++ b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift @@ -0,0 +1,65 @@ +import ReactiveSwift + +private let swizzledExternalClasses = Atomic>([]) + +private func subclassName(of class: ObjCClass) -> String { + return `class`.name.appending("_RACSwift") +} + +internal func swizzleClass(_ instance: NSObject) -> ObjCClass { + let key = (#function as StaticString).utf8Start + + if let knownSubclass = instance.value(forAssociatedKey: key) as! ObjCClass? { + return knownSubclass + } + + let perceivedClass = ObjCClass(instance.objcClass) + let realClass = ObjCClass.type(of: instance) + + if perceivedClass != realClass { + // If the class is already lying about what it is, it's probably a KVO + // dynamic subclass or something else that we shouldn't subclass + // ourselves. + // + // Use this runtime subclass directly. + swizzledExternalClasses.modify { classes in + if !classes.contains(realClass) { + classes.insert(realClass) + replaceGetClass(in: realClass, decoy: perceivedClass) + } + } + + return realClass + } else { + let name = subclassName(of: perceivedClass) + let subclass: ObjCClass + + if let existingClass = ObjCClass(name: name) { + subclass = existingClass + } else { + subclass = ObjCClass.allocate(name: name, superclass: perceivedClass) { subclass in + replaceGetClass(in: subclass, decoy: perceivedClass) + } + } + + instance.setClass(subclass) + instance.setValue(subclass, forAssociatedKey: key) + return subclass + } +} + +private func replaceGetClass(in class: ObjCClass, decoy perceivedClass: ObjCClass) { + let getClass: @convention(block) (Any) -> AnyClass = { _ in + return perceivedClass.reference + } + + // Swizzle `-class`. + _ = `class`.replaceMethod(with: CFunction(block: getClass), + for: ObjCSelector.getClass, + types: ObjCMethodEncoding.getClass) + + // Swizzle `+class`. + _ = `class`.metaclass.replaceMethod(with: CFunction(block: getClass), + for: ObjCSelector.getClass, + types: ObjCMethodEncoding.getClass) +} diff --git a/ReactiveCocoaTests/SwizzlingSpec.swift b/ReactiveCocoaTests/SwizzlingSpec.swift new file mode 100644 index 0000000000..f0251afbc4 --- /dev/null +++ b/ReactiveCocoaTests/SwizzlingSpec.swift @@ -0,0 +1,43 @@ +@testable import ReactiveCocoa +import Quick +import Nimble + +class SwizzledObject: NSObject {} + +class SwizzlingSpec: QuickSpec { + override func spec() { + describe("runtime subclassing") { + it("should swizzle the instance while still reporting the perceived class in `-class` and `+class`") { + let object = SwizzledObject() + expect(type(of: object)).to(beIdenticalTo(SwizzledObject.self)) + + let subclass = Swizzler.swizzleClass(of: object) + expect(type(of: object)).to(beIdenticalTo(subclass.reference)) + + let objcClass = (object as AnyObject).objcClass + expect(objcClass).to(beIdenticalTo(SwizzledObject.self)) + expect((objcClass as AnyObject).objcClass).to(beIdenticalTo(SwizzledObject.self)) + + expect(subclass.name).to(contain("_RACSwift")) + } + + it("should reuse the runtime subclass across instances") { + let object = SwizzledObject() + let subclass = Swizzler.swizzleClass(of: object) + + let object2 = SwizzledObject() + let subclass2 = Swizzler.swizzleClass(of: object2) + + expect(subclass) == subclass2 + } + + it("should return the known runtime subclass") { + let object = SwizzledObject() + let subclass = Swizzler.swizzleClass(of: object) + let subclass2 = Swizzler.swizzleClass(of: object) + + expect(subclass) == subclass2 + } + } + } +} From 76ca31d0f2633ac228e8ee8097e04201d181475b Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 17 Nov 2016 18:17:34 +0100 Subject: [PATCH 0583/1028] Migrated InterceptingSpec to use the runtime wrapper. --- ReactiveCocoaTests/InterceptingSpec.swift | 191 ++++++++-------------- 1 file changed, 67 insertions(+), 124 deletions(-) diff --git a/ReactiveCocoaTests/InterceptingSpec.swift b/ReactiveCocoaTests/InterceptingSpec.swift index 9c96a4a8e9..8c1dadbf5c 100644 --- a/ReactiveCocoaTests/InterceptingSpec.swift +++ b/ReactiveCocoaTests/InterceptingSpec.swift @@ -1,5 +1,5 @@ import Foundation -import ReactiveCocoa +@testable import ReactiveCocoa import ReactiveSwift import enum Result.NoError import Quick @@ -121,7 +121,7 @@ class InterceptingSpec: QuickSpec { .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) .startWithValues { objectValue in latestValue = objectValue as! Bool? - } + } expect(latestValue).to(beNil()) @@ -133,7 +133,7 @@ class InterceptingSpec: QuickSpec { .observeValues { x in firstValue = x[0] as! Bool? secondValue = x[1] as! String? - } + } object.set(first: true, second: "Winner") @@ -146,30 +146,30 @@ class InterceptingSpec: QuickSpec { expect(firstValue) == true expect(secondValue) == "Winner" } -/** + /** it("should send arguments for invocation and invoke the a KVO-swizzled then RAC-swizzled setter") { - var latestValue: Bool? + var latestValue: Bool? - object.reactive - .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) - .startWithValues { objectValue in - latestValue = objectValue as! Bool? - } + object.reactive + .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) + .startWithValues { objectValue in + latestValue = objectValue as! Bool? + } - expect(latestValue).to(beNil()) + expect(latestValue).to(beNil()) - var value: Bool? - object.reactive.signal(for: #selector(setter: object.objectValue)).observeValues { x in - value = x[0] as! Bool? - } + var value: Bool? + object.reactive.signal(for: #selector(setter: object.objectValue)).observeValues { x in + value = x[0] as! Bool? + } - object.objectValue = true + object.objectValue = true - expect(object.objectValue as! Bool?) == true - expect(latestValue) == true - expect(value) == true + expect(object.objectValue as! Bool?) == true + expect(latestValue) == true + expect(value) == true } -**/ + **/ it("should send arguments for invocation and invoke the a RAC-swizzled then KVO-swizzled setter") { let object = InterceptedObject() @@ -184,7 +184,7 @@ class InterceptingSpec: QuickSpec { .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) .startWithValues { objectValue in latestValue = objectValue as! Bool? - } + } expect(latestValue).to(beNil()) @@ -212,7 +212,7 @@ class InterceptingSpec: QuickSpec { .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) .startWithValues { objectValue in latestValue = objectValue as! Bool? - } + } expect(latestValue).to(beNil()) @@ -330,12 +330,12 @@ class InterceptingSpec: QuickSpec { describe("interoperability") { var invoked: Bool! var object: InterceptedObject! - var originalClass: AnyClass! + var originalClass: ObjCClass! beforeEach { invoked = false object = InterceptedObject() - originalClass = InterceptedObject.self + originalClass = ObjCClass(InterceptedObject.self) } it("should invoke the swizzled `forwardInvocation:` on an instance isa-swizzled by both RAC and KVO.") { @@ -348,28 +348,21 @@ class InterceptingSpec: QuickSpec { let swizzledSelector = #selector(object.lifeIsGood) // Redirect `swizzledSelector` to the forwarding machinery. - let method = class_getInstanceMethod(originalClass, swizzledSelector) - let typeDescription = method_getTypeEncoding(method) - let originalImp = class_replaceMethod(originalClass, swizzledSelector, _rac_objc_msgForward(), typeDescription) - - defer { - class_replaceMethod(originalClass, swizzledSelector, originalImp, typeDescription) - } + let method = originalClass.method(for: swizzledSelector)! + let original = method.replaceImplementation(.forwarding) + defer { _ = original.map(method.replaceImplementation) } // Swizzle `forwardInvocation:` to intercept `swizzledSelector`. - let patchForwardInvocationBlock: @convention(block) (AnyObject?, AnyObject?) -> Void = { _, invocation in + let forwardInvocationBlock: @convention(block) (AnyObject?, AnyObject?) -> Void = { _, invocation in if ((invocation as! NSInvocationProtocol).selector == swizzledSelector) { expect(invoked) == false invoked = true } } - let newForwardInvocation = imp_implementationWithBlock(patchForwardInvocationBlock as Any) - let oldForwardInvocation = class_replaceMethod(originalClass, NSObject.forwardInvocationSelector, newForwardInvocation, "v@:@") - - defer { - class_replaceMethod(originalClass, NSObject.forwardInvocationSelector, oldForwardInvocation, "v@:@") - } + let method2 = originalClass.method(for: ObjCSelector.forwardInvocation)! + let original2 = method2.replaceImplementation(CFunction(block: forwardInvocationBlock)) + defer { _ = original2.map(method2.replaceImplementation) } object.lifeIsGood(nil) expect(invoked) == true @@ -381,28 +374,21 @@ class InterceptingSpec: QuickSpec { let swizzledSelector = #selector(object.lifeIsGood) // Redirect `swizzledSelector` to the forwarding machinery. - let method = class_getInstanceMethod(originalClass, swizzledSelector) - let typeEncoding = method_getTypeEncoding(method) - let originalImp = class_replaceMethod(originalClass, swizzledSelector, _rac_objc_msgForward(), typeEncoding) - - defer { - class_replaceMethod(originalClass, swizzledSelector, originalImp, typeEncoding) - } + let method = originalClass.method(for: swizzledSelector)! + let original = method.replaceImplementation(.forwarding) + defer { _ = original.map(method.replaceImplementation) } // Swizzle `forwardInvocation:` to intercept `swizzledSelector`. - let patchForwardInvocationBlock: @convention(block) (AnyObject?, AnyObject?) -> Void = { _, invocation in + let forwardInvocationBlock: @convention(block) (AnyObject?, AnyObject?) -> Void = { _, invocation in if ((invocation as! NSInvocationProtocol).selector == swizzledSelector) { expect(invoked) == false invoked = true } } - let newForwardInvocation = imp_implementationWithBlock(patchForwardInvocationBlock as Any) - let oldForwardInvocation = class_replaceMethod(originalClass, NSObject.forwardInvocationSelector, newForwardInvocation, "v@:@") - - defer { - class_replaceMethod(originalClass, NSObject.forwardInvocationSelector, oldForwardInvocation, "v@:@") - } + let method2 = originalClass.method(for: ObjCSelector.forwardInvocation)! + let original2 = method2.replaceImplementation(CFunction(block: forwardInvocationBlock)) + defer { _ = original2.map(method2.replaceImplementation) } object.lifeIsGood(nil) expect(invoked) == true @@ -413,44 +399,14 @@ class InterceptingSpec: QuickSpec { let swizzledSelector = #selector(object.lifeIsGood) - let method = class_getInstanceMethod(originalClass, swizzledSelector) - let typeEncoding = method_getTypeEncoding(method) - - let methodSwizzlingBlock: @convention(block) (AnyObject?, AnyObject?) -> Void = { _ in + let lifeIsGoodBlock: @convention(block) (AnyObject?, AnyObject?) -> Void = { _ in expect(invoked) == false invoked = true } - let newImplementation = imp_implementationWithBlock(methodSwizzlingBlock as Any) - let oldImplementation = class_replaceMethod(originalClass, swizzledSelector, newImplementation, typeEncoding) - - defer { - class_replaceMethod(originalClass, swizzledSelector, oldImplementation, typeEncoding) - } - - object.lifeIsGood(nil) - expect(invoked) == true - } - - it("should invoke the swizzled setter on an instance isa-swizzled by RAC.") { - _ = object.reactive.trigger(for: #selector(setter: object.objectValue)) - - let swizzledSelector = #selector(object.lifeIsGood) - - let method = class_getInstanceMethod(originalClass, swizzledSelector) - let typeEncoding = method_getTypeEncoding(method) - - let methodSwizzlingBlock: @convention(block) (AnyObject?, AnyObject?) -> Void = { _ in - expect(invoked) == false - invoked = true - } - - let newImplementation = imp_implementationWithBlock(methodSwizzlingBlock as Any) - let oldImplementation = class_replaceMethod(originalClass, swizzledSelector, newImplementation, typeEncoding) - - defer { - class_replaceMethod(originalClass, swizzledSelector, oldImplementation, typeEncoding) - } + let method = originalClass.method(for: swizzledSelector)! + let original = method.replaceImplementation(CFunction(block: lifeIsGoodBlock)) + defer { _ = original.map(method.replaceImplementation) } object.lifeIsGood(nil) expect(invoked) == true @@ -466,7 +422,7 @@ class InterceptingSpec: QuickSpec { .signal(for: #selector(getter: object.description)) .observeValues { x in value = x - } + } expect(value).to(beNil()) @@ -483,7 +439,7 @@ class InterceptingSpec: QuickSpec { .signal(for: #selector(object.lifeIsGood)) .observeValues { x in value = x[0] as! Int? - } + } object.lifeIsGood(42) expect(value) == 42 @@ -505,7 +461,7 @@ class InterceptingSpec: QuickSpec { .signal(for: #selector(object.lifeIsGood)) .observeValues { x in value = x[0] as! Int? - } + } object.reactive .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) @@ -543,14 +499,14 @@ class InterceptingSpec: QuickSpec { .signal(for: #selector(InterceptedObject.foo)) .observeValues { args in superclassTuple = args - } + } subclassObj .reactive .signal(for: #selector(InterceptedObject.foo)) .observeValues { args in subclassTuple = args - } + } expect(superclassObj.foo(40, "foo")) == "Not Subclass 40 foo" @@ -567,14 +523,14 @@ class InterceptingSpec: QuickSpec { .signal(for: #selector(InterceptedObject.set(first:second:))) .observeValues { args in superclassTuple = args - } + } subclassObj .reactive .signal(for: #selector(InterceptedObject.set(first:second:))) .observeValues { args in subclassTuple = args - } + } superclassObj.set(first: "foo", second:"42") expect(superclassObj.hasInvokedSetObjectValueAndSecondObjectValue) == true @@ -601,7 +557,7 @@ class InterceptingSpec: QuickSpec { it("should report the original class") { _ = object.reactive.trigger(for: #selector(object.lifeIsGood)) - expect(object._class).to(beIdenticalTo(originalClass)) + expect((object as AnyObject).objcClass).to(beIdenticalTo(originalClass)) } it("should report the original class when it's KVO'd after dynamically subclassing") { @@ -611,7 +567,7 @@ class InterceptingSpec: QuickSpec { .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) .start() - expect(object._class).to(beIdenticalTo(originalClass)) + expect((object as AnyObject).objcClass).to(beIdenticalTo(originalClass)) } it("should report the original class when it's KVO'd before dynamically subclassing") { @@ -620,7 +576,7 @@ class InterceptingSpec: QuickSpec { .start() _ = object.reactive.trigger(for: #selector(object.lifeIsGood)) - expect(object._class).to(beIdenticalTo(originalClass)) + expect((object as AnyObject).objcClass).to(beIdenticalTo(originalClass)) } } @@ -823,25 +779,6 @@ class InterceptingSpec: QuickSpec { } } -extension NSObject { - fileprivate static var forwardInvocationSelector: Selector { - return Selector((("forwardInvocation:"))) - } - - /// Get the runtime class through the Objective-C `-class` instance method. - /// - /// Swift's `type(of:)` does not respect NSObject's `-class`, and returns the - /// runtime subclasses. - public var _class: AnyClass { - typealias ClassMethod = @convention(c) (AnyObject?, Selector?) -> AnyClass - - let classSelector = Selector((("class"))) - let method = class_getInstanceMethod(object_getClass(self), classSelector) - let impl = unsafeBitCast(method_getImplementation(method), to: ClassMethod.self) - return impl(self, classSelector) - } -} - private class ForwardInvocationTestObject: InterceptedObject { static let forwardedSelector = Selector((("forwarded"))) @@ -851,18 +788,24 @@ private class ForwardInvocationTestObject: InterceptedObject { override open class func initialize() { struct Static { static var token: Int = { - let impl: @convention(block) (AnyObject?, AnyObject?) -> Void = { object, invocation in + let impl: @convention(c) (Any, Selector, AnyObject) -> Void = { object, _, invocation in let object = object as! ForwardInvocationTestObject object.forwardedCount += 1 - object.forwardedSelector = invocation!.selector + object.forwardedSelector = invocation.selector } - let isSuccessful = class_addMethod(ForwardInvocationTestObject.self, NSObject.forwardInvocationSelector, imp_implementationWithBlock(impl as Any), UnsafeRawPointer(("v@:@" as StaticString).utf8Start).assumingMemoryBound(to: Int8.self)) - assert(isSuccessful) - assert(ForwardInvocationTestObject.instancesRespond(to: NSObject.forwardInvocationSelector)) + ObjCClass(ForwardInvocationTestObject.self) + .addMethod(with: CFunction(assuming: impl), + for: ObjCSelector.forwardInvocation, + types: ObjCMethodEncoding.forwardInvocation) + + assert(ForwardInvocationTestObject.instancesRespond(to: ObjCSelector.forwardInvocation)) + + ObjCClass(ForwardInvocationTestObject.self) + .addMethod(with: .forwarding, + for: ForwardInvocationTestObject.forwardedSelector, + types: ObjCMethodEncoding.forwardInvocation) - let isSuccessful2 = class_addMethod(ForwardInvocationTestObject.self, ForwardInvocationTestObject.forwardedSelector, _rac_objc_msgForward(), UnsafeRawPointer(("v@:" as StaticString).utf8Start).assumingMemoryBound(to: Int8.self)) - assert(isSuccessful) assert(ForwardInvocationTestObject.instancesRespond(to: ForwardInvocationTestObject.forwardedSelector)) return 0 @@ -910,10 +853,10 @@ private class InterceptedObject: NSObject { dynamic func set(first: Any?, second: Any?) { objectValue = first secondObjectValue = second - + hasInvokedSetObjectValueAndSecondObjectValue = true } - + dynamic func testNumericValues(c: CChar, s: CShort, i: CInt, l: CLong, ll: CLongLong, uc: CUnsignedChar, us: CUnsignedShort, ui: CUnsignedInt, ul: CUnsignedLong, ull: CUnsignedLongLong, f: CFloat, d: CDouble, b: CBool) {} dynamic func testReferences(nonnull: NSObject, nullable: NSObject?, iuo: NSObject!, class: AnyClass, nullableClass: AnyClass?, iuoClass: AnyClass!) {} dynamic func testBridgedStructs(p: CGPoint, s: CGSize, r: CGRect, a: CGAffineTransform) {} From 98eae5bfe5d8355e271bccacca1d56e383c583d8 Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 18 Nov 2016 00:54:16 +0100 Subject: [PATCH 0584/1028] Rewrote the ObjC object interception logic in Swift. --- ReactiveCocoa.podspec | 2 +- ReactiveCocoa.xcodeproj/project.pbxproj | 58 ++-- ReactiveCocoa/NSObject+Intercepting.swift | 327 ++++++++++++++------ ReactiveCocoa/ObjC+Messages.swift | 4 +- ReactiveCocoa/ObjC+Runtime.swift | 40 ++- ReactiveCocoa/ObjCRuntimeAliases.h | 5 + ReactiveCocoa/ObjCRuntimeAliases.m | 5 + ReactiveCocoa/RACObjCRuntimeUtilities.h | 19 -- ReactiveCocoa/RACObjCRuntimeUtilities.m | 347 ---------------------- ReactiveCocoa/module.modulemap | 2 +- ReactiveCocoaTests/InterceptingSpec.swift | 165 +++++----- ReactiveCocoaTests/SwizzlingSpec.swift | 10 +- 12 files changed, 371 insertions(+), 613 deletions(-) create mode 100644 ReactiveCocoa/ObjCRuntimeAliases.h create mode 100644 ReactiveCocoa/ObjCRuntimeAliases.m delete mode 100644 ReactiveCocoa/RACObjCRuntimeUtilities.h delete mode 100644 ReactiveCocoa/RACObjCRuntimeUtilities.m diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 14e5d7a6d8..740287f381 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |s| s.source = { :git => "https://github.com/ReactiveCocoa/ReactiveCocoa.git", :tag => "#{s.version}" } s.source_files = "ReactiveCocoa/*.{swift,h,m}", "ReactiveCocoa/Shared/*.{swift}" - s.private_header_files = "ReactiveCocoa/RACObjCRuntimeUtilities.h" + s.private_header_files = "ReactiveCocoa/ObjCRuntimeAliases.h" s.osx.source_files = "ReactiveCocoa/AppKit/*.{swift}" s.ios.source_files = "ReactiveCocoa/UIKit/*.{swift}", "ReactiveCocoa/UIKit/iOS/*.{swift}" s.tvos.source_files = "ReactiveCocoa/UIKit/*.{swift}" diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 793db7ee82..4b4532e9cb 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -111,10 +111,6 @@ 9A1D065B1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; 9A1D065C1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; 9A1D065E1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; - 9A1D06771D9415FF00ACF44C /* RACObjCRuntimeUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06761D9415FB00ACF44C /* RACObjCRuntimeUtilities.m */; }; - 9A1D06781D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06761D9415FB00ACF44C /* RACObjCRuntimeUtilities.m */; }; - 9A1D06791D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06761D9415FB00ACF44C /* RACObjCRuntimeUtilities.m */; }; - 9A1D067A1D94160100ACF44C /* RACObjCRuntimeUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D06761D9415FB00ACF44C /* RACObjCRuntimeUtilities.m */; }; 9A1E72BA1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; 9A1E72BB1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; 9A1E72BC1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; @@ -125,10 +121,10 @@ 9A6AAA0E1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; 9A6AAA0F1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; 9A6AAA101DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; - 9A6AAA191DB808A80013AAEA /* RACObjCRuntimeUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A1D06751D9415FB00ACF44C /* RACObjCRuntimeUtilities.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 9A6AAA1A1DB808A80013AAEA /* RACObjCRuntimeUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A1D06751D9415FB00ACF44C /* RACObjCRuntimeUtilities.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 9A6AAA1B1DB808A90013AAEA /* RACObjCRuntimeUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A1D06751D9415FB00ACF44C /* RACObjCRuntimeUtilities.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 9A6AAA1C1DB808AA0013AAEA /* RACObjCRuntimeUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A1D06751D9415FB00ACF44C /* RACObjCRuntimeUtilities.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 9A6AAA191DB808A80013AAEA /* ObjCRuntimeAliases.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A1D06751D9415FB00ACF44C /* ObjCRuntimeAliases.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 9A6AAA1A1DB808A80013AAEA /* ObjCRuntimeAliases.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A1D06751D9415FB00ACF44C /* ObjCRuntimeAliases.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 9A6AAA1B1DB808A90013AAEA /* ObjCRuntimeAliases.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A1D06751D9415FB00ACF44C /* ObjCRuntimeAliases.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 9A6AAA1C1DB808AA0013AAEA /* ObjCRuntimeAliases.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A1D06751D9415FB00ACF44C /* ObjCRuntimeAliases.h */; settings = {ATTRIBUTES = (Private, ); }; }; 9A6AAA231DB8F51C0013AAEA /* ReusableComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA221DB8F51C0013AAEA /* ReusableComponents.swift */; }; 9A6AAA241DB8F51C0013AAEA /* ReusableComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA221DB8F51C0013AAEA /* ReusableComponents.swift */; }; 9A6AAA261DB8F5280013AAEA /* ReusableComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA251DB8F5280013AAEA /* ReusableComponents.swift */; }; @@ -159,6 +155,10 @@ 9AA0BD901DDE29F800531FCF /* NSObject+ObjCRuntime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD8E1DDE29F800531FCF /* NSObject+ObjCRuntime.swift */; }; 9AA0BD911DDE29F800531FCF /* NSObject+ObjCRuntime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD8E1DDE29F800531FCF /* NSObject+ObjCRuntime.swift */; }; 9AA0BD921DDE29F800531FCF /* NSObject+ObjCRuntime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD8E1DDE29F800531FCF /* NSObject+ObjCRuntime.swift */; }; + 9AA0BD981DDE7A2200531FCF /* ObjCRuntimeAliases.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD971DDE7A2200531FCF /* ObjCRuntimeAliases.m */; }; + 9AA0BD991DDE7A2200531FCF /* ObjCRuntimeAliases.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD971DDE7A2200531FCF /* ObjCRuntimeAliases.m */; }; + 9AA0BD9A1DDE7A2200531FCF /* ObjCRuntimeAliases.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD971DDE7A2200531FCF /* ObjCRuntimeAliases.m */; }; + 9AA0BD9B1DDE7A2200531FCF /* ObjCRuntimeAliases.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD971DDE7A2200531FCF /* ObjCRuntimeAliases.m */; }; 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; 9AD0F06B1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; 9AD0F06C1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; @@ -344,8 +344,7 @@ 9A1D06331D93EA7E00ACF44C /* UITextViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextViewSpec.swift; sourceTree = ""; }; 9A1D06351D93EA7E00ACF44C /* UIViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewSpec.swift; sourceTree = ""; }; 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Intercepting.swift"; sourceTree = ""; }; - 9A1D06751D9415FB00ACF44C /* RACObjCRuntimeUtilities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RACObjCRuntimeUtilities.h; sourceTree = ""; }; - 9A1D06761D9415FB00ACF44C /* RACObjCRuntimeUtilities.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RACObjCRuntimeUtilities.m; sourceTree = ""; }; + 9A1D06751D9415FB00ACF44C /* ObjCRuntimeAliases.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ObjCRuntimeAliases.h; sourceTree = ""; }; 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyValueObservingSpec.swift; sourceTree = ""; }; 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaTarget.swift; sourceTree = ""; }; 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterceptingSpec.swift; sourceTree = ""; }; @@ -359,6 +358,7 @@ 9AA0BD851DDE0A8B00531FCF /* SwizzlingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwizzlingSpec.swift; sourceTree = ""; }; 9AA0BD891DDE153A00531FCF /* ObjC+Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObjC+Constants.swift"; sourceTree = ""; }; 9AA0BD8E1DDE29F800531FCF /* NSObject+ObjCRuntime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+ObjCRuntime.swift"; sourceTree = ""; }; + 9AA0BD971DDE7A2200531FCF /* ObjCRuntimeAliases.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ObjCRuntimeAliases.m; sourceTree = ""; }; 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+KeyValueObserving.swift"; sourceTree = ""; }; 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaAction.swift; sourceTree = ""; }; 9ADE4A881DA6D206005C2AC8 /* NSControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSControl.swift; sourceTree = ""; }; @@ -660,9 +660,6 @@ D04725EC19E49ED7006002AA /* ReactiveCocoa */ = { isa = PBXGroup; children = ( - D04725EF19E49ED7006002AA /* ReactiveCocoa.h */, - 9A1D06751D9415FB00ACF44C /* RACObjCRuntimeUtilities.h */, - 9A1D06761D9415FB00ACF44C /* RACObjCRuntimeUtilities.m */, 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */, 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */, CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */, @@ -689,6 +686,9 @@ D04725ED19E49ED7006002AA /* Supporting Files */ = { isa = PBXGroup; children = ( + D04725EF19E49ED7006002AA /* ReactiveCocoa.h */, + 9A1D06751D9415FB00ACF44C /* ObjCRuntimeAliases.h */, + 9AA0BD971DDE7A2200531FCF /* ObjCRuntimeAliases.m */, 9AE7C2A21DDD768500F7534C /* module.modulemap */, CDC42E2E1AE7AB8B00965373 /* Result.framework */, D04725EE19E49ED7006002AA /* Info.plist */, @@ -803,7 +803,7 @@ buildActionMask = 2147483647; files = ( 57A4D20A1BA13D7A00F7D4B1 /* ReactiveCocoa.h in Headers */, - 9A6AAA1C1DB808AA0013AAEA /* RACObjCRuntimeUtilities.h in Headers */, + 9A6AAA1C1DB808AA0013AAEA /* ObjCRuntimeAliases.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -812,7 +812,7 @@ buildActionMask = 2147483647; files = ( A9B315CA1B3940AB0001CB9C /* ReactiveCocoa.h in Headers */, - 9A6AAA1B1DB808A90013AAEA /* RACObjCRuntimeUtilities.h in Headers */, + 9A6AAA1B1DB808A90013AAEA /* ObjCRuntimeAliases.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -821,7 +821,7 @@ buildActionMask = 2147483647; files = ( D04725F019E49ED7006002AA /* ReactiveCocoa.h in Headers */, - 9A6AAA191DB808A80013AAEA /* RACObjCRuntimeUtilities.h in Headers */, + 9A6AAA191DB808A80013AAEA /* ObjCRuntimeAliases.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -830,7 +830,7 @@ buildActionMask = 2147483647; files = ( D037666419EDA43C00A782A9 /* ReactiveCocoa.h in Headers */, - 9A6AAA1A1DB808A80013AAEA /* RACObjCRuntimeUtilities.h in Headers */, + 9A6AAA1A1DB808A80013AAEA /* ObjCRuntimeAliases.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -976,7 +976,7 @@ ORGANIZATIONNAME = GitHub; TargetAttributes = { 57A4D1AF1BA13D7A00F7D4B1 = { - LastSwiftMigration = 0800; + LastSwiftMigration = 0810; }; 7DFBED021CDB8C9500EE435B = { CreatedOnToolsVersion = 7.3.1; @@ -984,11 +984,11 @@ }; A9B315531B3940610001CB9C = { CreatedOnToolsVersion = 7.0; - LastSwiftMigration = 0800; + LastSwiftMigration = 0810; }; D04725E919E49ED7006002AA = { CreatedOnToolsVersion = 6.1; - LastSwiftMigration = 0800; + LastSwiftMigration = 0810; }; D04725F419E49ED7006002AA = { CreatedOnToolsVersion = 6.1; @@ -996,7 +996,7 @@ }; D047260B19E49F82006002AA = { CreatedOnToolsVersion = 6.1; - LastSwiftMigration = 0800; + LastSwiftMigration = 0810; }; D047261519E49F82006002AA = { CreatedOnToolsVersion = 6.1; @@ -1099,8 +1099,8 @@ 9AA0BD841DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift in Sources */, 9AD0F06D1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, 9A1D06211D93EA0100ACF44C /* UIView.swift in Sources */, - 9A1D067A1D94160100ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 538DCB7B1DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */, + 9AA0BD9B1DDE7A2200531FCF /* ObjCRuntimeAliases.m in Sources */, 9ADE4A941DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, 9A1D05E31D93E99100ACF44C /* NSObject+Association.swift in Sources */, 419139491DB910570043C9D1 /* UIGestureRecognizer.swift in Sources */, @@ -1165,7 +1165,7 @@ 9A2E42601DAA6737006D909F /* CocoaTarget.swift in Sources */, 9A9DFEE51DA7B5500039EE1B /* NSObject+Intercepting.swift in Sources */, 9AA0BD7E1DDE03DE00531FCF /* ObjC+Runtime.swift in Sources */, - 9A1D06791D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */, + 9AA0BD9A1DDE7A2200531FCF /* ObjCRuntimeAliases.m in Sources */, 9ADE4A931DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, 9AA0BD8C1DDE153A00531FCF /* ObjC+Constants.swift in Sources */, 9ADFE5A71DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */, @@ -1188,7 +1188,6 @@ 4ABEFE2B1DCFD0030066A8C2 /* NSTableView.swift in Sources */, CD0C45DE1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 4ABEFE301DCFD0530066A8C2 /* NSCollectionView.swift in Sources */, - 9A1D06771D9415FF00ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, 9AA0BD8F1DDE29F800531FCF /* NSObject+ObjCRuntime.swift in Sources */, 9A1D05E01D93E99100ACF44C /* NSObject+Association.swift in Sources */, @@ -1200,6 +1199,7 @@ 9ADFE5A51DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */, 9ADE4A7C1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, 9ADE4A961DA6F018005C2AC8 /* NSTextField.swift in Sources */, + 9AA0BD981DDE7A2200531FCF /* ObjCRuntimeAliases.m in Sources */, 9A2E425E1DAA6737006D909F /* CocoaTarget.swift in Sources */, 9ADE4A891DA6D206005C2AC8 /* NSControl.swift in Sources */, 9AF0EA751D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, @@ -1247,7 +1247,6 @@ CD0C45DF1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9AE7C2A51DDD7F5100F7534C /* ObjC+Messages.swift in Sources */, 9A2E425F1DAA6737006D909F /* CocoaTarget.swift in Sources */, - 9A1D06781D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */, 53AC46CC1DD6F97400C799E1 /* UISlider.swift in Sources */, 9AA0BD821DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift in Sources */, 9A1D060F1D93EA0000ACF44C /* UIView.swift in Sources */, @@ -1271,6 +1270,7 @@ 531866F81DD7920400D1285F /* UIStepper.swift in Sources */, 538DCB7A1DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */, 9A1D06001D93EA0000ACF44C /* UIBarButtonItem.swift in Sources */, + 9AA0BD991DDE7A2200531FCF /* ObjCRuntimeAliases.m in Sources */, 9A1D05FF1D93EA0000ACF44C /* UIActivityIndicatorView.swift in Sources */, 9AA0BD8B1DDE153A00531FCF /* ObjC+Constants.swift in Sources */, 9A1D06091D93EA0000ACF44C /* UISegmentedControl.swift in Sources */, @@ -1533,7 +1533,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 3.0.1; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -1553,7 +1553,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 3.0.1; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -1672,7 +1672,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 3.0.1; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -1741,7 +1741,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)-Tests"; PRODUCT_NAME = "$(PROJECT_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 3.0.1; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 6a4272b456..6b5bd229c1 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -2,6 +2,8 @@ import Foundation import ReactiveSwift import enum Result.NoError +private let swizzledClasses = Atomic>([]) + extension Reactive where Base: NSObject { /// Create a signal which sends a `next` event at the end of every invocation /// of `selector` on the object. @@ -13,23 +15,7 @@ extension Reactive where Base: NSObject { /// A trigger signal. public func trigger(for selector: Selector) -> Signal<(), NoError> { return base.synchronized { - let map = associatedValue { _ in NSMutableDictionary() } - - let selectorName = String(describing: selector) as NSString - if let signal = map.object(forKey: selectorName) as! Signal<(), NoError>? { - return signal - } - - let (signal, observer) = Signal<(), NoError>.pipe() - let isSuccessful = base._rac_setupInvocationObservation(for: selector, - protocol: nil, - receiver: observer.send(value:)) - precondition(isSuccessful) - - lifetime.ended.observeCompleted(observer.sendCompleted) - map.setObject(signal, forKey: selectorName) - - return signal + return setupInterception(base, for: selector).map { _ in } } } @@ -43,99 +29,250 @@ extension Reactive where Base: NSObject { /// A signal that sends an array of bridged arguments. public func signal(for selector: Selector) -> Signal<[Any?], NoError> { return base.synchronized { - let map = associatedValue { _ in NSMutableDictionary() } + return setupInterception(base, for: selector).map(unpackInvocation) + } + } +} + +private func setupInterception(_ object: NSObject, for selector: Selector) -> Signal { + let invokingAlias = selector.invokingAlias + + if let signal = object.value(forAssociatedKey: invokingAlias.utf8Start) as! Signal? { + return signal + } - let selectorName = String(describing: selector) as NSString - if let signal = map.object(forKey: selectorName) as! Signal<[Any?], NoError>? { - return signal + let signal = Signal { observer in + let disposable = CompositeDisposable() + + disposable += object.reactive.lifetime.ended + .observeCompleted(observer.sendCompleted) + + let subclass = swizzleClass(object) + + swizzledClasses.modify { classes in + if !classes.contains(subclass) { + classes.insert(subclass) + enableMessageForwarding(subclass) } + } - let (signal, observer) = Signal<[Any?], NoError>.pipe() - let isSuccessful = base._rac_setupInvocationObservation(for: selector, - protocol: nil, - argsReceiver: bridge(observer)) - precondition(isSuccessful) + guard let method = subclass.method(for: selector) else { + fatalError("Selector `\(selector)` does not exist in class `\(subclass.superclass!.name)`.") + } + + if !method.function.isForwarder { + assert(checkTypeEncoding(method.typeEncoding)) - lifetime.ended.observeCompleted(observer.sendCompleted) - map.setObject(signal, forKey: selectorName) + if let existingMethod = subclass.method(for: selector, searchesAncestors: false) { + // Make a method alias for the existing method implementation, if it is + // defined in the runtime subclass. + let interopAlias = selector.interopAlias + try! subclass.addMethod(with: existingMethod.function, + for: interopAlias, + types: existingMethod.typeEncoding) + } - return signal + // Redefine the selector to call -forwardInvocation:. + _ = subclass.replaceMethod(with: .forwarding, + for: selector, + types: method.typeEncoding) } + + object.setValue(observer.send(value:), forAssociatedKey: selector.utf8Start) + return disposable + } + + object.setValue(signal, forAssociatedKey: invokingAlias.utf8Start) + return signal +} + +extension Selector { + fileprivate var invokingAlias: Selector { + return "rac0_".appending(String(cString: sel_getName(self))).withCString(sel_registerName) + } + + fileprivate var interopAlias: Selector { + return "rac1_".appending(String(cString: sel_getName(self))).withCString(sel_registerName) + } + + fileprivate var utf8Start: UnsafePointer { + return unsafeBitCast(self, to: UnsafePointer.self) } } -private func bridge(_ observer: Observer<[Any?], NoError>) -> (Any) -> Void { - return { arguments in - let arguments = arguments as AnyObject - let methodSignature = arguments.objcMethodSignature! - let count = UInt(methodSignature.numberOfArguments!) - - var bridged = [Any?]() - bridged.reserveCapacity(Int(count - 2)) - - // Ignore `self` and `_cmd`. - for position in 2 ..< count { - let rawEncoding = methodSignature.argumentType(at: position) - let encoding = TypeEncoding(rawValue: rawEncoding.pointee) ?? .undefined - - func extract(_ type: U.Type) -> U { - let pointer = UnsafeMutableRawPointer.allocate(bytes: MemoryLayout.size, - alignedTo: MemoryLayout.alignment) - defer { - pointer.deallocate(bytes: MemoryLayout.size, - alignedTo: MemoryLayout.alignment) - } - - arguments.copy(to: pointer, forArgumentAt: Int(position)) - return pointer.assumingMemoryBound(to: type).pointee +private func enableMessageForwarding(_ objcClass: ObjCClass) { + // Set up a new version of -forwardInvocation:. + // + // If the selector has been passed to -rac_signalForSelector:, invoke + // the aliased method, and forward the arguments to any attached signals. + // + // If the selector has not been passed to -rac_signalForSelector:, + // invoke any existing implementation of -forwardInvocation:. If there + // was no existing implementation, throw an unrecognized selector + // exception. + typealias ForwardInvocationImpl = @convention(c) (NSObject, Selector, AnyObject) -> Void + let newForwardInvocation: ForwardInvocationImpl = { object, _, invocation in + var shouldForward = true + + let selector = invocation.selector! + let invokingAlias = selector.invokingAlias + let interopAlias = selector.interopAlias + + let realClass = ObjCClass.type(of: object) + let perceivedClass = ObjCClass(object.objcClass) + + defer { + if let observer = object.value(forAssociatedKey: selector.utf8Start) as! ((AnyObject) -> Void)? { + observer(invocation) } + } - switch encoding { - case .char: - bridged.append(NSNumber(value: extract(CChar.self))) - case .int: - bridged.append(NSNumber(value: extract(CInt.self))) - case .short: - bridged.append(NSNumber(value: extract(CShort.self))) - case .long: - bridged.append(NSNumber(value: extract(CLong.self))) - case .longLong: - bridged.append(NSNumber(value: extract(CLongLong.self))) - case .unsignedChar: - bridged.append(NSNumber(value: extract(CUnsignedChar.self))) - case .unsignedInt: - bridged.append(NSNumber(value: extract(CUnsignedInt.self))) - case .unsignedShort: - bridged.append(NSNumber(value: extract(CUnsignedShort.self))) - case .unsignedLong: - bridged.append(NSNumber(value: extract(CUnsignedLong.self))) - case .unsignedLongLong: - bridged.append(NSNumber(value: extract(CUnsignedLongLong.self))) - case .float: - bridged.append(NSNumber(value: extract(CFloat.self))) - case .double: - bridged.append(NSNumber(value: extract(CDouble.self))) - case .bool: - bridged.append(NSNumber(value: extract(CBool.self))) - case .object: - bridged.append(extract((AnyObject?).self)) - case .type: - bridged.append(extract((AnyClass?).self)) - case .selector: - bridged.append(extract((Selector?).self)) - case .undefined: - var size = 0, alignment = 0 - NSGetSizeAndAlignment(rawEncoding, &size, &alignment) - let buffer = UnsafeMutableRawPointer.allocate(bytes: size, alignedTo: alignment) - defer { buffer.deallocate(bytes: size, alignedTo: alignment) } - - arguments.copy(to: buffer, forArgumentAt: Int(position)) - bridged.append(NSValue(bytes: buffer, objCType: rawEncoding)) + // RAC exchanges implementations at runtime so as to invoke the desired + // version without using fragile dependencies like libffi. This means + // all instances that had been applied `signalForSelector:` are + // non-threadsafe as any mutable instances. + + if realClass.reference.instancesRespond(to: interopAlias) { + // `self` uses a runtime subclass generated by third-party APIs, and RAC + // found an existing implementation for the selector at the setup time. + // Call that implementation if it is not the ObjC message forwarder. + let interopImpl = realClass.method(for: interopAlias)!.function + + if !interopImpl.isForwarder { + let method = realClass.method(for: selector)! + let previousImpl = realClass.replaceMethod(with: interopImpl, + for: selector, + types: method.typeEncoding) + + invocation.invoke() + + _ = realClass.replaceMethod(with: previousImpl, + for: selector, + types: method.typeEncoding) + + return + } + } + + if perceivedClass.reference.instancesRespond(to: selector) { + // The stated class has an implementation of the selector. Call that + // implementation if it is not the ObjC message forwarder. + let method = perceivedClass.method(for: selector)! + let originalImpl = method.function + + if !originalImpl.isForwarder { + _ = realClass.replaceMethod(with: originalImpl, + for: invokingAlias, + types: method.typeEncoding) + + invocation.setSelector(invokingAlias) + invocation.invoke() + + return } } - observer.send(value: bridged) + // Forward the invocation to the closest `forwardInvocation:` in the + // inheritance hierarchy. + var target = objc_super() + target.receiver = Unmanaged.passUnretained(object) + target.super_class = perceivedClass.reference + + typealias SuperForwardInvocation = @convention(c) (UnsafeMutablePointer, Selector, AnyObject) -> Void + let send = unsafeBitCast(_rac_objc_msgSendSuper, to: SuperForwardInvocation.self) + send(&target, ObjCSelector.forwardInvocation, invocation) } + + _ = objcClass.replaceMethod(with: CFunction(assuming: newForwardInvocation), + for: ObjCSelector.forwardInvocation, + types: ObjCMethodEncoding.forwardInvocation) +} + +func checkTypeEncoding(_ types: UnsafePointer) -> Bool { + // Some types, including vector types, are not encoded. In these cases the + // signature starts with the size of the argument frame. + assert(types.pointee < Int8(UInt8(ascii: "1")) || types.pointee > Int8(UInt8(ascii: "9")), + "unknown method return type not supported in type encoding: \(String(cString: types))") + + assert(types.pointee != Int8(UInt8(ascii: "(")), "union method return type not supported") + assert(types.pointee != Int8(UInt8(ascii: "{")), "struct method return type not supported") + assert(types.pointee != Int8(UInt8(ascii: "[")), "array method return type not supported") + + assert(types.pointee != Int8(UInt8(ascii: "j")), "complex method return type not supported") + + return true +} + +private func unpackInvocation(_ invocation: AnyObject) -> [Any?] { + let invocation = invocation as AnyObject + let methodSignature = invocation.objcMethodSignature! + let count = UInt(methodSignature.numberOfArguments!) + + var bridged = [Any?]() + bridged.reserveCapacity(Int(count - 2)) + + // Ignore `self` and `_cmd`. + for position in 2 ..< count { + let rawEncoding = methodSignature.argumentType(at: position) + let encoding = TypeEncoding(rawValue: rawEncoding.pointee) ?? .undefined + + func extract(_ type: U.Type) -> U { + let pointer = UnsafeMutableRawPointer.allocate(bytes: MemoryLayout.size, + alignedTo: MemoryLayout.alignment) + defer { + pointer.deallocate(bytes: MemoryLayout.size, + alignedTo: MemoryLayout.alignment) + } + + invocation.copy(to: pointer, forArgumentAt: Int(position)) + return pointer.assumingMemoryBound(to: type).pointee + } + + switch encoding { + case .char: + bridged.append(NSNumber(value: extract(CChar.self))) + case .int: + bridged.append(NSNumber(value: extract(CInt.self))) + case .short: + bridged.append(NSNumber(value: extract(CShort.self))) + case .long: + bridged.append(NSNumber(value: extract(CLong.self))) + case .longLong: + bridged.append(NSNumber(value: extract(CLongLong.self))) + case .unsignedChar: + bridged.append(NSNumber(value: extract(CUnsignedChar.self))) + case .unsignedInt: + bridged.append(NSNumber(value: extract(CUnsignedInt.self))) + case .unsignedShort: + bridged.append(NSNumber(value: extract(CUnsignedShort.self))) + case .unsignedLong: + bridged.append(NSNumber(value: extract(CUnsignedLong.self))) + case .unsignedLongLong: + bridged.append(NSNumber(value: extract(CUnsignedLongLong.self))) + case .float: + bridged.append(NSNumber(value: extract(CFloat.self))) + case .double: + bridged.append(NSNumber(value: extract(CDouble.self))) + case .bool: + bridged.append(NSNumber(value: extract(CBool.self))) + case .object: + bridged.append(extract((AnyObject?).self)) + case .type: + bridged.append(extract((AnyClass?).self)) + case .selector: + bridged.append(extract((Selector?).self)) + case .undefined: + var size = 0, alignment = 0 + NSGetSizeAndAlignment(rawEncoding, &size, &alignment) + let buffer = UnsafeMutableRawPointer.allocate(bytes: size, alignedTo: alignment) + defer { buffer.deallocate(bytes: size, alignedTo: alignment) } + + invocation.copy(to: buffer, forArgumentAt: Int(position)) + bridged.append(NSValue(bytes: buffer, objCType: rawEncoding)) + } + } + + return bridged } private enum TypeEncoding: Int8 { diff --git a/ReactiveCocoa/ObjC+Messages.swift b/ReactiveCocoa/ObjC+Messages.swift index 8a9dee71f8..3fd5d62291 100644 --- a/ReactiveCocoa/ObjC+Messages.swift +++ b/ReactiveCocoa/ObjC+Messages.swift @@ -4,8 +4,8 @@ } @objc internal protocol ObjCInvocation { - var target: NSObject? { get set } - var selector: Selector? { get set } + @objc(setSelector:) + func setSelector(_ selector: Selector) @objc(methodSignature) var objcMethodSignature: AnyObject { get } diff --git a/ReactiveCocoa/ObjC+Runtime.swift b/ReactiveCocoa/ObjC+Runtime.swift index f3089e531b..c01dcfd905 100644 --- a/ReactiveCocoa/ObjC+Runtime.swift +++ b/ReactiveCocoa/ObjC+Runtime.swift @@ -1,4 +1,8 @@ internal struct ObjCClass { + enum Error: Swift.Error { + case methodAlreadyExist + } + let reference: AnyClass var name: String { @@ -29,19 +33,21 @@ internal struct ObjCClass { } } - func addMethod(with implementation: CFunction, for selector: Selector, types: UnsafePointer) { - class_addMethod(reference, selector, implementation.reference, types) + func addMethod(with implementation: CFunction, for selector: Selector, types: UnsafePointer) throws { + if !class_addMethod(reference, selector, implementation.reference, types) { + throw Error.methodAlreadyExist + } } - func replaceMethod(with implementation: CFunction, for selector: Selector, types: UnsafePointer) -> CFunction? { - let imp = class_replaceMethod(reference, selector, implementation.reference, types) + func replaceMethod(with implementation: CFunction?, for selector: Selector, types: UnsafePointer) -> CFunction? { + let imp = class_replaceMethod(reference, selector, implementation?.reference, types) return imp.map(CFunction.init) } func method(for selector: Selector, searchesAncestors: Bool = true) -> ObjCMethod? { if searchesAncestors { if let method = class_getInstanceMethod(reference, selector) { - return ObjCMethod(method, in: self) + return ObjCMethod(method) } } else { var count: UInt32 = 0 @@ -52,7 +58,7 @@ internal struct ObjCClass { for method in methods { if method_getName(method!) == selector { - return ObjCMethod(method!, in: self) + return ObjCMethod(method!) } } } @@ -89,7 +95,6 @@ extension ObjCClass { } internal struct ObjCMethod { - fileprivate let parent: ObjCClass fileprivate let reference: Method var function: CFunction { @@ -100,23 +105,20 @@ internal struct ObjCMethod { return method_getTypeEncoding(reference) } - init(_ reference: Method, in class: ObjCClass) { + init(_ reference: Method) { self.reference = reference - self.parent = `class` - } - - func replaceImplementation(_ implementation: CFunction) -> CFunction? { - return parent.replaceMethod(with: function, - for: method_getName(reference), - types: method_getTypeEncoding(reference)) } } internal struct CFunction { - static let forwarding = CFunction(_rac_objc_msgForward()) + static let forwarding = CFunction(_rac_objc_msgForward) fileprivate let reference: IMP + var isForwarder: Bool { + return reference == CFunction.forwarding.reference + } + init(assuming block: U) { self.reference = unsafeBitCast(block, to: IMP.self) } @@ -129,3 +131,9 @@ internal struct CFunction { self.reference = imp_implementationWithBlock(block) } } + +extension CFunction: Equatable { + static func ==(left: CFunction, right: CFunction) -> Bool { + return left.reference == right.reference + } +} diff --git a/ReactiveCocoa/ObjCRuntimeAliases.h b/ReactiveCocoa/ObjCRuntimeAliases.h new file mode 100644 index 0000000000..1770c68163 --- /dev/null +++ b/ReactiveCocoa/ObjCRuntimeAliases.h @@ -0,0 +1,5 @@ +#import +#import + +const IMP _rac_objc_msgForward; +const IMP _rac_objc_msgSendSuper; diff --git a/ReactiveCocoa/ObjCRuntimeAliases.m b/ReactiveCocoa/ObjCRuntimeAliases.m new file mode 100644 index 0000000000..675c5a6696 --- /dev/null +++ b/ReactiveCocoa/ObjCRuntimeAliases.m @@ -0,0 +1,5 @@ +#import +#import + +const IMP _rac_objc_msgForward = _objc_msgForward; +const IMP _rac_objc_msgSendSuper = (typeof(IMP)) objc_msgSendSuper; diff --git a/ReactiveCocoa/RACObjCRuntimeUtilities.h b/ReactiveCocoa/RACObjCRuntimeUtilities.h deleted file mode 100644 index 27e5c9508b..0000000000 --- a/ReactiveCocoa/RACObjCRuntimeUtilities.h +++ /dev/null @@ -1,19 +0,0 @@ -#import - -NS_ASSUME_NONNULL_BEGIN -IMP _rac_objc_msgForward(); - -@interface NSObject (RACObjCRuntimeUtilities) - -/// Register a block which would be triggered when `selector` is called. -/// -/// Warning: The callee is responsible for synchronization. --(BOOL) _rac_setupInvocationObservationForSelector:(SEL)selector protocol:(nullable Protocol *)protocol argsReceiver:(void (^)(id)) receiverBlock; - -/// Register a block which would be triggered when `selector` is called. -/// -/// Warning: The callee is responsible for synchronization. --(BOOL) _rac_setupInvocationObservationForSelector:(SEL)selector protocol:(nullable Protocol *)protocol receiver:(void (^)(void)) receiverBlock; - -@end -NS_ASSUME_NONNULL_END diff --git a/ReactiveCocoa/RACObjCRuntimeUtilities.m b/ReactiveCocoa/RACObjCRuntimeUtilities.m deleted file mode 100644 index 1ace128fc1..0000000000 --- a/ReactiveCocoa/RACObjCRuntimeUtilities.m +++ /dev/null @@ -1,347 +0,0 @@ -#import "RACObjCRuntimeUtilities.h" -#import -#import -#import - -NSString * const RACSelectorSignalErrorDomain = @"RACSelectorSignalErrorDomain"; -const NSInteger RACSelectorSignalErrorMethodSwizzlingRace = 1; -const NSExceptionName RACSwizzleException = @"RACSwizzleException"; - -static NSString * const RACSignalForSelectorAliasPrefix = @"rac_swift_"; -static NSString * const RACSubclassSuffix = @"_RACIntercepting"; -static void *RACSubclassAssociationKey = &RACSubclassAssociationKey; - -static NSMutableSet *swizzledClasses() { - static NSMutableSet *set; - static dispatch_once_t pred; - - dispatch_once(&pred, ^{ - set = [[NSMutableSet alloc] init]; - }); - - return set; -} - -IMP _rac_objc_msgForward() { - return _objc_msgForward; -} - -@interface RACForwardingInfo : NSObject - -@property (readonly, nonatomic) BOOL isTrigger; -@property (readonly, nonatomic, retain) id block; - --(instancetype) initWithBlock:(id)block isTrigger:(BOOL)isTrigger; - -@end - -static SEL RACAliasForSelector(SEL originalSelector) { - NSString *selectorName = NSStringFromSelector(originalSelector); - return NSSelectorFromString([RACSignalForSelectorAliasPrefix stringByAppendingString:selectorName]); -} - -static BOOL RACForwardInvocation(id self, NSInvocation *invocation) { - SEL aliasSelector = RACAliasForSelector(invocation.selector); - RACForwardingInfo* receiver = objc_getAssociatedObject(self, aliasSelector); - - Class class = object_getClass(invocation.target); - BOOL respondsToAlias = [class instancesRespondToSelector:aliasSelector]; - if (respondsToAlias) { - invocation.selector = aliasSelector; - [invocation invoke]; - } - - if (receiver == nil) return respondsToAlias; - - if (receiver.isTrigger) { - __block void(^block)(void) = receiver.block; - block(); - } else { - __block void(^block)(id) = receiver.block; - block(invocation); - } - - return YES; -} - -static void RACSwizzleForwardInvocation(Class class) { - SEL forwardInvocationSEL = @selector(forwardInvocation:); - Method forwardInvocationMethod = class_getInstanceMethod(class, forwardInvocationSEL); - - // Preserve any existing implementation of -forwardInvocation:. - void (*originalForwardInvocation)(id, SEL, NSInvocation *) = NULL; - if (forwardInvocationMethod != NULL) { - originalForwardInvocation = (__typeof__(originalForwardInvocation))method_getImplementation(forwardInvocationMethod); - } - - // Set up a new version of -forwardInvocation:. - // - // If the selector has been passed to -rac_signalForSelector:, invoke - // the aliased method, and forward the arguments to any attached signals. - // - // If the selector has not been passed to -rac_signalForSelector:, - // invoke any existing implementation of -forwardInvocation:. If there - // was no existing implementation, throw an unrecognized selector - // exception. - id newForwardInvocation = ^(id self, NSInvocation *invocation) { - BOOL matched = RACForwardInvocation(self, invocation); - if (matched) return; - - if (originalForwardInvocation == NULL) { - [self doesNotRecognizeSelector:invocation.selector]; - } else { - originalForwardInvocation(self, forwardInvocationSEL, invocation); - } - }; - - class_replaceMethod(class, forwardInvocationSEL, imp_implementationWithBlock(newForwardInvocation), "v@:@"); -} - -Method rac_getImmediateInstanceMethod (Class aClass, SEL aSelector) { - unsigned methodCount = 0; - Method *methods = class_copyMethodList(aClass, &methodCount); - Method foundMethod = NULL; - - for (unsigned methodIndex = 0;methodIndex < methodCount;++methodIndex) { - if (method_getName(methods[methodIndex]) == aSelector) { - foundMethod = methods[methodIndex]; - break; - } - } - - free(methods); - return foundMethod; -} - -static void RACSwizzleRespondsToSelector(Class class) { - SEL respondsToSelectorSEL = @selector(respondsToSelector:); - - // Preserve existing implementation of -respondsToSelector:. - Method respondsToSelectorMethod = class_getInstanceMethod(class, respondsToSelectorSEL); - BOOL (*originalRespondsToSelector)(id, SEL, SEL) = (__typeof__(originalRespondsToSelector))method_getImplementation(respondsToSelectorMethod); - - // Set up a new version of -respondsToSelector: that returns YES for methods - // added by -rac_signalForSelector:. - // - // If the selector has a method defined on the receiver's actual class, and - // if that method's implementation is _objc_msgForward, then returns whether - // the instance has a signal for the selector. - // Otherwise, call the original -respondsToSelector:. - id newRespondsToSelector = ^ BOOL (id self, SEL selector) { - Method method = rac_getImmediateInstanceMethod(class, selector); - - if (method != NULL && method_getImplementation(method) == _objc_msgForward) { - SEL aliasSelector = RACAliasForSelector(selector); - if (objc_getAssociatedObject(self, aliasSelector) != nil) return YES; - } - - return originalRespondsToSelector(self, respondsToSelectorSEL, selector); - }; - - class_replaceMethod(class, respondsToSelectorSEL, imp_implementationWithBlock(newRespondsToSelector), method_getTypeEncoding(respondsToSelectorMethod)); -} - -static void RACSwizzleGetClass(Class class, Class statedClass) { - SEL selector = @selector(class); - Method method = class_getInstanceMethod(class, selector); - IMP newIMP = imp_implementationWithBlock(^(id self) { - return statedClass; - }); - class_replaceMethod(class, selector, newIMP, method_getTypeEncoding(method)); -} - -static void RACSwizzleMethodSignatureForSelector(Class class) { - IMP newIMP = imp_implementationWithBlock(^(id self, SEL selector) { - // Don't send the -class message to the receiver because we've changed - // that to return the original class. - Class actualClass = object_getClass(self); - Method method = class_getInstanceMethod(actualClass, selector); - if (method == NULL) { - // Messages that the original class dynamically implements fall - // here. - // - // Call the original class' -methodSignatureForSelector:. - struct objc_super target = { - .super_class = class_getSuperclass(class), - .receiver = self, - }; - NSMethodSignature * (*messageSend)(struct objc_super *, SEL, SEL) = (__typeof__(messageSend))objc_msgSendSuper; - return messageSend(&target, @selector(methodSignatureForSelector:), selector); - } - - char const *encoding = method_getTypeEncoding(method); - return [NSMethodSignature signatureWithObjCTypes:encoding]; - }); - - SEL selector = @selector(methodSignatureForSelector:); - Method methodSignatureForSelectorMethod = class_getInstanceMethod(class, selector); - class_replaceMethod(class, selector, newIMP, method_getTypeEncoding(methodSignatureForSelectorMethod)); -} - -// It's hard to tell which struct return types use _objc_msgForward, and -// which use _objc_msgForward_stret instead, so just exclude all struct, array, -// union, complex and vector return types. -static void RACCheckTypeEncoding(const char *typeEncoding) { -#if !NS_BLOCK_ASSERTIONS - // Some types, including vector types, are not encoded. In these cases the - // signature starts with the size of the argument frame. - NSCAssert(*typeEncoding < '1' || *typeEncoding > '9', @"unknown method return type not supported in type encoding: %s", typeEncoding); - NSCAssert(strstr(typeEncoding, "(") != typeEncoding, @"union method return type not supported"); - NSCAssert(strstr(typeEncoding, "{") != typeEncoding, @"struct method return type not supported"); - NSCAssert(strstr(typeEncoding, "[") != typeEncoding, @"array method return type not supported"); - NSCAssert(strstr(typeEncoding, @encode(_Complex float)) != typeEncoding, @"complex float method return type not supported"); - NSCAssert(strstr(typeEncoding, @encode(_Complex double)) != typeEncoding, @"complex double method return type not supported"); - NSCAssert(strstr(typeEncoding, @encode(_Complex long double)) != typeEncoding, @"complex long double method return type not supported"); - -#endif // !NS_BLOCK_ASSERTIONS -} - -static const char *RACSignatureForUndefinedSelector(SEL selector) { - const char *name = sel_getName(selector); - NSMutableString *signature = [NSMutableString stringWithString:@"v@:"]; - - while ((name = strchr(name, ':')) != NULL) { - [signature appendString:@"@"]; - name++; - } - - return signature.UTF8String; -} - -static Class RACSwizzleClass(NSObject *self) { - Class statedClass = self.class; - Class baseClass = object_getClass(self); - - // The "known dynamic subclass" is the subclass generated by RAC. - // It's stored as an associated object on every instance that's already - // been swizzled, so that even if something else swizzles the class of - // this instance, we can still access the RAC generated subclass. - Class knownDynamicSubclass = objc_getAssociatedObject(self, RACSubclassAssociationKey); - if (knownDynamicSubclass != Nil) return knownDynamicSubclass; - - NSString *className = NSStringFromClass(baseClass); - - if (statedClass != baseClass) { - // If the class is already lying about what it is, it's probably a KVO - // dynamic subclass or something else that we shouldn't subclass - // ourselves. - // - // Just swizzle -forwardInvocation: in-place. Since the object's class - // was almost certainly dynamically changed, we shouldn't see another of - // these classes in the hierarchy. - // - // Additionally, swizzle -respondsToSelector: because the default - // implementation may be ignorant of methods added to this class. - @synchronized (swizzledClasses()) { - if (![swizzledClasses() containsObject:className]) { - RACSwizzleForwardInvocation(baseClass); - RACSwizzleRespondsToSelector(baseClass); - RACSwizzleGetClass(baseClass, statedClass); - RACSwizzleGetClass(object_getClass(baseClass), statedClass); - RACSwizzleMethodSignatureForSelector(baseClass); - [swizzledClasses() addObject:className]; - } - } - - return baseClass; - } - - const char *subclassName = [className stringByAppendingString:RACSubclassSuffix].UTF8String; - Class subclass = objc_getClass(subclassName); - - if (subclass == nil) { - subclass = objc_allocateClassPair(baseClass, subclassName, 0); - if (subclass == nil) return nil; - - RACSwizzleForwardInvocation(subclass); - RACSwizzleRespondsToSelector(subclass); - - RACSwizzleGetClass(subclass, statedClass); - RACSwizzleGetClass(object_getClass(subclass), statedClass); - - RACSwizzleMethodSignatureForSelector(subclass); - - objc_registerClassPair(subclass); - } - - object_setClass(self, subclass); - objc_setAssociatedObject(self, RACSubclassAssociationKey, subclass, OBJC_ASSOCIATION_ASSIGN); - return subclass; -} - -@implementation NSObject (RACObjCRuntimeUtilities) - --(BOOL) _rac_setupInvocationObservationForSelector:(SEL)selector protocol:(Protocol *)protocol argsReceiver:(void (^)(id))receiverBlock { - return [self _rac_setupInvocationObservationForSelector:selector protocol:protocol isTrigger:false receiver:receiverBlock]; -} - --(BOOL) _rac_setupInvocationObservationForSelector:(SEL)selector protocol:(Protocol *)protocol receiver:(void (^)(void))receiverBlock { - return [self _rac_setupInvocationObservationForSelector:selector protocol:protocol isTrigger:true receiver:receiverBlock]; -} - --(BOOL) _rac_setupInvocationObservationForSelector:(SEL)selector protocol:(Protocol *)protocol isTrigger:(BOOL)isTrigger receiver:(id)receiverBlock { - SEL aliasSelector = RACAliasForSelector(selector); - - RACForwardingInfo* existingReceiver = objc_getAssociatedObject(self, aliasSelector); - if (existingReceiver != nil) return NO; - - Class class = RACSwizzleClass(self); - NSCAssert(class != nil, @"Could not swizzle class of %@", self); - - RACForwardingInfo* receiver = [[RACForwardingInfo alloc] initWithBlock:receiverBlock isTrigger:isTrigger]; - objc_setAssociatedObject(self, aliasSelector, receiver, OBJC_ASSOCIATION_RETAIN); - - Method targetMethod = class_getInstanceMethod(class, selector); - if (targetMethod == NULL) { - const char *typeEncoding; - if (protocol == NULL) { - typeEncoding = RACSignatureForUndefinedSelector(selector); - } else { - // Look for the selector as an optional instance method. - struct objc_method_description methodDescription = protocol_getMethodDescription(protocol, selector, NO, YES); - - if (methodDescription.name == NULL) { - // Then fall back to looking for a required instance - // method. - methodDescription = protocol_getMethodDescription(protocol, selector, YES, YES); - NSCAssert(methodDescription.name != NULL, @"Selector %@ does not exist in <%s>", NSStringFromSelector(selector), protocol_getName(protocol)); - } - - typeEncoding = methodDescription.types; - } - - RACCheckTypeEncoding(typeEncoding); - - // Define the selector to call -forwardInvocation:. - if (!class_addMethod(class, selector, _objc_msgForward, typeEncoding)) { - @throw [[NSException alloc] initWithName:RACSwizzleException reason:nil userInfo:nil]; - } - } else if (method_getImplementation(targetMethod) != _objc_msgForward) { - // Make a method alias for the existing method implementation. - const char *typeEncoding = method_getTypeEncoding(targetMethod); - - RACCheckTypeEncoding(typeEncoding); - - BOOL addedAlias __attribute__((unused)) = class_addMethod(class, aliasSelector, method_getImplementation(targetMethod), typeEncoding); - NSCAssert(addedAlias, @"Original implementation for %@ is already copied to %@ on %@", NSStringFromSelector(selector), NSStringFromSelector(aliasSelector), class); - - // Redefine the selector to call -forwardInvocation:. - class_replaceMethod(class, selector, _objc_msgForward, method_getTypeEncoding(targetMethod)); - } - - return YES; -} -@end - -@implementation RACForwardingInfo - --(instancetype) initWithBlock:(id)block isTrigger:(BOOL)isTrigger { - self = [super init]; - if (self) { - _block = block; - _isTrigger = isTrigger; - } - return self; -} - -@end diff --git a/ReactiveCocoa/module.modulemap b/ReactiveCocoa/module.modulemap index ca356bb8e5..77dc79fb5d 100644 --- a/ReactiveCocoa/module.modulemap +++ b/ReactiveCocoa/module.modulemap @@ -1,6 +1,6 @@ framework module ReactiveCocoa { umbrella header "ReactiveCocoa.h" - private header "RACObjCRuntimeUtilities.h" + private header "ObjCRuntimeAliases.h" export * module * { export * } diff --git a/ReactiveCocoaTests/InterceptingSpec.swift b/ReactiveCocoaTests/InterceptingSpec.swift index 8c1dadbf5c..1f56a50002 100644 --- a/ReactiveCocoaTests/InterceptingSpec.swift +++ b/ReactiveCocoaTests/InterceptingSpec.swift @@ -146,30 +146,30 @@ class InterceptingSpec: QuickSpec { expect(firstValue) == true expect(secondValue) == "Winner" } - /** + it("should send arguments for invocation and invoke the a KVO-swizzled then RAC-swizzled setter") { - var latestValue: Bool? + var latestValue: Bool? - object.reactive - .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) - .startWithValues { objectValue in - latestValue = objectValue as! Bool? - } + object.reactive + .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) + .startWithValues { objectValue in + latestValue = objectValue as! Bool? + } - expect(latestValue).to(beNil()) + expect(latestValue).to(beNil()) - var value: Bool? - object.reactive.signal(for: #selector(setter: object.objectValue)).observeValues { x in - value = x[0] as! Bool? - } + var value: Bool? + object.reactive.signal(for: #selector(setter: object.objectValue)).observeValues { x in + value = x[0] as! Bool? + } - object.objectValue = true + object.objectValue = true - expect(object.objectValue as! Bool?) == true - expect(latestValue) == true - expect(value) == true + expect(object.objectValue as! Bool?) == true + expect(latestValue) == true + expect(value) == true } - **/ + it("should send arguments for invocation and invoke the a RAC-swizzled then KVO-swizzled setter") { let object = InterceptedObject() @@ -228,67 +228,6 @@ class InterceptingSpec: QuickSpec { expect(secondValue) == "Winner" } - it("should properly implement -respondsToSelector: when called on KVO'd receiver") { - let object = InterceptedObject() - - // First, setup KVO on `object`, which gives us the desired side-effect - // of `object` taking on a KVO-custom subclass. - object.reactive - .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) - .start() - - let selector = NSSelectorFromString("anyOldSelector:") - - // With the KVO subclass in place, call -rac_signalForSelector: to - // implement -anyOldSelector: directly on the KVO subclass. - _ = object.reactive.trigger(for: selector) - - expect(object.responds(to: selector)) == true - } - - it("should properly implement -respondsToSelector: when called on signalForSelector'd receiver that has subsequently been KVO'd") { - let object = InterceptedObject() - - let selector = NSSelectorFromString("anyOldSelector:") - - // Implement -anyOldSelector: on the object first - _ = object.reactive.trigger(for: selector) - - expect(object.responds(to: selector)) == true - - // Then KVO the object - object.reactive - .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) - .start() - - expect(object.responds(to: selector)) == true - } - - it("should properly implement -respondsToSelector: when called on signalForSelector'd receiver that has subsequently been KVO'd, then signalForSelector'd again") { - let object = InterceptedObject() - - let selector = NSSelectorFromString("anyOldSelector:") - - // Implement -anyOldSelector: on the object first - _ = object.reactive.trigger(for: selector) - - expect(object.responds(to: selector)) == true - - // Then KVO the object - object.reactive - .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) - .start() - - expect(object.responds(to: selector)) == true - - let selector2 = NSSelectorFromString("anotherSelector:") - - // Then implement -anotherSelector: on the object - _ = object.reactive.trigger(for: selector2) - - expect(object.responds(to: selector2)) == true - } - it("should call the right signal for two instances of the same class, adding signals for the same selector") { let object1 = InterceptedObject() let object2 = InterceptedObject() @@ -326,7 +265,7 @@ class InterceptingSpec: QuickSpec { expect(invokedMethodBefore) == true } } -/** + describe("interoperability") { var invoked: Bool! var object: InterceptedObject! @@ -349,20 +288,32 @@ class InterceptingSpec: QuickSpec { // Redirect `swizzledSelector` to the forwarding machinery. let method = originalClass.method(for: swizzledSelector)! - let original = method.replaceImplementation(.forwarding) - defer { _ = original.map(method.replaceImplementation) } + let original = originalClass.replaceMethod(with: .forwarding, + for: swizzledSelector, + types: method.typeEncoding) + defer { + _ = originalClass.replaceMethod(with: original, + for: swizzledSelector, + types: method.typeEncoding) + } // Swizzle `forwardInvocation:` to intercept `swizzledSelector`. - let forwardInvocationBlock: @convention(block) (AnyObject?, AnyObject?) -> Void = { _, invocation in - if ((invocation as! NSInvocationProtocol).selector == swizzledSelector) { + let forwardInvocationBlock: @convention(block) (AnyObject, AnyObject) -> Void = { _, invocation in + if (invocation.selector == swizzledSelector) { expect(invoked) == false invoked = true } } let method2 = originalClass.method(for: ObjCSelector.forwardInvocation)! - let original2 = method2.replaceImplementation(CFunction(block: forwardInvocationBlock)) - defer { _ = original2.map(method2.replaceImplementation) } + let original2 = originalClass.replaceMethod(with: CFunction(block: forwardInvocationBlock), + for: ObjCSelector.forwardInvocation, + types: method2.typeEncoding) + defer { + _ = originalClass.replaceMethod(with: original2, + for: ObjCSelector.forwardInvocation, + types: method2.typeEncoding) + } object.lifeIsGood(nil) expect(invoked) == true @@ -375,20 +326,32 @@ class InterceptingSpec: QuickSpec { // Redirect `swizzledSelector` to the forwarding machinery. let method = originalClass.method(for: swizzledSelector)! - let original = method.replaceImplementation(.forwarding) - defer { _ = original.map(method.replaceImplementation) } + let original = originalClass.replaceMethod(with: .forwarding, + for: swizzledSelector, + types: method.typeEncoding) + defer { + _ = originalClass.replaceMethod(with: original, + for: swizzledSelector, + types: method.typeEncoding) + } // Swizzle `forwardInvocation:` to intercept `swizzledSelector`. - let forwardInvocationBlock: @convention(block) (AnyObject?, AnyObject?) -> Void = { _, invocation in - if ((invocation as! NSInvocationProtocol).selector == swizzledSelector) { + let forwardInvocationBlock: @convention(block) (AnyObject, AnyObject) -> Void = { _, invocation in + if (invocation.selector == swizzledSelector) { expect(invoked) == false invoked = true } } let method2 = originalClass.method(for: ObjCSelector.forwardInvocation)! - let original2 = method2.replaceImplementation(CFunction(block: forwardInvocationBlock)) - defer { _ = original2.map(method2.replaceImplementation) } + let original2 = originalClass.replaceMethod(with: CFunction(block: forwardInvocationBlock), + for: ObjCSelector.forwardInvocation, + types: method2.typeEncoding) + defer { + _ = originalClass.replaceMethod(with: original2, + for: ObjCSelector.forwardInvocation, + types: method2.typeEncoding) + } object.lifeIsGood(nil) expect(invoked) == true @@ -399,20 +362,26 @@ class InterceptingSpec: QuickSpec { let swizzledSelector = #selector(object.lifeIsGood) - let lifeIsGoodBlock: @convention(block) (AnyObject?, AnyObject?) -> Void = { _ in + let lifeIsGoodBlock: @convention(block) (AnyObject, AnyObject) -> Void = { _ in expect(invoked) == false invoked = true } let method = originalClass.method(for: swizzledSelector)! - let original = method.replaceImplementation(CFunction(block: lifeIsGoodBlock)) - defer { _ = original.map(method.replaceImplementation) } - + let original = originalClass.replaceMethod(with: CFunction(block: lifeIsGoodBlock), + for: swizzledSelector, + types: method.typeEncoding) + defer { + _ = originalClass.replaceMethod(with: original, + for: swizzledSelector, + types: method.typeEncoding) + } + object.lifeIsGood(nil) expect(invoked) == true } } -**/ + it("should swizzle an NSObject method") { let object = NSObject() @@ -794,14 +763,14 @@ private class ForwardInvocationTestObject: InterceptedObject { object.forwardedSelector = invocation.selector } - ObjCClass(ForwardInvocationTestObject.self) + try! ObjCClass(ForwardInvocationTestObject.self) .addMethod(with: CFunction(assuming: impl), for: ObjCSelector.forwardInvocation, types: ObjCMethodEncoding.forwardInvocation) assert(ForwardInvocationTestObject.instancesRespond(to: ObjCSelector.forwardInvocation)) - ObjCClass(ForwardInvocationTestObject.self) + try! ObjCClass(ForwardInvocationTestObject.self) .addMethod(with: .forwarding, for: ForwardInvocationTestObject.forwardedSelector, types: ObjCMethodEncoding.forwardInvocation) diff --git a/ReactiveCocoaTests/SwizzlingSpec.swift b/ReactiveCocoaTests/SwizzlingSpec.swift index f0251afbc4..87b3f4aacf 100644 --- a/ReactiveCocoaTests/SwizzlingSpec.swift +++ b/ReactiveCocoaTests/SwizzlingSpec.swift @@ -11,7 +11,7 @@ class SwizzlingSpec: QuickSpec { let object = SwizzledObject() expect(type(of: object)).to(beIdenticalTo(SwizzledObject.self)) - let subclass = Swizzler.swizzleClass(of: object) + let subclass = swizzleClass(object) expect(type(of: object)).to(beIdenticalTo(subclass.reference)) let objcClass = (object as AnyObject).objcClass @@ -23,18 +23,18 @@ class SwizzlingSpec: QuickSpec { it("should reuse the runtime subclass across instances") { let object = SwizzledObject() - let subclass = Swizzler.swizzleClass(of: object) + let subclass = swizzleClass(object) let object2 = SwizzledObject() - let subclass2 = Swizzler.swizzleClass(of: object2) + let subclass2 = swizzleClass(object2) expect(subclass) == subclass2 } it("should return the known runtime subclass") { let object = SwizzledObject() - let subclass = Swizzler.swizzleClass(of: object) - let subclass2 = Swizzler.swizzleClass(of: object) + let subclass = swizzleClass(object) + let subclass2 = swizzleClass(object) expect(subclass) == subclass2 } From 02aafb9d1304dc3c1eeddc24d09ed62795c0cdb3 Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 18 Nov 2016 01:52:41 +0100 Subject: [PATCH 0585/1028] Documentation. Added back a missing `fileprivate` modifier. --- ReactiveCocoa/NSObject+ObjCRuntime.swift | 8 ++ ReactiveCocoa/ObjC+Constants.swift | 2 + ReactiveCocoa/ObjC+Messages.swift | 9 ++ ReactiveCocoa/ObjC+Runtime.swift | 91 +++++++++++++++++++-- ReactiveCocoa/ObjC+RuntimeSubclassing.swift | 10 +++ 5 files changed, 115 insertions(+), 5 deletions(-) diff --git a/ReactiveCocoa/NSObject+ObjCRuntime.swift b/ReactiveCocoa/NSObject+ObjCRuntime.swift index 126c2c6f5d..5a2d21654f 100644 --- a/ReactiveCocoa/NSObject+ObjCRuntime.swift +++ b/ReactiveCocoa/NSObject+ObjCRuntime.swift @@ -1,8 +1,16 @@ extension NSObject { + /// The class of the instance reported by the ObjC `-class:` message. + /// + /// - note: `type(of:)` might return the runtime subclass, while this property + /// always returns the original class. @nonobjc internal var objcClass: AnyClass { return (self as AnyObject).objcClass } + /// Set the class of `self`. + /// + /// - parameters: + /// - class: The new class of `self`. @nonobjc internal func setClass(_ class: ObjCClass) { object_setClass(self, `class`.reference) } diff --git a/ReactiveCocoa/ObjC+Constants.swift b/ReactiveCocoa/ObjC+Constants.swift index e317acf3f4..3a183ab3fe 100644 --- a/ReactiveCocoa/ObjC+Constants.swift +++ b/ReactiveCocoa/ObjC+Constants.swift @@ -1,9 +1,11 @@ +// Unavailable selectors in Swift. internal enum ObjCSelector { static let forwardInvocation = Selector((("forwardInvocation:"))) static let methodSignatureForSelector = Selector((("methodSignatureForSelector:"))) static let getClass = Selector((("class"))) } +// Method encoding of the unavailable selectors. internal enum ObjCMethodEncoding { static let forwardInvocation = extract("v@:@") static let methodSignatureForSelector = extract("v@::") diff --git a/ReactiveCocoa/ObjC+Messages.swift b/ReactiveCocoa/ObjC+Messages.swift index 3fd5d62291..d6fa7e85aa 100644 --- a/ReactiveCocoa/ObjC+Messages.swift +++ b/ReactiveCocoa/ObjC+Messages.swift @@ -1,8 +1,16 @@ +// Unavailable classes like `NSInvocation` can still be passed into Swift as +// `AnyObject`, and receive messages via `AnyObject`'s message dispatching. +// +// These `@objc` protocols host the method signatures so that they can be used +// with `AnyObject`. + +// `-class` and `+class`. @objc internal protocol ObjCClassReporting { @objc(class) var objcClass: AnyClass! { get } } +// Methods of `NSInvocation`. @objc internal protocol ObjCInvocation { @objc(setSelector:) func setSelector(_ selector: Selector) @@ -16,6 +24,7 @@ func invoke() } +// Methods of `NSMethodSignature`. @objc internal protocol ObjCMethodSignature { var numberOfArguments: UInt { get } diff --git a/ReactiveCocoa/ObjC+Runtime.swift b/ReactiveCocoa/ObjC+Runtime.swift index c01dcfd905..cb6487a35d 100644 --- a/ReactiveCocoa/ObjC+Runtime.swift +++ b/ReactiveCocoa/ObjC+Runtime.swift @@ -1,18 +1,24 @@ +/// Represents a class in the Objective-C runtime. internal struct ObjCClass { enum Error: Swift.Error { + /// The selector has already been implemented in the class. case methodAlreadyExist } + /// The corresponding class object. let reference: AnyClass + /// The name of the class. var name: String { return String(cString: class_getName(reference)) } + /// The meta class of the class. var metaclass: ObjCClass { return ObjCClass(object_getClass(reference)!) } + /// The superclass of the class. var superclass: ObjCClass? { if let superclass = class_getSuperclass(reference) { return ObjCClass(superclass) @@ -21,10 +27,21 @@ internal struct ObjCClass { return nil } + /// Initialize from a class object. + /// + /// - parameters: + /// - reference: The class object. init(_ reference: AnyClass) { self.reference = reference } + /// Initialize from a class name. + /// + /// - parameters: + /// - name: The class name. + /// + /// - returns: + /// The found class, or `nil` if it does not match any. init?(name: String) { if let classRef = name.withCString(objc_getClass) { self.reference = classRef as! AnyClass @@ -33,17 +50,47 @@ internal struct ObjCClass { } } + /// Add a method to the class. + /// + /// - parameters: + /// - implementation: The implementation of the method. + /// - selector: The selector of the method. + /// - types: The type encoding string of the method. + /// + /// - throws: `Error.methodAlreadyExists` if the selector has already been + /// implemented. func addMethod(with implementation: CFunction, for selector: Selector, types: UnsafePointer) throws { if !class_addMethod(reference, selector, implementation.reference, types) { throw Error.methodAlreadyExist } } + /// Replace a method of the class. + /// + /// - parameters: + /// - implementation: The implementation of the method, or `nil` to remove + /// any implementation from the method. + /// - selector: The selector of the method. + /// - types: The type encoding string of the method. + /// + /// - returns: + /// The implementation being swapped out, or `nil` if the selector has not + /// ever been implemented before. func replaceMethod(with implementation: CFunction?, for selector: Selector, types: UnsafePointer) -> CFunction? { let imp = class_replaceMethod(reference, selector, implementation?.reference, types) return imp.map(CFunction.init) } + /// Search for a method that matches the supplied selector. + /// + /// - parameters: + /// - selector: The selector of the method. + /// - searchesAncestors: Indicates whether the search propagates to the + /// superclass, or should be limited to the class + /// itself. + /// + /// - returns: + /// The matching method, or `nil` if none is found. func method(for selector: Selector, searchesAncestors: Bool = true) -> ObjCMethod? { if searchesAncestors { if let method = class_getInstanceMethod(reference, selector) { @@ -78,10 +125,31 @@ extension ObjCClass: Hashable { } extension ObjCClass { + /// Get the class of `object`. + /// + /// - note: This method makes queries through the Objective-C runtime, and may + /// have different results from `[NSObject class]` in isa-swizzled + /// instances. + /// + /// - parameters: + /// - object: The object to query. + /// + /// - returns: + /// The runtime class of `object`. static func type(of object: NSObject) -> ObjCClass { return ObjCClass(object_getClass(object)) } + /// Create a runtime subclass. + /// + /// - parameters: + /// - name: The name of the subclass. + /// - superclass: The superclass of the subclass. + /// - setup: The action to be called before the subclass is registered to + /// the Objective-C runtime. + /// + /// - returns: + /// The created subclass. static func allocate(name: String, superclass: ObjCClass, setup: (ObjCClass) throws -> Void) rethrows -> ObjCClass { let subclass = name.withCString { name in return ObjCClass(objc_allocateClassPair(superclass.reference, name, 0)!) @@ -94,42 +162,55 @@ extension ObjCClass { } } +/// Represents a method of a class in the Objective-C runtime. internal struct ObjCMethod { fileprivate let reference: Method + /// The implementation of the method. var function: CFunction { return CFunction(method_getImplementation(reference)) } + /// The method type encoding string of the method. var typeEncoding: UnsafePointer { return method_getTypeEncoding(reference) } - init(_ reference: Method) { + fileprivate init(_ reference: Method) { self.reference = reference } } +/// Represents a function pointer that can be used as method implementations. internal struct CFunction { + /// The Objective-C message forwarder. static let forwarding = CFunction(_rac_objc_msgForward) fileprivate let reference: IMP + /// Indicates if `self` is the Objective-C message forwarder. var isForwarder: Bool { return reference == CFunction.forwarding.reference } + /// Create a pointer by casting `block` as a C function pointer. + /// + /// - warning: `block` must be a `@convention(c)` closure. init(assuming block: U) { self.reference = unsafeBitCast(block, to: IMP.self) } - fileprivate init(_ reference: IMP) { - self.reference = reference - } - + /// Create a pointer by casting `block` as a C function pointer. + /// + /// - warning: `block` must have its first argument being `Any` for the + /// receiver. init(block: Any) { self.reference = imp_implementationWithBlock(block) } + + fileprivate init(_ reference: IMP) { + self.reference = reference + } } extension CFunction: Equatable { diff --git a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift index cf53067150..692717ad2e 100644 --- a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift +++ b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift @@ -6,6 +6,16 @@ private func subclassName(of class: ObjCClass) -> String { return `class`.name.appending("_RACSwift") } +/// ISA-swizzle the class of the supplied instance. +/// +/// - note: If the instance has already been isa-swizzled, the swizzling happens +/// in place in the runtime subclass created by external parties. +/// +/// - parameters: +/// - instance: The instance to be swizzled. +/// +/// - returns: +/// The runtime subclass of the perceived class of the instance. internal func swizzleClass(_ instance: NSObject) -> ObjCClass { let key = (#function as StaticString).utf8Start From dcb35461a01b7110e248c61f9c102c1364ddba64 Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 18 Nov 2016 02:34:32 +0100 Subject: [PATCH 0586/1028] Removed the ObjC runtime wrappers. --- ReactiveCocoa/NSObject+Intercepting.swift | 77 ++++--- ReactiveCocoa/NSObject+ObjCRuntime.swift | 8 - ReactiveCocoa/ObjC+Runtime.swift | 223 +------------------- ReactiveCocoa/ObjC+RuntimeSubclassing.swift | 53 ++--- ReactiveCocoaTests/InterceptingSpec.swift | 114 ++++++---- ReactiveCocoaTests/SwizzlingSpec.swift | 18 +- 6 files changed, 152 insertions(+), 341 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 6b5bd229c1..f223cdfccb 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -2,7 +2,7 @@ import Foundation import ReactiveSwift import enum Result.NoError -private let swizzledClasses = Atomic>([]) +private let swizzledClasses = Atomic>([]) extension Reactive where Base: NSObject { /// Create a signal which sends a `next` event at the end of every invocation @@ -47,35 +47,36 @@ private func setupInterception(_ object: NSObject, for selector: Selector) -> Si disposable += object.reactive.lifetime.ended .observeCompleted(observer.sendCompleted) - let subclass = swizzleClass(object) + let subclass: AnyClass = swizzleClass(object) swizzledClasses.modify { classes in - if !classes.contains(subclass) { - classes.insert(subclass) + if !classes.contains(ObjectIdentifier(subclass)) { + classes.insert(ObjectIdentifier(subclass)) enableMessageForwarding(subclass) } } - guard let method = subclass.method(for: selector) else { - fatalError("Selector `\(selector)` does not exist in class `\(subclass.superclass!.name)`.") + guard let method = class_getInstanceMethod(subclass, selector) else { + fatalError("Selector `\(selector)` does not exist in class `\(String(cString: class_getName(class_getSuperclass(subclass))))`.") } - if !method.function.isForwarder { - assert(checkTypeEncoding(method.typeEncoding)) + let impl = method_getImplementation(method) - if let existingMethod = subclass.method(for: selector, searchesAncestors: false) { + if impl != _rac_objc_msgForward { + let typeEncoding = method_getTypeEncoding(method)! + assert(checkTypeEncoding(typeEncoding)) + + if let existingMethod = class_getImmediateMethod(subclass, selector) { // Make a method alias for the existing method implementation, if it is // defined in the runtime subclass. let interopAlias = selector.interopAlias - try! subclass.addMethod(with: existingMethod.function, - for: interopAlias, - types: existingMethod.typeEncoding) + let existingImpl = method_getImplementation(existingMethod) + let success = class_addMethod(subclass, interopAlias, existingImpl, typeEncoding) + precondition(success, "Unexpected state: Cannot overwrite a preserved implementation of \(selector) from the runtime subclass of `\(String(cString: class_getName(class_getSuperclass(subclass))))`.") } // Redefine the selector to call -forwardInvocation:. - _ = subclass.replaceMethod(with: .forwarding, - for: selector, - types: method.typeEncoding) + _ = class_replaceMethod(subclass, selector, _rac_objc_msgForward, typeEncoding) } object.setValue(observer.send(value:), forAssociatedKey: selector.utf8Start) @@ -100,7 +101,7 @@ extension Selector { } } -private func enableMessageForwarding(_ objcClass: ObjCClass) { +private func enableMessageForwarding(_ objcClass: AnyClass) { // Set up a new version of -forwardInvocation:. // // If the selector has been passed to -rac_signalForSelector:, invoke @@ -118,8 +119,8 @@ private func enableMessageForwarding(_ objcClass: ObjCClass) { let invokingAlias = selector.invokingAlias let interopAlias = selector.interopAlias - let realClass = ObjCClass.type(of: object) - let perceivedClass = ObjCClass(object.objcClass) + let realClass: AnyClass = object_getClass(object) + let perceivedClass: AnyClass = object.objcClass defer { if let observer = object.value(forAssociatedKey: selector.utf8Start) as! ((AnyObject) -> Void)? { @@ -132,39 +133,34 @@ private func enableMessageForwarding(_ objcClass: ObjCClass) { // all instances that had been applied `signalForSelector:` are // non-threadsafe as any mutable instances. - if realClass.reference.instancesRespond(to: interopAlias) { + if realClass.instancesRespond(to: interopAlias) { // `self` uses a runtime subclass generated by third-party APIs, and RAC // found an existing implementation for the selector at the setup time. // Call that implementation if it is not the ObjC message forwarder. - let interopImpl = realClass.method(for: interopAlias)!.function + let interopMethod = class_getInstanceMethod(realClass, interopAlias) + let interopImpl = method_getImplementation(interopMethod) + + if interopImpl != _rac_objc_msgForward { + let typeEncoding = method_getTypeEncoding(interopMethod) - if !interopImpl.isForwarder { - let method = realClass.method(for: selector)! - let previousImpl = realClass.replaceMethod(with: interopImpl, - for: selector, - types: method.typeEncoding) + let previousImpl = class_replaceMethod(realClass, selector, interopImpl, typeEncoding) invocation.invoke() - _ = realClass.replaceMethod(with: previousImpl, - for: selector, - types: method.typeEncoding) + _ = class_replaceMethod(realClass, selector, previousImpl, typeEncoding) return } } - if perceivedClass.reference.instancesRespond(to: selector) { + if perceivedClass.instancesRespond(to: selector) { // The stated class has an implementation of the selector. Call that // implementation if it is not the ObjC message forwarder. - let method = perceivedClass.method(for: selector)! - let originalImpl = method.function - - if !originalImpl.isForwarder { - _ = realClass.replaceMethod(with: originalImpl, - for: invokingAlias, - types: method.typeEncoding) + let method = class_getInstanceMethod(perceivedClass, selector) + let impl = method_getImplementation(method) + if impl != _rac_objc_msgForward { + _ = class_replaceMethod(realClass, invokingAlias, impl, method_getTypeEncoding(method)) invocation.setSelector(invokingAlias) invocation.invoke() @@ -176,16 +172,17 @@ private func enableMessageForwarding(_ objcClass: ObjCClass) { // inheritance hierarchy. var target = objc_super() target.receiver = Unmanaged.passUnretained(object) - target.super_class = perceivedClass.reference + target.super_class = perceivedClass typealias SuperForwardInvocation = @convention(c) (UnsafeMutablePointer, Selector, AnyObject) -> Void let send = unsafeBitCast(_rac_objc_msgSendSuper, to: SuperForwardInvocation.self) send(&target, ObjCSelector.forwardInvocation, invocation) } - _ = objcClass.replaceMethod(with: CFunction(assuming: newForwardInvocation), - for: ObjCSelector.forwardInvocation, - types: ObjCMethodEncoding.forwardInvocation) + _ = class_replaceMethod(objcClass, + ObjCSelector.forwardInvocation, + unsafeBitCast(newForwardInvocation, to: IMP.self), + ObjCMethodEncoding.forwardInvocation) } func checkTypeEncoding(_ types: UnsafePointer) -> Bool { diff --git a/ReactiveCocoa/NSObject+ObjCRuntime.swift b/ReactiveCocoa/NSObject+ObjCRuntime.swift index 5a2d21654f..bd9b98150e 100644 --- a/ReactiveCocoa/NSObject+ObjCRuntime.swift +++ b/ReactiveCocoa/NSObject+ObjCRuntime.swift @@ -6,12 +6,4 @@ extension NSObject { @nonobjc internal var objcClass: AnyClass { return (self as AnyObject).objcClass } - - /// Set the class of `self`. - /// - /// - parameters: - /// - class: The new class of `self`. - @nonobjc internal func setClass(_ class: ObjCClass) { - object_setClass(self, `class`.reference) - } } diff --git a/ReactiveCocoa/ObjC+Runtime.swift b/ReactiveCocoa/ObjC+Runtime.swift index cb6487a35d..5a7f004f10 100644 --- a/ReactiveCocoa/ObjC+Runtime.swift +++ b/ReactiveCocoa/ObjC+Runtime.swift @@ -1,220 +1,15 @@ -/// Represents a class in the Objective-C runtime. -internal struct ObjCClass { - enum Error: Swift.Error { - /// The selector has already been implemented in the class. - case methodAlreadyExist - } - - /// The corresponding class object. - let reference: AnyClass - - /// The name of the class. - var name: String { - return String(cString: class_getName(reference)) - } - - /// The meta class of the class. - var metaclass: ObjCClass { - return ObjCClass(object_getClass(reference)!) - } - - /// The superclass of the class. - var superclass: ObjCClass? { - if let superclass = class_getSuperclass(reference) { - return ObjCClass(superclass) - } - - return nil - } - - /// Initialize from a class object. - /// - /// - parameters: - /// - reference: The class object. - init(_ reference: AnyClass) { - self.reference = reference - } - - /// Initialize from a class name. - /// - /// - parameters: - /// - name: The class name. - /// - /// - returns: - /// The found class, or `nil` if it does not match any. - init?(name: String) { - if let classRef = name.withCString(objc_getClass) { - self.reference = classRef as! AnyClass - } else { - return nil - } - } - - /// Add a method to the class. - /// - /// - parameters: - /// - implementation: The implementation of the method. - /// - selector: The selector of the method. - /// - types: The type encoding string of the method. - /// - /// - throws: `Error.methodAlreadyExists` if the selector has already been - /// implemented. - func addMethod(with implementation: CFunction, for selector: Selector, types: UnsafePointer) throws { - if !class_addMethod(reference, selector, implementation.reference, types) { - throw Error.methodAlreadyExist - } - } - - /// Replace a method of the class. - /// - /// - parameters: - /// - implementation: The implementation of the method, or `nil` to remove - /// any implementation from the method. - /// - selector: The selector of the method. - /// - types: The type encoding string of the method. - /// - /// - returns: - /// The implementation being swapped out, or `nil` if the selector has not - /// ever been implemented before. - func replaceMethod(with implementation: CFunction?, for selector: Selector, types: UnsafePointer) -> CFunction? { - let imp = class_replaceMethod(reference, selector, implementation?.reference, types) - return imp.map(CFunction.init) - } - - /// Search for a method that matches the supplied selector. - /// - /// - parameters: - /// - selector: The selector of the method. - /// - searchesAncestors: Indicates whether the search propagates to the - /// superclass, or should be limited to the class - /// itself. - /// - /// - returns: - /// The matching method, or `nil` if none is found. - func method(for selector: Selector, searchesAncestors: Bool = true) -> ObjCMethod? { - if searchesAncestors { - if let method = class_getInstanceMethod(reference, selector) { - return ObjCMethod(method) - } - } else { - var count: UInt32 = 0 - let buffer = class_copyMethodList(reference, &count) - let methods = UnsafeBufferPointer(start: buffer, count: Int(count)) - - defer { free(buffer) } - - for method in methods { - if method_getName(method!) == selector { - return ObjCMethod(method!) - } - } - } - - return nil - } -} - -extension ObjCClass: Hashable { - static func ==(left: ObjCClass, right: ObjCClass) -> Bool { - return left.reference === right.reference - } +func class_getImmediateMethod(_ `class`: AnyClass, _ selector: Selector) -> Method? { + var count: UInt32 = 0 + let buffer = class_copyMethodList(`class`, &count) + let methods = UnsafeBufferPointer(start: buffer, count: Int(count)) - var hashValue: Int { - return ObjectIdentifier(reference).hashValue - } -} + defer { free(buffer) } -extension ObjCClass { - /// Get the class of `object`. - /// - /// - note: This method makes queries through the Objective-C runtime, and may - /// have different results from `[NSObject class]` in isa-swizzled - /// instances. - /// - /// - parameters: - /// - object: The object to query. - /// - /// - returns: - /// The runtime class of `object`. - static func type(of object: NSObject) -> ObjCClass { - return ObjCClass(object_getClass(object)) - } - - /// Create a runtime subclass. - /// - /// - parameters: - /// - name: The name of the subclass. - /// - superclass: The superclass of the subclass. - /// - setup: The action to be called before the subclass is registered to - /// the Objective-C runtime. - /// - /// - returns: - /// The created subclass. - static func allocate(name: String, superclass: ObjCClass, setup: (ObjCClass) throws -> Void) rethrows -> ObjCClass { - let subclass = name.withCString { name in - return ObjCClass(objc_allocateClassPair(superclass.reference, name, 0)!) + for method in methods { + if method_getName(method!) == selector { + return method! } - - try setup(subclass) - objc_registerClassPair(subclass.reference) - - return subclass - } -} - -/// Represents a method of a class in the Objective-C runtime. -internal struct ObjCMethod { - fileprivate let reference: Method - - /// The implementation of the method. - var function: CFunction { - return CFunction(method_getImplementation(reference)) - } - - /// The method type encoding string of the method. - var typeEncoding: UnsafePointer { - return method_getTypeEncoding(reference) - } - - fileprivate init(_ reference: Method) { - self.reference = reference - } -} - -/// Represents a function pointer that can be used as method implementations. -internal struct CFunction { - /// The Objective-C message forwarder. - static let forwarding = CFunction(_rac_objc_msgForward) - - fileprivate let reference: IMP - - /// Indicates if `self` is the Objective-C message forwarder. - var isForwarder: Bool { - return reference == CFunction.forwarding.reference - } - - /// Create a pointer by casting `block` as a C function pointer. - /// - /// - warning: `block` must be a `@convention(c)` closure. - init(assuming block: U) { - self.reference = unsafeBitCast(block, to: IMP.self) - } - - /// Create a pointer by casting `block` as a C function pointer. - /// - /// - warning: `block` must have its first argument being `Any` for the - /// receiver. - init(block: Any) { - self.reference = imp_implementationWithBlock(block) } - fileprivate init(_ reference: IMP) { - self.reference = reference - } -} - -extension CFunction: Equatable { - static func ==(left: CFunction, right: CFunction) -> Bool { - return left.reference == right.reference - } + return nil } diff --git a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift index 692717ad2e..0b9e5b686e 100644 --- a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift +++ b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift @@ -1,9 +1,9 @@ import ReactiveSwift -private let swizzledExternalClasses = Atomic>([]) +private let swizzledExternalClasses = Atomic>([]) -private func subclassName(of class: ObjCClass) -> String { - return `class`.name.appending("_RACSwift") +private func subclassName(of class: AnyClass) -> String { + return String(cString: class_getName(`class`)).appending("_RACSwift") } /// ISA-swizzle the class of the supplied instance. @@ -16,15 +16,15 @@ private func subclassName(of class: ObjCClass) -> String { /// /// - returns: /// The runtime subclass of the perceived class of the instance. -internal func swizzleClass(_ instance: NSObject) -> ObjCClass { +internal func swizzleClass(_ instance: NSObject) -> AnyClass { let key = (#function as StaticString).utf8Start - if let knownSubclass = instance.value(forAssociatedKey: key) as! ObjCClass? { + if let knownSubclass = instance.value(forAssociatedKey: key) as! AnyClass? { return knownSubclass } - let perceivedClass = ObjCClass(instance.objcClass) - let realClass = ObjCClass.type(of: instance) + let perceivedClass: AnyClass = instance.objcClass + let realClass: AnyClass = object_getClass(instance)! if perceivedClass != realClass { // If the class is already lying about what it is, it's probably a KVO @@ -33,8 +33,8 @@ internal func swizzleClass(_ instance: NSObject) -> ObjCClass { // // Use this runtime subclass directly. swizzledExternalClasses.modify { classes in - if !classes.contains(realClass) { - classes.insert(realClass) + if !classes.contains(ObjectIdentifier(realClass)) { + classes.insert(ObjectIdentifier(realClass)) replaceGetClass(in: realClass, decoy: perceivedClass) } } @@ -42,34 +42,39 @@ internal func swizzleClass(_ instance: NSObject) -> ObjCClass { return realClass } else { let name = subclassName(of: perceivedClass) - let subclass: ObjCClass - - if let existingClass = ObjCClass(name: name) { - subclass = existingClass - } else { - subclass = ObjCClass.allocate(name: name, superclass: perceivedClass) { subclass in + let subclass: AnyClass = name.withCString { name in + if let existingClass = objc_getClass(name) as! AnyClass? { + return existingClass + } else { + let subclass: AnyClass = objc_allocateClassPair(perceivedClass, name, 0)! replaceGetClass(in: subclass, decoy: perceivedClass) + objc_registerClassPair(subclass) + return subclass } } - instance.setClass(subclass) + object_setClass(instance, subclass) instance.setValue(subclass, forAssociatedKey: key) return subclass } } -private func replaceGetClass(in class: ObjCClass, decoy perceivedClass: ObjCClass) { +private func replaceGetClass(in class: AnyClass, decoy perceivedClass: AnyClass) { let getClass: @convention(block) (Any) -> AnyClass = { _ in - return perceivedClass.reference + return perceivedClass } + let impl = imp_implementationWithBlock(getClass as Any) + // Swizzle `-class`. - _ = `class`.replaceMethod(with: CFunction(block: getClass), - for: ObjCSelector.getClass, - types: ObjCMethodEncoding.getClass) + _ = class_replaceMethod(`class`, + ObjCSelector.getClass, + impl, + ObjCMethodEncoding.getClass) // Swizzle `+class`. - _ = `class`.metaclass.replaceMethod(with: CFunction(block: getClass), - for: ObjCSelector.getClass, - types: ObjCMethodEncoding.getClass) + _ = class_replaceMethod(object_getClass(`class`), + ObjCSelector.getClass, + impl, + ObjCMethodEncoding.getClass) } diff --git a/ReactiveCocoaTests/InterceptingSpec.swift b/ReactiveCocoaTests/InterceptingSpec.swift index 1f56a50002..c9eb5a50e0 100644 --- a/ReactiveCocoaTests/InterceptingSpec.swift +++ b/ReactiveCocoaTests/InterceptingSpec.swift @@ -269,12 +269,12 @@ class InterceptingSpec: QuickSpec { describe("interoperability") { var invoked: Bool! var object: InterceptedObject! - var originalClass: ObjCClass! + var originalClass: AnyClass! beforeEach { invoked = false object = InterceptedObject() - originalClass = ObjCClass(InterceptedObject.self) + originalClass = InterceptedObject.self } it("should invoke the swizzled `forwardInvocation:` on an instance isa-swizzled by both RAC and KVO.") { @@ -287,14 +287,18 @@ class InterceptingSpec: QuickSpec { let swizzledSelector = #selector(object.lifeIsGood) // Redirect `swizzledSelector` to the forwarding machinery. - let method = originalClass.method(for: swizzledSelector)! - let original = originalClass.replaceMethod(with: .forwarding, - for: swizzledSelector, - types: method.typeEncoding) + let method = class_getInstanceMethod(originalClass, swizzledSelector)! + let typeEncoding = method_getTypeEncoding(method) + + let original = class_replaceMethod(originalClass, + swizzledSelector, + _rac_objc_msgForward, + typeEncoding) defer { - _ = originalClass.replaceMethod(with: original, - for: swizzledSelector, - types: method.typeEncoding) + _ = class_replaceMethod(originalClass, + swizzledSelector, + original, + typeEncoding) } // Swizzle `forwardInvocation:` to intercept `swizzledSelector`. @@ -305,14 +309,18 @@ class InterceptingSpec: QuickSpec { } } - let method2 = originalClass.method(for: ObjCSelector.forwardInvocation)! - let original2 = originalClass.replaceMethod(with: CFunction(block: forwardInvocationBlock), - for: ObjCSelector.forwardInvocation, - types: method2.typeEncoding) + let method2 = class_getInstanceMethod(originalClass, ObjCSelector.forwardInvocation)! + let typeEncoding2 = method_getTypeEncoding(method2) + + let original2 = class_replaceMethod(originalClass, + ObjCSelector.forwardInvocation, + imp_implementationWithBlock(forwardInvocationBlock as Any), + typeEncoding2) defer { - _ = originalClass.replaceMethod(with: original2, - for: ObjCSelector.forwardInvocation, - types: method2.typeEncoding) + _ = class_replaceMethod(originalClass, + ObjCSelector.forwardInvocation, + original2, + typeEncoding2) } object.lifeIsGood(nil) @@ -325,14 +333,18 @@ class InterceptingSpec: QuickSpec { let swizzledSelector = #selector(object.lifeIsGood) // Redirect `swizzledSelector` to the forwarding machinery. - let method = originalClass.method(for: swizzledSelector)! - let original = originalClass.replaceMethod(with: .forwarding, - for: swizzledSelector, - types: method.typeEncoding) + let method = class_getInstanceMethod(originalClass, swizzledSelector)! + let typeEncoding = method_getTypeEncoding(method) + + let original = class_replaceMethod(originalClass, + swizzledSelector, + _rac_objc_msgForward, + typeEncoding) defer { - _ = originalClass.replaceMethod(with: original, - for: swizzledSelector, - types: method.typeEncoding) + _ = class_replaceMethod(originalClass, + swizzledSelector, + original, + typeEncoding) } // Swizzle `forwardInvocation:` to intercept `swizzledSelector`. @@ -343,14 +355,18 @@ class InterceptingSpec: QuickSpec { } } - let method2 = originalClass.method(for: ObjCSelector.forwardInvocation)! - let original2 = originalClass.replaceMethod(with: CFunction(block: forwardInvocationBlock), - for: ObjCSelector.forwardInvocation, - types: method2.typeEncoding) + let method2 = class_getInstanceMethod(originalClass, ObjCSelector.forwardInvocation)! + let typeEncoding2 = method_getTypeEncoding(method2) + + let original2 = class_replaceMethod(originalClass, + ObjCSelector.forwardInvocation, + imp_implementationWithBlock(forwardInvocationBlock as Any), + typeEncoding2) defer { - _ = originalClass.replaceMethod(with: original2, - for: ObjCSelector.forwardInvocation, - types: method2.typeEncoding) + _ = class_replaceMethod(originalClass, + ObjCSelector.forwardInvocation, + original2, + typeEncoding2) } object.lifeIsGood(nil) @@ -367,16 +383,20 @@ class InterceptingSpec: QuickSpec { invoked = true } - let method = originalClass.method(for: swizzledSelector)! - let original = originalClass.replaceMethod(with: CFunction(block: lifeIsGoodBlock), - for: swizzledSelector, - types: method.typeEncoding) + let method = class_getInstanceMethod(originalClass, swizzledSelector)! + let typeEncoding = method_getTypeEncoding(method) + + let original = class_replaceMethod(originalClass, + swizzledSelector, + imp_implementationWithBlock(lifeIsGoodBlock as Any), + typeEncoding) defer { - _ = originalClass.replaceMethod(with: original, - for: swizzledSelector, - types: method.typeEncoding) + _ = class_replaceMethod(originalClass, + swizzledSelector, + original, + typeEncoding) } - + object.lifeIsGood(nil) expect(invoked) == true } @@ -763,18 +783,20 @@ private class ForwardInvocationTestObject: InterceptedObject { object.forwardedSelector = invocation.selector } - try! ObjCClass(ForwardInvocationTestObject.self) - .addMethod(with: CFunction(assuming: impl), - for: ObjCSelector.forwardInvocation, - types: ObjCMethodEncoding.forwardInvocation) + let success = class_addMethod(ForwardInvocationTestObject.self, + ObjCSelector.forwardInvocation, + unsafeBitCast(impl, to: IMP.self), + ObjCMethodEncoding.forwardInvocation) + assert(success) assert(ForwardInvocationTestObject.instancesRespond(to: ObjCSelector.forwardInvocation)) - try! ObjCClass(ForwardInvocationTestObject.self) - .addMethod(with: .forwarding, - for: ForwardInvocationTestObject.forwardedSelector, - types: ObjCMethodEncoding.forwardInvocation) + let success2 = class_addMethod(ForwardInvocationTestObject.self, + ForwardInvocationTestObject.forwardedSelector, + _rac_objc_msgForward, + ObjCMethodEncoding.forwardInvocation) + assert(success2) assert(ForwardInvocationTestObject.instancesRespond(to: ForwardInvocationTestObject.forwardedSelector)) return 0 diff --git a/ReactiveCocoaTests/SwizzlingSpec.swift b/ReactiveCocoaTests/SwizzlingSpec.swift index 87b3f4aacf..aaa4c6ede1 100644 --- a/ReactiveCocoaTests/SwizzlingSpec.swift +++ b/ReactiveCocoaTests/SwizzlingSpec.swift @@ -11,32 +11,32 @@ class SwizzlingSpec: QuickSpec { let object = SwizzledObject() expect(type(of: object)).to(beIdenticalTo(SwizzledObject.self)) - let subclass = swizzleClass(object) - expect(type(of: object)).to(beIdenticalTo(subclass.reference)) + let subclass: AnyClass = swizzleClass(object) + expect(type(of: object)).to(beIdenticalTo(subclass)) let objcClass = (object as AnyObject).objcClass expect(objcClass).to(beIdenticalTo(SwizzledObject.self)) expect((objcClass as AnyObject).objcClass).to(beIdenticalTo(SwizzledObject.self)) - expect(subclass.name).to(contain("_RACSwift")) + expect(String(cString: class_getName(subclass))).to(contain("_RACSwift")) } it("should reuse the runtime subclass across instances") { let object = SwizzledObject() - let subclass = swizzleClass(object) + let subclass: AnyClass = swizzleClass(object) let object2 = SwizzledObject() - let subclass2 = swizzleClass(object2) + let subclass2: AnyClass = swizzleClass(object2) - expect(subclass) == subclass2 + expect(subclass).to(beIdenticalTo(subclass2)) } it("should return the known runtime subclass") { let object = SwizzledObject() - let subclass = swizzleClass(object) - let subclass2 = swizzleClass(object) + let subclass: AnyClass = swizzleClass(object) + let subclass2: AnyClass = swizzleClass(object) - expect(subclass) == subclass2 + expect(subclass).to(beIdenticalTo(subclass2)) } } } From a39a59809464ec658016946d019351653a1ee7e6 Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 18 Nov 2016 04:05:38 +0100 Subject: [PATCH 0587/1028] Minor refactoring in `NSObject+Interception` and `ObjC+*`. --- ReactiveCocoa/NSObject+Intercepting.swift | 133 ++++++++++---------- ReactiveCocoa/ObjC+Runtime.swift | 11 +- ReactiveCocoa/ObjC+RuntimeSubclassing.swift | 8 +- 3 files changed, 81 insertions(+), 71 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index f223cdfccb..5f1769bd42 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -8,6 +8,9 @@ extension Reactive where Base: NSObject { /// Create a signal which sends a `next` event at the end of every invocation /// of `selector` on the object. /// + /// - note: Observers to the resulting signal should not call the method + /// specified by the selector. + /// /// - parameters: /// - selector: The selector to observe. /// @@ -22,6 +25,9 @@ extension Reactive where Base: NSObject { /// Create a signal which sends a `next` event, containing an array of bridged /// arguments, at the end of every invocation of `selector` on the object. /// + /// - note: Observers to the resulting signal should not call the method + /// specified by the selector. + /// /// - parameters: /// - selector: The selector to observe. /// @@ -35,70 +41,47 @@ extension Reactive where Base: NSObject { } private func setupInterception(_ object: NSObject, for selector: Selector) -> Signal { - let invokingAlias = selector.invokingAlias + let interopAlias = selector.interopAlias - if let signal = object.value(forAssociatedKey: invokingAlias.utf8Start) as! Signal? { - return signal + if let state = object.value(forAssociatedKey: interopAlias.utf8Start) as! InterceptingState? { + return state.signal } - let signal = Signal { observer in - let disposable = CompositeDisposable() - - disposable += object.reactive.lifetime.ended - .observeCompleted(observer.sendCompleted) - - let subclass: AnyClass = swizzleClass(object) - - swizzledClasses.modify { classes in - if !classes.contains(ObjectIdentifier(subclass)) { - classes.insert(ObjectIdentifier(subclass)) - enableMessageForwarding(subclass) - } - } + let subclass: AnyClass = swizzleClass(object) - guard let method = class_getInstanceMethod(subclass, selector) else { - fatalError("Selector `\(selector)` does not exist in class `\(String(cString: class_getName(class_getSuperclass(subclass))))`.") + swizzledClasses.modify { classes in + if !classes.contains(ObjectIdentifier(subclass)) { + classes.insert(ObjectIdentifier(subclass)) + enableMessageForwarding(subclass) } + } - let impl = method_getImplementation(method) + guard let method = class_getInstanceMethod(subclass, selector) else { + fatalError("Selector `\(selector)` does not exist in class `\(String(cString: class_getName(class_getSuperclass(subclass))))`.") + } - if impl != _rac_objc_msgForward { - let typeEncoding = method_getTypeEncoding(method)! - assert(checkTypeEncoding(typeEncoding)) + let impl = method_getImplementation(method) - if let existingMethod = class_getImmediateMethod(subclass, selector) { - // Make a method alias for the existing method implementation, if it is - // defined in the runtime subclass. - let interopAlias = selector.interopAlias - let existingImpl = method_getImplementation(existingMethod) - let success = class_addMethod(subclass, interopAlias, existingImpl, typeEncoding) - precondition(success, "Unexpected state: Cannot overwrite a preserved implementation of \(selector) from the runtime subclass of `\(String(cString: class_getName(class_getSuperclass(subclass))))`.") - } + if impl != _rac_objc_msgForward { + let typeEncoding = method_getTypeEncoding(method)! + assert(checkTypeEncoding(typeEncoding)) - // Redefine the selector to call -forwardInvocation:. - _ = class_replaceMethod(subclass, selector, _rac_objc_msgForward, typeEncoding) + if let existingMethod = class_getImmediateMethod(subclass, selector) { + // Make a method alias for the existing method implementation, if it is + // defined in the runtime subclass. + let existingImpl = method_getImplementation(existingMethod) + let success = class_addMethod(subclass, interopAlias, existingImpl, typeEncoding) + precondition(success, "Unexpected state: Cannot overwrite a preserved implementation of \(selector) from the runtime subclass of `\(String(cString: class_getName(class_getSuperclass(subclass))))`.") } - object.setValue(observer.send(value:), forAssociatedKey: selector.utf8Start) - return disposable - } - - object.setValue(signal, forAssociatedKey: invokingAlias.utf8Start) - return signal -} - -extension Selector { - fileprivate var invokingAlias: Selector { - return "rac0_".appending(String(cString: sel_getName(self))).withCString(sel_registerName) + // Redefine the selector to call -forwardInvocation:. + _ = class_replaceMethod(subclass, selector, _rac_objc_msgForward, typeEncoding) } - fileprivate var interopAlias: Selector { - return "rac1_".appending(String(cString: sel_getName(self))).withCString(sel_registerName) - } + let state = InterceptingState(lifetime: object.reactive.lifetime) + object.setValue(state, forAssociatedKey: interopAlias.utf8Start) - fileprivate var utf8Start: UnsafePointer { - return unsafeBitCast(self, to: UnsafePointer.self) - } + return state.signal } private func enableMessageForwarding(_ objcClass: AnyClass) { @@ -113,18 +96,12 @@ private func enableMessageForwarding(_ objcClass: AnyClass) { // exception. typealias ForwardInvocationImpl = @convention(c) (NSObject, Selector, AnyObject) -> Void let newForwardInvocation: ForwardInvocationImpl = { object, _, invocation in - var shouldForward = true - let selector = invocation.selector! - let invokingAlias = selector.invokingAlias let interopAlias = selector.interopAlias - let realClass: AnyClass = object_getClass(object) - let perceivedClass: AnyClass = object.objcClass - defer { - if let observer = object.value(forAssociatedKey: selector.utf8Start) as! ((AnyObject) -> Void)? { - observer(invocation) + if let state = object.value(forAssociatedKey: interopAlias.utf8Start) as! InterceptingState? { + state.observer.send(value: invocation) } } @@ -133,6 +110,8 @@ private func enableMessageForwarding(_ objcClass: AnyClass) { // all instances that had been applied `signalForSelector:` are // non-threadsafe as any mutable instances. + let realClass: AnyClass = object_getClass(object) + if realClass.instancesRespond(to: interopAlias) { // `self` uses a runtime subclass generated by third-party APIs, and RAC // found an existing implementation for the selector at the setup time. @@ -153,6 +132,9 @@ private func enableMessageForwarding(_ objcClass: AnyClass) { } } + let perceivedClass: AnyClass = object.objcClass + let invokingAlias = selector.invokingAlias + if perceivedClass.instancesRespond(to: selector) { // The stated class has an implementation of the selector. Call that // implementation if it is not the ObjC message forwarder. @@ -170,13 +152,10 @@ private func enableMessageForwarding(_ objcClass: AnyClass) { // Forward the invocation to the closest `forwardInvocation:` in the // inheritance hierarchy. - var target = objc_super() - target.receiver = Unmanaged.passUnretained(object) - target.super_class = perceivedClass - - typealias SuperForwardInvocation = @convention(c) (UnsafeMutablePointer, Selector, AnyObject) -> Void - let send = unsafeBitCast(_rac_objc_msgSendSuper, to: SuperForwardInvocation.self) - send(&target, ObjCSelector.forwardInvocation, invocation) + typealias SuperForwardInvocation = @convention(c) (AnyObject, Selector, AnyObject) -> Void + let impl = class_getMethodImplementation(perceivedClass, ObjCSelector.forwardInvocation) + let forwardInvocation = unsafeBitCast(impl, to: SuperForwardInvocation.self) + forwardInvocation(object, ObjCSelector.forwardInvocation, invocation) } _ = class_replaceMethod(objcClass, @@ -185,7 +164,29 @@ private func enableMessageForwarding(_ objcClass: AnyClass) { ObjCMethodEncoding.forwardInvocation) } -func checkTypeEncoding(_ types: UnsafePointer) -> Bool { +private final class InterceptingState { + let (signal, observer) = Signal.pipe() + + init(lifetime: Lifetime) { + lifetime.ended.observeCompleted(observer.sendCompleted) + } +} + +extension Selector { + fileprivate var invokingAlias: Selector { + return "rac0_".appending(String(cString: sel_getName(self))).withCString(sel_registerName) + } + + fileprivate var interopAlias: Selector { + return "rac1_".appending(String(cString: sel_getName(self))).withCString(sel_registerName) + } + + fileprivate var utf8Start: UnsafePointer { + return unsafeBitCast(self, to: UnsafePointer.self) + } +} + +private func checkTypeEncoding(_ types: UnsafePointer) -> Bool { // Some types, including vector types, are not encoded. In these cases the // signature starts with the size of the argument frame. assert(types.pointee < Int8(UInt8(ascii: "1")) || types.pointee > Int8(UInt8(ascii: "9")), diff --git a/ReactiveCocoa/ObjC+Runtime.swift b/ReactiveCocoa/ObjC+Runtime.swift index 5a7f004f10..d2395c3bb7 100644 --- a/ReactiveCocoa/ObjC+Runtime.swift +++ b/ReactiveCocoa/ObjC+Runtime.swift @@ -1,4 +1,13 @@ -func class_getImmediateMethod(_ `class`: AnyClass, _ selector: Selector) -> Method? { +/// Search in `class` for any method that matches the supplied selector without +/// propagating to the ancestors. +/// +/// - parameters: +/// - class: The class to search the method in. +/// - selector: The selector of the method. +/// +/// - returns: +/// The matching method, or `nil` if none is found. +internal func class_getImmediateMethod(_ `class`: AnyClass, _ selector: Selector) -> Method? { var count: UInt32 = 0 let buffer = class_copyMethodList(`class`, &count) let methods = UnsafeBufferPointer(start: buffer, count: Int(count)) diff --git a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift index 0b9e5b686e..ae3ff7f251 100644 --- a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift +++ b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift @@ -2,10 +2,6 @@ import ReactiveSwift private let swizzledExternalClasses = Atomic>([]) -private func subclassName(of class: AnyClass) -> String { - return String(cString: class_getName(`class`)).appending("_RACSwift") -} - /// ISA-swizzle the class of the supplied instance. /// /// - note: If the instance has already been isa-swizzled, the swizzling happens @@ -59,6 +55,10 @@ internal func swizzleClass(_ instance: NSObject) -> AnyClass { } } +private func subclassName(of class: AnyClass) -> String { + return String(cString: class_getName(`class`)).appending("_RACSwift") +} + private func replaceGetClass(in class: AnyClass, decoy perceivedClass: AnyClass) { let getClass: @convention(block) (Any) -> AnyClass = { _ in return perceivedClass From 4b466f6ef134d38b8e5c0be4fbfbbed743e424e7 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Fri, 18 Nov 2016 09:20:07 +0100 Subject: [PATCH 0588/1028] Update changelog. --- CHANGELOG.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20c26b23d5..7b3f9a3cf0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -245,12 +245,12 @@ public final class MyController { RAC(label, text) Discover binding targets via .reactive on UI components. -

    label.reactive.text \<~ viewModel.name

    +

    label.reactive.text <~ viewModel.name

    RACObserve(object, keyPath) - `NSObject.reactive.values(forKeyPath:)` + NSObject.reactive.values(forKeyPath:) @@ -274,7 +274,8 @@ public final class MyController { Apply combineLatest to your signals, and pass the method as the action to observeValues.

    Signal.combineLatest([signal1, signal2])
    -	.observeValues(self.perform(first:second:))
    + .take(during: self.reactive.lifetime) + .observeValues { [weak self] in self?.perform(first: $1, second: $2) }

    @@ -300,7 +301,7 @@ public final class MyController { Control value changes, e.g. textField.rac_text Discover control value signals via .reactive on UI components. -

    viewModel.searchString \<~ textField.reactive.textValues

    +

    viewModel.searchString <~ textField.reactive.textValues

    From bcd84555a2df8fd132b58922e875bfff15a01bfe Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Fri, 18 Nov 2016 09:21:11 +0100 Subject: [PATCH 0589/1028] Fixed a couple of typos in the changelog. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b3f9a3cf0..aa88f581e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -275,7 +275,7 @@ public final class MyController {

    Signal.combineLatest([signal1, signal2])
     	.take(during: self.reactive.lifetime)
    -	.observeValues { [weak self] in self?.perform(first: $1, second: $2) }
    + .observeValues { [weak self] in self?.perform(first: $0, second: $1) }

    From 0b88ed17d5605bc08d795eeb6e1bac6e5fdbf28e Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 18 Nov 2016 18:17:04 +0100 Subject: [PATCH 0590/1028] Reduced the message forwarding overhead related to runtime selectors. --- ReactiveCocoa.xcodeproj/project.pbxproj | 8 ++ ReactiveCocoa/NSObject+Intercepting.swift | 113 +++++++++--------- .../InterceptingPerformanceTests.swift | 33 +++++ 3 files changed, 97 insertions(+), 57 deletions(-) create mode 100644 ReactiveCocoaTests/InterceptingPerformanceTests.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 4b4532e9cb..2ad4fd2eed 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -118,6 +118,9 @@ 9A2E425F1DAA6737006D909F /* CocoaTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */; }; 9A2E42601DAA6737006D909F /* CocoaTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */; }; 9A2E42611DAA6737006D909F /* CocoaTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */; }; + 9A54A2111DDF5B4D001739B3 /* InterceptingPerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A54A2101DDF5B4D001739B3 /* InterceptingPerformanceTests.swift */; }; + 9A54A2121DDF5B4D001739B3 /* InterceptingPerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A54A2101DDF5B4D001739B3 /* InterceptingPerformanceTests.swift */; }; + 9A54A2131DDF5B4D001739B3 /* InterceptingPerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A54A2101DDF5B4D001739B3 /* InterceptingPerformanceTests.swift */; }; 9A6AAA0E1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; 9A6AAA0F1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; 9A6AAA101DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; @@ -347,6 +350,7 @@ 9A1D06751D9415FB00ACF44C /* ObjCRuntimeAliases.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ObjCRuntimeAliases.h; sourceTree = ""; }; 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyValueObservingSpec.swift; sourceTree = ""; }; 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaTarget.swift; sourceTree = ""; }; + 9A54A2101DDF5B4D001739B3 /* InterceptingPerformanceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterceptingPerformanceTests.swift; sourceTree = ""; }; 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterceptingSpec.swift; sourceTree = ""; }; 9A6AAA221DB8F51C0013AAEA /* ReusableComponents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponents.swift; sourceTree = ""; }; 9A6AAA251DB8F5280013AAEA /* ReusableComponents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponents.swift; sourceTree = ""; }; @@ -708,6 +712,7 @@ B696FB801A7640C00075236D /* TestError.swift */, 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */, 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */, + 9A54A2101DDF5B4D001739B3 /* InterceptingPerformanceTests.swift */, 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */, 9AA0BD851DDE0A8B00531FCF /* SwizzlingSpec.swift */, D04725FA19E49ED7006002AA /* Supporting Files */, @@ -1153,6 +1158,7 @@ 9A1D06491D93EA7E00ACF44C /* UIProgressViewSpec.swift in Sources */, 4ABEFE291DCFCFA90066A8C2 /* UICollectionViewSpec.swift in Sources */, 9AA0BD881DDE0A8B00531FCF /* SwizzlingSpec.swift in Sources */, + 9A54A2131DDF5B4D001739B3 /* InterceptingPerformanceTests.swift in Sources */, BEE020661D637B0000DF261F /* TestError.swift in Sources */, 9A1D06531D93EA7E00ACF44C /* UITextFieldSpec.swift in Sources */, ); @@ -1216,6 +1222,7 @@ buildActionMask = 2147483647; files = ( 9A6AAA0E1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */, + 9A54A2111DDF5B4D001739B3 /* InterceptingPerformanceTests.swift in Sources */, 538DCB7D1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift in Sources */, 9ADE4A8F1DA6DA20005C2AC8 /* NSControlSpec.swift in Sources */, 9AA0BD861DDE0A8B00531FCF /* SwizzlingSpec.swift in Sources */, @@ -1299,6 +1306,7 @@ 9A1D06361D93EA7E00ACF44C /* UIActivityIndicatorViewSpec.swift in Sources */, 9A1D06461D93EA7E00ACF44C /* UILabelSpec.swift in Sources */, 9A9DFEEA1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */, + 9A54A2121DDF5B4D001739B3 /* InterceptingPerformanceTests.swift in Sources */, 9ADFE5A21DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */, 9A1D06421D93EA7E00ACF44C /* UIDatePickerSpec.swift in Sources */, 53AC46CF1DD6FC0000C799E1 /* UISliderSpec.swift in Sources */, diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 5f1769bd42..54903cd571 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -40,20 +40,41 @@ extension Reactive where Base: NSObject { } } +private var interopImplKey = 0 +private var interceptingStatesKey: StaticString = "RACIntercepting" + +private final class Box { + var value: Value + + init(_ value: Value) { + self.value = value + } +} + private func setupInterception(_ object: NSObject, for selector: Selector) -> Signal { - let interopAlias = selector.interopAlias + let states = object.reactive.associatedValue(forKey: interceptingStatesKey) { _ in + return Box([Selector: InterceptingState]()) + } - if let state = object.value(forAssociatedKey: interopAlias.utf8Start) as! InterceptingState? { + if let state = states.value[selector] { return state.signal } let subclass: AnyClass = swizzleClass(object) - swizzledClasses.modify { classes in + let interopImpls = swizzledClasses.modify { classes -> Atomic<[Selector: IMP]> in if !classes.contains(ObjectIdentifier(subclass)) { classes.insert(ObjectIdentifier(subclass)) enableMessageForwarding(subclass) } + + if let impls = objc_getAssociatedObject(subclass, &interopImplKey) as! Atomic<[Selector: IMP]>? { + return impls + } else { + let impls = Atomic<[Selector: IMP]>([:]) + objc_setAssociatedObject(subclass, &interopImplKey, impls, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + return impls + } } guard let method = class_getInstanceMethod(subclass, selector) else { @@ -70,8 +91,7 @@ private func setupInterception(_ object: NSObject, for selector: Selector) -> Si // Make a method alias for the existing method implementation, if it is // defined in the runtime subclass. let existingImpl = method_getImplementation(existingMethod) - let success = class_addMethod(subclass, interopAlias, existingImpl, typeEncoding) - precondition(success, "Unexpected state: Cannot overwrite a preserved implementation of \(selector) from the runtime subclass of `\(String(cString: class_getName(class_getSuperclass(subclass))))`.") + interopImpls.modify { $0[selector] = existingImpl } } // Redefine the selector to call -forwardInvocation:. @@ -79,7 +99,7 @@ private func setupInterception(_ object: NSObject, for selector: Selector) -> Si } let state = InterceptingState(lifetime: object.reactive.lifetime) - object.setValue(state, forAssociatedKey: interopAlias.utf8Start) + states.value[selector] = state return state.signal } @@ -94,13 +114,13 @@ private func enableMessageForwarding(_ objcClass: AnyClass) { // invoke any existing implementation of -forwardInvocation:. If there // was no existing implementation, throw an unrecognized selector // exception. - typealias ForwardInvocationImpl = @convention(c) (NSObject, Selector, AnyObject) -> Void - let newForwardInvocation: ForwardInvocationImpl = { object, _, invocation in + typealias ForwardInvocationImpl = @convention(block) (NSObject, AnyObject) -> Void + let newForwardInvocation: ForwardInvocationImpl = { object, invocation in let selector = invocation.selector! - let interopAlias = selector.interopAlias defer { - if let state = object.value(forAssociatedKey: interopAlias.utf8Start) as! InterceptingState? { + if let states = object.value(forAssociatedKey: interceptingStatesKey.utf8Start) as! Box<[Selector: InterceptingState]>?, + let state = states.value[selector] { state.observer.send(value: invocation) } } @@ -110,57 +130,44 @@ private func enableMessageForwarding(_ objcClass: AnyClass) { // all instances that had been applied `signalForSelector:` are // non-threadsafe as any mutable instances. - let realClass: AnyClass = object_getClass(object) + let realClass: AnyClass = objcClass + let perceivedClass: AnyClass = object.objcClass + let method = class_getInstanceMethod(perceivedClass, selector) + let typeEncoding = method_getTypeEncoding(method) + let interopImpls = objc_getAssociatedObject(realClass, &interopImplKey) as! Atomic<[Selector: IMP]> + + let invokingImpl: IMP? - if realClass.instancesRespond(to: interopAlias) { + if let interopImpl = interopImpls.modify({ $0[selector] }) { // `self` uses a runtime subclass generated by third-party APIs, and RAC // found an existing implementation for the selector at the setup time. // Call that implementation if it is not the ObjC message forwarder. - let interopMethod = class_getInstanceMethod(realClass, interopAlias) - let interopImpl = method_getImplementation(interopMethod) - - if interopImpl != _rac_objc_msgForward { - let typeEncoding = method_getTypeEncoding(interopMethod) - - let previousImpl = class_replaceMethod(realClass, selector, interopImpl, typeEncoding) - - invocation.invoke() - - _ = class_replaceMethod(realClass, selector, previousImpl, typeEncoding) - - return - } - } - - let perceivedClass: AnyClass = object.objcClass - let invokingAlias = selector.invokingAlias - - if perceivedClass.instancesRespond(to: selector) { + invokingImpl = interopImpl + } else if let impl = method.map(method_getImplementation), impl != _rac_objc_msgForward { // The stated class has an implementation of the selector. Call that // implementation if it is not the ObjC message forwarder. - let method = class_getInstanceMethod(perceivedClass, selector) - let impl = method_getImplementation(method) - - if impl != _rac_objc_msgForward { - _ = class_replaceMethod(realClass, invokingAlias, impl, method_getTypeEncoding(method)) - invocation.setSelector(invokingAlias) - invocation.invoke() - - return - } + invokingImpl = impl + } else { + invokingImpl = nil } - // Forward the invocation to the closest `forwardInvocation:` in the - // inheritance hierarchy. - typealias SuperForwardInvocation = @convention(c) (AnyObject, Selector, AnyObject) -> Void - let impl = class_getMethodImplementation(perceivedClass, ObjCSelector.forwardInvocation) - let forwardInvocation = unsafeBitCast(impl, to: SuperForwardInvocation.self) - forwardInvocation(object, ObjCSelector.forwardInvocation, invocation) + if let invokingImpl = invokingImpl { + let previousImpl = class_replaceMethod(realClass, selector, invokingImpl, typeEncoding) + invocation.invoke() + _ = class_replaceMethod(realClass, selector, previousImpl, typeEncoding) + } else { + // Forward the invocation to the closest `forwardInvocation:` in the + // inheritance hierarchy. + typealias SuperForwardInvocation = @convention(c) (AnyObject, Selector, AnyObject) -> Void + let impl = class_getMethodImplementation(perceivedClass, ObjCSelector.forwardInvocation) + let forwardInvocation = unsafeBitCast(impl, to: SuperForwardInvocation.self) + forwardInvocation(object, ObjCSelector.forwardInvocation, invocation) + } } _ = class_replaceMethod(objcClass, ObjCSelector.forwardInvocation, - unsafeBitCast(newForwardInvocation, to: IMP.self), + imp_implementationWithBlock(newForwardInvocation as Any), ObjCMethodEncoding.forwardInvocation) } @@ -173,14 +180,6 @@ private final class InterceptingState { } extension Selector { - fileprivate var invokingAlias: Selector { - return "rac0_".appending(String(cString: sel_getName(self))).withCString(sel_registerName) - } - - fileprivate var interopAlias: Selector { - return "rac1_".appending(String(cString: sel_getName(self))).withCString(sel_registerName) - } - fileprivate var utf8Start: UnsafePointer { return unsafeBitCast(self, to: UnsafePointer.self) } diff --git a/ReactiveCocoaTests/InterceptingPerformanceTests.swift b/ReactiveCocoaTests/InterceptingPerformanceTests.swift new file mode 100644 index 0000000000..fe076cb65c --- /dev/null +++ b/ReactiveCocoaTests/InterceptingPerformanceTests.swift @@ -0,0 +1,33 @@ +import XCTest +@testable import ReactiveCocoa +import ReactiveSwift + +private final class Receiver: NSObject { + dynamic func message1() {} + dynamic func message2() {} +} + +class InterceptingTests: XCTestCase { + fileprivate var receiver: Receiver! + + override func setUp() { + receiver = Receiver() + } + + func testDirectMessage() { + measure { + for _ in 0 ..< 50000 { + self.receiver.message1() + } + } + } + + func testInterceptedMessage() { + _ = receiver.reactive.trigger(for: #selector(receiver.message2)) + measure { + for _ in 0 ..< 50000 { + self.receiver.message2() + } + } + } +} From cb3b1d9169c5887dc7a76256585e2a3b63508a06 Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 18 Nov 2016 19:36:55 +0100 Subject: [PATCH 0591/1028] Method signature caching. Reduced dynamic casting overhead. --- ReactiveCocoa/NSObject+Intercepting.swift | 85 ++++++++++++++----- ReactiveCocoa/ObjC+Constants.swift | 1 + ReactiveCocoa/ObjC+Messages.swift | 3 + .../InterceptingPerformanceTests.swift | 1 + 4 files changed, 70 insertions(+), 20 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 54903cd571..7cafb99f00 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -41,8 +41,10 @@ extension Reactive where Base: NSObject { } private var interopImplKey = 0 -private var interceptingStatesKey: StaticString = "RACIntercepting" +private var interceptingStatesKey = 0 +private var interceptedSelectorsKey = 0 +// A container to circumvent Swift dynamic bridging overhead. private final class Box { var value: Value @@ -52,29 +54,32 @@ private final class Box { } private func setupInterception(_ object: NSObject, for selector: Selector) -> Signal { - let states = object.reactive.associatedValue(forKey: interceptingStatesKey) { _ in - return Box([Selector: InterceptingState]()) - } - - if let state = states.value[selector] { + let currentStates = object.value(forAssociatedKey: &interceptingStatesKey) as! Box<[Selector: InterceptingState]>? + if let state = currentStates?.value[selector] { return state.signal } + let newStates = currentStates ?? { + let box = Box([Selector: InterceptingState]()) + object.setValue(box, forAssociatedKey: &interceptingStatesKey) + return box + }() + let subclass: AnyClass = swizzleClass(object) - let interopImpls = swizzledClasses.modify { classes -> Atomic<[Selector: IMP]> in + swizzledClasses.modify { classes in if !classes.contains(ObjectIdentifier(subclass)) { classes.insert(ObjectIdentifier(subclass)) enableMessageForwarding(subclass) + setupMethodSignatureCaching(subclass) } - if let impls = objc_getAssociatedObject(subclass, &interopImplKey) as! Atomic<[Selector: IMP]>? { - return impls - } else { - let impls = Atomic<[Selector: IMP]>([:]) - objc_setAssociatedObject(subclass, &interopImplKey, impls, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - return impls + var selectors = objc_getAssociatedObject(subclass, &interceptedSelectorsKey) as! Box<[Selector: AnyObject]>? + if selectors == nil { + selectors = Box([:]) + objc_setAssociatedObject(subclass, &interceptedSelectorsKey, selectors!, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } + selectors!.value[selector] = getSignature(subclass, selector) } guard let method = class_getInstanceMethod(subclass, selector) else { @@ -91,7 +96,15 @@ private func setupInterception(_ object: NSObject, for selector: Selector) -> Si // Make a method alias for the existing method implementation, if it is // defined in the runtime subclass. let existingImpl = method_getImplementation(existingMethod) - interopImpls.modify { $0[selector] = existingImpl } + + swizzledClasses.modify { _ in + var map = objc_getAssociatedObject(subclass, &interopImplKey) as! Box<[Selector: IMP]>? + if map == nil { + map = Box([:]) + objc_setAssociatedObject(subclass, &interopImplKey, map!, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + map!.value[selector] = existingImpl + } } // Redefine the selector to call -forwardInvocation:. @@ -99,11 +112,18 @@ private func setupInterception(_ object: NSObject, for selector: Selector) -> Si } let state = InterceptingState(lifetime: object.reactive.lifetime) - states.value[selector] = state + newStates.value[selector] = state return state.signal } +private func getSignature(_ objcClass: AnyClass, _ selector: Selector) -> AnyObject { + let NSMethodSignature: AnyClass = NSClassFromString("NSMethodSignature")! + let method = class_getInstanceMethod(objcClass, selector) + let typeEncoding = method_getTypeEncoding(method)! + return NSMethodSignature.signature(withObjCTypes: typeEncoding) +} + private func enableMessageForwarding(_ objcClass: AnyClass) { // Set up a new version of -forwardInvocation:. // @@ -114,12 +134,16 @@ private func enableMessageForwarding(_ objcClass: AnyClass) { // invoke any existing implementation of -forwardInvocation:. If there // was no existing implementation, throw an unrecognized selector // exception. + + let realClass: AnyClass = objcClass + let perceivedClass: AnyClass = class_getSuperclass(objcClass) + typealias ForwardInvocationImpl = @convention(block) (NSObject, AnyObject) -> Void let newForwardInvocation: ForwardInvocationImpl = { object, invocation in let selector = invocation.selector! defer { - if let states = object.value(forAssociatedKey: interceptingStatesKey.utf8Start) as! Box<[Selector: InterceptingState]>?, + if let states = object.value(forAssociatedKey: &interceptingStatesKey) as! Box<[Selector: InterceptingState]>?, let state = states.value[selector] { state.observer.send(value: invocation) } @@ -130,15 +154,13 @@ private func enableMessageForwarding(_ objcClass: AnyClass) { // all instances that had been applied `signalForSelector:` are // non-threadsafe as any mutable instances. - let realClass: AnyClass = objcClass - let perceivedClass: AnyClass = object.objcClass let method = class_getInstanceMethod(perceivedClass, selector) let typeEncoding = method_getTypeEncoding(method) - let interopImpls = objc_getAssociatedObject(realClass, &interopImplKey) as! Atomic<[Selector: IMP]> + let interopImpls = objc_getAssociatedObject(realClass, &interopImplKey) as! Box<[Selector: IMP]>? let invokingImpl: IMP? - if let interopImpl = interopImpls.modify({ $0[selector] }) { + if let interopImpl = interopImpls?.value[selector] { // `self` uses a runtime subclass generated by third-party APIs, and RAC // found an existing implementation for the selector at the setup time. // Call that implementation if it is not the ObjC message forwarder. @@ -171,6 +193,29 @@ private func enableMessageForwarding(_ objcClass: AnyClass) { ObjCMethodEncoding.forwardInvocation) } +private func setupMethodSignatureCaching(_ objcClass: AnyClass) { + let realClass: AnyClass = objcClass + let perceivedClass: AnyClass = class_getSuperclass(objcClass) + + let newMethodSignatureForSelector: @convention(block) (NSObject, Selector) -> AnyObject? = { object, selector in + let interceptedSelectors = objc_getAssociatedObject(realClass, &interceptedSelectorsKey) as! Box<[Selector: AnyObject]> + + if let signature = interceptedSelectors.value[selector] { + return signature + } + + typealias SuperMethodSignatureForSelector = @convention(c) (AnyObject, Selector, Selector) -> AnyObject? + let impl = class_getMethodImplementation(perceivedClass, ObjCSelector.methodSignatureForSelector) + let methodSignatureForSelector = unsafeBitCast(impl, to: SuperMethodSignatureForSelector.self) + return methodSignatureForSelector(object, ObjCSelector.methodSignatureForSelector, selector) + } + + _ = class_replaceMethod(objcClass, + ObjCSelector.methodSignatureForSelector, + imp_implementationWithBlock(newMethodSignatureForSelector as Any), + ObjCMethodEncoding.methodSignatureForSelector) +} + private final class InterceptingState { let (signal, observer) = Signal.pipe() diff --git a/ReactiveCocoa/ObjC+Constants.swift b/ReactiveCocoa/ObjC+Constants.swift index 3a183ab3fe..659f858adf 100644 --- a/ReactiveCocoa/ObjC+Constants.swift +++ b/ReactiveCocoa/ObjC+Constants.swift @@ -10,6 +10,7 @@ internal enum ObjCMethodEncoding { static let forwardInvocation = extract("v@:@") static let methodSignatureForSelector = extract("v@::") static let getClass = extract("#@:") + static let repsondsToSelector = extract("c@::") private static func extract(_ string: StaticString) -> UnsafePointer { return UnsafeRawPointer(string.utf8Start).assumingMemoryBound(to: CChar.self) diff --git a/ReactiveCocoa/ObjC+Messages.swift b/ReactiveCocoa/ObjC+Messages.swift index d6fa7e85aa..c3058d1b56 100644 --- a/ReactiveCocoa/ObjC+Messages.swift +++ b/ReactiveCocoa/ObjC+Messages.swift @@ -30,4 +30,7 @@ @objc(getArgumentTypeAtIndex:) func argumentType(at index: UInt) -> UnsafePointer + + @objc(signatureWithObjCTypes:) + static func signature(withObjCTypes typeEncoding: UnsafePointer) -> AnyObject } diff --git a/ReactiveCocoaTests/InterceptingPerformanceTests.swift b/ReactiveCocoaTests/InterceptingPerformanceTests.swift index fe076cb65c..6fbe53ff56 100644 --- a/ReactiveCocoaTests/InterceptingPerformanceTests.swift +++ b/ReactiveCocoaTests/InterceptingPerformanceTests.swift @@ -4,6 +4,7 @@ import ReactiveSwift private final class Receiver: NSObject { dynamic func message1() {} + dynamic func message2() {} } From b8f124f24a3e6b49c7d18d4031d113d8db7c0b12 Mon Sep 17 00:00:00 2001 From: Anders Date: Sat, 19 Nov 2016 05:11:11 +0100 Subject: [PATCH 0592/1028] Avoid any swapping of implementations as much as possible. --- ReactiveCocoa/NSObject+Intercepting.swift | 127 +++++++++++++--------- ReactiveCocoa/ObjC+Messages.swift | 3 + ReactiveCocoa/ObjC+Runtime.swift | 16 +-- 3 files changed, 86 insertions(+), 60 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 7cafb99f00..4a6269806b 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -44,6 +44,9 @@ private var interopImplKey = 0 private var interceptingStatesKey = 0 private var interceptedSelectorsKey = 0 +private let NSInvocation: AnyClass = NSClassFromString("NSInvocation")! +private let NSMethodSignature: AnyClass = NSClassFromString("NSMethodSignature")! + // A container to circumvent Swift dynamic bridging overhead. private final class Box { var value: Value @@ -54,32 +57,30 @@ private final class Box { } private func setupInterception(_ object: NSObject, for selector: Selector) -> Signal { - let currentStates = object.value(forAssociatedKey: &interceptingStatesKey) as! Box<[Selector: InterceptingState]>? - if let state = currentStates?.value[selector] { + let alias = selector.prefixing("rac0_") + + if let state = object.value(forAssociatedKey: alias.utf8Start) as! InterceptingState? { return state.signal } - let newStates = currentStates ?? { - let box = Box([Selector: InterceptingState]()) - object.setValue(box, forAssociatedKey: &interceptingStatesKey) - return box - }() - let subclass: AnyClass = swizzleClass(object) swizzledClasses.modify { classes in if !classes.contains(ObjectIdentifier(subclass)) { classes.insert(ObjectIdentifier(subclass)) - enableMessageForwarding(subclass) - setupMethodSignatureCaching(subclass) - } - var selectors = objc_getAssociatedObject(subclass, &interceptedSelectorsKey) as! Box<[Selector: AnyObject]>? - if selectors == nil { - selectors = Box([:]) - objc_setAssociatedObject(subclass, &interceptedSelectorsKey, selectors!, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + let interopImpls = Box<[Selector: IMP]>([:]) + objc_setAssociatedObject(subclass, &interopImplKey, interopImpls, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + + let signatureCache = Box<[Selector: AnyObject]>([:]) + objc_setAssociatedObject(subclass, &interceptedSelectorsKey, signatureCache, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + + enableMessageForwarding(subclass, interopImpls) + setupMethodSignatureCaching(subclass, signatureCache) } - selectors!.value[selector] = getSignature(subclass, selector) + + let signatureCache = objc_getAssociatedObject(subclass, &interceptedSelectorsKey) as! Box<[Selector: AnyObject]> + signatureCache.value[selector] = getSignature(subclass, selector) } guard let method = class_getInstanceMethod(subclass, selector) else { @@ -98,12 +99,8 @@ private func setupInterception(_ object: NSObject, for selector: Selector) -> Si let existingImpl = method_getImplementation(existingMethod) swizzledClasses.modify { _ in - var map = objc_getAssociatedObject(subclass, &interopImplKey) as! Box<[Selector: IMP]>? - if map == nil { - map = Box([:]) - objc_setAssociatedObject(subclass, &interopImplKey, map!, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - } - map!.value[selector] = existingImpl + let interopImpl = objc_getAssociatedObject(subclass, &interopImplKey) as! Box<[Selector: IMP]> + interopImpl.value[selector] = existingImpl } } @@ -112,19 +109,18 @@ private func setupInterception(_ object: NSObject, for selector: Selector) -> Si } let state = InterceptingState(lifetime: object.reactive.lifetime) - newStates.value[selector] = state + object.setValue(state, forAssociatedKey: alias.utf8Start) return state.signal } private func getSignature(_ objcClass: AnyClass, _ selector: Selector) -> AnyObject { - let NSMethodSignature: AnyClass = NSClassFromString("NSMethodSignature")! let method = class_getInstanceMethod(objcClass, selector) let typeEncoding = method_getTypeEncoding(method)! return NSMethodSignature.signature(withObjCTypes: typeEncoding) } -private func enableMessageForwarding(_ objcClass: AnyClass) { +private func enableMessageForwarding(_ objcClass: AnyClass, _ interopImpls: Box<[Selector: IMP]>) { // Set up a new version of -forwardInvocation:. // // If the selector has been passed to -rac_signalForSelector:, invoke @@ -141,10 +137,10 @@ private func enableMessageForwarding(_ objcClass: AnyClass) { typealias ForwardInvocationImpl = @convention(block) (NSObject, AnyObject) -> Void let newForwardInvocation: ForwardInvocationImpl = { object, invocation in let selector = invocation.selector! + let alias = selector.prefixing("rac0_") defer { - if let states = object.value(forAssociatedKey: &interceptingStatesKey) as! Box<[Selector: InterceptingState]>?, - let state = states.value[selector] { + if let state = object.value(forAssociatedKey: alias.utf8Start) as! InterceptingState? { state.observer.send(value: invocation) } } @@ -156,35 +152,44 @@ private func enableMessageForwarding(_ objcClass: AnyClass) { let method = class_getInstanceMethod(perceivedClass, selector) let typeEncoding = method_getTypeEncoding(method) - let interopImpls = objc_getAssociatedObject(realClass, &interopImplKey) as! Box<[Selector: IMP]>? - - let invokingImpl: IMP? - if let interopImpl = interopImpls?.value[selector] { + if let interopImpl = interopImpls.value[selector] { // `self` uses a runtime subclass generated by third-party APIs, and RAC // found an existing implementation for the selector at the setup time. // Call that implementation if it is not the ObjC message forwarder. - invokingImpl = interopImpl - } else if let impl = method.map(method_getImplementation), impl != _rac_objc_msgForward { + // + // The IMP swapping would flush the IMP cache. + let previousImpl = class_replaceMethod(realClass, selector, interopImpl, typeEncoding) + invocation.invoke() + _ = class_replaceMethod(realClass, selector, previousImpl, typeEncoding) + + return + } + + if let impl = method.map(method_getImplementation), impl != _rac_objc_msgForward { // The stated class has an implementation of the selector. Call that // implementation if it is not the ObjC message forwarder. - invokingImpl = impl - } else { - invokingImpl = nil - } - if let invokingImpl = invokingImpl { - let previousImpl = class_replaceMethod(realClass, selector, invokingImpl, typeEncoding) + // Update the alias only if the implementation has been changed to avoid + // flushing the IMP cache. + let method = class_getImmediateMethod(realClass, alias) + + if method == nil || method_getImplementation(method!) != impl { + _ = class_replaceMethod(realClass, alias, impl, typeEncoding) + } + + invocation.setSelector(alias) invocation.invoke() - _ = class_replaceMethod(realClass, selector, previousImpl, typeEncoding) - } else { - // Forward the invocation to the closest `forwardInvocation:` in the - // inheritance hierarchy. - typealias SuperForwardInvocation = @convention(c) (AnyObject, Selector, AnyObject) -> Void - let impl = class_getMethodImplementation(perceivedClass, ObjCSelector.forwardInvocation) - let forwardInvocation = unsafeBitCast(impl, to: SuperForwardInvocation.self) - forwardInvocation(object, ObjCSelector.forwardInvocation, invocation) + + return } + + // Forward the invocation to the closest `forwardInvocation:` in the + // inheritance hierarchy. + typealias SuperForwardInvocation = @convention(c) (AnyObject, Selector, AnyObject) -> Void + let impl = class_getMethodImplementation(perceivedClass, ObjCSelector.forwardInvocation) + let forwardInvocation = unsafeBitCast(impl, to: SuperForwardInvocation.self) + forwardInvocation(object, ObjCSelector.forwardInvocation, invocation) } _ = class_replaceMethod(objcClass, @@ -193,14 +198,11 @@ private func enableMessageForwarding(_ objcClass: AnyClass) { ObjCMethodEncoding.forwardInvocation) } -private func setupMethodSignatureCaching(_ objcClass: AnyClass) { - let realClass: AnyClass = objcClass +private func setupMethodSignatureCaching(_ objcClass: AnyClass, _ signatureCache: Box<[Selector: AnyObject]>) { let perceivedClass: AnyClass = class_getSuperclass(objcClass) let newMethodSignatureForSelector: @convention(block) (NSObject, Selector) -> AnyObject? = { object, selector in - let interceptedSelectors = objc_getAssociatedObject(realClass, &interceptedSelectorsKey) as! Box<[Selector: AnyObject]> - - if let signature = interceptedSelectors.value[selector] { + if let signature = signatureCache.value[selector] { return signature } @@ -228,6 +230,27 @@ extension Selector { fileprivate var utf8Start: UnsafePointer { return unsafeBitCast(self, to: UnsafePointer.self) } + + public func prefixing(_ prefix: StaticString) -> Selector { + assert(prefix.isASCII) + + let length = Int(strlen(utf8Start)) + let prefixedLength = length + prefix.utf8CodeUnitCount + + let asciiPrefix = UnsafeRawPointer(prefix.utf8Start).assumingMemoryBound(to: Int8.self) + + let cString = UnsafeMutablePointer.allocate(capacity: prefixedLength + 1) + defer { + cString.deinitialize() + cString.deallocate(capacity: prefixedLength + 1) + } + + cString.initialize(from: asciiPrefix, count: prefix.utf8CodeUnitCount) + (cString + prefix.utf8CodeUnitCount).initialize(from: utf8Start, count: length) + (cString + prefixedLength).initialize(to: Int8(UInt8(ascii: "\0"))) + + return sel_registerName(cString) + } } private func checkTypeEncoding(_ types: UnsafePointer) -> Bool { diff --git a/ReactiveCocoa/ObjC+Messages.swift b/ReactiveCocoa/ObjC+Messages.swift index c3058d1b56..f0f1191fe2 100644 --- a/ReactiveCocoa/ObjC+Messages.swift +++ b/ReactiveCocoa/ObjC+Messages.swift @@ -22,6 +22,9 @@ func copy(to buffer: UnsafeMutableRawPointer?, forArgumentAt index: Int) func invoke() + + @objc(invocationWithMethodSignature:) + static func invocation(withMethodSignature signature: AnyObject) -> AnyObject } // Methods of `NSMethodSignature`. diff --git a/ReactiveCocoa/ObjC+Runtime.swift b/ReactiveCocoa/ObjC+Runtime.swift index d2395c3bb7..63a9117ae3 100644 --- a/ReactiveCocoa/ObjC+Runtime.swift +++ b/ReactiveCocoa/ObjC+Runtime.swift @@ -8,15 +8,15 @@ /// - returns: /// The matching method, or `nil` if none is found. internal func class_getImmediateMethod(_ `class`: AnyClass, _ selector: Selector) -> Method? { - var count: UInt32 = 0 - let buffer = class_copyMethodList(`class`, &count) - let methods = UnsafeBufferPointer(start: buffer, count: Int(count)) + if let buffer = class_copyMethodList(`class`, nil) { + defer { free(buffer) } - defer { free(buffer) } - - for method in methods { - if method_getName(method!) == selector { - return method! + var iterator = buffer + while let method = iterator.pointee { + if method_getName(method) == selector { + return method + } + iterator = iterator.advanced(by: 1) } } From 49df899f66d9e0ed7f9bff37a9e5e0af3f24da75 Mon Sep 17 00:00:00 2001 From: Anders Date: Sat, 19 Nov 2016 06:14:41 +0100 Subject: [PATCH 0593/1028] Documented and refactored `setupInterception`. --- ReactiveCocoa.xcodeproj/project.pbxproj | 10 ++ ReactiveCocoa/NSObject+Intercepting.swift | 163 +++++++--------------- ReactiveCocoa/ObjC+Constants.swift | 29 ++++ ReactiveCocoa/ObjC+Messages.swift | 6 +- ReactiveCocoa/ObjC+Selector.swift | 26 ++++ 5 files changed, 123 insertions(+), 111 deletions(-) create mode 100644 ReactiveCocoa/ObjC+Selector.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 2ad4fd2eed..36d99bb307 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -121,6 +121,10 @@ 9A54A2111DDF5B4D001739B3 /* InterceptingPerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A54A2101DDF5B4D001739B3 /* InterceptingPerformanceTests.swift */; }; 9A54A2121DDF5B4D001739B3 /* InterceptingPerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A54A2101DDF5B4D001739B3 /* InterceptingPerformanceTests.swift */; }; 9A54A2131DDF5B4D001739B3 /* InterceptingPerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A54A2101DDF5B4D001739B3 /* InterceptingPerformanceTests.swift */; }; + 9A54A21B1DE00D09001739B3 /* ObjC+Selector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A54A21A1DE00D09001739B3 /* ObjC+Selector.swift */; }; + 9A54A21C1DE00D09001739B3 /* ObjC+Selector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A54A21A1DE00D09001739B3 /* ObjC+Selector.swift */; }; + 9A54A21D1DE00D09001739B3 /* ObjC+Selector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A54A21A1DE00D09001739B3 /* ObjC+Selector.swift */; }; + 9A54A21E1DE00D09001739B3 /* ObjC+Selector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A54A21A1DE00D09001739B3 /* ObjC+Selector.swift */; }; 9A6AAA0E1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; 9A6AAA0F1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; 9A6AAA101DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; @@ -351,6 +355,7 @@ 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyValueObservingSpec.swift; sourceTree = ""; }; 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaTarget.swift; sourceTree = ""; }; 9A54A2101DDF5B4D001739B3 /* InterceptingPerformanceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterceptingPerformanceTests.swift; sourceTree = ""; }; + 9A54A21A1DE00D09001739B3 /* ObjC+Selector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObjC+Selector.swift"; sourceTree = ""; }; 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterceptingSpec.swift; sourceTree = ""; }; 9A6AAA221DB8F51C0013AAEA /* ReusableComponents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponents.swift; sourceTree = ""; }; 9A6AAA251DB8F5280013AAEA /* ReusableComponents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponents.swift; sourceTree = ""; }; @@ -677,6 +682,7 @@ 9AA0BD8E1DDE29F800531FCF /* NSObject+ObjCRuntime.swift */, 9AE7C2A31DDD7F5100F7534C /* ObjC+Messages.swift */, 9AA0BD891DDE153A00531FCF /* ObjC+Constants.swift */, + 9A54A21A1DE00D09001739B3 /* ObjC+Selector.swift */, 9AA0BD801DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift */, 9AA0BD771DDE03DE00531FCF /* ObjC+Runtime.swift */, 9A1D05E91D93E9F100ACF44C /* AppKit */, @@ -1126,6 +1132,7 @@ 9A1D06121D93EA0100ACF44C /* UIBarButtonItem.swift in Sources */, 9A1D06111D93EA0100ACF44C /* UIActivityIndicatorView.swift in Sources */, 9A1D061B1D93EA0100ACF44C /* UISegmentedControl.swift in Sources */, + 9A54A21E1DE00D09001739B3 /* ObjC+Selector.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1170,6 +1177,7 @@ files = ( 9A2E42601DAA6737006D909F /* CocoaTarget.swift in Sources */, 9A9DFEE51DA7B5500039EE1B /* NSObject+Intercepting.swift in Sources */, + 9A54A21D1DE00D09001739B3 /* ObjC+Selector.swift in Sources */, 9AA0BD7E1DDE03DE00531FCF /* ObjC+Runtime.swift in Sources */, 9AA0BD9A1DDE7A2200531FCF /* ObjCRuntimeAliases.m in Sources */, 9ADE4A931DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, @@ -1199,6 +1207,7 @@ 9A1D05E01D93E99100ACF44C /* NSObject+Association.swift in Sources */, 538DCB791DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */, 9AE7C2A41DDD7F5100F7534C /* ObjC+Messages.swift in Sources */, + 9A54A21B1DE00D09001739B3 /* ObjC+Selector.swift in Sources */, 9AA0BD8A1DDE153A00531FCF /* ObjC+Constants.swift in Sources */, 9A6AAA261DB8F5280013AAEA /* ReusableComponents.swift in Sources */, 9AA0BD7C1DDE03DE00531FCF /* ObjC+Runtime.swift in Sources */, @@ -1279,6 +1288,7 @@ 9A1D06001D93EA0000ACF44C /* UIBarButtonItem.swift in Sources */, 9AA0BD991DDE7A2200531FCF /* ObjCRuntimeAliases.m in Sources */, 9A1D05FF1D93EA0000ACF44C /* UIActivityIndicatorView.swift in Sources */, + 9A54A21C1DE00D09001739B3 /* ObjC+Selector.swift in Sources */, 9AA0BD8B1DDE153A00531FCF /* ObjC+Constants.swift in Sources */, 9A1D06091D93EA0000ACF44C /* UISegmentedControl.swift in Sources */, 9ADE4A7D1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 4a6269806b..a2c18451a3 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -17,9 +17,7 @@ extension Reactive where Base: NSObject { /// - returns: /// A trigger signal. public func trigger(for selector: Selector) -> Signal<(), NoError> { - return base.synchronized { - return setupInterception(base, for: selector).map { _ in } - } + return setupInterception(base, for: selector).map { _ in } } /// Create a signal which sends a `next` event, containing an array of bridged @@ -34,90 +32,74 @@ extension Reactive where Base: NSObject { /// - returns: /// A signal that sends an array of bridged arguments. public func signal(for selector: Selector) -> Signal<[Any?], NoError> { - return base.synchronized { - return setupInterception(base, for: selector).map(unpackInvocation) - } + return setupInterception(base, for: selector).map(unpackInvocation) } } -private var interopImplKey = 0 -private var interceptingStatesKey = 0 -private var interceptedSelectorsKey = 0 +private func setupInterception(_ object: NSObject, for selector: Selector) -> Signal { + let alias = selector.prefixing("rac0_") -private let NSInvocation: AnyClass = NSClassFromString("NSInvocation")! -private let NSMethodSignature: AnyClass = NSClassFromString("NSMethodSignature")! + guard let method = class_getInstanceMethod(object.objcClass, selector) else { + fatalError("Selector `\(selector)` does not exist in class `\(String(describing: object.objcClass))`.") + } -// A container to circumvent Swift dynamic bridging overhead. -private final class Box { - var value: Value + let typeEncoding = method_getTypeEncoding(method)! + assert(checkTypeEncoding(typeEncoding)) - init(_ value: Value) { - self.value = value - } -} + return object.synchronized { + if let state = object.value(forAssociatedKey: alias.utf8Start) as! InterceptingState? { + return state.signal + } -private func setupInterception(_ object: NSObject, for selector: Selector) -> Signal { - let alias = selector.prefixing("rac0_") + let subclass: AnyClass = swizzleClass(object) - if let state = object.value(forAssociatedKey: alias.utf8Start) as! InterceptingState? { - return state.signal - } + let hasNotBeenSwizzled = swizzledClasses + .modify { $0.insert(ObjectIdentifier(subclass)).inserted } - let subclass: AnyClass = swizzleClass(object) + objc_sync_enter(subclass) - swizzledClasses.modify { classes in - if !classes.contains(ObjectIdentifier(subclass)) { - classes.insert(ObjectIdentifier(subclass)) + let interopImpls: Box<[Selector: IMP]> + let signatureCache: Box<[Selector: AnyObject]> - let interopImpls = Box<[Selector: IMP]>([:]) - objc_setAssociatedObject(subclass, &interopImplKey, interopImpls, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + if hasNotBeenSwizzled { + interopImpls = Box([:]) + signatureCache = Box([:]) - let signatureCache = Box<[Selector: AnyObject]>([:]) + objc_setAssociatedObject(subclass, &interopImplKey, interopImpls, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) objc_setAssociatedObject(subclass, &interceptedSelectorsKey, signatureCache, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) enableMessageForwarding(subclass, interopImpls) setupMethodSignatureCaching(subclass, signatureCache) + } else { + interopImpls = objc_getAssociatedObject(subclass, &interopImplKey) as! Box<[Selector: IMP]> + signatureCache = objc_getAssociatedObject(subclass, &interceptedSelectorsKey) as! Box<[Selector: AnyObject]> } - let signatureCache = objc_getAssociatedObject(subclass, &interceptedSelectorsKey) as! Box<[Selector: AnyObject]> - signatureCache.value[selector] = getSignature(subclass, selector) - } - - guard let method = class_getInstanceMethod(subclass, selector) else { - fatalError("Selector `\(selector)` does not exist in class `\(String(cString: class_getName(class_getSuperclass(subclass))))`.") - } - - let impl = method_getImplementation(method) - - if impl != _rac_objc_msgForward { - let typeEncoding = method_getTypeEncoding(method)! - assert(checkTypeEncoding(typeEncoding)) - - if let existingMethod = class_getImmediateMethod(subclass, selector) { - // Make a method alias for the existing method implementation, if it is - // defined in the runtime subclass. - let existingImpl = method_getImplementation(existingMethod) + if signatureCache.value[selector] == nil { + signatureCache.value[selector] = NSMethodSignature.signature(withObjCTypes: typeEncoding) + } - swizzledClasses.modify { _ in - let interopImpl = objc_getAssociatedObject(subclass, &interopImplKey) as! Box<[Selector: IMP]> - interopImpl.value[selector] = existingImpl + if interopImpls.value[selector] == nil { + let immediateMethod = class_getImmediateMethod(subclass, selector) + interopImpls.value[selector] = immediateMethod.flatMap { + // If the subclass is generated by external parties, e.g. KVO, and has an + // implementation for the selector, take a record of that implementation + // for interoperability. + let immediateImpl = method_getImplementation($0) + return immediateImpl.flatMap { $0 != _rac_objc_msgForward ? $0 : nil } } } - // Redefine the selector to call -forwardInvocation:. - _ = class_replaceMethod(subclass, selector, _rac_objc_msgForward, typeEncoding) - } + objc_sync_exit(subclass) - let state = InterceptingState(lifetime: object.reactive.lifetime) - object.setValue(state, forAssociatedKey: alias.utf8Start) + let state = InterceptingState(lifetime: object.reactive.lifetime) + object.setValue(state, forAssociatedKey: alias.utf8Start) - return state.signal -} + // Start forwarding the messages of the selector. + _ = class_replaceMethod(subclass, selector, _rac_objc_msgForward, typeEncoding) -private func getSignature(_ objcClass: AnyClass, _ selector: Selector) -> AnyObject { - let method = class_getInstanceMethod(objcClass, selector) - let typeEncoding = method_getTypeEncoding(method)! - return NSMethodSignature.signature(withObjCTypes: typeEncoding) + return state.signal + } } private func enableMessageForwarding(_ objcClass: AnyClass, _ interopImpls: Box<[Selector: IMP]>) { @@ -226,30 +208,16 @@ private final class InterceptingState { } } -extension Selector { - fileprivate var utf8Start: UnsafePointer { - return unsafeBitCast(self, to: UnsafePointer.self) - } - - public func prefixing(_ prefix: StaticString) -> Selector { - assert(prefix.isASCII) - - let length = Int(strlen(utf8Start)) - let prefixedLength = length + prefix.utf8CodeUnitCount - - let asciiPrefix = UnsafeRawPointer(prefix.utf8Start).assumingMemoryBound(to: Int8.self) - - let cString = UnsafeMutablePointer.allocate(capacity: prefixedLength + 1) - defer { - cString.deinitialize() - cString.deallocate(capacity: prefixedLength + 1) - } +private var interopImplKey = 0 +private var interceptingStatesKey = 0 +private var interceptedSelectorsKey = 0 - cString.initialize(from: asciiPrefix, count: prefix.utf8CodeUnitCount) - (cString + prefix.utf8CodeUnitCount).initialize(from: utf8Start, count: length) - (cString + prefixedLength).initialize(to: Int8(UInt8(ascii: "\0"))) +// A container to box value type collections. +private final class Box { + var value: Value - return sel_registerName(cString) + init(_ value: Value) { + self.value = value } } @@ -279,7 +247,7 @@ private func unpackInvocation(_ invocation: AnyObject) -> [Any?] { // Ignore `self` and `_cmd`. for position in 2 ..< count { let rawEncoding = methodSignature.argumentType(at: position) - let encoding = TypeEncoding(rawValue: rawEncoding.pointee) ?? .undefined + let encoding = ObjCTypeEncoding(rawValue: rawEncoding.pointee) ?? .undefined func extract(_ type: U.Type) -> U { let pointer = UnsafeMutableRawPointer.allocate(bytes: MemoryLayout.size, @@ -339,28 +307,3 @@ private func unpackInvocation(_ invocation: AnyObject) -> [Any?] { return bridged } - -private enum TypeEncoding: Int8 { - case char = 99 - case int = 105 - case short = 115 - case long = 108 - case longLong = 113 - - case unsignedChar = 67 - case unsignedInt = 73 - case unsignedShort = 83 - case unsignedLong = 76 - case unsignedLongLong = 81 - - case float = 102 - case double = 100 - - case bool = 66 - - case object = 64 - case type = 35 - case selector = 58 - - case undefined = -1 -} diff --git a/ReactiveCocoa/ObjC+Constants.swift b/ReactiveCocoa/ObjC+Constants.swift index 659f858adf..0cbf831224 100644 --- a/ReactiveCocoa/ObjC+Constants.swift +++ b/ReactiveCocoa/ObjC+Constants.swift @@ -16,3 +16,32 @@ internal enum ObjCMethodEncoding { return UnsafeRawPointer(string.utf8Start).assumingMemoryBound(to: CChar.self) } } + +/// Objective-C type encoding. +/// +/// The enum does not cover all options, but only those that are expressive in +/// Swift. +internal enum ObjCTypeEncoding: Int8 { + case char = 99 + case int = 105 + case short = 115 + case long = 108 + case longLong = 113 + + case unsignedChar = 67 + case unsignedInt = 73 + case unsignedShort = 83 + case unsignedLong = 76 + case unsignedLongLong = 81 + + case float = 102 + case double = 100 + + case bool = 66 + + case object = 64 + case type = 35 + case selector = 58 + + case undefined = -1 +} diff --git a/ReactiveCocoa/ObjC+Messages.swift b/ReactiveCocoa/ObjC+Messages.swift index f0f1191fe2..fa7c07ef63 100644 --- a/ReactiveCocoa/ObjC+Messages.swift +++ b/ReactiveCocoa/ObjC+Messages.swift @@ -1,9 +1,13 @@ // Unavailable classes like `NSInvocation` can still be passed into Swift as -// `AnyObject`, and receive messages via `AnyObject`'s message dispatching. +// `AnyClass` and `AnyObject`, and receive messages as `AnyClass` and +// `AnyObject` existentials. // // These `@objc` protocols host the method signatures so that they can be used // with `AnyObject`. +internal let NSInvocation: AnyClass = NSClassFromString("NSInvocation")! +internal let NSMethodSignature: AnyClass = NSClassFromString("NSMethodSignature")! + // `-class` and `+class`. @objc internal protocol ObjCClassReporting { @objc(class) diff --git a/ReactiveCocoa/ObjC+Selector.swift b/ReactiveCocoa/ObjC+Selector.swift new file mode 100644 index 0000000000..8f3b323608 --- /dev/null +++ b/ReactiveCocoa/ObjC+Selector.swift @@ -0,0 +1,26 @@ +extension Selector { + internal var utf8Start: UnsafePointer { + return unsafeBitCast(self, to: UnsafePointer.self) + } + + internal func prefixing(_ prefix: StaticString) -> Selector { + assert(prefix.isASCII) + + let length = Int(strlen(utf8Start)) + let prefixedLength = length + prefix.utf8CodeUnitCount + + let asciiPrefix = UnsafeRawPointer(prefix.utf8Start).assumingMemoryBound(to: Int8.self) + + let cString = UnsafeMutablePointer.allocate(capacity: prefixedLength + 1) + defer { + cString.deinitialize() + cString.deallocate(capacity: prefixedLength + 1) + } + + cString.initialize(from: asciiPrefix, count: prefix.utf8CodeUnitCount) + (cString + prefix.utf8CodeUnitCount).initialize(from: utf8Start, count: length) + (cString + prefixedLength).initialize(to: Int8(UInt8(ascii: "\0"))) + + return sel_registerName(cString) + } +} From c7cab32664e6ea12ab6fce3396b5871d26556c13 Mon Sep 17 00:00:00 2001 From: Anders Date: Sat, 19 Nov 2016 07:12:38 +0100 Subject: [PATCH 0594/1028] Added two test cases to InterceptingSpec. --- ReactiveCocoaTests/InterceptingSpec.swift | 48 +++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/ReactiveCocoaTests/InterceptingSpec.swift b/ReactiveCocoaTests/InterceptingSpec.swift index c9eb5a50e0..4e49b9f48a 100644 --- a/ReactiveCocoaTests/InterceptingSpec.swift +++ b/ReactiveCocoaTests/InterceptingSpec.swift @@ -228,6 +228,54 @@ class InterceptingSpec: QuickSpec { expect(secondValue) == "Winner" } + it("should send a value event for every invocation of a method on a receiver that is subsequently KVO'd twice") { + var counter = 0 + + object.reactive.trigger(for: #selector(setter: InterceptedObject.objectValue)) + .observeValues { counter += 1 } + + object.reactive + .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) + .start() + .dispose() + + object.reactive + .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) + .start() + + expect(counter) == 0 + + object.objectValue = 1 + expect(counter) == 1 + + object.objectValue = 1 + expect(counter) == 2 + } + + it("should send a value event for every invocation of a method on a receiver that is KVO'd twice while being swizzled by RAC in between") { + var counter = 0 + + object.reactive + .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) + .start() + .dispose() + + object.reactive.trigger(for: #selector(setter: InterceptedObject.objectValue)) + .observeValues { counter += 1 } + + object.reactive + .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) + .start() + + expect(counter) == 0 + + object.objectValue = 1 + expect(counter) == 1 + + object.objectValue = 1 + expect(counter) == 2 + } + it("should call the right signal for two instances of the same class, adding signals for the same selector") { let object1 = InterceptedObject() let object2 = InterceptedObject() From 922b3503322639a931d2aa49189bd7b6198b1b0a Mon Sep 17 00:00:00 2001 From: Anders Date: Sat, 19 Nov 2016 07:27:32 +0100 Subject: [PATCH 0595/1028] Updated the intercepting performance tests. --- .../InterceptingPerformanceTests.swift | 46 +++++++++++++------ 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/ReactiveCocoaTests/InterceptingPerformanceTests.swift b/ReactiveCocoaTests/InterceptingPerformanceTests.swift index 6fbe53ff56..abf03072d2 100644 --- a/ReactiveCocoaTests/InterceptingPerformanceTests.swift +++ b/ReactiveCocoaTests/InterceptingPerformanceTests.swift @@ -2,32 +2,52 @@ import XCTest @testable import ReactiveCocoa import ReactiveSwift -private final class Receiver: NSObject { - dynamic func message1() {} +private final class Receiver1: NSObject { + dynamic func message() {} +} + +private final class Receiver2: NSObject { + dynamic var value: Int = 0 +} - dynamic func message2() {} +private final class Receiver3: NSObject { + dynamic var value: Int = 0 } class InterceptingTests: XCTestCase { - fileprivate var receiver: Receiver! + func testInterceptedMessage() { + let receiver = Receiver1() + _ = receiver.reactive.trigger(for: #selector(receiver.message)) - override func setUp() { - receiver = Receiver() + measure { + for _ in 0 ..< 50000 { + receiver.message() + } + } } - func testDirectMessage() { + func testRACKVOInterceptedMessage() { + let receiver = Receiver2() + + _ = receiver.reactive.trigger(for: #selector(setter: receiver.value)) + receiver.reactive.values(forKeyPath: #keyPath(Receiver2.value)).start() + measure { - for _ in 0 ..< 50000 { - self.receiver.message1() + for i in 0 ..< 50000 { + receiver.value = i } } } - func testInterceptedMessage() { - _ = receiver.reactive.trigger(for: #selector(receiver.message2)) + func testKVORACInterceptedMessage() { + let receiver = Receiver3() + + receiver.reactive.values(forKeyPath: #keyPath(Receiver3.value)).start() + _ = receiver.reactive.trigger(for: #selector(setter: receiver.value)) + measure { - for _ in 0 ..< 50000 { - self.receiver.message2() + for i in 0 ..< 50000 { + receiver.value = i } } } From e0cbaaaf5b2c445654cf46faef45a889474aadcf Mon Sep 17 00:00:00 2001 From: Anders Date: Sat, 19 Nov 2016 17:48:43 +0100 Subject: [PATCH 0596/1028] Abolished global atomic properties. --- ReactiveCocoa/NSObject+Intercepting.swift | 65 ++++++++++----------- ReactiveCocoa/NSObject+Synchronizing.swift | 6 ++ ReactiveCocoa/ObjC+RuntimeSubclassing.swift | 32 ++++++---- 3 files changed, 58 insertions(+), 45 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index a2c18451a3..bdd8140f38 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -2,8 +2,6 @@ import Foundation import ReactiveSwift import enum Result.NoError -private let swizzledClasses = Atomic>([]) - extension Reactive where Base: NSObject { /// Create a signal which sends a `next` event at the end of every invocation /// of `selector` on the object. @@ -53,45 +51,43 @@ private func setupInterception(_ object: NSObject, for selector: Selector) -> Si let subclass: AnyClass = swizzleClass(object) - let hasNotBeenSwizzled = swizzledClasses - .modify { $0.insert(ObjectIdentifier(subclass)).inserted } - - objc_sync_enter(subclass) + synchronized(subclass) { + let isSwizzled = objc_getAssociatedObject(subclass, &isSwizzledKey) as! Bool? ?? false - let interopImpls: Box<[Selector: IMP]> - let signatureCache: Box<[Selector: AnyObject]> + let interopImpls: Box<[Selector: IMP]> + let signatureCache: Box<[Selector: AnyObject]> - if hasNotBeenSwizzled { - interopImpls = Box([:]) - signatureCache = Box([:]) + if isSwizzled { + interopImpls = objc_getAssociatedObject(subclass, &interopImplKey) as! Box<[Selector: IMP]> + signatureCache = objc_getAssociatedObject(subclass, &interceptedSelectorsKey) as! Box<[Selector: AnyObject]> + } else { + interopImpls = Box([:]) + signatureCache = Box([:]) - objc_setAssociatedObject(subclass, &interopImplKey, interopImpls, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - objc_setAssociatedObject(subclass, &interceptedSelectorsKey, signatureCache, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + objc_setAssociatedObject(subclass, &interopImplKey, interopImpls, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + objc_setAssociatedObject(subclass, &interceptedSelectorsKey, signatureCache, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + objc_setAssociatedObject(subclass, &isSwizzledKey, true, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - enableMessageForwarding(subclass, interopImpls) - setupMethodSignatureCaching(subclass, signatureCache) - } else { - interopImpls = objc_getAssociatedObject(subclass, &interopImplKey) as! Box<[Selector: IMP]> - signatureCache = objc_getAssociatedObject(subclass, &interceptedSelectorsKey) as! Box<[Selector: AnyObject]> - } + enableMessageForwarding(subclass, interopImpls) + setupMethodSignatureCaching(subclass, signatureCache) + } - if signatureCache.value[selector] == nil { - signatureCache.value[selector] = NSMethodSignature.signature(withObjCTypes: typeEncoding) - } + if signatureCache.value[selector] == nil { + signatureCache.value[selector] = NSMethodSignature.signature(withObjCTypes: typeEncoding) + } - if interopImpls.value[selector] == nil { - let immediateMethod = class_getImmediateMethod(subclass, selector) - interopImpls.value[selector] = immediateMethod.flatMap { - // If the subclass is generated by external parties, e.g. KVO, and has an - // implementation for the selector, take a record of that implementation - // for interoperability. - let immediateImpl = method_getImplementation($0) - return immediateImpl.flatMap { $0 != _rac_objc_msgForward ? $0 : nil } + if interopImpls.value[selector] == nil { + let immediateMethod = class_getImmediateMethod(subclass, selector) + interopImpls.value[selector] = immediateMethod.flatMap { + // If the subclass is generated by external parties, e.g. KVO, and has an + // implementation for the selector, take a record of that implementation + // for interoperability. + let immediateImpl = method_getImplementation($0) + return immediateImpl.flatMap { $0 != _rac_objc_msgForward ? $0 : nil } + } } } - objc_sync_exit(subclass) - let state = InterceptingState(lifetime: object.reactive.lifetime) object.setValue(state, forAssociatedKey: alias.utf8Start) @@ -152,8 +148,8 @@ private func enableMessageForwarding(_ objcClass: AnyClass, _ interopImpls: Box< // The stated class has an implementation of the selector. Call that // implementation if it is not the ObjC message forwarder. - // Update the alias only if the implementation has been changed to avoid - // flushing the IMP cache. + // Update the alias only if the implementation has been changed, so as to + // avoid thrashing the IMP cache. let method = class_getImmediateMethod(realClass, alias) if method == nil || method_getImplementation(method!) != impl { @@ -208,6 +204,7 @@ private final class InterceptingState { } } +private var isSwizzledKey = 0 private var interopImplKey = 0 private var interceptingStatesKey = 0 private var interceptedSelectorsKey = 0 diff --git a/ReactiveCocoa/NSObject+Synchronizing.swift b/ReactiveCocoa/NSObject+Synchronizing.swift index 2b5d5a6036..02e6e8d8d2 100644 --- a/ReactiveCocoa/NSObject+Synchronizing.swift +++ b/ReactiveCocoa/NSObject+Synchronizing.swift @@ -5,3 +5,9 @@ extension NSObject { return try execute() } } + +internal func synchronized(_ token: AnyObject, execute: () throws -> Result) rethrows -> Result { + objc_sync_enter(token) + defer { objc_sync_exit(token) } + return try execute() +} diff --git a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift index ae3ff7f251..5c6a964566 100644 --- a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift +++ b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift @@ -1,6 +1,7 @@ import ReactiveSwift -private let swizzledExternalClasses = Atomic>([]) +private var isSwizzledKey = 0 +private var test = 0 /// ISA-swizzle the class of the supplied instance. /// @@ -24,25 +25,34 @@ internal func swizzleClass(_ instance: NSObject) -> AnyClass { if perceivedClass != realClass { // If the class is already lying about what it is, it's probably a KVO - // dynamic subclass or something else that we shouldn't subclass - // ourselves. - // - // Use this runtime subclass directly. - swizzledExternalClasses.modify { classes in - if !classes.contains(ObjectIdentifier(realClass)) { - classes.insert(ObjectIdentifier(realClass)) + // dynamic subclass or something else that we shouldn't subclass at runtime. + synchronized(realClass) { + let isSwizzled = objc_getAssociatedObject(realClass, &isSwizzledKey) as! Bool? ?? false + + if !isSwizzled { replaceGetClass(in: realClass, decoy: perceivedClass) + objc_setAssociatedObject(realClass, &isSwizzledKey, true, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + + let token = Lifetime.Token() + objc_setAssociatedObject(realClass, &test, token, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + + Lifetime(token).ended.observeCompleted { + + let a = 1 + _ = a + + } } } return realClass } else { let name = subclassName(of: perceivedClass) - let subclass: AnyClass = name.withCString { name in - if let existingClass = objc_getClass(name) as! AnyClass? { + let subclass: AnyClass = name.withCString { cString in + if let existingClass = objc_getClass(cString) as! AnyClass? { return existingClass } else { - let subclass: AnyClass = objc_allocateClassPair(perceivedClass, name, 0)! + let subclass: AnyClass = objc_allocateClassPair(perceivedClass, cString, 0)! replaceGetClass(in: subclass, decoy: perceivedClass) objc_registerClassPair(subclass) return subclass From a4bc5fc966abc210410fe3e633ae000960099268 Mon Sep 17 00:00:00 2001 From: Anders Date: Sat, 19 Nov 2016 17:49:25 +0100 Subject: [PATCH 0597/1028] Updated the intercepting performance tests. --- .../InterceptingPerformanceTests.swift | 104 +++++++++++++++++- 1 file changed, 99 insertions(+), 5 deletions(-) diff --git a/ReactiveCocoaTests/InterceptingPerformanceTests.swift b/ReactiveCocoaTests/InterceptingPerformanceTests.swift index abf03072d2..f0af8a2240 100644 --- a/ReactiveCocoaTests/InterceptingPerformanceTests.swift +++ b/ReactiveCocoaTests/InterceptingPerformanceTests.swift @@ -2,6 +2,8 @@ import XCTest @testable import ReactiveCocoa import ReactiveSwift +private let iterationCount = 5000 + private final class Receiver1: NSObject { dynamic func message() {} } @@ -14,41 +16,133 @@ private final class Receiver3: NSObject { dynamic var value: Int = 0 } +private final class Receiver4: NSObject { + dynamic var value: Int = 0 +} + +private final class Receiver5: NSObject { + dynamic var value: Int = 0 +} + +private final class Receiver6: NSObject { + dynamic var value: Int = 0 +} + class InterceptingTests: XCTestCase { + override class func setUp() { + // Swizzle all classes ahead. + + // General interception. + let receiver1 = Receiver1() + _ = receiver1.reactive.trigger(for: #selector(receiver1.message)) + + // Setter: RAC then KVO + let receiver2 = Receiver2() + _ = receiver2.reactive.trigger(for: #selector(setter: receiver2.value)) + receiver2.reactive.values(forKeyPath: #keyPath(Receiver2.value)).start() + + // Setter: KVO then RAC + let receiver3 = Receiver3() + receiver3.reactive.values(forKeyPath: #keyPath(Receiver3.value)).start() + _ = receiver3.reactive.trigger(for: #selector(setter: receiver3.value)) + + // RAC swizzled getter, and then KVO swizzled setter. + let receiver4 = Receiver4() + _ = receiver4.reactive.trigger(for: #selector(getter: receiver4.value)) + receiver4.reactive.values(forKeyPath: #keyPath(Receiver4.value)).start() + + // KVO swizzled setter, and then RAC swizzled getter. + let receiver5 = Receiver5() + receiver5.reactive.values(forKeyPath: #keyPath(Receiver5.value)).start() + _ = receiver5.reactive.trigger(for: #selector(getter: receiver5.value)) + + // Normal KVO + let receiver6 = Receiver6() + receiver6.reactive.values(forKeyPath: #keyPath(Receiver6.value)).start() + } + func testInterceptedMessage() { let receiver = Receiver1() _ = receiver.reactive.trigger(for: #selector(receiver.message)) measure { - for _ in 0 ..< 50000 { + for _ in 0 ..< iterationCount { receiver.message() } } } - func testRACKVOInterceptedMessage() { + func testRACKVOInterceptSetterThenSet() { let receiver = Receiver2() _ = receiver.reactive.trigger(for: #selector(setter: receiver.value)) receiver.reactive.values(forKeyPath: #keyPath(Receiver2.value)).start() measure { - for i in 0 ..< 50000 { + for i in 0 ..< iterationCount { receiver.value = i } } } - func testKVORACInterceptedMessage() { + func testKVORACInterceptSetterThenSet() { let receiver = Receiver3() receiver.reactive.values(forKeyPath: #keyPath(Receiver3.value)).start() _ = receiver.reactive.trigger(for: #selector(setter: receiver.value)) measure { - for i in 0 ..< 50000 { + for i in 0 ..< iterationCount { receiver.value = i } } } + + func testRACKVOInterceptSetterThenGet() { + let receiver = Receiver4() + + _ = receiver.reactive.trigger(for: #selector(getter: receiver.value)) + receiver.reactive.values(forKeyPath: #keyPath(Receiver4.value)).start() + + measure { + for _ in 0 ..< iterationCount { + _ = receiver.value + } + } + } + + func testKVORACInterceptSetterThenGet() { + let receiver = Receiver5() + + receiver.reactive.values(forKeyPath: #keyPath(Receiver5.value)).start() + _ = receiver.reactive.trigger(for: #selector(getter: receiver.value)) + + measure { + for _ in 0 ..< iterationCount { + _ = receiver.value + } + } + } + + func testJustKVOInterceptSetterThenSet() { + let receiver = Receiver6() + receiver.reactive.values(forKeyPath: #keyPath(Receiver6.value)).start() + + measure { + for i in 0 ..< iterationCount { + receiver.value = i + } + } + } + + func testJustKVOInterceptSetterThenGet() { + let receiver = Receiver6() + receiver.reactive.values(forKeyPath: #keyPath(Receiver6.value)).start() + + measure { + for _ in 0 ..< iterationCount { + _ = receiver.value + } + } + } } From 4e5367d00b5369efcd9100e19d34c5962b83e549 Mon Sep 17 00:00:00 2001 From: Anders Date: Sat, 19 Nov 2016 23:48:35 +0100 Subject: [PATCH 0598/1028] Fixed a thread safety issue in the method interception logic. --- ReactiveCocoa/NSObject+Intercepting.swift | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index bdd8140f38..b99acf81a6 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -72,19 +72,32 @@ private func setupInterception(_ object: NSObject, for selector: Selector) -> Si setupMethodSignatureCaching(subclass, signatureCache) } + // Mutations to `interopImpls` and `signatureCache` must be made on a + // a copy and subsequently assigned back, so as to avoid partially updated + // state from being visible to the concurrent readers. + if signatureCache.value[selector] == nil { - signatureCache.value[selector] = NSMethodSignature.signature(withObjCTypes: typeEncoding) + var signatures = signatureCache.value + signatures[selector] = NSMethodSignature.signature(withObjCTypes: typeEncoding) + signatureCache.value = signatures } if interopImpls.value[selector] == nil { let immediateMethod = class_getImmediateMethod(subclass, selector) - interopImpls.value[selector] = immediateMethod.flatMap { + + let immediateImpl: IMP? = immediateMethod.flatMap { // If the subclass is generated by external parties, e.g. KVO, and has an // implementation for the selector, take a record of that implementation // for interoperability. let immediateImpl = method_getImplementation($0) return immediateImpl.flatMap { $0 != _rac_objc_msgForward ? $0 : nil } } + + if let impl = immediateImpl { + var impls = interopImpls.value + impls[selector] = impl + interopImpls.value = impls + } } } From beeb1810fda68c707fdcf68e6345ef2d123777d4 Mon Sep 17 00:00:00 2001 From: Anders Date: Sun, 20 Nov 2016 01:28:31 +0100 Subject: [PATCH 0599/1028] Explicit locking for impl. table and signature cache. --- ReactiveCocoa.xcodeproj/project.pbxproj | 4 ++ ReactiveCocoa/Atomic.swift | 72 +++++++++++++++++++++++ ReactiveCocoa/NSObject+Intercepting.swift | 71 +++++++++------------- 3 files changed, 105 insertions(+), 42 deletions(-) create mode 100644 ReactiveCocoa/Atomic.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 36d99bb307..2875697d03 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -125,6 +125,7 @@ 9A54A21C1DE00D09001739B3 /* ObjC+Selector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A54A21A1DE00D09001739B3 /* ObjC+Selector.swift */; }; 9A54A21D1DE00D09001739B3 /* ObjC+Selector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A54A21A1DE00D09001739B3 /* ObjC+Selector.swift */; }; 9A54A21E1DE00D09001739B3 /* ObjC+Selector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A54A21A1DE00D09001739B3 /* ObjC+Selector.swift */; }; + 9A54A24F1DE123BD001739B3 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A54A24E1DE123BD001739B3 /* Atomic.swift */; }; 9A6AAA0E1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; 9A6AAA0F1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; 9A6AAA101DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; @@ -356,6 +357,7 @@ 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaTarget.swift; sourceTree = ""; }; 9A54A2101DDF5B4D001739B3 /* InterceptingPerformanceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterceptingPerformanceTests.swift; sourceTree = ""; }; 9A54A21A1DE00D09001739B3 /* ObjC+Selector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObjC+Selector.swift"; sourceTree = ""; }; + 9A54A24E1DE123BD001739B3 /* Atomic.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Atomic.swift; sourceTree = ""; }; 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterceptingSpec.swift; sourceTree = ""; }; 9A6AAA221DB8F51C0013AAEA /* ReusableComponents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponents.swift; sourceTree = ""; }; 9A6AAA251DB8F5280013AAEA /* ReusableComponents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponents.swift; sourceTree = ""; }; @@ -669,6 +671,7 @@ D04725EC19E49ED7006002AA /* ReactiveCocoa */ = { isa = PBXGroup; children = ( + 9A54A24E1DE123BD001739B3 /* Atomic.swift */, 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */, 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */, CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */, @@ -1218,6 +1221,7 @@ 9A2E425E1DAA6737006D909F /* CocoaTarget.swift in Sources */, 9ADE4A891DA6D206005C2AC8 /* NSControl.swift in Sources */, 9AF0EA751D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, + 9A54A24F1DE123BD001739B3 /* Atomic.swift in Sources */, 9ADE4A911DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, 53A6BED21DD4BCA90016C058 /* MKMapView.swift in Sources */, 9A1D065B1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, diff --git a/ReactiveCocoa/Atomic.swift b/ReactiveCocoa/Atomic.swift new file mode 100644 index 0000000000..64cc9f0e57 --- /dev/null +++ b/ReactiveCocoa/Atomic.swift @@ -0,0 +1,72 @@ +import ReactiveSwift + +// Redefined in ReactiveCocoa internally for performance until whole-module +// optimization is extended to incorporate dependencies. + +final class PosixThreadMutex: NSLocking { + private var mutex = pthread_mutex_t() + + init() { + let result = pthread_mutex_init(&mutex, nil) + precondition(result == 0, "Failed to initialize mutex with error \(result).") + } + + deinit { + let result = pthread_mutex_destroy(&mutex) + precondition(result == 0, "Failed to destroy mutex with error \(result).") + } + + func lock() { + let result = pthread_mutex_lock(&mutex) + precondition(result == 0, "Failed to lock \(self) with error \(result).") + } + + func unlock() { + let result = pthread_mutex_unlock(&mutex) + precondition(result == 0, "Failed to unlock \(self) with error \(result).") + } +} + +/// An atomic variable. +internal final class Atomic: AtomicProtocol { + private let lock: PosixThreadMutex + private var _value: Value + + /// Initialize the variable with the given initial value. + /// + /// - parameters: + /// - value: Initial value for `self`. + public init(_ value: Value) { + _value = value + lock = PosixThreadMutex() + } + + /// Atomically modifies the variable. + /// + /// - parameters: + /// - action: A closure that takes the current value. + /// + /// - returns: The result of the action. + @discardableResult + public func modify(_ action: (inout Value) throws -> Result) rethrows -> Result { + lock.lock() + defer { lock.unlock() } + + return try action(&_value) + } + + /// Atomically perform an arbitrary action using the current value of the + /// variable. + /// + /// - parameters: + /// - action: A closure that takes the current value. + /// + /// - returns: The result of the action. + @discardableResult + public func withValue(_ action: (Value) throws -> Result) rethrows -> Result { + lock.lock() + defer { lock.unlock() } + + return try action(_value) + } +} diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index b99acf81a6..82c2a08031 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -54,15 +54,15 @@ private func setupInterception(_ object: NSObject, for selector: Selector) -> Si synchronized(subclass) { let isSwizzled = objc_getAssociatedObject(subclass, &isSwizzledKey) as! Bool? ?? false - let interopImpls: Box<[Selector: IMP]> - let signatureCache: Box<[Selector: AnyObject]> + let interopImpls: Atomic<[Selector: IMP]> + let signatureCache: Atomic<[Selector: AnyObject]> if isSwizzled { - interopImpls = objc_getAssociatedObject(subclass, &interopImplKey) as! Box<[Selector: IMP]> - signatureCache = objc_getAssociatedObject(subclass, &interceptedSelectorsKey) as! Box<[Selector: AnyObject]> + interopImpls = objc_getAssociatedObject(subclass, &interopImplKey) as! Atomic<[Selector: IMP]> + signatureCache = objc_getAssociatedObject(subclass, &interceptedSelectorsKey) as! Atomic<[Selector: AnyObject]> } else { - interopImpls = Box([:]) - signatureCache = Box([:]) + interopImpls = Atomic([:]) + signatureCache = Atomic([:]) objc_setAssociatedObject(subclass, &interopImplKey, interopImpls, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) objc_setAssociatedObject(subclass, &interceptedSelectorsKey, signatureCache, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) @@ -72,31 +72,27 @@ private func setupInterception(_ object: NSObject, for selector: Selector) -> Si setupMethodSignatureCaching(subclass, signatureCache) } - // Mutations to `interopImpls` and `signatureCache` must be made on a - // a copy and subsequently assigned back, so as to avoid partially updated - // state from being visible to the concurrent readers. - - if signatureCache.value[selector] == nil { - var signatures = signatureCache.value - signatures[selector] = NSMethodSignature.signature(withObjCTypes: typeEncoding) - signatureCache.value = signatures - } - - if interopImpls.value[selector] == nil { - let immediateMethod = class_getImmediateMethod(subclass, selector) - - let immediateImpl: IMP? = immediateMethod.flatMap { - // If the subclass is generated by external parties, e.g. KVO, and has an - // implementation for the selector, take a record of that implementation - // for interoperability. - let immediateImpl = method_getImplementation($0) - return immediateImpl.flatMap { $0 != _rac_objc_msgForward ? $0 : nil } + signatureCache.modify { signatures in + if signatures[selector] == nil { + signatures[selector] = NSMethodSignature.signature(withObjCTypes: typeEncoding) } + } - if let impl = immediateImpl { - var impls = interopImpls.value - impls[selector] = impl - interopImpls.value = impls + interopImpls.modify { impls in + if impls[selector] == nil { + let immediateMethod = class_getImmediateMethod(subclass, selector) + + let immediateImpl: IMP? = immediateMethod.flatMap { + // If the subclass is generated by external parties, e.g. KVO, and has an + // implementation for the selector, take a record of that implementation + // for interoperability. + let immediateImpl = method_getImplementation($0) + return immediateImpl.flatMap { $0 != _rac_objc_msgForward ? $0 : nil } + } + + if let impl = immediateImpl { + impls[selector] = impl + } } } } @@ -111,7 +107,7 @@ private func setupInterception(_ object: NSObject, for selector: Selector) -> Si } } -private func enableMessageForwarding(_ objcClass: AnyClass, _ interopImpls: Box<[Selector: IMP]>) { +private func enableMessageForwarding(_ objcClass: AnyClass, _ interopImpls: Atomic<[Selector: IMP]>) { // Set up a new version of -forwardInvocation:. // // If the selector has been passed to -rac_signalForSelector:, invoke @@ -144,7 +140,7 @@ private func enableMessageForwarding(_ objcClass: AnyClass, _ interopImpls: Box< let method = class_getInstanceMethod(perceivedClass, selector) let typeEncoding = method_getTypeEncoding(method) - if let interopImpl = interopImpls.value[selector] { + if let interopImpl = interopImpls.withValue({ $0[selector] }) { // `self` uses a runtime subclass generated by third-party APIs, and RAC // found an existing implementation for the selector at the setup time. // Call that implementation if it is not the ObjC message forwarder. @@ -189,11 +185,11 @@ private func enableMessageForwarding(_ objcClass: AnyClass, _ interopImpls: Box< ObjCMethodEncoding.forwardInvocation) } -private func setupMethodSignatureCaching(_ objcClass: AnyClass, _ signatureCache: Box<[Selector: AnyObject]>) { +private func setupMethodSignatureCaching(_ objcClass: AnyClass, _ signatureCache: Atomic<[Selector: AnyObject]>) { let perceivedClass: AnyClass = class_getSuperclass(objcClass) let newMethodSignatureForSelector: @convention(block) (NSObject, Selector) -> AnyObject? = { object, selector in - if let signature = signatureCache.value[selector] { + if let signature = signatureCache.withValue({ $0[selector] }) { return signature } @@ -222,15 +218,6 @@ private var interopImplKey = 0 private var interceptingStatesKey = 0 private var interceptedSelectorsKey = 0 -// A container to box value type collections. -private final class Box { - var value: Value - - init(_ value: Value) { - self.value = value - } -} - private func checkTypeEncoding(_ types: UnsafePointer) -> Bool { // Some types, including vector types, are not encoded. In these cases the // signature starts with the size of the argument frame. From 48b0f5a58f33140ad717a98104aa956570a9d8d8 Mon Sep 17 00:00:00 2001 From: Anders Date: Sun, 20 Nov 2016 02:37:28 +0100 Subject: [PATCH 0600/1028] Abolished the interop-implementation table. --- ReactiveCocoa/NSObject+Intercepting.swift | 45 +++++++++++------------ 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 82c2a08031..76748e06ef 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -36,6 +36,7 @@ extension Reactive where Base: NSObject { private func setupInterception(_ object: NSObject, for selector: Selector) -> Signal { let alias = selector.prefixing("rac0_") + let interopAlias = selector.prefixing("rac1_") guard let method = class_getInstanceMethod(object.objcClass, selector) else { fatalError("Selector `\(selector)` does not exist in class `\(String(describing: object.objcClass))`.") @@ -54,21 +55,17 @@ private func setupInterception(_ object: NSObject, for selector: Selector) -> Si synchronized(subclass) { let isSwizzled = objc_getAssociatedObject(subclass, &isSwizzledKey) as! Bool? ?? false - let interopImpls: Atomic<[Selector: IMP]> let signatureCache: Atomic<[Selector: AnyObject]> if isSwizzled { - interopImpls = objc_getAssociatedObject(subclass, &interopImplKey) as! Atomic<[Selector: IMP]> signatureCache = objc_getAssociatedObject(subclass, &interceptedSelectorsKey) as! Atomic<[Selector: AnyObject]> } else { - interopImpls = Atomic([:]) signatureCache = Atomic([:]) - objc_setAssociatedObject(subclass, &interopImplKey, interopImpls, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) objc_setAssociatedObject(subclass, &interceptedSelectorsKey, signatureCache, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) objc_setAssociatedObject(subclass, &isSwizzledKey, true, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - enableMessageForwarding(subclass, interopImpls) + enableMessageForwarding(subclass) setupMethodSignatureCaching(subclass, signatureCache) } @@ -78,21 +75,19 @@ private func setupInterception(_ object: NSObject, for selector: Selector) -> Si } } - interopImpls.modify { impls in - if impls[selector] == nil { - let immediateMethod = class_getImmediateMethod(subclass, selector) - - let immediateImpl: IMP? = immediateMethod.flatMap { - // If the subclass is generated by external parties, e.g. KVO, and has an - // implementation for the selector, take a record of that implementation - // for interoperability. - let immediateImpl = method_getImplementation($0) - return immediateImpl.flatMap { $0 != _rac_objc_msgForward ? $0 : nil } - } - - if let impl = immediateImpl { - impls[selector] = impl - } + if !class_respondsToSelector(subclass, interopAlias) { + let immediateMethod = class_getImmediateMethod(subclass, selector) + + let immediateImpl: IMP? = immediateMethod.flatMap { + // If the subclass is generated by external parties, e.g. KVO, and has an + // implementation for the selector, take a record of that implementation + // for interoperability. + let immediateImpl = method_getImplementation($0) + return immediateImpl.flatMap { $0 != _rac_objc_msgForward ? $0 : nil } + } + + if let impl = immediateImpl { + class_addMethod(subclass, interopAlias, impl, typeEncoding) } } } @@ -107,7 +102,7 @@ private func setupInterception(_ object: NSObject, for selector: Selector) -> Si } } -private func enableMessageForwarding(_ objcClass: AnyClass, _ interopImpls: Atomic<[Selector: IMP]>) { +private func enableMessageForwarding(_ objcClass: AnyClass) { // Set up a new version of -forwardInvocation:. // // If the selector has been passed to -rac_signalForSelector:, invoke @@ -125,6 +120,7 @@ private func enableMessageForwarding(_ objcClass: AnyClass, _ interopImpls: Atom let newForwardInvocation: ForwardInvocationImpl = { object, invocation in let selector = invocation.selector! let alias = selector.prefixing("rac0_") + let interopAlias = selector.prefixing("rac1_") defer { if let state = object.value(forAssociatedKey: alias.utf8Start) as! InterceptingState? { @@ -137,15 +133,16 @@ private func enableMessageForwarding(_ objcClass: AnyClass, _ interopImpls: Atom // all instances that had been applied `signalForSelector:` are // non-threadsafe as any mutable instances. - let method = class_getInstanceMethod(perceivedClass, selector) + let method = class_getInstanceMethod(perceivedClass, selector)! let typeEncoding = method_getTypeEncoding(method) - if let interopImpl = interopImpls.withValue({ $0[selector] }) { + if class_respondsToSelector(realClass, interopAlias) { // `self` uses a runtime subclass generated by third-party APIs, and RAC // found an existing implementation for the selector at the setup time. // Call that implementation if it is not the ObjC message forwarder. // // The IMP swapping would flush the IMP cache. + let interopImpl = class_getMethodImplementation(realClass, interopAlias) let previousImpl = class_replaceMethod(realClass, selector, interopImpl, typeEncoding) invocation.invoke() _ = class_replaceMethod(realClass, selector, previousImpl, typeEncoding) @@ -153,7 +150,7 @@ private func enableMessageForwarding(_ objcClass: AnyClass, _ interopImpls: Atom return } - if let impl = method.map(method_getImplementation), impl != _rac_objc_msgForward { + if let impl = method_getImplementation(method), impl != _rac_objc_msgForward { // The stated class has an implementation of the selector. Call that // implementation if it is not the ObjC message forwarder. From 7ce8d92d583cf6ecb1018fecffdae61b2dfa4a1e Mon Sep 17 00:00:00 2001 From: Anders Date: Sun, 20 Nov 2016 02:57:23 +0100 Subject: [PATCH 0601/1028] Selector alias caching. --- ReactiveCocoa.xcodeproj/project.pbxproj | 26 +++++++++++----- ReactiveCocoa/NSObject+Intercepting.swift | 8 ++--- ReactiveCocoa/ObjC+Selector.swift | 38 +++++++++++++++++++++-- ReactiveCocoa/ThreadLocal.swift | 27 ++++++++++++++++ 4 files changed, 85 insertions(+), 14 deletions(-) create mode 100644 ReactiveCocoa/ThreadLocal.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 2875697d03..51c76da4ee 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -114,6 +114,13 @@ 9A1E72BA1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; 9A1E72BB1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; 9A1E72BC1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; + 9A24A83F1DE1386800987AF9 /* ThreadLocal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A24A83E1DE1386800987AF9 /* ThreadLocal.swift */; }; + 9A24A8401DE1386800987AF9 /* ThreadLocal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A24A83E1DE1386800987AF9 /* ThreadLocal.swift */; }; + 9A24A8411DE1386800987AF9 /* ThreadLocal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A24A83E1DE1386800987AF9 /* ThreadLocal.swift */; }; + 9A24A8421DE1386800987AF9 /* ThreadLocal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A24A83E1DE1386800987AF9 /* ThreadLocal.swift */; }; + 9A24A8451DE142A400987AF9 /* SwizzlingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A24A8431DE1429600987AF9 /* SwizzlingSpec.swift */; }; + 9A24A8461DE142A500987AF9 /* SwizzlingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A24A8431DE1429600987AF9 /* SwizzlingSpec.swift */; }; + 9A24A8471DE142A600987AF9 /* SwizzlingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A24A8431DE1429600987AF9 /* SwizzlingSpec.swift */; }; 9A2E425E1DAA6737006D909F /* CocoaTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */; }; 9A2E425F1DAA6737006D909F /* CocoaTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */; }; 9A2E42601DAA6737006D909F /* CocoaTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */; }; @@ -152,9 +159,6 @@ 9AA0BD821DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD801DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift */; }; 9AA0BD831DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD801DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift */; }; 9AA0BD841DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD801DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift */; }; - 9AA0BD861DDE0A8B00531FCF /* SwizzlingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD851DDE0A8B00531FCF /* SwizzlingSpec.swift */; }; - 9AA0BD871DDE0A8B00531FCF /* SwizzlingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD851DDE0A8B00531FCF /* SwizzlingSpec.swift */; }; - 9AA0BD881DDE0A8B00531FCF /* SwizzlingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD851DDE0A8B00531FCF /* SwizzlingSpec.swift */; }; 9AA0BD8A1DDE153A00531FCF /* ObjC+Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD891DDE153A00531FCF /* ObjC+Constants.swift */; }; 9AA0BD8B1DDE153A00531FCF /* ObjC+Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD891DDE153A00531FCF /* ObjC+Constants.swift */; }; 9AA0BD8C1DDE153A00531FCF /* ObjC+Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD891DDE153A00531FCF /* ObjC+Constants.swift */; }; @@ -354,6 +358,8 @@ 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Intercepting.swift"; sourceTree = ""; }; 9A1D06751D9415FB00ACF44C /* ObjCRuntimeAliases.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ObjCRuntimeAliases.h; sourceTree = ""; }; 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyValueObservingSpec.swift; sourceTree = ""; }; + 9A24A83E1DE1386800987AF9 /* ThreadLocal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThreadLocal.swift; sourceTree = ""; }; + 9A24A8431DE1429600987AF9 /* SwizzlingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwizzlingSpec.swift; sourceTree = ""; }; 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaTarget.swift; sourceTree = ""; }; 9A54A2101DDF5B4D001739B3 /* InterceptingPerformanceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterceptingPerformanceTests.swift; sourceTree = ""; }; 9A54A21A1DE00D09001739B3 /* ObjC+Selector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObjC+Selector.swift"; sourceTree = ""; }; @@ -366,7 +372,6 @@ 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssociationSpec.swift; sourceTree = ""; }; 9AA0BD771DDE03DE00531FCF /* ObjC+Runtime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObjC+Runtime.swift"; sourceTree = ""; }; 9AA0BD801DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObjC+RuntimeSubclassing.swift"; sourceTree = ""; }; - 9AA0BD851DDE0A8B00531FCF /* SwizzlingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwizzlingSpec.swift; sourceTree = ""; }; 9AA0BD891DDE153A00531FCF /* ObjC+Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObjC+Constants.swift"; sourceTree = ""; }; 9AA0BD8E1DDE29F800531FCF /* NSObject+ObjCRuntime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+ObjCRuntime.swift"; sourceTree = ""; }; 9AA0BD971DDE7A2200531FCF /* ObjCRuntimeAliases.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ObjCRuntimeAliases.m; sourceTree = ""; }; @@ -688,6 +693,7 @@ 9A54A21A1DE00D09001739B3 /* ObjC+Selector.swift */, 9AA0BD801DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift */, 9AA0BD771DDE03DE00531FCF /* ObjC+Runtime.swift */, + 9A24A83E1DE1386800987AF9 /* ThreadLocal.swift */, 9A1D05E91D93E9F100ACF44C /* AppKit */, 9A1D05EB1D93E9F100ACF44C /* UIKit */, 538DCB761DCA5E1600332880 /* Shared */, @@ -723,7 +729,7 @@ 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */, 9A54A2101DDF5B4D001739B3 /* InterceptingPerformanceTests.swift */, 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */, - 9AA0BD851DDE0A8B00531FCF /* SwizzlingSpec.swift */, + 9A24A8431DE1429600987AF9 /* SwizzlingSpec.swift */, D04725FA19E49ED7006002AA /* Supporting Files */, ); path = ReactiveCocoaTests; @@ -1128,6 +1134,7 @@ 9ADFE5A81DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */, CD0C45E11CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9AA0BD921DDE29F800531FCF /* NSObject+ObjCRuntime.swift in Sources */, + 9A24A8421DE1386800987AF9 /* ThreadLocal.swift in Sources */, 9A1D061A1D93EA0100ACF44C /* UIProgressView.swift in Sources */, 9AA0BD7F1DDE03DE00531FCF /* ObjC+Runtime.swift in Sources */, 9A2E42611DAA6737006D909F /* CocoaTarget.swift in Sources */, @@ -1153,6 +1160,7 @@ 9A1D06451D93EA7E00ACF44C /* UIImageViewSpec.swift in Sources */, 9A1D06551D93EA7E00ACF44C /* UITextViewSpec.swift in Sources */, 9A1D063B1D93EA7E00ACF44C /* UIButtonSpec.swift in Sources */, + 9A24A8471DE142A600987AF9 /* SwizzlingSpec.swift in Sources */, 9A1D064B1D93EA7E00ACF44C /* UISegmentedControlSpec.swift in Sources */, 9A1D063F1D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift in Sources */, 9A1D06371D93EA7E00ACF44C /* UIActivityIndicatorViewSpec.swift in Sources */, @@ -1167,7 +1175,6 @@ 4ABEFE221DCFCF0A0066A8C2 /* UITableViewSpec.swift in Sources */, 9A1D06491D93EA7E00ACF44C /* UIProgressViewSpec.swift in Sources */, 4ABEFE291DCFCFA90066A8C2 /* UICollectionViewSpec.swift in Sources */, - 9AA0BD881DDE0A8B00531FCF /* SwizzlingSpec.swift in Sources */, 9A54A2131DDF5B4D001739B3 /* InterceptingPerformanceTests.swift in Sources */, BEE020661D637B0000DF261F /* TestError.swift in Sources */, 9A1D06531D93EA7E00ACF44C /* UITextFieldSpec.swift in Sources */, @@ -1187,6 +1194,7 @@ 9AA0BD8C1DDE153A00531FCF /* ObjC+Constants.swift in Sources */, 9ADFE5A71DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */, 9ADE4A7E1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, + 9A24A8411DE1386800987AF9 /* ThreadLocal.swift in Sources */, 9AA0BD831DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift in Sources */, CD0C45E01CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9AA0BD911DDE29F800531FCF /* NSObject+ObjCRuntime.swift in Sources */, @@ -1227,6 +1235,7 @@ 9A1D065B1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, 4A0E10FF1D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, 9AA0BD811DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift in Sources */, + 9A24A83F1DE1386800987AF9 /* ThreadLocal.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1238,13 +1247,13 @@ 9A54A2111DDF5B4D001739B3 /* InterceptingPerformanceTests.swift in Sources */, 538DCB7D1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift in Sources */, 9ADE4A8F1DA6DA20005C2AC8 /* NSControlSpec.swift in Sources */, - 9AA0BD861DDE0A8B00531FCF /* SwizzlingSpec.swift in Sources */, D0A2260E1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */, 53A6BED61DD4BD2C0016C058 /* MKMapViewSpec.swift in Sources */, 9ADFE5A11DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */, 4ABEFE331DCFD0630066A8C2 /* NSCollectionViewSpec.swift in Sources */, B696FB811A7640C00075236D /* TestError.swift in Sources */, BFA6B94D1A7604D400C846D1 /* SignalProducerNimbleMatchers.swift in Sources */, + 9A24A8451DE142A400987AF9 /* SwizzlingSpec.swift in Sources */, 9A6AAA2B1DB8F85C0013AAEA /* ReusableComponentsSpec.swift in Sources */, CD8401831CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, 9A9DFEE91DA7EFB60039EE1B /* AssociationSpec.swift in Sources */, @@ -1264,6 +1273,7 @@ 9A1D060E1D93EA0000ACF44C /* UITextView.swift in Sources */, 9A1D06061D93EA0000ACF44C /* UIImageView.swift in Sources */, 9AF0EA761D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, + 9A24A8401DE1386800987AF9 /* ThreadLocal.swift in Sources */, CD0C45DF1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9AE7C2A51DDD7F5100F7534C /* ObjC+Messages.swift in Sources */, 9A2E425F1DAA6737006D909F /* CocoaTarget.swift in Sources */, @@ -1308,12 +1318,12 @@ BFA6B94E1A7604D500C846D1 /* SignalProducerNimbleMatchers.swift in Sources */, B696FB821A7640C00075236D /* TestError.swift in Sources */, 9A1D064C1D93EA7E00ACF44C /* UISwitchSpec.swift in Sources */, - 9AA0BD871DDE0A8B00531FCF /* SwizzlingSpec.swift in Sources */, 4ABEFE281DCFCFA90066A8C2 /* UICollectionViewSpec.swift in Sources */, 9A1D06441D93EA7E00ACF44C /* UIImageViewSpec.swift in Sources */, 9A1D06541D93EA7E00ACF44C /* UITextViewSpec.swift in Sources */, 9A1D063A1D93EA7E00ACF44C /* UIButtonSpec.swift in Sources */, 9A1D064A1D93EA7E00ACF44C /* UISegmentedControlSpec.swift in Sources */, + 9A24A8461DE142A500987AF9 /* SwizzlingSpec.swift in Sources */, 9A1D063E1D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift in Sources */, 4191394E1DBA01A00043C9D1 /* UIGestureRecognizerSpec.swift in Sources */, 531866FA1DD7925600D1285F /* UIStepperSpec.swift in Sources */, diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 76748e06ef..dd1cd4ca0f 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -35,8 +35,8 @@ extension Reactive where Base: NSObject { } private func setupInterception(_ object: NSObject, for selector: Selector) -> Signal { - let alias = selector.prefixing("rac0_") - let interopAlias = selector.prefixing("rac1_") + let alias = selector.alias + let interopAlias = selector.interopAlias guard let method = class_getInstanceMethod(object.objcClass, selector) else { fatalError("Selector `\(selector)` does not exist in class `\(String(describing: object.objcClass))`.") @@ -119,8 +119,8 @@ private func enableMessageForwarding(_ objcClass: AnyClass) { typealias ForwardInvocationImpl = @convention(block) (NSObject, AnyObject) -> Void let newForwardInvocation: ForwardInvocationImpl = { object, invocation in let selector = invocation.selector! - let alias = selector.prefixing("rac0_") - let interopAlias = selector.prefixing("rac1_") + let alias = selector.alias + let interopAlias = selector.interopAlias defer { if let state = object.value(forAssociatedKey: alias.utf8Start) as! InterceptingState? { diff --git a/ReactiveCocoa/ObjC+Selector.swift b/ReactiveCocoa/ObjC+Selector.swift index 8f3b323608..64c8873841 100644 --- a/ReactiveCocoa/ObjC+Selector.swift +++ b/ReactiveCocoa/ObjC+Selector.swift @@ -3,9 +3,43 @@ extension Selector { return unsafeBitCast(self, to: UnsafePointer.self) } - internal func prefixing(_ prefix: StaticString) -> Selector { - assert(prefix.isASCII) + final class Box { + var selectors: [Selector: Selector] = [:] + } + + internal var alias: Selector { + enum Static { + static let cache = ThreadLocal { Box() } + } + + let localCache = Static.cache.local + + if let selector = localCache.selectors[self] { + return selector + } else { + let selector = prefixing("rac0_") + localCache.selectors[self] = selector + return selector + } + } + internal var interopAlias: Selector { + enum Static { + static let cache = ThreadLocal { Box() } + } + + let localCache = Static.cache.local + + if let selector = localCache.selectors[self] { + return selector + } else { + let selector = prefixing("rac1_") + localCache.selectors[self] = selector + return selector + } + } + + internal func prefixing(_ prefix: StaticString) -> Selector { let length = Int(strlen(utf8Start)) let prefixedLength = length + prefix.utf8CodeUnitCount diff --git a/ReactiveCocoa/ThreadLocal.swift b/ReactiveCocoa/ThreadLocal.swift new file mode 100644 index 0000000000..2786451402 --- /dev/null +++ b/ReactiveCocoa/ThreadLocal.swift @@ -0,0 +1,27 @@ +internal final class ThreadLocal { + private let key: pthread_key_t + private let initializer: () -> Object + + var local: Object { + get { + if let pointer = pthread_getspecific(key) { + return Unmanaged.fromOpaque(pointer).takeUnretainedValue() + } else { + let object = initializer() + pthread_setspecific(key, Unmanaged.passRetained(object).toOpaque()) + return object + } + } + } + + init(initializer: @escaping () -> Object) { + var key = pthread_key_t() + let status = pthread_key_create(&key) { pointer in + Unmanaged.fromOpaque(pointer).release() + } + precondition(status == 0) + + self.key = key + self.initializer = initializer + } +} From 4686919b74bc2bc24581a02212dc39392402055e Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sun, 20 Nov 2016 03:35:42 +0100 Subject: [PATCH 0602/1028] Fixed a typo in a code snippet of the readme. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2659a71aa7..31f3b07b02 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ __ReactiveCocoa__ wraps various aspects of Cocoa frameworks with the declarative ```swift // Bind the `name` property of `person` to the text value of an `UILabel`. - nameLabel.text <~ person.name + nameLabel.reactive.text <~ person.name ``` 1. **Controls and User Interactions** From 4681778e602fa758de4aeea674f5bf7f70b478f9 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sun, 20 Nov 2016 03:36:48 +0100 Subject: [PATCH 0603/1028] Fixed a typo in a code snippet of the changelog. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa88f581e3..5e00fb1b89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -110,7 +110,7 @@ UI components now expose a collection of binding targets to which can be bound f ```swift // Bind the `name` property of `person` to the text value of an `UILabel`. - nameLabel.text <~ person.name + nameLabel.reactive.text <~ person.name ``` 1. **Controls and User Interactions** From f9357044303d7af4b86929443affbe9524c6c004 Mon Sep 17 00:00:00 2001 From: Anders Date: Sun, 20 Nov 2016 05:48:17 +0100 Subject: [PATCH 0604/1028] Removed unnecessary code and variables. --- ReactiveCocoa/NSObject+Intercepting.swift | 2 -- ReactiveCocoa/ObjC+Constants.swift | 1 - ReactiveCocoa/ObjC+RuntimeSubclassing.swift | 12 ------------ ReactiveCocoa/ObjCRuntimeAliases.h | 1 - ReactiveCocoa/ObjCRuntimeAliases.m | 1 - 5 files changed, 17 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index dd1cd4ca0f..e60cc90502 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -211,8 +211,6 @@ private final class InterceptingState { } private var isSwizzledKey = 0 -private var interopImplKey = 0 -private var interceptingStatesKey = 0 private var interceptedSelectorsKey = 0 private func checkTypeEncoding(_ types: UnsafePointer) -> Bool { diff --git a/ReactiveCocoa/ObjC+Constants.swift b/ReactiveCocoa/ObjC+Constants.swift index 0cbf831224..e11b89e7bf 100644 --- a/ReactiveCocoa/ObjC+Constants.swift +++ b/ReactiveCocoa/ObjC+Constants.swift @@ -10,7 +10,6 @@ internal enum ObjCMethodEncoding { static let forwardInvocation = extract("v@:@") static let methodSignatureForSelector = extract("v@::") static let getClass = extract("#@:") - static let repsondsToSelector = extract("c@::") private static func extract(_ string: StaticString) -> UnsafePointer { return UnsafeRawPointer(string.utf8Start).assumingMemoryBound(to: CChar.self) diff --git a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift index 5c6a964566..4a810518e4 100644 --- a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift +++ b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift @@ -1,7 +1,6 @@ import ReactiveSwift private var isSwizzledKey = 0 -private var test = 0 /// ISA-swizzle the class of the supplied instance. /// @@ -28,20 +27,9 @@ internal func swizzleClass(_ instance: NSObject) -> AnyClass { // dynamic subclass or something else that we shouldn't subclass at runtime. synchronized(realClass) { let isSwizzled = objc_getAssociatedObject(realClass, &isSwizzledKey) as! Bool? ?? false - if !isSwizzled { replaceGetClass(in: realClass, decoy: perceivedClass) objc_setAssociatedObject(realClass, &isSwizzledKey, true, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - - let token = Lifetime.Token() - objc_setAssociatedObject(realClass, &test, token, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - - Lifetime(token).ended.observeCompleted { - - let a = 1 - _ = a - - } } } diff --git a/ReactiveCocoa/ObjCRuntimeAliases.h b/ReactiveCocoa/ObjCRuntimeAliases.h index 1770c68163..f44be5fe95 100644 --- a/ReactiveCocoa/ObjCRuntimeAliases.h +++ b/ReactiveCocoa/ObjCRuntimeAliases.h @@ -2,4 +2,3 @@ #import const IMP _rac_objc_msgForward; -const IMP _rac_objc_msgSendSuper; diff --git a/ReactiveCocoa/ObjCRuntimeAliases.m b/ReactiveCocoa/ObjCRuntimeAliases.m index 675c5a6696..2bb9be9de3 100644 --- a/ReactiveCocoa/ObjCRuntimeAliases.m +++ b/ReactiveCocoa/ObjCRuntimeAliases.m @@ -2,4 +2,3 @@ #import const IMP _rac_objc_msgForward = _objc_msgForward; -const IMP _rac_objc_msgSendSuper = (typeof(IMP)) objc_msgSendSuper; From fcfe991056ac0f61b576957aaa1e84b6a56c2044 Mon Sep 17 00:00:00 2001 From: Anders Date: Sun, 20 Nov 2016 06:56:53 +0100 Subject: [PATCH 0605/1028] Improved the documentation with minor refactoring. --- ReactiveCocoa/NSObject+Intercepting.swift | 110 +++++++++++++------- ReactiveCocoa/ObjC+RuntimeSubclassing.swift | 7 +- ReactiveCocoa/ObjC+Selector.swift | 14 ++- ReactiveCocoa/ThreadLocal.swift | 12 +++ 4 files changed, 102 insertions(+), 41 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index e60cc90502..1e61ee7c86 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -6,6 +6,8 @@ extension Reactive where Base: NSObject { /// Create a signal which sends a `next` event at the end of every invocation /// of `selector` on the object. /// + /// It completes when the object deinitializes. + /// /// - note: Observers to the resulting signal should not call the method /// specified by the selector. /// @@ -21,6 +23,8 @@ extension Reactive where Base: NSObject { /// Create a signal which sends a `next` event, containing an array of bridged /// arguments, at the end of every invocation of `selector` on the object. /// + /// It completes when the object deinitializes. + /// /// - note: Observers to the resulting signal should not call the method /// specified by the selector. /// @@ -34,6 +38,15 @@ extension Reactive where Base: NSObject { } } +/// Setup the method interception. +/// +/// - parameters: +/// - object: The object to be intercepted. +/// - selector: The selector of the method to be intercepted. +/// +/// - returns: +/// A signal that sends the corresponding `NSInvocation` after every +/// invocation of the method. private func setupInterception(_ object: NSObject, for selector: Selector) -> Signal { let alias = selector.alias let interopAlias = selector.interopAlias @@ -79,14 +92,18 @@ private func setupInterception(_ object: NSObject, for selector: Selector) -> Si let immediateMethod = class_getImmediateMethod(subclass, selector) let immediateImpl: IMP? = immediateMethod.flatMap { - // If the subclass is generated by external parties, e.g. KVO, and has an - // implementation for the selector, take a record of that implementation - // for interoperability. let immediateImpl = method_getImplementation($0) return immediateImpl.flatMap { $0 != _rac_objc_msgForward ? $0 : nil } } if let impl = immediateImpl { + // If an immediate implementation of the selector is found in the + // runtime subclass the first time the selector is intercepted, + // preserve the implementation. + // + // Example: KVO setters if the instance is swizzled by KVO before RAC + // does. + class_addMethod(subclass, interopAlias, impl, typeEncoding) } } @@ -102,19 +119,12 @@ private func setupInterception(_ object: NSObject, for selector: Selector) -> Si } } -private func enableMessageForwarding(_ objcClass: AnyClass) { - // Set up a new version of -forwardInvocation:. - // - // If the selector has been passed to -rac_signalForSelector:, invoke - // the aliased method, and forward the arguments to any attached signals. - // - // If the selector has not been passed to -rac_signalForSelector:, - // invoke any existing implementation of -forwardInvocation:. If there - // was no existing implementation, throw an unrecognized selector - // exception. - - let realClass: AnyClass = objcClass - let perceivedClass: AnyClass = class_getSuperclass(objcClass) +/// Swizzle `realClass` to enable message forwarding for method interception. +/// +/// - parameters: +/// - realClass: The runtime subclass to be swizzled. +private func enableMessageForwarding(_ realClass: AnyClass) { + let perceivedClass: AnyClass = class_getSuperclass(realClass) typealias ForwardInvocationImpl = @convention(block) (NSObject, AnyObject) -> Void let newForwardInvocation: ForwardInvocationImpl = { object, invocation in @@ -128,20 +138,19 @@ private func enableMessageForwarding(_ objcClass: AnyClass) { } } - // RAC exchanges implementations at runtime so as to invoke the desired - // version without using fragile dependencies like libffi. This means - // all instances that had been applied `signalForSelector:` are - // non-threadsafe as any mutable instances. - let method = class_getInstanceMethod(perceivedClass, selector)! let typeEncoding = method_getTypeEncoding(method) if class_respondsToSelector(realClass, interopAlias) { - // `self` uses a runtime subclass generated by third-party APIs, and RAC - // found an existing implementation for the selector at the setup time. - // Call that implementation if it is not the ObjC message forwarder. + // RAC has preserved an immediate implementation found in the runtime + // subclass that was supplied by an external party. // - // The IMP swapping would flush the IMP cache. + // As the KVO setter relies on the selector to work, it has to be invoked + // by swapping in the preserved implementation and restore to the message + // forwarder afterwards. + // + // However, the IMP cache would be thrashed due to the swapping. + let interopImpl = class_getMethodImplementation(realClass, interopAlias) let previousImpl = class_replaceMethod(realClass, selector, interopImpl, typeEncoding) invocation.invoke() @@ -151,14 +160,17 @@ private func enableMessageForwarding(_ objcClass: AnyClass) { } if let impl = method_getImplementation(method), impl != _rac_objc_msgForward { - // The stated class has an implementation of the selector. Call that - // implementation if it is not the ObjC message forwarder. + // The perceived class, or its ancestors, responds to the selector. + // + // The implementation is invoked through the selector alias, which + // reflects the latest implementation of the selector in the perceived + // class. - // Update the alias only if the implementation has been changed, so as to - // avoid thrashing the IMP cache. let method = class_getImmediateMethod(realClass, alias) if method == nil || method_getImplementation(method!) != impl { + // Update the alias if and only if the implementation has changed, so as + // to avoid thrashing the IMP cache. _ = class_replaceMethod(realClass, alias, impl, typeEncoding) } @@ -168,22 +180,29 @@ private func enableMessageForwarding(_ objcClass: AnyClass) { return } - // Forward the invocation to the closest `forwardInvocation:` in the - // inheritance hierarchy. + // Forward the invocation to the closest `forwardInvocation(_:)` in the + // inheritance hierarchy, or the default handler returned by the runtime + // if it finds no implementation. typealias SuperForwardInvocation = @convention(c) (AnyObject, Selector, AnyObject) -> Void let impl = class_getMethodImplementation(perceivedClass, ObjCSelector.forwardInvocation) let forwardInvocation = unsafeBitCast(impl, to: SuperForwardInvocation.self) forwardInvocation(object, ObjCSelector.forwardInvocation, invocation) } - _ = class_replaceMethod(objcClass, + _ = class_replaceMethod(realClass, ObjCSelector.forwardInvocation, imp_implementationWithBlock(newForwardInvocation as Any), ObjCMethodEncoding.forwardInvocation) } -private func setupMethodSignatureCaching(_ objcClass: AnyClass, _ signatureCache: Atomic<[Selector: AnyObject]>) { - let perceivedClass: AnyClass = class_getSuperclass(objcClass) +/// Swizzle `realClass` to accelerate the method signature retrieval, using a +/// signature cache that covers all known intercepted selectors of `realClass`. +/// +/// - parameters: +/// - realClass: The runtime subclass to be swizzled. +/// - signatureCache: The method signature cache. +private func setupMethodSignatureCaching(_ realClass: AnyClass, _ signatureCache: Atomic<[Selector: AnyObject]>) { + let perceivedClass: AnyClass = class_getSuperclass(realClass) let newMethodSignatureForSelector: @convention(block) (NSObject, Selector) -> AnyObject? = { object, selector in if let signature = signatureCache.withValue({ $0[selector] }) { @@ -196,15 +215,20 @@ private func setupMethodSignatureCaching(_ objcClass: AnyClass, _ signatureCache return methodSignatureForSelector(object, ObjCSelector.methodSignatureForSelector, selector) } - _ = class_replaceMethod(objcClass, + _ = class_replaceMethod(realClass, ObjCSelector.methodSignatureForSelector, imp_implementationWithBlock(newMethodSignatureForSelector as Any), ObjCMethodEncoding.methodSignatureForSelector) } +/// The state of an intercepted method specific to an instance. private final class InterceptingState { let (signal, observer) = Signal.pipe() + /// Initialize a state specific to an instance. + /// + /// - parameters: + /// - lifetime: The lifetime of the instance. init(lifetime: Lifetime) { lifetime.ended.observeCompleted(observer.sendCompleted) } @@ -213,6 +237,13 @@ private final class InterceptingState { private var isSwizzledKey = 0 private var interceptedSelectorsKey = 0 +/// Assert that the method does not contain types that cannot be intercepted. +/// +/// - parameters: +/// - types: The type encoding C string of the method. +/// +/// - returns: +/// `true`. private func checkTypeEncoding(_ types: UnsafePointer) -> Bool { // Some types, including vector types, are not encoded. In these cases the // signature starts with the size of the argument frame. @@ -228,6 +259,13 @@ private func checkTypeEncoding(_ types: UnsafePointer) -> Bool { return true } +/// Extract the arguments of an `NSInvocation` as an array of objects. +/// +/// - parameters: +/// - invocation: The `NSInvocation` to unpack. +/// +/// - returns: +/// An array of objects. private func unpackInvocation(_ invocation: AnyObject) -> [Any?] { let invocation = invocation as AnyObject let methodSignature = invocation.objcMethodSignature! @@ -236,7 +274,7 @@ private func unpackInvocation(_ invocation: AnyObject) -> [Any?] { var bridged = [Any?]() bridged.reserveCapacity(Int(count - 2)) - // Ignore `self` and `_cmd`. + // Ignore `self` and `_cmd` at index 0 and 1. for position in 2 ..< count { let rawEncoding = methodSignature.argumentType(at: position) let encoding = ObjCTypeEncoding(rawValue: rawEncoding.pointee) ?? .undefined diff --git a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift index 4a810518e4..96f038fd9d 100644 --- a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift +++ b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift @@ -57,6 +57,11 @@ private func subclassName(of class: AnyClass) -> String { return String(cString: class_getName(`class`)).appending("_RACSwift") } +/// Swizzle the `-class` and `+class` methods. +/// +/// - parameters: +/// - class: The class to swizzle. +/// - perceivedClass: The class to be reported by the methods. private func replaceGetClass(in class: AnyClass, decoy perceivedClass: AnyClass) { let getClass: @convention(block) (Any) -> AnyClass = { _ in return perceivedClass @@ -64,13 +69,11 @@ private func replaceGetClass(in class: AnyClass, decoy perceivedClass: AnyClass) let impl = imp_implementationWithBlock(getClass as Any) - // Swizzle `-class`. _ = class_replaceMethod(`class`, ObjCSelector.getClass, impl, ObjCMethodEncoding.getClass) - // Swizzle `+class`. _ = class_replaceMethod(object_getClass(`class`), ObjCSelector.getClass, impl, diff --git a/ReactiveCocoa/ObjC+Selector.swift b/ReactiveCocoa/ObjC+Selector.swift index 64c8873841..68797d7ff4 100644 --- a/ReactiveCocoa/ObjC+Selector.swift +++ b/ReactiveCocoa/ObjC+Selector.swift @@ -1,15 +1,19 @@ extension Selector { + /// `self` as a pointer. It is uniqued across instances, similar to + /// `StaticString`. internal var utf8Start: UnsafePointer { return unsafeBitCast(self, to: UnsafePointer.self) } - final class Box { + /// Selector alias cache. + final class Cache { var selectors: [Selector: Selector] = [:] } + /// An alias of `self`, used in method interception. internal var alias: Selector { enum Static { - static let cache = ThreadLocal { Box() } + static let cache = ThreadLocal { Cache() } } let localCache = Static.cache.local @@ -23,9 +27,12 @@ extension Selector { } } + /// An alias of `self`, used in method interception specifically for + /// preserving (if found) an immediate implementation of `self` in the + /// runtime subclass. internal var interopAlias: Selector { enum Static { - static let cache = ThreadLocal { Box() } + static let cache = ThreadLocal { Cache() } } let localCache = Static.cache.local @@ -39,6 +46,7 @@ extension Selector { } } + /// internal func prefixing(_ prefix: StaticString) -> Selector { let length = Int(strlen(utf8Start)) let prefixedLength = length + prefix.utf8CodeUnitCount diff --git a/ReactiveCocoa/ThreadLocal.swift b/ReactiveCocoa/ThreadLocal.swift index 2786451402..70d311da62 100644 --- a/ReactiveCocoa/ThreadLocal.swift +++ b/ReactiveCocoa/ThreadLocal.swift @@ -1,7 +1,14 @@ +/// A lazily-initialized container that maintains thread-specific instances of +/// `Object`. +/// +/// When being accessed via `local`, if no existing instance of `Object` is +/// found in the container for the current thread, it creates an instance using +/// the supplied initializer. internal final class ThreadLocal { private let key: pthread_key_t private let initializer: () -> Object + /// The instance of `Object` specific to the current thread. var local: Object { get { if let pointer = pthread_getspecific(key) { @@ -14,6 +21,11 @@ internal final class ThreadLocal { } } + /// Initialize a container that maintains thread-specific instances of + /// `Object`. + /// + /// - parameters: + /// - initializer: The closure that creates an instance of `Object`. init(initializer: @escaping () -> Object) { var key = pthread_key_t() let status = pthread_key_create(&key) { pointer in From 3862b090a52b230e8ed59246b73ede5b9d9a83f4 Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 18 Nov 2016 09:40:59 +0100 Subject: [PATCH 0606/1028] Add sandboxes. --- .travis.yml | 1 + .../Sandbox.xcplaygroundpage/Contents.swift | 25 +++++++++++++++++++ .../Sources/PlaygroundUtility.swift | 10 ++++++++ .../contents.xcplayground | 2 ++ .../Sandbox.xcplaygroundpage/Contents.swift | 25 +++++++++++++++++++ .../Sources/PlaygroundUtility.swift | 10 ++++++++ .../contents.xcplayground | 2 ++ .../contents.xcworkspacedata | 6 +++++ script/validate-playground.sh | 7 +++++- 9 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 ReactiveCocoa-iOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift create mode 100644 ReactiveCocoa-iOS.playground/Sources/PlaygroundUtility.swift create mode 100644 ReactiveCocoa-iOS.playground/contents.xcplayground create mode 100644 ReactiveCocoa-macOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift create mode 100644 ReactiveCocoa-macOS.playground/Sources/PlaygroundUtility.swift create mode 100644 ReactiveCocoa-macOS.playground/contents.xcplayground diff --git a/.travis.yml b/.travis.yml index aa4511bbc1..a5b4321582 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,7 @@ matrix: - XCODE_ACTION="build test" - XCODE_DESTINATION="arch=x86_64" - XCODE_PLAYGROUND_TARGET="x86_64-apple-macosx10.10" + - PLAYGROUND="ReactiveCocoa-macOS.playground" - xcode_scheme: ReactiveCocoa-iOS env: - XCODE_SDK=iphonesimulator diff --git a/ReactiveCocoa-iOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift b/ReactiveCocoa-iOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000000..6b88602528 --- /dev/null +++ b/ReactiveCocoa-iOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift @@ -0,0 +1,25 @@ +/*: +> ## IMPORTANT: To use `ReactiveCocoa-iOS.playground`, please: + +1. Retrieve the project dependencies using one of the following terminal commands from the ReactiveCocoa project root directory: +- `script/bootstrap` +**OR**, if you have [Carthage](https://github.com/Carthage/Carthage) installed +- `carthage checkout --no-use-binaries` +1. Open `ReactiveCocoa.xcworkspace` +1. Build `Result-iOS` scheme +1. Build `ReactiveSwift-iOS` scheme +1. Build `ReactiveCocoa-iOS` scheme +1. Finally open the `ReactiveCocoa-iOS.playground` +1. Choose `View > Show Debug Area` +*/ + +import Result +import ReactiveCocoa +import ReactiveSwift +import UIKit + +/*: +## Sandbox + +A place where you can build your sand castles 🏖. +*/ diff --git a/ReactiveCocoa-iOS.playground/Sources/PlaygroundUtility.swift b/ReactiveCocoa-iOS.playground/Sources/PlaygroundUtility.swift new file mode 100644 index 0000000000..282b07f49e --- /dev/null +++ b/ReactiveCocoa-iOS.playground/Sources/PlaygroundUtility.swift @@ -0,0 +1,10 @@ +import Foundation + +public func scopedExample(_ exampleDescription: String, _ action: () -> Void) { + print("\n--- \(exampleDescription) ---\n") + action() +} + +public enum PlaygroundError: Error { + case example(String) +} diff --git a/ReactiveCocoa-iOS.playground/contents.xcplayground b/ReactiveCocoa-iOS.playground/contents.xcplayground new file mode 100644 index 0000000000..24cafab2f8 --- /dev/null +++ b/ReactiveCocoa-iOS.playground/contents.xcplayground @@ -0,0 +1,2 @@ + + diff --git a/ReactiveCocoa-macOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift b/ReactiveCocoa-macOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000000..8543a8d1fe --- /dev/null +++ b/ReactiveCocoa-macOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift @@ -0,0 +1,25 @@ +/*: +> ## IMPORTANT: To use `ReactiveCocoa-macOS.playground`, please: + +1. Retrieve the project dependencies using one of the following terminal commands from the ReactiveCocoa project root directory: +- `script/bootstrap` +**OR**, if you have [Carthage](https://github.com/Carthage/Carthage) installed +- `carthage checkout --no-use-binaries` +1. Open `ReactiveCocoa.xcworkspace` +1. Build `Result-Mac` scheme +1. Build `ReactiveSwift-macOS` scheme +1. Build `ReactiveCocoa-macOS` scheme +1. Finally open the `ReactiveCocoa-macOS.playground` +1. Choose `View > Show Debug Area` +*/ + +import Result +import ReactiveCocoa +import ReactiveSwift +import AppKit + +/*: +## Sandbox + +A place where you can build your sand castles 🏖. +*/ diff --git a/ReactiveCocoa-macOS.playground/Sources/PlaygroundUtility.swift b/ReactiveCocoa-macOS.playground/Sources/PlaygroundUtility.swift new file mode 100644 index 0000000000..282b07f49e --- /dev/null +++ b/ReactiveCocoa-macOS.playground/Sources/PlaygroundUtility.swift @@ -0,0 +1,10 @@ +import Foundation + +public func scopedExample(_ exampleDescription: String, _ action: () -> Void) { + print("\n--- \(exampleDescription) ---\n") + action() +} + +public enum PlaygroundError: Error { + case example(String) +} diff --git a/ReactiveCocoa-macOS.playground/contents.xcplayground b/ReactiveCocoa-macOS.playground/contents.xcplayground new file mode 100644 index 0000000000..3740719fea --- /dev/null +++ b/ReactiveCocoa-macOS.playground/contents.xcplayground @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/ReactiveCocoa.xcworkspace/contents.xcworkspacedata b/ReactiveCocoa.xcworkspace/contents.xcworkspacedata index 47b17f469d..766bcc2b56 100644 --- a/ReactiveCocoa.xcworkspace/contents.xcworkspacedata +++ b/ReactiveCocoa.xcworkspace/contents.xcworkspacedata @@ -1,6 +1,12 @@ + + + + diff --git a/script/validate-playground.sh b/script/validate-playground.sh index 17b6d4f2ea..7a1c3e6de1 100755 --- a/script/validate-playground.sh +++ b/script/validate-playground.sh @@ -10,6 +10,11 @@ if [ -z "$BUILD_DIRECTORY" ]; then exit 1 fi +if [ -z "$PLAYGROUND" ]; then + echo "\$PLAYGROUND is not set." + exit 1 +fi + if [ -z "$XCODE_PLAYGROUND_TARGET" ]; then echo "\$XCODE_PLAYGROUND_TARGET is not set." exit 1 @@ -17,7 +22,7 @@ fi PAGES_PATH=${BUILD_DIRECTORY}/Build/Products/${CONFIGURATION}/all-playground-pages.swift -cat ReactiveCocoa.playground/Sources/*.swift ReactiveCocoa.playground/Pages/**/*.swift > ${PAGES_PATH} +cat ${PLAYGROUND}/Sources/*.swift ${PLAYGROUND}/Pages/**/*.swift > ${PAGES_PATH} swift -v -target ${XCODE_PLAYGROUND_TARGET} -D NOT_IN_PLAYGROUND -F ${BUILD_DIRECTORY}/Build/Products/${CONFIGURATION} ${PAGES_PATH} > /dev/null result=$? From 47117181c76932ec05b2e453e3ab5b8d38e7c2da Mon Sep 17 00:00:00 2001 From: Anders Date: Mon, 21 Nov 2016 21:01:19 +0100 Subject: [PATCH 0607/1028] Added a playground for tvOS. --- .../Sandbox.xcplaygroundpage/Contents.swift | 25 +++++++++++++++++++ .../Sources/PlaygroundUtility.swift | 10 ++++++++ .../contents.xcplayground | 2 ++ .../contents.xcworkspacedata | 3 +++ 4 files changed, 40 insertions(+) create mode 100644 ReactiveCocoa-tvOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift create mode 100644 ReactiveCocoa-tvOS.playground/Sources/PlaygroundUtility.swift create mode 100644 ReactiveCocoa-tvOS.playground/contents.xcplayground diff --git a/ReactiveCocoa-tvOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift b/ReactiveCocoa-tvOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000000..fa540e3662 --- /dev/null +++ b/ReactiveCocoa-tvOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift @@ -0,0 +1,25 @@ +/*: +> ## IMPORTANT: To use `ReactiveCocoa-tvOS.playground`, please: + +1. Retrieve the project dependencies using one of the following terminal commands from the ReactiveCocoa project root directory: +- `script/bootstrap` +**OR**, if you have [Carthage](https://github.com/Carthage/Carthage) installed +- `carthage checkout --no-use-binaries` +1. Open `ReactiveCocoa.xcworkspace` +1. Build `Result-tvOS` scheme +1. Build `ReactiveSwift-tvOS` scheme +1. Build `ReactiveCocoa-tvOS` scheme +1. Finally open the `ReactiveCocoa-tvOS.playground` +1. Choose `View > Show Debug Area` +*/ + +import Result +import ReactiveCocoa +import ReactiveSwift +import UIKit + +/*: +## Sandbox + +A place where you can build your sand castles 🏖. +*/ diff --git a/ReactiveCocoa-tvOS.playground/Sources/PlaygroundUtility.swift b/ReactiveCocoa-tvOS.playground/Sources/PlaygroundUtility.swift new file mode 100644 index 0000000000..282b07f49e --- /dev/null +++ b/ReactiveCocoa-tvOS.playground/Sources/PlaygroundUtility.swift @@ -0,0 +1,10 @@ +import Foundation + +public func scopedExample(_ exampleDescription: String, _ action: () -> Void) { + print("\n--- \(exampleDescription) ---\n") + action() +} + +public enum PlaygroundError: Error { + case example(String) +} diff --git a/ReactiveCocoa-tvOS.playground/contents.xcplayground b/ReactiveCocoa-tvOS.playground/contents.xcplayground new file mode 100644 index 0000000000..5fd6ef8029 --- /dev/null +++ b/ReactiveCocoa-tvOS.playground/contents.xcplayground @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/ReactiveCocoa.xcworkspace/contents.xcworkspacedata b/ReactiveCocoa.xcworkspace/contents.xcworkspacedata index 766bcc2b56..7e17112c96 100644 --- a/ReactiveCocoa.xcworkspace/contents.xcworkspacedata +++ b/ReactiveCocoa.xcworkspace/contents.xcworkspacedata @@ -4,6 +4,9 @@ + + From 237225dec9ff246f9a26797bfd76a107c73b4352 Mon Sep 17 00:00:00 2001 From: Anders Date: Mon, 21 Nov 2016 21:01:39 +0100 Subject: [PATCH 0608/1028] Enabled playground CI validation for iOS and tvOS. --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index a5b4321582..1a8c394fea 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,11 +23,15 @@ matrix: - XCODE_SDK=iphonesimulator - XCODE_ACTION="build-for-testing test-without-building" - XCODE_DESTINATION="platform=iOS Simulator,name=iPhone 6s" + - XCODE_PLAYGROUND_TARGET="arm64-apple-ios9.3" + - PLAYGROUND="ReactiveCocoa-iOS.playground" - xcode_scheme: ReactiveCocoa-tvOS env: - XCODE_SDK=appletvsimulator - XCODE_ACTION="build-for-testing test-without-building" - XCODE_DESTINATION="platform=tvOS Simulator,name=Apple TV 1080p" + - XCODE_PLAYGROUND_TARGET="arm64-apple-tvos9.3" + - PLAYGROUND="ReactiveCocoa-tvOS.playground" - xcode_scheme: ReactiveCocoa-watchOS env: - XCODE_SDK=watchsimulator From 88adf14401741dad0aeb5ef29c8d81c50f199876 Mon Sep 17 00:00:00 2001 From: Victor Augusteo Date: Tue, 22 Nov 2016 14:12:20 +1100 Subject: [PATCH 0609/1028] add attributed text binding target --- ReactiveCocoa/UIKit/UITextView.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ReactiveCocoa/UIKit/UITextView.swift b/ReactiveCocoa/UIKit/UITextView.swift index e5fdcf0245..9f22a25cb2 100644 --- a/ReactiveCocoa/UIKit/UITextView.swift +++ b/ReactiveCocoa/UIKit/UITextView.swift @@ -7,6 +7,11 @@ extension Reactive where Base: UITextView { public var text: BindingTarget { return makeBindingTarget { $0.text = $1 } } + + /// Sets the attributed text of the text view. + public var attributedText: BindingTarget { + return makeBindingTarget { $0.attributedText = $1 } + } private func textValues(forName name: NSNotification.Name) -> Signal { return NotificationCenter.default From d24ce8a66a088c64b5d55d775d79ce6a452f2274 Mon Sep 17 00:00:00 2001 From: Victor Augusteo Date: Tue, 22 Nov 2016 14:19:42 +1100 Subject: [PATCH 0610/1028] add attributed text to uitextfield --- ReactiveCocoa/UIKit/UITextField.swift | 5 +++++ ReactiveCocoa/UIKit/UITextView.swift | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ReactiveCocoa/UIKit/UITextField.swift b/ReactiveCocoa/UIKit/UITextField.swift index 31ec54f50b..2fd6d056f5 100644 --- a/ReactiveCocoa/UIKit/UITextField.swift +++ b/ReactiveCocoa/UIKit/UITextField.swift @@ -7,6 +7,11 @@ extension Reactive where Base: UITextField { public var text: BindingTarget { return makeBindingTarget { $0.text = $1 } } + + /// Sets the attributed text of the text field. + public var attributedText: BindingTarget { + return makeBindingTarget { $0.attributedText = $1 } + } /// A signal of text values emitted by the text field upon end of editing. /// diff --git a/ReactiveCocoa/UIKit/UITextView.swift b/ReactiveCocoa/UIKit/UITextView.swift index 9f22a25cb2..e4d6904ef1 100644 --- a/ReactiveCocoa/UIKit/UITextView.swift +++ b/ReactiveCocoa/UIKit/UITextView.swift @@ -9,7 +9,7 @@ extension Reactive where Base: UITextView { } /// Sets the attributed text of the text view. - public var attributedText: BindingTarget { + public var attributedText: BindingTarget { return makeBindingTarget { $0.attributedText = $1 } } From cdffc01221fa6de6e57c84edd5a76cf2023381eb Mon Sep 17 00:00:00 2001 From: Victor Augusteo Date: Tue, 22 Nov 2016 14:23:57 +1100 Subject: [PATCH 0611/1028] add tests --- ReactiveCocoaTests/UIKit/UITextFieldSpec.swift | 16 ++++++++++++++++ ReactiveCocoaTests/UIKit/UITextViewSpec.swift | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift index 0c63644a14..4d276a194f 100644 --- a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift @@ -22,6 +22,22 @@ class UITextFieldSpec: QuickSpec { } expect(_textField).to(beNil()) } + + it("should accept changes from bindings to its attributed text value") { + let firstChange = NSAttributedString(string: "first") + let secondChange = NSAttributedString(string: "second") + + textField.attributedText = NSAttributedString(string: "") + + let (pipeSignal, observer) = Signal.pipe() + textField.reactive.attributedText <~ SignalProducer(signal: pipeSignal) + + observer.send(value: firstChange) + expect(textField.attributedText) == firstChange + + observer.send(value: secondChange) + expect(textField.attributedText) == secondChange + } it("should emit user initiated changes to its text value when the editing ends") { textField.text = "Test" diff --git a/ReactiveCocoaTests/UIKit/UITextViewSpec.swift b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift index c201b1a682..4c1a4fcd6c 100644 --- a/ReactiveCocoaTests/UIKit/UITextViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift @@ -23,6 +23,22 @@ class UITextViewSpec: QuickSpec { expect(_textView).toEventually(beNil()) } + it("should accept changes from bindings to its attributed text value") { + let firstChange = NSAttributedString(string: "first") + let secondChange = NSAttributedString(string: "second") + + textView.attributedText = NSAttributedString(string: "") + + let (pipeSignal, observer) = Signal.pipe() + textView.reactive.attributedText <~ SignalProducer(signal: pipeSignal) + + observer.send(value: firstChange) + expect(textView.attributedText) == firstChange + + observer.send(value: secondChange) + expect(textView.attributedText) == secondChange + } + it("should emit user initiated changes to its text value when the editing ends") { textView.text = "Test" From 649b272d954db2fe27d483183aeb6c86c6ac6b7d Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 22 Nov 2016 02:50:40 +0100 Subject: [PATCH 0612/1028] Corrected directory paths and parameters for playground validation. --- .travis.yml | 4 ++-- script/validate-playground.sh | 12 ++++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1a8c394fea..340517ab28 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,14 +23,14 @@ matrix: - XCODE_SDK=iphonesimulator - XCODE_ACTION="build-for-testing test-without-building" - XCODE_DESTINATION="platform=iOS Simulator,name=iPhone 6s" - - XCODE_PLAYGROUND_TARGET="arm64-apple-ios9.3" + - XCODE_PLAYGROUND_TARGET="x86_64-apple-ios9.3" - PLAYGROUND="ReactiveCocoa-iOS.playground" - xcode_scheme: ReactiveCocoa-tvOS env: - XCODE_SDK=appletvsimulator - XCODE_ACTION="build-for-testing test-without-building" - XCODE_DESTINATION="platform=tvOS Simulator,name=Apple TV 1080p" - - XCODE_PLAYGROUND_TARGET="arm64-apple-tvos9.3" + - XCODE_PLAYGROUND_TARGET="x86_64-apple-tvos9.3" - PLAYGROUND="ReactiveCocoa-tvOS.playground" - xcode_scheme: ReactiveCocoa-watchOS env: diff --git a/script/validate-playground.sh b/script/validate-playground.sh index 7a1c3e6de1..e380282e61 100755 --- a/script/validate-playground.sh +++ b/script/validate-playground.sh @@ -20,11 +20,19 @@ if [ -z "$XCODE_PLAYGROUND_TARGET" ]; then exit 1 fi -PAGES_PATH=${BUILD_DIRECTORY}/Build/Products/${CONFIGURATION}/all-playground-pages.swift +BUILD_DIR_PATH= + +if [ "$XCODE_SDK" == "macosx" ]; then + BUILD_DIR_PATH=Products/${CONFIGURATION} +else + BUILD_DIR_PATH=Intermediates/CodeCoverage/Products/${CONFIGURATION}-${XCODE_SDK} +fi + +PAGES_PATH=${BUILD_DIRECTORY}/Build/${BUILD_DIR_PATH}/all-playground-pages.swift cat ${PLAYGROUND}/Sources/*.swift ${PLAYGROUND}/Pages/**/*.swift > ${PAGES_PATH} -swift -v -target ${XCODE_PLAYGROUND_TARGET} -D NOT_IN_PLAYGROUND -F ${BUILD_DIRECTORY}/Build/Products/${CONFIGURATION} ${PAGES_PATH} > /dev/null +swift -v -target ${XCODE_PLAYGROUND_TARGET} -D NOT_IN_PLAYGROUND -F ${BUILD_DIRECTORY}/Build/${BUILD_DIR_PATH} ${PAGES_PATH} > /dev/null result=$? # Cleanup From c1c253fe5cc39132460b0b0ad7d3d1494a302878 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Tue, 22 Nov 2016 16:49:57 +0100 Subject: [PATCH 0613/1028] Get the SDK root and pass it to the Swift compiler. --- script/validate-playground.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/script/validate-playground.sh b/script/validate-playground.sh index e380282e61..017d9e68b7 100755 --- a/script/validate-playground.sh +++ b/script/validate-playground.sh @@ -28,11 +28,12 @@ else BUILD_DIR_PATH=Intermediates/CodeCoverage/Products/${CONFIGURATION}-${XCODE_SDK} fi +SDK_ROOT=$(xcrun --sdk ${XCODE_SDK} --show-sdk-path) PAGES_PATH=${BUILD_DIRECTORY}/Build/${BUILD_DIR_PATH}/all-playground-pages.swift cat ${PLAYGROUND}/Sources/*.swift ${PLAYGROUND}/Pages/**/*.swift > ${PAGES_PATH} -swift -v -target ${XCODE_PLAYGROUND_TARGET} -D NOT_IN_PLAYGROUND -F ${BUILD_DIRECTORY}/Build/${BUILD_DIR_PATH} ${PAGES_PATH} > /dev/null +swift -v -target ${XCODE_PLAYGROUND_TARGET} -sdk ${SDK_ROOT} -D NOT_IN_PLAYGROUND -F ${BUILD_DIRECTORY}/Build/${BUILD_DIR_PATH} ${PAGES_PATH} > /dev/null result=$? # Cleanup From 47c58107406cc2a5b2bd67402a8accf8d9c26eac Mon Sep 17 00:00:00 2001 From: Anders Date: Wed, 23 Nov 2016 00:57:44 +0100 Subject: [PATCH 0614/1028] Improved the documentation of the implementation. --- ReactiveCocoa/NSObject+Intercepting.swift | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 1e61ee7c86..90ecf6d84b 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -38,6 +38,25 @@ extension Reactive where Base: NSObject { } } + +// A summary of associated values involved. +// +// Runtime subclasses: +// - `&isSwizzledKey` (ObjC+RuntimeSubclassing.swift) +// Nullable. Exists only in runtime subclasses generated by external parties. +// `true` indicates the runtime subclass has already been swizzled. +// +// - `&isSwizzledKey` (This file) +// Nullable. `true` indicates the runtime subclass has already been prepared for method +// interception. +// +// - `&interceptedSelectorsKey` +// Holds the method signature cache of the runtime subclass. +// +// Intercepted instances: +// - `alias.utf8Start` +// Nullable. Holds the `InterceptingState` of the selector for the instance. + /// Setup the method interception. /// /// - parameters: From 2c3a6f879361b5092919e9c72e8c496d0ebddeae Mon Sep 17 00:00:00 2001 From: Victor Augusteo Date: Wed, 23 Nov 2016 11:38:36 +1100 Subject: [PATCH 0615/1028] clean spacing, import Result.NoError --- .../UIKit/UITextFieldSpec.swift | 1 + ReactiveCocoaTests/UIKit/UITextViewSpec.swift | 93 ++++++++++--------- 2 files changed, 48 insertions(+), 46 deletions(-) diff --git a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift index 4d276a194f..b35aea0ea8 100644 --- a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift @@ -3,6 +3,7 @@ import ReactiveCocoa import UIKit import Quick import Nimble +import enum Result.NoError class UITextFieldSpec: QuickSpec { override func spec() { diff --git a/ReactiveCocoaTests/UIKit/UITextViewSpec.swift b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift index 4c1a4fcd6c..e6f5528c50 100644 --- a/ReactiveCocoaTests/UIKit/UITextViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift @@ -3,66 +3,67 @@ import ReactiveCocoa import UIKit import Quick import Nimble +import enum Result.NoError class UITextViewSpec: QuickSpec { override func spec() { - var textView: UITextView! - weak var _textView: UITextView? + var textView: UITextView! + weak var _textView: UITextView? - beforeEach { - autoreleasepool { - textView = UITextView(frame: .zero) - _textView = textView - } - } - - afterEach { - autoreleasepool { - textView = nil - } - expect(_textView).toEventually(beNil()) + beforeEach { + autoreleasepool { + textView = UITextView(frame: .zero) + _textView = textView } + } - it("should accept changes from bindings to its attributed text value") { - let firstChange = NSAttributedString(string: "first") - let secondChange = NSAttributedString(string: "second") - - textView.attributedText = NSAttributedString(string: "") - - let (pipeSignal, observer) = Signal.pipe() - textView.reactive.attributedText <~ SignalProducer(signal: pipeSignal) - - observer.send(value: firstChange) - expect(textView.attributedText) == firstChange - - observer.send(value: secondChange) - expect(textView.attributedText) == secondChange + afterEach { + autoreleasepool { + textView = nil } + expect(_textView).toEventually(beNil()) + } - it("should emit user initiated changes to its text value when the editing ends") { - textView.text = "Test" + it("should accept changes from bindings to its attributed text value") { + let firstChange = NSAttributedString(string: "first") + let secondChange = NSAttributedString(string: "second") + + textView.attributedText = NSAttributedString(string: "") + + let (pipeSignal, observer) = Signal.pipe() + textView.reactive.attributedText <~ SignalProducer(signal: pipeSignal) + + observer.send(value: firstChange) + expect(textView.attributedText) == firstChange + + observer.send(value: secondChange) + expect(textView.attributedText) == secondChange + } - var latestValue: String? - textView.reactive.textValues.observeValues { text in - latestValue = text - } + it("should emit user initiated changes to its text value when the editing ends") { + textView.text = "Test" - NotificationCenter.default.post(name: .UITextViewTextDidEndEditing, - object: textView) - expect(latestValue) == textView.text + var latestValue: String? + textView.reactive.textValues.observeValues { text in + latestValue = text } - it("should emit user initiated changes to its text value continuously") { - textView.text = "Test" + NotificationCenter.default.post(name: .UITextViewTextDidEndEditing, + object: textView) + expect(latestValue) == textView.text + } + + it("should emit user initiated changes to its text value continuously") { + textView.text = "Test" - var latestValue: String? - textView.reactive.continuousTextValues.observeValues { text in - latestValue = text - } + var latestValue: String? + textView.reactive.continuousTextValues.observeValues { text in + latestValue = text + } - NotificationCenter.default.post(name: .UITextViewTextDidChange, - object: textView) - expect(latestValue) == textView.text + NotificationCenter.default.post(name: .UITextViewTextDidChange, + object: textView) + expect(latestValue) == textView.text } } } From 65d9b90d91e3999d0bde8e84c95475bce8693297 Mon Sep 17 00:00:00 2001 From: Victor Augusteo Date: Wed, 23 Nov 2016 13:42:44 +1100 Subject: [PATCH 0616/1028] textview optional text string binding --- ReactiveCocoa/UIKit/UITextView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa/UIKit/UITextView.swift b/ReactiveCocoa/UIKit/UITextView.swift index e4d6904ef1..498d166e3e 100644 --- a/ReactiveCocoa/UIKit/UITextView.swift +++ b/ReactiveCocoa/UIKit/UITextView.swift @@ -4,7 +4,7 @@ import enum Result.NoError extension Reactive where Base: UITextView { /// Sets the text of the text view. - public var text: BindingTarget { + public var text: BindingTarget { return makeBindingTarget { $0.text = $1 } } From d567d2430805f08e3b7b5125f2e4ba311abad4af Mon Sep 17 00:00:00 2001 From: Victor Augusteo Date: Wed, 23 Nov 2016 16:05:49 +1100 Subject: [PATCH 0617/1028] added attributedText signals --- ReactiveCocoa/UIKit/UITextField.swift | 27 +++++++-- ReactiveCocoa/UIKit/UITextView.swift | 33 +++++++++-- .../UIKit/UITextFieldSpec.swift | 56 +++++++++++++------ ReactiveCocoaTests/UIKit/UITextViewSpec.swift | 56 +++++++++++++------ 4 files changed, 130 insertions(+), 42 deletions(-) diff --git a/ReactiveCocoa/UIKit/UITextField.swift b/ReactiveCocoa/UIKit/UITextField.swift index 2fd6d056f5..6783505691 100644 --- a/ReactiveCocoa/UIKit/UITextField.swift +++ b/ReactiveCocoa/UIKit/UITextField.swift @@ -7,11 +7,6 @@ extension Reactive where Base: UITextField { public var text: BindingTarget { return makeBindingTarget { $0.text = $1 } } - - /// Sets the attributed text of the text field. - public var attributedText: BindingTarget { - return makeBindingTarget { $0.attributedText = $1 } - } /// A signal of text values emitted by the text field upon end of editing. /// @@ -29,4 +24,26 @@ extension Reactive where Base: UITextField { return trigger(for: .editingChanged) .map { [unowned base = self.base] in base.text } } + + /// Sets the attributed text of the text field. + public var attributedText: BindingTarget { + return makeBindingTarget { $0.attributedText = $1 } + } + + /// A signal of attributed text values emitted by the text field upon end of editing. + /// + /// - note: To observe attributed text values that change on all editing events, + /// see `continuousAttributedTextValues`. + public var attributedTextValues: Signal { + return trigger(for: .editingDidEnd) + .map { [unowned base = self.base] in base.attributedText } + } + + /// A signal of attributed text values emitted by the text field upon any changes. + /// + /// - note: To observe attributed text values only when editing ends, see `attributedTextValues`. + public var continuousAttributedTextValues: Signal { + return trigger(for: .editingDidEnd) + .map { [unowned base = self.base] in base.attributedText } + } } diff --git a/ReactiveCocoa/UIKit/UITextView.swift b/ReactiveCocoa/UIKit/UITextView.swift index 498d166e3e..a1c81b2f2d 100644 --- a/ReactiveCocoa/UIKit/UITextView.swift +++ b/ReactiveCocoa/UIKit/UITextView.swift @@ -7,11 +7,6 @@ extension Reactive where Base: UITextView { public var text: BindingTarget { return makeBindingTarget { $0.text = $1 } } - - /// Sets the attributed text of the text view. - public var attributedText: BindingTarget { - return makeBindingTarget { $0.attributedText = $1 } - } private func textValues(forName name: NSNotification.Name) -> Signal { return NotificationCenter.default @@ -35,4 +30,32 @@ extension Reactive where Base: UITextView { public var continuousTextValues: Signal { return textValues(forName: .UITextViewTextDidChange) } + + /// Sets the attributed text of the text view. + public var attributedText: BindingTarget { + return makeBindingTarget { $0.attributedText = $1 } + } + + private func attributedTextValues(forName name: NSNotification.Name) -> Signal { + return NotificationCenter.default + .reactive + .notifications(forName: name, object: base) + .take(during: lifetime) + .map { ($0.object as! UITextView).attributedText! } + } + + /// A signal of attributed text values emitted by the text view upon end of editing. + /// + /// - note: To observe attributed text values that change on all editing events, + /// see `continuousAttributedTextValues`. + public var attributedTextValues: Signal { + return attributedTextValues(forName: .UITextViewTextDidEndEditing) + } + + /// A signal of attributed text values emitted by the text view upon any changes. + /// + /// - note: To observe text values only when editing ends, see `attributedTextValues`. + public var attributedContinuousTextValues: Signal { + return attributedTextValues(forName: .UITextViewTextDidChange) + } } diff --git a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift index b35aea0ea8..6559920edb 100644 --- a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift @@ -23,22 +23,6 @@ class UITextFieldSpec: QuickSpec { } expect(_textField).to(beNil()) } - - it("should accept changes from bindings to its attributed text value") { - let firstChange = NSAttributedString(string: "first") - let secondChange = NSAttributedString(string: "second") - - textField.attributedText = NSAttributedString(string: "") - - let (pipeSignal, observer) = Signal.pipe() - textField.reactive.attributedText <~ SignalProducer(signal: pipeSignal) - - observer.send(value: firstChange) - expect(textField.attributedText) == firstChange - - observer.send(value: secondChange) - expect(textField.attributedText) == secondChange - } it("should emit user initiated changes to its text value when the editing ends") { textField.text = "Test" @@ -63,5 +47,45 @@ class UITextFieldSpec: QuickSpec { textField.sendActions(for: .editingChanged) expect(latestValue) == textField.text } + + it("should accept changes from bindings to its attributed text value") { + let firstChange = NSAttributedString(string: "first") + let secondChange = NSAttributedString(string: "second") + + textField.attributedText = NSAttributedString(string: "") + + let (pipeSignal, observer) = Signal.pipe() + textField.reactive.attributedText <~ SignalProducer(signal: pipeSignal) + + observer.send(value: firstChange) + expect(textField.attributedText) == firstChange + + observer.send(value: secondChange) + expect(textField.attributedText) == secondChange + } + + it("should emit user initiated changes to its attributed text value when the editing ends") { + textField.attributedText = NSAttributedString(string: "Test") + + var latestValue: NSAttributedString? + textField.reactive.attributedTextValues.observeValues { attributedText in + latestValue = attributedText + } + + textField.sendActions(for: .editingDidEnd) + expect(latestValue) == textField.attributedText + } + + it("should emit user initiated changes to its attributed text value continuously") { + textField.attributedText = NSAttributedString(string: "Test") + + var latestValue: NSAttributedString? + textField.reactive.continuousAttributedTextValues.observeValues { attributedText in + latestValue = attributedText + } + + textField.sendActions(for: .editingChanged) + expect(latestValue) == textField.attributedText + } } } diff --git a/ReactiveCocoaTests/UIKit/UITextViewSpec.swift b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift index e6f5528c50..c058b80867 100644 --- a/ReactiveCocoaTests/UIKit/UITextViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift @@ -24,22 +24,6 @@ class UITextViewSpec: QuickSpec { expect(_textView).toEventually(beNil()) } - it("should accept changes from bindings to its attributed text value") { - let firstChange = NSAttributedString(string: "first") - let secondChange = NSAttributedString(string: "second") - - textView.attributedText = NSAttributedString(string: "") - - let (pipeSignal, observer) = Signal.pipe() - textView.reactive.attributedText <~ SignalProducer(signal: pipeSignal) - - observer.send(value: firstChange) - expect(textView.attributedText) == firstChange - - observer.send(value: secondChange) - expect(textView.attributedText) == secondChange - } - it("should emit user initiated changes to its text value when the editing ends") { textView.text = "Test" @@ -65,5 +49,45 @@ class UITextViewSpec: QuickSpec { object: textView) expect(latestValue) == textView.text } + + it("should accept changes from bindings to its attributed text value") { + let firstChange = NSAttributedString(string: "first") + let secondChange = NSAttributedString(string: "second") + + textView.attributedText = NSAttributedString(string: "") + + let (pipeSignal, observer) = Signal.pipe() + textView.reactive.attributedText <~ SignalProducer(signal: pipeSignal) + + observer.send(value: firstChange) + expect(textView.attributedText) == firstChange + + observer.send(value: secondChange) + expect(textView.attributedText) == secondChange + } + + it("should emit user initiated changes to its attributed text value when the editing ends") { + textView.attributedText = NSAttributedString(string: "Test") + + var latestValue: NSAttributedString? + textView.reactive.attributedTextValues.observeValues { attributedText in + latestValue = attributedText + } + + textView.sendActions(for: .editingDidEnd) + expect(latestValue) == textView.attributedText + } + + it("should emit user initiated changes to its attributed text value continuously") { + textView.attributedText = NSAttributedString(string: "Test") + + var latestValue: NSAttributedString? + textView.reactive.continuousAttributedTextValues.observeValues { attributedText in + latestValue = attributedText + } + + textView.sendActions(for: .editingChanged) + expect(latestValue) == textView.attributedText + } } } From 38a95b8d5e0ed080c857708ec96e94b4ebcba63f Mon Sep 17 00:00:00 2001 From: Victor Augusteo Date: Wed, 23 Nov 2016 16:13:01 +1100 Subject: [PATCH 0618/1028] fix indentation --- ReactiveCocoaTests/UIKit/UITextViewSpec.swift | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ReactiveCocoaTests/UIKit/UITextViewSpec.swift b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift index c058b80867..6b552bbd0c 100644 --- a/ReactiveCocoaTests/UIKit/UITextViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift @@ -29,11 +29,10 @@ class UITextViewSpec: QuickSpec { var latestValue: String? textView.reactive.textValues.observeValues { text in - latestValue = text + latestValue = text } - NotificationCenter.default.post(name: .UITextViewTextDidEndEditing, - object: textView) + NotificationCenter.default.post(name: .UITextViewTextDidEndEditing, object: textView) expect(latestValue) == textView.text } @@ -41,8 +40,8 @@ class UITextViewSpec: QuickSpec { textView.text = "Test" var latestValue: String? - textView.reactive.continuousTextValues.observeValues { text in - latestValue = text + textView.reactive.continuousTextValues.observeValues { text in + latestValue = text } NotificationCenter.default.post(name: .UITextViewTextDidChange, From c836a33bad588a68671fca712ee31ff24cf9d1e1 Mon Sep 17 00:00:00 2001 From: Victor Augusteo Date: Wed, 23 Nov 2016 16:14:11 +1100 Subject: [PATCH 0619/1028] add optional strings in UITextView --- ReactiveCocoa/UIKit/UITextView.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoa/UIKit/UITextView.swift b/ReactiveCocoa/UIKit/UITextView.swift index a1c81b2f2d..dee8e439f3 100644 --- a/ReactiveCocoa/UIKit/UITextView.swift +++ b/ReactiveCocoa/UIKit/UITextView.swift @@ -20,14 +20,14 @@ extension Reactive where Base: UITextView { /// /// - note: To observe text values that change on all editing events, /// see `continuousTextValues`. - public var textValues: Signal { + public var textValues: Signal { return textValues(forName: .UITextViewTextDidEndEditing) } /// A signal of text values emitted by the text view upon any changes. /// /// - note: To observe text values only when editing ends, see `textValues`. - public var continuousTextValues: Signal { + public var continuousTextValues: Signal { return textValues(forName: .UITextViewTextDidChange) } From 4dbe248145716a3bf53fb0fad69f89f8becf048f Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Wed, 23 Nov 2016 21:26:03 +0100 Subject: [PATCH 0620/1028] Disable iOS and tvOS CI playground validation. --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 340517ab28..ae3b9a3205 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,15 +23,15 @@ matrix: - XCODE_SDK=iphonesimulator - XCODE_ACTION="build-for-testing test-without-building" - XCODE_DESTINATION="platform=iOS Simulator,name=iPhone 6s" - - XCODE_PLAYGROUND_TARGET="x86_64-apple-ios9.3" - - PLAYGROUND="ReactiveCocoa-iOS.playground" +# - XCODE_PLAYGROUND_TARGET="x86_64-apple-ios9.3" +# - PLAYGROUND="ReactiveCocoa-iOS.playground" - xcode_scheme: ReactiveCocoa-tvOS env: - XCODE_SDK=appletvsimulator - XCODE_ACTION="build-for-testing test-without-building" - XCODE_DESTINATION="platform=tvOS Simulator,name=Apple TV 1080p" - - XCODE_PLAYGROUND_TARGET="x86_64-apple-tvos9.3" - - PLAYGROUND="ReactiveCocoa-tvOS.playground" +# - XCODE_PLAYGROUND_TARGET="x86_64-apple-tvos9.3" +# - PLAYGROUND="ReactiveCocoa-tvOS.playground" - xcode_scheme: ReactiveCocoa-watchOS env: - XCODE_SDK=watchsimulator From 9bb73b37adc45377219a1d04c44174e5dfd34319 Mon Sep 17 00:00:00 2001 From: Victor Augusteo Date: Thu, 24 Nov 2016 09:15:52 +1100 Subject: [PATCH 0621/1028] fix type error --- ReactiveCocoa/UIKit/UITextView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa/UIKit/UITextView.swift b/ReactiveCocoa/UIKit/UITextView.swift index dee8e439f3..e46af9fb2b 100644 --- a/ReactiveCocoa/UIKit/UITextView.swift +++ b/ReactiveCocoa/UIKit/UITextView.swift @@ -36,7 +36,7 @@ extension Reactive where Base: UITextView { return makeBindingTarget { $0.attributedText = $1 } } - private func attributedTextValues(forName name: NSNotification.Name) -> Signal { + private func attributedTextValues(forName name: NSNotification.Name) -> Signal { return NotificationCenter.default .reactive .notifications(forName: name, object: base) From a555d599d11a94ee3b116c98eae952e90e4d8dcf Mon Sep 17 00:00:00 2001 From: Victor Augusteo Date: Thu, 24 Nov 2016 15:46:00 +1100 Subject: [PATCH 0622/1028] fixed tests --- ReactiveCocoa/UIKit/UITextField.swift | 2 +- ReactiveCocoa/UIKit/UITextView.swift | 4 ++-- .../UIKit/UITextFieldSpec.swift | 6 +++--- ReactiveCocoaTests/UIKit/UITextViewSpec.swift | 19 +++++++++++-------- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/ReactiveCocoa/UIKit/UITextField.swift b/ReactiveCocoa/UIKit/UITextField.swift index 6783505691..e6f808766e 100644 --- a/ReactiveCocoa/UIKit/UITextField.swift +++ b/ReactiveCocoa/UIKit/UITextField.swift @@ -43,7 +43,7 @@ extension Reactive where Base: UITextField { /// /// - note: To observe attributed text values only when editing ends, see `attributedTextValues`. public var continuousAttributedTextValues: Signal { - return trigger(for: .editingDidEnd) + return trigger(for: .editingChanged) .map { [unowned base = self.base] in base.attributedText } } } diff --git a/ReactiveCocoa/UIKit/UITextView.swift b/ReactiveCocoa/UIKit/UITextView.swift index e46af9fb2b..7926d2adba 100644 --- a/ReactiveCocoa/UIKit/UITextView.swift +++ b/ReactiveCocoa/UIKit/UITextView.swift @@ -8,7 +8,7 @@ extension Reactive where Base: UITextView { return makeBindingTarget { $0.text = $1 } } - private func textValues(forName name: NSNotification.Name) -> Signal { + private func textValues(forName name: NSNotification.Name) -> Signal { return NotificationCenter.default .reactive .notifications(forName: name, object: base) @@ -55,7 +55,7 @@ extension Reactive where Base: UITextView { /// A signal of attributed text values emitted by the text view upon any changes. /// /// - note: To observe text values only when editing ends, see `attributedTextValues`. - public var attributedContinuousTextValues: Signal { + public var continuousAttributedTextValues: Signal { return attributedTextValues(forName: .UITextViewTextDidChange) } } diff --git a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift index 6559920edb..ca10321c01 100644 --- a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift @@ -58,10 +58,10 @@ class UITextFieldSpec: QuickSpec { textField.reactive.attributedText <~ SignalProducer(signal: pipeSignal) observer.send(value: firstChange) - expect(textField.attributedText) == firstChange + expect(textField.attributedText?.string) == firstChange.string observer.send(value: secondChange) - expect(textField.attributedText) == secondChange + expect(textField.attributedText?.string) == secondChange.string } it("should emit user initiated changes to its attributed text value when the editing ends") { @@ -85,7 +85,7 @@ class UITextFieldSpec: QuickSpec { } textField.sendActions(for: .editingChanged) - expect(latestValue) == textField.attributedText + expect(latestValue?.string) == textField.attributedText?.string } } } diff --git a/ReactiveCocoaTests/UIKit/UITextViewSpec.swift b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift index 6b552bbd0c..15de1f7dec 100644 --- a/ReactiveCocoaTests/UIKit/UITextViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift @@ -9,6 +9,10 @@ class UITextViewSpec: QuickSpec { override func spec() { var textView: UITextView! weak var _textView: UITextView? + let attributes = [ + NSFontAttributeName: UIFont(name: "Georgia", size: 18.0)!, + NSForegroundColorAttributeName: UIColor.red + ] beforeEach { autoreleasepool { @@ -44,14 +48,13 @@ class UITextViewSpec: QuickSpec { latestValue = text } - NotificationCenter.default.post(name: .UITextViewTextDidChange, - object: textView) + NotificationCenter.default.post(name: .UITextViewTextDidChange, object: textView) expect(latestValue) == textView.text } it("should accept changes from bindings to its attributed text value") { - let firstChange = NSAttributedString(string: "first") - let secondChange = NSAttributedString(string: "second") + let firstChange = NSAttributedString(string: "first", attributes: attributes) + let secondChange = NSAttributedString(string: "second", attributes: attributes) textView.attributedText = NSAttributedString(string: "") @@ -66,26 +69,26 @@ class UITextViewSpec: QuickSpec { } it("should emit user initiated changes to its attributed text value when the editing ends") { - textView.attributedText = NSAttributedString(string: "Test") + textView.attributedText = NSAttributedString(string: "Test", attributes: attributes) var latestValue: NSAttributedString? textView.reactive.attributedTextValues.observeValues { attributedText in latestValue = attributedText } - textView.sendActions(for: .editingDidEnd) + NotificationCenter.default.post(name: .UITextViewTextDidEndEditing, object: textView) expect(latestValue) == textView.attributedText } it("should emit user initiated changes to its attributed text value continuously") { - textView.attributedText = NSAttributedString(string: "Test") + textView.attributedText = NSAttributedString(string: "Test", attributes: attributes) var latestValue: NSAttributedString? textView.reactive.continuousAttributedTextValues.observeValues { attributedText in latestValue = attributedText } - textView.sendActions(for: .editingChanged) + NotificationCenter.default.post(name: .UITextViewTextDidChange, object: textView) expect(latestValue) == textView.attributedText } } From 901a8ec44a387a0bd4678ec7c681cc512416d790 Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 24 Nov 2016 22:11:19 +0100 Subject: [PATCH 0623/1028] Abolish the lock for the signature cache. --- ReactiveCocoa/NSObject+Intercepting.swift | 49 ++++++++++++++++++----- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 90ecf6d84b..0301afa4ba 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -87,12 +87,12 @@ private func setupInterception(_ object: NSObject, for selector: Selector) -> Si synchronized(subclass) { let isSwizzled = objc_getAssociatedObject(subclass, &isSwizzledKey) as! Bool? ?? false - let signatureCache: Atomic<[Selector: AnyObject]> + let signatureCache: SignatureCache if isSwizzled { - signatureCache = objc_getAssociatedObject(subclass, &interceptedSelectorsKey) as! Atomic<[Selector: AnyObject]> + signatureCache = objc_getAssociatedObject(subclass, &interceptedSelectorsKey) as! SignatureCache } else { - signatureCache = Atomic([:]) + signatureCache = SignatureCache() objc_setAssociatedObject(subclass, &interceptedSelectorsKey, signatureCache, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) objc_setAssociatedObject(subclass, &isSwizzledKey, true, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) @@ -101,10 +101,9 @@ private func setupInterception(_ object: NSObject, for selector: Selector) -> Si setupMethodSignatureCaching(subclass, signatureCache) } - signatureCache.modify { signatures in - if signatures[selector] == nil { - signatures[selector] = NSMethodSignature.signature(withObjCTypes: typeEncoding) - } + if signatureCache[selector] == nil { + let signature = NSMethodSignature.signature(withObjCTypes: typeEncoding) + signatureCache[selector] = signature } if !class_respondsToSelector(subclass, interopAlias) { @@ -220,11 +219,11 @@ private func enableMessageForwarding(_ realClass: AnyClass) { /// - parameters: /// - realClass: The runtime subclass to be swizzled. /// - signatureCache: The method signature cache. -private func setupMethodSignatureCaching(_ realClass: AnyClass, _ signatureCache: Atomic<[Selector: AnyObject]>) { +private func setupMethodSignatureCaching(_ realClass: AnyClass, _ signatureCache: SignatureCache) { let perceivedClass: AnyClass = class_getSuperclass(realClass) let newMethodSignatureForSelector: @convention(block) (NSObject, Selector) -> AnyObject? = { object, selector in - if let signature = signatureCache.withValue({ $0[selector] }) { + if let signature = signatureCache[selector] { return signature } @@ -253,6 +252,38 @@ private final class InterceptingState { } } +// The signature cache for classes that have been swizzled for method +// interception. +// +// Read-copy-update is used here, since the cache has multiple readers but only +// one writer. +private final class SignatureCache { + // `Dictionary` takes 8 bytes for the reference to its storage and does CoW. + // So it should not encounter any corrupted, partially updated state. + private var map = [Selector: AnyObject]() + + init() {} + + /// Get or set the signature for the specified selector. + /// + /// - warning: Any invocation of the setter must be serialized. + /// + /// - parameters: + /// - selector: The selector. + subscript(selector: Selector) -> AnyObject? { + get { + return map[selector] + } + set { + if map[selector] == nil { + var newMap = map + newMap[selector] = newValue + map = newMap + } + } + } +} + private var isSwizzledKey = 0 private var interceptedSelectorsKey = 0 From ab7ee54987a45dda985c8923eb58a9d6b2b9b32b Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 25 Nov 2016 01:02:39 +0100 Subject: [PATCH 0624/1028] Remove references to 'script/bootstrap' from playground documentation. https://github.com/ReactiveCocoa/ReactiveSwift/pull/76 --- .../Pages/Sandbox.xcplaygroundpage/Contents.swift | 2 +- ReactiveCocoa-iOS.playground/contents.xcplayground | 2 +- .../Pages/Sandbox.xcplaygroundpage/Contents.swift | 2 +- .../Pages/Sandbox.xcplaygroundpage/Contents.swift | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ReactiveCocoa-iOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift b/ReactiveCocoa-iOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift index 6b88602528..98f546ec96 100644 --- a/ReactiveCocoa-iOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift +++ b/ReactiveCocoa-iOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift @@ -2,7 +2,7 @@ > ## IMPORTANT: To use `ReactiveCocoa-iOS.playground`, please: 1. Retrieve the project dependencies using one of the following terminal commands from the ReactiveCocoa project root directory: -- `script/bootstrap` +- `git submodule update --init` **OR**, if you have [Carthage](https://github.com/Carthage/Carthage) installed - `carthage checkout --no-use-binaries` 1. Open `ReactiveCocoa.xcworkspace` diff --git a/ReactiveCocoa-iOS.playground/contents.xcplayground b/ReactiveCocoa-iOS.playground/contents.xcplayground index 24cafab2f8..5ed29117bc 100644 --- a/ReactiveCocoa-iOS.playground/contents.xcplayground +++ b/ReactiveCocoa-iOS.playground/contents.xcplayground @@ -1,2 +1,2 @@ - + \ No newline at end of file diff --git a/ReactiveCocoa-macOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift b/ReactiveCocoa-macOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift index 8543a8d1fe..a31483f7fe 100644 --- a/ReactiveCocoa-macOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift +++ b/ReactiveCocoa-macOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift @@ -2,7 +2,7 @@ > ## IMPORTANT: To use `ReactiveCocoa-macOS.playground`, please: 1. Retrieve the project dependencies using one of the following terminal commands from the ReactiveCocoa project root directory: -- `script/bootstrap` +- `git submodule update --init` **OR**, if you have [Carthage](https://github.com/Carthage/Carthage) installed - `carthage checkout --no-use-binaries` 1. Open `ReactiveCocoa.xcworkspace` diff --git a/ReactiveCocoa-tvOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift b/ReactiveCocoa-tvOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift index fa540e3662..ae9e201761 100644 --- a/ReactiveCocoa-tvOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift +++ b/ReactiveCocoa-tvOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift @@ -2,7 +2,7 @@ > ## IMPORTANT: To use `ReactiveCocoa-tvOS.playground`, please: 1. Retrieve the project dependencies using one of the following terminal commands from the ReactiveCocoa project root directory: -- `script/bootstrap` +- `git submodule update --init` **OR**, if you have [Carthage](https://github.com/Carthage/Carthage) installed - `carthage checkout --no-use-binaries` 1. Open `ReactiveCocoa.xcworkspace` From 913cf89a6927f8521b60edd9975855ecbbaf72ad Mon Sep 17 00:00:00 2001 From: Victor Augusteo Date: Fri, 25 Nov 2016 11:13:43 +1100 Subject: [PATCH 0625/1028] change Georgia to system font --- ReactiveCocoaTests/UIKit/UITextViewSpec.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoaTests/UIKit/UITextViewSpec.swift b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift index 15de1f7dec..a1c3e27c3f 100644 --- a/ReactiveCocoaTests/UIKit/UITextViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift @@ -10,10 +10,10 @@ class UITextViewSpec: QuickSpec { var textView: UITextView! weak var _textView: UITextView? let attributes = [ - NSFontAttributeName: UIFont(name: "Georgia", size: 18.0)!, + NSFontAttributeName: UIFont.systemFont(ofSize: 18), NSForegroundColorAttributeName: UIColor.red ] - + beforeEach { autoreleasepool { textView = UITextView(frame: .zero) From e49ad415f5d9f54f2079554f069b6a6f6054a0b6 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Fri, 25 Nov 2016 13:36:16 +0900 Subject: [PATCH 0626/1028] Update Quick to v1.0.0 --- Cartfile.private | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/Quick | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cartfile.private b/Cartfile.private index c7331aaa2a..b26ca0850c 100644 --- a/Cartfile.private +++ b/Cartfile.private @@ -1,3 +1,3 @@ github "jspahrsummers/xcconfigs" "3d9d996" -github "Quick/Quick" ~> 0.10 +github "Quick/Quick" ~> 1.0 github "Quick/Nimble" ~> 5.1 diff --git a/Cartfile.resolved b/Cartfile.resolved index 718852967c..a2514beab0 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,5 +1,5 @@ github "Quick/Nimble" "v5.1.1" -github "Quick/Quick" "v0.10.0" +github "Quick/Quick" "v1.0.0" github "antitypical/Result" "3.0.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.3" diff --git a/Carthage/Checkouts/Quick b/Carthage/Checkouts/Quick index b441ba9d4a..9c032315fa 160000 --- a/Carthage/Checkouts/Quick +++ b/Carthage/Checkouts/Quick @@ -1 +1 @@ -Subproject commit b441ba9d4abdc87fc153c55de947834b737bbe04 +Subproject commit 9c032315fafca658668d49f6396f1f24b09a83b2 From 86387ea2e77870d91e79971bf6d872e05d771fe4 Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Mon, 28 Nov 2016 20:14:51 -0500 Subject: [PATCH 0627/1028] Only build pushes to master and PRs on CI See https://github.com/ReactiveCocoa/ReactiveSwift/pull/130. --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index ae3b9a3205..5d7a01b810 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,9 @@ before_install: true install: true git: submodules: false +branches: + only: + - master before_script: - git submodule update --init --recursive script: From c9dac3eb5593a70e482eec23afc7f4ade32f3a0f Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 29 Nov 2016 04:44:32 +0100 Subject: [PATCH 0628/1028] Update ReactiveSwift to 1.0.0-alpha.4. --- Cartfile | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- ReactiveCocoa.podspec | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cartfile b/Cartfile index db644ec958..fa2cd36b20 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.3" +github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.4" diff --git a/Cartfile.resolved b/Cartfile.resolved index a2514beab0..974213398c 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -2,4 +2,4 @@ github "Quick/Nimble" "v5.1.1" github "Quick/Quick" "v1.0.0" github "antitypical/Result" "3.0.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" -github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.3" +github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.4" diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index 55345ebd4e..bf9126ac05 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit 55345ebd4ec28baeacb4041a99d56839428bcaff +Subproject commit bf9126ac056d2532ccacd4f648fcdff15f9579e1 diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 14e5d7a6d8..9a408d8c14 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -23,5 +23,5 @@ Pod::Spec.new do |s| s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_map = "ReactiveCocoa/module.modulemap" - s.dependency 'ReactiveSwift', '~> 1.0.0-alpha.3' + s.dependency 'ReactiveSwift', '~> 1.0.0-alpha.4' end From 6c544871b545b6da9e6070746e3a8005c4860512 Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 29 Nov 2016 04:41:57 +0100 Subject: [PATCH 0629/1028] Added `UIView.Type.reactive.keyboardChange`. --- ReactiveCocoa.xcodeproj/project.pbxproj | 8 ++++ ReactiveCocoa/UIKit/iOS/UIKeyboard.swift | 40 +++++++++++++++++++ ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift | 37 +++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 ReactiveCocoa/UIKit/iOS/UIKeyboard.swift create mode 100644 ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 642cd938b9..d633f71906 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -140,6 +140,8 @@ 9A9DFEE91DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; 9A9DFEEA1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; 9A9DFEEB1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; + 9AAD49881DED2C350068EC9B /* UIKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AAD49871DED2C350068EC9B /* UIKeyboard.swift */; }; + 9AAD498A1DED2F380068EC9B /* UIKeyboardSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AAD49891DED2F380068EC9B /* UIKeyboardSpec.swift */; }; 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; 9AD0F06B1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; 9AD0F06C1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; @@ -331,6 +333,8 @@ 9A6AAA271DB8F7EB0013AAEA /* ReusableComponentsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponentsSpec.swift; sourceTree = ""; }; 9A6AAA291DB8F7F10013AAEA /* ReusableComponentsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponentsSpec.swift; sourceTree = ""; }; 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssociationSpec.swift; sourceTree = ""; }; + 9AAD49871DED2C350068EC9B /* UIKeyboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIKeyboard.swift; sourceTree = ""; }; + 9AAD49891DED2F380068EC9B /* UIKeyboardSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIKeyboardSpec.swift; sourceTree = ""; }; 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+KeyValueObserving.swift"; sourceTree = ""; }; 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaAction.swift; sourceTree = ""; }; 9ADE4A881DA6D206005C2AC8 /* NSControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSControl.swift; sourceTree = ""; }; @@ -550,6 +554,7 @@ 9A1D06321D93EA7E00ACF44C /* UITextFieldSpec.swift */, 9A1D06331D93EA7E00ACF44C /* UITextViewSpec.swift */, 9A1D06351D93EA7E00ACF44C /* UIViewSpec.swift */, + 9AAD49891DED2F380068EC9B /* UIKeyboardSpec.swift */, ); path = UIKit; sourceTree = ""; @@ -597,6 +602,7 @@ 53AC46CB1DD6F97400C799E1 /* UISlider.swift */, 531866F71DD7920400D1285F /* UIStepper.swift */, 9A1D05F71D93E9F100ACF44C /* UISwitch.swift */, + 9AAD49871DED2C350068EC9B /* UIKeyboard.swift */, ); path = iOS; sourceTree = ""; @@ -1193,6 +1199,7 @@ CD0C45DF1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9A2E425F1DAA6737006D909F /* CocoaTarget.swift in Sources */, 9A1D06781D94160000ACF44C /* RACObjCRuntimeUtilities.m in Sources */, + 9AAD49881DED2C350068EC9B /* UIKeyboard.swift in Sources */, 53AC46CC1DD6F97400C799E1 /* UISlider.swift in Sources */, 9A1D060F1D93EA0000ACF44C /* UIView.swift in Sources */, 9A1D06051D93EA0000ACF44C /* UIDatePicker.swift in Sources */, @@ -1224,6 +1231,7 @@ buildActionMask = 2147483647; files = ( 9A1D06581D93EA7E00ACF44C /* UIViewSpec.swift in Sources */, + 9AAD498A1DED2F380068EC9B /* UIKeyboardSpec.swift in Sources */, D0A2260F1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */, BFA6B94E1A7604D500C846D1 /* SignalProducerNimbleMatchers.swift in Sources */, B696FB821A7640C00075236D /* TestError.swift in Sources */, diff --git a/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift b/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift new file mode 100644 index 0000000000..27f454627b --- /dev/null +++ b/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift @@ -0,0 +1,40 @@ +import UIKit +import ReactiveSwift +import enum Result.NoError + +public struct KeyboardChangeContext { + private let base: [AnyHashable: Any] + + public var beginFrame: CGRect { + return base[UIKeyboardFrameBeginUserInfoKey] as! CGRect + } + + public var endFrame: CGRect { + return base[UIKeyboardFrameEndUserInfoKey] as! CGRect + } + + public var animationCurve: UIViewAnimationCurve { + return base[UIKeyboardAnimationCurveUserInfoKey] as! UIViewAnimationCurve + } + + public var animationDuration: Double { + return base[UIKeyboardAnimationDurationUserInfoKey] as! Double + } + + @available(iOS 9.0, *) + public var isLocal: Bool { + return base[UIKeyboardIsLocalUserInfoKey] as! Bool + } + + fileprivate init(_ userInfo: [AnyHashable: Any]) { + base = userInfo + } +} + +extension Reactive where Base: UIView { + public static var keyboardChange: Signal { + return NotificationCenter.default.reactive + .notifications(forName: .UIKeyboardWillChangeFrame) + .map { notification in KeyboardChangeContext(notification.userInfo!) } + } +} diff --git a/ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift b/ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift new file mode 100644 index 0000000000..0becf96dc0 --- /dev/null +++ b/ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift @@ -0,0 +1,37 @@ +import UIKit +import ReactiveSwift +import ReactiveCocoa +import Quick +import Nimble + +class UIKeyboardSpec: QuickSpec { + override func spec() { + describe("UIView.Type.reactive.keyboardChange") { + it("should emit a `value` event when the notification is posted") { + var context: KeyboardChangeContext? + + UIView.reactive.keyboardChange.observeValues { c in + context = c + } + + let dummyInfo: [AnyHashable: Any] = [ + UIKeyboardFrameBeginUserInfoKey: CGRect(x: 10, y: 10, width: 10, height: 10), + UIKeyboardFrameEndUserInfoKey: CGRect(x: 20, y: 20, width: 20, height: 20), + UIKeyboardAnimationDurationUserInfoKey: 1.0, + UIKeyboardAnimationCurveUserInfoKey: UIViewAnimationCurve.easeInOut + ] + + NotificationCenter.default.post(name: .UIKeyboardWillChangeFrame, + object: nil, + userInfo: dummyInfo) + + expect(context).toNot(beNil()) + + expect(context?.beginFrame) == CGRect(x: 10, y: 10, width: 10, height: 10) + expect(context?.endFrame) == CGRect(x: 20, y: 20, width: 20, height: 20) + expect(context?.animationCurve) == .easeInOut + expect(context?.animationDuration) == 1.0 + } + } + } +} From d4833b2936326166eb143d89fb8bd0310eefb36c Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Tue, 29 Nov 2016 10:54:31 +0100 Subject: [PATCH 0630/1028] Add ReactiveCocoa specific documentation --- Documentation/README.md | 108 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 2 deletions(-) diff --git a/Documentation/README.md b/Documentation/README.md index 4e0d4ed285..0b56f5dcf9 100644 --- a/Documentation/README.md +++ b/Documentation/README.md @@ -1,2 +1,106 @@ -This folder contains conceptual documentation and design guidelines that don't -fit well on a single class or in any specific header file. +# ReactiveSwift + +ReactiveCocoa is based on ReactiveSwift and extends ReactiveSwift by Cocoa (esp. UIKit and AppKit) specific aspects. + +For documentation of the basics, please refer to the [ReactiveSwift Documentation][ReactiveSwiftDocumentation]. + +This document outlines the additions that ReactiveCocoa brings over ReactiveSwift. + +# Additions in ReactiveCocoa + +## Foundation: Object Interception + +ReactiveCocoa includes a few object interception tools from ReactiveObjC, remastered for use with Swift. + +1. **Method Call Interception** + + Create signals that are sourced by intercepting Objective-C objects. + + ```swift + // Notify after every time `viewWillAppear(_:)` is called. + let appearing = view.reactive.trigger(for: #selector(viewWillAppear(_:))) + ``` + +1. **Object Lifetime** + + Obtain a `Lifetime` token for any `NSObject` to observe their deinitialization. + + ```swift + // Observe the lifetime of `object`. + object.reactive.lifetime.ended.observeCompleted(doCleanup) + ``` + +1. **Dynamic Property** + + The [`DynamicProperty`][] type can be used to bridge to Objective-C APIs that require Key-Value Coding (KVC) or Key-Value Observing (KVO), like `NSOperation`. Note that most AppKit and UIKit properties do _not_ support KVO, so their changes should be observed through other mechanisms. + + For binding UI, [UIKit and AppKit bindings](ui-bindings) provided by ReactiveCocoa are preferred. + + In all other cases, [`MutableProperty`][] should be preferred over dynamic properties whenever possible! + +1. **Expressive, Safe Key Path Observation** + + Establish key-value observations in the form of [`SignalProducer`][]s and + strong-typed [`DynamicProperty`][]s, and enjoy the inherited composability. + + ```swift + // A producer that sends the current value of `keyPath`, followed by + // subsequent changes. + // + // Terminate the KVO observation if the lifetime of `self` ends. + let producer = object.reactive.values(forKeyPath: #keyPath(key)) + .take(during: self.reactive.lifetime) + + // A parameterized property that represents the supplied key path of the + // wrapped object. It holds a weak reference to the wrapped object. + let property = DynamicProperty(object: person, + keyPath: #keyPath(person.name)) + ``` + + These are accessible via the `reactive` magic property that is available on any ObjC objects. + +## UI Bindings + +ReactiveCocoa provides UI bindings for UIKit and AppKit via the `reactive` structure. + +1. **BindingTarget** + + UI components expose [`BindingTarget`][]s, which accept bindings from any + kind of streams of values via the `<~` operator. + + ```swift + // Bind the `name` property of `person` to the text value of an `UILabel`. + nameLabel.reactive.text <~ person.name + ``` + +1. **Controls and User Interactions** + + Interactive UI components expose [`Signal`][]s for control events + and updates in the control value upon user interactions. + + A selected set of controls provide a convenience, expressive binding + API for [`Action`][]s. + + ```swift + // Update `allowsCookies` whenever the toggle is flipped. + preferences.allowsCookies <~ toggle.reactive.isOnValues + + // Compute live character counts from the continuous stream of user initiated + // changes in the text. + textField.reactive.continuousTextValues.map { $0.characters.count } + + // Trigger `commit` whenever the button is pressed. + button.reactive.pressed = CocoaAction(viewModel.commit) + ``` + + These are accessible via the `reactive` magic property that is available on any ObjC objects. + + CocoaAction wraps an Action for use by a GUI control (such as `NSControl` or `UIControl`), with KVO, or with Cocoa Bindings. + +[ReactiveSwiftDocumentation]: https://github.com/ReactiveCocoa/ReactiveSwift/tree/master/Documentation +[`Signal`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#signals +[`SignalProducer`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#signal-producers +[`Action`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#actions +[`BindingTarget`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#binding-target +[`MutableProperty`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Sources/Property.swift#L534 +[`DynamicProperty`]: https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/ReactiveCocoa/DynamicProperty.swift From 82e2d87f075a8b41b682a65d646622bb8a95108c Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 29 Nov 2016 04:44:32 +0100 Subject: [PATCH 0631/1028] Update ReactiveSwift to 1.0.0-alpha.4. --- Cartfile | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- ReactiveCocoa.podspec | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cartfile b/Cartfile index db644ec958..fa2cd36b20 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.3" +github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.4" diff --git a/Cartfile.resolved b/Cartfile.resolved index a2514beab0..974213398c 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -2,4 +2,4 @@ github "Quick/Nimble" "v5.1.1" github "Quick/Quick" "v1.0.0" github "antitypical/Result" "3.0.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" -github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.3" +github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.4" diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index 55345ebd4e..bf9126ac05 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit 55345ebd4ec28baeacb4041a99d56839428bcaff +Subproject commit bf9126ac056d2532ccacd4f648fcdff15f9579e1 diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 14e5d7a6d8..9a408d8c14 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -23,5 +23,5 @@ Pod::Spec.new do |s| s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_map = "ReactiveCocoa/module.modulemap" - s.dependency 'ReactiveSwift', '~> 1.0.0-alpha.3' + s.dependency 'ReactiveSwift', '~> 1.0.0-alpha.4' end From 62481dd5f5731459ec2b765cca1343c9ce02b4cc Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 29 Nov 2016 21:45:34 +0100 Subject: [PATCH 0632/1028] Moved `keyboardChange` back to `Reactive`. --- ReactiveCocoa/UIKit/iOS/UIKeyboard.swift | 22 +++++++++++++++---- ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift | 15 +++++++------ 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift b/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift index 27f454627b..b69cc01eb1 100644 --- a/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift +++ b/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift @@ -2,25 +2,35 @@ import UIKit import ReactiveSwift import enum Result.NoError +/// The context of an upcoming change in the frame of the system keyboard. public struct KeyboardChangeContext { private let base: [AnyHashable: Any] + /// The current frame of the system keyboard. public var beginFrame: CGRect { return base[UIKeyboardFrameBeginUserInfoKey] as! CGRect } + /// The final frame of the system keyboard. public var endFrame: CGRect { return base[UIKeyboardFrameEndUserInfoKey] as! CGRect } + /// The animation curve which the system keyboard will use to animate the + /// change in its frame. public var animationCurve: UIViewAnimationCurve { return base[UIKeyboardAnimationCurveUserInfoKey] as! UIViewAnimationCurve } + /// The duration in which the system keyboard expects to animate the change in + /// its frame. public var animationDuration: Double { return base[UIKeyboardAnimationDurationUserInfoKey] as! Double } + /// Indicates whether the change is triggered locally. Used in iPad + /// multitasking, where all foreground apps would be notified of any changes + /// in the system keyboard's frame. @available(iOS 9.0, *) public var isLocal: Bool { return base[UIKeyboardIsLocalUserInfoKey] as! Bool @@ -31,10 +41,14 @@ public struct KeyboardChangeContext { } } -extension Reactive where Base: UIView { - public static var keyboardChange: Signal { - return NotificationCenter.default.reactive - .notifications(forName: .UIKeyboardWillChangeFrame) +extension Reactive where Base: NotificationCenter { + /// Create a `Signal` that nofities whenever the system keyboard announces an + /// upcoming change in its frame. + /// + /// - returns: A `Signal` that emits the context of every change in the + /// system keyboard's frame. + public var keyboardChange: Signal { + return notifications(forName: .UIKeyboardWillChangeFrame) .map { notification in KeyboardChangeContext(notification.userInfo!) } } } diff --git a/ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift b/ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift index 0becf96dc0..35e0b0441c 100644 --- a/ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift @@ -6,13 +6,14 @@ import Nimble class UIKeyboardSpec: QuickSpec { override func spec() { - describe("UIView.Type.reactive.keyboardChange") { + describe("NotificationCenter.reactive.keyboardChange") { it("should emit a `value` event when the notification is posted") { var context: KeyboardChangeContext? - UIView.reactive.keyboardChange.observeValues { c in - context = c - } + let testCenter = NotificationCenter() + + testCenter.reactive.keyboardChange + .observeValues { context = $0 } let dummyInfo: [AnyHashable: Any] = [ UIKeyboardFrameBeginUserInfoKey: CGRect(x: 10, y: 10, width: 10, height: 10), @@ -21,9 +22,9 @@ class UIKeyboardSpec: QuickSpec { UIKeyboardAnimationCurveUserInfoKey: UIViewAnimationCurve.easeInOut ] - NotificationCenter.default.post(name: .UIKeyboardWillChangeFrame, - object: nil, - userInfo: dummyInfo) + testCenter.post(name: .UIKeyboardWillChangeFrame, + object: nil, + userInfo: dummyInfo) expect(context).toNot(beNil()) From 7019638abfed4a3a66c3418df23d57410d629feb Mon Sep 17 00:00:00 2001 From: Caleb Davenport Date: Tue, 29 Nov 2016 15:30:41 -0500 Subject: [PATCH 0633/1028] Add reactive refresh control. --- ReactiveCocoa.xcodeproj/project.pbxproj | 8 ++++ .../UIKit/iOS/UIRefreshControl.swift | 13 ++++++ .../UIKit/UIRefreshControlSpec.swift | 45 +++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift create mode 100644 ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index d633f71906..a32b30963d 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 3BCAAC7A1DEE19BC00B30335 /* UIRefreshControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BCAAC791DEE19BC00B30335 /* UIRefreshControl.swift */; }; + 3BCAAC7D1DEE1A2D00B30335 /* UIRefreshControlSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BCAAC7B1DEE1A2300B30335 /* UIRefreshControlSpec.swift */; }; 419139461DB910570043C9D1 /* UIGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 419139431DB910570043C9D1 /* UIGestureRecognizer.swift */; }; 419139491DB910570043C9D1 /* UIGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 419139431DB910570043C9D1 /* UIGestureRecognizer.swift */; }; 4191394E1DBA01A00043C9D1 /* UIGestureRecognizerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4191394B1DBA002C0043C9D1 /* UIGestureRecognizerSpec.swift */; }; @@ -268,6 +270,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 3BCAAC791DEE19BC00B30335 /* UIRefreshControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIRefreshControl.swift; sourceTree = ""; }; + 3BCAAC7B1DEE1A2300B30335 /* UIRefreshControlSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIRefreshControlSpec.swift; sourceTree = ""; }; 419139431DB910570043C9D1 /* UIGestureRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIGestureRecognizer.swift; sourceTree = ""; }; 4191394B1DBA002C0043C9D1 /* UIGestureRecognizerSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIGestureRecognizerSpec.swift; sourceTree = ""; }; 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Lifetime.swift"; sourceTree = ""; }; @@ -555,6 +559,7 @@ 9A1D06331D93EA7E00ACF44C /* UITextViewSpec.swift */, 9A1D06351D93EA7E00ACF44C /* UIViewSpec.swift */, 9AAD49891DED2F380068EC9B /* UIKeyboardSpec.swift */, + 3BCAAC7B1DEE1A2300B30335 /* UIRefreshControlSpec.swift */, ); path = UIKit; sourceTree = ""; @@ -603,6 +608,7 @@ 531866F71DD7920400D1285F /* UIStepper.swift */, 9A1D05F71D93E9F100ACF44C /* UISwitch.swift */, 9AAD49871DED2C350068EC9B /* UIKeyboard.swift */, + 3BCAAC791DEE19BC00B30335 /* UIRefreshControl.swift */, ); path = iOS; sourceTree = ""; @@ -1223,6 +1229,7 @@ 9A1D05FF1D93EA0000ACF44C /* UIActivityIndicatorView.swift in Sources */, 9A1D06091D93EA0000ACF44C /* UISegmentedControl.swift in Sources */, 9ADE4A7D1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, + 3BCAAC7A1DEE19BC00B30335 /* UIRefreshControl.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1251,6 +1258,7 @@ 9A1D06421D93EA7E00ACF44C /* UIDatePickerSpec.swift in Sources */, 53AC46CF1DD6FC0000C799E1 /* UISliderSpec.swift in Sources */, 9A6AAA0F1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */, + 3BCAAC7D1DEE1A2D00B30335 /* UIRefreshControlSpec.swift in Sources */, 53A6BED71DD4BD2C0016C058 /* MKMapViewSpec.swift in Sources */, 9A1D06381D93EA7E00ACF44C /* UIBarButtonItemSpec.swift in Sources */, 9A1E72BB1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, diff --git a/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift b/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift new file mode 100644 index 0000000000..5ea1c34fa5 --- /dev/null +++ b/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift @@ -0,0 +1,13 @@ +import ReactiveSwift +import enum Result.NoError +import UIKit + +extension Reactive where Base: UIRefreshControl { + public var isRefreshing: BindingTarget { + return makeBindingTarget { $1 ? $0.beginRefreshing() : $0.endRefreshing() } + } + + public var refresh: Signal { + return trigger(for: .valueChanged) + } +} diff --git a/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift b/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift new file mode 100644 index 0000000000..868e9981de --- /dev/null +++ b/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift @@ -0,0 +1,45 @@ +import Quick +import Nimble +import ReactiveSwift +import ReactiveCocoa +import Result + +class UIRefreshControlSpec: QuickSpec { + override func spec() { + var refreshControl: UIRefreshControl! + weak var _refreshControl: UIRefreshControl! + + beforeEach { + refreshControl = UIRefreshControl() + _refreshControl = refreshControl + } + + afterEach { + refreshControl = nil + expect(_refreshControl).to(beNil()) + } + + it("should accept changes from bindings to its refreshing state") { + let (pipeSignal, observer) = Signal.pipe() + refreshControl.reactive.isRefreshing <~ SignalProducer(signal: pipeSignal) + + observer.send(value: true) + expect(refreshControl.isRefreshing) == true + + observer.send(value: false) + expect(refreshControl.isRefreshing) == false + } + + it("should emit user's changes for its value") { + var count = 0 + + refreshControl.reactive.refresh.observeValues { + count += 1 + } + + expect(count) == 0 + refreshControl.sendActions(for: .valueChanged) + expect(count) == 1 + } + } +} From 4135229f6274e05764077ef64b166b5721aa77ae Mon Sep 17 00:00:00 2001 From: Caleb Davenport Date: Tue, 29 Nov 2016 15:32:09 -0500 Subject: [PATCH 0634/1028] Add comments. --- ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift b/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift index 5ea1c34fa5..917238d339 100644 --- a/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift +++ b/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift @@ -3,10 +3,12 @@ import enum Result.NoError import UIKit extension Reactive where Base: UIRefreshControl { + /// Sets whether the activity indicator should be refreshing. public var isRefreshing: BindingTarget { return makeBindingTarget { $1 ? $0.beginRefreshing() : $0.endRefreshing() } } + /// A trigger that sends `next` when the user performs a refresh. public var refresh: Signal { return trigger(for: .valueChanged) } From 3cd43b1fe85b8c3542f367269383f378df93de33 Mon Sep 17 00:00:00 2001 From: Caleb Davenport Date: Tue, 29 Nov 2016 15:49:24 -0500 Subject: [PATCH 0635/1028] Add attributed title binding. --- ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift | 7 ++++++- .../UIKit/UIRefreshControlSpec.swift | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift b/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift index 917238d339..c6c4f15728 100644 --- a/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift +++ b/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift @@ -3,11 +3,16 @@ import enum Result.NoError import UIKit extension Reactive where Base: UIRefreshControl { - /// Sets whether the activity indicator should be refreshing. + /// Sets whether the refresh control should be refreshing. public var isRefreshing: BindingTarget { return makeBindingTarget { $1 ? $0.beginRefreshing() : $0.endRefreshing() } } + /// Sets the attributed title of the refresh control. + public var attributedTitle: BindingTarget { + return makeBindingTarget { $0.attributedTitle = $1 } + } + /// A trigger that sends `next` when the user performs a refresh. public var refresh: Signal { return trigger(for: .valueChanged) diff --git a/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift b/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift index 868e9981de..d7ec1e2582 100644 --- a/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift @@ -30,6 +30,22 @@ class UIRefreshControlSpec: QuickSpec { expect(refreshControl.isRefreshing) == false } + it("should accept changes from bindings to its attributed title state") { + let (pipeSignal, observer) = Signal.pipe() + refreshControl.reactive.attributedTitle <~ SignalProducer(signal: pipeSignal) + + let string = NSAttributedString(string: "test") + + observer.send(value: nil) + expect(refreshControl.attributedTitle).to(beNil()) + + observer.send(value: string) + expect(refreshControl.attributedTitle) == string + + observer.send(value: nil) + expect(refreshControl.attributedTitle).to(beNil()) + } + it("should emit user's changes for its value") { var count = 0 From 14dbc73332bf3cae8d47b66729a3b9d08c6a45fa Mon Sep 17 00:00:00 2001 From: Caleb Davenport Date: Wed, 30 Nov 2016 20:15:54 -0500 Subject: [PATCH 0636/1028] Remove `refresh` signal and add `refreshed` action to UIRefreshControl. --- ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift | 17 ++++++++++++++--- .../UIKit/UIRefreshControlSpec.swift | 18 ++++++++++++------ 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift b/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift index c6c4f15728..e85d2ecd86 100644 --- a/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift +++ b/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift @@ -13,8 +13,19 @@ extension Reactive where Base: UIRefreshControl { return makeBindingTarget { $0.attributedTitle = $1 } } - /// A trigger that sends `next` when the user performs a refresh. - public var refresh: Signal { - return trigger(for: .valueChanged) + /// The action to be triggered when the refresh control is refreshed. It + /// also controls the enabled state of the refresh control. + public var refreshed: CocoaAction? { + get { + return associatedAction.withValue { info in + return info.flatMap { info in + return info.controlEvents == .valueChanged ? info.action : nil + } + } + } + + nonmutating set { + setAction(newValue, for: .valueChanged) + } } } diff --git a/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift b/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift index d7ec1e2582..4c782a9428 100644 --- a/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift @@ -46,16 +46,22 @@ class UIRefreshControlSpec: QuickSpec { expect(refreshControl.attributedTitle).to(beNil()) } - it("should emit user's changes for its value") { - var count = 0 + it("should execute the `refreshed` action upon receiving a `valueChanged` action message.") { + refreshControl.isEnabled = true + refreshControl.isUserInteractionEnabled = true - refreshControl.reactive.refresh.observeValues { - count += 1 + let refreshed = MutableProperty(false) + let action = Action<(), Bool, NoError> { _ in + SignalProducer(value: true) } - expect(count) == 0 + refreshed <~ SignalProducer(signal: action.values) + + refreshControl.reactive.refreshed = CocoaAction(action) + expect(refreshed.value) == false + refreshControl.sendActions(for: .valueChanged) - expect(count) == 1 + expect(refreshed.value) == true } } } From 09317fc6e5e47689bcf8ef8186ffc9c08d90916c Mon Sep 17 00:00:00 2001 From: Caleb Davenport Date: Thu, 1 Dec 2016 11:03:26 -0500 Subject: [PATCH 0637/1028] Bind action isExecuting to refresh control isRefreshing. --- .../UIKit/iOS/UIRefreshControl.swift | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift b/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift index e85d2ecd86..2abcb28a37 100644 --- a/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift +++ b/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift @@ -14,7 +14,7 @@ extension Reactive where Base: UIRefreshControl { } /// The action to be triggered when the refresh control is refreshed. It - /// also controls the enabled state of the refresh control. + /// also controls the enabled and refreshing states of the refresh control. public var refreshed: CocoaAction? { get { return associatedAction.withValue { info in @@ -25,7 +25,26 @@ extension Reactive where Base: UIRefreshControl { } nonmutating set { - setAction(newValue, for: .valueChanged) + associatedAction.modify { associatedAction in + associatedAction?.disposable.dispose() + + let controlEvents = UIControlEvents.valueChanged + + if let action = newValue { + base.addTarget(action, action: CocoaAction.selector, for: controlEvents) + + let disposable = CompositeDisposable() + disposable += isEnabled <~ action.isEnabled + disposable += isRefreshing <~ action.isExecuting + disposable += { [weak base = self.base] in + base?.removeTarget(action, action: CocoaAction.selector, for: controlEvents) + } + + associatedAction = (action, controlEvents, ScopedDisposable(disposable)) + } else { + associatedAction = nil + } + } } } } From 5a443bb79312424e1351dda474633cbe2e57f953 Mon Sep 17 00:00:00 2001 From: Caleb Davenport Date: Thu, 1 Dec 2016 11:04:50 -0500 Subject: [PATCH 0638/1028] Rename refresh action. --- ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift | 2 +- ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift b/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift index 2abcb28a37..e27ea73286 100644 --- a/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift +++ b/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift @@ -15,7 +15,7 @@ extension Reactive where Base: UIRefreshControl { /// The action to be triggered when the refresh control is refreshed. It /// also controls the enabled and refreshing states of the refresh control. - public var refreshed: CocoaAction? { + public var refresh: CocoaAction? { get { return associatedAction.withValue { info in return info.flatMap { info in diff --git a/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift b/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift index 4c782a9428..5291271d8e 100644 --- a/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift @@ -57,7 +57,7 @@ class UIRefreshControlSpec: QuickSpec { refreshed <~ SignalProducer(signal: action.values) - refreshControl.reactive.refreshed = CocoaAction(action) + refreshControl.reactive.refresh = CocoaAction(action) expect(refreshed.value) == false refreshControl.sendActions(for: .valueChanged) From b89e774b69bfb6a0aa820ebfe08fcc68b5077662 Mon Sep 17 00:00:00 2001 From: Caleb Davenport Date: Thu, 1 Dec 2016 11:13:47 -0500 Subject: [PATCH 0639/1028] Add tests for isRefreshing <~ isExecuting binding. --- .../UIKit/UIRefreshControlSpec.swift | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift b/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift index 5291271d8e..6b4a8e8890 100644 --- a/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift @@ -63,5 +63,22 @@ class UIRefreshControlSpec: QuickSpec { refreshControl.sendActions(for: .valueChanged) expect(refreshed.value) == true } + + it("should set `isRefreshing` while `refresh` is executing.") { + refreshControl.isEnabled = true + refreshControl.isUserInteractionEnabled = true + + let action = Action<(), Bool, NoError> { _ in + SignalProducer(value: true).delay(1, on: QueueScheduler.main) + } + + refreshControl.reactive.refresh = CocoaAction(action) + expect(refreshControl.isRefreshing) == false + + refreshControl.sendActions(for: .valueChanged) + expect(refreshControl.isRefreshing) == true + + expect(refreshControl.isRefreshing).toEventually(equal(false), timeout: 2) + } } } From 0f4af81e84f78b3b1eeaa3f559ddd59ebf8a59db Mon Sep 17 00:00:00 2001 From: Caleb Davenport Date: Thu, 1 Dec 2016 11:31:49 -0500 Subject: [PATCH 0640/1028] Reduce code duplication. --- ReactiveCocoa/UIKit/UIControl.swift | 4 +++- .../UIKit/iOS/UIRefreshControl.swift | 22 ++----------------- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index ef586a2128..2aa663ee98 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -15,7 +15,8 @@ extension Reactive where Base: UIControl { /// - parameters: /// - action: The action to be associated. /// - controlEvents: The control event mask. - internal func setAction(_ action: CocoaAction?, for controlEvents: UIControlEvents) { + /// - disposable: An external disposable. + internal func setAction(_ action: CocoaAction?, for controlEvents: UIControlEvents, disposable extraDisposable: Disposable? = nil) { associatedAction.modify { associatedAction in associatedAction?.disposable.dispose() @@ -27,6 +28,7 @@ extension Reactive where Base: UIControl { disposable += { [weak base = self.base] in base?.removeTarget(action, action: CocoaAction.selector, for: controlEvents) } + disposable += extraDisposable associatedAction = (action, controlEvents, ScopedDisposable(disposable)) } else { diff --git a/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift b/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift index e27ea73286..62139832de 100644 --- a/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift +++ b/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift @@ -25,26 +25,8 @@ extension Reactive where Base: UIRefreshControl { } nonmutating set { - associatedAction.modify { associatedAction in - associatedAction?.disposable.dispose() - - let controlEvents = UIControlEvents.valueChanged - - if let action = newValue { - base.addTarget(action, action: CocoaAction.selector, for: controlEvents) - - let disposable = CompositeDisposable() - disposable += isEnabled <~ action.isEnabled - disposable += isRefreshing <~ action.isExecuting - disposable += { [weak base = self.base] in - base?.removeTarget(action, action: CocoaAction.selector, for: controlEvents) - } - - associatedAction = (action, controlEvents, ScopedDisposable(disposable)) - } else { - associatedAction = nil - } - } + let disposable = action.map { isRefreshing <~ $0.isExecuting } + setAction(newValue, for: .touchUpInside, disposable: disposable) } } } From 209d4b0b38cd0bf9cce26fe2bd02267455012bde Mon Sep 17 00:00:00 2001 From: Caleb Davenport Date: Thu, 1 Dec 2016 11:37:13 -0500 Subject: [PATCH 0641/1028] Rename internal property names. --- ReactiveCocoa/UIKit/UIControl.swift | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index 2aa663ee98..9db5b2acf5 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -15,22 +15,23 @@ extension Reactive where Base: UIControl { /// - parameters: /// - action: The action to be associated. /// - controlEvents: The control event mask. - /// - disposable: An external disposable. - internal func setAction(_ action: CocoaAction?, for controlEvents: UIControlEvents, disposable extraDisposable: Disposable? = nil) { + /// - disposable: An outside disposable that will be bound to the scope of + /// the given `action`. + internal func setAction(_ action: CocoaAction?, for controlEvents: UIControlEvents, disposable: Disposable? = nil) { associatedAction.modify { associatedAction in associatedAction?.disposable.dispose() if let action = action { base.addTarget(action, action: CocoaAction.selector, for: controlEvents) - let disposable = CompositeDisposable() - disposable += isEnabled <~ action.isEnabled - disposable += { [weak base = self.base] in + let compositeDisposable = CompositeDisposable() + compositeDisposable += isEnabled <~ action.isEnabled + compositeDisposable += { [weak base = self.base] in base?.removeTarget(action, action: CocoaAction.selector, for: controlEvents) } - disposable += extraDisposable + compositeDisposable += disposable - associatedAction = (action, controlEvents, ScopedDisposable(disposable)) + associatedAction = (action, controlEvents, ScopedDisposable(compositeDisposable)) } else { associatedAction = nil } From 6a62cb0d0e2a5fc6bb41c5987da9515add6cdbf2 Mon Sep 17 00:00:00 2001 From: Caleb Davenport Date: Thu, 1 Dec 2016 11:54:20 -0500 Subject: [PATCH 0642/1028] Update test name. --- ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift b/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift index 6b4a8e8890..6d01c21123 100644 --- a/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift @@ -46,7 +46,7 @@ class UIRefreshControlSpec: QuickSpec { expect(refreshControl.attributedTitle).to(beNil()) } - it("should execute the `refreshed` action upon receiving a `valueChanged` action message.") { + it("should execute the `refresh` action upon receiving a `valueChanged` action message.") { refreshControl.isEnabled = true refreshControl.isUserInteractionEnabled = true From 434955cbb971c66617743f02787ba9622025330b Mon Sep 17 00:00:00 2001 From: Adam Sharp Date: Thu, 1 Dec 2016 13:35:10 -0500 Subject: [PATCH 0643/1028] Send the UIControl sender for each control event Changes `Reactive.trigger(for:)` to emit the control itself for each value, and as it is no longer just a trigger signal, renames it `controlEvents(_:)`. Streamlines the definition of the various value-changed convenience signals, and removes the need to capture an extra unowned reference. --- ReactiveCocoa/UIKit/UIControl.swift | 17 +++++++++++------ ReactiveCocoa/UIKit/UISegmentedControl.swift | 3 +-- ReactiveCocoa/UIKit/UITextField.swift | 12 ++++-------- ReactiveCocoa/UIKit/iOS/UIDatePicker.swift | 3 +-- ReactiveCocoa/UIKit/iOS/UISlider.swift | 3 +-- ReactiveCocoa/UIKit/iOS/UIStepper.swift | 3 +-- ReactiveCocoa/UIKit/iOS/UISwitch.swift | 3 +-- 7 files changed, 20 insertions(+), 24 deletions(-) diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index ef586a2128..9bf62ab40c 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -35,20 +35,20 @@ extension Reactive where Base: UIControl { } } - /// Create a signal which sends a `next` event for each of the specified + /// Create a signal which sends a `value` event for each of the specified /// control events. /// /// - parameters: /// - controlEvents: The control event mask. /// /// - returns: - /// A trigger signal. - public func trigger(for controlEvents: UIControlEvents) -> Signal<(), NoError> { + /// A signal that sends the control each time the control event occurs. + public func controlEvents(_ controlEvents: UIControlEvents) -> Signal { return Signal { observer in - let receiver = CocoaTarget(observer) + let receiver = CocoaTarget(observer) { $0 as! Base } base.addTarget(receiver, - action: #selector(receiver.sendNext), - for: controlEvents) + action: #selector(receiver.sendNext), + for: controlEvents) let disposable = lifetime.ended.observeCompleted(observer.sendCompleted) @@ -62,6 +62,11 @@ extension Reactive where Base: UIControl { } } + @available(*, unavailable, renamed: "controlEvents(_:)") + public func trigger(for controlEvents: UIControlEvents) -> Signal<(), NoError> { + fatalError() + } + /// Sets whether the control is enabled. public var isEnabled: BindingTarget { return makeBindingTarget { $0.isEnabled = $1 } diff --git a/ReactiveCocoa/UIKit/UISegmentedControl.swift b/ReactiveCocoa/UIKit/UISegmentedControl.swift index b4b275a939..db191a315a 100644 --- a/ReactiveCocoa/UIKit/UISegmentedControl.swift +++ b/ReactiveCocoa/UIKit/UISegmentedControl.swift @@ -10,7 +10,6 @@ extension Reactive where Base: UISegmentedControl { /// A signal of indexes of selections emitted by the segmented control. public var selectedSegmentIndexes: Signal { - return trigger(for: .valueChanged) - .map { [unowned base = self.base] in base.selectedSegmentIndex } + return controlEvents(.valueChanged).map { $0.selectedSegmentIndex } } } diff --git a/ReactiveCocoa/UIKit/UITextField.swift b/ReactiveCocoa/UIKit/UITextField.swift index e6f808766e..dbddfb1559 100644 --- a/ReactiveCocoa/UIKit/UITextField.swift +++ b/ReactiveCocoa/UIKit/UITextField.swift @@ -13,16 +13,14 @@ extension Reactive where Base: UITextField { /// - note: To observe text values that change on all editing events, /// see `continuousTextValues`. public var textValues: Signal { - return trigger(for: .editingDidEnd) - .map { [unowned base = self.base] in base.text } + return controlEvents(.editingDidEnd).map { $0.text } } /// A signal of text values emitted by the text field upon any changes. /// /// - note: To observe text values only when editing ends, see `textValues`. public var continuousTextValues: Signal { - return trigger(for: .editingChanged) - .map { [unowned base = self.base] in base.text } + return controlEvents(.editingChanged).map { $0.text } } /// Sets the attributed text of the text field. @@ -35,15 +33,13 @@ extension Reactive where Base: UITextField { /// - note: To observe attributed text values that change on all editing events, /// see `continuousAttributedTextValues`. public var attributedTextValues: Signal { - return trigger(for: .editingDidEnd) - .map { [unowned base = self.base] in base.attributedText } + return controlEvents(.editingDidEnd).map { $0.attributedText } } /// A signal of attributed text values emitted by the text field upon any changes. /// /// - note: To observe attributed text values only when editing ends, see `attributedTextValues`. public var continuousAttributedTextValues: Signal { - return trigger(for: .editingChanged) - .map { [unowned base = self.base] in base.attributedText } + return controlEvents(.editingChanged).map { $0.attributedText } } } diff --git a/ReactiveCocoa/UIKit/iOS/UIDatePicker.swift b/ReactiveCocoa/UIKit/iOS/UIDatePicker.swift index 4d00c65c72..a1a558a728 100644 --- a/ReactiveCocoa/UIKit/iOS/UIDatePicker.swift +++ b/ReactiveCocoa/UIKit/iOS/UIDatePicker.swift @@ -10,7 +10,6 @@ extension Reactive where Base: UIDatePicker { /// A signal of dates emitted by the date picker. public var dates: Signal { - return trigger(for: .valueChanged) - .map { [unowned base = self.base] in base.date } + return controlEvents(.valueChanged).map { $0.date } } } diff --git a/ReactiveCocoa/UIKit/iOS/UISlider.swift b/ReactiveCocoa/UIKit/iOS/UISlider.swift index 03f9638a90..8744660e35 100644 --- a/ReactiveCocoa/UIKit/iOS/UISlider.swift +++ b/ReactiveCocoa/UIKit/iOS/UISlider.swift @@ -25,7 +25,6 @@ extension Reactive where Base: UISlider { /// - note: If slider's `isContinuous` property is `false` then values are /// sent only when user releases the slider. public var values: Signal { - return trigger(for: .valueChanged) - .map { [unowned base = self.base] in base.value } + return controlEvents(.valueChanged).map { $0.value } } } diff --git a/ReactiveCocoa/UIKit/iOS/UIStepper.swift b/ReactiveCocoa/UIKit/iOS/UIStepper.swift index 12c4823dad..c0c88865d0 100644 --- a/ReactiveCocoa/UIKit/iOS/UIStepper.swift +++ b/ReactiveCocoa/UIKit/iOS/UIStepper.swift @@ -22,7 +22,6 @@ extension Reactive where Base: UIStepper { /// A signal of double values emitted by the stepper upon each user's /// interaction. public var values: Signal { - return trigger(for: .valueChanged) - .map { [unowned base = self.base] in base.value } + return controlEvents(.valueChanged).map { $0.value } } } diff --git a/ReactiveCocoa/UIKit/iOS/UISwitch.swift b/ReactiveCocoa/UIKit/iOS/UISwitch.swift index 42e13588ea..8b26b67d88 100644 --- a/ReactiveCocoa/UIKit/iOS/UISwitch.swift +++ b/ReactiveCocoa/UIKit/iOS/UISwitch.swift @@ -10,7 +10,6 @@ extension Reactive where Base: UISwitch { /// A signal of on-off states in `Bool` emitted by the switch. public var isOnValues: Signal { - return trigger(for: .valueChanged) - .map { [unowned base = self.base] in base.isOn } + return controlEvents(.valueChanged).map { $0.isOn } } } From 5e2ee39ff31264ecff89a77c90f88d616176c792 Mon Sep 17 00:00:00 2001 From: Caleb Davenport Date: Sat, 3 Dec 2016 17:42:02 -0500 Subject: [PATCH 0644/1028] Fix the build. --- ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift b/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift index 62139832de..4a9c856407 100644 --- a/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift +++ b/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift @@ -25,8 +25,8 @@ extension Reactive where Base: UIRefreshControl { } nonmutating set { - let disposable = action.map { isRefreshing <~ $0.isExecuting } - setAction(newValue, for: .touchUpInside, disposable: disposable) + let disposable = newValue.map { isRefreshing <~ $0.isExecuting } + setAction(newValue, for: .valueChanged, disposable: disposable) } } } From 43403320ed3b417510241a2943a61e50a23691d0 Mon Sep 17 00:00:00 2001 From: Anders Date: Mon, 5 Dec 2016 23:05:08 +0100 Subject: [PATCH 0645/1028] Fixed a `Lifetime` related KVO crash. --- ReactiveCocoa/NSObject+Lifetime.swift | 43 +++++++++++++++++++ ReactiveCocoa/RACObjCRuntimeUtilities.h | 3 ++ ReactiveCocoa/RACObjCRuntimeUtilities.m | 5 +++ .../KeyValueObservingSpec.swift | 13 ++++++ 4 files changed, 64 insertions(+) diff --git a/ReactiveCocoa/NSObject+Lifetime.swift b/ReactiveCocoa/NSObject+Lifetime.swift index b9f739a4c4..d9d95e1269 100644 --- a/ReactiveCocoa/NSObject+Lifetime.swift +++ b/ReactiveCocoa/NSObject+Lifetime.swift @@ -15,6 +15,44 @@ extension Reactive where Base: NSObject { let token = Lifetime.Token() let lifetime = Lifetime(token) + let objcClass: AnyClass = (base as AnyObject).objcClass + let deallocSelector = sel_registerName("dealloc")! + + // Swizzle `-dealloc` so that the lifetime token is released at the + // beginning of the deallocation chain, and only after the KVO `-dealloc`. + objc_sync_enter(objcClass) + if objc_getAssociatedObject(objcClass, &lifetimeKey) == nil { + objc_setAssociatedObject(objcClass, &lifetimeKey, true, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + + var existingImpl: IMP? = nil + + let newImplBlock: @convention(block) (UnsafeRawPointer) -> Void = { objectRef in + _rac_objc_setAssociatedObject(objectRef, &lifetimeTokenKey, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + + let impl: IMP + + if let existingImpl = existingImpl { + impl = existingImpl + } else { + let superclass: AnyClass = class_getSuperclass(objcClass) + impl = class_getMethodImplementation(superclass, deallocSelector) + } + + typealias Impl = @convention(c) (UnsafeRawPointer, Selector) -> Void + unsafeBitCast(impl, to: Impl.self)(objectRef, deallocSelector) + } + + let newImpl = imp_implementationWithBlock(newImplBlock as Any) + + if !class_addMethod(objcClass, deallocSelector, newImpl, "v@:") { + // The class has an existing `dealloc`. Preserve that as `existingImpl`. + let deallocMethod = class_getInstanceMethod(objcClass, deallocSelector) + existingImpl = method_getImplementation(deallocMethod) + existingImpl = method_setImplementation(deallocMethod, newImpl) + } + } + objc_sync_exit(objcClass) + objc_setAssociatedObject(base, &lifetimeTokenKey, token, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) objc_setAssociatedObject(base, &lifetimeKey, lifetime, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) @@ -22,3 +60,8 @@ extension Reactive where Base: NSObject { } } } + +@objc private protocol ObjCClassReporting { + @objc(class) + var objcClass: AnyClass { get } +} diff --git a/ReactiveCocoa/RACObjCRuntimeUtilities.h b/ReactiveCocoa/RACObjCRuntimeUtilities.h index d08364d8a4..b89a4f3ca8 100644 --- a/ReactiveCocoa/RACObjCRuntimeUtilities.h +++ b/ReactiveCocoa/RACObjCRuntimeUtilities.h @@ -1,6 +1,9 @@ #import +#import NS_ASSUME_NONNULL_BEGIN +void _rac_objc_setAssociatedObject(const void* object, const void* key, id _Nullable value, objc_AssociationPolicy policy); + @interface NSObject (RACObjCRuntimeUtilities) /// Register a block which would be triggered when `selector` is called. diff --git a/ReactiveCocoa/RACObjCRuntimeUtilities.m b/ReactiveCocoa/RACObjCRuntimeUtilities.m index f3c33dae34..ba5bd8d5e7 100644 --- a/ReactiveCocoa/RACObjCRuntimeUtilities.m +++ b/ReactiveCocoa/RACObjCRuntimeUtilities.m @@ -3,6 +3,11 @@ #import #import +void _rac_objc_setAssociatedObject(const void* object, const void* key, id value, objc_AssociationPolicy policy) { + __unsafe_unretained id obj = (__bridge typeof(obj)) object; + objc_setAssociatedObject(obj, key, value, policy); +} + NSString * const RACSelectorSignalErrorDomain = @"RACSelectorSignalErrorDomain"; const NSInteger RACSelectorSignalErrorMethodSwizzlingRace = 1; const NSExceptionName RACSwizzleException = @"RACSwizzleException"; diff --git a/ReactiveCocoaTests/KeyValueObservingSpec.swift b/ReactiveCocoaTests/KeyValueObservingSpec.swift index 93d48db4dc..02e92639e8 100644 --- a/ReactiveCocoaTests/KeyValueObservingSpec.swift +++ b/ReactiveCocoaTests/KeyValueObservingSpec.swift @@ -154,6 +154,19 @@ class KeyValueObservingSpec: QuickSpec { expect(weakOriginalInner).to(beNil()) } + describe("special tests") { + it("should not crash an Operation") { + // Related issue: + // https://github.com/ReactiveCocoa/ReactiveCocoa/issues/3329 + func invoke() { + let op = Operation() + op.reactive.values(forKeyPath: "isFinished").start() + } + + invoke() + } + } + describe("thread safety") { var testObject: ObservableObject! var concurrentQueue: DispatchQueue! From 49dd8dc8ffc5b399f52bdb6ea2acfd44512c8559 Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 6 Dec 2016 11:52:22 +0100 Subject: [PATCH 0646/1028] Use `Unmanaged` references in the message forwarding implementation. --- ReactiveCocoa/NSObject+Intercepting.swift | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 0301afa4ba..dc2af963d9 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -144,14 +144,14 @@ private func setupInterception(_ object: NSObject, for selector: Selector) -> Si private func enableMessageForwarding(_ realClass: AnyClass) { let perceivedClass: AnyClass = class_getSuperclass(realClass) - typealias ForwardInvocationImpl = @convention(block) (NSObject, AnyObject) -> Void - let newForwardInvocation: ForwardInvocationImpl = { object, invocation in + typealias ForwardInvocationImpl = @convention(block) (Unmanaged, AnyObject) -> Void + let newForwardInvocation: ForwardInvocationImpl = { objectRef, invocation in let selector = invocation.selector! let alias = selector.alias let interopAlias = selector.interopAlias defer { - if let state = object.value(forAssociatedKey: alias.utf8Start) as! InterceptingState? { + if let state = objectRef.takeUnretainedValue().value(forAssociatedKey: alias.utf8Start) as! InterceptingState? { state.observer.send(value: invocation) } } @@ -201,10 +201,10 @@ private func enableMessageForwarding(_ realClass: AnyClass) { // Forward the invocation to the closest `forwardInvocation(_:)` in the // inheritance hierarchy, or the default handler returned by the runtime // if it finds no implementation. - typealias SuperForwardInvocation = @convention(c) (AnyObject, Selector, AnyObject) -> Void + typealias SuperForwardInvocation = @convention(c) (Unmanaged, Selector, AnyObject) -> Void let impl = class_getMethodImplementation(perceivedClass, ObjCSelector.forwardInvocation) let forwardInvocation = unsafeBitCast(impl, to: SuperForwardInvocation.self) - forwardInvocation(object, ObjCSelector.forwardInvocation, invocation) + forwardInvocation(objectRef, ObjCSelector.forwardInvocation, invocation) } _ = class_replaceMethod(realClass, @@ -222,15 +222,15 @@ private func enableMessageForwarding(_ realClass: AnyClass) { private func setupMethodSignatureCaching(_ realClass: AnyClass, _ signatureCache: SignatureCache) { let perceivedClass: AnyClass = class_getSuperclass(realClass) - let newMethodSignatureForSelector: @convention(block) (NSObject, Selector) -> AnyObject? = { object, selector in + let newMethodSignatureForSelector: @convention(block) (Unmanaged, Selector) -> AnyObject? = { objectRef, selector in if let signature = signatureCache[selector] { return signature } - typealias SuperMethodSignatureForSelector = @convention(c) (AnyObject, Selector, Selector) -> AnyObject? + typealias SuperMethodSignatureForSelector = @convention(c) (Unmanaged, Selector, Selector) -> AnyObject? let impl = class_getMethodImplementation(perceivedClass, ObjCSelector.methodSignatureForSelector) let methodSignatureForSelector = unsafeBitCast(impl, to: SuperMethodSignatureForSelector.self) - return methodSignatureForSelector(object, ObjCSelector.methodSignatureForSelector, selector) + return methodSignatureForSelector(objectRef, ObjCSelector.methodSignatureForSelector, selector) } _ = class_replaceMethod(realClass, From 53ce17a429a5ec790d072d4776fce166538324ff Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 8 Dec 2016 05:54:03 +0100 Subject: [PATCH 0647/1028] Improved the documentation. Minor refactoring. --- ReactiveCocoa/NSObject+Lifetime.swift | 18 ++++++++++++++++++ ReactiveCocoa/RACObjCRuntimeUtilities.h | 2 ++ ReactiveCocoaTests/KeyValueObservingSpec.swift | 18 ++++++++---------- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/ReactiveCocoa/NSObject+Lifetime.swift b/ReactiveCocoa/NSObject+Lifetime.swift index d9d95e1269..6fea13ef2d 100644 --- a/ReactiveCocoa/NSObject+Lifetime.swift +++ b/ReactiveCocoa/NSObject+Lifetime.swift @@ -21,16 +21,25 @@ extension Reactive where Base: NSObject { // Swizzle `-dealloc` so that the lifetime token is released at the // beginning of the deallocation chain, and only after the KVO `-dealloc`. objc_sync_enter(objcClass) + + // Swizzle the class only if it has not been swizzled before. if objc_getAssociatedObject(objcClass, &lifetimeKey) == nil { objc_setAssociatedObject(objcClass, &lifetimeKey, true, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) var existingImpl: IMP? = nil let newImplBlock: @convention(block) (UnsafeRawPointer) -> Void = { objectRef in + // A custom trampoline of `objc_setAssociatedObject` is used, since + // the imported version has been inserted with ARC calls that would + // mess with the object deallocation chain. + + // Release the lifetime token. _rac_objc_setAssociatedObject(objectRef, &lifetimeTokenKey, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) let impl: IMP + // Call the existing implementation if one has been caught. Otherwise, + // call the one first available in the superclass hierarchy. if let existingImpl = existingImpl { impl = existingImpl } else { @@ -47,7 +56,13 @@ extension Reactive where Base: NSObject { if !class_addMethod(objcClass, deallocSelector, newImpl, "v@:") { // The class has an existing `dealloc`. Preserve that as `existingImpl`. let deallocMethod = class_getInstanceMethod(objcClass, deallocSelector) + + // Store the existing implementation to `existingImpl` to ensure it is + // available before our version is swapped in. existingImpl = method_getImplementation(deallocMethod) + + // Store the swapped-out implementation to `existingImpl` in case + // the implementation has been changed concurrently. existingImpl = method_setImplementation(deallocMethod, newImpl) } } @@ -61,7 +76,10 @@ extension Reactive where Base: NSObject { } } +// Signatures defined in `@objc` protocols would be available for ObjC message +// sending via `AnyObject`. @objc private protocol ObjCClassReporting { + // An alias for `-class`, which is unavailable in Swift. @objc(class) var objcClass: AnyClass { get } } diff --git a/ReactiveCocoa/RACObjCRuntimeUtilities.h b/ReactiveCocoa/RACObjCRuntimeUtilities.h index b89a4f3ca8..586b11893d 100644 --- a/ReactiveCocoa/RACObjCRuntimeUtilities.h +++ b/ReactiveCocoa/RACObjCRuntimeUtilities.h @@ -2,6 +2,8 @@ #import NS_ASSUME_NONNULL_BEGIN +/// A trampoline of `objc_setAssociatedObject` that is made to circumvent the +/// reference counting calls in the imported version in Swift. void _rac_objc_setAssociatedObject(const void* object, const void* key, id _Nullable value, objc_AssociationPolicy policy); @interface NSObject (RACObjCRuntimeUtilities) diff --git a/ReactiveCocoaTests/KeyValueObservingSpec.swift b/ReactiveCocoaTests/KeyValueObservingSpec.swift index 02e92639e8..ea7008f596 100644 --- a/ReactiveCocoaTests/KeyValueObservingSpec.swift +++ b/ReactiveCocoaTests/KeyValueObservingSpec.swift @@ -154,17 +154,15 @@ class KeyValueObservingSpec: QuickSpec { expect(weakOriginalInner).to(beNil()) } - describe("special tests") { - it("should not crash an Operation") { - // Related issue: - // https://github.com/ReactiveCocoa/ReactiveCocoa/issues/3329 - func invoke() { - let op = Operation() - op.reactive.values(forKeyPath: "isFinished").start() - } - - invoke() + it("should not crash an Operation") { + // Related issue: + // https://github.com/ReactiveCocoa/ReactiveCocoa/issues/3329 + func invoke() { + let op = Operation() + op.reactive.values(forKeyPath: "isFinished").start() } + + invoke() } describe("thread safety") { From ee4081e29063a61e06a8e92853a36ca6d2313aac Mon Sep 17 00:00:00 2001 From: Dave Caunt Date: Fri, 9 Dec 2016 12:51:27 +0000 Subject: [PATCH 0648/1028] Add reactive UISearchBar --- ReactiveCocoa.xcodeproj/project.pbxproj | 12 +++- ReactiveCocoa/UIKit/iOS/UISearchBar.swift | 48 +++++++++++++ .../UIKit/UISearchBarSpec.swift | 69 +++++++++++++++++++ 3 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 ReactiveCocoa/UIKit/iOS/UISearchBar.swift create mode 100644 ReactiveCocoaTests/UIKit/UISearchBarSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index a32b30963d..005069780c 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -184,6 +184,8 @@ BEE020661D637B0000DF261F /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B696FB801A7640C00075236D /* TestError.swift */; }; BFA6B94D1A7604D400C846D1 /* SignalProducerNimbleMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */; }; BFA6B94E1A7604D500C846D1 /* SignalProducerNimbleMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */; }; + BFCF775F1DFAD8A50058006E /* UISearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCF775E1DFAD8A50058006E /* UISearchBar.swift */; }; + BFCF77621DFAD9440058006E /* UISearchBarSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCF77601DFAD9120058006E /* UISearchBarSpec.swift */; }; CD0C45DE1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */; }; CD0C45DF1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */; }; CD0C45E01CC9A288009F5BF0 /* DynamicProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */; }; @@ -362,6 +364,8 @@ BE330A181D634F5900806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-appletvos/ReactiveSwift.framework"; sourceTree = ""; }; BE330A1A1D634F5F00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-appletvos/ReactiveSwift.framework"; sourceTree = ""; }; BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalProducerNimbleMatchers.swift; sourceTree = ""; }; + BFCF775E1DFAD8A50058006E /* UISearchBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISearchBar.swift; sourceTree = ""; }; + BFCF77601DFAD9120058006E /* UISearchBarSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISearchBarSpec.swift; sourceTree = ""; }; CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DynamicProperty.swift; sourceTree = ""; }; CD8401821CEE8ED7009F0ABF /* CocoaActionSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaActionSpec.swift; sourceTree = ""; }; CDC42E2E1AE7AB8B00965373 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -550,6 +554,7 @@ 9A1D062B1D93EA7E00ACF44C /* UIImageViewSpec.swift */, 9A1D062C1D93EA7E00ACF44C /* UILabelSpec.swift */, 9A1D062D1D93EA7E00ACF44C /* UIProgressViewSpec.swift */, + BFCF77601DFAD9120058006E /* UISearchBarSpec.swift */, 9A1D062E1D93EA7E00ACF44C /* UISegmentedControlSpec.swift */, 53AC46CE1DD6FC0000C799E1 /* UISliderSpec.swift */, 531866F91DD7925600D1285F /* UIStepperSpec.swift */, @@ -604,11 +609,12 @@ isa = PBXGroup; children = ( 9A1D05F21D93E9F100ACF44C /* UIDatePicker.swift */, + 9AAD49871DED2C350068EC9B /* UIKeyboard.swift */, + 3BCAAC791DEE19BC00B30335 /* UIRefreshControl.swift */, + BFCF775E1DFAD8A50058006E /* UISearchBar.swift */, 53AC46CB1DD6F97400C799E1 /* UISlider.swift */, 531866F71DD7920400D1285F /* UIStepper.swift */, 9A1D05F71D93E9F100ACF44C /* UISwitch.swift */, - 9AAD49871DED2C350068EC9B /* UIKeyboard.swift */, - 3BCAAC791DEE19BC00B30335 /* UIRefreshControl.swift */, ); path = iOS; sourceTree = ""; @@ -1200,6 +1206,7 @@ 9A1D060D1D93EA0000ACF44C /* UITextField.swift in Sources */, 9A6AAA231DB8F51C0013AAEA /* ReusableComponents.swift in Sources */, 9A1D060E1D93EA0000ACF44C /* UITextView.swift in Sources */, + BFCF775F1DFAD8A50058006E /* UISearchBar.swift in Sources */, 9A1D06061D93EA0000ACF44C /* UIImageView.swift in Sources */, 9AF0EA761D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, CD0C45DF1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, @@ -1239,6 +1246,7 @@ files = ( 9A1D06581D93EA7E00ACF44C /* UIViewSpec.swift in Sources */, 9AAD498A1DED2F380068EC9B /* UIKeyboardSpec.swift in Sources */, + BFCF77621DFAD9440058006E /* UISearchBarSpec.swift in Sources */, D0A2260F1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */, BFA6B94E1A7604D500C846D1 /* SignalProducerNimbleMatchers.swift in Sources */, B696FB821A7640C00075236D /* TestError.swift in Sources */, diff --git a/ReactiveCocoa/UIKit/iOS/UISearchBar.swift b/ReactiveCocoa/UIKit/iOS/UISearchBar.swift new file mode 100644 index 0000000000..c2b856944e --- /dev/null +++ b/ReactiveCocoa/UIKit/iOS/UISearchBar.swift @@ -0,0 +1,48 @@ +import ReactiveSwift +import enum Result.NoError +import UIKit + +private class ReactiveUISearchBarDelegate: NSObject, UISearchBarDelegate { + + @objc func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {} + @objc func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {} +} + +private var delegateKey: UInt8 = 0 + +extension Reactive where Base: UISearchBar { + + private var delegate: ReactiveUISearchBarDelegate { + if let delegate = objc_getAssociatedObject(base, &delegateKey) as? ReactiveUISearchBarDelegate { + return delegate + } + + let delegate = ReactiveUISearchBarDelegate() + base.delegate = delegate + objc_setAssociatedObject(base, &delegateKey, delegate, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + return delegate + } + + /// Sets the text of the search bar. + public var text: BindingTarget { + return makeBindingTarget { $0.text = $1 } + } + + /// A signal of text values emitted by the search bar upon end of editing. + /// + /// - note: To observe text values that change on all editing events, + /// see `continuousTextValues`. + public var textValues: Signal { + return delegate.reactive.trigger(for: #selector(UISearchBarDelegate.searchBarTextDidEndEditing)) + .map { [unowned base] in base.text } + } + + /// A signal of text values emitted by the search bar upon any changes. + /// + /// - note: To observe text values only when editing ends, see `textValues`. + public var continuousTextValues: Signal { + return delegate.reactive.trigger(for: #selector(UISearchBarDelegate.searchBar(_:textDidChange:))) + .map { [unowned base] in base.text } + } + +} diff --git a/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift b/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift new file mode 100644 index 0000000000..7824849f5c --- /dev/null +++ b/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift @@ -0,0 +1,69 @@ +import ReactiveSwift +import ReactiveCocoa +import UIKit +import Quick +import Nimble +import enum Result.NoError + +class UISearchBarSpec: QuickSpec { + override func spec() { + var searchBar: UISearchBar! + weak var _searchBar: UISearchBar? + + + beforeEach { + autoreleasepool { + searchBar = UISearchBar(frame: .zero) + _searchBar = searchBar + } + } + + afterEach { + autoreleasepool { + searchBar = nil + } + expect(_searchBar).toEventually(beNil()) + } + + it("should accept changes from bindings to its text value") { + let firstChange = "first" + let secondChange = "second" + + searchBar.text = "" + + let (pipeSignal, observer) = Signal.pipe() + searchBar.reactive.text <~ SignalProducer(signal: pipeSignal) + + observer.send(value: firstChange) + expect(searchBar.text) == firstChange + + observer.send(value: secondChange) + expect(searchBar.text) == secondChange + } + + it("should emit user initiated changes to its text value when the editing ends") { + searchBar.text = "Test" + + var latestValue: String? + searchBar.reactive.textValues.observeValues { text in + latestValue = text + } + + searchBar.delegate!.searchBarTextDidEndEditing!(searchBar) + + expect(latestValue) == searchBar.text + } + + it("should emit user initiated changes to its text value continuously") { + searchBar.text = "newValue" + + var latestValue: String? + searchBar.reactive.continuousTextValues.observeValues { text in + latestValue = text + } + + searchBar.delegate!.searchBar!(searchBar, textDidChange: "newValue") + expect(latestValue) == "newValue" + } + } +} From 09869ab965216dfb9383c25403389200d086ba4a Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Sun, 11 Dec 2016 11:40:49 +0100 Subject: [PATCH 0649/1028] Remove Documentation that is already in ReactiveSwift --- Documentation/BasicOperators.md | 498 ----------------------------- Documentation/FrameworkOverview.md | 210 ------------ 2 files changed, 708 deletions(-) delete mode 100644 Documentation/BasicOperators.md delete mode 100644 Documentation/FrameworkOverview.md diff --git a/Documentation/BasicOperators.md b/Documentation/BasicOperators.md deleted file mode 100644 index ee45dac7dc..0000000000 --- a/Documentation/BasicOperators.md +++ /dev/null @@ -1,498 +0,0 @@ -# Basic Operators - -This document explains some of the most common operators used in ReactiveCocoa, -and includes examples demonstrating their use. - -Note that “operators”, in this context, refers to functions that transform -[signals][] and [signal producers][], _not_ custom Swift operators. In other -words, these are composable primitives provided by ReactiveCocoa for working -with event streams. - -This document will use the term “event stream” when dealing with concepts that -apply to both `Signal` and `SignalProducer`. When the distinction matters, the -types will be referred to by name. - -**[Performing side effects with event streams](#performing-side-effects-with-event-streams)** - - 1. [Observation](#observation) - 1. [Injecting effects](#injecting-effects) - -**[Operator composition](#operator-composition)** - - 1. [Lifting](#lifting) - -**[Transforming event streams](#transforming-event-streams)** - - 1. [Mapping](#mapping) - 1. [Filtering](#filtering) - 1. [Aggregating](#aggregating) - -**[Combining event streams](#combining-event-streams)** - - 1. [Combining latest values](#combining-latest-values) - 1. [Zipping](#zipping) - -**[Flattening producers](#flattening-producers)** - - 1. [Merging](#merging) - 1. [Concatenating](#concatenating) - 1. [Switching to the latest](#switching-to-the-latest) - -**[Handling failures](#handling-failures)** - - 1. [Catching failures](#catching-failures) - 1. [Retrying](#retrying) - 1. [Mapping errors](#mapping-errors) - 1. [Promote](#promote) - -## Performing side effects with event streams - -### Observation - -`Signal`s can be observed with the `observe` function. It takes an `Observer` as argument to which any future events are sent. - -```Swift -signal.observe(Signal.Observer { event in - switch event { - case let .Next(next): - print("Next: \(next)") - case let .Failed(error): - print("Failed: \(error)") - case .Completed: - print("Completed") - case .Interrupted: - print("Interrupted") - } -}) -``` - -Alternatively, callbacks for the `Next`, `Failed`, `Completed` and `Interrupted` events can be provided which will be called when a corresponding event occurs. - -```Swift -signal.observeNext { next in - print("Next: \(next)") -} -signal.observeFailed { error in - print("Failed: \(error)") -} -signal.observeCompleted { - print("Completed") -} -signal.observeInterrupted { - print("Interrupted") -} -``` - -Note that it is not necessary to observe all four types of event - all of them are optional, you only need to provide callbacks for the events you care about. - -### Injecting effects - -Side effects can be injected on a `SignalProducer` with the `on` operator without actually subscribing to it. - -```Swift -let producer = signalProducer - .on(started: { - print("Started") - }, event: { event in - print("Event: \(event)") - }, failed: { error in - print("Failed: \(error)") - }, completed: { - print("Completed") - }, interrupted: { - print("Interrupted") - }, terminated: { - print("Terminated") - }, disposed: { - print("Disposed") - }, next: { value in - print("Next: \(value)") - }) -``` - -Similar to `observe`, all the parameters are optional and you only need to provide callbacks for the events you care about. - -Note that nothing will be printed until `producer` is started (possibly somewhere else). - -## Operator composition - -### Lifting - -`Signal` operators can be _lifted_ to operate upon `SignalProducer`s using the -`lift` method. - -This will create a new `SignalProducer` which will apply the given operator to -_every_ `Signal` created, just as if the operator had been applied to each -produced `Signal` individually. - -## Transforming event streams - -These operators transform an event stream into a new stream. - -### Mapping - -The `map` operator is used to transform the values in an event stream, creating -a new stream with the results. - -```Swift -let (signal, observer) = Signal.pipe() - -signal - .map { string in string.uppercaseString } - .observeNext { next in print(next) } - -observer.sendNext("a") // Prints A -observer.sendNext("b") // Prints B -observer.sendNext("c") // Prints C -``` - -[Interactive visualisation of the `map` operator.](http://neilpa.me/rac-marbles/#map) - -### Filtering - -The `filter` operator is used to only include values in an event stream that -satisfy a predicate. - -```Swift -let (signal, observer) = Signal.pipe() - -signal - .filter { number in number % 2 == 0 } - .observeNext { next in print(next) } - -observer.sendNext(1) // Not printed -observer.sendNext(2) // Prints 2 -observer.sendNext(3) // Not printed -observer.sendNext(4) // prints 4 -``` - -[Interactive visualisation of the `filter` operator.](http://neilpa.me/rac-marbles/#filter) - -### Aggregating - -The `reduce` operator is used to aggregate a event stream’s values into a single -combined value. Note that the final value is only sent after the input stream -completes. - -```Swift -let (signal, observer) = Signal.pipe() - -signal - .reduce(1) { $0 * $1 } - .observeNext { next in print(next) } - -observer.sendNext(1) // nothing printed -observer.sendNext(2) // nothing printed -observer.sendNext(3) // nothing printed -observer.sendCompleted() // prints 6 -``` - -The `collect` operator is used to aggregate a event stream’s values into -a single array value. Note that the final value is only sent after the input -stream completes. - -```Swift -let (signal, observer) = Signal.pipe() - -signal - .collect() - .observeNext { next in print(next) } - -observer.sendNext(1) // nothing printed -observer.sendNext(2) // nothing printed -observer.sendNext(3) // nothing printed -observer.sendCompleted() // prints [1, 2, 3] -``` - -[Interactive visualisation of the `reduce` operator.](http://neilpa.me/rac-marbles/#reduce) - -## Combining event streams - -These operators combine values from multiple event streams into a new, unified -stream. - -### Combining latest values - -The `combineLatest` function combines the latest values of two (or more) event -streams. - -The resulting stream will only send its first value after each input has sent at -least one value. After that, new values on any of the inputs will result in -a new value on the output. - -```Swift -let (numbersSignal, numbersObserver) = Signal.pipe() -let (lettersSignal, lettersObserver) = Signal.pipe() - -let signal = combineLatest(numbersSignal, lettersSignal) -signal.observeNext { next in print("Next: \(next)") } -signal.observeCompleted { print("Completed") } - -numbersObserver.sendNext(0) // nothing printed -numbersObserver.sendNext(1) // nothing printed -lettersObserver.sendNext("A") // prints (1, A) -numbersObserver.sendNext(2) // prints (2, A) -numbersObserver.sendCompleted() // nothing printed -lettersObserver.sendNext("B") // prints (2, B) -lettersObserver.sendNext("C") // prints (2, C) -lettersObserver.sendCompleted() // prints "Completed" -``` - -The `combineLatestWith` operator works in the same way, but as an operator. - -[Interactive visualisation of the `combineLatest` operator.](http://neilpa.me/rac-marbles/#combineLatest) - -### Zipping - -The `zip` function joins values of two (or more) event streams pair-wise. The -elements of any Nth tuple correspond to the Nth elements of the input streams. - -That means the Nth value of the output stream cannot be sent until each input -has sent at least N values. - -```Swift -let (numbersSignal, numbersObserver) = Signal.pipe() -let (lettersSignal, lettersObserver) = Signal.pipe() - -let signal = zip(numbersSignal, lettersSignal) -signal.observeNext { next in print("Next: \(next)") } -signal.observeCompleted { print("Completed") } - -numbersObserver.sendNext(0) // nothing printed -numbersObserver.sendNext(1) // nothing printed -lettersObserver.sendNext("A") // prints (0, A) -numbersObserver.sendNext(2) // nothing printed -numbersObserver.sendCompleted() // nothing printed -lettersObserver.sendNext("B") // prints (1, B) -lettersObserver.sendNext("C") // prints (2, C) & "Completed" - -``` - -The `zipWith` operator works in the same way, but as an operator. - -[Interactive visualisation of the `zip` operator.](http://neilpa.me/rac-marbles/#zip) - -## Flattening producers - -The `flatten` operator transforms a stream-of-streams into a single stream - where values are forwarded from the inner stream in accordance with the provided `FlattenStrategy`. The flattened result becomes that of the outer stream type - i.e. a `SignalProducer`-of-`SignalProducer`s or `SignalProducer`-of-`Signal`s gets flattened to a `SignalProducer`, and likewise a `Signal`-of-`SignalProducer`s or `Signal`-of-`Signal`s gets flattened to a `Signal`. - -To understand why there are different strategies and how they compare to each other, take a look at this example and imagine the column offsets as time: - -```Swift -let values = [ -// imagine column offset as time -[ 1, 2, 3 ], - [ 4, 5, 6 ], - [ 7, 8 ], -] - -let merge = -[ 1, 4, 2, 7,5, 3,8,6 ] - -let concat = -[ 1, 2, 3,4, 5, 6,7, 8] - -let latest = -[ 1, 4, 7, 8 ] -``` - -Note, how the values interleave and which values are even included in the resulting array. - - -### Merging - -The `.Merge` strategy immediately forwards every value of the inner `SignalProducer`s to the outer `SignalProducer`. Any failure sent on the outer producer or any inner producer is immediately sent on the flattened producer and terminates it. - -```Swift -let (producerA, lettersObserver) = SignalProducer.buffer(5) -let (producerB, numbersObserver) = SignalProducer.buffer(5) -let (signal, observer) = SignalProducer, NoError>.buffer(5) - -signal.flatten(.Merge).startWithNext { next in print(next) } - -observer.sendNext(producerA) -observer.sendNext(producerB) -observer.sendCompleted() - -lettersObserver.sendNext("a") // prints "a" -numbersObserver.sendNext("1") // prints "1" -lettersObserver.sendNext("b") // prints "b" -numbersObserver.sendNext("2") // prints "2" -lettersObserver.sendNext("c") // prints "c" -numbersObserver.sendNext("3") // prints "3" -``` - -[Interactive visualisation of the `flatten(.Merge)` operator.](http://neilpa.me/rac-marbles/#merge) - -### Concatenating - -The `.Concat` strategy is used to serialize work of the inner `SignalProducer`s. The outer producer is started immediately. Each subsequent producer is not started until the preceeding one has completed. Failures are immediately forwarded to the flattened producer. - -```Swift -let (producerA, lettersObserver) = SignalProducer.buffer(5) -let (producerB, numbersObserver) = SignalProducer.buffer(5) -let (signal, observer) = SignalProducer, NoError>.buffer(5) - -signal.flatten(.Concat).startWithNext { next in print(next) } - -observer.sendNext(producerA) -observer.sendNext(producerB) -observer.sendCompleted() - -numbersObserver.sendNext("1") // nothing printed -lettersObserver.sendNext("a") // prints "a" -lettersObserver.sendNext("b") // prints "b" -numbersObserver.sendNext("2") // nothing printed -lettersObserver.sendNext("c") // prints "c" -lettersObserver.sendCompleted() // prints "1", "2" -numbersObserver.sendNext("3") // prints "3" -numbersObserver.sendCompleted() -``` - -[Interactive visualisation of the `flatten(.Concat)` operator.](http://neilpa.me/rac-marbles/#concat) - -### Switching to the latest - -The `.Latest` strategy forwards only values from the latest input `SignalProducer`. - -```Swift -let (producerA, observerA) = SignalProducer.buffer(5) -let (producerB, observerB) = SignalProducer.buffer(5) -let (producerC, observerC) = SignalProducer.buffer(5) -let (signal, observer) = SignalProducer, NoError>.buffer(5) - -signal.flatten(.Latest).startWithNext { next in print(next) } - -observer.sendNext(producerA) // nothing printed -observerC.sendNext("X") // nothing printed -observerA.sendNext("a") // prints "a" -observerB.sendNext("1") // nothing printed -observer.sendNext(producerB) // prints "1" -observerA.sendNext("b") // nothing printed -observerB.sendNext("2") // prints "2" -observerC.sendNext("Y") // nothing printed -observerA.sendNext("c") // nothing printed -observer.sendNext(producerC) // prints "X", "Y" -observerB.sendNext("3") // nothing printed -observerC.sendNext("Z") // prints "Z" -``` - -## Handling failures - -These operators are used to handle failures that might occur on an event stream. - -### Catching failures - -The `flatMapError` operator catches any failure that may occur on the input `SignalProducer`, then starts a new `SignalProducer` in its place. - -```Swift -let (producer, observer) = SignalProducer.buffer(5) -let error = NSError(domain: "domain", code: 0, userInfo: nil) - -producer - .flatMapError { _ in SignalProducer(value: "Default") } - .startWithNext { next in print(next) } - - -observer.sendNext("First") // prints "First" -observer.sendNext("Second") // prints "Second" -observer.sendFailed(error) // prints "Default" -``` - -### Retrying - -The `retry` operator will restart the original `SignalProducer` on failure up to `count` times. - -```Swift -var tries = 0 -let limit = 2 -let error = NSError(domain: "domain", code: 0, userInfo: nil) -let producer = SignalProducer { (observer, _) in - if tries++ < limit { - observer.sendFailed(error) - } else { - observer.sendNext("Success") - observer.sendCompleted() - } -} - -producer - .on(failed: {e in print("Failure")}) // prints "Failure" twice - .retry(2) - .start { event in - switch event { - case let .Next(next): - print(next) // prints "Success" - case let .Failed(error): - print("Failed: \(error)") - case .Completed: - print("Completed") - case .Interrupted: - print("Interrupted") - } - } -``` - -If the `SignalProducer` does not succeed after `count` tries, the resulting `SignalProducer` will fail. E.g., if `retry(1)` is used in the example above instead of `retry(2)`, `"Signal Failure"` will be printed instead of `"Success"`. - -### Mapping errors - -The `mapError` operator transforms the error of any failure in an event stream into a new error. - -```Swift -enum CustomError: String, ErrorType { - case Foo = "Foo" - case Bar = "Bar" - case Other = "Other" - - var nsError: NSError { - return NSError(domain: "CustomError.\(rawValue)", code: 0, userInfo: nil) - } - - var description: String { - return "\(rawValue) Error" - } -} - -let (signal, observer) = Signal.pipe() - -signal - .mapError { (error: NSError) -> CustomError in - switch error.domain { - case "com.example.foo": - return .Foo - case "com.example.bar": - return .Bar - default: - return .Other - } - } - .observeFailed { error in - print(error) - } - -observer.sendFailed(NSError(domain: "com.example.foo", code: 42, userInfo: nil)) // prints "Foo Error" -``` - -### Promote - -The `promoteErrors` operator promotes an event stream that does not generate failures into one that can. - -```Swift -let (numbersSignal, numbersObserver) = Signal.pipe() -let (lettersSignal, lettersObserver) = Signal.pipe() - -numbersSignal - .promoteErrors(NSError) - .combineLatestWith(lettersSignal) -``` - -The given stream will still not _actually_ generate failures, but this is useful -because some operators to [combine streams](#combining-event-streams) require -the inputs to have matching error types. - - -[Signals]: FrameworkOverview.md#signals -[Signal Producers]: FrameworkOverview.md#signal-producers -[Observation]: FrameworkOverview.md#observation - diff --git a/Documentation/FrameworkOverview.md b/Documentation/FrameworkOverview.md deleted file mode 100644 index 10211955b5..0000000000 --- a/Documentation/FrameworkOverview.md +++ /dev/null @@ -1,210 +0,0 @@ -# Framework Overview - -This document contains a high-level description of the different components -within the ReactiveCocoa framework, and an attempt to explain how they work -together and divide responsibilities. This is meant to be a starting point for -learning about new modules and finding more specific documentation. - -For examples and help understanding how to use RAC, see the [README][] or -the [Design Guidelines][]. - -## Events - -An **event**, represented by the [`Event`][Event] type, is the formalized representation -of the fact that _something has happened_. In ReactiveCocoa, events are the centerpiece -of communication. An event might represent the press of a button, a piece -of information received from an API, the occurrence of an error, or the completion -of a long-running operation. In any case, something generates the events and sends them over a -[signal](#signals) to any number of [observers](#observers). - -`Event` is an enumerated type representing either a value or one of three -terminal events: - - * The `Next` event provides a new value from the source. - * The `Failed` event indicates that an error occurred before the signal could - finish. Events are parameterized by an `ErrorType`, which determines the kind - of failure that’s permitted to appear in the event. If a failure is not - permitted, the event can use type `NoError` to prevent any from being - provided. - * The `Completed` event indicates that the signal finished successfully, and - that no more values will be sent by the source. - * The `Interrupted` event indicates that the signal has terminated due to - cancellation, meaning that the operation was neither successful nor - unsuccessful. - -## Signals - -A **signal**, represented by the [`Signal`][Signal] type, is any series of [events](#events) -over time that can be observed. - -Signals are generally used to represent event streams that are already “in progress”, -like notifications, user input, etc. As work is performed or data is received, -events are _sent_ on the signal, which pushes them out to any observers. -All observers see the events at the same time. - -Users must [observe](#observers) a signal in order to access its events. -Observing a signal does not trigger any side effects. In other words, -signals are entirely producer-driven and push-based, and consumers (observers) -cannot have any effect on their lifetime. While observing a signal, the user -can only evaluate the events in the same order as they are sent on the signal. There -is no random access to values of a signal. - -Signals can be manipulated by applying [primitives][BasicOperators] to them. -Typical primitives to manipulate a single signal like `filter`, `map` and -`reduce` are available, as well as primitives to manipulate multiple signals -at once (`zip`). Primitives operate only on the `Next` events of a signal. - -The lifetime of a signal consists of any number of `Next` events, followed by -one terminating event, which may be any one of `Failed`, `Completed`, or -`Interrupted` (but not a combination). -Terminating events are not included in the signal’s values—they must be -handled specially. - -### Pipes - -A **pipe**, created by `Signal.pipe()`, is a [signal](#signals) -that can be manually controlled. - -The method returns a [signal](#signals) and an [observer](#observers). -The signal can be controlled by sending events to the observer. This -can be extremely useful for bridging non-RAC code into the world of signals. - -For example, instead of handling application logic in block callbacks, the -blocks can simply send events to the observer. Meanwhile, the signal -can be returned, hiding the implementation detail of the callbacks. - -## Signal Producers - -A **signal producer**, represented by the [`SignalProducer`][SignalProducer] type, creates -[signals](#signals) and performs side effects. - -They can be used to represent operations or tasks, like network -requests, where each invocation of `start()` will create a new underlying -operation, and allow the caller to observe the result(s). The -`startWithSignal()` variant gives access to the produced signal, allowing it to -be observed multiple times if desired. - -Because of the behavior of `start()`, each signal created from the same -producer may see a different ordering or version of events, or the stream might -even be completely different! Unlike a plain signal, no work is started (and -thus no events are generated) until an observer is attached, and the work is -restarted anew for each additional observer. - -Starting a signal producer returns a [disposable](#disposables) that can be used to -interrupt/cancel the work associated with the produced signal. - -Just like signals, signal producers can also be manipulated via primitives -like `map`, `filter`, etc. -Every signal primitive can be “lifted” to operate upon signal producers instead, -using the `lift` method. -Furthermore, there are additional primitives that control _when_ and _how_ work -is started—for example, `times`. - -### Buffers - -A **buffer**, created by `SignalProducer.buffer()`, is a (optionally bounded) -queue for [events](#events) that replays those events when new -[signals](#signals) are created from the producer. - -Similar to a [pipe](#pipes), the method returns an [observer](#observers). -Events sent to this observer will be added to the queue. If the buffer is already -at capacity when a new value arrives, the earliest (oldest) value will be -dropped to make room for it. - -## Observers - -An **observer** is anything that is waiting or capable of waiting for [events](#events) -from a [signal](#signals). Within RAC, an observer is represented as -an [`Observer`][Observer] that accepts [`Event`][Event] values. - -Observers can be implicitly created by using the callback-based versions of the -`Signal.observe` or `SignalProducer.start` methods. - -## Actions - -An **action**, represented by the [`Action`][Action] type, will do some work when -executed with an input. While executing, zero or more output values and/or a -failure may be generated. - -Actions are useful for performing side-effecting work upon user interaction, like when a button is -clicked. Actions can also be automatically disabled based on a [property](#properties), and this -disabled state can be represented in a UI by disabling any controls associated -with the action. - -For interaction with `NSControl` or `UIControl`, RAC provides the -[`CocoaAction`][CocoaAction] type for bridging actions to Objective-C. - -## Properties - -A **property**, represented by the [`PropertyType`][Property] protocol, -stores a value and notifies observers about future changes to that value. - -The current value of a property can be obtained from the `value` getter. The -`producer` getter returns a [signal producer](#signal-producers) that will send -the property’s current value, followed by all changes over time. - -The `<~` operator can be used to bind properties in different ways. Note that in -all cases, the target has to be a [`MutablePropertyType`][Property]. - -* `property <~ signal` binds a [signal](#signals) to the property, updating the - property’s value to the latest value sent by the signal. -* `property <~ producer` starts the given [signal producer](#signal-producers), - and binds the property’s value to the latest value sent on the started signal. -* `property <~ otherProperty` binds one property to another, so that the destination - property’s value is updated whenever the source property is updated. - -The [`DynamicProperty`][Property] type can be used to bridge to Objective-C APIs -that require Key-Value Coding (KVC) or Key-Value Observing (KVO), like -`NSOperation`. Note that most AppKit and UIKit properties do _not_ support KVO, -so their changes should be observed through other mechanisms. -[`MutableProperty`][Property] should be preferred over dynamic properties -whenever possible! - -## Disposables - -A **disposable**, represented by the [`Disposable`][Disposable] protocol, is a mechanism -for memory management and cancellation. - -When starting a [signal producer](#signal-producers), a disposable will be returned. -This disposable can be used by the caller to cancel the work that has been started -(e.g. background processing, network requests, etc.), clean up all temporary -resources, then send a final `Interrupted` event upon the particular -[signal](#signals) that was created. - -Observing a [signal](#signals) may also return a disposable. Disposing it will -prevent the observer from receiving any future events from that signal, but it -will not have any effect on the signal itself. - -For more information about cancellation, see the RAC [Design Guidelines][]. - -## Schedulers - -A **scheduler**, represented by the [`SchedulerType`][Scheduler] protocol, is a -serial execution queue to perform work or deliver results upon. - -[Signals](#signals) and [signal producers](#signal-producers) can be ordered to -deliver events on a specific scheduler. [Signal producers](#signal-producers) -can additionally be ordered to start their work on a specific scheduler. - -Schedulers are similar to Grand Central Dispatch queues, but schedulers support -cancellation (via [disposables](#disposables)), and always execute serially. -With the exception of the [`ImmediateScheduler`][Scheduler], schedulers do not -offer synchronous execution. This helps avoid deadlocks, and encourages the use -of [signal and signal producer primitives][BasicOperators] instead of blocking work. - -Schedulers are also somewhat similar to `NSOperationQueue`, but schedulers -do not allow tasks to be reordered or depend on one another. - - -[Design Guidelines]: DesignGuidelines.md -[BasicOperators]: BasicOperators.md -[README]: ../README.md -[Signal]: ../ReactiveCocoa/Swift/Signal.swift -[SignalProducer]: ../ReactiveCocoa/Swift/SignalProducer.swift -[Action]: ../ReactiveCocoa/Swift/Action.swift -[CocoaAction]: ../ReactiveCocoa/Swift/CocoaAction.swift -[Disposable]: ../ReactiveCocoa/Swift/Disposable.swift -[Scheduler]: ../ReactiveCocoa/Swift/Scheduler.swift -[Property]: ../ReactiveCocoa/Swift/Property.swift -[Event]: ../ReactiveCocoa/Swift/Event.swift -[Observer]: ../ReactiveCocoa/Swift/Observer.swift From c68803666c8b80990fd4635dd40063f661b06750 Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Sun, 11 Dec 2016 11:51:29 +0100 Subject: [PATCH 0650/1028] Update Design Guidelines --- Documentation/DesignGuidelines.md | 132 +++++++++++++++--------------- 1 file changed, 65 insertions(+), 67 deletions(-) diff --git a/Documentation/DesignGuidelines.md b/Documentation/DesignGuidelines.md index 897659d49c..182e977ede 100644 --- a/Documentation/DesignGuidelines.md +++ b/Documentation/DesignGuidelines.md @@ -5,15 +5,15 @@ ReactiveCocoa. The content here is heavily inspired by the [Rx Design Guidelines](http://blogs.msdn.com/b/rxteam/archive/2010/10/28/rx-design-guidelines.aspx). This document assumes basic familiarity -with the features of ReactiveCocoa. The [Framework Overview][] is a better -resource for getting up to speed on the main types and concepts provided by RAC. +with the features of ReactiveSwift on which ReactiveCocoa expands. The [Reactive Swift Framework Overview][] is a better +resource for getting up to speed on the main types and concepts provided by ReactiveSwift. **[The `Event` contract](#the-event-contract)** - 1. [`Next`s provide values or indicate the occurrence of events](#nexts-provide-values-or-indicate-the-occurrence-of-events) - 1. [Failures behave like exceptions and propagate immediately](#failures-behave-like-exceptions-and-propagate-immediately) - 1. [Completion indicates success](#completion-indicates-success) - 1. [Interruption cancels outstanding work and usually propagates immediately](#interruption-cancels-outstanding-work-and-usually-propagates-immediately) + 1. [`value`s provide values or indicate the occurrence of events](#values-provide-values-or-indicate-the-occurrence-of-events) + 1. [`failure`s behave like exceptions and propagate immediately](#failures-behave-like-exceptions-and-propagate-immediately) + 1. [`completion` indicates success](#completion-indicates-success) + 1. [`interruption`s cancel outstanding work and usually propagate immediately](#interruptions-cancel-outstanding-work-and-usually-propagate-immediately) 1. [Events are serial](#events-are-serial) 1. [Events cannot be sent recursively](#events-cannot-be-sent-recursively) 1. [Events are sent synchronously by default](#events-are-sent-synchronously-by-default) @@ -53,41 +53,41 @@ resource for getting up to speed on the main types and concepts provided by RAC. ## The `Event` contract -[Events][] are fundamental to ReactiveCocoa. [Signals][] and [signal producers][] both send +[Events][] are fundamental to ReactiveSwift. [Signals][] and [signal producers][] both send events, and may be collectively called “event streams.” Event streams must conform to the following grammar: ``` -Next* (Interrupted | Failed | Completed)? +value* (interrupted | failed | completed)? ``` This states that an event stream consists of: - 1. Any number of `Next` events - 1. Optionally followed by one terminating event, which is any of `Interrupted`, `Failed`, or `Completed` + 1. Any number of `value` events + 1. Optionally followed by one terminating event, which is any of `interrupted`, `failed`, or `completed` After a terminating event, no other events will be received. -#### `Next`s provide values or indicate the occurrence of events +#### `value`s provide values or indicate the occurrence of events -`Next` events contain a payload known as the “value.” Only `Next` events are -said to have a value. Since an event stream can contain any number of `Next`s, +`value` events contain a payload known as the “value”. Only `value` events are +said to have a value. Since an event stream can contain any number of `value`s, there are few restrictions on what those values can mean or be used for, except that they must be of the same type. As an example, the value might represent an element from a collection, or -a progress update about some long-running operation. The value of a `Next` event +a progress update about some long-running operation. The value of a `value` event might even represent nothing at all—for example, it’s common to use a value type of `()` to indicate that something happened, without being more specific about what that something was. -Most of the event stream [operators][] act upon `Next` events, as they represent the +Most of the event stream [operators][] act upon `value` events, as they represent the “meaningful data” of a signal or producer. -#### Failures behave like exceptions and propagate immediately +#### `failure`s behave like exceptions and propagate immediately -`Failed` events indicate that something went wrong, and contain a concrete error +`failed` events indicate that something went wrong, and contain a concrete error that indicates what happened. Failures are fatal, and propagate as quickly as possible to the consumer for handling. @@ -95,30 +95,30 @@ Failures also behave like exceptions, in that they “skip” operators, termina them along the way. In other words, most [operators][] immediately stop doing work when a failure is received, and then propagate the failure onward. This even applies to time-shifted operators, like [`delay`][delay]—which, despite its name, will forward any failures immediately. -Consequently, failures should only be used to represent “abnormal” termination. If it is important to let operators (or consumers) finish their work, a `Next` +Consequently, failures should only be used to represent “abnormal” termination. If it is important to let operators (or consumers) finish their work, a `value` event describing the result might be more appropriate. If an event stream can _never_ fail, it should be parameterized with the -special [`NoError`][NoError] type, which statically guarantees that a `Failed` +special [`NoError`][NoError] type, which statically guarantees that a `failed` event cannot be sent upon the stream. -#### Completion indicates success +#### `completion` indicates success -An event stream sends `Completed` when the operation has completed successfully, +An event stream sends `completed` when the operation has completed successfully, or to indicate that the stream has terminated normally. -Many operators manipulate the `Completed` event to shorten or extend the +Many operators manipulate the `completed` event to shorten or extend the lifetime of an event stream. For example, [`take`][take] will complete after the specified number of values have been received, thereby terminating the stream early. On the other hand, most operators that accept multiple signals or producers will wait until _all_ of -them have completed before forwarding a `Completed` event, since a successful +them have completed before forwarding a `completed` event, since a successful outcome will usually depend on all the inputs. -#### Interruption cancels outstanding work and usually propagates immediately +#### `interruption`s cancel outstanding work and usually propagate immediately -An `Interrupted` event is sent when an event stream should cancel processing. +An `interrupted` event is sent when an event stream should cancel processing. Interruption is somewhere between [success](#completion-indicates-success) and [failure](#failures-behave-like-exceptions-and-propagate-immediately)—the operation was not successful, because it did not get to finish, but it didn’t @@ -126,17 +126,17 @@ necessarily “fail” either. Most [operators][] will propagate interruption immediately, but there are some exceptions. For example, the [flattening operators][flatten] will ignore -`Interrupted` events that occur on the _inner_ producers, since the cancellation +`interrupted` events that occur on the _inner_ producers, since the cancellation of an inner operation should not necessarily cancel the larger unit of work. -RAC will automatically send an `Interrupted` event upon [disposal][Disposables], but it can +ReactiveSwift will automatically send an `interrupted` event upon [disposal][Disposables], but it can also be sent manually if necessary. Additionally, [custom operators](#implementing-new-operators) must make sure to forward interruption events to the observer. #### Events are serial -RAC guarantees that all events upon a stream will arrive serially. In other +ReactiveSwift guarantees that all events upon a stream will arrive serially. In other words, it’s impossible for the observer of a signal or producer to receive multiple `Event`s concurrently, even if the events are sent on multiple threads simultaneously. @@ -145,7 +145,7 @@ This simplifies [operator][Operators] implementations and [observers][]. #### Events cannot be sent recursively -Just like RAC guarantees that [events will not be received +Just like ReactiveSwift guarantees that [events will not be received concurrently](#events-are-serial), it also guarantees that they won’t be received recursively. As a consequence, [operators][] and [observers][] _do not_ need to be reentrant. @@ -161,7 +161,7 @@ an already-running event handler. #### Events are sent synchronously by default -RAC does not implicitly introduce concurrency or asynchrony. [Operators][] that +ReactiveSwift does not implicitly introduce concurrency or asynchrony. [Operators][] that accept a [scheduler][Schedulers] may, but they must be explicitly invoked by the consumer of the framework. @@ -177,7 +177,7 @@ distributed. A [signal][Signals] is a stream of values that obeys [the `Event` contract](#the-event-contract). -`Signal` is a reference type, because each signal has identity—in other words, each +`Signal` is a reference type, because each signal has identity — in other words, each signal has its own lifetime, and may eventually terminate. Once terminated, a signal cannot be restarted. @@ -212,7 +212,7 @@ observers effectively see the same stream of events. There is one exception to this rule: adding an observer to a signal _after_ it has already terminated will result in exactly one -[`Interrupted`](#interruption-cancels-outstanding-work-and-usually-propagates-immediately) +[`interrupted`](#interruption-cancels-outstanding-work-and-usually-propagates-immediately) event sent to that specific observer. #### A signal is alive as long as it is publicly reachable or is being observed @@ -297,7 +297,7 @@ automatically created and passed back. Disposing of this object will [interrupt](#interruption-cancels-outstanding-work-and-usually-propagates-immediately) the produced `Signal`, thereby canceling outstanding work and sending an -`Interrupted` [event][Events] to all [observers][], and will also dispose of +`interrupted` [event][Events] to all [observers][], and will also dispose of everything added to the [`CompositeDisposable`][CompositeDisposable] in [SignalProducer.init]. @@ -306,7 +306,7 @@ by the same `SignalProducer`. ## Best practices -The following recommendations are intended to help keep RAC-based code +The following recommendations are intended to help keep ReactiveSwift-based code predictable, understandable, and performant. They are, however, only guidelines. Use best judgement when determining whether @@ -408,9 +408,9 @@ disposal: ## Implementing new operators -RAC provides a long list of built-in [operators][] that should cover most use -cases; however, RAC is not a closed system. It's entirely valid to implement -additional operators for specialized uses, or for consideration in ReactiveCocoa +ReactiveSwift provides a long list of built-in [operators][] that should cover most use +cases; however, ReactiveSwift is not a closed system. It's entirely valid to implement +additional operators for specialized uses, or for consideration in ReactiveSwift itself. Implementing a new operator requires a careful attention to detail and a focus @@ -418,7 +418,7 @@ on simplicity, to avoid introducing bugs into the calling code. These guidelines cover some of the common pitfalls and help preserve the expected API contracts. It may also help to look at the implementations of -existing `Signal` and `SignalProducer` operators for reference points. +existing [`Signal`][Signals] and [`SignalProducer`][Signal Producers] operators for reference points. #### Prefer writing operators that apply to both signals and producers @@ -436,7 +436,7 @@ instead. #### Compose existing operators when possible -Considerable thought has been put into the operators provided by RAC, and they +Considerable thought has been put into the operators provided by ReactiveSwift, and they have been validated through automated tests and through their real world use in other projects. An operator that has been written from scratch may not be as robust, or might not handle a special case that the built-in operators are aware @@ -450,15 +450,13 @@ little code written from scratch. Unless an operator is specifically built to handle [failures](#failures-behave-like-exceptions-and-propagate-immediately) and -[interruption](#interruption-cancels-outstanding-work-and-usually-propagates-immedaitely) +[interruptions](#interruption-cancels-outstanding-work-and-usually-propagates-immedaitely) in a custom way, it should propagate those events to the observer as soon as possible, to ensure that their semantics are honored. #### Switch over `Event` values -Instead of using [`start(failed:completed:interrupted:next:)`][start] or -[`observe(failed:completed:interrupted:next:)`][observe], create your own -[observer][Observers] to process raw [`Event`][Events] values, and use +Create your own [observer][Observers] to process raw [`Event`][Events] values, and use a `switch` statement to determine the event type. For example: @@ -491,7 +489,7 @@ the potential for deadlocks and race conditions, operators should not concurrently perform their work. Callers always have the ability to [observe events on a specific -scheduler](#observe-events-on-a-known-scheduler), and RAC offers built-in ways +scheduler](#observe-events-on-a-known-scheduler), and ReactiveSwift offers built-in ways to parallelize work, so custom operators don’t need to be concerned with it. #### Avoid blocking in operators @@ -505,26 +503,26 @@ This guideline can be safely ignored when the purpose of an operator is to synchronously retrieve one or more values from a stream, like `single()` or `wait()`. -[CompositeDisposable]: ../ReactiveCocoa/Swift/Disposable.swift -[Disposables]: FrameworkOverview.md#disposables -[Events]: FrameworkOverview.md#events -[Framework Overview]: FrameworkOverview.md -[NoError]: ../ReactiveCocoa/Swift/Errors.swift -[Observers]: FrameworkOverview.md#observers -[Operators]: BasicOperators.md -[Properties]: FrameworkOverview.md#properties -[Schedulers]: FrameworkOverview.md#schedulers -[Signal Producers]: FrameworkOverview.md#signal-producers -[Signal.init]: ../ReactiveCocoa/Swift/Signal.swift -[Signal.pipe]: ../ReactiveCocoa/Swift/Signal.swift -[SignalProducer.init]: ../ReactiveCocoa/Swift/SignalProducer.swift -[Signals]: FrameworkOverview.md#signals -[delay]: ../ReactiveCocoa/Swift/Signal.swift -[flatten]: BasicOperators.md#flattening-producers -[lift]: ../ReactiveCocoa/Swift/SignalProducer.swift -[observe]: ../ReactiveCocoa/Swift/Signal.swift -[observeOn]: ../ReactiveCocoa/Swift/Signal.swift -[start]: ../ReactiveCocoa/Swift/SignalProducer.swift -[startWithSignal]: ../ReactiveCocoa/Swift/SignalProducer.swift -[take]: ../ReactiveCocoa/Swift/Signal.swift -[takeUntil]: ../ReactiveCocoa/Swift/Signal.swift +[CompositeDisposable]: https://github.com/ReactiveCocoa/ReactiveSwift/tree/master/Sources/Disposable.swift +[Disposables]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#disposables +[Events]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#events +[Reactive Swift Framework Overview]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md +[NoError]: https://github.com/ReactiveCocoa/ReactiveSwift/tree/master/Sources/Errors.swift +[Observers]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#observers +[Operators]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/BasicOperators.md +[Properties]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#properties +[Schedulers]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#schedulers +[Signal Producers]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#signal-producers +[Signal.init]: https://github.com/ReactiveCocoa/ReactiveSwift/tree/master/Sources/Signal.swift +[Signal.pipe]: https://github.com/ReactiveCocoa/ReactiveSwift/tree/master/Sources/Signal.swift +[SignalProducer.init]: https://github.com/ReactiveCocoa/ReactiveSwift/tree/master/Sources/SignalProducer.swift +[Signals]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#signals +[delay]: https://github.com/ReactiveCocoa/ReactiveSwift/tree/master/Sources/Signal.swift +[flatten]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/BasicOperators.md#flattening-producers +[lift]: https://github.com/ReactiveCocoa/ReactiveSwift/tree/master/Sources/SignalProducer.swift +[observe]: https://github.com/ReactiveCocoa/ReactiveSwift/tree/master/Sources/Signal.swift +[observeOn]: https://github.com/ReactiveCocoa/ReactiveSwift/tree/master/Sources/Signal.swift +[start]: https://github.com/ReactiveCocoa/ReactiveSwift/tree/master/Sources/SignalProducer.swift +[startWithSignal]: https://github.com/ReactiveCocoa/ReactiveSwift/tree/master/Sources/SignalProducer.swift +[take]: https://github.com/ReactiveCocoa/ReactiveSwift/tree/master/Sources/Signal.swift +[takeUntil]: https://github.com/ReactiveCocoa/ReactiveSwift/tree/master/Sources/Signal.swift From ea728990f9eb5192ac62345f5df8bab7f0f2980b Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Sun, 11 Dec 2016 11:51:43 +0100 Subject: [PATCH 0651/1028] Remove DesignGuidelines --- Documentation/DesignGuidelines.md | 528 ------------------------------ 1 file changed, 528 deletions(-) delete mode 100644 Documentation/DesignGuidelines.md diff --git a/Documentation/DesignGuidelines.md b/Documentation/DesignGuidelines.md deleted file mode 100644 index 182e977ede..0000000000 --- a/Documentation/DesignGuidelines.md +++ /dev/null @@ -1,528 +0,0 @@ -# Design Guidelines - -This document contains guidelines for projects that want to make use of -ReactiveCocoa. The content here is heavily inspired by the [Rx Design -Guidelines](http://blogs.msdn.com/b/rxteam/archive/2010/10/28/rx-design-guidelines.aspx). - -This document assumes basic familiarity -with the features of ReactiveSwift on which ReactiveCocoa expands. The [Reactive Swift Framework Overview][] is a better -resource for getting up to speed on the main types and concepts provided by ReactiveSwift. - -**[The `Event` contract](#the-event-contract)** - - 1. [`value`s provide values or indicate the occurrence of events](#values-provide-values-or-indicate-the-occurrence-of-events) - 1. [`failure`s behave like exceptions and propagate immediately](#failures-behave-like-exceptions-and-propagate-immediately) - 1. [`completion` indicates success](#completion-indicates-success) - 1. [`interruption`s cancel outstanding work and usually propagate immediately](#interruptions-cancel-outstanding-work-and-usually-propagate-immediately) - 1. [Events are serial](#events-are-serial) - 1. [Events cannot be sent recursively](#events-cannot-be-sent-recursively) - 1. [Events are sent synchronously by default](#events-are-sent-synchronously-by-default) - -**[The `Signal` contract](#the-signal-contract)** - - 1. [Signals start work when instantiated](#signals-start-work-when-instantiated) - 1. [Observing a signal does not have side effects](#observing-a-signal-does-not-have-side-effects) - 1. [All observers of a signal see the same events in the same order](#all-observers-of-a-signal-see-the-same-events-in-the-same-order) - 1. [A signal is alive as long as it is publicly reachable or is being observed](#a-signal-is-alive-as-long-as-it-is-publicly-reachable-or-is-being-observed) - 1. [Terminating events dispose of signal resources](#terminating-events-dispose-of-signal-resources) - -**[The `SignalProducer` contract](#the-signalproducer-contract)** - - 1. [Signal producers start work on demand by creating signals](#signal-producers-start-work-on-demand-by-creating-signals) - 1. [Each produced signal may send different events at different times](#each-produced-signal-may-send-different-events-at-different-times) - 1. [Signal operators can be lifted to apply to signal producers](#signal-operators-can-be-lifted-to-apply-to-signal-producers) - 1. [Disposing of a produced signal will interrupt it](#disposing-of-a-produced-signal-will-interrupt-it) - -**[Best practices](#best-practices)** - - 1. [Process only as many values as needed](#process-only-as-many-values-as-needed) - 1. [Observe events on a known scheduler](#observe-events-on-a-known-scheduler) - 1. [Switch schedulers in as few places as possible](#switch-schedulers-in-as-few-places-as-possible) - 1. [Capture side effects within signal producers](#capture-side-effects-within-signal-producers) - 1. [Share the side effects of a signal producer by sharing one produced signal](#share-the-side-effects-of-a-signal-producer-by-sharing-one-produced-signal) - 1. [Prefer managing lifetime with operators over explicit disposal](#prefer-managing-lifetime-with-operators-over-explicit-disposal) - -**[Implementing new operators](#implementing-new-operators)** - - 1. [Prefer writing operators that apply to both signals and producers](#prefer-writing-operators-that-apply-to-both-signals-and-producers) - 1. [Compose existing operators when possible](#compose-existing-operators-when-possible) - 1. [Forward failure and interruption events as soon as possible](#forward-failure-and-interruption-events-as-soon-as-possible) - 1. [Switch over `Event` values](#switch-over-event-values) - 1. [Avoid introducing concurrency](#avoid-introducing-concurrency) - 1. [Avoid blocking in operators](#avoid-blocking-in-operators) - -## The `Event` contract - -[Events][] are fundamental to ReactiveSwift. [Signals][] and [signal producers][] both send -events, and may be collectively called “event streams.” - -Event streams must conform to the following grammar: - -``` -value* (interrupted | failed | completed)? -``` - -This states that an event stream consists of: - - 1. Any number of `value` events - 1. Optionally followed by one terminating event, which is any of `interrupted`, `failed`, or `completed` - -After a terminating event, no other events will be received. - -#### `value`s provide values or indicate the occurrence of events - -`value` events contain a payload known as the “value”. Only `value` events are -said to have a value. Since an event stream can contain any number of `value`s, -there are few restrictions on what those values can mean or be used for, except -that they must be of the same type. - -As an example, the value might represent an element from a collection, or -a progress update about some long-running operation. The value of a `value` event -might even represent nothing at all—for example, it’s common to use a value type -of `()` to indicate that something happened, without being more specific about -what that something was. - -Most of the event stream [operators][] act upon `value` events, as they represent the -“meaningful data” of a signal or producer. - -#### `failure`s behave like exceptions and propagate immediately - -`failed` events indicate that something went wrong, and contain a concrete error -that indicates what happened. Failures are fatal, and propagate as quickly as -possible to the consumer for handling. - -Failures also behave like exceptions, in that they “skip” operators, terminating -them along the way. In other words, most [operators][] immediately stop doing -work when a failure is received, and then propagate the failure onward. This even applies to time-shifted operators, like [`delay`][delay]—which, despite its name, will forward any failures immediately. - -Consequently, failures should only be used to represent “abnormal” termination. If it is important to let operators (or consumers) finish their work, a `value` -event describing the result might be more appropriate. - -If an event stream can _never_ fail, it should be parameterized with the -special [`NoError`][NoError] type, which statically guarantees that a `failed` -event cannot be sent upon the stream. - -#### `completion` indicates success - -An event stream sends `completed` when the operation has completed successfully, -or to indicate that the stream has terminated normally. - -Many operators manipulate the `completed` event to shorten or extend the -lifetime of an event stream. - -For example, [`take`][take] will complete after the specified number of values have -been received, thereby terminating the stream early. On the other hand, most -operators that accept multiple signals or producers will wait until _all_ of -them have completed before forwarding a `completed` event, since a successful -outcome will usually depend on all the inputs. - -#### `interruption`s cancel outstanding work and usually propagate immediately - -An `interrupted` event is sent when an event stream should cancel processing. -Interruption is somewhere between [success](#completion-indicates-success) -and [failure](#failures-behave-like-exceptions-and-propagate-immediately)—the -operation was not successful, because it did not get to finish, but it didn’t -necessarily “fail” either. - -Most [operators][] will propagate interruption immediately, but there are some -exceptions. For example, the [flattening operators][flatten] will ignore -`interrupted` events that occur on the _inner_ producers, since the cancellation -of an inner operation should not necessarily cancel the larger unit of work. - -ReactiveSwift will automatically send an `interrupted` event upon [disposal][Disposables], but it can -also be sent manually if necessary. Additionally, [custom -operators](#implementing-new-operators) must make sure to forward interruption -events to the observer. - -#### Events are serial - -ReactiveSwift guarantees that all events upon a stream will arrive serially. In other -words, it’s impossible for the observer of a signal or producer to receive -multiple `Event`s concurrently, even if the events are sent on multiple threads -simultaneously. - -This simplifies [operator][Operators] implementations and [observers][]. - -#### Events cannot be sent recursively - -Just like ReactiveSwift guarantees that [events will not be received -concurrently](#events-are-serial), it also guarantees that they won’t be -received recursively. As a consequence, [operators][] and [observers][] _do not_ need to -be reentrant. - -If an event is sent upon a signal from a thread that is _already processing_ -a previous event from that signal, deadlock will result. This is because -recursive signals are usually programmer error, and the determinacy of -a deadlock is preferable to nondeterministic race conditions. - -When a recursive signal is explicitly desired, the recursive event should be -time-shifted, with an operator like [`delay`][delay], to ensure that it isn’t sent from -an already-running event handler. - -#### Events are sent synchronously by default - -ReactiveSwift does not implicitly introduce concurrency or asynchrony. [Operators][] that -accept a [scheduler][Schedulers] may, but they must be explicitly invoked by the consumer of -the framework. - -A “vanilla” signal or producer will send all of its events synchronously by -default, meaning that the [observer][Observers] will be synchronously invoked for each event -as it is sent, and that the underlying work will not resume until the event -handler finishes. - -This is similar to how `NSNotificationCenter` or `UIControl` events are -distributed. - -## The `Signal` contract - -A [signal][Signals] is a stream of values that obeys [the `Event` contract](#the-event-contract). - -`Signal` is a reference type, because each signal has identity — in other words, each -signal has its own lifetime, and may eventually terminate. Once terminated, -a signal cannot be restarted. - -#### Signals start work when instantiated - -[`Signal.init`][Signal.init] immediately executes the generator closure that is passed to it. -This means that side effects may occur even before the initializer returns. - -It is also possible to send [events][] before the initializer returns. However, -since it is impossible for any [observers][] to be attached at this point, any -events sent this way cannot be received. - -#### Observing a signal does not have side effects - -The work associated with a `Signal` does not start or stop when [observers][] are -added or removed, so the [`observe`][observe] method (or the cancellation thereof) never -has side effects. - -A signal’s side effects can only be stopped through [a terminating event](#signals-are-retained-until-a-terminating-event-occurs), or by a silent disposal at the point that [the signal is neither publicly reachable nor being observed](#a-signal-is-alive-as-long-as-it-is-publicly-reachable-or-is-being-observed). - -#### All observers of a signal see the same events in the same order - -Because [observation does not have side -effects](#observing-a-signal-does-not-have-side-effects), a `Signal` never -customizes events for different [observers][]. When an event is sent upon a signal, -it will be [synchronously](#events-are-sent-synchronously-by-default) -distributed to all observers that are attached at that time, much like -how `NSNotificationCenter` sends notifications. - -In other words, there are not different event “timelines” per observer. All -observers effectively see the same stream of events. - -There is one exception to this rule: adding an observer to a signal _after_ it -has already terminated will result in exactly one -[`interrupted`](#interruption-cancels-outstanding-work-and-usually-propagates-immediately) -event sent to that specific observer. - -#### A signal is alive as long as it is publicly reachable or is being observed - -A `Signal` must be publicly retained for attaching new observers, but not -necessarily for keeping the stream of events alive. Moreover, a `Signal` retains -itself as long as there is still an active observer. - -In other words, if a `Signal` is neither publicly retained nor being observed, -it would dispose of the signal resources silently. - -Note that the input observer of a signal does not retain the signal itself. - -Long-running side effects are recommended to be modeled as an observer to the -signal. - -#### Terminating events dispose of signal resources - -When a terminating [event][Events] is sent along a `Signal`, all [observers][] will be -released, and any resources being used to generate events should be disposed of. - -The easiest way to ensure proper resource cleanup is to return a [disposable][Disposables] -from the generator closure, which will be disposed of when termination occurs. -The disposable should be responsible for releasing memory, closing file handles, -canceling network requests, or anything else that may have been associated with -the work being performed. - -## The `SignalProducer` contract - -A [signal producer][Signal Producers] is like a “recipe” for creating -[signals][]. Signal producers do not do anything by themselves—[work begins only -when a signal is produced](#signal-producers-start-work-on-demand-by-creating-signals). - -Since a signal producer is just a declaration of _how_ to create signals, it is -a value type, and has no memory management to speak of. - -#### Signal producers start work on demand by creating signals - -The [`start`][start] and [`startWithSignal`][startWithSignal] methods each -produce a `Signal` (implicitly and explicitly, respectively). After -instantiating the signal, the closure that was passed to -[`SignalProducer.init`][SignalProducer.init] will be executed, to start the flow -of [events][] after any observers have been attached. - -Although the producer itself is not _really_ responsible for the execution of -work, it’s common to speak of “starting” and “canceling” a producer. These terms -refer to producing a `Signal` that will start work, and [disposing of that -signal](#disposing-of-a-produced-signal-will-interrupt-it) to stop work. - -A producer can be started any number of times (including zero), and the work -associated with it will execute exactly that many times as well. - -#### Each produced signal may send different events at different times - -Because signal producers [start work on -demand](#signal-producers-start-work-on-demand-by-creating-signals), there may -be different [observers][] associated with each execution, and those observers -may see completely different [event][Events] timelines. - -In other words, events are generated from scratch for each time the producer is -started, and can be completely different (or in a completely different order) -from other times the producer is started. - -Nonetheless, each execution of a signal producer will follow [the `Event` -contract](#the-event-contract). - -#### Signal operators can be lifted to apply to signal producers - -Due to the relationship between signals and signal producers, it is possible to -automatically promote any [operators][] over one or more `Signal`s to apply to -the same number of `SignalProducer`s instead, using the [`lift`][lift] method. - -`lift` will apply the behavior of the specified operator to each `Signal` that -is [created when the signal producer is started](#signal-producers-start-work-on-demand-by-creating-signals). - -#### Disposing of a produced signal will interrupt it - -When a producer is started using the [`start`][start] or -[`startWithSignal`][startWithSignal] methods, a [`Disposable`][Disposables] is -automatically created and passed back. - -Disposing of this object will -[interrupt](#interruption-cancels-outstanding-work-and-usually-propagates-immediately) -the produced `Signal`, thereby canceling outstanding work and sending an -`interrupted` [event][Events] to all [observers][], and will also dispose of -everything added to the [`CompositeDisposable`][CompositeDisposable] in -[SignalProducer.init]. - -Note that disposing of one produced `Signal` will not affect other signals created -by the same `SignalProducer`. - -## Best practices - -The following recommendations are intended to help keep ReactiveSwift-based code -predictable, understandable, and performant. - -They are, however, only guidelines. Use best judgement when determining whether -to apply the recommendations here to a given piece of code. - -#### Process only as many values as needed - -Keeping an event stream alive longer than necessary can waste CPU and memory, as -unnecessary work is performed for results that will never be used. - -If only a certain number of values or certain number of time is required from -a [signal][Signals] or [producer][Signal Producers], operators like -[`take`][take] or [`takeUntil`][takeUntil] can be used to -automatically complete the stream once a certain condition is fulfilled. - -The benefit is exponential, too, as this will terminate dependent operators -sooner, potentially saving a significant amount of work. - -#### Observe events on a known scheduler - -When receiving a [signal][Signals] or [producer][Signal Producers] from unknown -code, it can be difficult to know which thread [events][] will arrive upon. Although -events are [guaranteed to be serial](#events-are-serial), sometimes stronger -guarantees are needed, like when performing UI updates (which must occur on the -main thread). - -Whenever such a guarantee is important, the [`observeOn`][observeOn] -[operator][Operators] should be used to force events to be received upon -a specific [scheduler][Schedulers]. - -#### Switch schedulers in as few places as possible - -Notwithstanding the [above](#observe-events-on-a-known-scheduler), [events][] -should only be delivered to a specific [scheduler][Schedulers] when absolutely -necessary. Switching schedulers can introduce unnecessary delays and cause an -increase in CPU load. - -Generally, [`observeOn`][observeOn] should only be used right before observing -the [signal][Signals], starting the [producer][Signal Producers], or binding to -a [property][Properties]. This ensures that events arrive on the expected -scheduler, without introducing multiple thread hops before their arrival. - -#### Capture side effects within signal producers - -Because [signal producers start work on -demand](#signal-producers-start-work-on-demand-by-creating-signals), any -functions or methods that return a [signal producer][Signal Producers] should -make sure that side effects are captured _within_ the producer itself, instead -of being part of the function or method call. - -For example, a function like this: - -```swift -func search(text: String) -> SignalProducer -``` - -… should _not_ immediately start a search. - -Instead, the returned producer should execute the search once for every time -that it is started. This also means that if the producer is never started, -a search will never have to be performed either. - -#### Share the side effects of a signal producer by sharing one produced signal - -If multiple [observers][] are interested in the results of a [signal -producer][Signal Producers], calling [`start`][start] once for each observer -means that the work associated with the producer will [execute that many -times](#signal-producers-start-work-on-demand-by-creating-signals) and [may not -generate the same results](#each-produced-signal-may-send-different-events-at-different-times). - -If: - - 1. the observers need to receive the exact same results - 1. the observers know about each other, or - 1. the code starting the producer knows about each observer - -… it may be more appropriate to start the producer _just once_, and share the -results of that one [signal][Signals] to all observers, by attaching them within -the closure passed to the [`startWithSignal`][startWithSignal] method. - -#### Prefer managing lifetime with operators over explicit disposal - -Although the [disposable][Disposables] returned from [`start`][start] makes -canceling a [signal producer][Signal Producers] really easy, explicit use of -disposables can quickly lead to a rat's nest of resource management and cleanup -code. - -There are almost always higher-level [operators][] that can be used instead of manual -disposal: - - * [`take`][take] can be used to automatically terminate a stream once a certain - number of values have been received. - * [`takeUntil`][takeUntil] can be used to automatically terminate - a [signal][Signals] or producer when an event occurs (for example, when - a “Cancel” button is pressed in the UI). - * [Properties][] and the `<~` operator can be used to “bind” the result of - a signal or producer, until termination or until the property is deallocated. - This can replace a manual observation that sets a value somewhere. - -## Implementing new operators - -ReactiveSwift provides a long list of built-in [operators][] that should cover most use -cases; however, ReactiveSwift is not a closed system. It's entirely valid to implement -additional operators for specialized uses, or for consideration in ReactiveSwift -itself. - -Implementing a new operator requires a careful attention to detail and a focus -on simplicity, to avoid introducing bugs into the calling code. - -These guidelines cover some of the common pitfalls and help preserve the -expected API contracts. It may also help to look at the implementations of -existing [`Signal`][Signals] and [`SignalProducer`][Signal Producers] operators for reference points. - -#### Prefer writing operators that apply to both signals and producers - -Since any [signal operator can apply to signal -producers](#signal-operators-can-be-lifted-to-apply-to-signal-producers), -writing custom operators in terms of [`Signal`][Signals] means that -[`SignalProducer`][Signal Producers] will get it “for free.” - -Even if the caller only needs to apply the new operator to signal producers at -first, this generality can save time and effort in the future. - -Of course, some capabilities _require_ producers (for example, any retrying or -repeating), so it may not always be possible to write a signal-based version -instead. - -#### Compose existing operators when possible - -Considerable thought has been put into the operators provided by ReactiveSwift, and they -have been validated through automated tests and through their real world use in -other projects. An operator that has been written from scratch may not be as -robust, or might not handle a special case that the built-in operators are aware -of. - -To minimize duplication and possible bugs, use the provided operators as much as -possible in a custom operator implementation. Generally, there should be very -little code written from scratch. - -#### Forward failure and interruption events as soon as possible - -Unless an operator is specifically built to handle -[failures](#failures-behave-like-exceptions-and-propagate-immediately) and -[interruptions](#interruption-cancels-outstanding-work-and-usually-propagates-immedaitely) -in a custom way, it should propagate those events to the observer as soon as -possible, to ensure that their semantics are honored. - -#### Switch over `Event` values - -Create your own [observer][Observers] to process raw [`Event`][Events] values, and use -a `switch` statement to determine the event type. - -For example: - -```swift -producer.start { event in - switch event { - case let .Next(value): - print("Next event: \(value)") - - case let .Failed(error): - print("Failed event: \(error)") - - case .Completed: - print("Completed event") - - case .Interrupted: - print("Interrupted event") - } -} -``` - -Since the compiler will generate a warning if the `switch` is missing any case, -this prevents mistakes in a custom operator’s event handling. - -#### Avoid introducing concurrency - -Concurrency is an extremely common source of bugs in programming. To minimize -the potential for deadlocks and race conditions, operators should not -concurrently perform their work. - -Callers always have the ability to [observe events on a specific -scheduler](#observe-events-on-a-known-scheduler), and ReactiveSwift offers built-in ways -to parallelize work, so custom operators don’t need to be concerned with it. - -#### Avoid blocking in operators - -Signal or producer operators should return a new signal or producer -(respectively) as quickly as possible. Any work that the operator needs to -perform should be part of the event handling logic, _not_ part of the operator -invocation itself. - -This guideline can be safely ignored when the purpose of an operator is to -synchronously retrieve one or more values from a stream, like `single()` or -`wait()`. - -[CompositeDisposable]: https://github.com/ReactiveCocoa/ReactiveSwift/tree/master/Sources/Disposable.swift -[Disposables]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#disposables -[Events]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#events -[Reactive Swift Framework Overview]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md -[NoError]: https://github.com/ReactiveCocoa/ReactiveSwift/tree/master/Sources/Errors.swift -[Observers]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#observers -[Operators]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/BasicOperators.md -[Properties]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#properties -[Schedulers]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#schedulers -[Signal Producers]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#signal-producers -[Signal.init]: https://github.com/ReactiveCocoa/ReactiveSwift/tree/master/Sources/Signal.swift -[Signal.pipe]: https://github.com/ReactiveCocoa/ReactiveSwift/tree/master/Sources/Signal.swift -[SignalProducer.init]: https://github.com/ReactiveCocoa/ReactiveSwift/tree/master/Sources/SignalProducer.swift -[Signals]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#signals -[delay]: https://github.com/ReactiveCocoa/ReactiveSwift/tree/master/Sources/Signal.swift -[flatten]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/BasicOperators.md#flattening-producers -[lift]: https://github.com/ReactiveCocoa/ReactiveSwift/tree/master/Sources/SignalProducer.swift -[observe]: https://github.com/ReactiveCocoa/ReactiveSwift/tree/master/Sources/Signal.swift -[observeOn]: https://github.com/ReactiveCocoa/ReactiveSwift/tree/master/Sources/Signal.swift -[start]: https://github.com/ReactiveCocoa/ReactiveSwift/tree/master/Sources/SignalProducer.swift -[startWithSignal]: https://github.com/ReactiveCocoa/ReactiveSwift/tree/master/Sources/SignalProducer.swift -[take]: https://github.com/ReactiveCocoa/ReactiveSwift/tree/master/Sources/Signal.swift -[takeUntil]: https://github.com/ReactiveCocoa/ReactiveSwift/tree/master/Sources/Signal.swift From d444544a4c227a010546d587b1f3535f5984d150 Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Sun, 11 Dec 2016 11:55:36 +0100 Subject: [PATCH 0652/1028] Add specific references to documents in ReactiveSwift that are now removed in ReactiveCocoa --- Documentation/README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Documentation/README.md b/Documentation/README.md index 0b56f5dcf9..b4e048a518 100644 --- a/Documentation/README.md +++ b/Documentation/README.md @@ -2,7 +2,11 @@ ReactiveCocoa is based on ReactiveSwift and extends ReactiveSwift by Cocoa (esp. UIKit and AppKit) specific aspects. -For documentation of the basics, please refer to the [ReactiveSwift Documentation][ReactiveSwiftDocumentation]. +For documentation of the basics, please refer to the [ReactiveSwift Documentation][ReactiveSwiftDocumentation]. Specifically, you can find documentation about + +* [Framework Overview][] +* [Basic Operators][] +* [Design Guidelines][] This document outlines the additions that ReactiveCocoa brings over ReactiveSwift. @@ -98,6 +102,9 @@ ReactiveCocoa provides UI bindings for UIKit and AppKit via the `reactive` struc CocoaAction wraps an Action for use by a GUI control (such as `NSControl` or `UIControl`), with KVO, or with Cocoa Bindings. [ReactiveSwiftDocumentation]: https://github.com/ReactiveCocoa/ReactiveSwift/tree/master/Documentation +[Framework Overview]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md +[Basic Operators]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/BasicOperators.md +[Design Guidelines]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/DesignGuidelines.md [`Signal`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#signals [`SignalProducer`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#signal-producers [`Action`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#actions From f1e1acdda9c9aefea0c718203df10a054babfa15 Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Sun, 11 Dec 2016 12:48:41 +0100 Subject: [PATCH 0653/1028] Update DebuggingTechniques --- Documentation/DebuggingTechniques.md | 66 +++++++--------------------- 1 file changed, 15 insertions(+), 51 deletions(-) diff --git a/Documentation/DebuggingTechniques.md b/Documentation/DebuggingTechniques.md index d605844f87..ebe81dc230 100644 --- a/Documentation/DebuggingTechniques.md +++ b/Documentation/DebuggingTechniques.md @@ -15,7 +15,7 @@ Below is an example of type-error scenario: ```swift SignalProducer(value:42) - .on(next: { answer in + .on(value: { answer in return _ }) .startWithCompleted { @@ -23,12 +23,11 @@ SignalProducer(value:42) } ``` -The code above will not compile with the following error on a `print` call `error: ambiguous reference to member 'print' -print("Completed.")`. To find the actual compile error, the chain needs to be broken apart. Add explicit definitions of closure types on each of the steps: +The code above will not compile with the following error on the `.startWithCompleted` call `error: cannot convert value of type 'Disposable' to closure result type '()'. To find the actual compile error, the chain needs to be broken apart. Add explicit definitions of closure types on each of the steps: ```swift let initialProducer = SignalProducer.init(value:42) -let sideEffectProducer = initialProducer.on(next: { (answer: Int) in +let sideEffectProducer = initialProducer.on(value: { (answer: Int) in return _ }) let disposable = sideEffectProducer.startWithCompleted { @@ -36,45 +35,15 @@ let disposable = sideEffectProducer.startWithCompleted { } ``` -The code above will not compile too, but with the error `error: cannot convert value of type '(_) -> _' to expected argument type '(Int -> ())?'` on definition of `on` closure. This gives enough of information to locate unexpected `return _` since `on` closure should not have any return value. - -#### Binding `DynamicProperty` with `<~` operator - -Using the `<~` operator to bind a `Signal` or a `SignalProducer` to a `DynamicProperty` can result in unexpected compiler errors. - -Below is an example of this scenario: - -```swift -let label = UILabel() -let property = MutableProperty("") - -DynamicProperty(object: label, keyPath: "text") <~ property.producer -``` - -This will often result in a compiler error: - -> error: binary operator '<~' cannot be applied to operands of type 'DynamicProperty' and 'SignalProducer' -DynamicProperty(object: label, keyPath: "text") <~ property.producer - -The reason is a limitation in the swift type checker - A `DynamicProperty` always has a type of `AnyObject?`, but the `<~` operator requires the values of both sides to have the same type, so the right side value would have to be `AnyObject?` as well, but usually a more concrete type is used (in this example `String`). - -Usually, the fix is as easy as adding a `.map{ $0 }`. - -```swift -DynamicProperty(object: label, keyPath: "text") <~ property.producer.map { $0 } -``` - -This allows the type checker to infer that `String` can be converted to `AnyProperty?` and thus, the binding succeeds. +The code above will not compile too, but with the error `error: cannot convert value of type '(Int) -> _' to expected argument type '((Int) -> Void)?'` on definition of `on` closure. This gives enough of information to locate unexpected `return _` since `on` closure should not have any return value. #### Debugging event streams As mentioned in the README, stream debugging can be quite difficut and tedious, so we provide the `logEvents` operator. In its simplest form: ```swift -let searchString = textField.rac_textSignal() - .toSignalProducer() - .map { text in text as! String } - .throttle(0.5, onScheduler: QueueScheduler.mainQueueScheduler) +let searchString = textField.reactive.continuousTextValues + .throttle(0.5, on: QueueScheduler.main) .logEvents() ``` @@ -98,31 +67,26 @@ func debugLog(identifier: String, event: String, fileName: String, functionName: You would then: ```swift -let searchString = textField.rac_textSignal() - .toSignalProducer() - .map { text in text as! String } - .throttle(0.5, onScheduler: QueueScheduler.mainQueueScheduler) +let searchString = textField.reactive.continuousTextValues + .throttle(0.5, on: QueueScheduler.main) .logEvents(logger: debugLog) ``` We also provide the `identifier` parameter. This is useful when you are debugging multiple streams and you don't want to get lost: ```swift -let searchString = textField.rac_textSignal() - .toSignalProducer() - .map { text in text as! String } - .throttle(0.5, onScheduler: QueueScheduler.mainQueueScheduler) +let searchString = textField.reactive.continuousTextValues + .throttle(0.5, on: QueueScheduler.main) .logEvents(identifier: "✨My awesome stream ✨") ``` -There also cases, specially with [hot signals][[Signals]], when there is simply too much output. For those, you can specify which events you are interested in: +There also cases, specially with [hot signals][Signal], when there is simply too much output. For those, you can specify which events you are interested in: ```swift -let searchString = textField.rac_textSignal() - .toSignalProducer() - .map { text in text as! String } - .throttle(0.5, onScheduler: QueueScheduler.mainQueueScheduler) - .logEvents(events:[.Disposed]) // This will happen when the `UITextField` is released +let searchString = textField.reactive.continuousTextValues + .throttle(0.5, on: QueueScheduler.main) + .logEvents(events: [.disposed]) ``` +[Signal]: ../Sources/Signal.swift From cccc44600d8b88c524e82e0b8ba9ff05a3037b9a Mon Sep 17 00:00:00 2001 From: Seil Oh Date: Tue, 13 Dec 2016 11:33:28 +0900 Subject: [PATCH 0654/1028] Add reactive NSPopUpButton --- ReactiveCocoa.xcodeproj/project.pbxproj | 8 ++ ReactiveCocoa/AppKit/NSPopUpButton.swift | 40 +++++++++ .../AppKit/NSPopUpButtonSpec.swift | 86 +++++++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 ReactiveCocoa/AppKit/NSPopUpButton.swift create mode 100644 ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index a32b30963d..e72739f154 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -212,6 +212,8 @@ D05E662619EDD83000904ACA /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; }; D0A2260E1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */; }; D0A2260F1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */; }; + D9558AB81DFF805A003254E1 /* NSPopUpButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9558AB71DFF805A003254E1 /* NSPopUpButton.swift */; }; + D9558AB91DFF86C0003254E1 /* NSPopUpButtonSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9558AB51DFF7B90003254E1 /* NSPopUpButtonSpec.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -394,6 +396,8 @@ D047263C19E49FE8006002AA /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; D05E662419EDD82000904ACA /* Nimble.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Nimble.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = DynamicPropertySpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + D9558AB51DFF7B90003254E1 /* NSPopUpButtonSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSPopUpButtonSpec.swift; sourceTree = ""; }; + D9558AB71DFF805A003254E1 /* NSPopUpButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSPopUpButton.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -507,6 +511,7 @@ 4ABEFE2A1DCFD0030066A8C2 /* NSTableView.swift */, 9ADE4A951DA6F018005C2AC8 /* NSTextField.swift */, 9A6AAA251DB8F5280013AAEA /* ReusableComponents.swift */, + D9558AB71DFF805A003254E1 /* NSPopUpButton.swift */, ); path = AppKit; sourceTree = ""; @@ -571,6 +576,7 @@ 9ADE4A8D1DA6D965005C2AC8 /* NSControlSpec.swift */, 4ABEFE2C1DCFD0180066A8C2 /* NSTableViewSpec.swift */, 9A6AAA271DB8F7EB0013AAEA /* ReusableComponentsSpec.swift */, + D9558AB51DFF7B90003254E1 /* NSPopUpButtonSpec.swift */, ); path = AppKit; sourceTree = ""; @@ -1163,6 +1169,7 @@ 9A2E425E1DAA6737006D909F /* CocoaTarget.swift in Sources */, 9ADE4A891DA6D206005C2AC8 /* NSControl.swift in Sources */, 9AF0EA751D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, + D9558AB81DFF805A003254E1 /* NSPopUpButton.swift in Sources */, 9ADE4A911DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, 53A6BED21DD4BCA90016C058 /* MKMapView.swift in Sources */, 9A1D065B1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, @@ -1182,6 +1189,7 @@ 9ADFE5A11DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */, 4ABEFE331DCFD0630066A8C2 /* NSCollectionViewSpec.swift in Sources */, B696FB811A7640C00075236D /* TestError.swift in Sources */, + D9558AB91DFF86C0003254E1 /* NSPopUpButtonSpec.swift in Sources */, BFA6B94D1A7604D400C846D1 /* SignalProducerNimbleMatchers.swift in Sources */, 9A6AAA2B1DB8F85C0013AAEA /* ReusableComponentsSpec.swift in Sources */, CD8401831CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, diff --git a/ReactiveCocoa/AppKit/NSPopUpButton.swift b/ReactiveCocoa/AppKit/NSPopUpButton.swift new file mode 100644 index 0000000000..87a09c98af --- /dev/null +++ b/ReactiveCocoa/AppKit/NSPopUpButton.swift @@ -0,0 +1,40 @@ +// +// NSPopUpButton.swift +// ReactiveCocoa +// +// Created by Seil Oh on 2016. 12. 13.. +// Copyright © 2016년 GitHub. All rights reserved. +// + +import ReactiveSwift +import AppKit +import enum Result.NoError + +extension Reactive where Base: NSPopUpButton { + + /// A signal of selected indexes + public var selectedIndexes: Signal { + return self.integerValues.map { [unowned base = self.base] _ -> Int? in + guard let item = base.selectedItem else { return nil } + return base.index(of: item) + } + .skipNil() + } + /// Sets the button with an index. + public var selectedIndex: BindingTarget { + return makeBindingTarget { $0.selectItem(at: $1) } + } + + /// A signal of selected title + public var selectedTitles: Signal { + return self.integerValues.map { [unowned base = self.base] _ -> String? in + return base.selectedItem?.title + } + .skipNil() + } + + /// Sets the button with title. + public var selectedTitle: BindingTarget { + return makeBindingTarget { $0.selectItem(withTitle: $1) } + } +} diff --git a/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift b/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift new file mode 100644 index 0000000000..b20cb76d4d --- /dev/null +++ b/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift @@ -0,0 +1,86 @@ +// +// NSPopUpButtonSpec.swift +// ReactiveCocoa +// +// Created by Seil Oh on 2016. 12. 13.. +// Copyright © 2016년 GitHub. All rights reserved. +// + +import Quick +import Nimble +import ReactiveCocoa +import ReactiveSwift +import Result +import AppKit + +final class NSPopUpButtonSepc: QuickSpec { + + override func spec() { + + describe("NSPopUpButton") { + var button: NSPopUpButton! + var window: NSWindow! + + weak var _button: NSButton? + + let testTitles = (0..<100).map { $0.description } + + beforeEach { + window = NSWindow() + button = NSPopUpButton(frame: .zero) + _button = button + button.addItems(withTitles: testTitles ) + window.contentView?.addSubview(button) + } + + afterEach { + autoreleasepool { + button.removeFromSuperview() + button = nil + } + expect(_button).to(beNil()) + } + + it("should emit selected index changes") { + var values = [Int]() + button.reactive.selectedIndexes.observeValues { values.append($0) } + + button.menu?.performActionForItem(at: 1) + button.menu?.performActionForItem(at: 99) + + expect(values) == [1, 99] + } + + it("should emit selected title changes") { + var values = [String]() + button.reactive.selectedTitles.observeValues { values.append($0) } + + button.menu?.performActionForItem(at: 1) + button.menu?.performActionForItem(at: 99) + + expect(values) == ["1", "99"] + } + + it("should accept changes from its bindings to its index values") { + + let (signal, observer) = Signal.pipe() + button.reactive.selectedIndex <~ SignalProducer(signal: signal) + observer.send(value: 1) + expect(button.selectedItem?.title) == "1" + observer.send(value: 99) + expect(button.selectedItem?.title) == "99" + } + + it("should accept changes from its bindings to its title values") { + + let (signal, observer) = Signal.pipe() + button.reactive.selectedTitle <~ SignalProducer(signal: signal) + observer.send(value: "1") + expect(button.selectedItem?.title) == "1" + observer.send(value: "99") + expect(button.selectedItem?.title) == "99" + } + } + + } +} From b1f48b3e5e1cddb2af4fad54c558f1f22f1f9ed9 Mon Sep 17 00:00:00 2001 From: David Caunt Date: Tue, 13 Dec 2016 18:35:32 +0000 Subject: [PATCH 0655/1028] Add docs, runtime error for delegate override --- ReactiveCocoa/UIKit/iOS/UISearchBar.swift | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ReactiveCocoa/UIKit/iOS/UISearchBar.swift b/ReactiveCocoa/UIKit/iOS/UISearchBar.swift index c2b856944e..72fddfaae9 100644 --- a/ReactiveCocoa/UIKit/iOS/UISearchBar.swift +++ b/ReactiveCocoa/UIKit/iOS/UISearchBar.swift @@ -15,6 +15,8 @@ extension Reactive where Base: UISearchBar { private var delegate: ReactiveUISearchBarDelegate { if let delegate = objc_getAssociatedObject(base, &delegateKey) as? ReactiveUISearchBarDelegate { return delegate + } else if let _ = base.delegate { + fatalError("Cannot use reactive values on UISearchBar with a custom delegate!") } let delegate = ReactiveUISearchBarDelegate() @@ -30,6 +32,10 @@ extension Reactive where Base: UISearchBar { /// A signal of text values emitted by the search bar upon end of editing. /// + /// - important: Creating this `Signal` requires the reactive extension + /// provider to become the `delegate` of the search bar. Setting your own + /// `delegate` is not supported and will result in a runtime error. + /// /// - note: To observe text values that change on all editing events, /// see `continuousTextValues`. public var textValues: Signal { @@ -39,6 +45,10 @@ extension Reactive where Base: UISearchBar { /// A signal of text values emitted by the search bar upon any changes. /// + /// - important: Creating this `Signal` requires the reactive extension + /// provider to become the `delegate` of the search bar. Setting your own + /// `delegate` is not supported and will result in a runtime error. + /// /// - note: To observe text values only when editing ends, see `textValues`. public var continuousTextValues: Signal { return delegate.reactive.trigger(for: #selector(UISearchBarDelegate.searchBar(_:textDidChange:))) From 771933d30e73501379d15d67bc55584798b7dc8c Mon Sep 17 00:00:00 2001 From: Seil Oh Date: Wed, 14 Dec 2016 09:11:57 +0900 Subject: [PATCH 0656/1028] Improved formattings. --- ReactiveCocoa/AppKit/NSPopUpButton.swift | 29 ++++++++----------- .../AppKit/NSPopUpButtonSpec.swift | 19 +++--------- 2 files changed, 16 insertions(+), 32 deletions(-) diff --git a/ReactiveCocoa/AppKit/NSPopUpButton.swift b/ReactiveCocoa/AppKit/NSPopUpButton.swift index 87a09c98af..59b818a3cc 100644 --- a/ReactiveCocoa/AppKit/NSPopUpButton.swift +++ b/ReactiveCocoa/AppKit/NSPopUpButton.swift @@ -1,11 +1,3 @@ -// -// NSPopUpButton.swift -// ReactiveCocoa -// -// Created by Seil Oh on 2016. 12. 13.. -// Copyright © 2016년 GitHub. All rights reserved. -// - import ReactiveSwift import AppKit import enum Result.NoError @@ -14,12 +6,14 @@ extension Reactive where Base: NSPopUpButton { /// A signal of selected indexes public var selectedIndexes: Signal { - return self.integerValues.map { [unowned base = self.base] _ -> Int? in - guard let item = base.selectedItem else { return nil } - return base.index(of: item) - } - .skipNil() + return self.integerValues + .map { [unowned base = self.base] _ -> Int? in + guard let item = base.selectedItem else { return nil } + return base.index(of: item) + } + .skipNil() } + /// Sets the button with an index. public var selectedIndex: BindingTarget { return makeBindingTarget { $0.selectItem(at: $1) } @@ -27,10 +21,11 @@ extension Reactive where Base: NSPopUpButton { /// A signal of selected title public var selectedTitles: Signal { - return self.integerValues.map { [unowned base = self.base] _ -> String? in - return base.selectedItem?.title - } - .skipNil() + return self.integerValues + .map { [unowned base = self.base] _ -> String? in + return base.selectedItem?.title + } + .skipNil() } /// Sets the button with title. diff --git a/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift b/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift index b20cb76d4d..864d9708b3 100644 --- a/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift @@ -1,11 +1,3 @@ -// -// NSPopUpButtonSpec.swift -// ReactiveCocoa -// -// Created by Seil Oh on 2016. 12. 13.. -// Copyright © 2016년 GitHub. All rights reserved. -// - import Quick import Nimble import ReactiveCocoa @@ -14,15 +6,11 @@ import Result import AppKit final class NSPopUpButtonSepc: QuickSpec { - override func spec() { - describe("NSPopUpButton") { var button: NSPopUpButton! var window: NSWindow! - weak var _button: NSButton? - let testTitles = (0..<100).map { $0.description } beforeEach { @@ -62,25 +50,26 @@ final class NSPopUpButtonSepc: QuickSpec { } it("should accept changes from its bindings to its index values") { - let (signal, observer) = Signal.pipe() button.reactive.selectedIndex <~ SignalProducer(signal: signal) + observer.send(value: 1) expect(button.selectedItem?.title) == "1" + observer.send(value: 99) expect(button.selectedItem?.title) == "99" } it("should accept changes from its bindings to its title values") { - let (signal, observer) = Signal.pipe() button.reactive.selectedTitle <~ SignalProducer(signal: signal) + observer.send(value: "1") expect(button.selectedItem?.title) == "1" + observer.send(value: "99") expect(button.selectedItem?.title) == "99" } } - } } From 5db41f1be9b0d8fec4c64b69b1ea7ac3f175f02e Mon Sep 17 00:00:00 2001 From: David Caunt Date: Wed, 14 Dec 2016 19:25:30 +0000 Subject: [PATCH 0657/1028] Update comment --- ReactiveCocoa/UIKit/iOS/UISearchBar.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ReactiveCocoa/UIKit/iOS/UISearchBar.swift b/ReactiveCocoa/UIKit/iOS/UISearchBar.swift index 72fddfaae9..78fac291d8 100644 --- a/ReactiveCocoa/UIKit/iOS/UISearchBar.swift +++ b/ReactiveCocoa/UIKit/iOS/UISearchBar.swift @@ -32,9 +32,9 @@ extension Reactive where Base: UISearchBar { /// A signal of text values emitted by the search bar upon end of editing. /// - /// - important: Creating this `Signal` requires the reactive extension - /// provider to become the `delegate` of the search bar. Setting your own - /// `delegate` is not supported and will result in a runtime error. + /// - important: Creating this Signal will make the reactive extension + /// provider the delegate of the search bar. Setting your own delegate is + /// not supported and will result in a runtime error. /// /// - note: To observe text values that change on all editing events, /// see `continuousTextValues`. @@ -45,9 +45,9 @@ extension Reactive where Base: UISearchBar { /// A signal of text values emitted by the search bar upon any changes. /// - /// - important: Creating this `Signal` requires the reactive extension - /// provider to become the `delegate` of the search bar. Setting your own - /// `delegate` is not supported and will result in a runtime error. + /// - important: Creating this Signal will make the reactive extension + /// provider the delegate of the search bar. Setting your own delegate is + /// not supported and will result in a runtime error. /// /// - note: To observe text values only when editing ends, see `textValues`. public var continuousTextValues: Signal { From e6cc1ba10206ecfc6a03b5e0af93f9abb5290ea5 Mon Sep 17 00:00:00 2001 From: Dave Caunt Date: Thu, 15 Dec 2016 10:39:57 +0000 Subject: [PATCH 0658/1028] Fix a typo --- ReactiveCocoa/UIKit/iOS/UIKeyboard.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift b/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift index b69cc01eb1..4b57b96151 100644 --- a/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift +++ b/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift @@ -42,7 +42,7 @@ public struct KeyboardChangeContext { } extension Reactive where Base: NotificationCenter { - /// Create a `Signal` that nofities whenever the system keyboard announces an + /// Create a `Signal` that notifies whenever the system keyboard announces an /// upcoming change in its frame. /// /// - returns: A `Signal` that emits the context of every change in the From 936d842f0553025934506a59abbc343d841d18b9 Mon Sep 17 00:00:00 2001 From: Dave Caunt Date: Thu, 15 Dec 2016 16:12:45 +0000 Subject: [PATCH 0659/1028] Add reactive extensions for UIScrollView --- ReactiveCocoa.xcodeproj/project.pbxproj | 8 ++ ReactiveCocoa/UIKit/UIScrollView.swift | 34 ++++++ .../UIKit/UIScrollViewSpec.swift | 111 ++++++++++++++++++ 3 files changed, 153 insertions(+) create mode 100644 ReactiveCocoa/UIKit/UIScrollView.swift create mode 100644 ReactiveCocoaTests/UIKit/UIScrollViewSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index a32b30963d..00cdda406f 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -182,6 +182,8 @@ BE330A191D634F5900806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A181D634F5900806963 /* ReactiveSwift.framework */; }; BE330A1B1D634F5F00806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A1A1D634F5F00806963 /* ReactiveSwift.framework */; }; BEE020661D637B0000DF261F /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B696FB801A7640C00075236D /* TestError.swift */; }; + BF4335651E02AC7600AC88DD /* UIScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF4335641E02AC7600AC88DD /* UIScrollView.swift */; }; + BF4335681E02EF0600AC88DD /* UIScrollViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF4335661E02EEDE00AC88DD /* UIScrollViewSpec.swift */; }; BFA6B94D1A7604D400C846D1 /* SignalProducerNimbleMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */; }; BFA6B94E1A7604D500C846D1 /* SignalProducerNimbleMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */; }; CD0C45DE1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */; }; @@ -361,6 +363,8 @@ BE330A161D634F4E00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-watchos/ReactiveSwift.framework"; sourceTree = ""; }; BE330A181D634F5900806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-appletvos/ReactiveSwift.framework"; sourceTree = ""; }; BE330A1A1D634F5F00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-appletvos/ReactiveSwift.framework"; sourceTree = ""; }; + BF4335641E02AC7600AC88DD /* UIScrollView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIScrollView.swift; sourceTree = ""; }; + BF4335661E02EEDE00AC88DD /* UIScrollViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIScrollViewSpec.swift; sourceTree = ""; }; BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalProducerNimbleMatchers.swift; sourceTree = ""; }; CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DynamicProperty.swift; sourceTree = ""; }; CD8401821CEE8ED7009F0ABF /* CocoaActionSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaActionSpec.swift; sourceTree = ""; }; @@ -526,6 +530,7 @@ 9A1D05F31D93E9F100ACF44C /* UIImageView.swift */, 9A1D05F41D93E9F100ACF44C /* UILabel.swift */, 9A1D05F51D93E9F100ACF44C /* UIProgressView.swift */, + BF4335641E02AC7600AC88DD /* UIScrollView.swift */, 9A1D05F61D93E9F100ACF44C /* UISegmentedControl.swift */, 4ABEFDE11DCFC8560066A8C2 /* UITableView.swift */, 9A1D05FA1D93E9F100ACF44C /* UITextField.swift */, @@ -550,6 +555,7 @@ 9A1D062B1D93EA7E00ACF44C /* UIImageViewSpec.swift */, 9A1D062C1D93EA7E00ACF44C /* UILabelSpec.swift */, 9A1D062D1D93EA7E00ACF44C /* UIProgressViewSpec.swift */, + BF4335661E02EEDE00AC88DD /* UIScrollViewSpec.swift */, 9A1D062E1D93EA7E00ACF44C /* UISegmentedControlSpec.swift */, 53AC46CE1DD6FC0000C799E1 /* UISliderSpec.swift */, 531866F91DD7925600D1285F /* UIStepperSpec.swift */, @@ -1216,6 +1222,7 @@ 9A1D060A1D93EA0000ACF44C /* UISwitch.swift in Sources */, 9A1D065C1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, 9A1D06071D93EA0000ACF44C /* UILabel.swift in Sources */, + BF4335651E02AC7600AC88DD /* UIScrollView.swift in Sources */, 53A6BED31DD4BCA90016C058 /* MKMapView.swift in Sources */, 4A0E11001D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, 9ADE4A921DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, @@ -1258,6 +1265,7 @@ 9A1D06421D93EA7E00ACF44C /* UIDatePickerSpec.swift in Sources */, 53AC46CF1DD6FC0000C799E1 /* UISliderSpec.swift in Sources */, 9A6AAA0F1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */, + BF4335681E02EF0600AC88DD /* UIScrollViewSpec.swift in Sources */, 3BCAAC7D1DEE1A2D00B30335 /* UIRefreshControlSpec.swift in Sources */, 53A6BED71DD4BD2C0016C058 /* MKMapViewSpec.swift in Sources */, 9A1D06381D93EA7E00ACF44C /* UIBarButtonItemSpec.swift in Sources */, diff --git a/ReactiveCocoa/UIKit/UIScrollView.swift b/ReactiveCocoa/UIKit/UIScrollView.swift new file mode 100644 index 0000000000..384c9dc214 --- /dev/null +++ b/ReactiveCocoa/UIKit/UIScrollView.swift @@ -0,0 +1,34 @@ +import ReactiveSwift +import UIKit + +extension Reactive where Base: UIScrollView { + /// Sets the content inset of the scroll view. + public var contentInset: BindingTarget { + return makeBindingTarget { $0.contentInset = $1 } + } + + /// Sets the scroll indicator insets of the scroll view. + public var scrollIndicatorInsets: BindingTarget { + return makeBindingTarget { $0.scrollIndicatorInsets = $1 } + } + + /// Sets whether scrolling the scroll view is enabled. + public var isScrollEnabled: BindingTarget { + return makeBindingTarget { $0.isScrollEnabled = $1 } + } + + /// Sets the zoom scale of the scroll view. + public var zoomScale: BindingTarget { + return makeBindingTarget { $0.zoomScale = $1 } + } + + /// Sets the minimum zoom scale of the scroll view. + public var minimumZoomScale: BindingTarget { + return makeBindingTarget { $0.minimumZoomScale = $1 } + } + + /// Sets the maximum zoom scale of the scroll view. + public var maximumZoomScale: BindingTarget { + return makeBindingTarget { $0.maximumZoomScale = $1 } + } +} diff --git a/ReactiveCocoaTests/UIKit/UIScrollViewSpec.swift b/ReactiveCocoaTests/UIKit/UIScrollViewSpec.swift new file mode 100644 index 0000000000..cffc31bc1c --- /dev/null +++ b/ReactiveCocoaTests/UIKit/UIScrollViewSpec.swift @@ -0,0 +1,111 @@ +import ReactiveSwift +import ReactiveCocoa +import UIKit +import Quick +import Nimble +import enum Result.NoError + +private final class UIScrollViewDelegateForZooming: NSObject, UIScrollViewDelegate { + func viewForZooming(in scrollView: UIScrollView) -> UIView? { + return scrollView.subviews.first! + } +} + +class UIScrollViewSpec: QuickSpec { + override func spec() { + var scrollView: UIScrollView! + weak var _scrollView: UIScrollView? + + beforeEach { + scrollView = UIScrollView(frame: .zero) + _scrollView = scrollView + } + + afterEach { + scrollView = nil + expect(_scrollView).to(beNil()) + } + + it("should accept changes from bindings to its content inset value") { + scrollView.contentInset = .zero + + let (pipeSignal, observer) = Signal.pipe() + scrollView.reactive.contentInset <~ SignalProducer(signal: pipeSignal) + + observer.send(value: UIEdgeInsets(top: 1, left: 2, bottom: 3, right: 4)) + expect(scrollView.contentInset) == UIEdgeInsets(top: 1, left: 2, bottom: 3, right: 4) + + observer.send(value: .zero) + expect(scrollView.contentInset) == UIEdgeInsets.zero + } + + it("should accept changes from bindings to its scroll indicator insets value") { + scrollView.scrollIndicatorInsets = .zero + + let (pipeSignal, observer) = Signal.pipe() + scrollView.reactive.scrollIndicatorInsets <~ SignalProducer(signal: pipeSignal) + + observer.send(value: UIEdgeInsets(top: 1, left: 2, bottom: 3, right: 4)) + expect(scrollView.scrollIndicatorInsets) == UIEdgeInsets(top: 1, left: 2, bottom: 3, right: 4) + + observer.send(value: .zero) + expect(scrollView.scrollIndicatorInsets) == UIEdgeInsets.zero + } + + it("should accept changes from bindings to its scroll enabled state") { + scrollView.isScrollEnabled = true + + let (pipeSignal, observer) = Signal.pipe() + scrollView.reactive.isScrollEnabled <~ SignalProducer(signal: pipeSignal) + + observer.send(value: true) + expect(scrollView.isScrollEnabled) == true + + observer.send(value: false) + expect(scrollView.isScrollEnabled) == false + } + + it("should accept changes from bindings to its zoom scale value") { + let contentView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) + scrollView.addSubview(contentView) + let delegate = UIScrollViewDelegateForZooming() + scrollView.delegate = delegate + + scrollView.minimumZoomScale = 1 + scrollView.maximumZoomScale = 5 + scrollView.zoomScale = 1 + + let (pipeSignal, observer) = Signal.pipe() + scrollView.reactive.zoomScale <~ SignalProducer(signal: pipeSignal) + + observer.send(value: 3) + expect(scrollView.zoomScale) == 3 + observer.send(value: 1) + expect(scrollView.zoomScale) == 1 + } + + it("should accept changes from bindings to its minimum zoom scale value") { + scrollView.minimumZoomScale = 0 + + let (pipeSignal, observer) = Signal.pipe() + scrollView.reactive.minimumZoomScale <~ SignalProducer(signal: pipeSignal) + + observer.send(value: 42) + expect(scrollView.minimumZoomScale) == 42 + observer.send(value: 0) + expect(scrollView.minimumZoomScale) == 0 + } + + it("should accept changes from bindings to its maximum zoom scale value") { + scrollView.maximumZoomScale = 0 + + let (pipeSignal, observer) = Signal.pipe() + scrollView.reactive.maximumZoomScale <~ SignalProducer(signal: pipeSignal) + + observer.send(value: 42) + expect(scrollView.maximumZoomScale) == 42 + observer.send(value: 0) + expect(scrollView.maximumZoomScale) == 0 + } + } +} From c17064681bfaf1471c661a8e9052bcbcb068b802 Mon Sep 17 00:00:00 2001 From: Seil Oh Date: Fri, 16 Dec 2016 09:27:06 +0900 Subject: [PATCH 0660/1028] convert all to optional --- ReactiveCocoa/AppKit/NSPopUpButton.swift | 29 ++++++++------- .../AppKit/NSPopUpButtonSpec.swift | 35 +++++++++++++------ 2 files changed, 40 insertions(+), 24 deletions(-) diff --git a/ReactiveCocoa/AppKit/NSPopUpButton.swift b/ReactiveCocoa/AppKit/NSPopUpButton.swift index 59b818a3cc..d71affbd00 100644 --- a/ReactiveCocoa/AppKit/NSPopUpButton.swift +++ b/ReactiveCocoa/AppKit/NSPopUpButton.swift @@ -5,31 +5,34 @@ import enum Result.NoError extension Reactive where Base: NSPopUpButton { /// A signal of selected indexes - public var selectedIndexes: Signal { - return self.integerValues + public var selectedIndexes: Signal { + return self.objectValues .map { [unowned base = self.base] _ -> Int? in - guard let item = base.selectedItem else { return nil } - return base.index(of: item) + guard base.indexOfSelectedItem != -1 else { return nil } + return base.indexOfSelectedItem } - .skipNil() } /// Sets the button with an index. - public var selectedIndex: BindingTarget { - return makeBindingTarget { $0.selectItem(at: $1) } + public var selectedIndex: BindingTarget { + return makeBindingTarget { + $0.selectItem(at: $1 ?? -1) + } } /// A signal of selected title - public var selectedTitles: Signal { - return self.integerValues + public var selectedTitles: Signal { + return self.objectValues .map { [unowned base = self.base] _ -> String? in - return base.selectedItem?.title + return base.titleOfSelectedItem } - .skipNil() } /// Sets the button with title. - public var selectedTitle: BindingTarget { - return makeBindingTarget { $0.selectItem(withTitle: $1) } + /// note: emitting nil to this target will set selectedTitle to empty string + public var selectedTitle: BindingTarget { + return makeBindingTarget { + $0.selectItem(withTitle: $1 ?? "") + } } } diff --git a/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift b/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift index 864d9708b3..0e5731e830 100644 --- a/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift @@ -30,38 +30,47 @@ final class NSPopUpButtonSepc: QuickSpec { } it("should emit selected index changes") { - var values = [Int]() + var values = [Int?]() button.reactive.selectedIndexes.observeValues { values.append($0) } - + button.menu?.performActionForItem(at: 1) button.menu?.performActionForItem(at: 99) - - expect(values) == [1, 99] + button.menu?.performActionForItem(at: -1) +// TODO: find out how to invoke nil as user action + + expect(values[0]) == 1 + expect(values[1]) == 99 } it("should emit selected title changes") { - var values = [String]() + var values = [String?]() button.reactive.selectedTitles.observeValues { values.append($0) } - + button.menu?.performActionForItem(at: 1) button.menu?.performActionForItem(at: 99) + //TODO: find out how to invoke nil as user action - expect(values) == ["1", "99"] + expect(values[0]) == "1" + expect(values[1]) == "99" } it("should accept changes from its bindings to its index values") { - let (signal, observer) = Signal.pipe() + let (signal, observer) = Signal.pipe() button.reactive.selectedIndex <~ SignalProducer(signal: signal) observer.send(value: 1) - expect(button.selectedItem?.title) == "1" + expect(button.indexOfSelectedItem) == 1 observer.send(value: 99) - expect(button.selectedItem?.title) == "99" + expect(button.indexOfSelectedItem) == 99 + + observer.send(value: nil) + expect(button.indexOfSelectedItem) == -1 + expect(button.selectedItem?.title).to(beNil()) } it("should accept changes from its bindings to its title values") { - let (signal, observer) = Signal.pipe() + let (signal, observer) = Signal.pipe() button.reactive.selectedTitle <~ SignalProducer(signal: signal) observer.send(value: "1") @@ -69,6 +78,10 @@ final class NSPopUpButtonSepc: QuickSpec { observer.send(value: "99") expect(button.selectedItem?.title) == "99" + + observer.send(value: nil) + expect(button.selectedItem?.title).to(beNil()) + expect(button.indexOfSelectedItem) == -1 } } } From a581574e87fd45f87b9de9a865ccb66ca1a49359 Mon Sep 17 00:00:00 2001 From: Seil Oh Date: Fri, 16 Dec 2016 10:58:29 +0900 Subject: [PATCH 0661/1028] revert emitting signals to non-optional --- ReactiveCocoa/AppKit/NSPopUpButton.swift | 10 +++++----- ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift | 13 ++++--------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/ReactiveCocoa/AppKit/NSPopUpButton.swift b/ReactiveCocoa/AppKit/NSPopUpButton.swift index d71affbd00..041b0e8b61 100644 --- a/ReactiveCocoa/AppKit/NSPopUpButton.swift +++ b/ReactiveCocoa/AppKit/NSPopUpButton.swift @@ -5,10 +5,9 @@ import enum Result.NoError extension Reactive where Base: NSPopUpButton { /// A signal of selected indexes - public var selectedIndexes: Signal { - return self.objectValues - .map { [unowned base = self.base] _ -> Int? in - guard base.indexOfSelectedItem != -1 else { return nil } + public var selectedIndexes: Signal { + return self.integerValues + .map { [unowned base = self.base] _ -> Int in return base.indexOfSelectedItem } } @@ -21,11 +20,12 @@ extension Reactive where Base: NSPopUpButton { } /// A signal of selected title - public var selectedTitles: Signal { + public var selectedTitles: Signal { return self.objectValues .map { [unowned base = self.base] _ -> String? in return base.titleOfSelectedItem } + .skipNil() } /// Sets the button with title. diff --git a/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift b/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift index 0e5731e830..087a6fcca1 100644 --- a/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift @@ -30,28 +30,23 @@ final class NSPopUpButtonSepc: QuickSpec { } it("should emit selected index changes") { - var values = [Int?]() + var values = [Int]() button.reactive.selectedIndexes.observeValues { values.append($0) } button.menu?.performActionForItem(at: 1) button.menu?.performActionForItem(at: 99) - button.menu?.performActionForItem(at: -1) -// TODO: find out how to invoke nil as user action - expect(values[0]) == 1 - expect(values[1]) == 99 + expect(values) == [1, 99] } it("should emit selected title changes") { - var values = [String?]() + var values = [String]() button.reactive.selectedTitles.observeValues { values.append($0) } button.menu?.performActionForItem(at: 1) button.menu?.performActionForItem(at: 99) - //TODO: find out how to invoke nil as user action - expect(values[0]) == "1" - expect(values[1]) == "99" + expect(values) == ["1", "99"] } it("should accept changes from its bindings to its index values") { From d32003e5a326a0f6356bd63708593558fb42d462 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Fri, 16 Dec 2016 04:58:33 +0100 Subject: [PATCH 0662/1028] Release Roadmap --- README.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/README.md b/README.md index 31f3b07b02..e166984ad1 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ 🎉 [Migrating from RAC 4.x?][CHANGELOG] +🚄 [Release Roadmap](#release-roadmap) + ## What is ReactiveSwift? __ReactiveSwift__ offers composable, declarative and flexible primitives that are built around the grand concept of ___streams of values over time___. These primitives can be used to uniformly represent common Cocoa and generic programming patterns that are fundamentally an act of observation. @@ -124,6 +126,32 @@ pod 'ReactiveCocoa', '5.0.0-alpha.3' ## Have a question? If you need any help, please visit our [GitHub issues][] or [Stack Overflow][]. Feel free to file an issue if you do not manage to find any solution from the archives. +## Release Roadmap +**Current Stable Release:**
    [![GitHub release](https://img.shields.io/github/release/ReactiveCocoa/ReactiveCocoa.svg)](https://github.com/ReactiveCocoa/ReactiveCocoa/releases) + +### In Development: ReactiveCocoa 5.0 +It targets Swift 3.0.x and ReactiveSwift 1.0. The tentative schedule of a Gold Master release is January 2017. + +A Release Candidate would be released after an important bug fix is cleared, which should happen no later than Christmas 2016. + +### Plan of Record +#### ReactiveCocoa 6.0 +It targets Swift 3.1.x and ReactiveSwift 2.0. The estimated schedule is Spring 2017. + +Since ReactiveSwift 2.0 would contain breaking changes, the public API of ReactiveCocoa is considered breaking too. + +As resilience would be enforced in Swift 4.0, it is important for us to have a clean and steady API to start with. The expectation is to **have the API reviewing to be concluded in ReactiveCocoa 6.0**, before we move on to ReactiveSwift 3.0 and Swift 4.0. Any contribution to help realising this goal is welcomed. + +#### ReactiveCocoa 7.0 +It targets Swift 4.0.x and ReactiveSwift 3.0. The estimated schedule is late 2017. + +The release may contain breaking changes due to changes in ReactiveSwift 3.0. + +ReactiveCocoa 7.0 would focus on three main goals: + +1. Swift 4.0 Resilience +1. Migration to ReactiveSwift 3.0 +1. Support new features introduced to AppKit, UIKit and Swift. [ReactiveSwift]: https://github.com/ReactiveCocoa/ReactiveSwift [ReactiveObjC]: https://github.com/ReactiveCocoa/ReactiveObjC From 938050692510ecd2634c03782f9be6eb54733188 Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 16 Dec 2016 05:11:08 +0100 Subject: [PATCH 0663/1028] Update ReactiveSwift to 1.0.0-rc.1. --- Cartfile | 2 +- Cartfile.resolved | 4 ++-- Carthage/Checkouts/ReactiveSwift | 2 +- Carthage/Checkouts/Result | 2 +- ReactiveCocoa.podspec | 2 +- ReactiveCocoa/UIKit/UIBarButtonItem.swift | 4 ++-- ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift | 2 +- .../UIKit/UIActivityIndicatorViewSpec.swift | 2 +- ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift | 6 +++--- ReactiveCocoaTests/UIKit/UIButtonSpec.swift | 4 ++-- ReactiveCocoaTests/UIKit/UIControlSpec.swift | 10 +++++----- ReactiveCocoaTests/UIKit/UIImageViewSpec.swift | 4 ++-- ReactiveCocoaTests/UIKit/UILabelSpec.swift | 6 +++--- ReactiveCocoaTests/UIKit/UIProgressViewSpec.swift | 2 +- ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift | 6 +++--- ReactiveCocoaTests/UIKit/UISearchBarSpec.swift | 2 +- ReactiveCocoaTests/UIKit/UISegmentedControlSpec.swift | 2 +- ReactiveCocoaTests/UIKit/UISwitchSpec.swift | 9 ++------- ReactiveCocoaTests/UIKit/UITextFieldSpec.swift | 2 +- ReactiveCocoaTests/UIKit/UITextViewSpec.swift | 2 +- ReactiveCocoaTests/UIKit/UIViewSpec.swift | 8 ++++---- 21 files changed, 39 insertions(+), 44 deletions(-) diff --git a/Cartfile b/Cartfile index fa2cd36b20..fa03f76dc1 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.4" +github "ReactiveCocoa/ReactiveSwift" "1.0.0-rc.1" diff --git a/Cartfile.resolved b/Cartfile.resolved index 974213398c..4d3f2e0521 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,5 +1,5 @@ github "Quick/Nimble" "v5.1.1" github "Quick/Quick" "v1.0.0" -github "antitypical/Result" "3.0.0" +github "antitypical/Result" "3.1.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" -github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.4" +github "ReactiveCocoa/ReactiveSwift" "1.0.0-rc.1" diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index bf9126ac05..3495369ccf 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit bf9126ac056d2532ccacd4f648fcdff15f9579e1 +Subproject commit 3495369ccfd16fdeaf028e61ccffe6fc3b87931b diff --git a/Carthage/Checkouts/Result b/Carthage/Checkouts/Result index df233a8d23..2dd1a0c7bc 160000 --- a/Carthage/Checkouts/Result +++ b/Carthage/Checkouts/Result @@ -1 +1 @@ -Subproject commit df233a8d23a545153d5b5de13b1ac8db2503f088 +Subproject commit 2dd1a0c7bca4cc44082f72020fe8b14491056f88 diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 9a408d8c14..f47b644caf 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -23,5 +23,5 @@ Pod::Spec.new do |s| s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_map = "ReactiveCocoa/module.modulemap" - s.dependency 'ReactiveSwift', '~> 1.0.0-alpha.4' + s.dependency 'ReactiveSwift', '~> 1.0.0-rc.1' end diff --git a/ReactiveCocoa/UIKit/UIBarButtonItem.swift b/ReactiveCocoa/UIKit/UIBarButtonItem.swift index 04e199ae75..57960651fb 100644 --- a/ReactiveCocoa/UIKit/UIBarButtonItem.swift +++ b/ReactiveCocoa/UIKit/UIBarButtonItem.swift @@ -3,7 +3,7 @@ import UIKit extension Reactive where Base: UIBarButtonItem { /// The current associated action of `self`. - private var associatedAction: Atomic<(action: CocoaAction, disposable: Disposable)?> { + private var associatedAction: Atomic<(action: CocoaAction, disposable: Disposable?)?> { return associatedValue { _ in Atomic(nil) } } @@ -23,7 +23,7 @@ extension Reactive where Base: UIBarButtonItem { let disposable = isEnabled <~ action.isEnabled return (action, disposable) })? - .disposable.dispose() + .disposable?.dispose() } } } diff --git a/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift b/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift index 4a9c856407..ae485851f8 100644 --- a/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift +++ b/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift @@ -25,7 +25,7 @@ extension Reactive where Base: UIRefreshControl { } nonmutating set { - let disposable = newValue.map { isRefreshing <~ $0.isExecuting } + let disposable = newValue.flatMap { isRefreshing <~ $0.isExecuting } setAction(newValue, for: .valueChanged, disposable: disposable) } } diff --git a/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewSpec.swift b/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewSpec.swift index 187089a593..5ceea45bef 100644 --- a/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewSpec.swift @@ -21,7 +21,7 @@ class UIActivityIndicatorSpec: QuickSpec { it("should accept changes from bindings to its animating state") { let (pipeSignal, observer) = Signal.pipe() - activityIndicatorView.reactive.isAnimating <~ SignalProducer(signal: pipeSignal) + activityIndicatorView.reactive.isAnimating <~ SignalProducer(pipeSignal) observer.send(value: true) expect(activityIndicatorView.isAnimating) == true diff --git a/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift b/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift index db79250ea9..32fc762f8c 100644 --- a/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift @@ -27,7 +27,7 @@ class UIBarButtonItemSpec: QuickSpec { it("should accept changes from bindings to its enabling state") { let (pipeSignal, observer) = Signal.pipe() - barButtonItem.reactive.isEnabled <~ SignalProducer(signal: pipeSignal) + barButtonItem.reactive.isEnabled <~ SignalProducer(pipeSignal) observer.send(value: false) expect(barButtonItem.isEnabled) == false @@ -38,7 +38,7 @@ class UIBarButtonItemSpec: QuickSpec { it("should accept changes from bindings to its title") { let (pipeSignal, observer) = Signal.pipe() - barButtonItem.reactive.title <~ SignalProducer(signal: pipeSignal) + barButtonItem.reactive.title <~ SignalProducer(pipeSignal) observer.send(value: "title") expect(barButtonItem.title) == "title" @@ -49,7 +49,7 @@ class UIBarButtonItemSpec: QuickSpec { it("should accept changes from bindings to its image") { let (pipeSignal, observer) = Signal.pipe() - barButtonItem.reactive.image <~ SignalProducer(signal: pipeSignal) + barButtonItem.reactive.image <~ SignalProducer(pipeSignal) let image = UIImage() expect(image).notTo(beNil()) diff --git a/ReactiveCocoaTests/UIKit/UIButtonSpec.swift b/ReactiveCocoaTests/UIKit/UIButtonSpec.swift index 5f4c571c11..a51b58354a 100644 --- a/ReactiveCocoaTests/UIKit/UIButtonSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIButtonSpec.swift @@ -25,7 +25,7 @@ class UIButtonSpec: QuickSpec { let secondTitle = "Second title" let (pipeSignal, observer) = Signal.pipe() - button.reactive.title <~ SignalProducer(signal: pipeSignal) + button.reactive.title <~ SignalProducer(pipeSignal) button.setTitle("", for: .selected) button.setTitle("", for: .highlighted) @@ -49,7 +49,7 @@ class UIButtonSpec: QuickSpec { SignalProducer(value: true) } - pressed <~ SignalProducer(signal: action.values) + pressed <~ SignalProducer(action.values) button.reactive.pressed = CocoaAction(action) expect(pressed.value) == false diff --git a/ReactiveCocoaTests/UIKit/UIControlSpec.swift b/ReactiveCocoaTests/UIKit/UIControlSpec.swift index 7a5ad0a107..353e45a893 100644 --- a/ReactiveCocoaTests/UIKit/UIControlSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIControlSpec.swift @@ -23,7 +23,7 @@ class UIControlSpec: QuickSpec { control.isEnabled = false let (pipeSignal, observer) = Signal.pipe() - control.reactive.isEnabled <~ SignalProducer(signal: pipeSignal) + control.reactive.isEnabled <~ SignalProducer(pipeSignal) observer.send(value: true) expect(control.isEnabled) == true @@ -36,7 +36,7 @@ class UIControlSpec: QuickSpec { control.isSelected = false let (pipeSignal, observer) = Signal.pipe() - control.reactive.isSelected <~ SignalProducer(signal: pipeSignal) + control.reactive.isSelected <~ SignalProducer(pipeSignal) observer.send(value: true) expect(control.isSelected) == true @@ -49,7 +49,7 @@ class UIControlSpec: QuickSpec { control.isHighlighted = false let (pipeSignal, observer) = Signal.pipe() - control.reactive.isHighlighted <~ SignalProducer(signal: pipeSignal) + control.reactive.isHighlighted <~ SignalProducer(pipeSignal) observer.send(value: true) expect(control.isHighlighted) == true @@ -64,8 +64,8 @@ class UIControlSpec: QuickSpec { let (pipeSignalSelected, observerSelected) = Signal.pipe() let (pipeSignalEnabled, observerEnabled) = Signal.pipe() - control.reactive.isSelected <~ SignalProducer(signal: pipeSignalSelected) - control.reactive.isEnabled <~ SignalProducer(signal: pipeSignalEnabled) + control.reactive.isSelected <~ SignalProducer(pipeSignalSelected) + control.reactive.isEnabled <~ SignalProducer(pipeSignalEnabled) observerSelected.send(value: true) observerEnabled.send(value: true) diff --git a/ReactiveCocoaTests/UIKit/UIImageViewSpec.swift b/ReactiveCocoaTests/UIKit/UIImageViewSpec.swift index 03df3c4c26..c311572afa 100644 --- a/ReactiveCocoaTests/UIKit/UIImageViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIImageViewSpec.swift @@ -25,7 +25,7 @@ class UIImageViewSpec: QuickSpec { let secondChange = UIImage() let (pipeSignal, observer) = Signal.pipe() - imageView.reactive.image <~ SignalProducer(signal: pipeSignal) + imageView.reactive.image <~ SignalProducer(pipeSignal) observer.send(value: firstChange) expect(imageView.image) == firstChange @@ -39,7 +39,7 @@ class UIImageViewSpec: QuickSpec { let secondChange = UIImage() let (pipeSignal, observer) = Signal.pipe() - imageView.reactive.highlightedImage <~ SignalProducer(signal: pipeSignal) + imageView.reactive.highlightedImage <~ SignalProducer(pipeSignal) observer.send(value: firstChange) expect(imageView.highlightedImage) == firstChange diff --git a/ReactiveCocoaTests/UIKit/UILabelSpec.swift b/ReactiveCocoaTests/UIKit/UILabelSpec.swift index 382c0d7e7f..374549783b 100644 --- a/ReactiveCocoaTests/UIKit/UILabelSpec.swift +++ b/ReactiveCocoaTests/UIKit/UILabelSpec.swift @@ -27,7 +27,7 @@ class UILabelSpec: QuickSpec { label.text = "" let (pipeSignal, observer) = Signal.pipe() - label.reactive.text <~ SignalProducer(signal: pipeSignal) + label.reactive.text <~ SignalProducer(pipeSignal) observer.send(value: firstChange) expect(label.text) == firstChange @@ -46,7 +46,7 @@ class UILabelSpec: QuickSpec { label.attributedText = NSAttributedString(string: "") let (pipeSignal, observer) = Signal.pipe() - label.reactive.attributedText <~ SignalProducer(signal: pipeSignal) + label.reactive.attributedText <~ SignalProducer(pipeSignal) observer.send(value: firstChange) expect(label.attributedText) == firstChange @@ -63,7 +63,7 @@ class UILabelSpec: QuickSpec { let (pipeSignal, observer) = Signal.pipe() label.textColor = UIColor.black - label.reactive.textColor <~ SignalProducer(signal: pipeSignal) + label.reactive.textColor <~ SignalProducer(pipeSignal) observer.send(value: firstChange) expect(label.textColor) == firstChange diff --git a/ReactiveCocoaTests/UIKit/UIProgressViewSpec.swift b/ReactiveCocoaTests/UIKit/UIProgressViewSpec.swift index 040f5cf94e..93f2d25765 100644 --- a/ReactiveCocoaTests/UIKit/UIProgressViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIProgressViewSpec.swift @@ -27,7 +27,7 @@ class UIProgressViewSpec: QuickSpec { progressView.progress = 1.0 let (pipeSignal, observer) = Signal.pipe() - progressView.reactive.progress <~ SignalProducer(signal: pipeSignal) + progressView.reactive.progress <~ SignalProducer(pipeSignal) observer.send(value: firstChange) expect(progressView.progress) ≈ firstChange diff --git a/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift b/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift index 6d01c21123..35ae44d639 100644 --- a/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift @@ -21,7 +21,7 @@ class UIRefreshControlSpec: QuickSpec { it("should accept changes from bindings to its refreshing state") { let (pipeSignal, observer) = Signal.pipe() - refreshControl.reactive.isRefreshing <~ SignalProducer(signal: pipeSignal) + refreshControl.reactive.isRefreshing <~ SignalProducer(pipeSignal) observer.send(value: true) expect(refreshControl.isRefreshing) == true @@ -32,7 +32,7 @@ class UIRefreshControlSpec: QuickSpec { it("should accept changes from bindings to its attributed title state") { let (pipeSignal, observer) = Signal.pipe() - refreshControl.reactive.attributedTitle <~ SignalProducer(signal: pipeSignal) + refreshControl.reactive.attributedTitle <~ SignalProducer(pipeSignal) let string = NSAttributedString(string: "test") @@ -55,7 +55,7 @@ class UIRefreshControlSpec: QuickSpec { SignalProducer(value: true) } - refreshed <~ SignalProducer(signal: action.values) + refreshed <~ SignalProducer(action.values) refreshControl.reactive.refresh = CocoaAction(action) expect(refreshed.value) == false diff --git a/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift b/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift index 7824849f5c..9dcade1e3c 100644 --- a/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift +++ b/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift @@ -32,7 +32,7 @@ class UISearchBarSpec: QuickSpec { searchBar.text = "" let (pipeSignal, observer) = Signal.pipe() - searchBar.reactive.text <~ SignalProducer(signal: pipeSignal) + searchBar.reactive.text <~ SignalProducer(pipeSignal) observer.send(value: firstChange) expect(searchBar.text) == firstChange diff --git a/ReactiveCocoaTests/UIKit/UISegmentedControlSpec.swift b/ReactiveCocoaTests/UIKit/UISegmentedControlSpec.swift index fad7680ed7..a56331ce5d 100644 --- a/ReactiveCocoaTests/UIKit/UISegmentedControlSpec.swift +++ b/ReactiveCocoaTests/UIKit/UISegmentedControlSpec.swift @@ -12,7 +12,7 @@ class UISegmentedControlSpec: QuickSpec { expect(s.numberOfSegments) == 3 let (pipeSignal, observer) = Signal.pipe() - s.reactive.selectedSegmentIndex <~ SignalProducer(signal: pipeSignal) + s.reactive.selectedSegmentIndex <~ SignalProducer(pipeSignal) expect(s.selectedSegmentIndex) == UISegmentedControlNoSegment diff --git a/ReactiveCocoaTests/UIKit/UISwitchSpec.swift b/ReactiveCocoaTests/UIKit/UISwitchSpec.swift index 7534ea98a8..d216660591 100644 --- a/ReactiveCocoaTests/UIKit/UISwitchSpec.swift +++ b/ReactiveCocoaTests/UIKit/UISwitchSpec.swift @@ -16,19 +16,14 @@ class UISwitchSpec: QuickSpec { afterEach { toggle = nil - - // Disabled due to an issue of the iOS SDK. - // Please refer to https://github.com/ReactiveCocoa/ReactiveCocoa/issues/3251 - // for more information. - // - // expect(_toggle).to(beNil()) + expect(_toggle).to(beNil()) } it("should accept changes from bindings to its `isOn` state") { toggle.isOn = false let (pipeSignal, observer) = Signal.pipe() - toggle.reactive.isOn <~ SignalProducer(signal: pipeSignal) + toggle.reactive.isOn <~ SignalProducer(pipeSignal) observer.send(value: true) expect(toggle.isOn) == true diff --git a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift index ca10321c01..01b504a5e0 100644 --- a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift @@ -55,7 +55,7 @@ class UITextFieldSpec: QuickSpec { textField.attributedText = NSAttributedString(string: "") let (pipeSignal, observer) = Signal.pipe() - textField.reactive.attributedText <~ SignalProducer(signal: pipeSignal) + textField.reactive.attributedText <~ SignalProducer(pipeSignal) observer.send(value: firstChange) expect(textField.attributedText?.string) == firstChange.string diff --git a/ReactiveCocoaTests/UIKit/UITextViewSpec.swift b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift index a1c3e27c3f..413f0464f2 100644 --- a/ReactiveCocoaTests/UIKit/UITextViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift @@ -59,7 +59,7 @@ class UITextViewSpec: QuickSpec { textView.attributedText = NSAttributedString(string: "") let (pipeSignal, observer) = Signal.pipe() - textView.reactive.attributedText <~ SignalProducer(signal: pipeSignal) + textView.reactive.attributedText <~ SignalProducer(pipeSignal) observer.send(value: firstChange) expect(textView.attributedText) == firstChange diff --git a/ReactiveCocoaTests/UIKit/UIViewSpec.swift b/ReactiveCocoaTests/UIKit/UIViewSpec.swift index 20d876646a..e42bdd57f3 100644 --- a/ReactiveCocoaTests/UIKit/UIViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIViewSpec.swift @@ -24,7 +24,7 @@ class UIViewSpec: QuickSpec { view.isHidden = true let (pipeSignal, observer) = Signal.pipe() - view.reactive.isHidden <~ SignalProducer(signal: pipeSignal) + view.reactive.isHidden <~ SignalProducer(pipeSignal) observer.send(value: true) expect(view.isHidden) == true @@ -40,7 +40,7 @@ class UIViewSpec: QuickSpec { let secondChange = CGFloat(0.7) let (pipeSignal, observer) = Signal.pipe() - view.reactive.alpha <~ SignalProducer(signal: pipeSignal) + view.reactive.alpha <~ SignalProducer(pipeSignal) observer.send(value: firstChange) expect(view.alpha) ≈ firstChange @@ -53,7 +53,7 @@ class UIViewSpec: QuickSpec { view.isUserInteractionEnabled = true let (pipeSignal, observer) = Signal.pipe() - view.reactive.isUserInteractionEnabled <~ SignalProducer(signal: pipeSignal) + view.reactive.isUserInteractionEnabled <~ SignalProducer(pipeSignal) observer.send(value: true) expect(view.isUserInteractionEnabled) == true @@ -66,7 +66,7 @@ class UIViewSpec: QuickSpec { view.backgroundColor = .white let (pipeSignal, observer) = Signal.pipe() - view.reactive.backgroundColor <~ SignalProducer(signal: pipeSignal) + view.reactive.backgroundColor <~ SignalProducer(pipeSignal) observer.send(value: .yellow) expect(view.backgroundColor) == .yellow From d847c493b7665c8a026851278da274ca4f577aec Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 16 Dec 2016 05:45:51 +0100 Subject: [PATCH 0664/1028] Force CI to use iOS 10.2 Simulator. --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5d7a01b810..b557ab6ea6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: objective-c -osx_image: xcode8.1 +osx_image: xcode8.2 before_install: true install: true git: @@ -25,7 +25,8 @@ matrix: env: - XCODE_SDK=iphonesimulator - XCODE_ACTION="build-for-testing test-without-building" - - XCODE_DESTINATION="platform=iOS Simulator,name=iPhone 6s" + - XCODE_DESTINATION="platform=iOS Simulator,id=31E6604A-19AA-4B68-B560-C33C584BC80D" # iPhone 6s, iOS 10.2 +# - XCODE_DESTINATION="platform=iOS Simulator,name=iPhone 6s,OS=10.2" # - XCODE_PLAYGROUND_TARGET="x86_64-apple-ios9.3" # - PLAYGROUND="ReactiveCocoa-iOS.playground" - xcode_scheme: ReactiveCocoa-tvOS From 69d4ca9fde4350cdbc22761fa10c5bea7876b5b2 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Fri, 16 Dec 2016 04:34:48 +0100 Subject: [PATCH 0665/1028] Public `NSObject.makeBindingTarget`. --- ReactiveCocoa/NSObject+BindingTarget.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa/NSObject+BindingTarget.swift b/ReactiveCocoa/NSObject+BindingTarget.swift index 12b018c8f1..8e654194f7 100644 --- a/ReactiveCocoa/NSObject+BindingTarget.swift +++ b/ReactiveCocoa/NSObject+BindingTarget.swift @@ -13,7 +13,7 @@ extension Reactive where Base: NSObject { /// /// - returns: /// A binding target that holds no strong references to the object. - internal func makeBindingTarget(_ key: StaticString = #function, action: @escaping (Base, U) -> Void) -> BindingTarget { + public func makeBindingTarget(_ key: StaticString = #function, action: @escaping (Base, U) -> Void) -> BindingTarget { return BindingTarget(on: UIScheduler(), lifetime: lifetime) { [weak base = self.base] value in if let base = base { action(base, value) From 4c3f0a09c60399f3e531bdda442fb3944ea2c809 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Fri, 16 Dec 2016 04:39:54 +0100 Subject: [PATCH 0666/1028] Remove the unused parameter in `makeBindingTarget`. --- ReactiveCocoa/NSObject+BindingTarget.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa/NSObject+BindingTarget.swift b/ReactiveCocoa/NSObject+BindingTarget.swift index 8e654194f7..beb774d539 100644 --- a/ReactiveCocoa/NSObject+BindingTarget.swift +++ b/ReactiveCocoa/NSObject+BindingTarget.swift @@ -13,7 +13,7 @@ extension Reactive where Base: NSObject { /// /// - returns: /// A binding target that holds no strong references to the object. - public func makeBindingTarget(_ key: StaticString = #function, action: @escaping (Base, U) -> Void) -> BindingTarget { + public func makeBindingTarget(_ action: @escaping (Base, U) -> Void) -> BindingTarget { return BindingTarget(on: UIScheduler(), lifetime: lifetime) { [weak base = self.base] value in if let base = base { action(base, value) From d99c9853c523cc1b8d55d463d30e81ed4bf1dd5f Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 16 Dec 2016 05:42:05 +0100 Subject: [PATCH 0667/1028] Allow any arbitrary scheduler in `makeBindingTarget`. --- ReactiveCocoa/NSObject+BindingTarget.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ReactiveCocoa/NSObject+BindingTarget.swift b/ReactiveCocoa/NSObject+BindingTarget.swift index beb774d539..fdd89d50a1 100644 --- a/ReactiveCocoa/NSObject+BindingTarget.swift +++ b/ReactiveCocoa/NSObject+BindingTarget.swift @@ -6,15 +6,15 @@ extension Reactive where Base: NSObject { /// references the object so that the supplied `action` is triggered only if /// the object has not deinitialized. /// - /// - important: The binding target is bound to the main queue. - /// /// - parameters: + /// - scheduler: An optional scheduler that the binding target uses. If it + /// is not specified, a UI scheduler would be used. /// - action: The action to consume values from the bindings. /// /// - returns: /// A binding target that holds no strong references to the object. - public func makeBindingTarget(_ action: @escaping (Base, U) -> Void) -> BindingTarget { - return BindingTarget(on: UIScheduler(), lifetime: lifetime) { [weak base = self.base] value in + public func makeBindingTarget(on scheduler: SchedulerProtocol = UIScheduler(), _ action: @escaping (Base, U) -> Void) -> BindingTarget { + return BindingTarget(on: scheduler, lifetime: lifetime) { [weak base = self.base] value in if let base = base { action(base, value) } From 9ddfa96d9bb3a110d6458d1920562a3fb4a0b8a6 Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 16 Dec 2016 05:29:16 +0100 Subject: [PATCH 0668/1028] Bound the properties of `CocoaAction` to the main thread. --- ReactiveCocoa/CocoaAction.swift | 20 ++++++++--------- ReactiveCocoaTests/CocoaActionSpec.swift | 28 ++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/ReactiveCocoa/CocoaAction.swift b/ReactiveCocoa/CocoaAction.swift index 6d649b6c59..067292bb70 100644 --- a/ReactiveCocoa/CocoaAction.swift +++ b/ReactiveCocoa/CocoaAction.swift @@ -2,8 +2,8 @@ import Foundation import ReactiveSwift import enum Result.NoError -/// CocoaAction wraps an Action for use by a GUI control (such as `NSControl` or -/// `UIControl`), with KVO, or with Cocoa Bindings. +/// CocoaAction wraps an Action for use by a UI control (such as `NSControl` or +/// `UIControl`). public final class CocoaAction: NSObject { /// The selector for message senders. public static var selector: Selector { @@ -12,14 +12,12 @@ public final class CocoaAction: NSObject { /// Whether the action is enabled. /// - /// This property will only change on the main thread, and will generate a - /// KVO notification for every change. + /// This property will only change on the main thread. public let isEnabled: Property /// Whether the action is executing. /// - /// This property will only change on the main thread, and will generate a - /// KVO notification for every change. + /// This property will only change on the main thread. public let isExecuting: Property private let _execute: (Sender) -> Void @@ -37,9 +35,11 @@ public final class CocoaAction: NSObject { producer.start() } - isEnabled = action.isEnabled - isExecuting = action.isExecuting - + isEnabled = Property(initial: action.isEnabled.value, + then: action.isEnabled.producer.observe(on: UIScheduler())) + isExecuting = Property(initial: action.isExecuting.value, + then: action.isExecuting.producer.observe(on: UIScheduler())) + super.init() } @@ -66,7 +66,7 @@ public final class CocoaAction: NSObject { /// /// - parameters: /// - sender: The sender which initiates the attempt. - @IBAction public func execute(_ sender: Any?) { + @IBAction public func execute(_ sender: Any) { _execute(sender as! Sender) } } diff --git a/ReactiveCocoaTests/CocoaActionSpec.swift b/ReactiveCocoaTests/CocoaActionSpec.swift index 85eb5322e6..7ac03f9dfd 100644 --- a/ReactiveCocoaTests/CocoaActionSpec.swift +++ b/ReactiveCocoaTests/CocoaActionSpec.swift @@ -66,6 +66,34 @@ class CocoaActionSpec: QuickSpec { _ = cocoaAction } + it("should emit `isExecuting` changes only on the main thread") { + var counter = 0 + + cocoaAction.isExecuting.producer + .startWithValues { _ in + counter += Thread.current.isMainThread ? 1 : 0 + } + + expect(counter) == 1 + + action.apply(0).start() + expect(counter) == 3 + } + + it("should emit `isEnabled` changes only on the main thread") { + var counter = 0 + + cocoaAction.isEnabled.producer + .startWithValues { _ in + counter += Thread.current.isMainThread ? 1 : 0 + } + + expect(counter) == 1 + + action.apply(0).start() + expect(counter) == 3 + } + context("lifetime") { it("CocoaAction should not create a retain cycle") { weak var weakAction = action From 6e6bc2e25c578b374b61f2f19d3c3c49c2adb723 Mon Sep 17 00:00:00 2001 From: Anders Date: Sat, 17 Dec 2016 04:45:17 +0100 Subject: [PATCH 0669/1028] Fix NSPopUpButtonSpec for RAS 1.0.0-rc.1. --- ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift b/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift index 087a6fcca1..b0c2a0144e 100644 --- a/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift @@ -51,7 +51,7 @@ final class NSPopUpButtonSepc: QuickSpec { it("should accept changes from its bindings to its index values") { let (signal, observer) = Signal.pipe() - button.reactive.selectedIndex <~ SignalProducer(signal: signal) + button.reactive.selectedIndex <~ SignalProducer(signal) observer.send(value: 1) expect(button.indexOfSelectedItem) == 1 @@ -66,7 +66,7 @@ final class NSPopUpButtonSepc: QuickSpec { it("should accept changes from its bindings to its title values") { let (signal, observer) = Signal.pipe() - button.reactive.selectedTitle <~ SignalProducer(signal: signal) + button.reactive.selectedTitle <~ SignalProducer(signal) observer.send(value: "1") expect(button.selectedItem?.title) == "1" From 51e0ad52b871ceb791775ad61a3aafe96e9b6650 Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Wed, 30 Nov 2016 15:03:45 +0100 Subject: [PATCH 0670/1028] Add reactive.valueChanged action to UISwitch --- ReactiveCocoa/UIKit/iOS/UISwitch.swift | 15 +++++++++++++ ReactiveCocoaTests/UIKit/UISwitchSpec.swift | 25 +++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/ReactiveCocoa/UIKit/iOS/UISwitch.swift b/ReactiveCocoa/UIKit/iOS/UISwitch.swift index 8b26b67d88..d491f7ece6 100644 --- a/ReactiveCocoa/UIKit/iOS/UISwitch.swift +++ b/ReactiveCocoa/UIKit/iOS/UISwitch.swift @@ -3,6 +3,21 @@ import enum Result.NoError import UIKit extension Reactive where Base: UISwitch { + /// The action to be triggered when the switch is changed. It also controlls + /// the enabled state of the switch + public var valueChanged: CocoaAction? { + get { + return associatedAction.withValue { info in + return info.flatMap { info in + return info.controlEvents == .valueChanged ? info.action : nil + } + } + } + + nonmutating set { + setAction(newValue, for: .valueChanged) + } + } /// Sets the on-off state of the switch. public var isOn: BindingTarget { return makeBindingTarget { $0.isOn = $1 } diff --git a/ReactiveCocoaTests/UIKit/UISwitchSpec.swift b/ReactiveCocoaTests/UIKit/UISwitchSpec.swift index d216660591..14057b910c 100644 --- a/ReactiveCocoaTests/UIKit/UISwitchSpec.swift +++ b/ReactiveCocoaTests/UIKit/UISwitchSpec.swift @@ -40,5 +40,30 @@ class UISwitchSpec: QuickSpec { toggle.sendActions(for: .valueChanged) expect(latestValue!) == true } + + it("should execute the `valueChanged` actoun upon receiving a `valueChanged` action message.") { + toggle.isOn = false + toggle.isEnabled = true + toggle.isUserInteractionEnabled = true + + let isOn = MutableProperty(false) + let action = Action { isOn in + return SignalProducer(value: isOn) + } + isOn <~ SignalProducer(signal: action.values) + + toggle.reactive.valueChanged = CocoaAction(action) { return $0.isOn } + + expect(isOn.value) == false + + toggle.isOn = true + toggle.sendActions(for: .valueChanged) + expect(isOn.value) == true + + toggle.isOn = false + toggle.sendActions(for: .valueChanged) + expect(isOn.value) == false + + } } } From 2ed0c9090ef2fbb65a5ff6b4490f7f8094bf22b0 Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Thu, 1 Dec 2016 10:05:14 +0100 Subject: [PATCH 0671/1028] Change name of UISwitch action to `toggled` --- ReactiveCocoa/UIKit/iOS/UISwitch.swift | 4 ++-- ReactiveCocoaTests/UIKit/UISwitchSpec.swift | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ReactiveCocoa/UIKit/iOS/UISwitch.swift b/ReactiveCocoa/UIKit/iOS/UISwitch.swift index d491f7ece6..dec9880a4b 100644 --- a/ReactiveCocoa/UIKit/iOS/UISwitch.swift +++ b/ReactiveCocoa/UIKit/iOS/UISwitch.swift @@ -3,9 +3,9 @@ import enum Result.NoError import UIKit extension Reactive where Base: UISwitch { - /// The action to be triggered when the switch is changed. It also controlls + /// The action to be triggered when the switch is changed. It also controls /// the enabled state of the switch - public var valueChanged: CocoaAction? { + public var toggled: CocoaAction? { get { return associatedAction.withValue { info in return info.flatMap { info in diff --git a/ReactiveCocoaTests/UIKit/UISwitchSpec.swift b/ReactiveCocoaTests/UIKit/UISwitchSpec.swift index 14057b910c..08d33aab8a 100644 --- a/ReactiveCocoaTests/UIKit/UISwitchSpec.swift +++ b/ReactiveCocoaTests/UIKit/UISwitchSpec.swift @@ -41,7 +41,7 @@ class UISwitchSpec: QuickSpec { expect(latestValue!) == true } - it("should execute the `valueChanged` actoun upon receiving a `valueChanged` action message.") { + it("should execute the `toggled` action upon receiving a `valueChanged` action message.") { toggle.isOn = false toggle.isEnabled = true toggle.isUserInteractionEnabled = true @@ -52,7 +52,7 @@ class UISwitchSpec: QuickSpec { } isOn <~ SignalProducer(signal: action.values) - toggle.reactive.valueChanged = CocoaAction(action) { return $0.isOn } + toggle.reactive.toggled = CocoaAction(action) { return $0.isOn } expect(isOn.value) == false From 2a435b10a902f508171987e973c548f4808902b9 Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Sat, 17 Dec 2016 11:47:55 +0100 Subject: [PATCH 0672/1028] Fix testcase for recent version of master --- ReactiveCocoaTests/UIKit/UISwitchSpec.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoaTests/UIKit/UISwitchSpec.swift b/ReactiveCocoaTests/UIKit/UISwitchSpec.swift index 08d33aab8a..b5dac2b9bf 100644 --- a/ReactiveCocoaTests/UIKit/UISwitchSpec.swift +++ b/ReactiveCocoaTests/UIKit/UISwitchSpec.swift @@ -50,7 +50,7 @@ class UISwitchSpec: QuickSpec { let action = Action { isOn in return SignalProducer(value: isOn) } - isOn <~ SignalProducer(signal: action.values) + isOn <~ SignalProducer(action.values) toggle.reactive.toggled = CocoaAction(action) { return $0.isOn } From e659fd453f506db3957efaac2502877ae98aba6f Mon Sep 17 00:00:00 2001 From: David Caunt Date: Sat, 17 Dec 2016 17:17:16 +0000 Subject: [PATCH 0673/1028] Update tests for ReactiveSwift 1.0 RC1 --- ReactiveCocoaTests/UIKit/UIScrollViewSpec.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ReactiveCocoaTests/UIKit/UIScrollViewSpec.swift b/ReactiveCocoaTests/UIKit/UIScrollViewSpec.swift index cffc31bc1c..745a15bae4 100644 --- a/ReactiveCocoaTests/UIKit/UIScrollViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIScrollViewSpec.swift @@ -30,7 +30,7 @@ class UIScrollViewSpec: QuickSpec { scrollView.contentInset = .zero let (pipeSignal, observer) = Signal.pipe() - scrollView.reactive.contentInset <~ SignalProducer(signal: pipeSignal) + scrollView.reactive.contentInset <~ SignalProducer(pipeSignal) observer.send(value: UIEdgeInsets(top: 1, left: 2, bottom: 3, right: 4)) expect(scrollView.contentInset) == UIEdgeInsets(top: 1, left: 2, bottom: 3, right: 4) @@ -43,7 +43,7 @@ class UIScrollViewSpec: QuickSpec { scrollView.scrollIndicatorInsets = .zero let (pipeSignal, observer) = Signal.pipe() - scrollView.reactive.scrollIndicatorInsets <~ SignalProducer(signal: pipeSignal) + scrollView.reactive.scrollIndicatorInsets <~ SignalProducer(pipeSignal) observer.send(value: UIEdgeInsets(top: 1, left: 2, bottom: 3, right: 4)) expect(scrollView.scrollIndicatorInsets) == UIEdgeInsets(top: 1, left: 2, bottom: 3, right: 4) @@ -56,7 +56,7 @@ class UIScrollViewSpec: QuickSpec { scrollView.isScrollEnabled = true let (pipeSignal, observer) = Signal.pipe() - scrollView.reactive.isScrollEnabled <~ SignalProducer(signal: pipeSignal) + scrollView.reactive.isScrollEnabled <~ SignalProducer(pipeSignal) observer.send(value: true) expect(scrollView.isScrollEnabled) == true @@ -76,7 +76,7 @@ class UIScrollViewSpec: QuickSpec { scrollView.zoomScale = 1 let (pipeSignal, observer) = Signal.pipe() - scrollView.reactive.zoomScale <~ SignalProducer(signal: pipeSignal) + scrollView.reactive.zoomScale <~ SignalProducer(pipeSignal) observer.send(value: 3) expect(scrollView.zoomScale) == 3 @@ -88,7 +88,7 @@ class UIScrollViewSpec: QuickSpec { scrollView.minimumZoomScale = 0 let (pipeSignal, observer) = Signal.pipe() - scrollView.reactive.minimumZoomScale <~ SignalProducer(signal: pipeSignal) + scrollView.reactive.minimumZoomScale <~ SignalProducer(pipeSignal) observer.send(value: 42) expect(scrollView.minimumZoomScale) == 42 @@ -100,7 +100,7 @@ class UIScrollViewSpec: QuickSpec { scrollView.maximumZoomScale = 0 let (pipeSignal, observer) = Signal.pipe() - scrollView.reactive.maximumZoomScale <~ SignalProducer(signal: pipeSignal) + scrollView.reactive.maximumZoomScale <~ SignalProducer(pipeSignal) observer.send(value: 42) expect(scrollView.maximumZoomScale) == 42 From 16e15c90e6dc250726de286db94884964a7f2226 Mon Sep 17 00:00:00 2001 From: Anders Date: Sun, 18 Dec 2016 14:25:46 +0100 Subject: [PATCH 0674/1028] 5.0.0-alpha.4 --- README.md | 4 ++-- ReactiveCocoa.podspec | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e166984ad1..24ec5ec83f 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ If you use [Carthage][] to manage your dependencies, simply add ReactiveCocoa to your `Cartfile`: ``` -github "ReactiveCocoa/ReactiveCocoa" "5.0.0-alpha.3" +github "ReactiveCocoa/ReactiveCocoa" "5.0.0-alpha.4" ``` If you use Carthage to build your dependencies, make sure you have added `ReactiveCocoa.framework`, `ReactiveSwift.framework`, and `Result.framework` to the "_Linked Frameworks and Libraries_" section of your target, and have included them in your Carthage framework copying build phase. @@ -105,7 +105,7 @@ If you use [CocoaPods][] to manage your dependencies, simply add ReactiveCocoa to your `Podfile`: ``` -pod 'ReactiveCocoa', '5.0.0-alpha.3' +pod 'ReactiveCocoa', '5.0.0-alpha.4' ``` #### Git submodule diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index f47b644caf..3ef5881cd9 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "5.0.0-alpha.3" + s.version = "5.0.0-alpha.4" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. @@ -23,5 +23,5 @@ Pod::Spec.new do |s| s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_map = "ReactiveCocoa/module.modulemap" - s.dependency 'ReactiveSwift', '~> 1.0.0-rc.1' + s.dependency 'ReactiveSwift', '1.0.0-rc.1' end From a216bc77faf47d92a9f519ee24e5b70e9cba97e6 Mon Sep 17 00:00:00 2001 From: Anders Date: Sun, 18 Dec 2016 21:36:27 +0100 Subject: [PATCH 0675/1028] 5.0.0-alpha.5 --- Cartfile | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- README.md | 4 ++-- ReactiveCocoa.podspec | 4 ++-- ReactiveCocoa/NSObject+KeyValueObserving.swift | 4 ++-- ReactiveCocoaTests/KeyValueObservingSpec.swift | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cartfile b/Cartfile index fa03f76dc1..6e6e16c6e0 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" "1.0.0-rc.1" +github "ReactiveCocoa/ReactiveSwift" "1.0.0-rc.2" diff --git a/Cartfile.resolved b/Cartfile.resolved index 4d3f2e0521..eddf8cc9dc 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -2,4 +2,4 @@ github "Quick/Nimble" "v5.1.1" github "Quick/Quick" "v1.0.0" github "antitypical/Result" "3.1.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" -github "ReactiveCocoa/ReactiveSwift" "1.0.0-rc.1" +github "ReactiveCocoa/ReactiveSwift" "1.0.0-rc.2" diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index 3495369ccf..df08a05210 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit 3495369ccfd16fdeaf028e61ccffe6fc3b87931b +Subproject commit df08a05210157143d7f06c87feb6f9ad3c7603d6 diff --git a/README.md b/README.md index 24ec5ec83f..6c1b184ebc 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ If you use [Carthage][] to manage your dependencies, simply add ReactiveCocoa to your `Cartfile`: ``` -github "ReactiveCocoa/ReactiveCocoa" "5.0.0-alpha.4" +github "ReactiveCocoa/ReactiveCocoa" "5.0.0-alpha.5" ``` If you use Carthage to build your dependencies, make sure you have added `ReactiveCocoa.framework`, `ReactiveSwift.framework`, and `Result.framework` to the "_Linked Frameworks and Libraries_" section of your target, and have included them in your Carthage framework copying build phase. @@ -105,7 +105,7 @@ If you use [CocoaPods][] to manage your dependencies, simply add ReactiveCocoa to your `Podfile`: ``` -pod 'ReactiveCocoa', '5.0.0-alpha.4' +pod 'ReactiveCocoa', '5.0.0-alpha.5' ``` #### Git submodule diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 3ef5881cd9..931e4f45fa 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "5.0.0-alpha.4" + s.version = "5.0.0-alpha.5" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. @@ -23,5 +23,5 @@ Pod::Spec.new do |s| s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_map = "ReactiveCocoa/module.modulemap" - s.dependency 'ReactiveSwift', '1.0.0-rc.1' + s.dependency 'ReactiveSwift', '1.0.0-rc.2' end diff --git a/ReactiveCocoa/NSObject+KeyValueObserving.swift b/ReactiveCocoa/NSObject+KeyValueObserving.swift index 911707afd0..47eee2cf5b 100644 --- a/ReactiveCocoa/NSObject+KeyValueObserving.swift +++ b/ReactiveCocoa/NSObject+KeyValueObserving.swift @@ -133,7 +133,7 @@ extension KeyValueObserver { } let headDisposable = CompositeDisposable() - headSerialDisposable.innerDisposable = headDisposable + headSerialDisposable.inner = headDisposable if shouldObserveDeinit { let disposable = value.reactive.lifetime.ended.observeCompleted { @@ -165,7 +165,7 @@ extension KeyValueObserver { let disposable = value.reactive.lifetime.ended.observeCompleted { action(nil) } - headSerialDisposable.innerDisposable = disposable + headSerialDisposable.inner = disposable } // Send the latest value of the key. diff --git a/ReactiveCocoaTests/KeyValueObservingSpec.swift b/ReactiveCocoaTests/KeyValueObservingSpec.swift index 93d48db4dc..b05eceaff9 100644 --- a/ReactiveCocoaTests/KeyValueObservingSpec.swift +++ b/ReactiveCocoaTests/KeyValueObservingSpec.swift @@ -285,7 +285,7 @@ class KeyValueObservingSpec: QuickSpec { .values(forKeyPath: #keyPath(ObservableObject.rac_value)) .startWithCompleted {} - serialDisposable.innerDisposable = disposable + serialDisposable.inner = disposable concurrentQueue.async { testObject.rac_value = index From f16d6c7c498b77cde52c54a8dff4ac41b4b1c5d8 Mon Sep 17 00:00:00 2001 From: Seil Oh Date: Wed, 21 Dec 2016 11:35:08 +0900 Subject: [PATCH 0676/1028] fixed typo --- ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift b/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift index b0c2a0144e..2f2546c526 100644 --- a/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift @@ -5,7 +5,7 @@ import ReactiveSwift import Result import AppKit -final class NSPopUpButtonSepc: QuickSpec { +final class NSPopUpButtonSpec: QuickSpec { override func spec() { describe("NSPopUpButton") { var button: NSPopUpButton! From 4f2769bb556e06b5bb7b9d7036a4ddad2cca5855 Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Wed, 21 Dec 2016 17:40:39 +0100 Subject: [PATCH 0677/1028] Mention the necessary import of ReactiveSwift --- CHANGELOG.md | 2 ++ Documentation/DebuggingTechniques.md | 4 ++++ README.md | 2 ++ 3 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e00fb1b89..1bea1b2fca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ In version 5.0, we split ReactiveCocoa into multiple repositories for reasons ex **If you’re using both the Swift and Objective-C APIs**, you likely require both ReactiveCocoa and [ReactiveObjCBridge][], which depend on [ReactiveSwift][] and [ReactiveObjC][]. +**Attention:** If youre using ReactiveCocoa, you'll most likely need to import ReactiveSwift as well when using classes or operators that are implemented in ReactiveSwift. + #### ReactiveCocoa The ReactiveCocoa library is newly focused on Swift and the UI layers of Apple’s platforms, building on the work of [Rex](https://github.com/neilpa/Rex). diff --git a/Documentation/DebuggingTechniques.md b/Documentation/DebuggingTechniques.md index ebe81dc230..a6bcd2d0ca 100644 --- a/Documentation/DebuggingTechniques.md +++ b/Documentation/DebuggingTechniques.md @@ -2,6 +2,10 @@ This document lists debugging techniques and infrastructure helpful for debugging ReactiveCocoa applications. +#### Use of unresolved operator '<~' or not found in RAC 5 + +Since the split into ReactiveCocoa and ReactiveSwift, you'll need to `import ReactiveSwift` as well when using classes or operators that are implemented in ReactiveSwift. + #### Unscrambling Swift compiler errors Type inferrence can be a source of hard-to-debug compiler errors. There are two potential places to be wrong when type inferrence used: diff --git a/README.md b/README.md index 6c1b184ebc..df1d3761a5 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,8 @@ __ReactiveCocoa__ wraps various aspects of Cocoa frameworks with the declarative nameLabel.reactive.text <~ person.name ``` + _Note_: You'll need to import ReactiveSwift as well to make use of the `<~` operator. + 1. **Controls and User Interactions** Interactive UI components expose [`Signal`][]s for control events From 8b9397463d27aaa12bf2a7aac0510132347ac9f7 Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 23 Dec 2016 00:29:36 +0100 Subject: [PATCH 0678/1028] 5.0.0-alpha.6 --- Cartfile | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- README.md | 4 ++-- ReactiveCocoa.podspec | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cartfile b/Cartfile index 6e6e16c6e0..c97e0fea63 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" "1.0.0-rc.2" +github "ReactiveCocoa/ReactiveSwift" ~> 1.0 diff --git a/Cartfile.resolved b/Cartfile.resolved index eddf8cc9dc..27bbccc431 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -2,4 +2,4 @@ github "Quick/Nimble" "v5.1.1" github "Quick/Quick" "v1.0.0" github "antitypical/Result" "3.1.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" -github "ReactiveCocoa/ReactiveSwift" "1.0.0-rc.2" +github "ReactiveCocoa/ReactiveSwift" "1.0.0-rc.3" diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index df08a05210..ec47295258 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit df08a05210157143d7f06c87feb6f9ad3c7603d6 +Subproject commit ec47295258656a647a53ea7f8b08580f6cecd9d5 diff --git a/README.md b/README.md index df1d3761a5..4ead10f8d4 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ If you use [Carthage][] to manage your dependencies, simply add ReactiveCocoa to your `Cartfile`: ``` -github "ReactiveCocoa/ReactiveCocoa" "5.0.0-alpha.5" +github "ReactiveCocoa/ReactiveCocoa" "5.0.0-alpha.6" ``` If you use Carthage to build your dependencies, make sure you have added `ReactiveCocoa.framework`, `ReactiveSwift.framework`, and `Result.framework` to the "_Linked Frameworks and Libraries_" section of your target, and have included them in your Carthage framework copying build phase. @@ -107,7 +107,7 @@ If you use [CocoaPods][] to manage your dependencies, simply add ReactiveCocoa to your `Podfile`: ``` -pod 'ReactiveCocoa', '5.0.0-alpha.5' +pod 'ReactiveCocoa', '5.0.0-alpha.6' ``` #### Git submodule diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 931e4f45fa..7369bcd69d 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "5.0.0-alpha.5" + s.version = "5.0.0-alpha.6" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. @@ -23,5 +23,5 @@ Pod::Spec.new do |s| s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_map = "ReactiveCocoa/module.modulemap" - s.dependency 'ReactiveSwift', '1.0.0-rc.2' + s.dependency 'ReactiveSwift', '~> 1.0' end From 8f631bd56a9350f6e811bbb81c8a6bdffdd35009 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Fri, 23 Dec 2016 09:30:02 +0900 Subject: [PATCH 0679/1028] Fix podspec which was broken in 5.0.0-alpha.6 --- ReactiveCocoa.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 7369bcd69d..c05ce8849b 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -23,5 +23,5 @@ Pod::Spec.new do |s| s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_map = "ReactiveCocoa/module.modulemap" - s.dependency 'ReactiveSwift', '~> 1.0' + s.dependency 'ReactiveSwift', '~> 1.0.0-rc.3' end From ba24e04510d995393fc26f35fbfe1e0a71b2288e Mon Sep 17 00:00:00 2001 From: Anders Date: Sat, 24 Dec 2016 00:15:30 +0100 Subject: [PATCH 0680/1028] Removed the unused `Atomic` copy. --- ReactiveCocoa.xcodeproj/project.pbxproj | 4 -- ReactiveCocoa/Atomic.swift | 72 ------------------------- 2 files changed, 76 deletions(-) delete mode 100644 ReactiveCocoa/Atomic.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 347431d747..d97eca6785 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -134,7 +134,6 @@ 9A54A21C1DE00D09001739B3 /* ObjC+Selector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A54A21A1DE00D09001739B3 /* ObjC+Selector.swift */; }; 9A54A21D1DE00D09001739B3 /* ObjC+Selector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A54A21A1DE00D09001739B3 /* ObjC+Selector.swift */; }; 9A54A21E1DE00D09001739B3 /* ObjC+Selector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A54A21A1DE00D09001739B3 /* ObjC+Selector.swift */; }; - 9A54A24F1DE123BD001739B3 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A54A24E1DE123BD001739B3 /* Atomic.swift */; }; 9A6AAA0E1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; 9A6AAA0F1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; 9A6AAA101DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */; }; @@ -375,7 +374,6 @@ 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaTarget.swift; sourceTree = ""; }; 9A54A2101DDF5B4D001739B3 /* InterceptingPerformanceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterceptingPerformanceTests.swift; sourceTree = ""; }; 9A54A21A1DE00D09001739B3 /* ObjC+Selector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObjC+Selector.swift"; sourceTree = ""; }; - 9A54A24E1DE123BD001739B3 /* Atomic.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Atomic.swift; sourceTree = ""; }; 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterceptingSpec.swift; sourceTree = ""; }; 9A6AAA221DB8F51C0013AAEA /* ReusableComponents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponents.swift; sourceTree = ""; }; 9A6AAA251DB8F5280013AAEA /* ReusableComponents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponents.swift; sourceTree = ""; }; @@ -706,7 +704,6 @@ D04725EC19E49ED7006002AA /* ReactiveCocoa */ = { isa = PBXGroup; children = ( - 9A54A24E1DE123BD001739B3 /* Atomic.swift */, 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */, 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */, CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */, @@ -1259,7 +1256,6 @@ 9A2E425E1DAA6737006D909F /* CocoaTarget.swift in Sources */, 9ADE4A891DA6D206005C2AC8 /* NSControl.swift in Sources */, 9AF0EA751D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, - 9A54A24F1DE123BD001739B3 /* Atomic.swift in Sources */, D9558AB81DFF805A003254E1 /* NSPopUpButton.swift in Sources */, 9ADE4A911DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, 53A6BED21DD4BCA90016C058 /* MKMapView.swift in Sources */, diff --git a/ReactiveCocoa/Atomic.swift b/ReactiveCocoa/Atomic.swift deleted file mode 100644 index 64cc9f0e57..0000000000 --- a/ReactiveCocoa/Atomic.swift +++ /dev/null @@ -1,72 +0,0 @@ -import ReactiveSwift - -// Redefined in ReactiveCocoa internally for performance until whole-module -// optimization is extended to incorporate dependencies. - -final class PosixThreadMutex: NSLocking { - private var mutex = pthread_mutex_t() - - init() { - let result = pthread_mutex_init(&mutex, nil) - precondition(result == 0, "Failed to initialize mutex with error \(result).") - } - - deinit { - let result = pthread_mutex_destroy(&mutex) - precondition(result == 0, "Failed to destroy mutex with error \(result).") - } - - func lock() { - let result = pthread_mutex_lock(&mutex) - precondition(result == 0, "Failed to lock \(self) with error \(result).") - } - - func unlock() { - let result = pthread_mutex_unlock(&mutex) - precondition(result == 0, "Failed to unlock \(self) with error \(result).") - } -} - -/// An atomic variable. -internal final class Atomic: AtomicProtocol { - private let lock: PosixThreadMutex - private var _value: Value - - /// Initialize the variable with the given initial value. - /// - /// - parameters: - /// - value: Initial value for `self`. - public init(_ value: Value) { - _value = value - lock = PosixThreadMutex() - } - - /// Atomically modifies the variable. - /// - /// - parameters: - /// - action: A closure that takes the current value. - /// - /// - returns: The result of the action. - @discardableResult - public func modify(_ action: (inout Value) throws -> Result) rethrows -> Result { - lock.lock() - defer { lock.unlock() } - - return try action(&_value) - } - - /// Atomically perform an arbitrary action using the current value of the - /// variable. - /// - /// - parameters: - /// - action: A closure that takes the current value. - /// - /// - returns: The result of the action. - @discardableResult - public func withValue(_ action: (Value) throws -> Result) rethrows -> Result { - lock.lock() - defer { lock.unlock() } - - return try action(_value) - } -} From 812fa2a1e517d8291c6d1f36735441461e2151c9 Mon Sep 17 00:00:00 2001 From: Anders Date: Sat, 24 Dec 2016 00:30:08 +0100 Subject: [PATCH 0681/1028] Allocate a contiguous memory block for association keys. --- ReactiveCocoa.xcodeproj/project.pbxproj | 8 +++---- ReactiveCocoa/NSObject+Association.swift | 18 ++++++++++++---- ReactiveCocoa/NSObject+Intercepting.swift | 23 +++++++++------------ ReactiveCocoa/NSObject+Lifetime.swift | 15 ++++++-------- ReactiveCocoa/ObjC+RuntimeSubclassing.swift | 10 ++++----- 5 files changed, 38 insertions(+), 36 deletions(-) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index d97eca6785..2b11917254 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -1601,7 +1601,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; - SWIFT_VERSION = 3.0.1; + SWIFT_VERSION = 3.0; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -1621,7 +1621,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; - SWIFT_VERSION = 3.0.1; + SWIFT_VERSION = 3.0; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -1740,7 +1740,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; - SWIFT_VERSION = 3.0.1; + SWIFT_VERSION = 3.0; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -1809,7 +1809,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)-Tests"; PRODUCT_NAME = "$(PROJECT_NAME)"; - SWIFT_VERSION = 3.0.1; + SWIFT_VERSION = 3.0; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; diff --git a/ReactiveCocoa/NSObject+Association.swift b/ReactiveCocoa/NSObject+Association.swift index 2488a2f0d4..409d4bcee8 100644 --- a/ReactiveCocoa/NSObject+Association.swift +++ b/ReactiveCocoa/NSObject+Association.swift @@ -1,5 +1,15 @@ import ReactiveSwift +internal struct AssociationKey { + private static let contiguous = UnsafeMutablePointer.allocate(capacity: 5) + + static let intercepted = contiguous + static let signatureCache = contiguous + 1 + static let runtimeSubclassed = contiguous + 2 + static let lifetime = contiguous + 3 + static let lifetimeToken = contiguous + 4 +} + extension Reactive where Base: NSObject { /// Retrieve the associated value for the specified key. If the value does not /// exist, `initial` would be called and the returned value would be @@ -12,10 +22,10 @@ extension Reactive where Base: NSObject { /// - returns: /// The associated value for the specified key. internal func associatedValue(forKey key: StaticString = #function, initial: (Base) -> T) -> T { - var value = base.value(forAssociatedKey: key.utf8Start) as! T? + var value = base.value(forAssociationKey: key.utf8Start) as! T? if value == nil { value = initial(base) - base.setValue(value, forAssociatedKey: key.utf8Start) + base.setValue(value, forAssociationKey: key.utf8Start) } return value! } @@ -29,7 +39,7 @@ extension NSObject { /// /// - returns: /// The associated value, or `nil` if no value is associated with the key. - @nonobjc internal func value(forAssociatedKey key: UnsafeRawPointer) -> Any? { + @nonobjc internal func value(forAssociationKey key: UnsafeRawPointer) -> Any? { return objc_getAssociatedObject(self, key) } @@ -40,7 +50,7 @@ extension NSObject { /// - key: The key. /// - weak: `true` if the value should be weakly referenced. `false` /// otherwise. - @nonobjc internal func setValue(_ value: Any?, forAssociatedKey key: UnsafeRawPointer, weak: Bool = false) { + @nonobjc internal func setValue(_ value: Any?, forAssociationKey key: UnsafeRawPointer, weak: Bool = false) { objc_setAssociatedObject(self, key, value, weak ? .OBJC_ASSOCIATION_ASSIGN : .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index dc2af963d9..2efed737a9 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -42,15 +42,15 @@ extension Reactive where Base: NSObject { // A summary of associated values involved. // // Runtime subclasses: -// - `&isSwizzledKey` (ObjC+RuntimeSubclassing.swift) +// - `&AssociationKey.intercepted` (ObjC+RuntimeSubclassing.swift) // Nullable. Exists only in runtime subclasses generated by external parties. // `true` indicates the runtime subclass has already been swizzled. // -// - `&isSwizzledKey` (This file) +// - `&AssociationKey.intercepted` (This file) // Nullable. `true` indicates the runtime subclass has already been prepared for method // interception. // -// - `&interceptedSelectorsKey` +// - `&AssociationKey.signatureCache` // Holds the method signature cache of the runtime subclass. // // Intercepted instances: @@ -78,24 +78,24 @@ private func setupInterception(_ object: NSObject, for selector: Selector) -> Si assert(checkTypeEncoding(typeEncoding)) return object.synchronized { - if let state = object.value(forAssociatedKey: alias.utf8Start) as! InterceptingState? { + if let state = object.value(forAssociationKey: alias.utf8Start) as! InterceptingState? { return state.signal } let subclass: AnyClass = swizzleClass(object) synchronized(subclass) { - let isSwizzled = objc_getAssociatedObject(subclass, &isSwizzledKey) as! Bool? ?? false + let isSwizzled = objc_getAssociatedObject(subclass, AssociationKey.intercepted) as! Bool? ?? false let signatureCache: SignatureCache if isSwizzled { - signatureCache = objc_getAssociatedObject(subclass, &interceptedSelectorsKey) as! SignatureCache + signatureCache = objc_getAssociatedObject(subclass, AssociationKey.signatureCache) as! SignatureCache } else { signatureCache = SignatureCache() - objc_setAssociatedObject(subclass, &interceptedSelectorsKey, signatureCache, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - objc_setAssociatedObject(subclass, &isSwizzledKey, true, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + objc_setAssociatedObject(subclass, AssociationKey.signatureCache, signatureCache, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + objc_setAssociatedObject(subclass, AssociationKey.intercepted, true, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) enableMessageForwarding(subclass) setupMethodSignatureCaching(subclass, signatureCache) @@ -128,7 +128,7 @@ private func setupInterception(_ object: NSObject, for selector: Selector) -> Si } let state = InterceptingState(lifetime: object.reactive.lifetime) - object.setValue(state, forAssociatedKey: alias.utf8Start) + object.setValue(state, forAssociationKey: alias.utf8Start) // Start forwarding the messages of the selector. _ = class_replaceMethod(subclass, selector, _rac_objc_msgForward, typeEncoding) @@ -151,7 +151,7 @@ private func enableMessageForwarding(_ realClass: AnyClass) { let interopAlias = selector.interopAlias defer { - if let state = objectRef.takeUnretainedValue().value(forAssociatedKey: alias.utf8Start) as! InterceptingState? { + if let state = objectRef.takeUnretainedValue().value(forAssociationKey: alias.utf8Start) as! InterceptingState? { state.observer.send(value: invocation) } } @@ -284,9 +284,6 @@ private final class SignatureCache { } } -private var isSwizzledKey = 0 -private var interceptedSelectorsKey = 0 - /// Assert that the method does not contain types that cannot be intercepted. /// /// - parameters: diff --git a/ReactiveCocoa/NSObject+Lifetime.swift b/ReactiveCocoa/NSObject+Lifetime.swift index 5428764ca7..3bddfff76d 100644 --- a/ReactiveCocoa/NSObject+Lifetime.swift +++ b/ReactiveCocoa/NSObject+Lifetime.swift @@ -1,14 +1,11 @@ import Foundation import ReactiveSwift -private var lifetimeKey: UInt8 = 0 -private var lifetimeTokenKey: UInt8 = 0 - extension Reactive where Base: NSObject { /// Returns a lifetime that ends when the object is deallocated. @nonobjc public var lifetime: Lifetime { return base.synchronized { - if let lifetime = base.value(forAssociatedKey: &lifetimeKey) as! Lifetime? { + if let lifetime = base.value(forAssociationKey: AssociationKey.lifetime) as! Lifetime? { return lifetime } @@ -22,8 +19,8 @@ extension Reactive where Base: NSObject { // beginning of the deallocation chain, and only after the KVO `-dealloc`. synchronized(objcClass) { // Swizzle the class only if it has not been swizzled before. - if objc_getAssociatedObject(objcClass, &lifetimeKey) == nil { - objc_setAssociatedObject(objcClass, &lifetimeKey, true, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + if objc_getAssociatedObject(objcClass, AssociationKey.lifetime) == nil { + objc_setAssociatedObject(objcClass, AssociationKey.lifetime, true, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) var existingImpl: IMP? = nil @@ -33,7 +30,7 @@ extension Reactive where Base: NSObject { // mess with the object deallocation chain. // Release the lifetime token. - _rac_objc_setAssociatedObject(objectRef, &lifetimeTokenKey, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + _rac_objc_setAssociatedObject(objectRef, AssociationKey.lifetimeToken, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) let impl: IMP @@ -67,8 +64,8 @@ extension Reactive where Base: NSObject { } } - objc_setAssociatedObject(base, &lifetimeTokenKey, token, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - objc_setAssociatedObject(base, &lifetimeKey, lifetime, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + base.setValue(token, forAssociationKey: AssociationKey.lifetimeToken) + base.setValue(lifetime, forAssociationKey: AssociationKey.lifetime) return lifetime } diff --git a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift index 96f038fd9d..dbc496fb41 100644 --- a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift +++ b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift @@ -1,7 +1,5 @@ import ReactiveSwift -private var isSwizzledKey = 0 - /// ISA-swizzle the class of the supplied instance. /// /// - note: If the instance has already been isa-swizzled, the swizzling happens @@ -15,7 +13,7 @@ private var isSwizzledKey = 0 internal func swizzleClass(_ instance: NSObject) -> AnyClass { let key = (#function as StaticString).utf8Start - if let knownSubclass = instance.value(forAssociatedKey: key) as! AnyClass? { + if let knownSubclass = instance.value(forAssociationKey: key) as! AnyClass? { return knownSubclass } @@ -26,10 +24,10 @@ internal func swizzleClass(_ instance: NSObject) -> AnyClass { // If the class is already lying about what it is, it's probably a KVO // dynamic subclass or something else that we shouldn't subclass at runtime. synchronized(realClass) { - let isSwizzled = objc_getAssociatedObject(realClass, &isSwizzledKey) as! Bool? ?? false + let isSwizzled = objc_getAssociatedObject(realClass, AssociationKey.runtimeSubclassed) as! Bool? ?? false if !isSwizzled { replaceGetClass(in: realClass, decoy: perceivedClass) - objc_setAssociatedObject(realClass, &isSwizzledKey, true, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + objc_setAssociatedObject(realClass, AssociationKey.runtimeSubclassed, true, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } @@ -48,7 +46,7 @@ internal func swizzleClass(_ instance: NSObject) -> AnyClass { } object_setClass(instance, subclass) - instance.setValue(subclass, forAssociatedKey: key) + instance.setValue(subclass, forAssociationKey: key) return subclass } } From 32584def97bf3fd9fea697bf6e4fc4110a40141f Mon Sep 17 00:00:00 2001 From: Anders Date: Sat, 24 Dec 2016 01:05:09 +0100 Subject: [PATCH 0682/1028] Migrate selector cache to use Read-Copy-Update. --- ReactiveCocoa.xcodeproj/project.pbxproj | 10 --- ReactiveCocoa/NSObject+Association.swift | 9 +-- ReactiveCocoa/NSObject+Intercepting.swift | 87 +++++++++++++++++++---- ReactiveCocoa/ObjC+Selector.swift | 34 +-------- ReactiveCocoa/ThreadLocal.swift | 39 ---------- 5 files changed, 79 insertions(+), 100 deletions(-) delete mode 100644 ReactiveCocoa/ThreadLocal.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 2b11917254..fceca4d5c9 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -116,10 +116,6 @@ 9A1E72BA1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; 9A1E72BB1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; 9A1E72BC1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */; }; - 9A24A83F1DE1386800987AF9 /* ThreadLocal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A24A83E1DE1386800987AF9 /* ThreadLocal.swift */; }; - 9A24A8401DE1386800987AF9 /* ThreadLocal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A24A83E1DE1386800987AF9 /* ThreadLocal.swift */; }; - 9A24A8411DE1386800987AF9 /* ThreadLocal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A24A83E1DE1386800987AF9 /* ThreadLocal.swift */; }; - 9A24A8421DE1386800987AF9 /* ThreadLocal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A24A83E1DE1386800987AF9 /* ThreadLocal.swift */; }; 9A24A8451DE142A400987AF9 /* SwizzlingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A24A8431DE1429600987AF9 /* SwizzlingSpec.swift */; }; 9A24A8461DE142A500987AF9 /* SwizzlingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A24A8431DE1429600987AF9 /* SwizzlingSpec.swift */; }; 9A24A8471DE142A600987AF9 /* SwizzlingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A24A8431DE1429600987AF9 /* SwizzlingSpec.swift */; }; @@ -369,7 +365,6 @@ 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Intercepting.swift"; sourceTree = ""; }; 9A1D06751D9415FB00ACF44C /* ObjCRuntimeAliases.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ObjCRuntimeAliases.h; sourceTree = ""; }; 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyValueObservingSpec.swift; sourceTree = ""; }; - 9A24A83E1DE1386800987AF9 /* ThreadLocal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThreadLocal.swift; sourceTree = ""; }; 9A24A8431DE1429600987AF9 /* SwizzlingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwizzlingSpec.swift; sourceTree = ""; }; 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaTarget.swift; sourceTree = ""; }; 9A54A2101DDF5B4D001739B3 /* InterceptingPerformanceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterceptingPerformanceTests.swift; sourceTree = ""; }; @@ -720,7 +715,6 @@ 9A54A21A1DE00D09001739B3 /* ObjC+Selector.swift */, 9AA0BD801DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift */, 9AA0BD771DDE03DE00531FCF /* ObjC+Runtime.swift */, - 9A24A83E1DE1386800987AF9 /* ThreadLocal.swift */, 9A1D05E91D93E9F100ACF44C /* AppKit */, 9A1D05EB1D93E9F100ACF44C /* UIKit */, 538DCB761DCA5E1600332880 /* Shared */, @@ -1161,7 +1155,6 @@ 9ADFE5A81DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */, CD0C45E11CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9AA0BD921DDE29F800531FCF /* NSObject+ObjCRuntime.swift in Sources */, - 9A24A8421DE1386800987AF9 /* ThreadLocal.swift in Sources */, 9A1D061A1D93EA0100ACF44C /* UIProgressView.swift in Sources */, 9AA0BD7F1DDE03DE00531FCF /* ObjC+Runtime.swift in Sources */, 9A2E42611DAA6737006D909F /* CocoaTarget.swift in Sources */, @@ -1221,7 +1214,6 @@ 9AA0BD8C1DDE153A00531FCF /* ObjC+Constants.swift in Sources */, 9ADFE5A71DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */, 9ADE4A7E1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, - 9A24A8411DE1386800987AF9 /* ThreadLocal.swift in Sources */, 9AA0BD831DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift in Sources */, CD0C45E01CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9AA0BD911DDE29F800531FCF /* NSObject+ObjCRuntime.swift in Sources */, @@ -1262,7 +1254,6 @@ 9A1D065B1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, 4A0E10FF1D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, 9AA0BD811DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift in Sources */, - 9A24A83F1DE1386800987AF9 /* ThreadLocal.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1302,7 +1293,6 @@ BFCF775F1DFAD8A50058006E /* UISearchBar.swift in Sources */, 9A1D06061D93EA0000ACF44C /* UIImageView.swift in Sources */, 9AF0EA761D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, - 9A24A8401DE1386800987AF9 /* ThreadLocal.swift in Sources */, CD0C45DF1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9AE7C2A51DDD7F5100F7534C /* ObjC+Messages.swift in Sources */, 9A2E425F1DAA6737006D909F /* CocoaTarget.swift in Sources */, diff --git a/ReactiveCocoa/NSObject+Association.swift b/ReactiveCocoa/NSObject+Association.swift index 409d4bcee8..ab635da630 100644 --- a/ReactiveCocoa/NSObject+Association.swift +++ b/ReactiveCocoa/NSObject+Association.swift @@ -1,13 +1,14 @@ import ReactiveSwift internal struct AssociationKey { - private static let contiguous = UnsafeMutablePointer.allocate(capacity: 5) + private static let contiguous = UnsafeMutablePointer.allocate(capacity: 6) static let intercepted = contiguous static let signatureCache = contiguous + 1 - static let runtimeSubclassed = contiguous + 2 - static let lifetime = contiguous + 3 - static let lifetimeToken = contiguous + 4 + static let selectorCache = contiguous + 2 + static let runtimeSubclassed = contiguous + 3 + static let lifetime = contiguous + 4 + static let lifetimeToken = contiguous + 5 } extension Reactive where Base: NSObject { diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 2efed737a9..a82251ecde 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -67,9 +67,6 @@ extension Reactive where Base: NSObject { /// A signal that sends the corresponding `NSInvocation` after every /// invocation of the method. private func setupInterception(_ object: NSObject, for selector: Selector) -> Signal { - let alias = selector.alias - let interopAlias = selector.interopAlias - guard let method = class_getInstanceMethod(object.objcClass, selector) else { fatalError("Selector `\(selector)` does not exist in class `\(String(describing: object.objcClass))`.") } @@ -78,6 +75,9 @@ private func setupInterception(_ object: NSObject, for selector: Selector) -> Si assert(checkTypeEncoding(typeEncoding)) return object.synchronized { + let alias = selector.alias + let interopAlias = selector.interopAlias + if let state = object.value(forAssociationKey: alias.utf8Start) as! InterceptingState? { return state.signal } @@ -88,24 +88,32 @@ private func setupInterception(_ object: NSObject, for selector: Selector) -> Si let isSwizzled = objc_getAssociatedObject(subclass, AssociationKey.intercepted) as! Bool? ?? false let signatureCache: SignatureCache + let selectorCache: SelectorCache if isSwizzled { signatureCache = objc_getAssociatedObject(subclass, AssociationKey.signatureCache) as! SignatureCache + selectorCache = objc_getAssociatedObject(subclass, AssociationKey.selectorCache) as! SelectorCache } else { signatureCache = SignatureCache() - - objc_setAssociatedObject(subclass, AssociationKey.signatureCache, signatureCache, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - objc_setAssociatedObject(subclass, AssociationKey.intercepted, true, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - - enableMessageForwarding(subclass) - setupMethodSignatureCaching(subclass, signatureCache) + selectorCache = SelectorCache() } + selectorCache.allocate(selector) + if signatureCache[selector] == nil { let signature = NSMethodSignature.signature(withObjCTypes: typeEncoding) signatureCache[selector] = signature } + if !isSwizzled { + objc_setAssociatedObject(subclass, AssociationKey.signatureCache, signatureCache, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + objc_setAssociatedObject(subclass, AssociationKey.selectorCache, selectorCache, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + objc_setAssociatedObject(subclass, AssociationKey.intercepted, true, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + + enableMessageForwarding(subclass, selectorCache) + setupMethodSignatureCaching(subclass, signatureCache) + } + if !class_respondsToSelector(subclass, interopAlias) { let immediateMethod = class_getImmediateMethod(subclass, selector) @@ -141,14 +149,14 @@ private func setupInterception(_ object: NSObject, for selector: Selector) -> Si /// /// - parameters: /// - realClass: The runtime subclass to be swizzled. -private func enableMessageForwarding(_ realClass: AnyClass) { +private func enableMessageForwarding(_ realClass: AnyClass, _ selectorCache: SelectorCache) { let perceivedClass: AnyClass = class_getSuperclass(realClass) typealias ForwardInvocationImpl = @convention(block) (Unmanaged, AnyObject) -> Void let newForwardInvocation: ForwardInvocationImpl = { objectRef, invocation in let selector = invocation.selector! - let alias = selector.alias - let interopAlias = selector.interopAlias + let alias = selectorCache[main: selector] + let interopAlias = selectorCache[interop: selector] defer { if let state = objectRef.takeUnretainedValue().value(forAssociationKey: alias.utf8Start) as! InterceptingState? { @@ -252,6 +260,54 @@ private final class InterceptingState { } } +private final class SelectorCache { + private var map: [Selector: (main: Selector, interop: Selector)] = [:] + + init() {} + + /// Cache the aliases of the specified selector in the cache. + /// + /// - warning: Any invocation of this method must be synchronized against the + /// runtime subclass. + @discardableResult + func allocate(_ selector: Selector) -> (main: Selector, interop: Selector) { + if let pair = map[selector] { + return pair + } + + var copy = map + let aliases = (selector.alias, selector.interopAlias) + copy[selector] = aliases + map = copy + + return aliases + } + + /// Get the alias of the specified selector. + /// + /// - parameters: + /// - selector: The selector alias. + subscript(main selector: Selector) -> Selector { + if let (main, _) = map[selector] { + return main + } + + return selector.alias + } + + /// Get the secondary alias of the specified selector. + /// + /// - parameters: + /// - selector: The selector alias. + subscript(interop selector: Selector) -> Selector { + if let (_, interop) = map[selector] { + return interop + } + + return selector.interopAlias + } +} + // The signature cache for classes that have been swizzled for method // interception. // @@ -260,16 +316,17 @@ private final class InterceptingState { private final class SignatureCache { // `Dictionary` takes 8 bytes for the reference to its storage and does CoW. // So it should not encounter any corrupted, partially updated state. - private var map = [Selector: AnyObject]() + private var map: [Selector: AnyObject] = [:] init() {} /// Get or set the signature for the specified selector. /// - /// - warning: Any invocation of the setter must be serialized. + /// - warning: Any invocation of the setter must be synchronized against the + /// runtime subclass. /// /// - parameters: - /// - selector: The selector. + /// - selector: The method signature. subscript(selector: Selector) -> AnyObject? { get { return map[selector] diff --git a/ReactiveCocoa/ObjC+Selector.swift b/ReactiveCocoa/ObjC+Selector.swift index 68797d7ff4..a6dd4fe884 100644 --- a/ReactiveCocoa/ObjC+Selector.swift +++ b/ReactiveCocoa/ObjC+Selector.swift @@ -5,48 +5,18 @@ extension Selector { return unsafeBitCast(self, to: UnsafePointer.self) } - /// Selector alias cache. - final class Cache { - var selectors: [Selector: Selector] = [:] - } - /// An alias of `self`, used in method interception. internal var alias: Selector { - enum Static { - static let cache = ThreadLocal { Cache() } - } - - let localCache = Static.cache.local - - if let selector = localCache.selectors[self] { - return selector - } else { - let selector = prefixing("rac0_") - localCache.selectors[self] = selector - return selector - } + return prefixing("rac0_") } /// An alias of `self`, used in method interception specifically for /// preserving (if found) an immediate implementation of `self` in the /// runtime subclass. internal var interopAlias: Selector { - enum Static { - static let cache = ThreadLocal { Cache() } - } - - let localCache = Static.cache.local - - if let selector = localCache.selectors[self] { - return selector - } else { - let selector = prefixing("rac1_") - localCache.selectors[self] = selector - return selector - } + return prefixing("rac1_") } - /// internal func prefixing(_ prefix: StaticString) -> Selector { let length = Int(strlen(utf8Start)) let prefixedLength = length + prefix.utf8CodeUnitCount diff --git a/ReactiveCocoa/ThreadLocal.swift b/ReactiveCocoa/ThreadLocal.swift deleted file mode 100644 index 70d311da62..0000000000 --- a/ReactiveCocoa/ThreadLocal.swift +++ /dev/null @@ -1,39 +0,0 @@ -/// A lazily-initialized container that maintains thread-specific instances of -/// `Object`. -/// -/// When being accessed via `local`, if no existing instance of `Object` is -/// found in the container for the current thread, it creates an instance using -/// the supplied initializer. -internal final class ThreadLocal { - private let key: pthread_key_t - private let initializer: () -> Object - - /// The instance of `Object` specific to the current thread. - var local: Object { - get { - if let pointer = pthread_getspecific(key) { - return Unmanaged.fromOpaque(pointer).takeUnretainedValue() - } else { - let object = initializer() - pthread_setspecific(key, Unmanaged.passRetained(object).toOpaque()) - return object - } - } - } - - /// Initialize a container that maintains thread-specific instances of - /// `Object`. - /// - /// - parameters: - /// - initializer: The closure that creates an instance of `Object`. - init(initializer: @escaping () -> Object) { - var key = pthread_key_t() - let status = pthread_key_create(&key) { pointer in - Unmanaged.fromOpaque(pointer).release() - } - precondition(status == 0) - - self.key = key - self.initializer = initializer - } -} From 8820525e7f5408580915592bf2ff12c0d36c6e4a Mon Sep 17 00:00:00 2001 From: Anders Date: Sat, 24 Dec 2016 02:16:36 +0100 Subject: [PATCH 0683/1028] Use IMP cache backed calls in the `forwardInvocation` impl. --- ReactiveCocoa/NSObject+Intercepting.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index a82251ecde..a1316d607f 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -192,9 +192,7 @@ private func enableMessageForwarding(_ realClass: AnyClass, _ selectorCache: Sel // reflects the latest implementation of the selector in the perceived // class. - let method = class_getImmediateMethod(realClass, alias) - - if method == nil || method_getImplementation(method!) != impl { + if class_getMethodImplementation(realClass, alias) != impl { // Update the alias if and only if the implementation has changed, so as // to avoid thrashing the IMP cache. _ = class_replaceMethod(realClass, alias, impl, typeEncoding) From 2ba53bf9e2cb320b5fdd045ab8782886fb0da977 Mon Sep 17 00:00:00 2001 From: Anders Date: Sat, 24 Dec 2016 02:25:04 +0100 Subject: [PATCH 0684/1028] Move `setupInterception` into an `NSObject` extension. --- ReactiveCocoa/NSObject+Intercepting.swift | 159 ++++++++++------------ 1 file changed, 71 insertions(+), 88 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index a1316d607f..6aa95d00f2 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -17,7 +17,7 @@ extension Reactive where Base: NSObject { /// - returns: /// A trigger signal. public func trigger(for selector: Selector) -> Signal<(), NoError> { - return setupInterception(base, for: selector).map { _ in } + return base.setupInterception(for: selector).map { _ in } } /// Create a signal which sends a `next` event, containing an array of bridged @@ -34,114 +34,97 @@ extension Reactive where Base: NSObject { /// - returns: /// A signal that sends an array of bridged arguments. public func signal(for selector: Selector) -> Signal<[Any?], NoError> { - return setupInterception(base, for: selector).map(unpackInvocation) + return base.setupInterception(for: selector).map(unpackInvocation) } } - -// A summary of associated values involved. -// -// Runtime subclasses: -// - `&AssociationKey.intercepted` (ObjC+RuntimeSubclassing.swift) -// Nullable. Exists only in runtime subclasses generated by external parties. -// `true` indicates the runtime subclass has already been swizzled. -// -// - `&AssociationKey.intercepted` (This file) -// Nullable. `true` indicates the runtime subclass has already been prepared for method -// interception. -// -// - `&AssociationKey.signatureCache` -// Holds the method signature cache of the runtime subclass. -// -// Intercepted instances: -// - `alias.utf8Start` -// Nullable. Holds the `InterceptingState` of the selector for the instance. - -/// Setup the method interception. -/// -/// - parameters: -/// - object: The object to be intercepted. -/// - selector: The selector of the method to be intercepted. -/// -/// - returns: -/// A signal that sends the corresponding `NSInvocation` after every -/// invocation of the method. -private func setupInterception(_ object: NSObject, for selector: Selector) -> Signal { - guard let method = class_getInstanceMethod(object.objcClass, selector) else { - fatalError("Selector `\(selector)` does not exist in class `\(String(describing: object.objcClass))`.") - } - - let typeEncoding = method_getTypeEncoding(method)! - assert(checkTypeEncoding(typeEncoding)) - - return object.synchronized { - let alias = selector.alias - let interopAlias = selector.interopAlias - - if let state = object.value(forAssociationKey: alias.utf8Start) as! InterceptingState? { - return state.signal +extension NSObject { + /// Setup the method interception. + /// + /// - parameters: + /// - object: The object to be intercepted. + /// - selector: The selector of the method to be intercepted. + /// + /// - returns: + /// A signal that sends the corresponding `NSInvocation` after every + /// invocation of the method. + @nonobjc fileprivate func setupInterception(for selector: Selector) -> Signal { + guard let method = class_getInstanceMethod(objcClass, selector) else { + fatalError("Selector `\(selector)` does not exist in class `\(String(describing: objcClass))`.") } - let subclass: AnyClass = swizzleClass(object) + let typeEncoding = method_getTypeEncoding(method)! + assert(checkTypeEncoding(typeEncoding)) - synchronized(subclass) { - let isSwizzled = objc_getAssociatedObject(subclass, AssociationKey.intercepted) as! Bool? ?? false + return synchronized { + let alias = selector.alias + let interopAlias = selector.interopAlias - let signatureCache: SignatureCache - let selectorCache: SelectorCache - - if isSwizzled { - signatureCache = objc_getAssociatedObject(subclass, AssociationKey.signatureCache) as! SignatureCache - selectorCache = objc_getAssociatedObject(subclass, AssociationKey.selectorCache) as! SelectorCache - } else { - signatureCache = SignatureCache() - selectorCache = SelectorCache() + if let state = value(forAssociationKey: alias.utf8Start) as! InterceptingState? { + return state.signal } - selectorCache.allocate(selector) + let subclass: AnyClass = swizzleClass(self) - if signatureCache[selector] == nil { - let signature = NSMethodSignature.signature(withObjCTypes: typeEncoding) - signatureCache[selector] = signature - } + // FIXME: Compiler asks to handle a mysterious throw. + try! ReactiveCocoa.synchronized(subclass) { + let isSwizzled = objc_getAssociatedObject(subclass, AssociationKey.intercepted) as! Bool? ?? false - if !isSwizzled { - objc_setAssociatedObject(subclass, AssociationKey.signatureCache, signatureCache, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - objc_setAssociatedObject(subclass, AssociationKey.selectorCache, selectorCache, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - objc_setAssociatedObject(subclass, AssociationKey.intercepted, true, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + let signatureCache: SignatureCache + let selectorCache: SelectorCache - enableMessageForwarding(subclass, selectorCache) - setupMethodSignatureCaching(subclass, signatureCache) - } + if isSwizzled { + signatureCache = objc_getAssociatedObject(subclass, AssociationKey.signatureCache) as! SignatureCache + selectorCache = objc_getAssociatedObject(subclass, AssociationKey.selectorCache) as! SelectorCache + } else { + signatureCache = SignatureCache() + selectorCache = SelectorCache() + } - if !class_respondsToSelector(subclass, interopAlias) { - let immediateMethod = class_getImmediateMethod(subclass, selector) + selectorCache.allocate(selector) - let immediateImpl: IMP? = immediateMethod.flatMap { - let immediateImpl = method_getImplementation($0) - return immediateImpl.flatMap { $0 != _rac_objc_msgForward ? $0 : nil } + if signatureCache[selector] == nil { + let signature = NSMethodSignature.signature(withObjCTypes: typeEncoding) + signatureCache[selector] = signature } - if let impl = immediateImpl { - // If an immediate implementation of the selector is found in the - // runtime subclass the first time the selector is intercepted, - // preserve the implementation. - // - // Example: KVO setters if the instance is swizzled by KVO before RAC - // does. + if !isSwizzled { + objc_setAssociatedObject(subclass, AssociationKey.signatureCache, signatureCache, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + objc_setAssociatedObject(subclass, AssociationKey.selectorCache, selectorCache, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + objc_setAssociatedObject(subclass, AssociationKey.intercepted, true, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - class_addMethod(subclass, interopAlias, impl, typeEncoding) + enableMessageForwarding(subclass, selectorCache) + setupMethodSignatureCaching(subclass, signatureCache) } - } - } - let state = InterceptingState(lifetime: object.reactive.lifetime) - object.setValue(state, forAssociationKey: alias.utf8Start) + // If an immediate implementation of the selector is found in the + // runtime subclass the first time the selector is intercepted, + // preserve the implementation. + // + // Example: KVO setters if the instance is swizzled by KVO before RAC + // does. + if !class_respondsToSelector(subclass, interopAlias) { + let immediateMethod = class_getImmediateMethod(subclass, selector) + + let immediateImpl: IMP? = immediateMethod.flatMap { + let immediateImpl = method_getImplementation($0) + return immediateImpl.flatMap { $0 != _rac_objc_msgForward ? $0 : nil } + } + + if let impl = immediateImpl { + class_addMethod(subclass, interopAlias, impl, typeEncoding) + } + } + } - // Start forwarding the messages of the selector. - _ = class_replaceMethod(subclass, selector, _rac_objc_msgForward, typeEncoding) + let state = InterceptingState(lifetime: reactive.lifetime) + setValue(state, forAssociationKey: alias.utf8Start) - return state.signal + // Start forwarding the messages of the selector. + _ = class_replaceMethod(subclass, selector, _rac_objc_msgForward, typeEncoding) + + return state.signal + } } } From 4669f75add78bab7a7421d3f8439f955265cf147 Mon Sep 17 00:00:00 2001 From: Anders Date: Sat, 24 Dec 2016 02:27:39 +0100 Subject: [PATCH 0685/1028] Rename association APIs. Document the association keys. --- ReactiveCocoa/NSObject+Association.swift | 21 +++++++++++++++++---- ReactiveCocoa/NSObject+Intercepting.swift | 6 +++--- ReactiveCocoa/NSObject+Lifetime.swift | 6 +++--- ReactiveCocoa/ObjC+RuntimeSubclassing.swift | 4 ++-- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/ReactiveCocoa/NSObject+Association.swift b/ReactiveCocoa/NSObject+Association.swift index ab635da630..e618e5242a 100644 --- a/ReactiveCocoa/NSObject+Association.swift +++ b/ReactiveCocoa/NSObject+Association.swift @@ -3,11 +3,24 @@ import ReactiveSwift internal struct AssociationKey { private static let contiguous = UnsafeMutablePointer.allocate(capacity: 6) + /// Nullable. `true` indicates the runtime subclass has already been prepared for method + /// interception. static let intercepted = contiguous + + /// Holds the method signature cache of the runtime subclass. static let signatureCache = contiguous + 1 + + /// Holds the method selector cache of the runtime subclass. static let selectorCache = contiguous + 2 + + /// Nullable. Exists only in runtime subclasses generated by external parties. + /// `true` indicates the runtime subclass has already been swizzled. static let runtimeSubclassed = contiguous + 3 + + /// Holds the `Lifetime` of the object. static let lifetime = contiguous + 4 + + /// Holds the `Lifetime.Token` of the object. static let lifetimeToken = contiguous + 5 } @@ -23,10 +36,10 @@ extension Reactive where Base: NSObject { /// - returns: /// The associated value for the specified key. internal func associatedValue(forKey key: StaticString = #function, initial: (Base) -> T) -> T { - var value = base.value(forAssociationKey: key.utf8Start) as! T? + var value = base.associatedValue(forKey: key.utf8Start) as! T? if value == nil { value = initial(base) - base.setValue(value, forAssociationKey: key.utf8Start) + base.setAssociatedValue(value, forKey: key.utf8Start) } return value! } @@ -40,7 +53,7 @@ extension NSObject { /// /// - returns: /// The associated value, or `nil` if no value is associated with the key. - @nonobjc internal func value(forAssociationKey key: UnsafeRawPointer) -> Any? { + @nonobjc internal func associatedValue(forKey key: UnsafeRawPointer) -> Any? { return objc_getAssociatedObject(self, key) } @@ -51,7 +64,7 @@ extension NSObject { /// - key: The key. /// - weak: `true` if the value should be weakly referenced. `false` /// otherwise. - @nonobjc internal func setValue(_ value: Any?, forAssociationKey key: UnsafeRawPointer, weak: Bool = false) { + @nonobjc internal func setAssociatedValue(_ value: Any?, forKey key: UnsafeRawPointer, weak: Bool = false) { objc_setAssociatedObject(self, key, value, weak ? .OBJC_ASSOCIATION_ASSIGN : .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 6aa95d00f2..dad1b771f0 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -60,7 +60,7 @@ extension NSObject { let alias = selector.alias let interopAlias = selector.interopAlias - if let state = value(forAssociationKey: alias.utf8Start) as! InterceptingState? { + if let state = associatedValue(forKey: alias.utf8Start) as! InterceptingState? { return state.signal } @@ -118,7 +118,7 @@ extension NSObject { } let state = InterceptingState(lifetime: reactive.lifetime) - setValue(state, forAssociationKey: alias.utf8Start) + setAssociatedValue(state, forKey: alias.utf8Start) // Start forwarding the messages of the selector. _ = class_replaceMethod(subclass, selector, _rac_objc_msgForward, typeEncoding) @@ -142,7 +142,7 @@ private func enableMessageForwarding(_ realClass: AnyClass, _ selectorCache: Sel let interopAlias = selectorCache[interop: selector] defer { - if let state = objectRef.takeUnretainedValue().value(forAssociationKey: alias.utf8Start) as! InterceptingState? { + if let state = objectRef.takeUnretainedValue().associatedValue(forKey: alias.utf8Start) as! InterceptingState? { state.observer.send(value: invocation) } } diff --git a/ReactiveCocoa/NSObject+Lifetime.swift b/ReactiveCocoa/NSObject+Lifetime.swift index 3bddfff76d..b8bc267024 100644 --- a/ReactiveCocoa/NSObject+Lifetime.swift +++ b/ReactiveCocoa/NSObject+Lifetime.swift @@ -5,7 +5,7 @@ extension Reactive where Base: NSObject { /// Returns a lifetime that ends when the object is deallocated. @nonobjc public var lifetime: Lifetime { return base.synchronized { - if let lifetime = base.value(forAssociationKey: AssociationKey.lifetime) as! Lifetime? { + if let lifetime = base.associatedValue(forKey: AssociationKey.lifetime) as! Lifetime? { return lifetime } @@ -64,8 +64,8 @@ extension Reactive where Base: NSObject { } } - base.setValue(token, forAssociationKey: AssociationKey.lifetimeToken) - base.setValue(lifetime, forAssociationKey: AssociationKey.lifetime) + base.setAssociatedValue(token, forKey: AssociationKey.lifetimeToken) + base.setAssociatedValue(lifetime, forKey: AssociationKey.lifetime) return lifetime } diff --git a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift index dbc496fb41..c9d4ffcac7 100644 --- a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift +++ b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift @@ -13,7 +13,7 @@ import ReactiveSwift internal func swizzleClass(_ instance: NSObject) -> AnyClass { let key = (#function as StaticString).utf8Start - if let knownSubclass = instance.value(forAssociationKey: key) as! AnyClass? { + if let knownSubclass = instance.associatedValue(forKey: key) as! AnyClass? { return knownSubclass } @@ -46,7 +46,7 @@ internal func swizzleClass(_ instance: NSObject) -> AnyClass { } object_setClass(instance, subclass) - instance.setValue(subclass, forAssociationKey: key) + instance.setAssociatedValue(subclass, forKey: key) return subclass } } From c1fa1b25a893a5c4c2e3091f437c3a925b5d035d Mon Sep 17 00:00:00 2001 From: Anders Date: Sat, 24 Dec 2016 09:26:56 +0100 Subject: [PATCH 0686/1028] Gardening in `NSObject+Intercepting`. --- ReactiveCocoa/NSObject+Intercepting.swift | 87 +++++++++++------------ 1 file changed, 41 insertions(+), 46 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index dad1b771f0..a2435f01d8 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -17,7 +17,7 @@ extension Reactive where Base: NSObject { /// - returns: /// A trigger signal. public func trigger(for selector: Selector) -> Signal<(), NoError> { - return base.setupInterception(for: selector).map { _ in } + return base.intercept(selector).map { _ in } } /// Create a signal which sends a `next` event, containing an array of bridged @@ -34,7 +34,7 @@ extension Reactive where Base: NSObject { /// - returns: /// A signal that sends an array of bridged arguments. public func signal(for selector: Selector) -> Signal<[Any?], NoError> { - return base.setupInterception(for: selector).map(unpackInvocation) + return base.intercept(selector).map(unpackInvocation) } } @@ -48,7 +48,7 @@ extension NSObject { /// - returns: /// A signal that sends the corresponding `NSInvocation` after every /// invocation of the method. - @nonobjc fileprivate func setupInterception(for selector: Selector) -> Signal { + @nonobjc fileprivate func intercept(_ selector: Selector) -> Signal { guard let method = class_getInstanceMethod(objcClass, selector) else { fatalError("Selector `\(selector)` does not exist in class `\(String(describing: objcClass))`.") } @@ -79,16 +79,7 @@ extension NSObject { } else { signatureCache = SignatureCache() selectorCache = SelectorCache() - } - - selectorCache.allocate(selector) - if signatureCache[selector] == nil { - let signature = NSMethodSignature.signature(withObjCTypes: typeEncoding) - signatureCache[selector] = signature - } - - if !isSwizzled { objc_setAssociatedObject(subclass, AssociationKey.signatureCache, signatureCache, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) objc_setAssociatedObject(subclass, AssociationKey.selectorCache, selectorCache, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) objc_setAssociatedObject(subclass, AssociationKey.intercepted, true, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) @@ -97,6 +88,13 @@ extension NSObject { setupMethodSignatureCaching(subclass, signatureCache) } + selectorCache.cache(selector) + + if signatureCache[selector] == nil { + let signature = NSMethodSignature.signature(withObjCTypes: typeEncoding) + signatureCache[selector] = signature + } + // If an immediate implementation of the selector is found in the // runtime subclass the first time the selector is intercepted, // preserve the implementation. @@ -104,12 +102,9 @@ extension NSObject { // Example: KVO setters if the instance is swizzled by KVO before RAC // does. if !class_respondsToSelector(subclass, interopAlias) { - let immediateMethod = class_getImmediateMethod(subclass, selector) - - let immediateImpl: IMP? = immediateMethod.flatMap { - let immediateImpl = method_getImplementation($0) - return immediateImpl.flatMap { $0 != _rac_objc_msgForward ? $0 : nil } - } + let immediateImpl = class_getImmediateMethod(subclass, selector) + .flatMap(method_getImplementation) + .flatMap { $0 != _rac_objc_msgForward ? $0 : nil } if let impl = immediateImpl { class_addMethod(subclass, interopAlias, impl, typeEncoding) @@ -138,8 +133,8 @@ private func enableMessageForwarding(_ realClass: AnyClass, _ selectorCache: Sel typealias ForwardInvocationImpl = @convention(block) (Unmanaged, AnyObject) -> Void let newForwardInvocation: ForwardInvocationImpl = { objectRef, invocation in let selector = invocation.selector! - let alias = selectorCache[main: selector] - let interopAlias = selectorCache[interop: selector] + let alias = selectorCache.alias(for: selector) + let interopAlias = selectorCache.interopAlias(for: selector) defer { if let state = objectRef.takeUnretainedValue().associatedValue(forKey: alias.utf8Start) as! InterceptingState? { @@ -251,15 +246,13 @@ private final class SelectorCache { /// - warning: Any invocation of this method must be synchronized against the /// runtime subclass. @discardableResult - func allocate(_ selector: Selector) -> (main: Selector, interop: Selector) { + func cache(_ selector: Selector) -> (main: Selector, interop: Selector) { if let pair = map[selector] { return pair } - var copy = map let aliases = (selector.alias, selector.interopAlias) - copy[selector] = aliases - map = copy + map[selector] = aliases return aliases } @@ -268,7 +261,7 @@ private final class SelectorCache { /// /// - parameters: /// - selector: The selector alias. - subscript(main selector: Selector) -> Selector { + func alias(for selector: Selector) -> Selector { if let (main, _) = map[selector] { return main } @@ -280,7 +273,7 @@ private final class SelectorCache { /// /// - parameters: /// - selector: The selector alias. - subscript(interop selector: Selector) -> Selector { + func interopAlias(for selector: Selector) -> Selector { if let (_, interop) = map[selector] { return interop } @@ -314,9 +307,7 @@ private final class SignatureCache { } set { if map[selector] == nil { - var newMap = map - newMap[selector] = newValue - map = newMap + map[selector] = newValue } } } @@ -376,39 +367,41 @@ private func unpackInvocation(_ invocation: AnyObject) -> [Any?] { return pointer.assumingMemoryBound(to: type).pointee } + let value: Any? + switch encoding { case .char: - bridged.append(NSNumber(value: extract(CChar.self))) + value = NSNumber(value: extract(CChar.self)) case .int: - bridged.append(NSNumber(value: extract(CInt.self))) + value = NSNumber(value: extract(CInt.self)) case .short: - bridged.append(NSNumber(value: extract(CShort.self))) + value = NSNumber(value: extract(CShort.self)) case .long: - bridged.append(NSNumber(value: extract(CLong.self))) + value = NSNumber(value: extract(CLong.self)) case .longLong: - bridged.append(NSNumber(value: extract(CLongLong.self))) + value = NSNumber(value: extract(CLongLong.self)) case .unsignedChar: - bridged.append(NSNumber(value: extract(CUnsignedChar.self))) + value = NSNumber(value: extract(CUnsignedChar.self)) case .unsignedInt: - bridged.append(NSNumber(value: extract(CUnsignedInt.self))) + value = NSNumber(value: extract(CUnsignedInt.self)) case .unsignedShort: - bridged.append(NSNumber(value: extract(CUnsignedShort.self))) + value = NSNumber(value: extract(CUnsignedShort.self)) case .unsignedLong: - bridged.append(NSNumber(value: extract(CUnsignedLong.self))) + value = NSNumber(value: extract(CUnsignedLong.self)) case .unsignedLongLong: - bridged.append(NSNumber(value: extract(CUnsignedLongLong.self))) + value = NSNumber(value: extract(CUnsignedLongLong.self)) case .float: - bridged.append(NSNumber(value: extract(CFloat.self))) + value = NSNumber(value: extract(CFloat.self)) case .double: - bridged.append(NSNumber(value: extract(CDouble.self))) + value = NSNumber(value: extract(CDouble.self)) case .bool: - bridged.append(NSNumber(value: extract(CBool.self))) + value = NSNumber(value: extract(CBool.self)) case .object: - bridged.append(extract((AnyObject?).self)) + value = extract((AnyObject?).self) case .type: - bridged.append(extract((AnyClass?).self)) + value = extract((AnyClass?).self) case .selector: - bridged.append(extract((Selector?).self)) + value = extract((Selector?).self) case .undefined: var size = 0, alignment = 0 NSGetSizeAndAlignment(rawEncoding, &size, &alignment) @@ -416,8 +409,10 @@ private func unpackInvocation(_ invocation: AnyObject) -> [Any?] { defer { buffer.deallocate(bytes: size, alignedTo: alignment) } invocation.copy(to: buffer, forArgumentAt: Int(position)) - bridged.append(NSValue(bytes: buffer, objCType: rawEncoding)) + value = NSValue(bytes: buffer, objCType: rawEncoding) } + + bridged.append(value) } return bridged From da245122f299e405de21025f2d7f9655efb80cf8 Mon Sep 17 00:00:00 2001 From: Anders Date: Sat, 24 Dec 2016 21:42:38 +0100 Subject: [PATCH 0687/1028] Gardening in `NSObject+Association` and related call sites. --- ReactiveCocoa/NSObject+Association.swift | 122 +++++++++++++++----- ReactiveCocoa/NSObject+Intercepting.swift | 31 +++-- ReactiveCocoa/NSObject+Lifetime.swift | 23 +++- ReactiveCocoa/ObjC+RuntimeSubclassing.swift | 18 ++- 4 files changed, 143 insertions(+), 51 deletions(-) diff --git a/ReactiveCocoa/NSObject+Association.swift b/ReactiveCocoa/NSObject+Association.swift index e618e5242a..798a521e47 100644 --- a/ReactiveCocoa/NSObject+Association.swift +++ b/ReactiveCocoa/NSObject+Association.swift @@ -1,27 +1,51 @@ import ReactiveSwift -internal struct AssociationKey { - private static let contiguous = UnsafeMutablePointer.allocate(capacity: 6) +internal struct AssociationKey { + fileprivate let address: UnsafeRawPointer + fileprivate let `default`: Value! - /// Nullable. `true` indicates the runtime subclass has already been prepared for method - /// interception. - static let intercepted = contiguous - - /// Holds the method signature cache of the runtime subclass. - static let signatureCache = contiguous + 1 + /// Create an ObjC association key. + /// + /// - warning: The key must be uniqued. + /// + /// - parameters: + /// - default: The default value, or `nil` to trap on undefined value. It is + /// ignored if `Value` is an optional. + init(default: Value? = nil) { + self.address = UnsafeRawPointer(UnsafeMutablePointer.allocate(capacity: 1)) + self.default = `default` + } - /// Holds the method selector cache of the runtime subclass. - static let selectorCache = contiguous + 2 + /// Create an ObjC association key from a `StaticString`. + /// + /// - precondition: `key` has a pointer representation. + /// + /// - parameters: + /// - default: The default value, or `nil` to trap on undefined value. It is + /// ignored if `Value` is an optional. + init(_ key: StaticString, default: Value? = nil) { + assert(key.hasPointerRepresentation) + self.address = UnsafeRawPointer(key.utf8Start) + self.default = `default` + } - /// Nullable. Exists only in runtime subclasses generated by external parties. - /// `true` indicates the runtime subclass has already been swizzled. - static let runtimeSubclassed = contiguous + 3 + /// Create an ObjC association key from a `Selector`. + /// + /// - parameters: + /// - default: The default value, or `nil` to trap on undefined value. It is + /// ignored if `Value` is an optional. + init(_ key: Selector, default: Value? = nil) { + self.address = UnsafeRawPointer(key.utf8Start) + self.default = `default` + } +} - /// Holds the `Lifetime` of the object. - static let lifetime = contiguous + 4 +internal struct Associations { + fileprivate let base: Base - /// Holds the `Lifetime.Token` of the object. - static let lifetimeToken = contiguous + 5 + init(_ base: Base) { + self.base = base + } } extension Reactive where Base: NSObject { @@ -36,25 +60,46 @@ extension Reactive where Base: NSObject { /// - returns: /// The associated value for the specified key. internal func associatedValue(forKey key: StaticString = #function, initial: (Base) -> T) -> T { - var value = base.associatedValue(forKey: key.utf8Start) as! T? - if value == nil { - value = initial(base) - base.setAssociatedValue(value, forKey: key.utf8Start) + let key = AssociationKey(key) + + if let value = base.associations.value(forKey: key) { + return value } - return value! + + let value = initial(base) + base.associations.setValue(value, forKey: key) + + return value } } extension NSObject { + @nonobjc internal var associations: Associations { + return Associations(self) + } +} + +extension Associations { /// Retrieve the associated value for the specified key. /// /// - parameters: /// - key: The key. /// - /// - returns: - /// The associated value, or `nil` if no value is associated with the key. - @nonobjc internal func associatedValue(forKey key: UnsafeRawPointer) -> Any? { - return objc_getAssociatedObject(self, key) + /// - returns: The associated value, or the default value if no value has been + /// associated with the key. + internal func value(forKey key: AssociationKey) -> Value { + return (objc_getAssociatedObject(base, key.address) as! Value?) ?? key.default + } + + /// Retrieve the associated value for the specified key. + /// + /// - parameters: + /// - key: The key. + /// + /// - returns: The associated value, or `nil` if no value is associated with + /// the key. + internal func value(forKey key: AssociationKey) -> Value? { + return objc_getAssociatedObject(base, key.address) as! Value? } /// Set the associated value for the specified key. @@ -62,9 +107,26 @@ extension NSObject { /// - parameters: /// - value: The value to be associated. /// - key: The key. - /// - weak: `true` if the value should be weakly referenced. `false` - /// otherwise. - @nonobjc internal func setAssociatedValue(_ value: Any?, forKey key: UnsafeRawPointer, weak: Bool = false) { - objc_setAssociatedObject(self, key, value, weak ? .OBJC_ASSOCIATION_ASSIGN : .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + internal func setValue(_ value: Value, forKey key: AssociationKey) { + objc_setAssociatedObject(base, key.address, value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } + + /// Set the associated value for the specified key. + /// + /// - parameters: + /// - value: The value to be associated. + /// - key: The key. + internal func setValue(_ value: Value?, forKey key: AssociationKey) { + objc_setAssociatedObject(base, key.address, value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } +} + +/// Set the associated value for the specified key. +/// +/// - parameters: +/// - value: The value to be associated. +/// - key: The key. +/// - address: The address of the object. +internal func unsafeSetAssociatedValue(_ value: Value?, forKey key: AssociationKey, forObjectAt address: UnsafeRawPointer) { + _rac_objc_setAssociatedObject(address, key.address, value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index a2435f01d8..12e657904f 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -2,6 +2,16 @@ import Foundation import ReactiveSwift import enum Result.NoError +/// Whether the runtime subclass has already been prepared for method +/// interception. +fileprivate let interceptedKey = AssociationKey(default: false) + +/// Holds the method signature cache of the runtime subclass. +fileprivate let signatureCacheKey = AssociationKey() + +/// Holds the method selector cache of the runtime subclass. +fileprivate let selectorCacheKey = AssociationKey() + extension Reactive where Base: NSObject { /// Create a signal which sends a `next` event at the end of every invocation /// of `selector` on the object. @@ -58,31 +68,33 @@ extension NSObject { return synchronized { let alias = selector.alias + let stateKey = AssociationKey(alias) let interopAlias = selector.interopAlias - if let state = associatedValue(forKey: alias.utf8Start) as! InterceptingState? { + if let state = associations.value(forKey: stateKey) { return state.signal } let subclass: AnyClass = swizzleClass(self) + let subclassAssociations = Associations(subclass as AnyObject) // FIXME: Compiler asks to handle a mysterious throw. try! ReactiveCocoa.synchronized(subclass) { - let isSwizzled = objc_getAssociatedObject(subclass, AssociationKey.intercepted) as! Bool? ?? false + let isSwizzled = subclassAssociations.value(forKey: interceptedKey) let signatureCache: SignatureCache let selectorCache: SelectorCache if isSwizzled { - signatureCache = objc_getAssociatedObject(subclass, AssociationKey.signatureCache) as! SignatureCache - selectorCache = objc_getAssociatedObject(subclass, AssociationKey.selectorCache) as! SelectorCache + signatureCache = subclassAssociations.value(forKey: signatureCacheKey) + selectorCache = subclassAssociations.value(forKey: selectorCacheKey) } else { signatureCache = SignatureCache() selectorCache = SelectorCache() - objc_setAssociatedObject(subclass, AssociationKey.signatureCache, signatureCache, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - objc_setAssociatedObject(subclass, AssociationKey.selectorCache, selectorCache, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - objc_setAssociatedObject(subclass, AssociationKey.intercepted, true, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + subclassAssociations.setValue(signatureCache, forKey: signatureCacheKey) + subclassAssociations.setValue(selectorCache, forKey: selectorCacheKey) + subclassAssociations.setValue(true, forKey: interceptedKey) enableMessageForwarding(subclass, selectorCache) setupMethodSignatureCaching(subclass, signatureCache) @@ -113,7 +125,7 @@ extension NSObject { } let state = InterceptingState(lifetime: reactive.lifetime) - setAssociatedValue(state, forKey: alias.utf8Start) + associations.setValue(state, forKey: stateKey) // Start forwarding the messages of the selector. _ = class_replaceMethod(subclass, selector, _rac_objc_msgForward, typeEncoding) @@ -137,7 +149,8 @@ private func enableMessageForwarding(_ realClass: AnyClass, _ selectorCache: Sel let interopAlias = selectorCache.interopAlias(for: selector) defer { - if let state = objectRef.takeUnretainedValue().associatedValue(forKey: alias.utf8Start) as! InterceptingState? { + let stateKey = AssociationKey(alias) + if let state = objectRef.takeUnretainedValue().associations.value(forKey: stateKey) { state.observer.send(value: invocation) } } diff --git a/ReactiveCocoa/NSObject+Lifetime.swift b/ReactiveCocoa/NSObject+Lifetime.swift index b8bc267024..5311d03392 100644 --- a/ReactiveCocoa/NSObject+Lifetime.swift +++ b/ReactiveCocoa/NSObject+Lifetime.swift @@ -1,11 +1,20 @@ import Foundation import ReactiveSwift +/// Holds the `Lifetime` of the object. +fileprivate let isSwizzledKey = AssociationKey(default: false) + +/// Holds the `Lifetime` of the object. +fileprivate let lifetimeKey = AssociationKey(default: nil) + +/// Holds the `Lifetime.Token` of the object. +fileprivate let lifetimeTokenKey = AssociationKey(default: nil) + extension Reactive where Base: NSObject { /// Returns a lifetime that ends when the object is deallocated. @nonobjc public var lifetime: Lifetime { return base.synchronized { - if let lifetime = base.associatedValue(forKey: AssociationKey.lifetime) as! Lifetime? { + if let lifetime = base.associations.value(forKey: lifetimeKey) { return lifetime } @@ -13,14 +22,16 @@ extension Reactive where Base: NSObject { let lifetime = Lifetime(token) let objcClass: AnyClass = (base as AnyObject).objcClass + let objcClassAssociations = Associations(objcClass as AnyObject) + let deallocSelector = sel_registerName("dealloc")! // Swizzle `-dealloc` so that the lifetime token is released at the // beginning of the deallocation chain, and only after the KVO `-dealloc`. synchronized(objcClass) { // Swizzle the class only if it has not been swizzled before. - if objc_getAssociatedObject(objcClass, AssociationKey.lifetime) == nil { - objc_setAssociatedObject(objcClass, AssociationKey.lifetime, true, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + if !objcClassAssociations.value(forKey: isSwizzledKey) { + objcClassAssociations.setValue(true, forKey: isSwizzledKey) var existingImpl: IMP? = nil @@ -30,7 +41,7 @@ extension Reactive where Base: NSObject { // mess with the object deallocation chain. // Release the lifetime token. - _rac_objc_setAssociatedObject(objectRef, AssociationKey.lifetimeToken, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + unsafeSetAssociatedValue(nil, forKey: lifetimeTokenKey, forObjectAt: objectRef) let impl: IMP @@ -64,8 +75,8 @@ extension Reactive where Base: NSObject { } } - base.setAssociatedValue(token, forKey: AssociationKey.lifetimeToken) - base.setAssociatedValue(lifetime, forKey: AssociationKey.lifetime) + base.associations.setValue(token, forKey: lifetimeTokenKey) + base.associations.setValue(lifetime, forKey: lifetimeKey) return lifetime } diff --git a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift index c9d4ffcac7..0b8caa4065 100644 --- a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift +++ b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift @@ -1,5 +1,12 @@ import ReactiveSwift +/// Whether the runtime subclass has already been swizzled. +fileprivate let runtimeSubclassedKey = AssociationKey(default: false) + +/// A known RAC runtime subclass of the instance. `nil` if the runtime subclass +/// has not been requested for the instance before. +fileprivate let knownRuntimeSubclassKey = AssociationKey(default: nil) + /// ISA-swizzle the class of the supplied instance. /// /// - note: If the instance has already been isa-swizzled, the swizzling happens @@ -11,23 +18,22 @@ import ReactiveSwift /// - returns: /// The runtime subclass of the perceived class of the instance. internal func swizzleClass(_ instance: NSObject) -> AnyClass { - let key = (#function as StaticString).utf8Start - - if let knownSubclass = instance.associatedValue(forKey: key) as! AnyClass? { + if let knownSubclass = instance.associations.value(forKey: knownRuntimeSubclassKey) { return knownSubclass } let perceivedClass: AnyClass = instance.objcClass let realClass: AnyClass = object_getClass(instance)! + let realClassAssociations = Associations(realClass as AnyObject) if perceivedClass != realClass { // If the class is already lying about what it is, it's probably a KVO // dynamic subclass or something else that we shouldn't subclass at runtime. synchronized(realClass) { - let isSwizzled = objc_getAssociatedObject(realClass, AssociationKey.runtimeSubclassed) as! Bool? ?? false + let isSwizzled = realClassAssociations.value(forKey: runtimeSubclassedKey) if !isSwizzled { replaceGetClass(in: realClass, decoy: perceivedClass) - objc_setAssociatedObject(realClass, AssociationKey.runtimeSubclassed, true, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + realClassAssociations.setValue(true, forKey: runtimeSubclassedKey) } } @@ -46,7 +52,7 @@ internal func swizzleClass(_ instance: NSObject) -> AnyClass { } object_setClass(instance, subclass) - instance.setAssociatedValue(subclass, forKey: key) + instance.associations.setValue(subclass, forKey: knownRuntimeSubclassKey) return subclass } } From 7554d6b89cca040426291984e44910e9ed4a5d6e Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 27 Dec 2016 23:05:46 +0100 Subject: [PATCH 0688/1028] 5.0.0-rc.1 --- README.md | 6 +++--- ReactiveCocoa.podspec | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4ead10f8d4..20cfa0de6a 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ If you use [Carthage][] to manage your dependencies, simply add ReactiveCocoa to your `Cartfile`: ``` -github "ReactiveCocoa/ReactiveCocoa" "5.0.0-alpha.6" +github "ReactiveCocoa/ReactiveCocoa" "5.0.0-rc.1" ``` If you use Carthage to build your dependencies, make sure you have added `ReactiveCocoa.framework`, `ReactiveSwift.framework`, and `Result.framework` to the "_Linked Frameworks and Libraries_" section of your target, and have included them in your Carthage framework copying build phase. @@ -107,7 +107,7 @@ If you use [CocoaPods][] to manage your dependencies, simply add ReactiveCocoa to your `Podfile`: ``` -pod 'ReactiveCocoa', '5.0.0-alpha.6' +pod 'ReactiveCocoa', '5.0.0-rc.1' ``` #### Git submodule @@ -134,7 +134,7 @@ If you need any help, please visit our [GitHub issues][] or [Stack Overflow][]. ### In Development: ReactiveCocoa 5.0 It targets Swift 3.0.x and ReactiveSwift 1.0. The tentative schedule of a Gold Master release is January 2017. -A Release Candidate would be released after an important bug fix is cleared, which should happen no later than Christmas 2016. +[Release Candidate 1](https://github.com/ReactiveCocoa/ReactiveCocoa/releases/tag/5.0.0-rc.1/) has been released. ### Plan of Record #### ReactiveCocoa 6.0 diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 3eb3a820ad..08e2885e1b 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "5.0.0-alpha.6" + s.version = "5.0.0-rc.1" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. From 98ebab15d5d205d70e570f439c916cef32b16a61 Mon Sep 17 00:00:00 2001 From: Anders Date: Sat, 31 Dec 2016 21:58:10 +0100 Subject: [PATCH 0689/1028] Update the README layout to match ReactiveSwift. [skip ci] --- Logo/PNG/Docs.png | Bin 0 -> 10021 bytes Logo/PNG/JoinSlack.png | Bin 0 -> 9700 bytes Logo/PNG/logo-unpadded.png | Bin 0 -> 81935 bytes Logo/PNG/logo.png | Bin 81935 -> 58185 bytes README.md | 9 ++++++--- 5 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 Logo/PNG/Docs.png create mode 100644 Logo/PNG/JoinSlack.png create mode 100644 Logo/PNG/logo-unpadded.png diff --git a/Logo/PNG/Docs.png b/Logo/PNG/Docs.png new file mode 100644 index 0000000000000000000000000000000000000000..fb17387014e858560a039f37eb088b8c35ce9ed4 GIT binary patch literal 10021 zcmXY11z1$y(_WC4?(XjH5)>AYT2fNFJC;U%l(c{dA|bGJgLFx`grqb}cS|>X*Z=c9 z&+a~V=iYnHoS75v%sVk!8p=3Wlvp4T2uB6{8Ug|#T>#hd7|6gcYTfUCKoI7jsHo)y zQC6T=d7~&QAR#KsBgiWN0v}L>Y@Xh&^i~%GKY-P}vYVI*fc?vkkmf zb2-22G&XWgTO?s|eP~_fTh(vTj|Hpnni7ds%$~cJt6XK}oCl3wXYK4|?R;SQqzef+ zqK8dVjL02%aHFMDU-HfE!f4P<8QvJM1QSS3xp<$4C-0y z59ZaXW}#4I<=Ykp=vuzLD2Zp5sDBQH&>3*7DqcE zGAZJ^b&BS{V+iQXzoh*TOf*`}Y2-)7+xUG$8^<9p-u3pTLbO8#`g>iMfz+e>rDRnBAB9-EqCo$kq8!riV%#vJ?UG$-R9Rj*qhC4G;ZixH z?Qs%@g|%Ms;_H84dK57IK}cwyc{9RPfqX6TCP?lnIk07O`L*BK^1bZIy`^|!fl=Wk zeIzA^2)OVSC(a~2teXIkbz*}+Q;OsY-2pa^t9-w65yY2q%~a3OZ5MfN@Qxm z8k{aMiJZdkM*@A1Z6>c(ff%9Z~ioy zq{+5g=V-M`t03P~x2S4j3kd`ouiIu&Mk3S|>oZ_$E*CBVhglCfnh}CPA7Y$u2eh@a zX>ZF=p*V>!EZzJM6WSn92@|=(74jy%d{)^-zX3xt*A%-s?nWWd4~RF)#HDOp&C1c8 zPcOem(7xsc2xP?{@p@(;(QRR{2nDJl!Kpv>!w68yQG;DxLq9E7U&VcP6>JhUq}!m+ z1IR_Jdlg4#(!9H=ULI3YZw-Ee=-i6{&1ew?;d(ZSkggfYnTF4^hIq zO(D1)zykVdX2Z$MA$Fs4w`x`vL7)o)y>GKb7zC7l{;S8(t1xD_sN(?T{ zMU;>s;@-dE2L#dX5wREFwn@58^P=>EfR{Q`I4*5j^-!)EcRwM4KHSA;Ts?_4rPq-?WrC0b4ix$I~q%B;#`M>k?DUjo&o6iIK73m*zuH zaWdT>62L67FXP$~sOk}QdiuxVs@j}N; z^-BC1g@fBK^;(icU~T)d zoG(X)B^zJJ^X=gr=Jt&vFy+rJ#G6m%J8hn;j`~u5eqZ&-4V_@D5n;OYfB5^8-PQC$Rnfm@0B{0cXBp-Be=~QHIPwCp+xUs)h(TS_VSWzXt?S5 zRH|MNc=V3|5rR$z9Mk3d1}g5bU=a4IJASI~xPUXa|71imX_h zpIhvTk3H&1D+8|f#=s(+)8!{0Y}w?+ z2$b<(%xXv*qpXr|eAPXcE=g@l3_b!0bcyt$TjqM2YiZ+C3oSQ!>(+_=o>$e?Wv z>;E?$CT@N`H_}^h%%!F0dVJgpN3|3P7Xe&GBtVVGHZ|GrMAlPYc~jjfP*MdX0xKg9*K$QntSROV;(<*=nioX4r5p#_!ALuSGje_{KHvWA8R zAJZ9}4`s1ZS7Ub~at?I4vL4d5o4d&yvQ~|%U&4;MWms;HJF^#m2dCYUAhtZ`2RGnv zC3NWVUqkn1I(DMF^DM?Gk-2r5lby|;xoS6m9%q{dUQDX#_Kb%T647Zl^Kw zO~U2!jD?>V+mKyICP6YW?e0)ciPOqrZrKyPVZCSm53rE9&FiZ?bV7>`kjb^U)4rq) z`b1OX!uzL3g3@(3(^7hnEn^A_QhyG`2Cyc<3rWvzk-er5O?$3ZX{je?ka@e&`uqD| z+Nzt^`er9p)BW*5@;S;4zkf#5t3l_+|9A~Ahi|u;X!y>+YHRya^qgkV(W14+E-szc zcXthSbzxit7$`^)(|7U>wKC1Iy#W3ay?FyCD4sM>ZZxCABCzNM&e>HP{UvLt?xX` z$U-HLe@%12{+R7-(@OZgzu7%~J4FDZBuLioI+?rYXL(l$K}C!HspaSrw(9D@kL~5* z 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 literal 0 HcmV?d00001 diff --git a/Logo/PNG/JoinSlack.png b/Logo/PNG/JoinSlack.png new file mode 100644 index 0000000000000000000000000000000000000000..dfcb7bf9ff27c3b5ffff50db2b919e52e132534c GIT binary patch literal 9700 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 literal 0 HcmV?d00001 diff --git a/Logo/PNG/logo-unpadded.png b/Logo/PNG/logo-unpadded.png new file mode 100644 index 0000000000000000000000000000000000000000..c14eb29fdbaa8870a3447f4e02ba80e3ab319fd7 GIT binary patch literal 81935 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 Z_z$fQJGywh7kdBz002ovPDHLkV1h!xmH+?% literal 0 HcmV?d00001 diff --git a/Logo/PNG/logo.png b/Logo/PNG/logo.png index c14eb29fdbaa8870a3447f4e02ba80e3ab319fd7..d64e8ecdf382ff3611b475b643afd58bee6362f0 100644 GIT binary patch literal 58185 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 Z_z$fQJGywh7kdBz002ovPDHLkV1h!xmH+?% diff --git a/README.md b/README.md index 20cfa0de6a..8d150eeab7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ -![](Logo/header.png) -#### Reactive extensions to Cocoa frameworks, built on top of [ReactiveSwift][]. - +

    +
    [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](#carthage) [![CocoaPods compatible](https://img.shields.io/cocoapods/v/ReactiveCocoa.svg)](#cocoapods) [![GitHub release](https://img.shields.io/github/release/ReactiveCocoa/ReactiveCocoa.svg)](https://github.com/ReactiveCocoa/ReactiveCocoa/releases) ![Swift 3.0.x](https://img.shields.io/badge/Swift-3.0.x-orange.svg) ![platforms](https://img.shields.io/badge/platforms-iOS%20%7C%20OS%20X%20%7C%20watchOS%20%7C%20tvOS%20-lightgrey.svg) ⚠️ [Looking for the Objective-C API?][] ⚠️ [Still using Swift 2.x?][] From bae9e54664e779ac5f1390b3572ac0927b28505b Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Mon, 2 Jan 2017 11:17:20 +0900 Subject: [PATCH 0690/1028] Update ReactiveSwift to 1.0.0 --- Cartfile.resolved | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- ReactiveCocoa.podspec | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cartfile.resolved b/Cartfile.resolved index 27bbccc431..b18139e1fe 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -2,4 +2,4 @@ github "Quick/Nimble" "v5.1.1" github "Quick/Quick" "v1.0.0" github "antitypical/Result" "3.1.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" -github "ReactiveCocoa/ReactiveSwift" "1.0.0-rc.3" +github "ReactiveCocoa/ReactiveSwift" "1.0.0" diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index ec47295258..7920f196fd 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit ec47295258656a647a53ea7f8b08580f6cecd9d5 +Subproject commit 7920f196fd5073f4c98e03fa40b4e4b72470b935 diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 08e2885e1b..b81706e596 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -23,5 +23,5 @@ Pod::Spec.new do |s| s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_map = "ReactiveCocoa/module.modulemap" - s.dependency 'ReactiveSwift', '~> 1.0.0-rc.3' + s.dependency 'ReactiveSwift', '~> 1.0.0' end From ee78f8f16546d7765a3ebc6649303334f8228d23 Mon Sep 17 00:00:00 2001 From: NachoSoto Date: Fri, 6 Jan 2017 11:02:48 -0800 Subject: [PATCH 0691/1028] Small improvement to CocoaAction docs Added back-ticks around `Action` to indicate that it's a type. --- ReactiveCocoa/CocoaAction.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa/CocoaAction.swift b/ReactiveCocoa/CocoaAction.swift index 067292bb70..42135a601d 100644 --- a/ReactiveCocoa/CocoaAction.swift +++ b/ReactiveCocoa/CocoaAction.swift @@ -2,7 +2,7 @@ import Foundation import ReactiveSwift import enum Result.NoError -/// CocoaAction wraps an Action for use by a UI control (such as `NSControl` or +/// CocoaAction wraps an `Action` for use by a UI control (such as `NSControl` or /// `UIControl`). public final class CocoaAction: NSObject { /// The selector for message senders. From 44568ca3afb0c9e646090702e30d757c33b8f98d Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Mon, 9 Jan 2017 16:47:35 +0100 Subject: [PATCH 0692/1028] Minor changes to the ObjC-to-Swift table in CHANGELOG. [skip ci] --- CHANGELOG.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bea1b2fca..b4f41513ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -273,9 +273,9 @@ public final class MyController { rac_liftSelector:withSignals: - Apply combineLatest to your signals, and pass the method as the action to observeValues. + Apply combineLatest to your signals, and invoke the method in observeValues.

    -

    Signal.combineLatest([signal1, signal2])
    +
    Signal.combineLatest(signal1, signal2)
     	.take(during: self.reactive.lifetime)
     	.observeValues { [weak self] in self?.perform(first: $0, second: $1) }

    @@ -301,8 +301,8 @@ public final class MyController { - Control value changes, e.g. textField.rac_text - Discover control value signals via .reactive on UI components. + Control value changes, e.g. textField.rac_textSignal() + Discover control value `Signal`s via .reactive on UI components.

    viewModel.searchString <~ textField.reactive.textValues

    @@ -311,9 +311,9 @@ public final class MyController { UIControl.reactive.trigger(for:) - rac_command` + rac_command Discover action binding APIs via .reactive on UI components. -

    button.pressed = CocoaAction(viewModel.submitAction)

    +

    button.reactive.pressed = CocoaAction(viewModel.submitAction)

    From 29bb1c0885b5419d547209df41d0353a7f9e3870 Mon Sep 17 00:00:00 2001 From: David Ohayon Date: Wed, 11 Jan 2017 11:20:46 -0500 Subject: [PATCH 0693/1028] add pressed signal for nsbutton --- ReactiveCocoa.xcodeproj/project.pbxproj | 4 ++++ ReactiveCocoa/AppKit/NSButton.swift | 11 +++++++++++ ReactiveCocoa/AppKit/NSControl.swift | 2 +- 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 ReactiveCocoa/AppKit/NSButton.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index fceca4d5c9..e7dcdaf5de 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 006518761E26865800C3139A /* NSButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 006518751E26865800C3139A /* NSButton.swift */; }; 3BCAAC7A1DEE19BC00B30335 /* UIRefreshControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BCAAC791DEE19BC00B30335 /* UIRefreshControl.swift */; }; 3BCAAC7D1DEE1A2D00B30335 /* UIRefreshControlSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BCAAC7B1DEE1A2300B30335 /* UIRefreshControlSpec.swift */; }; 419139461DB910570043C9D1 /* UIGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 419139431DB910570043C9D1 /* UIGestureRecognizer.swift */; }; @@ -306,6 +307,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 006518751E26865800C3139A /* NSButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSButton.swift; sourceTree = ""; }; 3BCAAC791DEE19BC00B30335 /* UIRefreshControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIRefreshControl.swift; sourceTree = ""; }; 3BCAAC7B1DEE1A2300B30335 /* UIRefreshControlSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIRefreshControlSpec.swift; sourceTree = ""; }; 419139431DB910570043C9D1 /* UIGestureRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIGestureRecognizer.swift; sourceTree = ""; }; @@ -559,6 +561,7 @@ 9ADE4A951DA6F018005C2AC8 /* NSTextField.swift */, 9A6AAA251DB8F5280013AAEA /* ReusableComponents.swift */, D9558AB71DFF805A003254E1 /* NSPopUpButton.swift */, + 006518751E26865800C3139A /* NSButton.swift */, ); path = AppKit; sourceTree = ""; @@ -1237,6 +1240,7 @@ 9A1D05E01D93E99100ACF44C /* NSObject+Association.swift in Sources */, 538DCB791DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */, 9AE7C2A41DDD7F5100F7534C /* ObjC+Messages.swift in Sources */, + 006518761E26865800C3139A /* NSButton.swift in Sources */, 9A54A21B1DE00D09001739B3 /* ObjC+Selector.swift in Sources */, 9AA0BD8A1DDE153A00531FCF /* ObjC+Constants.swift in Sources */, 9A6AAA261DB8F5280013AAEA /* ReusableComponents.swift in Sources */, diff --git a/ReactiveCocoa/AppKit/NSButton.swift b/ReactiveCocoa/AppKit/NSButton.swift new file mode 100644 index 0000000000..b6dffbce69 --- /dev/null +++ b/ReactiveCocoa/AppKit/NSButton.swift @@ -0,0 +1,11 @@ +import ReactiveSwift +import enum Result.NoError +import AppKit + +extension Reactive where Base: NSButton { + + public var pressed: Signal { + return trigger.map { [unowned base = self.base] in base } + } + +} diff --git a/ReactiveCocoa/AppKit/NSControl.swift b/ReactiveCocoa/AppKit/NSControl.swift index 3769360336..dbdf35dde4 100644 --- a/ReactiveCocoa/AppKit/NSControl.swift +++ b/ReactiveCocoa/AppKit/NSControl.swift @@ -92,7 +92,7 @@ extension Reactive where Base: NSControl { /// A trigger signal that sends a `next` event for every action messages /// received from the control, and completes when the control deinitializes. - private var trigger: Signal<(), NoError> { + internal var trigger: Signal<(), NoError> { return associatedValue { base in let (signal, observer) = Signal<(), NoError>.pipe() From 0c1ac132e228db029d363a2064a1816307d7f020 Mon Sep 17 00:00:00 2001 From: David Ohayon Date: Wed, 11 Jan 2017 13:25:02 -0500 Subject: [PATCH 0694/1028] Use CocoaAction --- ReactiveCocoa/AppKit/NSButton.swift | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoa/AppKit/NSButton.swift b/ReactiveCocoa/AppKit/NSButton.swift index b6dffbce69..5b0c804289 100644 --- a/ReactiveCocoa/AppKit/NSButton.swift +++ b/ReactiveCocoa/AppKit/NSButton.swift @@ -4,8 +4,24 @@ import AppKit extension Reactive where Base: NSButton { - public var pressed: Signal { - return trigger.map { [unowned base = self.base] in base } + internal var associatedAction: Atomic<(action: CocoaAction, disposable: Disposable?)?> { + return associatedValue { _ in Atomic(nil) } } + public var pressed: CocoaAction? { + get { + return associatedAction.value?.action + } + + nonmutating set { + base.target = newValue + base.action = newValue.map { _ in CocoaAction.selector } + associatedAction + .swap(newValue.map { action in + let disposable = isEnabled <~ action.isEnabled + return (action, disposable) + })? + .disposable?.dispose() + } + } } From 468f586d5e8f6bb5a9ee03ac89d06d4b972f0b8d Mon Sep 17 00:00:00 2001 From: David Ohayon Date: Wed, 11 Jan 2017 13:26:12 -0500 Subject: [PATCH 0695/1028] go back to private trigger --- ReactiveCocoa/AppKit/NSControl.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa/AppKit/NSControl.swift b/ReactiveCocoa/AppKit/NSControl.swift index dbdf35dde4..3769360336 100644 --- a/ReactiveCocoa/AppKit/NSControl.swift +++ b/ReactiveCocoa/AppKit/NSControl.swift @@ -92,7 +92,7 @@ extension Reactive where Base: NSControl { /// A trigger signal that sends a `next` event for every action messages /// received from the control, and completes when the control deinitializes. - internal var trigger: Signal<(), NoError> { + private var trigger: Signal<(), NoError> { return associatedValue { base in let (signal, observer) = Signal<(), NoError>.pipe() From 15e31b529e356e3ec7fb57f3c2c0d362b82e781a Mon Sep 17 00:00:00 2001 From: Anders Date: Wed, 11 Jan 2017 21:34:21 +0100 Subject: [PATCH 0696/1028] Mark `unsafeCocoaAction` as unavailable. --- ReactiveCocoa.xcodeproj/project.pbxproj | 10 ++++++++++ ReactiveCocoa/Deprecations+Removals.swift | 6 ++++++ 2 files changed, 16 insertions(+) create mode 100644 ReactiveCocoa/Deprecations+Removals.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index fceca4d5c9..67d0c3c9c6 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -170,6 +170,10 @@ 9AA0BD9B1DDE7A2200531FCF /* ObjCRuntimeAliases.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD971DDE7A2200531FCF /* ObjCRuntimeAliases.m */; }; 9AAD49881DED2C350068EC9B /* UIKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AAD49871DED2C350068EC9B /* UIKeyboard.swift */; }; 9AAD498A1DED2F380068EC9B /* UIKeyboardSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AAD49891DED2F380068EC9B /* UIKeyboardSpec.swift */; }; + 9AB15C7A1E26CD9A00997378 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AB15C791E26CD9A00997378 /* Deprecations+Removals.swift */; }; + 9AB15C7B1E26CD9A00997378 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AB15C791E26CD9A00997378 /* Deprecations+Removals.swift */; }; + 9AB15C7C1E26CD9A00997378 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AB15C791E26CD9A00997378 /* Deprecations+Removals.swift */; }; + 9AB15C7D1E26CD9A00997378 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AB15C791E26CD9A00997378 /* Deprecations+Removals.swift */; }; 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; 9AD0F06B1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; 9AD0F06C1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; @@ -382,6 +386,7 @@ 9AA0BD971DDE7A2200531FCF /* ObjCRuntimeAliases.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ObjCRuntimeAliases.m; sourceTree = ""; }; 9AAD49871DED2C350068EC9B /* UIKeyboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIKeyboard.swift; sourceTree = ""; }; 9AAD49891DED2F380068EC9B /* UIKeyboardSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIKeyboardSpec.swift; sourceTree = ""; }; + 9AB15C791E26CD9A00997378 /* Deprecations+Removals.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deprecations+Removals.swift"; sourceTree = ""; }; 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+KeyValueObserving.swift"; sourceTree = ""; }; 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaAction.swift; sourceTree = ""; }; 9ADE4A881DA6D206005C2AC8 /* NSControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSControl.swift; sourceTree = ""; }; @@ -719,6 +724,7 @@ 9A1D05EB1D93E9F100ACF44C /* UIKit */, 538DCB761DCA5E1600332880 /* Shared */, D04725ED19E49ED7006002AA /* Supporting Files */, + 9AB15C791E26CD9A00997378 /* Deprecations+Removals.swift */, ); path = ReactiveCocoa; sourceTree = ""; @@ -1131,6 +1137,7 @@ 9A1D06131D93EA0100ACF44C /* UIBarItem.swift in Sources */, 9A1D061F1D93EA0100ACF44C /* UITextField.swift in Sources */, 4ABEFE261DCFCF640066A8C2 /* UICollectionView.swift in Sources */, + 9AB15C7D1E26CD9A00997378 /* Deprecations+Removals.swift in Sources */, 9ADE4A7F1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, 9AE7C2A71DDD7F5100F7534C /* ObjC+Messages.swift in Sources */, 9A1D06201D93EA0100ACF44C /* UITextView.swift in Sources */, @@ -1208,6 +1215,7 @@ 9A2E42601DAA6737006D909F /* CocoaTarget.swift in Sources */, 9A9DFEE51DA7B5500039EE1B /* NSObject+Intercepting.swift in Sources */, 9A54A21D1DE00D09001739B3 /* ObjC+Selector.swift in Sources */, + 9AB15C7C1E26CD9A00997378 /* Deprecations+Removals.swift in Sources */, 9AA0BD7E1DDE03DE00531FCF /* ObjC+Runtime.swift in Sources */, 9AA0BD9A1DDE7A2200531FCF /* ObjCRuntimeAliases.m in Sources */, 9ADE4A931DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, @@ -1246,6 +1254,7 @@ 9ADE4A961DA6F018005C2AC8 /* NSTextField.swift in Sources */, 9AA0BD981DDE7A2200531FCF /* ObjCRuntimeAliases.m in Sources */, 9A2E425E1DAA6737006D909F /* CocoaTarget.swift in Sources */, + 9AB15C7A1E26CD9A00997378 /* Deprecations+Removals.swift in Sources */, 9ADE4A891DA6D206005C2AC8 /* NSControl.swift in Sources */, 9AF0EA751D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, D9558AB81DFF805A003254E1 /* NSPopUpButton.swift in Sources */, @@ -1288,6 +1297,7 @@ 4ABEFE251DCFCF630066A8C2 /* UICollectionView.swift in Sources */, 9A1D06011D93EA0000ACF44C /* UIBarItem.swift in Sources */, 9A1D060D1D93EA0000ACF44C /* UITextField.swift in Sources */, + 9AB15C7B1E26CD9A00997378 /* Deprecations+Removals.swift in Sources */, 9A6AAA231DB8F51C0013AAEA /* ReusableComponents.swift in Sources */, 9A1D060E1D93EA0000ACF44C /* UITextView.swift in Sources */, BFCF775F1DFAD8A50058006E /* UISearchBar.swift in Sources */, diff --git a/ReactiveCocoa/Deprecations+Removals.swift b/ReactiveCocoa/Deprecations+Removals.swift new file mode 100644 index 0000000000..2a3e2e848f --- /dev/null +++ b/ReactiveCocoa/Deprecations+Removals.swift @@ -0,0 +1,6 @@ +import ReactiveSwift + +extension Action { + @available(*, unavailable, message:"Use the `CocoaAction` initializers instead.") + public var unsafeCocoaAction: CocoaAction { fatalError() } +} From 0c8217929388ce60ecad91b3a8e72d269377fa54 Mon Sep 17 00:00:00 2001 From: David Ohayon Date: Wed, 11 Jan 2017 16:13:38 -0500 Subject: [PATCH 0697/1028] add tests --- ReactiveCocoa.xcodeproj/project.pbxproj | 4 ++ ReactiveCocoa/AppKit/NSButton.swift | 8 ++-- ReactiveCocoa/AppKit/NSControl.swift | 2 +- ReactiveCocoaTests/AppKit/NSButtonSpec.swift | 40 ++++++++++++++++++++ 4 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 ReactiveCocoaTests/AppKit/NSButtonSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index e7dcdaf5de..a6aeb13fbd 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 004FD0071E26CDB300A03A82 /* NSButtonSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 004FD0061E26CDB300A03A82 /* NSButtonSpec.swift */; }; 006518761E26865800C3139A /* NSButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 006518751E26865800C3139A /* NSButton.swift */; }; 3BCAAC7A1DEE19BC00B30335 /* UIRefreshControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BCAAC791DEE19BC00B30335 /* UIRefreshControl.swift */; }; 3BCAAC7D1DEE1A2D00B30335 /* UIRefreshControlSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BCAAC7B1DEE1A2300B30335 /* UIRefreshControlSpec.swift */; }; @@ -307,6 +308,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 004FD0061E26CDB300A03A82 /* NSButtonSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSButtonSpec.swift; sourceTree = ""; }; 006518751E26865800C3139A /* NSButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSButton.swift; sourceTree = ""; }; 3BCAAC791DEE19BC00B30335 /* UIRefreshControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIRefreshControl.swift; sourceTree = ""; }; 3BCAAC7B1DEE1A2300B30335 /* UIRefreshControlSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIRefreshControlSpec.swift; sourceTree = ""; }; @@ -630,6 +632,7 @@ 4ABEFE2C1DCFD0180066A8C2 /* NSTableViewSpec.swift */, 9A6AAA271DB8F7EB0013AAEA /* ReusableComponentsSpec.swift */, D9558AB51DFF7B90003254E1 /* NSPopUpButtonSpec.swift */, + 004FD0061E26CDB300A03A82 /* NSButtonSpec.swift */, ); path = AppKit; sourceTree = ""; @@ -1274,6 +1277,7 @@ 9ADFE5A11DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */, 4ABEFE331DCFD0630066A8C2 /* NSCollectionViewSpec.swift in Sources */, B696FB811A7640C00075236D /* TestError.swift in Sources */, + 004FD0071E26CDB300A03A82 /* NSButtonSpec.swift in Sources */, D9558AB91DFF86C0003254E1 /* NSPopUpButtonSpec.swift in Sources */, BFA6B94D1A7604D400C846D1 /* SignalProducerNimbleMatchers.swift in Sources */, 9A24A8451DE142A400987AF9 /* SwizzlingSpec.swift in Sources */, diff --git a/ReactiveCocoa/AppKit/NSButton.swift b/ReactiveCocoa/AppKit/NSButton.swift index 5b0c804289..c3c5755e27 100644 --- a/ReactiveCocoa/AppKit/NSButton.swift +++ b/ReactiveCocoa/AppKit/NSButton.swift @@ -14,11 +14,13 @@ extension Reactive where Base: NSButton { } nonmutating set { - base.target = newValue - base.action = newValue.map { _ in CocoaAction.selector } associatedAction .swap(newValue.map { action in - let disposable = isEnabled <~ action.isEnabled + let disposable = CompositeDisposable() + disposable += isEnabled <~ action.isEnabled + disposable += trigger.observeValues { [unowned base = self.base] in + action.execute(base) + } return (action, disposable) })? .disposable?.dispose() diff --git a/ReactiveCocoa/AppKit/NSControl.swift b/ReactiveCocoa/AppKit/NSControl.swift index 3769360336..dbdf35dde4 100644 --- a/ReactiveCocoa/AppKit/NSControl.swift +++ b/ReactiveCocoa/AppKit/NSControl.swift @@ -92,7 +92,7 @@ extension Reactive where Base: NSControl { /// A trigger signal that sends a `next` event for every action messages /// received from the control, and completes when the control deinitializes. - private var trigger: Signal<(), NoError> { + internal var trigger: Signal<(), NoError> { return associatedValue { base in let (signal, observer) = Signal<(), NoError>.pipe() diff --git a/ReactiveCocoaTests/AppKit/NSButtonSpec.swift b/ReactiveCocoaTests/AppKit/NSButtonSpec.swift new file mode 100644 index 0000000000..5c34564640 --- /dev/null +++ b/ReactiveCocoaTests/AppKit/NSButtonSpec.swift @@ -0,0 +1,40 @@ +import Quick +import Nimble +import ReactiveSwift +import ReactiveCocoa +import AppKit +import enum Result.NoError + +class NSButtonSpec: QuickSpec { + override func spec() { + var button: NSButton! + weak var _button: NSButton? + + beforeEach { + button = NSButton(frame: .zero) + _button = button + } + + afterEach { + button = nil + expect(_button).to(beNil()) + } + + it("should execute the `pressed` action upon receiving a click") { + button.isEnabled = true + + let pressed = MutableProperty(false) + let action = Action<(), Bool, NoError> { _ in + SignalProducer(value: true) + } + + pressed <~ SignalProducer(action.values) + button.reactive.pressed = CocoaAction(action) + expect(pressed.value) == false + + button.performClick(nil) + expect(pressed.value) == true + } + } +} + From 578b41df769036849e04b9d224d8dcf65329433c Mon Sep 17 00:00:00 2001 From: David Ohayon Date: Thu, 12 Jan 2017 11:33:10 -0500 Subject: [PATCH 0698/1028] Add signal for selected NSMenuItems to NSPopUpButton --- ReactiveCocoa/AppKit/NSPopUpButton.swift | 8 ++++++++ ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/ReactiveCocoa/AppKit/NSPopUpButton.swift b/ReactiveCocoa/AppKit/NSPopUpButton.swift index 041b0e8b61..b0fd8144ef 100644 --- a/ReactiveCocoa/AppKit/NSPopUpButton.swift +++ b/ReactiveCocoa/AppKit/NSPopUpButton.swift @@ -35,4 +35,12 @@ extension Reactive where Base: NSPopUpButton { $0.selectItem(withTitle: $1 ?? "") } } + + public var selectedItems: Signal { + return self.objectValues + .map { [unowned base = self.base] _ -> NSMenuItem? in + return base.selectedItem + } + .skipNil() + } } diff --git a/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift b/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift index 2f2546c526..fb1edd7ac8 100644 --- a/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift @@ -78,6 +78,17 @@ final class NSPopUpButtonSpec: QuickSpec { expect(button.selectedItem?.title).to(beNil()) expect(button.indexOfSelectedItem) == -1 } + + it("should emit selected item changes") { + var values = [NSMenuItem]() + button.reactive.selectedItems.observeValues { values.append($0) } + + button.menu?.performActionForItem(at: 1) + button.menu?.performActionForItem(at: 99) + + let titles = values.map { $0.title } + expect(titles) == ["1", "99"] + } } } } From 6dda12f5fe6083cb6a7155434736df317dcb7907 Mon Sep 17 00:00:00 2001 From: David Ohayon Date: Thu, 12 Jan 2017 12:04:25 -0500 Subject: [PATCH 0699/1028] use modify --- ReactiveCocoa/AppKit/NSButton.swift | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/ReactiveCocoa/AppKit/NSButton.swift b/ReactiveCocoa/AppKit/NSButton.swift index c3c5755e27..ed3e9bc857 100644 --- a/ReactiveCocoa/AppKit/NSButton.swift +++ b/ReactiveCocoa/AppKit/NSButton.swift @@ -4,7 +4,7 @@ import AppKit extension Reactive where Base: NSButton { - internal var associatedAction: Atomic<(action: CocoaAction, disposable: Disposable?)?> { + internal var associatedAction: Atomic<(action: CocoaAction, disposable: CompositeDisposable)?> { return associatedValue { _ in Atomic(nil) } } @@ -15,15 +15,17 @@ extension Reactive where Base: NSButton { nonmutating set { associatedAction - .swap(newValue.map { action in - let disposable = CompositeDisposable() - disposable += isEnabled <~ action.isEnabled - disposable += trigger.observeValues { [unowned base = self.base] in - action.execute(base) + .modify { action in + action?.disposable.dispose() + action = newValue.map { action in + let disposable = CompositeDisposable() + disposable += isEnabled <~ action.isEnabled + disposable += trigger.observeValues { [unowned base = self.base] in + action.execute(base) + } + return (action, disposable) } - return (action, disposable) - })? - .disposable?.dispose() + } } } } From f65724c68c64e4018b114f48c42f302bce00e5fe Mon Sep 17 00:00:00 2001 From: David Ohayon Date: Thu, 12 Jan 2017 12:09:06 -0500 Subject: [PATCH 0700/1028] test isenabled --- ReactiveCocoaTests/AppKit/NSButtonSpec.swift | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ReactiveCocoaTests/AppKit/NSButtonSpec.swift b/ReactiveCocoaTests/AppKit/NSButtonSpec.swift index 5c34564640..81aa9b68d5 100644 --- a/ReactiveCocoaTests/AppKit/NSButtonSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSButtonSpec.swift @@ -20,6 +20,19 @@ class NSButtonSpec: QuickSpec { expect(_button).to(beNil()) } + it("should accept changes from bindings to its enabling state") { + button.isEnabled = false + + let (pipeSignal, observer) = Signal.pipe() + button.reactive.isEnabled <~ SignalProducer(pipeSignal) + + observer.send(value: true) + expect(button.isEnabled) == true + + observer.send(value: false) + expect(button.isEnabled) == false + } + it("should execute the `pressed` action upon receiving a click") { button.isEnabled = true From 8ac48742eef97a0da319fcf78d1f03cbaa862bfc Mon Sep 17 00:00:00 2001 From: David Ohayon Date: Thu, 12 Jan 2017 12:36:42 -0500 Subject: [PATCH 0701/1028] update test to make sure click disables the button --- ReactiveCocoaTests/AppKit/NSButtonSpec.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ReactiveCocoaTests/AppKit/NSButtonSpec.swift b/ReactiveCocoaTests/AppKit/NSButtonSpec.swift index 81aa9b68d5..733b14bc6a 100644 --- a/ReactiveCocoaTests/AppKit/NSButtonSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSButtonSpec.swift @@ -37,8 +37,10 @@ class NSButtonSpec: QuickSpec { button.isEnabled = true let pressed = MutableProperty(false) + + let (executionSignal, observer) = Signal.pipe() let action = Action<(), Bool, NoError> { _ in - SignalProducer(value: true) + SignalProducer(executionSignal) } pressed <~ SignalProducer(action.values) @@ -46,6 +48,12 @@ class NSButtonSpec: QuickSpec { expect(pressed.value) == false button.performClick(nil) + expect(button.isEnabled) == false + + observer.send(value: true) + observer.sendCompleted() + + expect(button.isEnabled) == true expect(pressed.value) == true } } From a8f03f7923c4be2afba12a8d626c01615ff3a24e Mon Sep 17 00:00:00 2001 From: Dave Caunt Date: Fri, 13 Jan 2017 13:29:08 +0000 Subject: [PATCH 0702/1028] Fix README BindingTarget link --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8d150eeab7..313e5afe9a 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ __ReactiveCocoa__ wraps various aspects of Cocoa frameworks with the declarative 1. **UI Bindings** - UI components exposes [`BindingTarget`][]s, which accept bindings from any + UI components expose [`BindingTarget`][]s, which accept bindings from any kind of streams of values via the `<~` operator. ```swift @@ -171,4 +171,4 @@ ReactiveCocoa 7.0 would focus on three main goals: [`Signal`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#signals [`SignalProducer`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#signal-producers [`Action`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#actions -[`BindingTarget`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#binding-target +[`BindingTarget`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#properties From b06edd15847d19d285f44f2df549699d66a12ef4 Mon Sep 17 00:00:00 2001 From: Dave Caunt Date: Fri, 13 Jan 2017 13:30:14 +0000 Subject: [PATCH 0703/1028] Update MutableProperty line link --- Documentation/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/README.md b/Documentation/README.md index b4e048a518..84bf091857 100644 --- a/Documentation/README.md +++ b/Documentation/README.md @@ -109,5 +109,5 @@ ReactiveCocoa provides UI bindings for UIKit and AppKit via the `reactive` struc [`SignalProducer`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#signal-producers [`Action`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#actions [`BindingTarget`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#binding-target -[`MutableProperty`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Sources/Property.swift#L534 +[`MutableProperty`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Sources/Property.swift#L583 [`DynamicProperty`]: https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/ReactiveCocoa/DynamicProperty.swift From 53e9fa206baed3e9186f068e1853c91e168ef7ab Mon Sep 17 00:00:00 2001 From: Dave Caunt Date: Fri, 13 Jan 2017 13:32:16 +0000 Subject: [PATCH 0704/1028] Fix documentation index BindingTarget link --- Documentation/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/README.md b/Documentation/README.md index 84bf091857..58b1072d85 100644 --- a/Documentation/README.md +++ b/Documentation/README.md @@ -108,6 +108,6 @@ ReactiveCocoa provides UI bindings for UIKit and AppKit via the `reactive` struc [`Signal`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#signals [`SignalProducer`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#signal-producers [`Action`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#actions -[`BindingTarget`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#binding-target +[`BindingTarget`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#properties [`MutableProperty`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Sources/Property.swift#L583 [`DynamicProperty`]: https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/ReactiveCocoa/DynamicProperty.swift From 5c177a217fdee2eeaa2ac793bf41df71a18ed9aa Mon Sep 17 00:00:00 2001 From: Dave Caunt Date: Fri, 13 Jan 2017 13:39:42 +0000 Subject: [PATCH 0705/1028] Update binding links --- Documentation/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/README.md b/Documentation/README.md index 58b1072d85..f17d87636e 100644 --- a/Documentation/README.md +++ b/Documentation/README.md @@ -38,8 +38,8 @@ ReactiveCocoa includes a few object interception tools from ReactiveObjC, remast The [`DynamicProperty`][] type can be used to bridge to Objective-C APIs that require Key-Value Coding (KVC) or Key-Value Observing (KVO), like `NSOperation`. Note that most AppKit and UIKit properties do _not_ support KVO, so their changes should be observed through other mechanisms. - For binding UI, [UIKit and AppKit bindings](ui-bindings) provided by ReactiveCocoa are preferred. + For binding UI, [UIKit][UIKit-bindings] and [AppKit](AppKit-bindings) bindings provided by ReactiveCocoa are preferred. In all other cases, [`MutableProperty`][] should be preferred over dynamic properties whenever possible! 1. **Expressive, Safe Key Path Observation** @@ -111,3 +111,5 @@ ReactiveCocoa provides UI bindings for UIKit and AppKit via the `reactive` struc [`BindingTarget`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#properties [`MutableProperty`]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Sources/Property.swift#L583 [`DynamicProperty`]: https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/ReactiveCocoa/DynamicProperty.swift +[UIKit-bindings]: https://github.com/ReactiveCocoa/ReactiveCocoa/tree/master/ReactiveCocoa/UIKit +[AppKit-bindings]: https://github.com/ReactiveCocoa/ReactiveCocoa/tree/master/ReactiveCocoa/AppKit From 80b66f85c423b1e8ee5656f22954cd0b6c0a82bf Mon Sep 17 00:00:00 2001 From: Dave Caunt Date: Fri, 13 Jan 2017 13:43:33 +0000 Subject: [PATCH 0706/1028] Fix broken link --- Documentation/DebuggingTechniques.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/DebuggingTechniques.md b/Documentation/DebuggingTechniques.md index a6bcd2d0ca..778752a32c 100644 --- a/Documentation/DebuggingTechniques.md +++ b/Documentation/DebuggingTechniques.md @@ -84,7 +84,7 @@ let searchString = textField.reactive.continuousTextValues .logEvents(identifier: "✨My awesome stream ✨") ``` -There also cases, specially with [hot signals][Signal], when there is simply too much output. For those, you can specify which events you are interested in: +There also cases, especially with [hot signals][Signal], when there is simply too much output. For those, you can specify which events you are interested in: ```swift let searchString = textField.reactive.continuousTextValues @@ -92,5 +92,5 @@ let searchString = textField.reactive.continuousTextValues .logEvents(events: [.disposed]) ``` -[Signal]: ../Sources/Signal.swift +[Signal]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Sources/Signal.swift From b390ecb38d506cf7e3b580876a9440f04f297e1f Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Mon, 16 Jan 2017 09:01:07 -0500 Subject: [PATCH 0707/1028] 5.0.0 --- README.md | 32 +++++++++++++------------------- ReactiveCocoa.podspec | 8 ++++---- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 313e5afe9a..de1e1116de 100644 --- a/README.md +++ b/README.md @@ -37,32 +37,32 @@ __ReactiveCocoa__ wraps various aspects of Cocoa frameworks with the declarative Interactive UI components expose [`Signal`][]s for control events and updates in the control value upon user interactions. - + A selected set of controls provide a convenience, expressive binding API for [`Action`][]s. - - + + ```swift // Update `allowsCookies` whenever the toggle is flipped. - preferences.allowsCookies <~ toggle.reactive.isOnValues - + preferences.allowsCookies <~ toggle.reactive.isOnValues + // Compute live character counts from the continuous stream of user initiated // changes in the text. textField.reactive.continuousTextValues.map { $0.characters.count } - + // Trigger `commit` whenever the button is pressed. button.reactive.pressed = CocoaAction(viewModel.commit) ``` - + 1. **Declarative Objective-C Dynamism** Create signals that are sourced by intercepting Objective-C objects, e.g. method call interception and object deinitialization. - + ```swift // Notify after every time `viewWillAppear(_:)` is called. let appearing = view.reactive.trigger(for: #selector(viewWillAppear(_:))) - + // Observe the lifetime of `object`. object.reactive.lifetime.ended.observeCompleted(doCleanup) ``` @@ -71,7 +71,7 @@ __ReactiveCocoa__ wraps various aspects of Cocoa frameworks with the declarative Establish key-value observations in the form of [`SignalProducer`][]s and `DynamicProperty`s, and enjoy the inherited composability. - + ```swift // A producer that sends the current value of `keyPath`, followed by // subsequent changes. @@ -79,7 +79,7 @@ __ReactiveCocoa__ wraps various aspects of Cocoa frameworks with the declarative // Terminate the KVO observation if the lifetime of `self` ends. let producer = object.reactive.values(forKeyPath: #keyPath(key)) .take(during: self.reactive.lifetime) - + // A parameterized property that represents the supplied key path of the // wrapped object. It holds a weak reference to the wrapped object. let property = DynamicProperty(object: person, @@ -99,7 +99,7 @@ If you use [Carthage][] to manage your dependencies, simply add ReactiveCocoa to your `Cartfile`: ``` -github "ReactiveCocoa/ReactiveCocoa" "5.0.0-rc.1" +github "ReactiveCocoa/ReactiveCocoa" "5.0.0" ``` If you use Carthage to build your dependencies, make sure you have added `ReactiveCocoa.framework`, `ReactiveSwift.framework`, and `Result.framework` to the "_Linked Frameworks and Libraries_" section of your target, and have included them in your Carthage framework copying build phase. @@ -110,7 +110,7 @@ If you use [CocoaPods][] to manage your dependencies, simply add ReactiveCocoa to your `Podfile`: ``` -pod 'ReactiveCocoa', '5.0.0-rc.1' +pod 'ReactiveCocoa', '~> 5.0.0' ``` #### Git submodule @@ -134,12 +134,6 @@ If you need any help, please visit our [GitHub issues][] or [Stack Overflow][]. ## Release Roadmap **Current Stable Release:**
    [![GitHub release](https://img.shields.io/github/release/ReactiveCocoa/ReactiveCocoa.svg)](https://github.com/ReactiveCocoa/ReactiveCocoa/releases) -### In Development: ReactiveCocoa 5.0 -It targets Swift 3.0.x and ReactiveSwift 1.0. The tentative schedule of a Gold Master release is January 2017. - -[Release Candidate 1](https://github.com/ReactiveCocoa/ReactiveCocoa/releases/tag/5.0.0-rc.1/) has been released. - -### Plan of Record #### ReactiveCocoa 6.0 It targets Swift 3.1.x and ReactiveSwift 2.0. The estimated schedule is Spring 2017. diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index b81706e596..fb75cd748c 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "5.0.0-rc.1" + s.version = "5.0.0" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. @@ -8,12 +8,12 @@ Pod::Spec.new do |s| s.homepage = "https://github.com/ReactiveCocoa/ReactiveCocoa" s.license = { :type => "MIT", :file => "LICENSE.md" } s.author = "ReactiveCocoa" - + s.osx.deployment_target = "10.9" s.ios.deployment_target = "8.0" s.tvos.deployment_target = "9.0" s.watchos.deployment_target = "2.0" - + s.source = { :git => "https://github.com/ReactiveCocoa/ReactiveCocoa.git", :tag => "#{s.version}" } s.source_files = "ReactiveCocoa/*.{swift,h,m}", "ReactiveCocoa/Shared/*.{swift}" s.private_header_files = "ReactiveCocoa/ObjCRuntimeAliases.h" @@ -22,6 +22,6 @@ Pod::Spec.new do |s| s.tvos.source_files = "ReactiveCocoa/UIKit/*.{swift}" s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_map = "ReactiveCocoa/module.modulemap" - + s.dependency 'ReactiveSwift', '~> 1.0.0' end From 31ef96e9a64072cc8f627d8e0690eb1feb0398c4 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Mon, 16 Jan 2017 23:45:05 +0900 Subject: [PATCH 0708/1028] Update README and Info.plist for 5.0 release --- README.md | 2 +- ReactiveCocoa/Info.plist | 2 +- ReactiveCocoaTests/Info.plist | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index de1e1116de..608ec16e2b 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ If you use [Carthage][] to manage your dependencies, simply add ReactiveCocoa to your `Cartfile`: ``` -github "ReactiveCocoa/ReactiveCocoa" "5.0.0" +github "ReactiveCocoa/ReactiveCocoa" ~> 5.0 ``` If you use Carthage to build your dependencies, make sure you have added `ReactiveCocoa.framework`, `ReactiveSwift.framework`, and `Result.framework` to the "_Linked Frameworks and Libraries_" section of your target, and have included them in your Carthage framework copying build phase. diff --git a/ReactiveCocoa/Info.plist b/ReactiveCocoa/Info.plist index 9a11afdc43..7138859eb1 100644 --- a/ReactiveCocoa/Info.plist +++ b/ReactiveCocoa/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.0 + 5.0.0 CFBundleSignature ???? CFBundleVersion diff --git a/ReactiveCocoaTests/Info.plist b/ReactiveCocoaTests/Info.plist index ba72822e87..7559011202 100644 --- a/ReactiveCocoaTests/Info.plist +++ b/ReactiveCocoaTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.0 + 5.0.0 CFBundleSignature ???? CFBundleVersion From 84ba3bfb3713f5bb3eca8e3e561ffed6d5ca3e9b Mon Sep 17 00:00:00 2001 From: Christopher Liscio Date: Thu, 20 Oct 2016 16:34:49 -0400 Subject: [PATCH 0709/1028] Add some extra control bindings --- ReactiveCocoa.xcodeproj/project.pbxproj | 4 ++++ ReactiveCocoa/AppKit/NSImageView.swift | 10 ++++++++++ ReactiveCocoa/AppKit/NSTextField.swift | 6 ++++++ 3 files changed, 20 insertions(+) create mode 100644 ReactiveCocoa/AppKit/NSImageView.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 97dfb9c6b6..2c4acddd90 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -56,6 +56,7 @@ 7DFBED211CDB8D8300EE435B /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 7DFBED281CDB8DE300EE435B /* DynamicPropertySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */; }; 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */; }; + 8392D8FD1DB93E5E00504ED4 /* NSImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8392D8FC1DB93E5E00504ED4 /* NSImageView.swift */; }; 9A1D05E01D93E99100ACF44C /* NSObject+Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */; }; 9A1D05E11D93E99100ACF44C /* NSObject+Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */; }; 9A1D05E21D93E99100ACF44C /* NSObject+Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */; }; @@ -341,6 +342,7 @@ 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Framework.xcconfig"; sourceTree = ""; }; 57A4D2471BA13F9700F7D4B1 /* tvOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-StaticLibrary.xcconfig"; sourceTree = ""; }; 7DFBED031CDB8C9500EE435B /* ReactiveCocoaTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveCocoaTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 8392D8FC1DB93E5E00504ED4 /* NSImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSImageView.swift; sourceTree = ""; }; 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Association.swift"; sourceTree = ""; }; 9A1D05EC1D93E9F100ACF44C /* UIActivityIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorView.swift; sourceTree = ""; }; 9A1D05ED1D93E9F100ACF44C /* UIBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIBarButtonItem.swift; sourceTree = ""; }; @@ -569,6 +571,7 @@ 9A6AAA251DB8F5280013AAEA /* ReusableComponents.swift */, D9558AB71DFF805A003254E1 /* NSPopUpButton.swift */, 006518751E26865800C3139A /* NSButton.swift */, + 8392D8FC1DB93E5E00504ED4 /* NSImageView.swift */, ); path = AppKit; sourceTree = ""; @@ -1262,6 +1265,7 @@ 9AA0BD981DDE7A2200531FCF /* ObjCRuntimeAliases.m in Sources */, 9A2E425E1DAA6737006D909F /* CocoaTarget.swift in Sources */, 9AB15C7A1E26CD9A00997378 /* Deprecations+Removals.swift in Sources */, + 8392D8FD1DB93E5E00504ED4 /* NSImageView.swift in Sources */, 9ADE4A891DA6D206005C2AC8 /* NSControl.swift in Sources */, 9AF0EA751D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, D9558AB81DFF805A003254E1 /* NSPopUpButton.swift in Sources */, diff --git a/ReactiveCocoa/AppKit/NSImageView.swift b/ReactiveCocoa/AppKit/NSImageView.swift new file mode 100644 index 0000000000..f538720a6b --- /dev/null +++ b/ReactiveCocoa/AppKit/NSImageView.swift @@ -0,0 +1,10 @@ +import ReactiveSwift +import enum Result.NoError +import AppKit + +extension Reactive where Base: NSImageView { + /// Sets the currently displayed image + public var image: BindingTarget { + return makeBindingTarget { $0.image = $1 } + } +} diff --git a/ReactiveCocoa/AppKit/NSTextField.swift b/ReactiveCocoa/AppKit/NSTextField.swift index 746d0792df..dbc7150353 100644 --- a/ReactiveCocoa/AppKit/NSTextField.swift +++ b/ReactiveCocoa/AppKit/NSTextField.swift @@ -22,4 +22,10 @@ extension Reactive where Base: NSTextField { return notifications .map { ($0.object as! NSTextField).attributedStringValue } } + + /// Wraps the `stringValue` binding target from NSControl for + /// cross-platform compatibility + public var text: BindingTarget { + return stringValue + } } From a3746ca8f8d28b38dadb01e2b838c55efedd38d1 Mon Sep 17 00:00:00 2001 From: Christopher Liscio Date: Fri, 20 Jan 2017 10:25:21 -0500 Subject: [PATCH 0710/1028] Adding some additional AppKit control bindings --- ReactiveCocoa/AppKit/NSButton.swift | 43 ++++++++++++++++++++++++ ReactiveCocoa/AppKit/NSPopUpButton.swift | 16 +++++++++ ReactiveCocoa/AppKit/NSTextField.swift | 5 +++ 3 files changed, 64 insertions(+) diff --git a/ReactiveCocoa/AppKit/NSButton.swift b/ReactiveCocoa/AppKit/NSButton.swift index ed3e9bc857..860c534368 100644 --- a/ReactiveCocoa/AppKit/NSButton.swift +++ b/ReactiveCocoa/AppKit/NSButton.swift @@ -2,6 +2,38 @@ import ReactiveSwift import enum Result.NoError import AppKit +public extension NSButton { + public enum State { + case on + case off + case mixed + + init?(rawValue: Int) { + switch rawValue { + case NSOnState: + self = .on + case NSOffState: + self = .off + case NSMixedState: + self = .mixed + default: + return nil + } + } + + var rawValue: Int { + switch self { + case .on: + return NSOnState + case .off: + return NSOffState + case .mixed: + return NSMixedState + } + } + } +} + extension Reactive where Base: NSButton { internal var associatedAction: Atomic<(action: CocoaAction, disposable: CompositeDisposable)?> { @@ -28,4 +60,15 @@ extension Reactive where Base: NSButton { } } } + + /// A signal of integer states, emitted by the button. + public var states: Signal { + return trigger.map { [unowned base = self.base] in return NSButton.State(rawValue: base.state)! } + } + + public var state: BindingTarget { + return makeBindingTarget { + $0.state = $1.rawValue + } + } } diff --git a/ReactiveCocoa/AppKit/NSPopUpButton.swift b/ReactiveCocoa/AppKit/NSPopUpButton.swift index b0fd8144ef..bc17ca5b0b 100644 --- a/ReactiveCocoa/AppKit/NSPopUpButton.swift +++ b/ReactiveCocoa/AppKit/NSPopUpButton.swift @@ -43,4 +43,20 @@ extension Reactive where Base: NSPopUpButton { } .skipNil() } + + + /// A signal of selected tags + public var selectedTags: Signal { + return self.integerValues + .map { [unowned base = self.base] _ -> Int in + return base.selectedTag() + } + } + + /// Sets the button with an index. + public var selectedTag: BindingTarget { + return makeBindingTarget { + $0.selectItem(withTag: $1) + } + } } diff --git a/ReactiveCocoa/AppKit/NSTextField.swift b/ReactiveCocoa/AppKit/NSTextField.swift index 746d0792df..252b97bb50 100644 --- a/ReactiveCocoa/AppKit/NSTextField.swift +++ b/ReactiveCocoa/AppKit/NSTextField.swift @@ -22,4 +22,9 @@ extension Reactive where Base: NSTextField { return notifications .map { ($0.object as! NSTextField).attributedStringValue } } + + /// Sets the color of the text with an `NSColor`. + public var textColor: BindingTarget { + return makeBindingTarget { $0.textColor = $1 } + } } From 8979594f5fbd1338c7e23c2234ab20c4e463be2b Mon Sep 17 00:00:00 2001 From: Christopher Liscio Date: Mon, 30 Jan 2017 11:30:38 -0500 Subject: [PATCH 0711/1028] Add another cross-platform wrapper --- ReactiveCocoa/AppKit/NSTextField.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ReactiveCocoa/AppKit/NSTextField.swift b/ReactiveCocoa/AppKit/NSTextField.swift index dbc7150353..bf5cce066c 100644 --- a/ReactiveCocoa/AppKit/NSTextField.swift +++ b/ReactiveCocoa/AppKit/NSTextField.swift @@ -28,4 +28,10 @@ extension Reactive where Base: NSTextField { public var text: BindingTarget { return stringValue } + + /// Wraps the `stringValue` binding target from NSControl for + /// cross-platform compatibility + public var attributedText: BindingTarget { + return attributedStringValue + } } From f2b767e3fe173960de8bff7f9e96c8201c272611 Mon Sep 17 00:00:00 2001 From: Christopher Liscio Date: Mon, 30 Jan 2017 11:30:58 -0500 Subject: [PATCH 0712/1028] Add missing `extern` keyword --- ReactiveCocoa/ObjCRuntimeAliases.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa/ObjCRuntimeAliases.h b/ReactiveCocoa/ObjCRuntimeAliases.h index 9a00edc7f1..2dc6391c12 100644 --- a/ReactiveCocoa/ObjCRuntimeAliases.h +++ b/ReactiveCocoa/ObjCRuntimeAliases.h @@ -3,7 +3,7 @@ NS_ASSUME_NONNULL_BEGIN -const IMP _rac_objc_msgForward; +extern const IMP _rac_objc_msgForward; /// A trampoline of `objc_setAssociatedObject` that is made to circumvent the /// reference counting calls in the imported version in Swift. From 4709764823263a2a0d06ad6e675efd9115e2ef7c Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 27 Jan 2017 14:55:37 +0000 Subject: [PATCH 0713/1028] Delegate interception for UISearchBar. --- ReactiveCocoa.xcodeproj/project.pbxproj | 10 ++ ReactiveCocoa/DelegateProxy.swift | 106 ++++++++++++++++++ ReactiveCocoa/UIKit/iOS/UISearchBar.swift | 33 +++--- .../UIKit/UISearchBarSpec.swift | 58 ++++++++++ 4 files changed, 189 insertions(+), 18 deletions(-) create mode 100644 ReactiveCocoa/DelegateProxy.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 97dfb9c6b6..837b99c3fc 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -145,6 +145,10 @@ 9A6AAA2B1DB8F85C0013AAEA /* ReusableComponentsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA271DB8F7EB0013AAEA /* ReusableComponentsSpec.swift */; }; 9A6AAA2E1DB903A20013AAEA /* ReusableComponentsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA291DB8F7F10013AAEA /* ReusableComponentsSpec.swift */; }; 9A6AAA2F1DB903A40013AAEA /* ReusableComponentsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA291DB8F7F10013AAEA /* ReusableComponentsSpec.swift */; }; + 9A7488481E3B8ACE00CD0317 /* DelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7488471E3B8ACE00CD0317 /* DelegateProxy.swift */; }; + 9A7488491E3B8ACE00CD0317 /* DelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7488471E3B8ACE00CD0317 /* DelegateProxy.swift */; }; + 9A74884A1E3B8ACE00CD0317 /* DelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7488471E3B8ACE00CD0317 /* DelegateProxy.swift */; }; + 9A74884B1E3B8ACE00CD0317 /* DelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7488471E3B8ACE00CD0317 /* DelegateProxy.swift */; }; 9A9A129A1DC7A97100D10223 /* UIGestureRecognizerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4191394B1DBA002C0043C9D1 /* UIGestureRecognizerSpec.swift */; }; 9A9DFEE51DA7B5500039EE1B /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; 9A9DFEE91DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; @@ -382,6 +386,7 @@ 9A6AAA251DB8F5280013AAEA /* ReusableComponents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponents.swift; sourceTree = ""; }; 9A6AAA271DB8F7EB0013AAEA /* ReusableComponentsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponentsSpec.swift; sourceTree = ""; }; 9A6AAA291DB8F7F10013AAEA /* ReusableComponentsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponentsSpec.swift; sourceTree = ""; }; + 9A7488471E3B8ACE00CD0317 /* DelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DelegateProxy.swift; sourceTree = ""; }; 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssociationSpec.swift; sourceTree = ""; }; 9AA0BD771DDE03DE00531FCF /* ObjC+Runtime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObjC+Runtime.swift"; sourceTree = ""; }; 9AA0BD801DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObjC+RuntimeSubclassing.swift"; sourceTree = ""; }; @@ -713,6 +718,7 @@ 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */, 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */, CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */, + 9A7488471E3B8ACE00CD0317 /* DelegateProxy.swift */, 9ADE4A901DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift */, 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */, 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */, @@ -1140,6 +1146,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9A74884B1E3B8ACE00CD0317 /* DelegateProxy.swift in Sources */, 9A1D06131D93EA0100ACF44C /* UIBarItem.swift in Sources */, 9A1D061F1D93EA0100ACF44C /* UITextField.swift in Sources */, 4ABEFE261DCFCF640066A8C2 /* UICollectionView.swift in Sources */, @@ -1219,6 +1226,7 @@ buildActionMask = 2147483647; files = ( 9A2E42601DAA6737006D909F /* CocoaTarget.swift in Sources */, + 9A74884A1E3B8ACE00CD0317 /* DelegateProxy.swift in Sources */, 9A9DFEE51DA7B5500039EE1B /* NSObject+Intercepting.swift in Sources */, 9A54A21D1DE00D09001739B3 /* ObjC+Selector.swift in Sources */, 9AB15C7C1E26CD9A00997378 /* Deprecations+Removals.swift in Sources */, @@ -1250,6 +1258,7 @@ 9AA0BD8F1DDE29F800531FCF /* NSObject+ObjCRuntime.swift in Sources */, 9A1D05E01D93E99100ACF44C /* NSObject+Association.swift in Sources */, 538DCB791DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */, + 9A7488481E3B8ACE00CD0317 /* DelegateProxy.swift in Sources */, 9AE7C2A41DDD7F5100F7534C /* ObjC+Messages.swift in Sources */, 006518761E26865800C3139A /* NSButton.swift in Sources */, 9A54A21B1DE00D09001739B3 /* ObjC+Selector.swift in Sources */, @@ -1307,6 +1316,7 @@ 9A1D060D1D93EA0000ACF44C /* UITextField.swift in Sources */, 9AB15C7B1E26CD9A00997378 /* Deprecations+Removals.swift in Sources */, 9A6AAA231DB8F51C0013AAEA /* ReusableComponents.swift in Sources */, + 9A7488491E3B8ACE00CD0317 /* DelegateProxy.swift in Sources */, 9A1D060E1D93EA0000ACF44C /* UITextView.swift in Sources */, BFCF775F1DFAD8A50058006E /* UISearchBar.swift in Sources */, 9A1D06061D93EA0000ACF44C /* UIImageView.swift in Sources */, diff --git a/ReactiveCocoa/DelegateProxy.swift b/ReactiveCocoa/DelegateProxy.swift new file mode 100644 index 0000000000..659b25a321 --- /dev/null +++ b/ReactiveCocoa/DelegateProxy.swift @@ -0,0 +1,106 @@ +import ReactiveSwift +import enum Result.NoError + +internal class DelegateProxy: NSObject { + internal weak var forwardee: Delegate? { + didSet { + originalSetter(self) + } + } + + internal var interceptedSelectors: Set = [] + + private let originalSetter: (AnyObject) -> Void + + required init(_ originalSetter: @escaping (AnyObject) -> Void) { + self.originalSetter = originalSetter + } + + override func forwardingTarget(for selector: Selector!) -> Any? { + return interceptedSelectors.contains(selector) ? nil : forwardee + } + + func intercept(_ selector: Selector) -> Signal<(), NoError> { + interceptedSelectors.insert(selector) + originalSetter(self) + return self.reactive.trigger(for: selector) + } + + override func responds(to selector: Selector!) -> Bool { + if interceptedSelectors.contains(selector) { + return true + } + + return forwardee?.responds(to: selector) ?? super.responds(to: selector) + } +} + +private let hasSwizzledKey = AssociationKey(default: false) + +internal protocol DelegateProxyProtocol: class { + associatedtype Delegate: NSObjectProtocol + + weak var forwardee: Delegate? { get set } + + init(_ originalSetter: @escaping (AnyObject) -> Void) +} + +extension DelegateProxy: DelegateProxyProtocol {} + +extension DelegateProxyProtocol { + internal static func proxy(for instance: NSObject, setter: Selector, getter: Selector, _ key: AssociationKey) -> Self { + return instance.synchronized { + if let proxy = instance.associations.value(forKey: key) { + return proxy + } + + let subclass: AnyClass = swizzleClass(instance) + + // Hide the original setter, and redirect subsequent delegate assignment + // to the proxy. + synchronized(subclass) { + let subclassAssociations = Associations(subclass as AnyObject) + + if !subclassAssociations.value(forKey: hasSwizzledKey) { + subclassAssociations.setValue(true, forKey: hasSwizzledKey) + + let method = class_getInstanceMethod(subclass, setter) + let typeEncoding = method_getTypeEncoding(method)! + + let newSetterImpl: @convention(block) (NSObject, NSObject) -> Void = { object, delegate in + let proxy = object.associations.value(forKey: key)! + proxy.forwardee = (delegate as! Delegate) + } + + class_replaceMethod(subclass, + setter, + imp_implementationWithBlock(newSetterImpl as Any), + typeEncoding) + } + } + + // Set the proxy as the delegate. + let realClass: AnyClass = class_getSuperclass(subclass) + let originalSetterImpl = class_getMethodImplementation(realClass, setter) + let getterImpl = class_getMethodImplementation(realClass, getter) + + typealias Setter = @convention(c) (AnyObject, Selector, AnyObject) -> Void + typealias Getter = @convention(c) (AnyObject, Selector) -> NSObject? + + // As Objective-C classes may cache the information of their delegate at + // the time the delegates are set, the information has to be "flushed" + // whenever the proxy forwardee is replaced or a selector is intercepted. + let proxy = self.init { [weak instance] proxy in + guard let instance = instance else { return } + unsafeBitCast(originalSetterImpl, to: Setter.self)(instance, setter, proxy) + } + + instance.associations.setValue(proxy, forKey: key) + + proxy.forwardee = unsafeBitCast(getterImpl, to: Getter.self)(instance, getter) as! Delegate? + unsafeBitCast(originalSetterImpl, to: Setter.self)(instance, setter, proxy) + + return proxy + } + } +} diff --git a/ReactiveCocoa/UIKit/iOS/UISearchBar.swift b/ReactiveCocoa/UIKit/iOS/UISearchBar.swift index 78fac291d8..7c1920375d 100644 --- a/ReactiveCocoa/UIKit/iOS/UISearchBar.swift +++ b/ReactiveCocoa/UIKit/iOS/UISearchBar.swift @@ -2,27 +2,24 @@ import ReactiveSwift import enum Result.NoError import UIKit -private class ReactiveUISearchBarDelegate: NSObject, UISearchBarDelegate { +private class SearchBarDelegateProxy: DelegateProxy, UISearchBarDelegate { + @objc func searchBarTextDidEndEditing(_ searchBar: UISearchBar) { + forwardee?.searchBarTextDidEndEditing?(searchBar) + } - @objc func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {} - @objc func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {} + @objc func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { + forwardee?.searchBar?(searchBar, textDidChange: searchText) + } } -private var delegateKey: UInt8 = 0 +private let proxyKey = AssociationKey() extension Reactive where Base: UISearchBar { - - private var delegate: ReactiveUISearchBarDelegate { - if let delegate = objc_getAssociatedObject(base, &delegateKey) as? ReactiveUISearchBarDelegate { - return delegate - } else if let _ = base.delegate { - fatalError("Cannot use reactive values on UISearchBar with a custom delegate!") - } - - let delegate = ReactiveUISearchBarDelegate() - base.delegate = delegate - objc_setAssociatedObject(base, &delegateKey, delegate, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - return delegate + private var proxy: SearchBarDelegateProxy { + return SearchBarDelegateProxy.proxy(for: base, + setter: #selector(setter: base.delegate), + getter: #selector(getter: base.delegate), + proxyKey) } /// Sets the text of the search bar. @@ -39,7 +36,7 @@ extension Reactive where Base: UISearchBar { /// - note: To observe text values that change on all editing events, /// see `continuousTextValues`. public var textValues: Signal { - return delegate.reactive.trigger(for: #selector(UISearchBarDelegate.searchBarTextDidEndEditing)) + return proxy.intercept(#selector(UISearchBarDelegate.searchBarTextDidEndEditing)) .map { [unowned base] in base.text } } @@ -51,7 +48,7 @@ extension Reactive where Base: UISearchBar { /// /// - note: To observe text values only when editing ends, see `textValues`. public var continuousTextValues: Signal { - return delegate.reactive.trigger(for: #selector(UISearchBarDelegate.searchBar(_:textDidChange:))) + return proxy.intercept(#selector(UISearchBarDelegate.searchBar(_:textDidChange:))) .map { [unowned base] in base.text } } diff --git a/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift b/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift index 9dcade1e3c..546eb685c7 100644 --- a/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift +++ b/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift @@ -65,5 +65,63 @@ class UISearchBarSpec: QuickSpec { searchBar.delegate!.searchBar!(searchBar, textDidChange: "newValue") expect(latestValue) == "newValue" } + + it("should pass through the intercepted calls") { + searchBar.text = "newValue" + + var latestValue: String? + searchBar.reactive.continuousTextValues.observeValues { text in + latestValue = text + } + + let receiver = SearchBarDelegateReceiver() + searchBar.delegate = receiver + expect(receiver.textDidChangeCounter) == 0 + + searchBar.delegate!.searchBar!(searchBar, textDidChange: "newValue") + expect(latestValue) == "newValue" + expect(receiver.textDidChangeCounter) == 1 + } + + it("should pass through the unintercepted calls") { + searchBar.reactive.continuousTextValues.observe { _ in } + + let receiver = SearchBarDelegateReceiver() + searchBar.delegate = receiver + expect(receiver.searchButtonClickedCounter) == 0 + + searchBar.delegate!.searchBarSearchButtonClicked!(searchBar) + expect(receiver.searchButtonClickedCounter) == 1 + } + + it("should preserve the original delegate, and pass through the unintercepted calls") { + let receiver = SearchBarDelegateReceiver() + searchBar.delegate = receiver + expect(receiver.searchButtonClickedCounter) == 0 + + searchBar.reactive.continuousTextValues.observe { _ in } + expect(receiver.searchButtonClickedCounter) == 0 + + searchBar.delegate!.searchBarSearchButtonClicked!(searchBar) + expect(receiver.searchButtonClickedCounter) == 1 + } + } +} + +class SearchBarDelegateReceiver: NSObject, UISearchBarDelegate { + var textDidChangeCounter = 0 + var textDidEndEditingCounter = 0 + var searchButtonClickedCounter = 0 + + func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { + searchButtonClickedCounter += 1 + } + + func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { + textDidChangeCounter += 1 + } + + func searchBarTextDidEndEditing(_ searchBar: UISearchBar) { + textDidEndEditingCounter += 1 } } From a58a3360bb7fa60785b693800249c735aced12ed Mon Sep 17 00:00:00 2001 From: Christopher Liscio Date: Tue, 31 Jan 2017 13:27:26 -0500 Subject: [PATCH 0714/1028] Removing some unnecessary bits, adding tests --- ReactiveCocoa.xcodeproj/project.pbxproj | 4 ++ ReactiveCocoa/AppKit/NSButton.swift | 45 +++------------- ReactiveCocoa/AppKit/NSPopUpButton.swift | 2 +- ReactiveCocoaTests/AppKit/NSButtonSpec.swift | 44 ++++++++++++++++ .../AppKit/NSImageViewSpec.swift | 51 +++++++++++++++++++ .../AppKit/NSPopUpButtonSpec.swift | 40 ++++++++++++++- 6 files changed, 145 insertions(+), 41 deletions(-) create mode 100644 ReactiveCocoaTests/AppKit/NSImageViewSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 2c4acddd90..27e8a2041c 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -56,6 +56,7 @@ 7DFBED211CDB8D8300EE435B /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 7DFBED281CDB8DE300EE435B /* DynamicPropertySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */; }; 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */; }; + 834DE1011E4109750099F4E5 /* NSImageViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 834DE1001E4109750099F4E5 /* NSImageViewSpec.swift */; }; 8392D8FD1DB93E5E00504ED4 /* NSImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8392D8FC1DB93E5E00504ED4 /* NSImageView.swift */; }; 9A1D05E01D93E99100ACF44C /* NSObject+Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */; }; 9A1D05E11D93E99100ACF44C /* NSObject+Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */; }; @@ -342,6 +343,7 @@ 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Framework.xcconfig"; sourceTree = ""; }; 57A4D2471BA13F9700F7D4B1 /* tvOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-StaticLibrary.xcconfig"; sourceTree = ""; }; 7DFBED031CDB8C9500EE435B /* ReactiveCocoaTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveCocoaTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 834DE1001E4109750099F4E5 /* NSImageViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSImageViewSpec.swift; sourceTree = ""; }; 8392D8FC1DB93E5E00504ED4 /* NSImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSImageView.swift; sourceTree = ""; }; 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Association.swift"; sourceTree = ""; }; 9A1D05EC1D93E9F100ACF44C /* UIActivityIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorView.swift; sourceTree = ""; }; @@ -641,6 +643,7 @@ 9A6AAA271DB8F7EB0013AAEA /* ReusableComponentsSpec.swift */, D9558AB51DFF7B90003254E1 /* NSPopUpButtonSpec.swift */, 004FD0061E26CDB300A03A82 /* NSButtonSpec.swift */, + 834DE1001E4109750099F4E5 /* NSImageViewSpec.swift */, ); path = AppKit; sourceTree = ""; @@ -1281,6 +1284,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 834DE1011E4109750099F4E5 /* NSImageViewSpec.swift in Sources */, 9A6AAA0E1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */, 9A54A2111DDF5B4D001739B3 /* InterceptingPerformanceTests.swift in Sources */, 538DCB7D1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift in Sources */, diff --git a/ReactiveCocoa/AppKit/NSButton.swift b/ReactiveCocoa/AppKit/NSButton.swift index 860c534368..3919070666 100644 --- a/ReactiveCocoa/AppKit/NSButton.swift +++ b/ReactiveCocoa/AppKit/NSButton.swift @@ -2,38 +2,6 @@ import ReactiveSwift import enum Result.NoError import AppKit -public extension NSButton { - public enum State { - case on - case off - case mixed - - init?(rawValue: Int) { - switch rawValue { - case NSOnState: - self = .on - case NSOffState: - self = .off - case NSMixedState: - self = .mixed - default: - return nil - } - } - - var rawValue: Int { - switch self { - case .on: - return NSOnState - case .off: - return NSOffState - case .mixed: - return NSMixedState - } - } - } -} - extension Reactive where Base: NSButton { internal var associatedAction: Atomic<(action: CocoaAction, disposable: CompositeDisposable)?> { @@ -61,14 +29,13 @@ extension Reactive where Base: NSButton { } } - /// A signal of integer states, emitted by the button. - public var states: Signal { - return trigger.map { [unowned base = self.base] in return NSButton.State(rawValue: base.state)! } + /// A signal of integer states (On, Off, Mixed), emitted by the button. + public var states: Signal { + return trigger.map { [unowned base = self.base] in base.state } } - public var state: BindingTarget { - return makeBindingTarget { - $0.state = $1.rawValue - } + /// Sets the button's state + public var state: BindingTarget { + return makeBindingTarget { $0.state = $1 } } } diff --git a/ReactiveCocoa/AppKit/NSPopUpButton.swift b/ReactiveCocoa/AppKit/NSPopUpButton.swift index bc17ca5b0b..03e0e68de1 100644 --- a/ReactiveCocoa/AppKit/NSPopUpButton.swift +++ b/ReactiveCocoa/AppKit/NSPopUpButton.swift @@ -53,7 +53,7 @@ extension Reactive where Base: NSPopUpButton { } } - /// Sets the button with an index. + /// Sets the selected tag public var selectedTag: BindingTarget { return makeBindingTarget { $0.selectItem(withTag: $1) diff --git a/ReactiveCocoaTests/AppKit/NSButtonSpec.swift b/ReactiveCocoaTests/AppKit/NSButtonSpec.swift index 733b14bc6a..e2a87d5242 100644 --- a/ReactiveCocoaTests/AppKit/NSButtonSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSButtonSpec.swift @@ -33,6 +33,50 @@ class NSButtonSpec: QuickSpec { expect(button.isEnabled) == false } + it("should accept changes from bindings to its state") { + button.allowsMixedState = true + button.state = NSOffState + + let (pipeSignal, observer) = Signal.pipe() + button.reactive.state <~ SignalProducer(pipeSignal) + + observer.send(value: NSOffState) + expect(button.state) == NSOffState + + observer.send(value: NSMixedState) + expect(button.state) == NSMixedState + + observer.send(value: NSOnState) + expect(button.state) == NSOnState + } + + it("should send along state changes") { + button.setButtonType(.pushOnPushOff) + button.allowsMixedState = false + button.state = NSOffState + + let state = MutableProperty(NSOffState) + state <~ button.reactive.states + + button.performClick(nil) + expect(state.value) == NSOnState + + button.performClick(nil) + expect(state.value) == NSOffState + + button.allowsMixedState = true + + button.performClick(nil) + expect(state.value) == NSMixedState + + button.performClick(nil) + expect(state.value) == NSOnState + + button.performClick(nil) + expect(state.value) == NSOffState + + } + it("should execute the `pressed` action upon receiving a click") { button.isEnabled = true diff --git a/ReactiveCocoaTests/AppKit/NSImageViewSpec.swift b/ReactiveCocoaTests/AppKit/NSImageViewSpec.swift new file mode 100644 index 0000000000..055a268dc6 --- /dev/null +++ b/ReactiveCocoaTests/AppKit/NSImageViewSpec.swift @@ -0,0 +1,51 @@ +import Quick +import Nimble +import ReactiveSwift +import ReactiveCocoa +import AppKit +import enum Result.NoError + +class NSImageViewSpec: QuickSpec { + override func spec() { + var imageView: NSImageView! + weak var _imageView: NSImageView? + + beforeEach { + imageView = NSImageView(frame: .zero) + _imageView = imageView + } + + afterEach { + imageView = nil + expect(_imageView).to(beNil()) + } + + it("should accept changes from bindings to its enabling state") { + imageView.isEnabled = false + + let (pipeSignal, observer) = Signal.pipe() + imageView.reactive.isEnabled <~ SignalProducer(pipeSignal) + + observer.send(value: true) + expect(imageView.isEnabled) == true + + observer.send(value: false) + expect(imageView.isEnabled) == false + } + + it("should accept changes from bindings to its image") { + + let (pipeSignal, observer) = Signal.pipe() + imageView.reactive.image <~ SignalProducer(pipeSignal) + + let theImage = NSImage(named: NSImageNameUser) + + observer.send(value: theImage) + expect(imageView.image) == theImage + + observer.send(value: nil) + expect(imageView.image).to(beNil()) + } + } +} + diff --git a/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift b/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift index fb1edd7ac8..6a3cab0386 100644 --- a/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift @@ -17,7 +17,13 @@ final class NSPopUpButtonSpec: QuickSpec { window = NSWindow() button = NSPopUpButton(frame: .zero) _button = button - button.addItems(withTitles: testTitles ) + for (i, title) in testTitles.enumerated() { + let item = NSMenuItem(title: title, action: nil, keyEquivalent: "") + item.tag = 1000 + i + + button.menu?.addItem(item) + } + window.contentView?.addSubview(button) } @@ -89,6 +95,38 @@ final class NSPopUpButtonSpec: QuickSpec { let titles = values.map { $0.title } expect(titles) == ["1", "99"] } + + it("should emit selected tag changes") { + var values = [Int]() + button.reactive.selectedTags.observeValues { values.append($0) } + + button.menu?.performActionForItem(at: 1) + button.menu?.performActionForItem(at: 99) + + expect(values) == [1001, 1099] + } + + it("should accept changes from its bindings to its tag values") { + let (signal, observer) = Signal.pipe() + button.reactive.selectedTag <~ SignalProducer(signal) + + observer.send(value: 1001) + expect(button.selectedItem?.tag) == 1001 + expect(button.indexOfSelectedItem) == 1 + + observer.send(value: 1099) + expect(button.selectedItem?.tag) == 1099 + expect(button.indexOfSelectedItem) == 99 + + observer.send(value: 1042) + expect(button.selectedItem?.tag) == 1042 + expect(button.indexOfSelectedItem) == 42 + + // Sending an invalid tag number doesn't change the selection + observer.send(value: testTitles.count + 1) + expect(button.selectedItem?.tag) == 1042 + expect(button.indexOfSelectedItem) == 42 + } } } } From 9d755031d9915422ee6c9337e272db1832fd1223 Mon Sep 17 00:00:00 2001 From: Christopher Liscio Date: Thu, 2 Feb 2017 10:32:25 -0500 Subject: [PATCH 0715/1028] Add a few missing bindings --- ReactiveCocoa.xcodeproj/project.pbxproj | 8 +++++++ ReactiveCocoa/AppKit/NSButton.swift | 5 +++++ ReactiveCocoa/AppKit/NSControl.swift | 2 ++ ReactiveCocoa/AppKit/NSSegmentedControl.swift | 22 +++++++++++++++++++ ReactiveCocoa/AppKit/NSSlider.swift | 11 ++++++++++ ReactiveCocoa/UIKit/UIButton.swift | 9 ++++++++ 6 files changed, 57 insertions(+) create mode 100644 ReactiveCocoa/AppKit/NSSegmentedControl.swift create mode 100644 ReactiveCocoa/AppKit/NSSlider.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 27e8a2041c..bd1556b091 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -57,6 +57,8 @@ 7DFBED281CDB8DE300EE435B /* DynamicPropertySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */; }; 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */; }; 834DE1011E4109750099F4E5 /* NSImageViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 834DE1001E4109750099F4E5 /* NSImageViewSpec.swift */; }; + 834DE1121E4120340099F4E5 /* NSSegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 834DE1111E4120340099F4E5 /* NSSegmentedControl.swift */; }; + 834DE1141E4122910099F4E5 /* NSSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 834DE1131E4122910099F4E5 /* NSSlider.swift */; }; 8392D8FD1DB93E5E00504ED4 /* NSImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8392D8FC1DB93E5E00504ED4 /* NSImageView.swift */; }; 9A1D05E01D93E99100ACF44C /* NSObject+Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */; }; 9A1D05E11D93E99100ACF44C /* NSObject+Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */; }; @@ -344,6 +346,8 @@ 57A4D2471BA13F9700F7D4B1 /* tvOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-StaticLibrary.xcconfig"; sourceTree = ""; }; 7DFBED031CDB8C9500EE435B /* ReactiveCocoaTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveCocoaTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 834DE1001E4109750099F4E5 /* NSImageViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSImageViewSpec.swift; sourceTree = ""; }; + 834DE1111E4120340099F4E5 /* NSSegmentedControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSSegmentedControl.swift; sourceTree = ""; }; + 834DE1131E4122910099F4E5 /* NSSlider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSSlider.swift; sourceTree = ""; }; 8392D8FC1DB93E5E00504ED4 /* NSImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSImageView.swift; sourceTree = ""; }; 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Association.swift"; sourceTree = ""; }; 9A1D05EC1D93E9F100ACF44C /* UIActivityIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorView.swift; sourceTree = ""; }; @@ -573,6 +577,8 @@ 9A6AAA251DB8F5280013AAEA /* ReusableComponents.swift */, D9558AB71DFF805A003254E1 /* NSPopUpButton.swift */, 006518751E26865800C3139A /* NSButton.swift */, + 834DE1111E4120340099F4E5 /* NSSegmentedControl.swift */, + 834DE1131E4122910099F4E5 /* NSSlider.swift */, 8392D8FC1DB93E5E00504ED4 /* NSImageView.swift */, ); path = AppKit; @@ -1272,7 +1278,9 @@ 9ADE4A891DA6D206005C2AC8 /* NSControl.swift in Sources */, 9AF0EA751D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, D9558AB81DFF805A003254E1 /* NSPopUpButton.swift in Sources */, + 834DE1141E4122910099F4E5 /* NSSlider.swift in Sources */, 9ADE4A911DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, + 834DE1121E4120340099F4E5 /* NSSegmentedControl.swift in Sources */, 53A6BED21DD4BCA90016C058 /* MKMapView.swift in Sources */, 9A1D065B1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, 4A0E10FF1D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, diff --git a/ReactiveCocoa/AppKit/NSButton.swift b/ReactiveCocoa/AppKit/NSButton.swift index 3919070666..fee3668f19 100644 --- a/ReactiveCocoa/AppKit/NSButton.swift +++ b/ReactiveCocoa/AppKit/NSButton.swift @@ -38,4 +38,9 @@ extension Reactive where Base: NSButton { public var state: BindingTarget { return makeBindingTarget { $0.state = $1 } } + + /// Sets the button's image + public var image: BindingTarget { + return makeBindingTarget { $0.image = $1 } + } } diff --git a/ReactiveCocoa/AppKit/NSControl.swift b/ReactiveCocoa/AppKit/NSControl.swift index dbdf35dde4..3580dfc2c3 100644 --- a/ReactiveCocoa/AppKit/NSControl.swift +++ b/ReactiveCocoa/AppKit/NSControl.swift @@ -109,3 +109,5 @@ extension Reactive where Base: NSControl { } } } + + diff --git a/ReactiveCocoa/AppKit/NSSegmentedControl.swift b/ReactiveCocoa/AppKit/NSSegmentedControl.swift new file mode 100644 index 0000000000..0c613ecd35 --- /dev/null +++ b/ReactiveCocoa/AppKit/NSSegmentedControl.swift @@ -0,0 +1,22 @@ +import ReactiveSwift +import enum Result.NoError +import AppKit + +extension Reactive where Base: NSSegmentedControl { + /// Changes the selected segment of the segmented control. + public var selectedSegment: BindingTarget { + return makeBindingTarget { $0.selectedSegment = $1 } + } + + /// A signal of indexes of selections emitted by the segmented control. + public var selectedSegments: Signal { + return trigger.map { [unowned base = self.base] in base.selectedSegment } + } + + /// The below are provided for cross-platform compatibility + + /// Changes the selected segment of the segmented control. + public var selectedSegmentIndex: BindingTarget { return selectedSegment } + /// A signal of indexes of selections emitted by the segmented control. + public var selectedSegmentIndexes: Signal { return selectedSegments } +} diff --git a/ReactiveCocoa/AppKit/NSSlider.swift b/ReactiveCocoa/AppKit/NSSlider.swift new file mode 100644 index 0000000000..64f14735cf --- /dev/null +++ b/ReactiveCocoa/AppKit/NSSlider.swift @@ -0,0 +1,11 @@ +import ReactiveSwift +import enum Result.NoError +import AppKit + +extension Reactive where Base: NSSlider { + + // Provided for cross-platform compatibility + + public var value: BindingTarget { return floatValue } + public var values: Signal { return floatValues } +} diff --git a/ReactiveCocoa/UIKit/UIButton.swift b/ReactiveCocoa/UIKit/UIButton.swift index 4f4a418742..2c6486819d 100644 --- a/ReactiveCocoa/UIKit/UIButton.swift +++ b/ReactiveCocoa/UIKit/UIButton.swift @@ -27,4 +27,13 @@ extension Reactive where Base: UIButton { public func title(for state: UIControlState) -> BindingTarget { return makeBindingTarget { $0.setTitle($1, for: state) } } + + /// Sets the image of the button for the specified state. + public func image(for state: UIControlState) -> BindingTarget { + return makeBindingTarget { $0.setImage($1, for: state) } + } + + public var image: BindingTarget { + return image(for: .normal) + } } From 18e61814b652b0bdbfddfd4d0c93a760dc06c5a3 Mon Sep 17 00:00:00 2001 From: Jan Klausa Date: Fri, 3 Feb 2017 17:20:03 +0100 Subject: [PATCH 0716/1028] Add `isSecureTextEntry` binding to UITextField. --- ReactiveCocoa/UIKit/UITextField.swift | 5 +++++ ReactiveCocoaTests/UIKit/UITextFieldSpec.swift | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/ReactiveCocoa/UIKit/UITextField.swift b/ReactiveCocoa/UIKit/UITextField.swift index dbddfb1559..ca575a5029 100644 --- a/ReactiveCocoa/UIKit/UITextField.swift +++ b/ReactiveCocoa/UIKit/UITextField.swift @@ -42,4 +42,9 @@ extension Reactive where Base: UITextField { public var continuousAttributedTextValues: Signal { return controlEvents(.editingChanged).map { $0.attributedText } } + + /// Sets the secure text entry attribute on the text field. + public var isSecureTextEntry: BindingTarget { + return makeBindingTarget { $0.isSecureTextEntry = $1 } + } } diff --git a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift index 01b504a5e0..5bf7aa393a 100644 --- a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift @@ -87,5 +87,16 @@ class UITextFieldSpec: QuickSpec { textField.sendActions(for: .editingChanged) expect(latestValue?.string) == textField.attributedText?.string } + + it("should accept changes from bindings to its secureTextEntry attribute") { + let (pipeSignal, observer) = Signal.pipe() + textField.reactive.isSecureTextEntry <~ pipeSignal + + observer.send(value: true) + expect(textField.isSecureTextEntry) == true + + observer.send(value: false) + expect(textField.isSecureTextEntry) == false + } } } From 2016867a1647ef0ec98265399af5ed13befee2fd Mon Sep 17 00:00:00 2001 From: Keita Date: Fri, 3 Feb 2017 14:06:12 -0800 Subject: [PATCH 0717/1028] Fix typo - Add an apostrophe. - Fix header level. --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4f41513ef..06a05a93b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ In version 5.0, we split ReactiveCocoa into multiple repositories for reasons ex **If you’re using both the Swift and Objective-C APIs**, you likely require both ReactiveCocoa and [ReactiveObjCBridge][], which depend on [ReactiveSwift][] and [ReactiveObjC][]. -**Attention:** If youre using ReactiveCocoa, you'll most likely need to import ReactiveSwift as well when using classes or operators that are implemented in ReactiveSwift. +**Attention:** If you're using ReactiveCocoa, you'll most likely need to import ReactiveSwift as well when using classes or operators that are implemented in ReactiveSwift. #### ReactiveCocoa The ReactiveCocoa library is newly focused on Swift and the UI layers of Apple’s platforms, building on the work of [Rex](https://github.com/neilpa/Rex). @@ -41,7 +41,7 @@ We hope that this move will enable continued support of ReactiveObjC. [ReactiveObjC]: https://github.com/ReactiveCocoa/ReactiveObjC -##### ReactiveObjCBridge +#### ReactiveObjCBridge Moving the Swift and Objective-C APIs to separate repositories meant that a new home was needed for the bridging layer between the two. This bridge is an important tool for users that are working in mixed-language code bases. Whether you are slowly adding Swift to a mature product built with the ReactiveCocoa Objective-C APIs, or looking to adopt ReactiveCocoa in a mixed code base, the bridge is required to communicate between Swift and Objective-C code. From af67e4e337c282ecdb3c391b926187674ce1d394 Mon Sep 17 00:00:00 2001 From: Anders Date: Sun, 5 Feb 2017 00:02:59 +0800 Subject: [PATCH 0718/1028] Workaround to compiler issues. --- ReactiveCocoa/DelegateProxy.swift | 40 +++++++++++++++-------- ReactiveCocoa/UIKit/iOS/UISearchBar.swift | 9 ++--- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/ReactiveCocoa/DelegateProxy.swift b/ReactiveCocoa/DelegateProxy.swift index 659b25a321..697c86f22e 100644 --- a/ReactiveCocoa/DelegateProxy.swift +++ b/ReactiveCocoa/DelegateProxy.swift @@ -37,18 +37,28 @@ internal class DelegateProxy: NSObject { private let hasSwizzledKey = AssociationKey(default: false) -internal protocol DelegateProxyProtocol: class { - associatedtype Delegate: NSObjectProtocol - - weak var forwardee: Delegate? { get set } - - init(_ originalSetter: @escaping (AnyObject) -> Void) -} +extension DelegateProxy { + // FIXME: This is a workaround to a compiler issue, where any use of `Self` + // through a protocol would result in the following error messages: + // 1. PHI node operands are not the same type as the result! + // 2. LLVM ERROR: Broken function found, compilation aborted! + internal static func proxy>( + for instance: NSObject, + setter: Selector, + getter: Selector, + _ key: StaticString = #function + ) -> P { + return _proxy(for: instance, setter: setter, getter: getter, key) as! P + } -extension DelegateProxy: DelegateProxyProtocol {} + private static func _proxy( + for instance: NSObject, + setter: Selector, + getter: Selector, + _ key: StaticString = #function + ) -> AnyObject { + let key = AssociationKey?>(key) -extension DelegateProxyProtocol { - internal static func proxy(for instance: NSObject, setter: Selector, getter: Selector, _ key: AssociationKey) -> Self { return instance.synchronized { if let proxy = instance.associations.value(forKey: key) { return proxy @@ -58,7 +68,7 @@ extension DelegateProxyProtocol { // Hide the original setter, and redirect subsequent delegate assignment // to the proxy. - synchronized(subclass) { + try! ReactiveCocoa.synchronized(subclass) { let subclassAssociations = Associations(subclass as AnyObject) if !subclassAssociations.value(forKey: hasSwizzledKey) { @@ -81,8 +91,8 @@ extension DelegateProxyProtocol { // Set the proxy as the delegate. let realClass: AnyClass = class_getSuperclass(subclass) - let originalSetterImpl = class_getMethodImplementation(realClass, setter) - let getterImpl = class_getMethodImplementation(realClass, getter) + let originalSetterImpl: IMP = class_getMethodImplementation(realClass, setter) + let getterImpl: IMP = class_getMethodImplementation(realClass, getter) typealias Setter = @convention(c) (AnyObject, Selector, AnyObject) -> Void typealias Getter = @convention(c) (AnyObject, Selector) -> NSObject? @@ -97,7 +107,9 @@ extension DelegateProxyProtocol { instance.associations.setValue(proxy, forKey: key) - proxy.forwardee = unsafeBitCast(getterImpl, to: Getter.self)(instance, getter) as! Delegate? + let original = unsafeBitCast(getterImpl, to: Getter.self)(instance, getter) as! Delegate? + proxy.forwardee = original + unsafeBitCast(originalSetterImpl, to: Setter.self)(instance, setter, proxy) return proxy diff --git a/ReactiveCocoa/UIKit/iOS/UISearchBar.swift b/ReactiveCocoa/UIKit/iOS/UISearchBar.swift index 7c1920375d..d3ed6b43b4 100644 --- a/ReactiveCocoa/UIKit/iOS/UISearchBar.swift +++ b/ReactiveCocoa/UIKit/iOS/UISearchBar.swift @@ -12,14 +12,11 @@ private class SearchBarDelegateProxy: DelegateProxy, UISear } } -private let proxyKey = AssociationKey() - extension Reactive where Base: UISearchBar { private var proxy: SearchBarDelegateProxy { - return SearchBarDelegateProxy.proxy(for: base, - setter: #selector(setter: base.delegate), - getter: #selector(getter: base.delegate), - proxyKey) + return .proxy(for: base, + setter: #selector(setter: base.delegate), + getter: #selector(getter: base.delegate)) } /// Sets the text of the search bar. From f94682b151bcfc740ffb854c5b88e537d6a44125 Mon Sep 17 00:00:00 2001 From: Anders Date: Sun, 5 Feb 2017 00:04:31 +0800 Subject: [PATCH 0719/1028] Removed UISearchBar docs that are no longer necessary. --- ReactiveCocoa/UIKit/iOS/UISearchBar.swift | 8 -------- 1 file changed, 8 deletions(-) diff --git a/ReactiveCocoa/UIKit/iOS/UISearchBar.swift b/ReactiveCocoa/UIKit/iOS/UISearchBar.swift index d3ed6b43b4..c89bdbff77 100644 --- a/ReactiveCocoa/UIKit/iOS/UISearchBar.swift +++ b/ReactiveCocoa/UIKit/iOS/UISearchBar.swift @@ -26,10 +26,6 @@ extension Reactive where Base: UISearchBar { /// A signal of text values emitted by the search bar upon end of editing. /// - /// - important: Creating this Signal will make the reactive extension - /// provider the delegate of the search bar. Setting your own delegate is - /// not supported and will result in a runtime error. - /// /// - note: To observe text values that change on all editing events, /// see `continuousTextValues`. public var textValues: Signal { @@ -39,10 +35,6 @@ extension Reactive where Base: UISearchBar { /// A signal of text values emitted by the search bar upon any changes. /// - /// - important: Creating this Signal will make the reactive extension - /// provider the delegate of the search bar. Setting your own delegate is - /// not supported and will result in a runtime error. - /// /// - note: To observe text values only when editing ends, see `textValues`. public var continuousTextValues: Signal { return proxy.intercept(#selector(UISearchBarDelegate.searchBar(_:textDidChange:))) From 7ac4e142a23a026a5883b6233bdcd70d74711a8c Mon Sep 17 00:00:00 2001 From: Dave Caunt Date: Tue, 7 Feb 2017 11:59:10 +0000 Subject: [PATCH 0720/1028] Add UIPickerView reactive extensions --- ReactiveCocoa.xcodeproj/project.pbxproj | 8 + ReactiveCocoa/DelegateProxy.swift | 6 + ReactiveCocoa/UIKit/iOS/UIPickerView.swift | 43 +++++ .../UIKit/UIPickerViewSpec.swift | 153 ++++++++++++++++++ 4 files changed, 210 insertions(+) create mode 100644 ReactiveCocoa/UIKit/iOS/UIPickerView.swift create mode 100644 ReactiveCocoaTests/UIKit/UIPickerViewSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 1ff3f92ffc..01d08ff70c 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -230,8 +230,10 @@ BF4335681E02EF0600AC88DD /* UIScrollViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF4335661E02EEDE00AC88DD /* UIScrollViewSpec.swift */; }; BFA6B94D1A7604D400C846D1 /* SignalProducerNimbleMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */; }; BFA6B94E1A7604D500C846D1 /* SignalProducerNimbleMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */; }; + BFBD68451E48DBD3003CB580 /* UIPickerViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFBD68431E48DA21003CB580 /* UIPickerViewSpec.swift */; }; BFCF775F1DFAD8A50058006E /* UISearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCF775E1DFAD8A50058006E /* UISearchBar.swift */; }; BFCF77621DFAD9440058006E /* UISearchBarSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCF77601DFAD9120058006E /* UISearchBarSpec.swift */; }; + BFE1458A1E439AB000208736 /* UIPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE145881E43991A00208736 /* UIPickerView.swift */; }; CD0C45DE1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */; }; CD0C45DF1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */; }; CD0C45E01CC9A288009F5BF0 /* DynamicProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */; }; @@ -431,8 +433,10 @@ BF4335641E02AC7600AC88DD /* UIScrollView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIScrollView.swift; sourceTree = ""; }; BF4335661E02EEDE00AC88DD /* UIScrollViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIScrollViewSpec.swift; sourceTree = ""; }; BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalProducerNimbleMatchers.swift; sourceTree = ""; }; + BFBD68431E48DA21003CB580 /* UIPickerViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIPickerViewSpec.swift; sourceTree = ""; }; BFCF775E1DFAD8A50058006E /* UISearchBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISearchBar.swift; sourceTree = ""; }; BFCF77601DFAD9120058006E /* UISearchBarSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISearchBarSpec.swift; sourceTree = ""; }; + BFE145881E43991A00208736 /* UIPickerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIPickerView.swift; sourceTree = ""; }; CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DynamicProperty.swift; sourceTree = ""; }; CD8401821CEE8ED7009F0ABF /* CocoaActionSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaActionSpec.swift; sourceTree = ""; }; CDC42E2E1AE7AB8B00965373 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -628,6 +632,7 @@ 4191394B1DBA002C0043C9D1 /* UIGestureRecognizerSpec.swift */, 9A1D062B1D93EA7E00ACF44C /* UIImageViewSpec.swift */, 9A1D062C1D93EA7E00ACF44C /* UILabelSpec.swift */, + BFBD68431E48DA21003CB580 /* UIPickerViewSpec.swift */, 9A1D062D1D93EA7E00ACF44C /* UIProgressViewSpec.swift */, BF4335661E02EEDE00AC88DD /* UIScrollViewSpec.swift */, BFCF77601DFAD9120058006E /* UISearchBarSpec.swift */, @@ -689,6 +694,7 @@ children = ( 9A1D05F21D93E9F100ACF44C /* UIDatePicker.swift */, 9AAD49871DED2C350068EC9B /* UIKeyboard.swift */, + BFE145881E43991A00208736 /* UIPickerView.swift */, 3BCAAC791DEE19BC00B30335 /* UIRefreshControl.swift */, BFCF775E1DFAD8A50058006E /* UISearchBar.swift */, 53AC46CB1DD6F97400C799E1 /* UISlider.swift */, @@ -1351,6 +1357,7 @@ 9AA0BD901DDE29F800531FCF /* NSObject+ObjCRuntime.swift in Sources */, 419139461DB910570043C9D1 /* UIGestureRecognizer.swift in Sources */, 9AA0BD7D1DDE03DE00531FCF /* ObjC+Runtime.swift in Sources */, + BFE1458A1E439AB000208736 /* UIPickerView.swift in Sources */, 9A1D060A1D93EA0000ACF44C /* UISwitch.swift in Sources */, 9A1D065C1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, 9A1D06071D93EA0000ACF44C /* UILabel.swift in Sources */, @@ -1385,6 +1392,7 @@ D0A2260F1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */, BFA6B94E1A7604D500C846D1 /* SignalProducerNimbleMatchers.swift in Sources */, B696FB821A7640C00075236D /* TestError.swift in Sources */, + BFBD68451E48DBD3003CB580 /* UIPickerViewSpec.swift in Sources */, 9A1D064C1D93EA7E00ACF44C /* UISwitchSpec.swift in Sources */, 4ABEFE281DCFCFA90066A8C2 /* UICollectionViewSpec.swift in Sources */, 9A1D06441D93EA7E00ACF44C /* UIImageViewSpec.swift in Sources */, diff --git a/ReactiveCocoa/DelegateProxy.swift b/ReactiveCocoa/DelegateProxy.swift index 697c86f22e..21bd8b6f90 100644 --- a/ReactiveCocoa/DelegateProxy.swift +++ b/ReactiveCocoa/DelegateProxy.swift @@ -26,6 +26,12 @@ internal class DelegateProxy: NSObject { return self.reactive.trigger(for: selector) } + func intercept(_ selector: Selector) -> Signal<[Any?], NoError> { + interceptedSelectors.insert(selector) + originalSetter(self) + return self.reactive.signal(for: selector) + } + override func responds(to selector: Selector!) -> Bool { if interceptedSelectors.contains(selector) { return true diff --git a/ReactiveCocoa/UIKit/iOS/UIPickerView.swift b/ReactiveCocoa/UIKit/iOS/UIPickerView.swift new file mode 100644 index 0000000000..a6c5979972 --- /dev/null +++ b/ReactiveCocoa/UIKit/iOS/UIPickerView.swift @@ -0,0 +1,43 @@ +import ReactiveSwift +import enum Result.NoError +import UIKit + +private class PickerViewDelegateProxy: DelegateProxy, UIPickerViewDelegate { + @objc func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { + forwardee?.pickerView?(pickerView, didSelectRow: row, inComponent: component) + } +} + +extension Reactive where Base: UIPickerView { + + private var proxy: PickerViewDelegateProxy { + return .proxy(for: base, + setter: #selector(setter: base.delegate), + getter: #selector(getter: base.delegate)) + } + + /// Sets the selected row in the specified component, without animating the + /// position. + public func selectedRow(inComponent component: Int) -> BindingTarget { + return makeBindingTarget { $0.selectRow($1, inComponent: component, animated: false) } + } + + /// Reloads all components + public var reloadAllComponents: BindingTarget<()> { + return makeBindingTarget { base, _ in base.reloadAllComponents() } + } + + /// Reloads the specified component + public var reloadComponent: BindingTarget { + return makeBindingTarget { $0.reloadComponent($1) } + } + + /// Create a signal which sends a `value` event for each row selection + /// + /// - returns: + /// A trigger signal. + public var selections: Signal<(Int, Int), NoError> { + return proxy.intercept(#selector(UIPickerViewDelegate.pickerView(_:didSelectRow:inComponent:))) + .map { ($0[1] as! Int, $0[2] as! Int) } + } +} diff --git a/ReactiveCocoaTests/UIKit/UIPickerViewSpec.swift b/ReactiveCocoaTests/UIKit/UIPickerViewSpec.swift new file mode 100644 index 0000000000..41cb976288 --- /dev/null +++ b/ReactiveCocoaTests/UIKit/UIPickerViewSpec.swift @@ -0,0 +1,153 @@ +import ReactiveSwift +import ReactiveCocoa +import UIKit +import Quick +import Nimble +import enum Result.NoError + +private final class PickerDataSource: NSObject, UIPickerViewDataSource { + @objc func numberOfComponents(in pickerView: UIPickerView) -> Int { + return 2 + } + + @objc func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { + return 4 + } +} + +final class UIPickerViewSpec: QuickSpec { + override func spec() { + var dataSource: UIPickerViewDataSource! + var pickerView: TestPickerView! + weak var _pickerView: UIPickerView? + + beforeEach { + autoreleasepool { + dataSource = PickerDataSource() + + pickerView = TestPickerView() + pickerView.dataSource = dataSource + pickerView.reloadAllComponents() + _pickerView = pickerView + } + } + + afterEach { + autoreleasepool { + dataSource = nil + pickerView = nil + } + expect(_pickerView).toEventually(beNil()) + } + + it("should accept changes from bindings to selected rows") { + + let (pipeSignal, observer) = Signal.pipe() + pickerView.reactive.selectedRow(inComponent: 0) <~ SignalProducer(pipeSignal) + + let (anotherPipeSignal, anotherObserver) = Signal.pipe() + pickerView.reactive.selectedRow(inComponent: 1) <~ SignalProducer(anotherPipeSignal) + + observer.send(value: 1) + expect(pickerView.selectedRow(inComponent: 0)) == 1 + + anotherObserver.send(value: 3) + expect(pickerView.selectedRow(inComponent: 1)) == 3 + + observer.send(value: 2) + expect(pickerView.selectedRow(inComponent: 0)) == 2 + } + + it("should emit user initiated changes for row selection") { + var latestValue = (0, 0) + pickerView.reactive.selections.observeValues { + latestValue = $0 + } + + pickerView.selectRow(1, inComponent: 0, animated: false) + pickerView.delegate!.pickerView!(pickerView, didSelectRow: 1, inComponent: 0) + expect(latestValue.0) == pickerView.selectedRow(inComponent: 0) + expect(latestValue.1) == 0 + + pickerView.selectRow(2, inComponent: 1, animated: false) + pickerView.delegate!.pickerView!(pickerView, didSelectRow: 2, inComponent: 1) + expect(latestValue.0) == pickerView.selectedRow(inComponent: 1) + expect(latestValue.1) == 1 + } + + it("invokes reloadAllComponents whenever the bound signal sends a value") { + let (signal, observer) = Signal<(), NoError>.pipe() + + var reloadAllComponentsCount = 0 + + pickerView.reloadAllComponentsSignal.observeValues { + reloadAllComponentsCount += 1 + } + + pickerView.reactive.reloadAllComponents <~ signal + + observer.send(value: ()) + observer.send(value: ()) + + expect(reloadAllComponentsCount) == 2 + } + + it("invokes reloadComponent whenever the bound signal sends a value") { + let (signal, observer) = Signal.pipe() + + var reloadFirstComponentCount = 0 + var reloadSecondComponentCount = 0 + + pickerView.reloadComponentSignal.observeValues { component in + if (component == 0) { + reloadFirstComponentCount += 1 + } else if (component == 1) { + reloadSecondComponentCount += 1 + } + } + + pickerView.reactive.reloadComponent <~ signal + + observer.send(value: 3) + expect(reloadFirstComponentCount) == 0 + expect(reloadSecondComponentCount) == 0 + + observer.send(value: 0) + observer.send(value: 0) + expect(reloadFirstComponentCount) == 2 + + observer.send(value: 1) + expect(reloadSecondComponentCount) == 1 + } + } +} + +private final class TestPickerView: UIPickerView { + let reloadAllComponentsSignal: Signal<(), NoError> + private let reloadAllComponentsObserver: Signal<(), NoError>.Observer + + let reloadComponentSignal: Signal + private let reloadComponentObserver: Signal.Observer + + init() { + (reloadAllComponentsSignal, reloadAllComponentsObserver) = Signal.pipe() + (reloadComponentSignal, reloadComponentObserver) = Signal.pipe() + super.init(frame: .zero) + } + + required init?(coder aDecoder: NSCoder) { + (reloadAllComponentsSignal, reloadAllComponentsObserver) = Signal.pipe() + (reloadComponentSignal, reloadComponentObserver) = Signal.pipe() + super.init(coder: aDecoder) + } + + override func reloadAllComponents() { + super.reloadAllComponents() + reloadAllComponentsObserver.send(value: ()) + } + + override func reloadComponent(_ component: Int) { + super.reloadComponent(component) + reloadComponentObserver.send(value: component) + } +} From 51470c8a11b44197c1c24e57a0dee0923c06cf34 Mon Sep 17 00:00:00 2001 From: Dave Caunt Date: Wed, 8 Feb 2017 15:26:53 +0000 Subject: [PATCH 0721/1028] Add Selection type for UIPickerView extension --- ReactiveCocoa/UIKit/iOS/UIPickerView.swift | 9 +++++++-- ReactiveCocoaTests/UIKit/UIPickerViewSpec.swift | 10 +++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/ReactiveCocoa/UIKit/iOS/UIPickerView.swift b/ReactiveCocoa/UIKit/iOS/UIPickerView.swift index a6c5979972..6757bb6275 100644 --- a/ReactiveCocoa/UIKit/iOS/UIPickerView.swift +++ b/ReactiveCocoa/UIKit/iOS/UIPickerView.swift @@ -8,6 +8,11 @@ private class PickerViewDelegateProxy: DelegateProxy, UIPi } } +public struct UIPickerViewSelection { + public let row: Int + public let component: Int +} + extension Reactive where Base: UIPickerView { private var proxy: PickerViewDelegateProxy { @@ -36,8 +41,8 @@ extension Reactive where Base: UIPickerView { /// /// - returns: /// A trigger signal. - public var selections: Signal<(Int, Int), NoError> { + public var selections: Signal { return proxy.intercept(#selector(UIPickerViewDelegate.pickerView(_:didSelectRow:inComponent:))) - .map { ($0[1] as! Int, $0[2] as! Int) } + .map { UIPickerViewSelection(row: $0[1] as! Int, component: $0[2] as! Int) } } } diff --git a/ReactiveCocoaTests/UIKit/UIPickerViewSpec.swift b/ReactiveCocoaTests/UIKit/UIPickerViewSpec.swift index 41cb976288..6a967b76b8 100644 --- a/ReactiveCocoaTests/UIKit/UIPickerViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIPickerViewSpec.swift @@ -59,20 +59,20 @@ final class UIPickerViewSpec: QuickSpec { } it("should emit user initiated changes for row selection") { - var latestValue = (0, 0) + var latestValue: UIPickerViewSelection! pickerView.reactive.selections.observeValues { latestValue = $0 } pickerView.selectRow(1, inComponent: 0, animated: false) pickerView.delegate!.pickerView!(pickerView, didSelectRow: 1, inComponent: 0) - expect(latestValue.0) == pickerView.selectedRow(inComponent: 0) - expect(latestValue.1) == 0 + expect(latestValue.component) == 0 + expect(latestValue.row) == 1 pickerView.selectRow(2, inComponent: 1, animated: false) pickerView.delegate!.pickerView!(pickerView, didSelectRow: 2, inComponent: 1) - expect(latestValue.0) == pickerView.selectedRow(inComponent: 1) - expect(latestValue.1) == 1 + expect(latestValue.component) == 1 + expect(latestValue.row) == 2 } it("invokes reloadAllComponents whenever the bound signal sends a value") { From 2600b7e3b1c8b51a6f1708a7d674862e71c14c39 Mon Sep 17 00:00:00 2001 From: Alper Cugun Date: Wed, 8 Feb 2017 18:04:16 +0100 Subject: [PATCH 0722/1028] Correctly initialize the submodules This seems to be a bug as discussed in #3383 --- .../Pages/Sandbox.xcplaygroundpage/Contents.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa-iOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift b/ReactiveCocoa-iOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift index 98f546ec96..55082bfd30 100644 --- a/ReactiveCocoa-iOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift +++ b/ReactiveCocoa-iOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift @@ -2,7 +2,7 @@ > ## IMPORTANT: To use `ReactiveCocoa-iOS.playground`, please: 1. Retrieve the project dependencies using one of the following terminal commands from the ReactiveCocoa project root directory: -- `git submodule update --init` +- `git submodule update --init --recursive` **OR**, if you have [Carthage](https://github.com/Carthage/Carthage) installed - `carthage checkout --no-use-binaries` 1. Open `ReactiveCocoa.xcworkspace` From 0ad76d8d24ef7b10f41b83ee152088bd5683ca2f Mon Sep 17 00:00:00 2001 From: kean Date: Thu, 9 Feb 2017 14:48:12 +0300 Subject: [PATCH 0723/1028] Fix #selector usage in README and docs --- CHANGELOG.md | 2 +- Documentation/README.md | 2 +- README.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 06a05a93b8..32806b7984 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -68,7 +68,7 @@ RAC 5.0 includes a few object interception tools from ReactiveObjC, remastered f ```swift // Notify after every time `viewWillAppear(_:)` is called. - let appearing = view.reactive.trigger(for: #selector(viewWillAppear(_:))) + let appearing = viewController.reactive.trigger(for: #selector(UIViewController.viewWillAppear(_:))) ``` 1. **Object Lifetime** diff --git a/Documentation/README.md b/Documentation/README.md index f17d87636e..78d6dd2cd8 100644 --- a/Documentation/README.md +++ b/Documentation/README.md @@ -22,7 +22,7 @@ ReactiveCocoa includes a few object interception tools from ReactiveObjC, remast ```swift // Notify after every time `viewWillAppear(_:)` is called. - let appearing = view.reactive.trigger(for: #selector(viewWillAppear(_:))) + let appearing = viewController.reactive.trigger(for: #selector(UIViewController.viewWillAppear(_:))) ``` 1. **Object Lifetime** diff --git a/README.md b/README.md index 608ec16e2b..761106db92 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ __ReactiveCocoa__ wraps various aspects of Cocoa frameworks with the declarative ```swift // Notify after every time `viewWillAppear(_:)` is called. - let appearing = view.reactive.trigger(for: #selector(viewWillAppear(_:))) + let appearing = viewController.reactive.trigger(for: #selector(UIViewController.viewWillAppear(_:))) // Observe the lifetime of `object`. object.reactive.lifetime.ended.observeCompleted(doCleanup) From d06e4843cee0de03b02ca86c13523c20f63ec0ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alper=20=C3=87ugun?= Date: Thu, 9 Feb 2017 14:47:22 +0100 Subject: [PATCH 0724/1028] Added parameter to the other documentation as well. --- .../Pages/Sandbox.xcplaygroundpage/Contents.swift | 2 +- .../Pages/Sandbox.xcplaygroundpage/Contents.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoa-macOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift b/ReactiveCocoa-macOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift index a31483f7fe..acbb9a577b 100644 --- a/ReactiveCocoa-macOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift +++ b/ReactiveCocoa-macOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift @@ -2,7 +2,7 @@ > ## IMPORTANT: To use `ReactiveCocoa-macOS.playground`, please: 1. Retrieve the project dependencies using one of the following terminal commands from the ReactiveCocoa project root directory: -- `git submodule update --init` +- `git submodule update --init --recursive` **OR**, if you have [Carthage](https://github.com/Carthage/Carthage) installed - `carthage checkout --no-use-binaries` 1. Open `ReactiveCocoa.xcworkspace` diff --git a/ReactiveCocoa-tvOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift b/ReactiveCocoa-tvOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift index ae9e201761..455016c32f 100644 --- a/ReactiveCocoa-tvOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift +++ b/ReactiveCocoa-tvOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift @@ -2,7 +2,7 @@ > ## IMPORTANT: To use `ReactiveCocoa-tvOS.playground`, please: 1. Retrieve the project dependencies using one of the following terminal commands from the ReactiveCocoa project root directory: -- `git submodule update --init` +- `git submodule update --init --recursive` **OR**, if you have [Carthage](https://github.com/Carthage/Carthage) installed - `carthage checkout --no-use-binaries` 1. Open `ReactiveCocoa.xcworkspace` From dcc47054e02eb678148d7650414537b26bcb5cfa Mon Sep 17 00:00:00 2001 From: Dave Caunt Date: Thu, 9 Feb 2017 14:03:25 +0000 Subject: [PATCH 0725/1028] Represent selection with a named tuple --- ReactiveCocoa/UIKit/iOS/UIPickerView.swift | 9 ++------- ReactiveCocoaTests/UIKit/UIPickerViewSpec.swift | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/ReactiveCocoa/UIKit/iOS/UIPickerView.swift b/ReactiveCocoa/UIKit/iOS/UIPickerView.swift index 6757bb6275..2e792d21bf 100644 --- a/ReactiveCocoa/UIKit/iOS/UIPickerView.swift +++ b/ReactiveCocoa/UIKit/iOS/UIPickerView.swift @@ -8,11 +8,6 @@ private class PickerViewDelegateProxy: DelegateProxy, UIPi } } -public struct UIPickerViewSelection { - public let row: Int - public let component: Int -} - extension Reactive where Base: UIPickerView { private var proxy: PickerViewDelegateProxy { @@ -41,8 +36,8 @@ extension Reactive where Base: UIPickerView { /// /// - returns: /// A trigger signal. - public var selections: Signal { + public var selections: Signal<(row: Int, component: Int), NoError> { return proxy.intercept(#selector(UIPickerViewDelegate.pickerView(_:didSelectRow:inComponent:))) - .map { UIPickerViewSelection(row: $0[1] as! Int, component: $0[2] as! Int) } + .map { (row: $0[1] as! Int, component: $0[2] as! Int) } } } diff --git a/ReactiveCocoaTests/UIKit/UIPickerViewSpec.swift b/ReactiveCocoaTests/UIKit/UIPickerViewSpec.swift index 6a967b76b8..b6ecd0e195 100644 --- a/ReactiveCocoaTests/UIKit/UIPickerViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIPickerViewSpec.swift @@ -59,7 +59,7 @@ final class UIPickerViewSpec: QuickSpec { } it("should emit user initiated changes for row selection") { - var latestValue: UIPickerViewSelection! + var latestValue: (row: Int, component: Int)! pickerView.reactive.selections.observeValues { latestValue = $0 } From 6c6727d3224542e2568a252237db790367cf095e Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 28 Feb 2017 21:50:00 +0800 Subject: [PATCH 0726/1028] Update ReactiveSwift to 1.1.0. --- Cartfile.resolved | 4 ++-- Carthage/Checkouts/Quick | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cartfile.resolved b/Cartfile.resolved index b18139e1fe..a965c5ff51 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,5 +1,5 @@ github "Quick/Nimble" "v5.1.1" -github "Quick/Quick" "v1.0.0" +github "Quick/Quick" "v1.1.0" +github "ReactiveCocoa/ReactiveSwift" "1.1.0" github "antitypical/Result" "3.1.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" -github "ReactiveCocoa/ReactiveSwift" "1.0.0" diff --git a/Carthage/Checkouts/Quick b/Carthage/Checkouts/Quick index 9c032315fa..e4fa1e85c0 160000 --- a/Carthage/Checkouts/Quick +++ b/Carthage/Checkouts/Quick @@ -1 +1 @@ -Subproject commit 9c032315fafca658668d49f6396f1f24b09a83b2 +Subproject commit e4fa1e85c0305ba4e0866f25812d3fa398f3a048 diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index 7920f196fd..c83de32f1a 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit 7920f196fd5073f4c98e03fa40b4e4b72470b935 +Subproject commit c83de32f1adbcef89618cde4222f69a8bcecd7c2 From 41bcdf79c36f456e79e3fad574cc66a72d33809c Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Wed, 1 Mar 2017 00:15:08 +0900 Subject: [PATCH 0727/1028] Update Cartfile and podspec as well Since RAS 1.0 and 1.1 are not binary compatible, we should explicitly require RAS 1.1. --- Cartfile | 2 +- ReactiveCocoa.podspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cartfile b/Cartfile index c97e0fea63..fee9fdb6a7 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" ~> 1.0 +github "ReactiveCocoa/ReactiveSwift" ~> 1.1 diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index fb75cd748c..af2ff33ebf 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -23,5 +23,5 @@ Pod::Spec.new do |s| s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_map = "ReactiveCocoa/module.modulemap" - s.dependency 'ReactiveSwift', '~> 1.0.0' + s.dependency 'ReactiveSwift', '~> 1.1' end From 52718627ac7addd4894a634e48b38ef4394f86a4 Mon Sep 17 00:00:00 2001 From: Anders Date: Wed, 1 Mar 2017 15:06:42 +0800 Subject: [PATCH 0728/1028] 5.0.1 --- ReactiveCocoa.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index af2ff33ebf..1726e1b3bd 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "5.0.0" + s.version = "5.0.1" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. From 866637c46ae4ca64761ca09c886e2ce25670c3d4 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Wed, 1 Mar 2017 18:53:31 +0900 Subject: [PATCH 0729/1028] Fix a `SchedulerProtocol` usage warning --- ReactiveCocoa/NSObject+BindingTarget.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa/NSObject+BindingTarget.swift b/ReactiveCocoa/NSObject+BindingTarget.swift index fdd89d50a1..d95d384597 100644 --- a/ReactiveCocoa/NSObject+BindingTarget.swift +++ b/ReactiveCocoa/NSObject+BindingTarget.swift @@ -13,7 +13,7 @@ extension Reactive where Base: NSObject { /// /// - returns: /// A binding target that holds no strong references to the object. - public func makeBindingTarget(on scheduler: SchedulerProtocol = UIScheduler(), _ action: @escaping (Base, U) -> Void) -> BindingTarget { + public func makeBindingTarget(on scheduler: Scheduler = UIScheduler(), _ action: @escaping (Base, U) -> Void) -> BindingTarget { return BindingTarget(on: scheduler, lifetime: lifetime) { [weak base = self.base] value in if let base = base { action(base, value) From 3702f866f72f3a7b7c5025ac72ca0d6d5f3312e5 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Sat, 4 Mar 2017 22:57:10 +0900 Subject: [PATCH 0730/1028] Utilize `Lifetime.make()` from RAS 1.1 --- ReactiveCocoa/NSObject+Lifetime.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ReactiveCocoa/NSObject+Lifetime.swift b/ReactiveCocoa/NSObject+Lifetime.swift index 5311d03392..ed90fb60ae 100644 --- a/ReactiveCocoa/NSObject+Lifetime.swift +++ b/ReactiveCocoa/NSObject+Lifetime.swift @@ -18,8 +18,7 @@ extension Reactive where Base: NSObject { return lifetime } - let token = Lifetime.Token() - let lifetime = Lifetime(token) + let (lifetime, token) = Lifetime.make() let objcClass: AnyClass = (base as AnyObject).objcClass let objcClassAssociations = Associations(objcClass as AnyObject) From b0031eee409b890e46ff3b289a86cf42f8436fc6 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Mon, 6 Mar 2017 13:52:30 +0900 Subject: [PATCH 0731/1028] Update Nimble to be built with Swift 3.1 --- .travis.yml | 8 -------- Cartfile.private | 4 ++-- Cartfile.resolved | 2 +- Carthage/Checkouts/Nimble | 2 +- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index b557ab6ea6..e1e2a7a505 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,26 +42,18 @@ matrix: - XCODE_ACTION=build - XCODE_DESTINATION="platform=watchOS Simulator,name=Apple Watch - 38mm" - script: - - brew update - - brew outdated carthage || brew upgrade carthage - carthage build --no-skip-current --platform mac env: - JOB=CARTHAGE-macOS - script: - - brew update - - brew outdated carthage || brew upgrade carthage - carthage build --no-skip-current --platform iOS env: - JOB=CARTHAGE-iOS - script: - - brew update - - brew outdated carthage || brew upgrade carthage - carthage build --no-skip-current --platform tvOS env: - JOB=CARTHAGE-tvOS - script: - - brew update - - brew outdated carthage || brew upgrade carthage - carthage build --no-skip-current --platform watchOS env: - JOB=CARTHAGE-watchOS diff --git a/Cartfile.private b/Cartfile.private index b26ca0850c..04fba310d3 100644 --- a/Cartfile.private +++ b/Cartfile.private @@ -1,3 +1,3 @@ github "jspahrsummers/xcconfigs" "3d9d996" -github "Quick/Quick" ~> 1.0 -github "Quick/Nimble" ~> 5.1 +github "Quick/Quick" ~> 1.1 +github "Quick/Nimble" "8116a83864ee78339798c3ef425c42f6ca6bf034" diff --git a/Cartfile.resolved b/Cartfile.resolved index a965c5ff51..83e47da93d 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,4 +1,4 @@ -github "Quick/Nimble" "v5.1.1" +github "Quick/Nimble" "8116a83864ee78339798c3ef425c42f6ca6bf034" github "Quick/Quick" "v1.1.0" github "ReactiveCocoa/ReactiveSwift" "1.1.0" github "antitypical/Result" "3.1.0" diff --git a/Carthage/Checkouts/Nimble b/Carthage/Checkouts/Nimble index 3720e6b0f6..8116a83864 160000 --- a/Carthage/Checkouts/Nimble +++ b/Carthage/Checkouts/Nimble @@ -1 +1 @@ -Subproject commit 3720e6b0f6de6d6435f79f8a174fb4bb92df5dc4 +Subproject commit 8116a83864ee78339798c3ef425c42f6ca6bf034 From 6743fc74cdaa4ea8659a3d0e164abab727de3e0a Mon Sep 17 00:00:00 2001 From: ningxiaoming Date: Sat, 11 Mar 2017 17:09:21 +0800 Subject: [PATCH 0732/1028] add Sets the textColor of the text field --- ReactiveCocoa/UIKit/UITextField.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ReactiveCocoa/UIKit/UITextField.swift b/ReactiveCocoa/UIKit/UITextField.swift index ca575a5029..2701f441e1 100644 --- a/ReactiveCocoa/UIKit/UITextField.swift +++ b/ReactiveCocoa/UIKit/UITextField.swift @@ -28,6 +28,11 @@ extension Reactive where Base: UITextField { return makeBindingTarget { $0.attributedText = $1 } } + /// Sets the textColor of the text field. + public var textColor: BindingTarget { + return makeBindingTarget { $0.textColor = $1 } + } + /// A signal of attributed text values emitted by the text field upon end of editing. /// /// - note: To observe attributed text values that change on all editing events, From a92ac7540d34c332e6dc904f6e0c4085f75b1b11 Mon Sep 17 00:00:00 2001 From: ningxiaoming Date: Sun, 12 Mar 2017 19:38:19 +0800 Subject: [PATCH 0733/1028] add textColor QuickSpec --- ReactiveCocoaTests/UIKit/UITextFieldSpec.swift | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift index 5bf7aa393a..428911d630 100644 --- a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift @@ -98,5 +98,16 @@ class UITextFieldSpec: QuickSpec { observer.send(value: false) expect(textField.isSecureTextEntry) == false } + + it("should accept changes from bindings to its textColor attribute") { + let (pipeSignal, observer) = Signal.pipe() + textField.reactive.textColor <~ pipeSignal + + observer.send(value: UIColor.red) + expect(textField.textColor == UIColor.red) == true + + observer.send(value: UIColor.blue) + expect(textField.textColor == UIColor.red) == false + } } } From d89ae2fed54868da0d156b5533682d23a31a314f Mon Sep 17 00:00:00 2001 From: Kim Burge Strand Date: Fri, 17 Mar 2017 10:42:03 +0100 Subject: [PATCH 0734/1028] Rename `NSObject.reactive.values(forKeyPath:)` to `producer(forKeyPath:)`. See ReactiveCocoa/ReactiveCocoa#3413. --- ReactiveCocoa.xcodeproj/project.pbxproj | 8 +++++ ReactiveCocoa/Deprecations+Removals.swift | 8 +++++ ReactiveCocoa/DynamicProperty.swift | 2 +- .../NSObject+KeyValueObserving.swift | 2 +- ReactiveCocoaTests/DeprecationsSpec.swift | 30 +++++++++++++++++++ .../InterceptingPerformanceTests.swift | 22 +++++++------- ReactiveCocoaTests/InterceptingSpec.swift | 24 +++++++-------- .../KeyValueObservingSpec.swift | 26 ++++++++-------- 8 files changed, 84 insertions(+), 38 deletions(-) create mode 100644 ReactiveCocoaTests/DeprecationsSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 01d08ff70c..c5a123b59d 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -9,6 +9,9 @@ /* Begin PBXBuildFile section */ 004FD0071E26CDB300A03A82 /* NSButtonSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 004FD0061E26CDB300A03A82 /* NSButtonSpec.swift */; }; 006518761E26865800C3139A /* NSButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 006518751E26865800C3139A /* NSButton.swift */; }; + 3B30EE8C1E7BE529007CC8EF /* DeprecationsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B30EE8B1E7BE529007CC8EF /* DeprecationsSpec.swift */; }; + 3B30EE8D1E7BE529007CC8EF /* DeprecationsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B30EE8B1E7BE529007CC8EF /* DeprecationsSpec.swift */; }; + 3B30EE8E1E7BE529007CC8EF /* DeprecationsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B30EE8B1E7BE529007CC8EF /* DeprecationsSpec.swift */; }; 3BCAAC7A1DEE19BC00B30335 /* UIRefreshControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BCAAC791DEE19BC00B30335 /* UIRefreshControl.swift */; }; 3BCAAC7D1DEE1A2D00B30335 /* UIRefreshControlSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BCAAC7B1DEE1A2300B30335 /* UIRefreshControlSpec.swift */; }; 419139461DB910570043C9D1 /* UIGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 419139431DB910570043C9D1 /* UIGestureRecognizer.swift */; }; @@ -324,6 +327,7 @@ /* Begin PBXFileReference section */ 004FD0061E26CDB300A03A82 /* NSButtonSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSButtonSpec.swift; sourceTree = ""; }; 006518751E26865800C3139A /* NSButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSButton.swift; sourceTree = ""; }; + 3B30EE8B1E7BE529007CC8EF /* DeprecationsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeprecationsSpec.swift; sourceTree = ""; }; 3BCAAC791DEE19BC00B30335 /* UIRefreshControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIRefreshControl.swift; sourceTree = ""; }; 3BCAAC7B1DEE1A2300B30335 /* UIRefreshControlSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIRefreshControlSpec.swift; sourceTree = ""; }; 419139431DB910570043C9D1 /* UIGestureRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIGestureRecognizer.swift; sourceTree = ""; }; @@ -787,6 +791,7 @@ 9A54A2101DDF5B4D001739B3 /* InterceptingPerformanceTests.swift */, 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */, 9A24A8431DE1429600987AF9 /* SwizzlingSpec.swift */, + 3B30EE8B1E7BE529007CC8EF /* DeprecationsSpec.swift */, D04725FA19E49ED7006002AA /* Supporting Files */, ); path = ReactiveCocoaTests; @@ -1211,6 +1216,7 @@ 9A9A129A1DC7A97100D10223 /* UIGestureRecognizerSpec.swift in Sources */, 9A1D06591D93EA7E00ACF44C /* UIViewSpec.swift in Sources */, 53A6BED81DD4BD2C0016C058 /* MKMapViewSpec.swift in Sources */, + 3B30EE8E1E7BE529007CC8EF /* DeprecationsSpec.swift in Sources */, 7DFBED281CDB8DE300EE435B /* DynamicPropertySpec.swift in Sources */, 9A9DFEEB1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */, CD8401851CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, @@ -1324,6 +1330,7 @@ 9A6AAA2B1DB8F85C0013AAEA /* ReusableComponentsSpec.swift in Sources */, CD8401831CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, 9A9DFEE91DA7EFB60039EE1B /* AssociationSpec.swift in Sources */, + 3B30EE8C1E7BE529007CC8EF /* DeprecationsSpec.swift in Sources */, 9A1E72BA1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, 4ABEFE2E1DCFD01F0066A8C2 /* NSTableViewSpec.swift in Sources */, ); @@ -1405,6 +1412,7 @@ 531866FA1DD7925600D1285F /* UIStepperSpec.swift in Sources */, 9A1D06361D93EA7E00ACF44C /* UIActivityIndicatorViewSpec.swift in Sources */, 9A1D06461D93EA7E00ACF44C /* UILabelSpec.swift in Sources */, + 3B30EE8D1E7BE529007CC8EF /* DeprecationsSpec.swift in Sources */, 9A9DFEEA1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */, 9A54A2121DDF5B4D001739B3 /* InterceptingPerformanceTests.swift in Sources */, 9ADFE5A21DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */, diff --git a/ReactiveCocoa/Deprecations+Removals.swift b/ReactiveCocoa/Deprecations+Removals.swift index 2a3e2e848f..dc685c598e 100644 --- a/ReactiveCocoa/Deprecations+Removals.swift +++ b/ReactiveCocoa/Deprecations+Removals.swift @@ -1,6 +1,14 @@ import ReactiveSwift +import enum Result.NoError extension Action { @available(*, unavailable, message:"Use the `CocoaAction` initializers instead.") public var unsafeCocoaAction: CocoaAction { fatalError() } } + +extension Reactive where Base: NSObject { + @available(*, deprecated, renamed: "producer(forKeyPath:)") + public func values(forKeyPath keyPath: String) -> SignalProducer { + return producer(forKeyPath: keyPath) + } +} diff --git a/ReactiveCocoa/DynamicProperty.swift b/ReactiveCocoa/DynamicProperty.swift index ed79540e8a..a197ad8b52 100644 --- a/ReactiveCocoa/DynamicProperty.swift +++ b/ReactiveCocoa/DynamicProperty.swift @@ -36,7 +36,7 @@ public final class DynamicProperty: MutablePropertyProtocol { /// - important: This only works if the object given to init() is KVO-compliant. /// Most UI controls are not! public var producer: SignalProducer { - return (object.map { $0.reactive.values(forKeyPath: keyPath) } ?? .empty) + return (object.map { $0.reactive.producer(forKeyPath: keyPath) } ?? .empty) .map { $0 as! Value } } diff --git a/ReactiveCocoa/NSObject+KeyValueObserving.swift b/ReactiveCocoa/NSObject+KeyValueObserving.swift index 47eee2cf5b..4873836461 100644 --- a/ReactiveCocoa/NSObject+KeyValueObserving.swift +++ b/ReactiveCocoa/NSObject+KeyValueObserving.swift @@ -13,7 +13,7 @@ extension Reactive where Base: NSObject { /// /// - returns: /// A producer emitting values of the property specified by the key path. - public func values(forKeyPath keyPath: String) -> SignalProducer { + public func producer(forKeyPath keyPath: String) -> SignalProducer { return SignalProducer { observer, disposable in disposable += KeyValueObserver.observe( self.base, diff --git a/ReactiveCocoaTests/DeprecationsSpec.swift b/ReactiveCocoaTests/DeprecationsSpec.swift new file mode 100644 index 0000000000..d3bfa48a5f --- /dev/null +++ b/ReactiveCocoaTests/DeprecationsSpec.swift @@ -0,0 +1,30 @@ +import Foundation +import ReactiveCocoa +import ReactiveSwift +import enum Result.NoError +import Quick +import Nimble + +class DeprecationsSpec: QuickSpec { + override func spec() { + describe("NSObject.reactive.values(forKeyPath:)") { + class TestKVOObject: NSObject { + dynamic var value: Int = 0 + } + + it("should observe the initial value and changes for the key path") { + let object = TestKVOObject() + var values: [Int] = [] + + object.reactive.values(forKeyPath: #keyPath(TestKVOObject.value)).startWithValues { value in + values.append(value as! Int) + } + + expect(values) == [0] + + object.value = 1 + expect(values) == [0, 1] + } + } + } +} diff --git a/ReactiveCocoaTests/InterceptingPerformanceTests.swift b/ReactiveCocoaTests/InterceptingPerformanceTests.swift index f0af8a2240..a4ebb43a38 100644 --- a/ReactiveCocoaTests/InterceptingPerformanceTests.swift +++ b/ReactiveCocoaTests/InterceptingPerformanceTests.swift @@ -39,26 +39,26 @@ class InterceptingTests: XCTestCase { // Setter: RAC then KVO let receiver2 = Receiver2() _ = receiver2.reactive.trigger(for: #selector(setter: receiver2.value)) - receiver2.reactive.values(forKeyPath: #keyPath(Receiver2.value)).start() + receiver2.reactive.producer(forKeyPath: #keyPath(Receiver2.value)).start() // Setter: KVO then RAC let receiver3 = Receiver3() - receiver3.reactive.values(forKeyPath: #keyPath(Receiver3.value)).start() + receiver3.reactive.producer(forKeyPath: #keyPath(Receiver3.value)).start() _ = receiver3.reactive.trigger(for: #selector(setter: receiver3.value)) // RAC swizzled getter, and then KVO swizzled setter. let receiver4 = Receiver4() _ = receiver4.reactive.trigger(for: #selector(getter: receiver4.value)) - receiver4.reactive.values(forKeyPath: #keyPath(Receiver4.value)).start() + receiver4.reactive.producer(forKeyPath: #keyPath(Receiver4.value)).start() // KVO swizzled setter, and then RAC swizzled getter. let receiver5 = Receiver5() - receiver5.reactive.values(forKeyPath: #keyPath(Receiver5.value)).start() + receiver5.reactive.producer(forKeyPath: #keyPath(Receiver5.value)).start() _ = receiver5.reactive.trigger(for: #selector(getter: receiver5.value)) // Normal KVO let receiver6 = Receiver6() - receiver6.reactive.values(forKeyPath: #keyPath(Receiver6.value)).start() + receiver6.reactive.producer(forKeyPath: #keyPath(Receiver6.value)).start() } func testInterceptedMessage() { @@ -76,7 +76,7 @@ class InterceptingTests: XCTestCase { let receiver = Receiver2() _ = receiver.reactive.trigger(for: #selector(setter: receiver.value)) - receiver.reactive.values(forKeyPath: #keyPath(Receiver2.value)).start() + receiver.reactive.producer(forKeyPath: #keyPath(Receiver2.value)).start() measure { for i in 0 ..< iterationCount { @@ -88,7 +88,7 @@ class InterceptingTests: XCTestCase { func testKVORACInterceptSetterThenSet() { let receiver = Receiver3() - receiver.reactive.values(forKeyPath: #keyPath(Receiver3.value)).start() + receiver.reactive.producer(forKeyPath: #keyPath(Receiver3.value)).start() _ = receiver.reactive.trigger(for: #selector(setter: receiver.value)) measure { @@ -102,7 +102,7 @@ class InterceptingTests: XCTestCase { let receiver = Receiver4() _ = receiver.reactive.trigger(for: #selector(getter: receiver.value)) - receiver.reactive.values(forKeyPath: #keyPath(Receiver4.value)).start() + receiver.reactive.producer(forKeyPath: #keyPath(Receiver4.value)).start() measure { for _ in 0 ..< iterationCount { @@ -114,7 +114,7 @@ class InterceptingTests: XCTestCase { func testKVORACInterceptSetterThenGet() { let receiver = Receiver5() - receiver.reactive.values(forKeyPath: #keyPath(Receiver5.value)).start() + receiver.reactive.producer(forKeyPath: #keyPath(Receiver5.value)).start() _ = receiver.reactive.trigger(for: #selector(getter: receiver.value)) measure { @@ -126,7 +126,7 @@ class InterceptingTests: XCTestCase { func testJustKVOInterceptSetterThenSet() { let receiver = Receiver6() - receiver.reactive.values(forKeyPath: #keyPath(Receiver6.value)).start() + receiver.reactive.producer(forKeyPath: #keyPath(Receiver6.value)).start() measure { for i in 0 ..< iterationCount { @@ -137,7 +137,7 @@ class InterceptingTests: XCTestCase { func testJustKVOInterceptSetterThenGet() { let receiver = Receiver6() - receiver.reactive.values(forKeyPath: #keyPath(Receiver6.value)).start() + receiver.reactive.producer(forKeyPath: #keyPath(Receiver6.value)).start() measure { for _ in 0 ..< iterationCount { diff --git a/ReactiveCocoaTests/InterceptingSpec.swift b/ReactiveCocoaTests/InterceptingSpec.swift index 367c8e4f47..56f5e61694 100644 --- a/ReactiveCocoaTests/InterceptingSpec.swift +++ b/ReactiveCocoaTests/InterceptingSpec.swift @@ -126,7 +126,7 @@ class InterceptingSpec: QuickSpec { var latestValue: Bool? object.reactive - .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) + .producer(forKeyPath: #keyPath(InterceptedObject.objectValue)) .startWithValues { objectValue in latestValue = objectValue as! Bool? } @@ -159,7 +159,7 @@ class InterceptingSpec: QuickSpec { var latestValue: Bool? object.reactive - .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) + .producer(forKeyPath: #keyPath(InterceptedObject.objectValue)) .startWithValues { objectValue in latestValue = objectValue as! Bool? } @@ -189,7 +189,7 @@ class InterceptingSpec: QuickSpec { var latestValue: Bool? object.reactive - .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) + .producer(forKeyPath: #keyPath(InterceptedObject.objectValue)) .startWithValues { objectValue in latestValue = objectValue as! Bool? } @@ -217,7 +217,7 @@ class InterceptingSpec: QuickSpec { var latestValue: Bool? object.reactive - .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) + .producer(forKeyPath: #keyPath(InterceptedObject.objectValue)) .startWithValues { objectValue in latestValue = objectValue as! Bool? } @@ -243,12 +243,12 @@ class InterceptingSpec: QuickSpec { .observeValues { counter += 1 } object.reactive - .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) + .producer(forKeyPath: #keyPath(InterceptedObject.objectValue)) .start() .dispose() object.reactive - .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) + .producer(forKeyPath: #keyPath(InterceptedObject.objectValue)) .start() expect(counter) == 0 @@ -264,7 +264,7 @@ class InterceptingSpec: QuickSpec { var counter = 0 object.reactive - .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) + .producer(forKeyPath: #keyPath(InterceptedObject.objectValue)) .start() .dispose() @@ -272,7 +272,7 @@ class InterceptingSpec: QuickSpec { .observeValues { counter += 1 } object.reactive - .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) + .producer(forKeyPath: #keyPath(InterceptedObject.objectValue)) .start() expect(counter) == 0 @@ -335,7 +335,7 @@ class InterceptingSpec: QuickSpec { it("should invoke the swizzled `forwardInvocation:` on an instance isa-swizzled by both RAC and KVO.") { object.reactive - .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) + .producer(forKeyPath: #keyPath(InterceptedObject.objectValue)) .start() _ = object.reactive.trigger(for: #selector(object.lifeIsGood)) @@ -509,7 +509,7 @@ class InterceptingSpec: QuickSpec { } object.reactive - .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) + .producer(forKeyPath: #keyPath(InterceptedObject.objectValue)) .start() object.lifeIsGood(42) @@ -609,7 +609,7 @@ class InterceptingSpec: QuickSpec { _ = object.reactive.trigger(for: #selector(object.lifeIsGood)) object.reactive - .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) + .producer(forKeyPath: #keyPath(InterceptedObject.objectValue)) .start() expect((object as AnyObject).objcClass).to(beIdenticalTo(originalClass)) @@ -617,7 +617,7 @@ class InterceptingSpec: QuickSpec { it("should report the original class when it's KVO'd before dynamically subclassing") { object.reactive - .values(forKeyPath: #keyPath(InterceptedObject.objectValue)) + .producer(forKeyPath: #keyPath(InterceptedObject.objectValue)) .start() _ = object.reactive.trigger(for: #selector(object.lifeIsGood)) diff --git a/ReactiveCocoaTests/KeyValueObservingSpec.swift b/ReactiveCocoaTests/KeyValueObservingSpec.swift index efe7286b65..b187732dbc 100644 --- a/ReactiveCocoaTests/KeyValueObservingSpec.swift +++ b/ReactiveCocoaTests/KeyValueObservingSpec.swift @@ -13,7 +13,7 @@ class KeyValueObservingSpec: QuickSpec { var values: [Int] = [] object.reactive - .values(forKeyPath: #keyPath(ObservableObject.rac_value)) + .producer(forKeyPath: #keyPath(ObservableObject.rac_value)) .startWithValues { value in expect(value).notTo(beNil()) values.append(value as! Int) @@ -33,7 +33,7 @@ class KeyValueObservingSpec: QuickSpec { var values: [Int] = [] object.reactive - .values(forKeyPath: #keyPath(ObservableObject.rac_value)) + .producer(forKeyPath: #keyPath(ObservableObject.rac_value)) .startWithValues { value in expect(value).notTo(beNil()) values.append(value as! Int) @@ -56,7 +56,7 @@ class KeyValueObservingSpec: QuickSpec { let object = ObservableObject() object.reactive - .values(forKeyPath: #keyPath(ObservableObject.rac_value)) + .producer(forKeyPath: #keyPath(ObservableObject.rac_value)) .startWithCompleted { completed = true } expect(completed) == false @@ -70,7 +70,7 @@ class KeyValueObservingSpec: QuickSpec { let object = ObservableObject() let disposable = object.reactive - .values(forKeyPath: #keyPath(ObservableObject.rac_value)) + .producer(forKeyPath: #keyPath(ObservableObject.rac_value)) .startWithInterrupted { interrupted = true } expect(interrupted) == false @@ -85,7 +85,7 @@ class KeyValueObservingSpec: QuickSpec { parentObject .reactive - .values(forKeyPath: #keyPath(NestedObservableObject.rac_object.rac_value)) + .producer(forKeyPath: #keyPath(NestedObservableObject.rac_object.rac_value)) .map { $0 as! NSNumber } .map { $0.intValue } .startWithValues { @@ -118,7 +118,7 @@ class KeyValueObservingSpec: QuickSpec { var values: [Int] = [] parentObject .reactive - .values(forKeyPath: "rac_weakObject.rac_value") + .producer(forKeyPath: "rac_weakObject.rac_value") .map { $0 as! NSNumber } .map { $0.intValue } .startWithValues { @@ -159,7 +159,7 @@ class KeyValueObservingSpec: QuickSpec { // https://github.com/ReactiveCocoa/ReactiveCocoa/issues/3329 func invoke() { let op = Operation() - op.reactive.values(forKeyPath: "isFinished").start() + op.reactive.producer(forKeyPath: "isFinished").start() } invoke() @@ -180,7 +180,7 @@ class KeyValueObservingSpec: QuickSpec { testObject .reactive - .values(forKeyPath: #keyPath(ObservableObject.rac_value)) + .producer(forKeyPath: #keyPath(ObservableObject.rac_value)) .skip(first: 1) .take(first: 1) .map { $0 as! NSNumber } @@ -202,7 +202,7 @@ class KeyValueObservingSpec: QuickSpec { testObject .reactive - .values(forKeyPath: #keyPath(ObservableObject.rac_value)) + .producer(forKeyPath: #keyPath(ObservableObject.rac_value)) .skip(first: 1) .take(first: 1) .observe(on: UIScheduler()) @@ -225,7 +225,7 @@ class KeyValueObservingSpec: QuickSpec { testObject .reactive - .values(forKeyPath: #keyPath(ObservableObject.rac_value)) + .producer(forKeyPath: #keyPath(ObservableObject.rac_value)) .observe(on: UIScheduler()) .map { $0 as! NSNumber } .map { $0.intValue } @@ -271,7 +271,7 @@ class KeyValueObservingSpec: QuickSpec { DispatchQueue.concurrentPerform(iterations: numIterations) { index in testObject .reactive - .values(forKeyPath: #keyPath(ObservableObject.rac_value)) + .producer(forKeyPath: #keyPath(ObservableObject.rac_value)) .skip(first: 1) .observe(on: deliveringObserver) .map { $0 as! NSNumber } @@ -293,7 +293,7 @@ class KeyValueObservingSpec: QuickSpec { iterationQueue.async { DispatchQueue.concurrentPerform(iterations: numIterations) { index in let disposable = testObject.reactive - .values(forKeyPath: #keyPath(ObservableObject.rac_value)) + .producer(forKeyPath: #keyPath(ObservableObject.rac_value)) .startWithCompleted {} serialDisposable.inner = disposable @@ -322,7 +322,7 @@ class KeyValueObservingSpec: QuickSpec { } let replayProducer = testObject.reactive - .values(forKeyPath: #keyPath(ObservableObject.rac_value)) + .producer(forKeyPath: #keyPath(ObservableObject.rac_value)) .map { $0 as! NSNumber } .map { $0.intValue } .map { $0 % 2 == 0 } From 9983971bf8e89be0a863a40fe986fbe7f282240c Mon Sep 17 00:00:00 2001 From: Kim Burge Strand Date: Fri, 17 Mar 2017 12:22:16 +0100 Subject: [PATCH 0735/1028] Add `NSObject.reactive.signal(forKeyPath:)`. Tests remain largely the same for both `signal` and `producer` of a key path, with a few special cases for the producer regarding the initial value. There's actually two `NOTE`s in this code, both related to weak references: - if a weak reference in a key path is deallocated, should a change happen with a `nil` value? - it appears that the underlying implementation might keep weak references in a key path alive for longer than they should be alive --- .../NSObject+KeyValueObserving.swift | 28 +- .../KeyValueObservingSpec.swift | 453 ++++++++++-------- 2 files changed, 283 insertions(+), 198 deletions(-) diff --git a/ReactiveCocoa/NSObject+KeyValueObserving.swift b/ReactiveCocoa/NSObject+KeyValueObserving.swift index 4873836461..15a757c835 100644 --- a/ReactiveCocoa/NSObject+KeyValueObserving.swift +++ b/ReactiveCocoa/NSObject+KeyValueObserving.swift @@ -24,6 +24,32 @@ extension Reactive where Base: NSObject { disposable += self.lifetime.ended.observeCompleted(observer.sendCompleted) } } + + /// Create a signal all changes of the property specified by the key path. + /// + /// The signal completes when the object deinitializes. + /// + /// - note: + /// Does not send the initial value. See `producer(forKeyPath:)`. + /// + /// - parameters: + /// - keyPath: The key path of the property to be observed. + /// + /// - returns: + /// A producer emitting values of the property specified by the key path. + public func signal(forKeyPath keyPath: String) -> Signal { + return Signal { observer in + let disposable = CompositeDisposable() + disposable += KeyValueObserver.observe( + self.base, + keyPath: keyPath, + options: [.new], + action: observer.send + ) + disposable += self.lifetime.ended.observeCompleted(observer.sendCompleted) + return disposable + } + } } internal final class KeyValueObserver: NSObject { @@ -126,7 +152,7 @@ extension KeyValueObserver { let observer: KeyValueObserver if isNested { - observer = KeyValueObserver(observing: object, key: keyPathHead, options: options) { object in + observer = KeyValueObserver(observing: object, key: keyPathHead, options: options.union(.initial)) { object in guard let value = object?.value(forKey: keyPathHead) as! NSObject? else { action(nil) return diff --git a/ReactiveCocoaTests/KeyValueObservingSpec.swift b/ReactiveCocoaTests/KeyValueObservingSpec.swift index b187732dbc..b91e383bbe 100644 --- a/ReactiveCocoaTests/KeyValueObservingSpec.swift +++ b/ReactiveCocoaTests/KeyValueObservingSpec.swift @@ -7,151 +7,190 @@ import Nimble class KeyValueObservingSpec: QuickSpec { override func spec() { - describe("NSObject.valuesForKeyPath") { - it("should sends the current value and then the changes for the key path") { + describe("NSObject.signal(forKeyPath:)") { + it("should not send the initial value") { let object = ObservableObject() var values: [Int] = [] object.reactive - .producer(forKeyPath: #keyPath(ObservableObject.rac_value)) - .startWithValues { value in - expect(value).notTo(beNil()) - values.append(value as! Int) - } + .signal(forKeyPath: #keyPath(ObservableObject.rac_value)) + .observeValues { values.append(($0 as! NSNumber).intValue) } - expect(values) == [ 0 ] - - object.rac_value = 1 - expect(values) == [ 0, 1 ] + expect(values) == [] + } - object.rac_value = 2 - expect(values) == [ 0, 1, 2 ] + itBehavesLike("a reactive key value observer") { + [ + "observe": { (object: NSObject, keyPath: String) in + return object.reactive.signal(forKeyPath: keyPath) + } + ] } + } - it("should sends the current value and then the changes for the key path, even if the value actually remains unchanged") { + describe("NSObject.producer(forKeyPath:)") { + it("should send the initial value") { let object = ObservableObject() var values: [Int] = [] object.reactive .producer(forKeyPath: #keyPath(ObservableObject.rac_value)) .startWithValues { value in - expect(value).notTo(beNil()) values.append(value as! Int) } - expect(values) == [ 0 ] - - object.rac_value = 0 - expect(values) == [ 0, 0 ] - - object.rac_value = 0 - expect(values) == [ 0, 0, 0 ] + expect(values) == [0] } - it("should complete when the object deallocates") { - var completed = false + it("should send the initial value for nested key path") { + let parentObject = NestedObservableObject() + var values: [Int] = [] - _ = { - // Use a closure so this object has a shorter lifetime. - let object = ObservableObject() + parentObject + .reactive + .producer(forKeyPath: #keyPath(NestedObservableObject.rac_object.rac_value)) + .startWithValues { values.append(($0 as! NSNumber).intValue) } - object.reactive - .producer(forKeyPath: #keyPath(ObservableObject.rac_value)) - .startWithCompleted { completed = true } + expect(values) == [0] + } - expect(completed) == false - }() + it("should send the initial value for weak nested key path") { + let parentObject = NestedObservableObject() + let innerObject = Optional(ObservableObject()) + parentObject.rac_weakObject = innerObject + var values: [Int] = [] - expect(completed).toEventually(beTruthy()) + parentObject + .reactive + .producer(forKeyPath: "rac_weakObject.rac_value") + .startWithValues { values.append(($0 as! NSNumber).intValue) } + + expect(values) == [0] } - it("should interrupt") { - var interrupted = false + itBehavesLike("a reactive key value observer") { + [ + "observe": { (object: NSObject, keyPath: String) in + return object.reactive.producer(forKeyPath: keyPath) + } + ] + } + } - let object = ObservableObject() - let disposable = object.reactive - .producer(forKeyPath: #keyPath(ObservableObject.rac_value)) - .startWithInterrupted { interrupted = true } + describe("property type and attribute query") { + let object = TestAttributeQueryObject() - expect(interrupted) == false + it("should be able to classify weak references") { + "weakProperty".withCString { cString in + let propertyPointer = class_getProperty(type(of: object), cString) + expect(propertyPointer) != nil - disposable.dispose() - expect(interrupted) == true + if let pointer = propertyPointer { + let attributes = PropertyAttributes(property: pointer) + expect(attributes.isWeak) == true + expect(attributes.isObject) == true + expect(attributes.isBlock) == false + expect(attributes.objectClass).to(beIdenticalTo(NSObject.self)) + } + } } - it("should observe changes in a nested key path") { - let parentObject = NestedObservableObject() - var values: [Int] = [] + it("should be able to classify blocks") { + "block".withCString { cString in + let propertyPointer = class_getProperty(type(of: object), cString) + expect(propertyPointer) != nil - parentObject - .reactive - .producer(forKeyPath: #keyPath(NestedObservableObject.rac_object.rac_value)) - .map { $0 as! NSNumber } - .map { $0.intValue } - .startWithValues { - values.append($0) + if let pointer = propertyPointer { + let attributes = PropertyAttributes(property: pointer) + expect(attributes.isWeak) == false + expect(attributes.isObject) == true + expect(attributes.isBlock) == true + expect(attributes.objectClass).to(beNil()) } + } + } - expect(values) == [0] + it("should be able to classify non object properties") { + "integer".withCString { cString in + let propertyPointer = class_getProperty(type(of: object), cString) + expect(propertyPointer) != nil - parentObject.rac_object.rac_value = 1 - expect(values) == [0, 1] + if let pointer = propertyPointer { + let attributes = PropertyAttributes(property: pointer) + expect(attributes.isWeak) == false + expect(attributes.isObject) == false + expect(attributes.isBlock) == false + expect(attributes.objectClass).to(beNil()) + } + } + } + } + } +} - let oldInnerObject = parentObject.rac_object +// Shared examples to ensure both `signal(forKeyPath:)` and `producer(forKeyPath:)` +// share common behavior. +fileprivate class KeyValueObservingSpecConfiguration: QuickConfiguration { + class Context { + let context: [String: Any] - let newInnerObject = ObservableObject() - parentObject.rac_object = newInnerObject - expect(values) == [0, 1, 0] + init(_ context: [String: Any]) { + self.context = context + } - parentObject.rac_object.rac_value = 10 - oldInnerObject.rac_value = 2 - expect(values) == [0, 1, 0, 10] + func observe(_ object: NSObject, _ keyPath: String) -> SignalProducer { + if let block = context["observe"] as? (NSObject, String) -> Signal { + return SignalProducer(block(object, keyPath)) + } else if let block = context["observe"] as? (NSObject, String) -> SignalProducer { + return block(object, keyPath).skip(first: 1) + } else { + fatalError("What is this?") } + } - it("should observe changes in a nested weak key path") { - let parentObject = NestedObservableObject() - var innerObject = Optional(ObservableObject()) - parentObject.rac_weakObject = innerObject + func isFinished(_ object: Operation) -> SignalProducer { + return observe(object, #keyPath(Operation.isFinished)) + } - // `#keyPath` does not work with weak relationships. + func changes(_ object: NSObject) -> SignalProducer { + return observe(object, #keyPath(ObservableObject.rac_value)) + } - var values: [Int] = [] - parentObject - .reactive - .producer(forKeyPath: "rac_weakObject.rac_value") - .map { $0 as! NSNumber } - .map { $0.intValue } - .startWithValues { - values.append($0) - } + func nestedChanges(_ object: NSObject) -> SignalProducer { + return observe(object, #keyPath(NestedObservableObject.rac_object.rac_value)) + } - expect(values) == [0] + func weakNestedChanges(_ object: NSObject) -> SignalProducer { + // `#keyPath` does not work with weak relationships. + return observe(object, "rac_weakObject.rac_value") + } + } - innerObject?.rac_value = 1 - expect(values) == [0, 1] + override class func configure(_ configuration: Configuration) { + sharedExamples("a reactive key value observer") { (sharedExampleContext: @escaping SharedExampleContext) in + var context: Context! - autoreleasepool { - innerObject = nil - } + beforeEach { context = Context(sharedExampleContext()) } + afterEach { context = nil } - expect(values) == [0, 1] + it("should send new values for the key path (even if the value remains unchanged)") { + let object = ObservableObject() + var values: [Int] = [] - innerObject = ObservableObject() - parentObject.rac_weakObject = innerObject - expect(values) == [0, 1, 0] + context.changes(object).startWithValues { + values.append(($0 as! NSNumber).intValue) + } - innerObject?.rac_value = 10 - expect(values) == [0, 1, 0, 10] - } + expect(values) == [] - it("should not retain replaced value in a nested key path") { - let parentObject = NestedObservableObject() + object.rac_value = 0 + expect(values) == [0] - weak var weakOriginalInner: ObservableObject? = parentObject.rac_object - expect(weakOriginalInner).toNot(beNil()) + object.rac_value = 1 + expect(values) == [0, 1] - parentObject.rac_object = ObservableObject() - expect(weakOriginalInner).to(beNil()) + object.rac_value = 1 + expect(values) == [0, 1, 1] } it("should not crash an Operation") { @@ -159,38 +198,120 @@ class KeyValueObservingSpec: QuickSpec { // https://github.com/ReactiveCocoa/ReactiveCocoa/issues/3329 func invoke() { let op = Operation() - op.reactive.producer(forKeyPath: "isFinished").start() + context.isFinished(op).start() } invoke() } + describe("signal behavior") { + it("should complete when the object deallocates") { + var completed = false + + _ = { + // Use a closure so this object has a shorter lifetime. + let object = ObservableObject() + + context.changes(object).startWithCompleted { + completed = true + } + + expect(completed) == false + }() + + expect(completed).toEventually(beTruthy()) + } + } + + describe("nested key paths") { + it("should observe changes in a nested key path") { + let parentObject = NestedObservableObject() + var values: [Int] = [] + + context.nestedChanges(parentObject).startWithValues { + values.append(($0 as! NSNumber).intValue) + } + + expect(values) == [] + + parentObject.rac_object.rac_value = 1 + expect(values) == [1] + + let oldInnerObject = parentObject.rac_object + + let newInnerObject = ObservableObject() + parentObject.rac_object = newInnerObject + expect(values) == [1, 0] + + parentObject.rac_object.rac_value = 10 + oldInnerObject.rac_value = 2 + expect(values) == [1, 0, 10] + } + + it("should observe changes in a nested weak key path") { + let parentObject = NestedObservableObject() + var innerObject = Optional(ObservableObject()) + parentObject.rac_weakObject = innerObject + var values: [Int] = [] + + context.weakNestedChanges(parentObject).startWithValues { + values.append(($0 as! NSNumber).intValue) + } + + expect(values) == [] + + innerObject?.rac_value = 1 + expect(values) == [1] + + autoreleasepool { + innerObject = nil + } + + // NOTE: [1] or [Optional(1), nil]? + expect(values) == [1] + + innerObject = ObservableObject() + parentObject.rac_weakObject = innerObject + expect(values) == [1, 0] + + innerObject?.rac_value = 10 + expect(values) == [1, 0, 10] + } + + + it("should not retain replaced value in a nested key path") { + let parentObject = NestedObservableObject() + context.nestedChanges(parentObject) + .start() + + weak var weakOriginalInner: ObservableObject? = parentObject.rac_object + expect(weakOriginalInner).toNot(beNil()) + + parentObject.rac_object = ObservableObject() + expect(weakOriginalInner).toEventually(beNil()) + } + } + describe("thread safety") { - var testObject: ObservableObject! var concurrentQueue: DispatchQueue! beforeEach { - testObject = ObservableObject() - concurrentQueue = DispatchQueue(label: "org.reactivecocoa.ReactiveCocoa.DynamicPropertySpec.concurrentQueue", - attributes: .concurrent) + concurrentQueue = DispatchQueue( + label: "org.reactivecocoa.ReactiveCocoa.DynamicPropertySpec.concurrentQueue", + attributes: .concurrent + ) } it("should handle changes being made on another queue") { + let object = ObservableObject() var observedValue = 0 - testObject - .reactive - .producer(forKeyPath: #keyPath(ObservableObject.rac_value)) - .skip(first: 1) + context.changes(object) .take(first: 1) - .map { $0 as! NSNumber } - .map { $0.intValue } - .startWithValues { - observedValue = $0 - } + .startWithValues { observedValue = ($0 as! NSNumber).intValue } concurrentQueue.async { - testObject.rac_value = 2 + object.rac_value = 2 } concurrentQueue.sync(flags: .barrier) {} @@ -198,22 +319,16 @@ class KeyValueObservingSpec: QuickSpec { } it("should handle changes being made on another queue using deliverOn") { + let object = ObservableObject() var observedValue = 0 - testObject - .reactive - .producer(forKeyPath: #keyPath(ObservableObject.rac_value)) - .skip(first: 1) + context.changes(object) .take(first: 1) .observe(on: UIScheduler()) - .map { $0 as! NSNumber } - .map { $0.intValue } - .startWithValues { - observedValue = $0 - } + .startWithValues { observedValue = ($0 as! NSNumber).intValue } concurrentQueue.async { - testObject.rac_value = 2 + object.rac_value = 2 } concurrentQueue.sync(flags: .barrier) {} @@ -221,21 +336,16 @@ class KeyValueObservingSpec: QuickSpec { } it("async disposal of target") { + var object: ObservableObject? = ObservableObject() var observedValue = 0 - testObject - .reactive - .producer(forKeyPath: #keyPath(ObservableObject.rac_value)) + context.changes(object!) .observe(on: UIScheduler()) - .map { $0 as! NSNumber } - .map { $0.intValue } - .startWithValues { - observedValue = $0 - } + .startWithValues { observedValue = ($0 as! NSNumber).intValue } concurrentQueue.async { - testObject.rac_value = 2 - testObject = nil + object!.rac_value = 2 + object = nil } concurrentQueue.sync(flags: .barrier) {} @@ -252,10 +362,14 @@ class KeyValueObservingSpec: QuickSpec { beforeEach { testObject = ObservableObject() - iterationQueue = DispatchQueue(label: "org.reactivecocoa.ReactiveCocoa.RACKVOProxySpec.iterationQueue", - attributes: .concurrent) - concurrentQueue = DispatchQueue(label: "org.reactivecocoa.ReactiveCocoa.DynamicPropertySpec.concurrentQueue", - attributes: .concurrent) + iterationQueue = DispatchQueue( + label: "org.reactivecocoa.ReactiveCocoa.RACKVOProxySpec.iterationQueue", + attributes: .concurrent + ) + concurrentQueue = DispatchQueue( + label: "org.reactivecocoa.ReactiveCocoa.DynamicPropertySpec.concurrentQueue", + attributes: .concurrent + ) } it("attach observers") { @@ -269,10 +383,7 @@ class KeyValueObservingSpec: QuickSpec { var atomicCounter = Int64(0) DispatchQueue.concurrentPerform(iterations: numIterations) { index in - testObject - .reactive - .producer(forKeyPath: #keyPath(ObservableObject.rac_value)) - .skip(first: 1) + context.changes(testObject) .observe(on: deliveringObserver) .map { $0 as! NSNumber } .map { $0.int64Value } @@ -292,8 +403,7 @@ class KeyValueObservingSpec: QuickSpec { iterationQueue.async { DispatchQueue.concurrentPerform(iterations: numIterations) { index in - let disposable = testObject.reactive - .producer(forKeyPath: #keyPath(ObservableObject.rac_value)) + let disposable = context.changes(testObject) .startWithCompleted {} serialDisposable.inner = disposable @@ -321,14 +431,12 @@ class KeyValueObservingSpec: QuickSpec { otherScheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) } - let replayProducer = testObject.reactive - .producer(forKeyPath: #keyPath(ObservableObject.rac_value)) - .map { $0 as! NSNumber } - .map { $0.intValue } - .map { $0 % 2 == 0 } - .observe(on: otherScheduler) - .take(during: lifetime) - .replayLazily(upTo: 1) + let replayProducer = context.changes(testObject) + .map { ($0 as! NSNumber).intValue } + .map { $0 % 2 == 0 } + .observe(on: otherScheduler) + .take(during: lifetime) + .replayLazily(upTo: 1) replayProducer.start() @@ -354,55 +462,6 @@ class KeyValueObservingSpec: QuickSpec { } } } - - describe("property type and attribute query") { - let object = TestAttributeQueryObject() - - it("should be able to classify weak references") { - "weakProperty".withCString { cString in - let propertyPointer = class_getProperty(type(of: object), cString) - expect(propertyPointer) != nil - - if let pointer = propertyPointer { - let attributes = PropertyAttributes(property: pointer) - expect(attributes.isWeak) == true - expect(attributes.isObject) == true - expect(attributes.isBlock) == false - expect(attributes.objectClass).to(beIdenticalTo(NSObject.self)) - } - } - } - - it("should be able to classify blocks") { - "block".withCString { cString in - let propertyPointer = class_getProperty(type(of: object), cString) - expect(propertyPointer) != nil - - if let pointer = propertyPointer { - let attributes = PropertyAttributes(property: pointer) - expect(attributes.isWeak) == false - expect(attributes.isObject) == true - expect(attributes.isBlock) == true - expect(attributes.objectClass).to(beNil()) - } - } - } - - it("should be able to classify non object properties") { - "integer".withCString { cString in - let propertyPointer = class_getProperty(type(of: object), cString) - expect(propertyPointer) != nil - - if let pointer = propertyPointer { - let attributes = PropertyAttributes(property: pointer) - expect(attributes.isWeak) == false - expect(attributes.isObject) == false - expect(attributes.isBlock) == false - expect(attributes.objectClass).to(beNil()) - } - } - } - } } } From a36bd533838b43a4e7d12296d059cfbadf597177 Mon Sep 17 00:00:00 2001 From: Kim Burge Strand Date: Thu, 23 Mar 2017 10:02:30 +0100 Subject: [PATCH 0736/1028] Mark forgotten test from master as pending due to off-topic failure. See https://github.com/ReactiveCocoa/ReactiveCocoa/pull/3413#issuecomment-288329465 --- ReactiveCocoaTests/KeyValueObservingSpec.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoaTests/KeyValueObservingSpec.swift b/ReactiveCocoaTests/KeyValueObservingSpec.swift index b91e383bbe..32881a0830 100644 --- a/ReactiveCocoaTests/KeyValueObservingSpec.swift +++ b/ReactiveCocoaTests/KeyValueObservingSpec.swift @@ -278,8 +278,8 @@ fileprivate class KeyValueObservingSpecConfiguration: QuickConfiguration { expect(values) == [1, 0, 10] } - - it("should not retain replaced value in a nested key path") { + // NOTE: https://github.com/ReactiveCocoa/ReactiveCocoa/pull/3413#issuecomment-288329465 + pending("should not retain replaced value in a nested key path") { let parentObject = NestedObservableObject() context.nestedChanges(parentObject) .start() From 2a8989f3adc8f449686fefeaf8317322fabb0229 Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 24 Mar 2017 16:17:00 +0800 Subject: [PATCH 0737/1028] Fixed an occurrence of exception due to key path tail observers not being detached. This happens specifically when a nested key path is being observed, for example `parent.child`, where `parent` is being replaced by a new object. `KeyValueObserver` is not eager enough to clean up the tails, causing the then-deallocated object (the original `parent`) to have still observers attached. --- .../NSObject+KeyValueObserving.swift | 13 +++++++---- .../KeyValueObservingSpec.swift | 22 +++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/ReactiveCocoa/NSObject+KeyValueObserving.swift b/ReactiveCocoa/NSObject+KeyValueObserving.swift index 47eee2cf5b..8c29defe7d 100644 --- a/ReactiveCocoa/NSObject+KeyValueObserving.swift +++ b/ReactiveCocoa/NSObject+KeyValueObserving.swift @@ -110,13 +110,13 @@ extension KeyValueObserver { // // Attempting to observe non-weak properties using dynamic getters will // result in broken behavior, so don't even try. - let shouldObserveDeinit = keyPathHead.withCString { cString -> Bool in + let (shouldObserveDeinit, isWeak) = keyPathHead.withCString { cString -> (Bool, Bool) in if let propertyPointer = class_getProperty(type(of: object), cString) { let attributes = PropertyAttributes(property: propertyPointer) - return attributes.isObject && attributes.isWeak && attributes.objectClass != NSClassFromString("Protocol") && !attributes.isBlock + return (attributes.isObject && attributes.objectClass != NSClassFromString("Protocol") && !attributes.isBlock, attributes.isWeak) } - return false + return (false, false) } // Establish the observation. @@ -137,7 +137,12 @@ extension KeyValueObserver { if shouldObserveDeinit { let disposable = value.reactive.lifetime.ended.observeCompleted { - action(nil) + if isWeak { + action(nil) + } + + // Detach the key path tail observers eagarly. + headSerialDisposable.inner = nil } headDisposable += disposable } diff --git a/ReactiveCocoaTests/KeyValueObservingSpec.swift b/ReactiveCocoaTests/KeyValueObservingSpec.swift index efe7286b65..e4ef3727b4 100644 --- a/ReactiveCocoaTests/KeyValueObservingSpec.swift +++ b/ReactiveCocoaTests/KeyValueObservingSpec.swift @@ -144,9 +144,31 @@ class KeyValueObservingSpec: QuickSpec { expect(values) == [0, 1, 0, 10] } + it("should not retain the replaced value") { + let parentObject = NestedObservableObject() + + autoreleasepool { + _ = parentObject.reactive + .values(forKeyPath: "rac_object") + .start() + } + + weak var weakOriginalInner: ObservableObject? = parentObject.rac_object + expect(weakOriginalInner).toNot(beNil()) + + parentObject.rac_object = ObservableObject() + expect(weakOriginalInner).to(beNil()) + } + it("should not retain replaced value in a nested key path") { let parentObject = NestedObservableObject() + autoreleasepool { + _ = parentObject.reactive + .values(forKeyPath: "rac_object.rac_value") + .start() + } + weak var weakOriginalInner: ObservableObject? = parentObject.rac_object expect(weakOriginalInner).toNot(beNil()) From a2de5166524ded7e08fcd2a501661baf05467570 Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 7 Feb 2017 11:31:00 +0800 Subject: [PATCH 0738/1028] Hide the NSControl interception. --- ReactiveCocoa.xcodeproj/project.pbxproj | 4 + ReactiveCocoa/AppKit/ActionProxy.swift | 104 ++++++++++++++++++ ReactiveCocoa/AppKit/NSButton.swift | 6 +- ReactiveCocoa/AppKit/NSControl.swift | 39 ++----- ReactiveCocoa/AppKit/NSPopUpButton.swift | 22 +--- ReactiveCocoa/AppKit/NSSegmentedControl.swift | 2 +- 6 files changed, 125 insertions(+), 52 deletions(-) create mode 100644 ReactiveCocoa/AppKit/ActionProxy.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 01d08ff70c..ca69676f00 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -210,6 +210,7 @@ 9AE7C2A51DDD7F5100F7534C /* ObjC+Messages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE7C2A31DDD7F5100F7534C /* ObjC+Messages.swift */; }; 9AE7C2A61DDD7F5100F7534C /* ObjC+Messages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE7C2A31DDD7F5100F7534C /* ObjC+Messages.swift */; }; 9AE7C2A71DDD7F5100F7534C /* ObjC+Messages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE7C2A31DDD7F5100F7534C /* ObjC+Messages.swift */; }; + 9AED64C51E496A3700321004 /* ActionProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AED64C41E496A3700321004 /* ActionProxy.swift */; }; 9AF0EA751D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; 9AF0EA761D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; 9AF0EA771D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; @@ -416,6 +417,7 @@ 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronizing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Synchronizing.swift"; sourceTree = ""; }; 9AE7C2A21DDD768500F7534C /* module.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = ""; }; 9AE7C2A31DDD7F5100F7534C /* ObjC+Messages.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObjC+Messages.swift"; sourceTree = ""; }; + 9AED64C41E496A3700321004 /* ActionProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionProxy.swift; sourceTree = ""; }; 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+BindingTarget.swift"; sourceTree = ""; }; A97451331B3A935E00F48E55 /* watchOS-Application.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Application.xcconfig"; sourceTree = ""; }; A97451341B3A935E00F48E55 /* watchOS-Base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Base.xcconfig"; sourceTree = ""; }; @@ -579,6 +581,7 @@ 9A1D05E91D93E9F100ACF44C /* AppKit */ = { isa = PBXGroup; children = ( + 9AED64C41E496A3700321004 /* ActionProxy.swift */, 4ABEFE2F1DCFD0530066A8C2 /* NSCollectionView.swift */, 9ADE4A881DA6D206005C2AC8 /* NSControl.swift */, 4ABEFE2A1DCFD0030066A8C2 /* NSTableView.swift */, @@ -1281,6 +1284,7 @@ 006518761E26865800C3139A /* NSButton.swift in Sources */, 9A54A21B1DE00D09001739B3 /* ObjC+Selector.swift in Sources */, 9AA0BD8A1DDE153A00531FCF /* ObjC+Constants.swift in Sources */, + 9AED64C51E496A3700321004 /* ActionProxy.swift in Sources */, 9A6AAA261DB8F5280013AAEA /* ReusableComponents.swift in Sources */, 9AA0BD7C1DDE03DE00531FCF /* ObjC+Runtime.swift in Sources */, 9ADFE5A51DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */, diff --git a/ReactiveCocoa/AppKit/ActionProxy.swift b/ReactiveCocoa/AppKit/ActionProxy.swift new file mode 100644 index 0000000000..243fd2cda8 --- /dev/null +++ b/ReactiveCocoa/AppKit/ActionProxy.swift @@ -0,0 +1,104 @@ +import AppKit +import ReactiveSwift +import enum Result.NoError + +internal final class ActionProxy { + internal weak var target: AnyObject? + internal var action: Selector? + internal let signal: Signal + + private let observer: Signal.Observer + private unowned let owner: Owner + + internal init(owner: Owner, lifetime: Lifetime) { + self.owner = owner + (signal, observer) = Signal.pipe() + lifetime.ended.observeCompleted(observer.sendCompleted) + } + + // In AppKit, action messages always have only one parameter. + @objc func consume(_ sender: Any?) { + if let action = action { + if let target = target { + _ = target.perform(action, with: sender) + } else { + NSApp.sendAction(action, to: nil, from: sender) + } + } + + observer.send(value: owner) + } +} + +private let hasSwizzledKey = AssociationKey(default: false) + +@objc internal protocol ActionMessageSending: class { + weak var target: AnyObject? { get set } + var action: Selector? { get set } +} + +extension Reactive where Base: NSObject, Base: ActionMessageSending { + internal var proxy: ActionProxy { + let key = AssociationKey?>((#function as StaticString)) + + return base.synchronized { + if let proxy = base.associations.value(forKey: key) { + return proxy + } + + let proxy = ActionProxy(owner: base, lifetime: lifetime) + base.associations.setValue(proxy, forKey: key) + + proxy.target = base.target + proxy.action = base.action + + base.target = proxy + base.action = #selector(proxy.consume(_:)) + + // Swizzle the instance only after setting up the proxy. + let subclass: AnyClass = swizzleClass(base) + + let targetSetter = #selector(setter: base.target) + let actionSetter = #selector(setter: base.action) + + // Swizzle the original setters, and redirect subsequent target and action + // assignments to the proxy. + try! ReactiveCocoa.synchronized(subclass) { + print(subclass) + let subclassAssociations = Associations(subclass as AnyObject) + + if !subclassAssociations.value(forKey: hasSwizzledKey) { + subclassAssociations.setValue(true, forKey: hasSwizzledKey) + + let targetSetterMethod = class_getInstanceMethod(subclass, targetSetter) + let targetSetterTypeEncoding = method_getTypeEncoding(targetSetterMethod)! + + let newTargetSetterImpl: @convention(block) (NSObject, AnyObject?) -> Void = { object, target in + let proxy = object.associations.value(forKey: key)! + proxy.target = target + } + + class_replaceMethod(subclass, + targetSetter, + imp_implementationWithBlock(newTargetSetterImpl as Any), + targetSetterTypeEncoding) + + let actionSetterMethod = class_getInstanceMethod(subclass, actionSetter) + let actionSetterTypeEncoding = method_getTypeEncoding(actionSetterMethod)! + + let newActionSetterImpl: @convention(block) (NSObject, Selector?) -> Void = { object, selector in + let proxy = object.associations.value(forKey: key)! + proxy.action = selector + } + + class_replaceMethod(subclass, + actionSetter, + imp_implementationWithBlock(newActionSetterImpl as Any), + actionSetterTypeEncoding) + } + } + + return proxy + } + } +} diff --git a/ReactiveCocoa/AppKit/NSButton.swift b/ReactiveCocoa/AppKit/NSButton.swift index fee3668f19..f6626f36dc 100644 --- a/ReactiveCocoa/AppKit/NSButton.swift +++ b/ReactiveCocoa/AppKit/NSButton.swift @@ -20,9 +20,7 @@ extension Reactive where Base: NSButton { action = newValue.map { action in let disposable = CompositeDisposable() disposable += isEnabled <~ action.isEnabled - disposable += trigger.observeValues { [unowned base = self.base] in - action.execute(base) - } + disposable += proxy.signal.observeValues(action.execute) return (action, disposable) } } @@ -31,7 +29,7 @@ extension Reactive where Base: NSButton { /// A signal of integer states (On, Off, Mixed), emitted by the button. public var states: Signal { - return trigger.map { [unowned base = self.base] in base.state } + return proxy.signal.map { $0.state } } /// Sets the button's state diff --git a/ReactiveCocoa/AppKit/NSControl.swift b/ReactiveCocoa/AppKit/NSControl.swift index 3580dfc2c3..8064ae168c 100644 --- a/ReactiveCocoa/AppKit/NSControl.swift +++ b/ReactiveCocoa/AppKit/NSControl.swift @@ -2,6 +2,8 @@ import ReactiveSwift import enum Result.NoError import AppKit +extension NSControl: ActionMessageSending {} + extension Reactive where Base: NSControl { /// Sets whether the control is enabled. public var isEnabled: BindingTarget { @@ -15,7 +17,7 @@ extension Reactive where Base: NSControl { /// A signal of values in `NSAttributedString`, emitted by the control. public var attributedStringValues: Signal { - return trigger.map { [unowned base = self.base] in base.attributedStringValue } + return proxy.signal.map { $0.attributedStringValue } } /// Sets the value of the control with a `Bool`. @@ -25,9 +27,7 @@ extension Reactive where Base: NSControl { /// A signal of values in `Bool`, emitted by the control. public var boolValues: Signal { - return trigger.map { [unowned base = self.base] in - return base.integerValue == NSOffState ? false : true - } + return proxy.signal.map { $0.integerValue != NSOffState } } /// Sets the value of the control with a `Double`. @@ -37,7 +37,7 @@ extension Reactive where Base: NSControl { /// A signal of values in `Double`, emitted by the control. public var doubleValues: Signal { - return trigger.map { [unowned base = self.base] in base.doubleValue } + return proxy.signal.map { $0.doubleValue } } /// Sets the value of the control with a `Float`. @@ -47,7 +47,7 @@ extension Reactive where Base: NSControl { /// A signal of values in `Float`, emitted by the control. public var floatValues: Signal { - return trigger.map { [unowned base = self.base] in base.floatValue } + return proxy.signal.map { $0.floatValue } } /// Sets the value of the control with an `Int32`. @@ -57,7 +57,7 @@ extension Reactive where Base: NSControl { /// A signal of values in `Int32`, emitted by the control. public var intValues: Signal { - return trigger.map { [unowned base = self.base] in base.intValue } + return proxy.signal.map { $0.intValue } } /// Sets the value of the control with an `Int`. @@ -67,7 +67,7 @@ extension Reactive where Base: NSControl { /// A signal of values in `Int`, emitted by the control. public var integerValues: Signal { - return trigger.map { [unowned base = self.base] in base.integerValue } + return proxy.signal.map { $0.integerValue } } /// Sets the value of the control. @@ -77,7 +77,7 @@ extension Reactive where Base: NSControl { /// A signal of values in `Any?`, emitted by the control. public var objectValues: Signal { - return trigger.map { [unowned base = self.base] in base.objectValue } + return proxy.signal.map { $0.objectValue } } /// Sets the value of the control with a `String`. @@ -87,26 +87,7 @@ extension Reactive where Base: NSControl { /// A signal of values in `String`, emitted by the control. public var stringValues: Signal { - return trigger.map { [unowned base = self.base] in base.stringValue } - } - - /// A trigger signal that sends a `next` event for every action messages - /// received from the control, and completes when the control deinitializes. - internal var trigger: Signal<(), NoError> { - return associatedValue { base in - let (signal, observer) = Signal<(), NoError>.pipe() - - let receiver = CocoaTarget(observer) - base.target = receiver - base.action = #selector(receiver.sendNext) - - lifetime.ended.observeCompleted { - _ = receiver - observer.sendCompleted() - } - - return signal - } + return proxy.signal.map { $0.stringValue } } } diff --git a/ReactiveCocoa/AppKit/NSPopUpButton.swift b/ReactiveCocoa/AppKit/NSPopUpButton.swift index 03e0e68de1..bd4282ce8a 100644 --- a/ReactiveCocoa/AppKit/NSPopUpButton.swift +++ b/ReactiveCocoa/AppKit/NSPopUpButton.swift @@ -6,10 +6,7 @@ extension Reactive where Base: NSPopUpButton { /// A signal of selected indexes public var selectedIndexes: Signal { - return self.integerValues - .map { [unowned base = self.base] _ -> Int in - return base.indexOfSelectedItem - } + return proxy.signal.map { $0.indexOfSelectedItem } } /// Sets the button with an index. @@ -21,11 +18,7 @@ extension Reactive where Base: NSPopUpButton { /// A signal of selected title public var selectedTitles: Signal { - return self.objectValues - .map { [unowned base = self.base] _ -> String? in - return base.titleOfSelectedItem - } - .skipNil() + return proxy.signal.map { $0.titleOfSelectedItem }.skipNil() } /// Sets the button with title. @@ -37,20 +30,13 @@ extension Reactive where Base: NSPopUpButton { } public var selectedItems: Signal { - return self.objectValues - .map { [unowned base = self.base] _ -> NSMenuItem? in - return base.selectedItem - } - .skipNil() + return proxy.signal.map { $0.selectedItem }.skipNil() } /// A signal of selected tags public var selectedTags: Signal { - return self.integerValues - .map { [unowned base = self.base] _ -> Int in - return base.selectedTag() - } + return proxy.signal.map { $0.selectedTag() } } /// Sets the selected tag diff --git a/ReactiveCocoa/AppKit/NSSegmentedControl.swift b/ReactiveCocoa/AppKit/NSSegmentedControl.swift index 0c613ecd35..baa435383c 100644 --- a/ReactiveCocoa/AppKit/NSSegmentedControl.swift +++ b/ReactiveCocoa/AppKit/NSSegmentedControl.swift @@ -10,7 +10,7 @@ extension Reactive where Base: NSSegmentedControl { /// A signal of indexes of selections emitted by the segmented control. public var selectedSegments: Signal { - return trigger.map { [unowned base = self.base] in base.selectedSegment } + return proxy.signal.map { $0.selectedSegment } } /// The below are provided for cross-platform compatibility From 5cf239c9973fe928c117650665e034ad73143c19 Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 7 Feb 2017 17:09:40 +0800 Subject: [PATCH 0739/1028] Added two test cases for the `ActionProxy` related swizzling. --- ReactiveCocoaTests/AppKit/NSControlSpec.swift | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/ReactiveCocoaTests/AppKit/NSControlSpec.swift b/ReactiveCocoaTests/AppKit/NSControlSpec.swift index 526bc9a979..d34aaab27f 100644 --- a/ReactiveCocoaTests/AppKit/NSControlSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSControlSpec.swift @@ -130,6 +130,56 @@ class NSControlSpec: QuickSpec { expect(valuesB) == [true, false] expect(valuesC) == [1, 0] } + + it("should not overwrite the existing target") { + let target = TestTarget() + control.target = target + control.action = #selector(target.execute) + + control.performClick(nil) + expect(target.counter) == 1 + + var signalCounter = 0 + control.reactive.integerValues.observeValues { _ in signalCounter += 1 } + expect(control.target).toNot(beIdenticalTo(target)) + + control.performClick(nil) + expect(signalCounter) == 1 + expect(target.counter) == 2 + + control.performClick(nil) + expect(signalCounter) == 2 + expect(target.counter) == 3 + } + + it("should not overwrite the proxy") { + var signalCounter = 0 + control.reactive.integerValues.observeValues { _ in signalCounter += 1 } + + control.performClick(nil) + expect(signalCounter) == 1 + + let target = TestTarget() + control.target = target + control.action = #selector(target.execute) + + control.performClick(nil) + expect(signalCounter) == 2 + expect(target.counter) == 1 + + + control.performClick(nil) + expect(signalCounter) == 3 + expect(target.counter) == 2 + } } } } + +private final class TestTarget { + var counter = 0 + + @objc func execute(_ sender: Any?) { + counter += 1 + } +} From c3a038718750031a0239cc57e931595f8c4640f0 Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 28 Mar 2017 10:50:20 +0800 Subject: [PATCH 0740/1028] Share swizzling logic between ActionProxy and DelegateProxy. --- ReactiveCocoa/AppKit/ActionProxy.swift | 56 ++++++--------------- ReactiveCocoa/DelegateProxy.swift | 30 +++-------- ReactiveCocoa/ObjC+RuntimeSubclassing.swift | 23 +++++++++ 3 files changed, 43 insertions(+), 66 deletions(-) diff --git a/ReactiveCocoa/AppKit/ActionProxy.swift b/ReactiveCocoa/AppKit/ActionProxy.swift index 243fd2cda8..2c43a9ec0b 100644 --- a/ReactiveCocoa/AppKit/ActionProxy.swift +++ b/ReactiveCocoa/AppKit/ActionProxy.swift @@ -55,49 +55,21 @@ extension Reactive where Base: NSObject, Base: ActionMessageSending { base.target = proxy base.action = #selector(proxy.consume(_:)) - // Swizzle the instance only after setting up the proxy. - let subclass: AnyClass = swizzleClass(base) - - let targetSetter = #selector(setter: base.target) - let actionSetter = #selector(setter: base.action) - - // Swizzle the original setters, and redirect subsequent target and action - // assignments to the proxy. - try! ReactiveCocoa.synchronized(subclass) { - print(subclass) - let subclassAssociations = Associations(subclass as AnyObject) - - if !subclassAssociations.value(forKey: hasSwizzledKey) { - subclassAssociations.setValue(true, forKey: hasSwizzledKey) - - let targetSetterMethod = class_getInstanceMethod(subclass, targetSetter) - let targetSetterTypeEncoding = method_getTypeEncoding(targetSetterMethod)! - - let newTargetSetterImpl: @convention(block) (NSObject, AnyObject?) -> Void = { object, target in - let proxy = object.associations.value(forKey: key)! - proxy.target = target - } - - class_replaceMethod(subclass, - targetSetter, - imp_implementationWithBlock(newTargetSetterImpl as Any), - targetSetterTypeEncoding) - - let actionSetterMethod = class_getInstanceMethod(subclass, actionSetter) - let actionSetterTypeEncoding = method_getTypeEncoding(actionSetterMethod)! - - let newActionSetterImpl: @convention(block) (NSObject, Selector?) -> Void = { object, selector in - let proxy = object.associations.value(forKey: key)! - proxy.action = selector - } - - class_replaceMethod(subclass, - actionSetter, - imp_implementationWithBlock(newActionSetterImpl as Any), - actionSetterTypeEncoding) - } + let newTargetSetterImpl: @convention(block) (NSObject, AnyObject?) -> Void = { object, target in + let proxy = object.associations.value(forKey: key)! + proxy.target = target + } + + let newActionSetterImpl: @convention(block) (NSObject, Selector?) -> Void = { object, selector in + let proxy = object.associations.value(forKey: key)! + proxy.action = selector } - + + // Swizzle the instance only after setting up the proxy. + base.swizzle((#selector(setter: base.target), newTargetSetterImpl), + (#selector(setter: base.action), newActionSetterImpl), + key: hasSwizzledKey) + return proxy } } diff --git a/ReactiveCocoa/DelegateProxy.swift b/ReactiveCocoa/DelegateProxy.swift index 21bd8b6f90..c6bbc50437 100644 --- a/ReactiveCocoa/DelegateProxy.swift +++ b/ReactiveCocoa/DelegateProxy.swift @@ -70,33 +70,15 @@ extension DelegateProxy { return proxy } - let subclass: AnyClass = swizzleClass(instance) - - // Hide the original setter, and redirect subsequent delegate assignment - // to the proxy. - try! ReactiveCocoa.synchronized(subclass) { - let subclassAssociations = Associations(subclass as AnyObject) - - if !subclassAssociations.value(forKey: hasSwizzledKey) { - subclassAssociations.setValue(true, forKey: hasSwizzledKey) - - let method = class_getInstanceMethod(subclass, setter) - let typeEncoding = method_getTypeEncoding(method)! - - let newSetterImpl: @convention(block) (NSObject, NSObject) -> Void = { object, delegate in - let proxy = object.associations.value(forKey: key)! - proxy.forwardee = (delegate as! Delegate) - } - - class_replaceMethod(subclass, - setter, - imp_implementationWithBlock(newSetterImpl as Any), - typeEncoding) - } + let newSetterImpl: @convention(block) (NSObject, NSObject) -> Void = { object, delegate in + let proxy = object.associations.value(forKey: key)! + proxy.forwardee = (delegate as! Delegate) } + instance.swizzle((setter, newSetterImpl), key: hasSwizzledKey) + // Set the proxy as the delegate. - let realClass: AnyClass = class_getSuperclass(subclass) + let realClass: AnyClass = class_getSuperclass(object_getClass(instance)) let originalSetterImpl: IMP = class_getMethodImplementation(realClass, setter) let getterImpl: IMP = class_getMethodImplementation(realClass, getter) diff --git a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift index 0b8caa4065..01dc89bb4e 100644 --- a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift +++ b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift @@ -7,6 +7,29 @@ fileprivate let runtimeSubclassedKey = AssociationKey(default: false) /// has not been requested for the instance before. fileprivate let knownRuntimeSubclassKey = AssociationKey(default: nil) +extension NSObject { + internal func swizzle(_ pairs: (Selector, Any)..., key hasSwizzledKey: AssociationKey) { + let subclass: AnyClass = swizzleClass(self) + + // Hide the original setter, and redirect subsequent delegate assignment + // to the proxy. + try! ReactiveCocoa.synchronized(subclass) { + let subclassAssociations = Associations(subclass as AnyObject) + + if !subclassAssociations.value(forKey: hasSwizzledKey) { + subclassAssociations.setValue(true, forKey: hasSwizzledKey) + + for (selector, body) in pairs { + let method = class_getInstanceMethod(subclass, selector) + let typeEncoding = method_getTypeEncoding(method)! + + class_replaceMethod(subclass, selector, imp_implementationWithBlock(body), typeEncoding) + } + } + } + } +} + /// ISA-swizzle the class of the supplied instance. /// /// - note: If the instance has already been isa-swizzled, the swizzling happens From 58768288f9991fd16a8ae5d8c942eea927c7ab06 Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 28 Mar 2017 10:52:23 +0800 Subject: [PATCH 0741/1028] Streamlined `ActionProxy.consume`. --- ReactiveCocoa/AppKit/ActionProxy.swift | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/ReactiveCocoa/AppKit/ActionProxy.swift b/ReactiveCocoa/AppKit/ActionProxy.swift index 2c43a9ec0b..58178a5b62 100644 --- a/ReactiveCocoa/AppKit/ActionProxy.swift +++ b/ReactiveCocoa/AppKit/ActionProxy.swift @@ -19,11 +19,7 @@ internal final class ActionProxy { // In AppKit, action messages always have only one parameter. @objc func consume(_ sender: Any?) { if let action = action { - if let target = target { - _ = target.perform(action, with: sender) - } else { - NSApp.sendAction(action, to: nil, from: sender) - } + NSApp.sendAction(action, to: target, from: sender) } observer.send(value: owner) From 6582b3c0b77ab1804d54fd6e721c51e85c83764c Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 28 Mar 2017 10:54:51 +0800 Subject: [PATCH 0742/1028] ActionProxy: s/signal/invoked/; s/consume/invoke/ --- ReactiveCocoa/AppKit/ActionProxy.swift | 8 ++++---- ReactiveCocoa/AppKit/NSButton.swift | 4 ++-- ReactiveCocoa/AppKit/NSControl.swift | 16 ++++++++-------- ReactiveCocoa/AppKit/NSPopUpButton.swift | 8 ++++---- ReactiveCocoa/AppKit/NSSegmentedControl.swift | 2 +- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/ReactiveCocoa/AppKit/ActionProxy.swift b/ReactiveCocoa/AppKit/ActionProxy.swift index 58178a5b62..ff35ed2c22 100644 --- a/ReactiveCocoa/AppKit/ActionProxy.swift +++ b/ReactiveCocoa/AppKit/ActionProxy.swift @@ -5,19 +5,19 @@ import enum Result.NoError internal final class ActionProxy { internal weak var target: AnyObject? internal var action: Selector? - internal let signal: Signal + internal let invoked: Signal private let observer: Signal.Observer private unowned let owner: Owner internal init(owner: Owner, lifetime: Lifetime) { self.owner = owner - (signal, observer) = Signal.pipe() + (invoked, observer) = Signal.pipe() lifetime.ended.observeCompleted(observer.sendCompleted) } // In AppKit, action messages always have only one parameter. - @objc func consume(_ sender: Any?) { + @objc func invoke(_ sender: Any?) { if let action = action { NSApp.sendAction(action, to: target, from: sender) } @@ -49,7 +49,7 @@ extension Reactive where Base: NSObject, Base: ActionMessageSending { proxy.action = base.action base.target = proxy - base.action = #selector(proxy.consume(_:)) + base.action = #selector(proxy.invoke(_:)) let newTargetSetterImpl: @convention(block) (NSObject, AnyObject?) -> Void = { object, target in let proxy = object.associations.value(forKey: key)! diff --git a/ReactiveCocoa/AppKit/NSButton.swift b/ReactiveCocoa/AppKit/NSButton.swift index f6626f36dc..677efbf2d8 100644 --- a/ReactiveCocoa/AppKit/NSButton.swift +++ b/ReactiveCocoa/AppKit/NSButton.swift @@ -20,7 +20,7 @@ extension Reactive where Base: NSButton { action = newValue.map { action in let disposable = CompositeDisposable() disposable += isEnabled <~ action.isEnabled - disposable += proxy.signal.observeValues(action.execute) + disposable += proxy.invoked.observeValues(action.execute) return (action, disposable) } } @@ -29,7 +29,7 @@ extension Reactive where Base: NSButton { /// A signal of integer states (On, Off, Mixed), emitted by the button. public var states: Signal { - return proxy.signal.map { $0.state } + return proxy.invoked.map { $0.state } } /// Sets the button's state diff --git a/ReactiveCocoa/AppKit/NSControl.swift b/ReactiveCocoa/AppKit/NSControl.swift index 8064ae168c..1e29888a45 100644 --- a/ReactiveCocoa/AppKit/NSControl.swift +++ b/ReactiveCocoa/AppKit/NSControl.swift @@ -17,7 +17,7 @@ extension Reactive where Base: NSControl { /// A signal of values in `NSAttributedString`, emitted by the control. public var attributedStringValues: Signal { - return proxy.signal.map { $0.attributedStringValue } + return proxy.invoked.map { $0.attributedStringValue } } /// Sets the value of the control with a `Bool`. @@ -27,7 +27,7 @@ extension Reactive where Base: NSControl { /// A signal of values in `Bool`, emitted by the control. public var boolValues: Signal { - return proxy.signal.map { $0.integerValue != NSOffState } + return proxy.invoked.map { $0.integerValue != NSOffState } } /// Sets the value of the control with a `Double`. @@ -37,7 +37,7 @@ extension Reactive where Base: NSControl { /// A signal of values in `Double`, emitted by the control. public var doubleValues: Signal { - return proxy.signal.map { $0.doubleValue } + return proxy.invoked.map { $0.doubleValue } } /// Sets the value of the control with a `Float`. @@ -47,7 +47,7 @@ extension Reactive where Base: NSControl { /// A signal of values in `Float`, emitted by the control. public var floatValues: Signal { - return proxy.signal.map { $0.floatValue } + return proxy.invoked.map { $0.floatValue } } /// Sets the value of the control with an `Int32`. @@ -57,7 +57,7 @@ extension Reactive where Base: NSControl { /// A signal of values in `Int32`, emitted by the control. public var intValues: Signal { - return proxy.signal.map { $0.intValue } + return proxy.invoked.map { $0.intValue } } /// Sets the value of the control with an `Int`. @@ -67,7 +67,7 @@ extension Reactive where Base: NSControl { /// A signal of values in `Int`, emitted by the control. public var integerValues: Signal { - return proxy.signal.map { $0.integerValue } + return proxy.invoked.map { $0.integerValue } } /// Sets the value of the control. @@ -77,7 +77,7 @@ extension Reactive where Base: NSControl { /// A signal of values in `Any?`, emitted by the control. public var objectValues: Signal { - return proxy.signal.map { $0.objectValue } + return proxy.invoked.map { $0.objectValue } } /// Sets the value of the control with a `String`. @@ -87,7 +87,7 @@ extension Reactive where Base: NSControl { /// A signal of values in `String`, emitted by the control. public var stringValues: Signal { - return proxy.signal.map { $0.stringValue } + return proxy.invoked.map { $0.stringValue } } } diff --git a/ReactiveCocoa/AppKit/NSPopUpButton.swift b/ReactiveCocoa/AppKit/NSPopUpButton.swift index bd4282ce8a..ef1186f356 100644 --- a/ReactiveCocoa/AppKit/NSPopUpButton.swift +++ b/ReactiveCocoa/AppKit/NSPopUpButton.swift @@ -6,7 +6,7 @@ extension Reactive where Base: NSPopUpButton { /// A signal of selected indexes public var selectedIndexes: Signal { - return proxy.signal.map { $0.indexOfSelectedItem } + return proxy.invoked.map { $0.indexOfSelectedItem } } /// Sets the button with an index. @@ -18,7 +18,7 @@ extension Reactive where Base: NSPopUpButton { /// A signal of selected title public var selectedTitles: Signal { - return proxy.signal.map { $0.titleOfSelectedItem }.skipNil() + return proxy.invoked.map { $0.titleOfSelectedItem }.skipNil() } /// Sets the button with title. @@ -30,13 +30,13 @@ extension Reactive where Base: NSPopUpButton { } public var selectedItems: Signal { - return proxy.signal.map { $0.selectedItem }.skipNil() + return proxy.invoked.map { $0.selectedItem }.skipNil() } /// A signal of selected tags public var selectedTags: Signal { - return proxy.signal.map { $0.selectedTag() } + return proxy.invoked.map { $0.selectedTag() } } /// Sets the selected tag diff --git a/ReactiveCocoa/AppKit/NSSegmentedControl.swift b/ReactiveCocoa/AppKit/NSSegmentedControl.swift index baa435383c..2957e34196 100644 --- a/ReactiveCocoa/AppKit/NSSegmentedControl.swift +++ b/ReactiveCocoa/AppKit/NSSegmentedControl.swift @@ -10,7 +10,7 @@ extension Reactive where Base: NSSegmentedControl { /// A signal of indexes of selections emitted by the segmented control. public var selectedSegments: Signal { - return proxy.signal.map { $0.selectedSegment } + return proxy.invoked.map { $0.selectedSegment } } /// The below are provided for cross-platform compatibility From 5fed5a1e2a1a38c5e62b676a3a06fa4d066a7723 Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 28 Mar 2017 11:13:13 +0800 Subject: [PATCH 0743/1028] Improve the documentation of `NSObject.swizzle`. --- ReactiveCocoa/DelegateProxy.swift | 2 ++ ReactiveCocoa/ObjC+RuntimeSubclassing.swift | 19 +++++++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoa/DelegateProxy.swift b/ReactiveCocoa/DelegateProxy.swift index c6bbc50437..a11bccef21 100644 --- a/ReactiveCocoa/DelegateProxy.swift +++ b/ReactiveCocoa/DelegateProxy.swift @@ -75,6 +75,8 @@ extension DelegateProxy { proxy.forwardee = (delegate as! Delegate) } + // Hide the original setter, and redirect subsequent delegate assignment + // to the proxy. instance.swizzle((setter, newSetterImpl), key: hasSwizzledKey) // Set the proxy as the delegate. diff --git a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift index 01dc89bb4e..ae28582003 100644 --- a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift +++ b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift @@ -8,11 +8,21 @@ fileprivate let runtimeSubclassedKey = AssociationKey(default: false) fileprivate let knownRuntimeSubclassKey = AssociationKey(default: nil) extension NSObject { + /// Swizzle the given selectors. + /// + /// - warning: The swizzling **does not** apply on a per-instance basis. In + /// other words, repetitive swizzling of the same selector would + /// overwrite previous swizzling attempts, despite a different + /// instance being supplied. + /// + /// - parameters: + /// - pairs: Tuples of selectors and the respective implementions to be + /// swapped in. + /// - key: An association key which determines if the swizzling has already + /// been performed. internal func swizzle(_ pairs: (Selector, Any)..., key hasSwizzledKey: AssociationKey) { let subclass: AnyClass = swizzleClass(self) - // Hide the original setter, and redirect subsequent delegate assignment - // to the proxy. try! ReactiveCocoa.synchronized(subclass) { let subclassAssociations = Associations(subclass as AnyObject) @@ -35,6 +45,11 @@ extension NSObject { /// - note: If the instance has already been isa-swizzled, the swizzling happens /// in place in the runtime subclass created by external parties. /// +/// - warning: The swizzling **does not** apply on a per-instance basis. In +/// other words, repetitive swizzling of the same selector would +/// overwrite previous swizzling attempts, despite a different +/// instance being supplied. +/// /// - parameters: /// - instance: The instance to be swizzled. /// From 6b38abc1efc8863e71877de4d38a805e3b359cdd Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 30 Mar 2017 18:33:59 +0800 Subject: [PATCH 0744/1028] Fixed an bug preventing the forwardee from being cleared in `DelegateProxy`. --- ReactiveCocoa/DelegateProxy.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ReactiveCocoa/DelegateProxy.swift b/ReactiveCocoa/DelegateProxy.swift index a11bccef21..192b08ba88 100644 --- a/ReactiveCocoa/DelegateProxy.swift +++ b/ReactiveCocoa/DelegateProxy.swift @@ -70,9 +70,9 @@ extension DelegateProxy { return proxy } - let newSetterImpl: @convention(block) (NSObject, NSObject) -> Void = { object, delegate in + let newSetterImpl: @convention(block) (NSObject, AnyObject?) -> Void = { object, delegate in let proxy = object.associations.value(forKey: key)! - proxy.forwardee = (delegate as! Delegate) + proxy.forwardee = (delegate as! Delegate?) } // Hide the original setter, and redirect subsequent delegate assignment @@ -84,8 +84,8 @@ extension DelegateProxy { let originalSetterImpl: IMP = class_getMethodImplementation(realClass, setter) let getterImpl: IMP = class_getMethodImplementation(realClass, getter) - typealias Setter = @convention(c) (AnyObject, Selector, AnyObject) -> Void - typealias Getter = @convention(c) (AnyObject, Selector) -> NSObject? + typealias Setter = @convention(c) (NSObject, Selector, AnyObject?) -> Void + typealias Getter = @convention(c) (NSObject, Selector) -> AnyObject? // As Objective-C classes may cache the information of their delegate at // the time the delegates are set, the information has to be "flushed" From d0b874fccd096718eac8a06d1f0eeed7ed847ebe Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 30 Mar 2017 19:28:53 +0800 Subject: [PATCH 0745/1028] The proxy signals should respect the lifetime of the delegator. --- ReactiveCocoa/DelegateProxy.swift | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ReactiveCocoa/DelegateProxy.swift b/ReactiveCocoa/DelegateProxy.swift index 192b08ba88..880fcb11eb 100644 --- a/ReactiveCocoa/DelegateProxy.swift +++ b/ReactiveCocoa/DelegateProxy.swift @@ -10,9 +10,11 @@ internal class DelegateProxy: NSObject { internal var interceptedSelectors: Set = [] + private let lifetime: Lifetime private let originalSetter: (AnyObject) -> Void - required init(_ originalSetter: @escaping (AnyObject) -> Void) { + required init(lifetime: Lifetime, _ originalSetter: @escaping (AnyObject) -> Void) { + self.lifetime = lifetime self.originalSetter = originalSetter } @@ -23,13 +25,13 @@ internal class DelegateProxy: NSObject { func intercept(_ selector: Selector) -> Signal<(), NoError> { interceptedSelectors.insert(selector) originalSetter(self) - return self.reactive.trigger(for: selector) + return self.reactive.trigger(for: selector).take(during: lifetime) } func intercept(_ selector: Selector) -> Signal<[Any?], NoError> { interceptedSelectors.insert(selector) originalSetter(self) - return self.reactive.signal(for: selector) + return self.reactive.signal(for: selector).take(during: lifetime) } override func responds(to selector: Selector!) -> Bool { @@ -90,7 +92,7 @@ extension DelegateProxy { // As Objective-C classes may cache the information of their delegate at // the time the delegates are set, the information has to be "flushed" // whenever the proxy forwardee is replaced or a selector is intercepted. - let proxy = self.init { [weak instance] proxy in + let proxy = self.init(lifetime: instance.reactive.lifetime) { [weak instance] proxy in guard let instance = instance else { return } unsafeBitCast(originalSetterImpl, to: Setter.self)(instance, setter, proxy) } From f21fb2bb8eb4df674a045c0f5bc51dd49f471642 Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 30 Mar 2017 19:40:10 +0800 Subject: [PATCH 0746/1028] Saved an unnecessary call to the original setter in `DelegateProxy`. --- ReactiveCocoa/DelegateProxy.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoa/DelegateProxy.swift b/ReactiveCocoa/DelegateProxy.swift index 880fcb11eb..2420366a4a 100644 --- a/ReactiveCocoa/DelegateProxy.swift +++ b/ReactiveCocoa/DelegateProxy.swift @@ -99,11 +99,11 @@ extension DelegateProxy { instance.associations.setValue(proxy, forKey: key) + // `proxy.forwardee` would invoke the original setter regardless of + // `original` being `nil` or not. let original = unsafeBitCast(getterImpl, to: Getter.self)(instance, getter) as! Delegate? proxy.forwardee = original - unsafeBitCast(originalSetterImpl, to: Setter.self)(instance, setter, proxy) - return proxy } } From 79add39d676e62e9870d667de3d882bfc72e5b9a Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 30 Mar 2017 19:41:47 +0800 Subject: [PATCH 0747/1028] Added `DelegateProxySpec`. --- ReactiveCocoa.xcodeproj/project.pbxproj | 8 + ReactiveCocoaTests/DelegateProxySpec.swift | 216 +++++++++++++++++++++ 2 files changed, 224 insertions(+) create mode 100644 ReactiveCocoaTests/DelegateProxySpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index ca69676f00..18eceb3e23 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -153,6 +153,9 @@ 9A7488491E3B8ACE00CD0317 /* DelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7488471E3B8ACE00CD0317 /* DelegateProxy.swift */; }; 9A74884A1E3B8ACE00CD0317 /* DelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7488471E3B8ACE00CD0317 /* DelegateProxy.swift */; }; 9A74884B1E3B8ACE00CD0317 /* DelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7488471E3B8ACE00CD0317 /* DelegateProxy.swift */; }; + 9A892D8F1E8D19BE00EA35F3 /* DelegateProxySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A892D8E1E8D19BE00EA35F3 /* DelegateProxySpec.swift */; }; + 9A892D901E8D19BE00EA35F3 /* DelegateProxySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A892D8E1E8D19BE00EA35F3 /* DelegateProxySpec.swift */; }; + 9A892D911E8D19BE00EA35F3 /* DelegateProxySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A892D8E1E8D19BE00EA35F3 /* DelegateProxySpec.swift */; }; 9A9A129A1DC7A97100D10223 /* UIGestureRecognizerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4191394B1DBA002C0043C9D1 /* UIGestureRecognizerSpec.swift */; }; 9A9DFEE51DA7B5500039EE1B /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; 9A9DFEE91DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; @@ -398,6 +401,7 @@ 9A6AAA271DB8F7EB0013AAEA /* ReusableComponentsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponentsSpec.swift; sourceTree = ""; }; 9A6AAA291DB8F7F10013AAEA /* ReusableComponentsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponentsSpec.swift; sourceTree = ""; }; 9A7488471E3B8ACE00CD0317 /* DelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DelegateProxy.swift; sourceTree = ""; }; + 9A892D8E1E8D19BE00EA35F3 /* DelegateProxySpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DelegateProxySpec.swift; sourceTree = ""; }; 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssociationSpec.swift; sourceTree = ""; }; 9AA0BD771DDE03DE00531FCF /* ObjC+Runtime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObjC+Runtime.swift"; sourceTree = ""; }; 9AA0BD801DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObjC+RuntimeSubclassing.swift"; sourceTree = ""; }; @@ -783,6 +787,7 @@ 538DCB771DCA5E3200332880 /* Shared */, 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */, CD8401821CEE8ED7009F0ABF /* CocoaActionSpec.swift */, + 9A892D8E1E8D19BE00EA35F3 /* DelegateProxySpec.swift */, D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */, B696FB801A7640C00075236D /* TestError.swift */, 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */, @@ -1227,6 +1232,7 @@ 9A1D06371D93EA7E00ACF44C /* UIActivityIndicatorViewSpec.swift in Sources */, 9A6AAA2F1DB903A40013AAEA /* ReusableComponentsSpec.swift in Sources */, 538DCB7F1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift in Sources */, + 9A892D911E8D19BE00EA35F3 /* DelegateProxySpec.swift in Sources */, 9A1D06471D93EA7E00ACF44C /* UILabelSpec.swift in Sources */, 9A1D06391D93EA7E00ACF44C /* UIBarButtonItemSpec.swift in Sources */, 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */, @@ -1321,6 +1327,7 @@ 9ADFE5A11DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */, 4ABEFE331DCFD0630066A8C2 /* NSCollectionViewSpec.swift in Sources */, B696FB811A7640C00075236D /* TestError.swift in Sources */, + 9A892D8F1E8D19BE00EA35F3 /* DelegateProxySpec.swift in Sources */, 004FD0071E26CDB300A03A82 /* NSButtonSpec.swift in Sources */, D9558AB91DFF86C0003254E1 /* NSPopUpButtonSpec.swift in Sources */, BFA6B94D1A7604D400C846D1 /* SignalProducerNimbleMatchers.swift in Sources */, @@ -1406,6 +1413,7 @@ 9A24A8461DE142A500987AF9 /* SwizzlingSpec.swift in Sources */, 9A1D063E1D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift in Sources */, 4191394E1DBA01A00043C9D1 /* UIGestureRecognizerSpec.swift in Sources */, + 9A892D901E8D19BE00EA35F3 /* DelegateProxySpec.swift in Sources */, 531866FA1DD7925600D1285F /* UIStepperSpec.swift in Sources */, 9A1D06361D93EA7E00ACF44C /* UIActivityIndicatorViewSpec.swift in Sources */, 9A1D06461D93EA7E00ACF44C /* UILabelSpec.swift in Sources */, diff --git a/ReactiveCocoaTests/DelegateProxySpec.swift b/ReactiveCocoaTests/DelegateProxySpec.swift new file mode 100644 index 0000000000..6754f8d8c3 --- /dev/null +++ b/ReactiveCocoaTests/DelegateProxySpec.swift @@ -0,0 +1,216 @@ +import Quick +import Nimble +import enum Result.NoError +import ReactiveSwift +@testable import ReactiveCocoa + +@objc protocol ObjectDelegate: NSObjectProtocol { + func foo() + @objc optional func bar() + @objc optional func nop() +} + +class Object: NSObject { + var delegateSetCount = 0 + var delegateSelectors: [Selector] = [] + + weak var delegate: ObjectDelegate? { + didSet { + delegateSetCount += 1 + delegateSelectors = Array() + + if delegate?.responds(to: #selector(ObjectDelegate.foo)) ?? false { + delegateSelectors.append(#selector(ObjectDelegate.foo)) + } + + if delegate?.responds(to: #selector(ObjectDelegate.bar)) ?? false { + delegateSelectors.append(#selector(ObjectDelegate.bar)) + } + + if delegate?.responds(to: #selector(ObjectDelegate.nop)) ?? false { + delegateSelectors.append(#selector(ObjectDelegate.nop)) + } + } + } + + deinit { + // Mimic the behavior of clearing delegates of some Cocoa classes. + delegate = nil + } +} + +class ObjectDelegateCounter: NSObject, ObjectDelegate { + var fooCounter = 0 + var nopCounter = 0 + + func foo() { + fooCounter += 1 + } + + func nop() { + nopCounter += 1 + } +} + +class ObjectDelegateProxy: DelegateProxy, ObjectDelegate { + func foo() { + forwardee?.foo() + } + + func bar() { + forwardee?.bar?() + } +} + +class DelegateProxySpec: QuickSpec { + override func spec() { + describe("DelegateProxy") { + var object: Object! + var proxy: DelegateProxy! + + beforeEach { + object = Object() + proxy = ObjectDelegateProxy.proxy(for: object, + setter: #selector(setter: object.delegate), + getter: #selector(getter: object.delegate)) + } + + afterEach { + weak var weakObject = object + + object = nil + expect(weakObject).to(beNil()) + } + + it("should be automatically set as the object's delegate.") { + expect(object.delegate).to(beIdenticalTo(proxy)) + } + + it("should respond to the protocol requirement checks.") { + expect(proxy.responds(to: #selector(ObjectDelegate.foo))) == true + expect(proxy.responds(to: #selector(ObjectDelegate.bar))) == true + expect(proxy.responds(to: #selector(ObjectDelegate.nop))) == false + } + + it("should complete its signals when the object deinitializes") { + var isCompleted = false + + let foo: Signal<(), NoError> = proxy.intercept(#selector(ObjectDelegate.foo)) + foo.observeCompleted { isCompleted = true } + + expect(isCompleted) == false + + object = nil + expect(isCompleted) == true + } + + it("should interrupt the observers if the object has already deinitialized") { + var isInterrupted = false + + object = nil + + let foo: Signal<(), NoError> = proxy.intercept(#selector(ObjectDelegate.foo)) + foo.observeInterrupted { isInterrupted = true } + + expect(isInterrupted) == true + } + + it("should emit a `value` event whenever any delegate method is invoked.") { + var fooCount = 0 + var barCount = 0 + + let foo: Signal<(), NoError> = proxy.intercept(#selector(ObjectDelegate.foo)) + foo.observeValues { fooCount += 1 } + + let bar: Signal<(), NoError> = proxy.intercept(#selector(ObjectDelegate.bar)) + bar.observeValues { barCount += 1 } + + expect(fooCount) == 0 + expect(barCount) == 0 + + object.delegate?.foo() + object.delegate?.bar?() + expect(fooCount) == 1 + expect(barCount) == 1 + + object.delegate?.foo() + expect(fooCount) == 2 + + object.delegate?.bar?() + expect(barCount) == 2 + } + + it("should accomodate forwardee changes when responding to the protocol requirement checks.") { + expect(proxy.responds(to: #selector(ObjectDelegate.nop))) == false + + let forwardee = ObjectDelegateCounter() + proxy.forwardee = forwardee + expect(proxy.responds(to: #selector(ObjectDelegate.nop))) == true + + proxy.forwardee = nil + expect(proxy.responds(to: #selector(ObjectDelegate.nop))) == false + } + + it("should invoke the method on the forwardee.") { + let forwardee = ObjectDelegateCounter() + proxy.forwardee = forwardee + + var fooCount = 0 + + let foo: Signal<(), NoError> = proxy.intercept(#selector(ObjectDelegate.foo)) + foo.observeValues { fooCount += 1 } + + expect(fooCount) == 0 + expect(forwardee.fooCounter) == 0 + + object.delegate?.foo() + expect(fooCount) == 1 + expect(forwardee.fooCounter) == 1 + + object.delegate?.foo() + expect(fooCount) == 2 + expect(forwardee.fooCounter) == 2 + } + + it("should emit a `value` event for an optional requirement even if the forwardee does not implement it.") { + let forwardee = ObjectDelegateCounter() + proxy.forwardee = forwardee + + var barCount = 0 + + let bar: Signal<(), NoError> = proxy.intercept(#selector(ObjectDelegate.bar)) + bar.observeValues { barCount += 1 } + + object.delegate?.bar?() + expect(barCount) == 1 + } + + it("should invoke an optional requirement on the forwardee even if it does not implement it.") { + let forwardee = ObjectDelegateCounter() + proxy.forwardee = forwardee + + expect(forwardee.nopCounter) == 0 + + object.delegate?.nop?() + expect(forwardee.nopCounter) == 1 + } + + it("should invoke the original delegate setter whenever the forwardee is updated.") { + // The expected initial count is accounted for the proxy setup. + expect(object.delegateSetCount) == 1 + expect(object.delegateSelectors) == [#selector(ObjectDelegate.foo), #selector(ObjectDelegate.bar)] + + let forwardee = ObjectDelegateCounter() + proxy.forwardee = forwardee + expect(object.delegateSetCount) == 2 + expect(object.delegateSelectors) == [#selector(ObjectDelegate.foo), #selector(ObjectDelegate.bar), #selector(ObjectDelegate.nop)] + expect(object.delegate).to(beIdenticalTo(proxy)) + + proxy.forwardee = nil + expect(object.delegateSetCount) == 3 + expect(object.delegateSelectors) == [#selector(ObjectDelegate.foo), #selector(ObjectDelegate.bar)] + expect(object.delegate).to(beIdenticalTo(proxy)) + } + } + } +} From a9631a05742247edd7a50e6603e8fa029fe62320 Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 30 Mar 2017 19:52:50 +0800 Subject: [PATCH 0748/1028] Fixed a false negative in `DelegateProxy.responds(to:)`. It happened on a selector when the proxy has a forwardee, is implemented by the forwardee and is not implemented by the proxy. --- ReactiveCocoa/DelegateProxy.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa/DelegateProxy.swift b/ReactiveCocoa/DelegateProxy.swift index 2420366a4a..796ac288c5 100644 --- a/ReactiveCocoa/DelegateProxy.swift +++ b/ReactiveCocoa/DelegateProxy.swift @@ -39,7 +39,7 @@ internal class DelegateProxy: NSObject { return true } - return forwardee?.responds(to: selector) ?? super.responds(to: selector) + return (forwardee?.responds(to: selector) ?? false) || super.responds(to: selector) } } From 2be913dd5300fdeff575a5a854f1dbac3569617a Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 30 Mar 2017 22:35:34 +0800 Subject: [PATCH 0749/1028] Update dependencies (Nimble, RAS & Result). --- Cartfile.private | 2 +- Cartfile.resolved | 6 +++--- Carthage/Checkouts/Nimble | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- Carthage/Checkouts/Result | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cartfile.private b/Cartfile.private index 04fba310d3..43b7f07f26 100644 --- a/Cartfile.private +++ b/Cartfile.private @@ -1,3 +1,3 @@ github "jspahrsummers/xcconfigs" "3d9d996" github "Quick/Quick" ~> 1.1 -github "Quick/Nimble" "8116a83864ee78339798c3ef425c42f6ca6bf034" +github "Quick/Nimble" ~> 6.1 diff --git a/Cartfile.resolved b/Cartfile.resolved index 83e47da93d..d1ccb5c42b 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,5 +1,5 @@ -github "Quick/Nimble" "8116a83864ee78339798c3ef425c42f6ca6bf034" +github "Quick/Nimble" "v6.1.0" github "Quick/Quick" "v1.1.0" -github "ReactiveCocoa/ReactiveSwift" "1.1.0" -github "antitypical/Result" "3.1.0" +github "ReactiveCocoa/ReactiveSwift" "1.1.1" +github "antitypical/Result" "3.2.1" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" diff --git a/Carthage/Checkouts/Nimble b/Carthage/Checkouts/Nimble index 8116a83864..e3c1a78af3 160000 --- a/Carthage/Checkouts/Nimble +++ b/Carthage/Checkouts/Nimble @@ -1 +1 @@ -Subproject commit 8116a83864ee78339798c3ef425c42f6ca6bf034 +Subproject commit e3c1a78af33b3fa778207fcc6ba33221838c0788 diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index c83de32f1a..1556b846de 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit c83de32f1adbcef89618cde4222f69a8bcecd7c2 +Subproject commit 1556b846de627cac0b96291ec2835fbc349c5e21 diff --git a/Carthage/Checkouts/Result b/Carthage/Checkouts/Result index 2dd1a0c7bc..2af7c14607 160000 --- a/Carthage/Checkouts/Result +++ b/Carthage/Checkouts/Result @@ -1 +1 @@ -Subproject commit 2dd1a0c7bca4cc44082f72020fe8b14491056f88 +Subproject commit 2af7c146071c8d8fb3953f19924ecebf15c88ea7 From c8a184d4fb9614bf2d21302e971e77126785b827 Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 30 Mar 2017 22:36:21 +0800 Subject: [PATCH 0750/1028] Use Xcode 8.3 (Swift 3.1) image for CI. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e1e2a7a505..7a4e765bbb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: objective-c -osx_image: xcode8.2 +osx_image: xcode8.3 before_install: true install: true git: From 85b93818ff3047a2b45a664173db067a8872103a Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 30 Mar 2017 22:38:12 +0800 Subject: [PATCH 0751/1028] Update `.swift-version`. --- .swift-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.swift-version b/.swift-version index 9f55b2ccb5..8c50098d8a 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -3.0 +3.1 From 362d64994b0b9135eadc6743561b32b44bcdf66d Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 31 Mar 2017 03:20:39 +0800 Subject: [PATCH 0752/1028] 5.0.2 --- ReactiveCocoa.podspec | 2 +- ReactiveCocoa/Info.plist | 2 +- ReactiveCocoaTests/Info.plist | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 1726e1b3bd..f4bd55121d 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "5.0.1" + s.version = "5.0.2" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveCocoa/Info.plist b/ReactiveCocoa/Info.plist index 7138859eb1..ddc3040872 100644 --- a/ReactiveCocoa/Info.plist +++ b/ReactiveCocoa/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 5.0.0 + 5.0.2 CFBundleSignature ???? CFBundleVersion diff --git a/ReactiveCocoaTests/Info.plist b/ReactiveCocoaTests/Info.plist index 7559011202..b5296d04bb 100644 --- a/ReactiveCocoaTests/Info.plist +++ b/ReactiveCocoaTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 5.0.0 + 5.0.2 CFBundleSignature ???? CFBundleVersion From 5f6dbbaa33f42d60d493bb753a35f42464841dd8 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Fri, 31 Mar 2017 03:22:10 +0800 Subject: [PATCH 0753/1028] Fixed badges in the README. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 761106db92..6a21aa30c0 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ Join the ReactiveSwift Slack community.


    + [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](#carthage) [![CocoaPods compatible](https://img.shields.io/cocoapods/v/ReactiveCocoa.svg)](#cocoapods) [![GitHub release](https://img.shields.io/github/release/ReactiveCocoa/ReactiveCocoa.svg)](https://github.com/ReactiveCocoa/ReactiveCocoa/releases) ![Swift 3.0.x](https://img.shields.io/badge/Swift-3.0.x-orange.svg) ![platforms](https://img.shields.io/badge/platforms-iOS%20%7C%20OS%20X%20%7C%20watchOS%20%7C%20tvOS%20-lightgrey.svg) ⚠️ [Looking for the Objective-C API?][] ⚠️ [Still using Swift 2.x?][] From 9ab7d8f08eba990446e6df22f3ec59842693ff06 Mon Sep 17 00:00:00 2001 From: Seil Oh Date: Sun, 2 Apr 2017 12:40:18 +0900 Subject: [PATCH 0754/1028] Put button inside NSWindow for responder chain to take effect. --- ReactiveCocoaTests/AppKit/NSButtonSpec.swift | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ReactiveCocoaTests/AppKit/NSButtonSpec.swift b/ReactiveCocoaTests/AppKit/NSButtonSpec.swift index e2a87d5242..c5bdc93096 100644 --- a/ReactiveCocoaTests/AppKit/NSButtonSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSButtonSpec.swift @@ -9,14 +9,21 @@ class NSButtonSpec: QuickSpec { override func spec() { var button: NSButton! weak var _button: NSButton? + + var window: NSWindow! beforeEach { button = NSButton(frame: .zero) _button = button + window = NSWindow() + window.contentView?.addSubview(button) } afterEach { - button = nil + autoreleasepool { + button.removeFromSuperview() + button = nil + } expect(_button).to(beNil()) } From 2d17addc7230fbc669212df6e2d466f13e4bf1a0 Mon Sep 17 00:00:00 2001 From: Seil Oh Date: Sun, 2 Apr 2017 13:01:16 +0900 Subject: [PATCH 0755/1028] add test case to demonstrate nsbutton inside nsttackview issue. --- ReactiveCocoaTests/AppKit/NSButtonSpec.swift | 38 ++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/ReactiveCocoaTests/AppKit/NSButtonSpec.swift b/ReactiveCocoaTests/AppKit/NSButtonSpec.swift index e2a87d5242..a248c758aa 100644 --- a/ReactiveCocoaTests/AppKit/NSButtonSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSButtonSpec.swift @@ -76,6 +76,44 @@ class NSButtonSpec: QuickSpec { expect(state.value) == NSOffState } + + if #available(OSX 10.11, *) { + it("should send along state changes embedded within NSStackView") { + + let button1 = NSButton() + let button2 = NSButton() + + button1.setButtonType(.pushOnPushOff) + button1.allowsMixedState = false + button1.state = NSOffState + + button2.setButtonType(.pushOnPushOff) + button2.allowsMixedState = false + button2.state = NSOnState + + let stackView = NSStackView() + stackView.addArrangedSubview(button1) + stackView.addArrangedSubview(button2) + + window.contentView?.addSubview(stackView) + + let state = MutableProperty(NSOffState) + state <~ button1.reactive.states + state <~ button2.reactive.states + + button1.performClick(nil) + expect(state.value) == NSOnState + + button2.performClick(nil) + expect(state.value) == NSOffState + + autoreleasepool { + button1.removeFromSuperview() + button2.removeFromSuperview() + stackView.removeFromSuperview() + } + } + } it("should execute the `pressed` action upon receiving a click") { button.isEnabled = true From aef73316518c43063a091b625d263ebabd177fb0 Mon Sep 17 00:00:00 2001 From: Seil Oh Date: Sun, 2 Apr 2017 14:40:10 +0900 Subject: [PATCH 0756/1028] add window --- ReactiveCocoaTests/AppKit/NSButtonSpec.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ReactiveCocoaTests/AppKit/NSButtonSpec.swift b/ReactiveCocoaTests/AppKit/NSButtonSpec.swift index a248c758aa..6c8b1df8ec 100644 --- a/ReactiveCocoaTests/AppKit/NSButtonSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSButtonSpec.swift @@ -80,6 +80,8 @@ class NSButtonSpec: QuickSpec { if #available(OSX 10.11, *) { it("should send along state changes embedded within NSStackView") { + let window = NSWindow() + let button1 = NSButton() let button2 = NSButton() From ba80357c77264008bcfad0004ee60da9cff978a4 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Mon, 3 Apr 2017 00:37:45 +0800 Subject: [PATCH 0757/1028] Disable the MapView deallocation check for macOS. --- ReactiveCocoaTests/Shared/MKMapViewSpec.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ReactiveCocoaTests/Shared/MKMapViewSpec.swift b/ReactiveCocoaTests/Shared/MKMapViewSpec.swift index 73117a8d07..506cdec6a3 100644 --- a/ReactiveCocoaTests/Shared/MKMapViewSpec.swift +++ b/ReactiveCocoaTests/Shared/MKMapViewSpec.swift @@ -18,9 +18,15 @@ class MKMapViewSpec: QuickSpec { afterEach { mapView = nil + // FIXME: SDK_ISSUE + // + // Temporarily disabled since the expectation keeps failing with + // Xcode 8.3 and macOS Sierra 10.12.4. + #if !os(macOS) // using toEventually(beNil()) here // since it takes time to release MKMapView expect(_mapView).toEventually(beNil()) + #endif } it("should accept changes from bindings to its map type") { From 9c40526d1ab434ad849d140bcc65467f74cdd625 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Mon, 3 Apr 2017 00:41:13 +0800 Subject: [PATCH 0758/1028] Tune down the interception perf tests. --- ReactiveCocoaTests/InterceptingPerformanceTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoaTests/InterceptingPerformanceTests.swift b/ReactiveCocoaTests/InterceptingPerformanceTests.swift index a4ebb43a38..17ff223c77 100644 --- a/ReactiveCocoaTests/InterceptingPerformanceTests.swift +++ b/ReactiveCocoaTests/InterceptingPerformanceTests.swift @@ -2,7 +2,7 @@ import XCTest @testable import ReactiveCocoa import ReactiveSwift -private let iterationCount = 5000 +private let iterationCount = 500 private final class Receiver1: NSObject { dynamic func message() {} From b5a9e313feb9477f8ba2e4b346cc2cb1702ea78a Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Mon, 3 Apr 2017 17:55:32 +0800 Subject: [PATCH 0759/1028] Native Swift Object support in RAC KVO API. --- .../NSObject+KeyValueObserving.swift | 15 +-- ReactiveCocoa/NSObject+Lifetime.swift | 21 +++++ .../KeyValueObservingSpec.swift | 94 ++++++++++++++++++- 3 files changed, 123 insertions(+), 7 deletions(-) diff --git a/ReactiveCocoa/NSObject+KeyValueObserving.swift b/ReactiveCocoa/NSObject+KeyValueObserving.swift index 57f3f292c3..2c361ef885 100644 --- a/ReactiveCocoa/NSObject+KeyValueObserving.swift +++ b/ReactiveCocoa/NSObject+KeyValueObserving.swift @@ -21,7 +21,7 @@ extension Reactive where Base: NSObject { options: [.initial, .new], action: observer.send ) - disposable += self.lifetime.ended.observeCompleted(observer.sendCompleted) + disposable += self.lifetime.observeEnded(observer.sendCompleted) } } @@ -46,7 +46,7 @@ extension Reactive where Base: NSObject { options: [.new], action: observer.send ) - disposable += self.lifetime.ended.observeCompleted(observer.sendCompleted) + disposable += self.lifetime.observeEnded(observer.sendCompleted) return disposable } } @@ -162,7 +162,7 @@ extension KeyValueObserver { headSerialDisposable.inner = headDisposable if shouldObserveDeinit { - let disposable = value.reactive.lifetime.ended.observeCompleted { + let disposable = value.reactive.lifetime.observeEnded { if isWeak { action(nil) } @@ -187,15 +187,18 @@ extension KeyValueObserver { } } else { observer = KeyValueObserver(observing: object, key: keyPathHead, options: options) { object in - guard let value = object?.value(forKey: keyPathHead) as! NSObject? else { + guard let value = object?.value(forKey: keyPathHead) as AnyObject? else { action(nil) return } - if shouldObserveDeinit { - let disposable = value.reactive.lifetime.ended.observeCompleted { + // For a direct key path, the deinitialization needs to be + // observed only if the key path is a weak property. + if shouldObserveDeinit && isWeak { + let disposable = lifetime(of: value).observeEnded { action(nil) } + headSerialDisposable.inner = disposable } diff --git a/ReactiveCocoa/NSObject+Lifetime.swift b/ReactiveCocoa/NSObject+Lifetime.swift index ed90fb60ae..92cae58338 100644 --- a/ReactiveCocoa/NSObject+Lifetime.swift +++ b/ReactiveCocoa/NSObject+Lifetime.swift @@ -10,6 +10,27 @@ fileprivate let lifetimeKey = AssociationKey(default: nil) /// Holds the `Lifetime.Token` of the object. fileprivate let lifetimeTokenKey = AssociationKey(default: nil) +internal func lifetime(of object: AnyObject) -> Lifetime { + if let object = object as? NSObject { + return object.reactive.lifetime + } + + return synchronized(object) { + let associations = Associations(object) + + if let lifetime = associations.value(forKey: lifetimeKey) { + return lifetime + } + + let (lifetime, token) = Lifetime.make() + + associations.setValue(token, forKey: lifetimeTokenKey) + associations.setValue(lifetime, forKey: lifetimeKey) + + return lifetime + } +} + extension Reactive where Base: NSObject { /// Returns a lifetime that ends when the object is deallocated. @nonobjc public var lifetime: Lifetime { diff --git a/ReactiveCocoaTests/KeyValueObservingSpec.swift b/ReactiveCocoaTests/KeyValueObservingSpec.swift index 6f70b844f8..0acc37b558 100644 --- a/ReactiveCocoaTests/KeyValueObservingSpec.swift +++ b/ReactiveCocoaTests/KeyValueObservingSpec.swift @@ -164,6 +164,14 @@ fileprivate class KeyValueObservingSpecConfiguration: QuickConfiguration { // `#keyPath` does not work with weak relationships. return observe(object, "rac_weakObject.rac_value") } + + func strongReferenceChanges(_ object: NSObject) -> SignalProducer { + return observe(object, #keyPath(ObservableObject.target)) + } + + func weakReferenceChanges(_ object: NSObject) -> SignalProducer { + return observe(object, #keyPath(ObservableObject.weakTarget)) + } } override class func configure(_ configuration: Configuration) { @@ -221,6 +229,79 @@ fileprivate class KeyValueObservingSpecConfiguration: QuickConfiguration { expect(completed).toEventually(beTruthy()) } + + it("should support native Swift objects") { + let object = ObservableObject() + var value: Any? + + context + .strongReferenceChanges(object) + .startWithValues { value = $0 } + + expect(value).to(beNil()) + + let token = Token() + object.target = token + expect(value).to(beIdenticalTo(token)) + } + + it("should emit a `nil` when the key path is being cleared due to the deallocation of the Objective-C object it held.") { + let object = ObservableObject() + let null = ObjectIdentifier(NSNull()) + var ids: [ObjectIdentifier] = [] + + context + .weakReferenceChanges(object) + .startWithValues { ids.append($0.map { ObjectIdentifier($0 as AnyObject) } ?? null) } + + expect(ids) == [] + + var token: NSObject? = NSObject() + let tokenId = ObjectIdentifier(token!) + + // KVO would create autoreleasing references of the values being + // passed. So we have to ensure that they are cleared before + // we move on. + autoreleasepool { + object.weakTarget = token + } + + expect(ids) == [tokenId] + + token = nil + + expect(ids) == [tokenId, null] + expect(object.weakTarget).to(beNil()) + } + + it("should emit a `nil` when the key path is being cleared due to the deallocation of the native Swift object it held.") { + let object = ObservableObject() + let null = ObjectIdentifier(NSNull()) + var ids: [ObjectIdentifier] = [] + + context + .weakReferenceChanges(object) + .startWithValues { ids.append($0.map { ObjectIdentifier($0 as AnyObject) } ?? null) } + + expect(ids) == [] + + var token: Token? = Token() + let tokenId = ObjectIdentifier(token!) + + // KVO would create autoreleasing references of the values being + // passed. So we have to ensure that they are cleared before + // we move on. + autoreleasepool { + object.weakTarget = token + } + + expect(ids) == [tokenId] + + token = nil + + expect(ids) == [tokenId, null] + expect(object.weakTarget).to(beNil()) + } } describe("nested key paths") { @@ -279,7 +360,10 @@ fileprivate class KeyValueObservingSpecConfiguration: QuickConfiguration { } it("should not retain replaced value in a nested key path") { + // NOTE: The producer version of this test cases somehow + // fails when the spec is being run alone. let parentObject = NestedObservableObject() + weak var weakOriginalInner: ObservableObject? = parentObject.rac_object expect(weakOriginalInner).toNot(beNil()) @@ -287,10 +371,13 @@ fileprivate class KeyValueObservingSpecConfiguration: QuickConfiguration { _ = context .nestedChanges(parentObject) .start() + } + + autoreleasepool { parentObject.rac_object = ObservableObject() } - expect(weakOriginalInner).toEventually(beNil()) + expect(weakOriginalInner).to(beNil()) } } @@ -467,8 +554,13 @@ fileprivate class KeyValueObservingSpecConfiguration: QuickConfiguration { } } +private final class Token {} + private class ObservableObject: NSObject { dynamic var rac_value: Int = 0 + + dynamic var target: AnyObject? + dynamic weak var weakTarget: AnyObject? } private class NestedObservableObject: NSObject { From ba0ad5e1c489741910acf60c87729382d792cb0f Mon Sep 17 00:00:00 2001 From: Eimantas Vaiciunas Date: Mon, 3 Apr 2017 21:16:03 +0300 Subject: [PATCH 0760/1028] Add an item about documenting return value --- Documentation/DocumentingCode.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Documentation/DocumentingCode.md b/Documentation/DocumentingCode.md index 059953020a..c893660a7a 100644 --- a/Documentation/DocumentingCode.md +++ b/Documentation/DocumentingCode.md @@ -130,3 +130,14 @@ init(count: Int) /// DON'T: /// Does something magical and returns pixie dust from `self`. ``` + +- Document return value on the same line as `return:` delimiter. + +``` +/// DO: +/// - returns: A signal with mapped value over given function +/// +/// DON'T: +/// - returns: +/// A signal with mapped value over given function +``` From 817fb40d4951eabbbf3aad89ef966079d6ac47a3 Mon Sep 17 00:00:00 2001 From: Eimantas Vaiciunas Date: Mon, 3 Apr 2017 21:16:41 +0300 Subject: [PATCH 0761/1028] Update documentation This update contains changes to the documentation of return values. --- ReactiveCocoa/NSObject+Association.swift | 3 +-- ReactiveCocoa/NSObject+BindingTarget.swift | 10 +++---- ReactiveCocoa/NSObject+Intercepting.swift | 26 ++++++++----------- .../NSObject+KeyValueObserving.swift | 12 ++++----- ReactiveCocoa/ObjC+Runtime.swift | 3 +-- ReactiveCocoa/ObjC+RuntimeSubclassing.swift | 3 +-- ReactiveCocoa/UIKit/UIControl.swift | 4 +-- ReactiveCocoa/UIKit/UIGestureRecognizer.swift | 3 +-- ReactiveCocoa/UIKit/iOS/UIPickerView.swift | 3 +-- 9 files changed, 29 insertions(+), 38 deletions(-) diff --git a/ReactiveCocoa/NSObject+Association.swift b/ReactiveCocoa/NSObject+Association.swift index 798a521e47..421596bcd5 100644 --- a/ReactiveCocoa/NSObject+Association.swift +++ b/ReactiveCocoa/NSObject+Association.swift @@ -57,8 +57,7 @@ extension Reactive where Base: NSObject { /// - key: An optional key to differentiate different values. /// - initial: The action that supples an initial value. /// - /// - returns: - /// The associated value for the specified key. + /// - returns: The associated value for the specified key. internal func associatedValue(forKey key: StaticString = #function, initial: (Base) -> T) -> T { let key = AssociationKey(key) diff --git a/ReactiveCocoa/NSObject+BindingTarget.swift b/ReactiveCocoa/NSObject+BindingTarget.swift index d95d384597..c4640305f2 100644 --- a/ReactiveCocoa/NSObject+BindingTarget.swift +++ b/ReactiveCocoa/NSObject+BindingTarget.swift @@ -2,17 +2,17 @@ import Foundation import ReactiveSwift extension Reactive where Base: NSObject { - /// Creates a binding target which uses the lifetime of the object, and weakly - /// references the object so that the supplied `action` is triggered only if - /// the object has not deinitialized. + /// Creates a binding target which uses the lifetime of the object, and + /// weakly references the object so that the supplied `action` is triggered + /// only if the object has not deinitialized. /// /// - parameters: /// - scheduler: An optional scheduler that the binding target uses. If it /// is not specified, a UI scheduler would be used. /// - action: The action to consume values from the bindings. /// - /// - returns: - /// A binding target that holds no strong references to the object. + /// - returns: A binding target that holds no strong references to the + /// object. public func makeBindingTarget(on scheduler: Scheduler = UIScheduler(), _ action: @escaping (Base, U) -> Void) -> BindingTarget { return BindingTarget(on: scheduler, lifetime: lifetime) { [weak base = self.base] value in if let base = base { diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 12e657904f..41189e73f9 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -13,8 +13,8 @@ fileprivate let signatureCacheKey = AssociationKey() fileprivate let selectorCacheKey = AssociationKey() extension Reactive where Base: NSObject { - /// Create a signal which sends a `next` event at the end of every invocation - /// of `selector` on the object. + /// Create a signal which sends a `next` event at the end of every + /// invocation of `selector` on the object. /// /// It completes when the object deinitializes. /// @@ -24,14 +24,14 @@ extension Reactive where Base: NSObject { /// - parameters: /// - selector: The selector to observe. /// - /// - returns: - /// A trigger signal. + /// - returns: A trigger signal. public func trigger(for selector: Selector) -> Signal<(), NoError> { return base.intercept(selector).map { _ in } } - /// Create a signal which sends a `next` event, containing an array of bridged - /// arguments, at the end of every invocation of `selector` on the object. + /// Create a signal which sends a `next` event, containing an array of + /// bridged arguments, at the end of every invocation of `selector` on the + /// object. /// /// It completes when the object deinitializes. /// @@ -41,8 +41,7 @@ extension Reactive where Base: NSObject { /// - parameters: /// - selector: The selector to observe. /// - /// - returns: - /// A signal that sends an array of bridged arguments. + /// - returns: A signal that sends an array of bridged arguments. public func signal(for selector: Selector) -> Signal<[Any?], NoError> { return base.intercept(selector).map(unpackInvocation) } @@ -55,9 +54,8 @@ extension NSObject { /// - object: The object to be intercepted. /// - selector: The selector of the method to be intercepted. /// - /// - returns: - /// A signal that sends the corresponding `NSInvocation` after every - /// invocation of the method. + /// - returns: A signal that sends the corresponding `NSInvocation` after + /// every invocation of the method. @nonobjc fileprivate func intercept(_ selector: Selector) -> Signal { guard let method = class_getInstanceMethod(objcClass, selector) else { fatalError("Selector `\(selector)` does not exist in class `\(String(describing: objcClass))`.") @@ -331,8 +329,7 @@ private final class SignatureCache { /// - parameters: /// - types: The type encoding C string of the method. /// -/// - returns: -/// `true`. +/// - returns: `true`. private func checkTypeEncoding(_ types: UnsafePointer) -> Bool { // Some types, including vector types, are not encoded. In these cases the // signature starts with the size of the argument frame. @@ -353,8 +350,7 @@ private func checkTypeEncoding(_ types: UnsafePointer) -> Bool { /// - parameters: /// - invocation: The `NSInvocation` to unpack. /// -/// - returns: -/// An array of objects. +/// - returns: An array of objects. private func unpackInvocation(_ invocation: AnyObject) -> [Any?] { let invocation = invocation as AnyObject let methodSignature = invocation.objcMethodSignature! diff --git a/ReactiveCocoa/NSObject+KeyValueObserving.swift b/ReactiveCocoa/NSObject+KeyValueObserving.swift index 57f3f292c3..cd06fdd511 100644 --- a/ReactiveCocoa/NSObject+KeyValueObserving.swift +++ b/ReactiveCocoa/NSObject+KeyValueObserving.swift @@ -11,8 +11,8 @@ extension Reactive where Base: NSObject { /// - parameters: /// - keyPath: The key path of the property to be observed. /// - /// - returns: - /// A producer emitting values of the property specified by the key path. + /// - returns: A producer emitting values of the property specified by the + /// key path. public func producer(forKeyPath keyPath: String) -> SignalProducer { return SignalProducer { observer, disposable in disposable += KeyValueObserver.observe( @@ -35,8 +35,8 @@ extension Reactive where Base: NSObject { /// - parameters: /// - keyPath: The key path of the property to be observed. /// - /// - returns: - /// A producer emitting values of the property specified by the key path. + /// - returns: A producer emitting values of the property specified by the + /// key path. public func signal(forKeyPath keyPath: String) -> Signal { return Signal { observer in let disposable = CompositeDisposable() @@ -106,8 +106,8 @@ extension KeyValueObserver { /// - options: The desired configuration of the observation. /// - action: The action to be invoked upon arrival of changes. /// - /// - returns: - /// A disposable that would tear down the observation upon disposal. + /// - returns: A disposable that would tear down the observation upon + /// disposal. static func observe( _ object: NSObject, keyPath: String, diff --git a/ReactiveCocoa/ObjC+Runtime.swift b/ReactiveCocoa/ObjC+Runtime.swift index 63a9117ae3..de29eb7a21 100644 --- a/ReactiveCocoa/ObjC+Runtime.swift +++ b/ReactiveCocoa/ObjC+Runtime.swift @@ -5,8 +5,7 @@ /// - class: The class to search the method in. /// - selector: The selector of the method. /// -/// - returns: -/// The matching method, or `nil` if none is found. +/// - returns: The matching method, or `nil` if none is found. internal func class_getImmediateMethod(_ `class`: AnyClass, _ selector: Selector) -> Method? { if let buffer = class_copyMethodList(`class`, nil) { defer { free(buffer) } diff --git a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift index ae28582003..dead0d2cd6 100644 --- a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift +++ b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift @@ -53,8 +53,7 @@ extension NSObject { /// - parameters: /// - instance: The instance to be swizzled. /// -/// - returns: -/// The runtime subclass of the perceived class of the instance. +/// - returns: The runtime subclass of the perceived class of the instance. internal func swizzleClass(_ instance: NSObject) -> AnyClass { if let knownSubclass = instance.associations.value(forKey: knownRuntimeSubclassKey) { return knownSubclass diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index 403247ee36..b7d961795e 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -44,8 +44,8 @@ extension Reactive where Base: UIControl { /// - parameters: /// - controlEvents: The control event mask. /// - /// - returns: - /// A signal that sends the control each time the control event occurs. + /// - returns: A signal that sends the control each time the control event + /// occurs. public func controlEvents(_ controlEvents: UIControlEvents) -> Signal { return Signal { observer in let receiver = CocoaTarget(observer) { $0 as! Base } diff --git a/ReactiveCocoa/UIKit/UIGestureRecognizer.swift b/ReactiveCocoa/UIKit/UIGestureRecognizer.swift index 6ffc5a89f1..23a9470c10 100644 --- a/ReactiveCocoa/UIKit/UIGestureRecognizer.swift +++ b/ReactiveCocoa/UIKit/UIGestureRecognizer.swift @@ -5,8 +5,7 @@ import enum Result.NoError extension Reactive where Base: UIGestureRecognizer { /// Create a signal which sends a `next` event for each gesture event /// - /// - returns: - /// A trigger signal. + /// - returns: A trigger signal. public var stateChanged: Signal { return Signal { observer in let receiver = CocoaTarget(observer) { gestureRecognizer in diff --git a/ReactiveCocoa/UIKit/iOS/UIPickerView.swift b/ReactiveCocoa/UIKit/iOS/UIPickerView.swift index 2e792d21bf..91e02a1514 100644 --- a/ReactiveCocoa/UIKit/iOS/UIPickerView.swift +++ b/ReactiveCocoa/UIKit/iOS/UIPickerView.swift @@ -34,8 +34,7 @@ extension Reactive where Base: UIPickerView { /// Create a signal which sends a `value` event for each row selection /// - /// - returns: - /// A trigger signal. + /// - returns: A trigger signal. public var selections: Signal<(row: Int, component: Int), NoError> { return proxy.intercept(#selector(UIPickerViewDelegate.pickerView(_:didSelectRow:inComponent:))) .map { (row: $0[1] as! Int, component: $0[2] as! Int) } From 122b076cc42fb3e8339667e64d7ae4ab28e3b35e Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Wed, 5 Apr 2017 03:14:25 +0800 Subject: [PATCH 0762/1028] Update the pod spec to silent deprecation warnings. --- .travis.yml | 2 +- ReactiveCocoa.podspec | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7a4e765bbb..3a851c935b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -60,7 +60,7 @@ matrix: - script: - gem install cocoapods - pod repo update --silent - - pod lib lint --allow-warnings + - pod lib lint ReactiveCocoa.podspec env: - JOB=PODSPEC notifications: diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index f4bd55121d..6cebfdc648 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -24,4 +24,6 @@ Pod::Spec.new do |s| s.module_map = "ReactiveCocoa/module.modulemap" s.dependency 'ReactiveSwift', '~> 1.1' + + s.pod_target_xcconfig = {"OTHER_SWIFT_FLAGS[config=Release]" => "-suppress-warnings" } end From 62c1740ecc71a39725b21361da0a3620a13a5178 Mon Sep 17 00:00:00 2001 From: Marco Cancellieri Date: Wed, 5 Apr 2017 12:03:39 +0200 Subject: [PATCH 0763/1028] Add Extensions for UINavigationItem & UITabBarItem UINavigationItem - title - prompt UITabBarItem - badgeValue - badgeColor --- ReactiveCocoa.xcodeproj/project.pbxproj | 24 +++++++ ReactiveCocoa/UIKit/UINavigationItem.swift | 14 +++++ ReactiveCocoa/UIKit/UITabBarItem.swift | 16 +++++ .../UIKit/UINavigationItemSpec.swift | 61 ++++++++++++++++++ .../UIKit/UITabBarItemSpec.swift | 63 +++++++++++++++++++ 5 files changed, 178 insertions(+) create mode 100644 ReactiveCocoa/UIKit/UINavigationItem.swift create mode 100644 ReactiveCocoa/UIKit/UITabBarItem.swift create mode 100644 ReactiveCocoaTests/UIKit/UINavigationItemSpec.swift create mode 100644 ReactiveCocoaTests/UIKit/UITabBarItemSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 6ceb101d64..9918d38d2e 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -223,6 +223,14 @@ 9AF0EA781D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; A9B315C91B3940980001CB9C /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; A9B315CA1B3940AB0001CB9C /* ReactiveCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9EB3D201E94F08A002A9BCC /* UINavigationItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D1C1E94ECAC002A9BCC /* UINavigationItem.swift */; }; + A9EB3D211E94F0AF002A9BCC /* UINavigationItemSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D1E1E94ED84002A9BCC /* UINavigationItemSpec.swift */; }; + A9EB3D221E94F308002A9BCC /* UINavigationItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D1C1E94ECAC002A9BCC /* UINavigationItem.swift */; }; + A9EB3D231E94F314002A9BCC /* UINavigationItemSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D1E1E94ED84002A9BCC /* UINavigationItemSpec.swift */; }; + A9EB3D291E94F3D3002A9BCC /* UITabBarItemSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D261E94F3C7002A9BCC /* UITabBarItemSpec.swift */; }; + A9EB3D2B1E94F3D9002A9BCC /* UITabBarItemSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D261E94F3C7002A9BCC /* UITabBarItemSpec.swift */; }; + A9EB3D2C1E94F5A2002A9BCC /* UITabBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D241E94F335002A9BCC /* UITabBarItem.swift */; }; + A9EB3D2D1E94F5A2002A9BCC /* UITabBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D241E94F335002A9BCC /* UITabBarItem.swift */; }; B696FB811A7640C00075236D /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B696FB801A7640C00075236D /* TestError.swift */; }; B696FB821A7640C00075236D /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B696FB801A7640C00075236D /* TestError.swift */; }; BE330A0F1D634F1E00806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A0E1D634F1E00806963 /* ReactiveSwift.framework */; }; @@ -432,6 +440,10 @@ A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Framework.xcconfig"; sourceTree = ""; }; A97451361B3A935E00F48E55 /* watchOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-StaticLibrary.xcconfig"; sourceTree = ""; }; A9B315541B3940610001CB9C /* ReactiveCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + A9EB3D1C1E94ECAC002A9BCC /* UINavigationItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UINavigationItem.swift; sourceTree = ""; }; + A9EB3D1E1E94ED84002A9BCC /* UINavigationItemSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UINavigationItemSpec.swift; sourceTree = ""; }; + A9EB3D241E94F335002A9BCC /* UITabBarItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITabBarItem.swift; sourceTree = ""; }; + A9EB3D261E94F3C7002A9BCC /* UITabBarItemSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITabBarItemSpec.swift; sourceTree = ""; }; B696FB801A7640C00075236D /* TestError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestError.swift; sourceTree = ""; }; BE330A0E1D634F1E00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = build/Debug/ReactiveSwift.framework; sourceTree = ""; }; BE330A101D634F2900806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = build/Debug/ReactiveSwift.framework; sourceTree = ""; }; @@ -618,10 +630,12 @@ 419139431DB910570043C9D1 /* UIGestureRecognizer.swift */, 9A1D05F31D93E9F100ACF44C /* UIImageView.swift */, 9A1D05F41D93E9F100ACF44C /* UILabel.swift */, + A9EB3D1C1E94ECAC002A9BCC /* UINavigationItem.swift */, 9A1D05F51D93E9F100ACF44C /* UIProgressView.swift */, BF4335641E02AC7600AC88DD /* UIScrollView.swift */, 9A1D05F61D93E9F100ACF44C /* UISegmentedControl.swift */, 4ABEFDE11DCFC8560066A8C2 /* UITableView.swift */, + A9EB3D241E94F335002A9BCC /* UITabBarItem.swift */, 9A1D05FA1D93E9F100ACF44C /* UITextField.swift */, 9A1D05FB1D93E9F100ACF44C /* UITextView.swift */, 9A1D05FC1D93E9F100ACF44C /* UIView.swift */, @@ -643,6 +657,7 @@ 4191394B1DBA002C0043C9D1 /* UIGestureRecognizerSpec.swift */, 9A1D062B1D93EA7E00ACF44C /* UIImageViewSpec.swift */, 9A1D062C1D93EA7E00ACF44C /* UILabelSpec.swift */, + A9EB3D1E1E94ED84002A9BCC /* UINavigationItemSpec.swift */, BFBD68431E48DA21003CB580 /* UIPickerViewSpec.swift */, 9A1D062D1D93EA7E00ACF44C /* UIProgressViewSpec.swift */, BF4335661E02EEDE00AC88DD /* UIScrollViewSpec.swift */, @@ -652,6 +667,7 @@ 531866F91DD7925600D1285F /* UIStepperSpec.swift */, 9A1D062F1D93EA7E00ACF44C /* UISwitchSpec.swift */, 4ABEFDE31DCFCCD70066A8C2 /* UITableViewSpec.swift */, + A9EB3D261E94F3C7002A9BCC /* UITabBarItemSpec.swift */, 9A1D06321D93EA7E00ACF44C /* UITextFieldSpec.swift */, 9A1D06331D93EA7E00ACF44C /* UITextViewSpec.swift */, 9A1D06351D93EA7E00ACF44C /* UIViewSpec.swift */, @@ -1177,6 +1193,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + A9EB3D221E94F308002A9BCC /* UINavigationItem.swift in Sources */, 9A74884B1E3B8ACE00CD0317 /* DelegateProxy.swift in Sources */, 9A1D06131D93EA0100ACF44C /* UIBarItem.swift in Sources */, 9A1D061F1D93EA0100ACF44C /* UITextField.swift in Sources */, @@ -1205,6 +1222,7 @@ 4A0E11021D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, 9ADFE5A81DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */, CD0C45E11CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, + A9EB3D2D1E94F5A2002A9BCC /* UITabBarItem.swift in Sources */, 9AA0BD921DDE29F800531FCF /* NSObject+ObjCRuntime.swift in Sources */, 9A1D061A1D93EA0100ACF44C /* UIProgressView.swift in Sources */, 9AA0BD7F1DDE03DE00531FCF /* ObjC+Runtime.swift in Sources */, @@ -1221,6 +1239,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + A9EB3D231E94F314002A9BCC /* UINavigationItemSpec.swift in Sources */, 9A9A129A1DC7A97100D10223 /* UIGestureRecognizerSpec.swift in Sources */, 9A1D06591D93EA7E00ACF44C /* UIViewSpec.swift in Sources */, 53A6BED81DD4BD2C0016C058 /* MKMapViewSpec.swift in Sources */, @@ -1241,6 +1260,7 @@ 9A892D911E8D19BE00EA35F3 /* DelegateProxySpec.swift in Sources */, 9A1D06471D93EA7E00ACF44C /* UILabelSpec.swift in Sources */, 9A1D06391D93EA7E00ACF44C /* UIBarButtonItemSpec.swift in Sources */, + A9EB3D2B1E94F3D9002A9BCC /* UITabBarItemSpec.swift in Sources */, 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */, 9ADFE5A31DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */, 9A1D06411D93EA7E00ACF44C /* UIControlSpec.swift in Sources */, @@ -1351,6 +1371,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + A9EB3D201E94F08A002A9BCC /* UINavigationItem.swift in Sources */, 4ABEFE251DCFCF630066A8C2 /* UICollectionView.swift in Sources */, 9A1D06011D93EA0000ACF44C /* UIBarItem.swift in Sources */, 9A1D060D1D93EA0000ACF44C /* UITextField.swift in Sources */, @@ -1388,6 +1409,7 @@ 4ABEFE1F1DCFCEF60066A8C2 /* UITableView.swift in Sources */, 9A1D06081D93EA0000ACF44C /* UIProgressView.swift in Sources */, 531866F81DD7920400D1285F /* UIStepper.swift in Sources */, + A9EB3D2C1E94F5A2002A9BCC /* UITabBarItem.swift in Sources */, 538DCB7A1DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */, 9A1D06001D93EA0000ACF44C /* UIBarButtonItem.swift in Sources */, 9AA0BD991DDE7A2200531FCF /* ObjCRuntimeAliases.m in Sources */, @@ -1404,6 +1426,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + A9EB3D211E94F0AF002A9BCC /* UINavigationItemSpec.swift in Sources */, 9A1D06581D93EA7E00ACF44C /* UIViewSpec.swift in Sources */, 9AAD498A1DED2F380068EC9B /* UIKeyboardSpec.swift in Sources */, BFCF77621DFAD9440058006E /* UISearchBarSpec.swift in Sources */, @@ -1441,6 +1464,7 @@ 9A1D06401D93EA7E00ACF44C /* UIControlSpec.swift in Sources */, 9A1D06481D93EA7E00ACF44C /* UIProgressViewSpec.swift in Sources */, CD8401841CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, + A9EB3D291E94F3D3002A9BCC /* UITabBarItemSpec.swift in Sources */, 538DCB7E1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift in Sources */, 9A1D06521D93EA7E00ACF44C /* UITextFieldSpec.swift in Sources */, ); diff --git a/ReactiveCocoa/UIKit/UINavigationItem.swift b/ReactiveCocoa/UIKit/UINavigationItem.swift new file mode 100644 index 0000000000..e38c527d05 --- /dev/null +++ b/ReactiveCocoa/UIKit/UINavigationItem.swift @@ -0,0 +1,14 @@ +import ReactiveSwift +import UIKit + +extension Reactive where Base: UINavigationItem { + /// Sets the title of the navigation item. + public var title: BindingTarget { + return makeBindingTarget { $0.title = $1 } + } + + /// Sets the prompt of the navigation item. + public var prompt: BindingTarget { + return makeBindingTarget { $0.prompt = $1 } + } +} diff --git a/ReactiveCocoa/UIKit/UITabBarItem.swift b/ReactiveCocoa/UIKit/UITabBarItem.swift new file mode 100644 index 0000000000..9081e3b88c --- /dev/null +++ b/ReactiveCocoa/UIKit/UITabBarItem.swift @@ -0,0 +1,16 @@ +import ReactiveSwift +import UIKit + +extension Reactive where Base: UITabBarItem { + /// Sets the badge value of the tab bar item. + public var badgeValue: BindingTarget { + return makeBindingTarget { $0.badgeValue = $1 } + } + + + /// Sets the badge color of the tab bar item. + @available(iOS 10, *) + public var badgeColor: BindingTarget { + return makeBindingTarget { $0.badgeColor = $1 } + } +} diff --git a/ReactiveCocoaTests/UIKit/UINavigationItemSpec.swift b/ReactiveCocoaTests/UIKit/UINavigationItemSpec.swift new file mode 100644 index 0000000000..22f56b1c75 --- /dev/null +++ b/ReactiveCocoaTests/UIKit/UINavigationItemSpec.swift @@ -0,0 +1,61 @@ +import ReactiveSwift +import ReactiveCocoa +import UIKit +import Quick +import Nimble +import enum Result.NoError + +class UINavigationItemSpec: QuickSpec { + override func spec() { + var navigationItem: UINavigationItem! + weak var _navigationItem: UINavigationItem? + + beforeEach { + navigationItem = UINavigationItem(title: "initial") + _navigationItem = navigationItem + } + + afterEach { + navigationItem = nil + expect(_navigationItem).to(beNil()) + } + + it("should accept changes from bindings to its title value") { + let firstChange = "first" + let secondChange = "second" + + navigationItem.title = "" + + let (pipeSignal, observer) = Signal.pipe() + navigationItem.reactive.title <~ SignalProducer(pipeSignal) + + observer.send(value: firstChange) + expect(navigationItem.title) == firstChange + + observer.send(value: secondChange) + expect(navigationItem.title) == secondChange + + observer.send(value: nil) + expect(navigationItem.title).to(beNil()) + } + + it("should accept changes from bindings to its prompt value") { + let firstChange = "first" + let secondChange = "second" + + navigationItem.prompt = "" + + let (pipeSignal, observer) = Signal.pipe() + navigationItem.reactive.prompt <~ SignalProducer(pipeSignal) + + observer.send(value: firstChange) + expect(navigationItem.prompt) == firstChange + + observer.send(value: secondChange) + expect(navigationItem.prompt) == secondChange + + observer.send(value: nil) + expect(navigationItem.prompt).to(beNil()) + } + } +} diff --git a/ReactiveCocoaTests/UIKit/UITabBarItemSpec.swift b/ReactiveCocoaTests/UIKit/UITabBarItemSpec.swift new file mode 100644 index 0000000000..84e874f994 --- /dev/null +++ b/ReactiveCocoaTests/UIKit/UITabBarItemSpec.swift @@ -0,0 +1,63 @@ +import ReactiveSwift +import ReactiveCocoa +import UIKit +import Quick +import Nimble +import enum Result.NoError + +class UITabBarSpec: QuickSpec { + override func spec() { + var tabBarItem: UITabBarItem! + weak var _tabBarItem: UITabBarItem? + + beforeEach { + tabBarItem = UITabBarItem(tabBarSystemItem: .downloads, tag: 1) + _tabBarItem = tabBarItem + } + + afterEach { + tabBarItem = nil + expect(_tabBarItem).to(beNil()) + } + + it("should accept changes from bindings to its badge value") { + let firstChange = "first" + let secondChange = "second" + + tabBarItem.badgeValue = "" + + let (pipeSignal, observer) = Signal.pipe() + tabBarItem.reactive.badgeValue <~ SignalProducer(pipeSignal) + + observer.send(value: firstChange) + expect(tabBarItem.badgeValue) == firstChange + + observer.send(value: secondChange) + expect(tabBarItem.badgeValue) == secondChange + + observer.send(value: nil) + expect(tabBarItem.badgeValue).to(beNil()) + } + + if #available(iOS 10, *) { + it("should accept changes from bindings to its badge color value") { + let firstChange: UIColor = .red + let secondChange: UIColor = .green + + tabBarItem.badgeColor = .blue + + let (pipeSignal, observer) = Signal.pipe() + tabBarItem.reactive.badgeColor <~ SignalProducer(pipeSignal) + + observer.send(value: firstChange) + expect(tabBarItem.badgeColor) == firstChange + + observer.send(value: secondChange) + expect(tabBarItem.badgeColor) == secondChange + + observer.send(value: nil) + expect(tabBarItem.badgeColor).to(beNil()) + } + } + } +} From 157758774394a724c425b829f460fd46003d3d8b Mon Sep 17 00:00:00 2001 From: Marco Cancellieri Date: Wed, 5 Apr 2017 13:22:15 +0200 Subject: [PATCH 0764/1028] fix availabilities --- ReactiveCocoa/UIKit/UINavigationItem.swift | 10 +++-- ReactiveCocoa/UIKit/UITabBarItem.swift | 1 + .../UIKit/UINavigationItemSpec.swift | 40 ++++++++++--------- .../UIKit/UITabBarItemSpec.swift | 6 +-- 4 files changed, 31 insertions(+), 26 deletions(-) diff --git a/ReactiveCocoa/UIKit/UINavigationItem.swift b/ReactiveCocoa/UIKit/UINavigationItem.swift index e38c527d05..3db6460624 100644 --- a/ReactiveCocoa/UIKit/UINavigationItem.swift +++ b/ReactiveCocoa/UIKit/UINavigationItem.swift @@ -7,8 +7,10 @@ extension Reactive where Base: UINavigationItem { return makeBindingTarget { $0.title = $1 } } - /// Sets the prompt of the navigation item. - public var prompt: BindingTarget { - return makeBindingTarget { $0.prompt = $1 } - } + #if os(iOS) + /// Sets the prompt of the navigation item. + public var prompt: BindingTarget { + return makeBindingTarget { $0.prompt = $1 } + } + #endif } diff --git a/ReactiveCocoa/UIKit/UITabBarItem.swift b/ReactiveCocoa/UIKit/UITabBarItem.swift index 9081e3b88c..415fa64f8c 100644 --- a/ReactiveCocoa/UIKit/UITabBarItem.swift +++ b/ReactiveCocoa/UIKit/UITabBarItem.swift @@ -10,6 +10,7 @@ extension Reactive where Base: UITabBarItem { /// Sets the badge color of the tab bar item. @available(iOS 10, *) + @available(tvOS 10, *) public var badgeColor: BindingTarget { return makeBindingTarget { $0.badgeColor = $1 } } diff --git a/ReactiveCocoaTests/UIKit/UINavigationItemSpec.swift b/ReactiveCocoaTests/UIKit/UINavigationItemSpec.swift index 22f56b1c75..e9e5127427 100644 --- a/ReactiveCocoaTests/UIKit/UINavigationItemSpec.swift +++ b/ReactiveCocoaTests/UIKit/UINavigationItemSpec.swift @@ -27,7 +27,7 @@ class UINavigationItemSpec: QuickSpec { navigationItem.title = "" let (pipeSignal, observer) = Signal.pipe() - navigationItem.reactive.title <~ SignalProducer(pipeSignal) + navigationItem.reactive.title <~ pipeSignal observer.send(value: firstChange) expect(navigationItem.title) == firstChange @@ -39,23 +39,25 @@ class UINavigationItemSpec: QuickSpec { expect(navigationItem.title).to(beNil()) } - it("should accept changes from bindings to its prompt value") { - let firstChange = "first" - let secondChange = "second" - - navigationItem.prompt = "" - - let (pipeSignal, observer) = Signal.pipe() - navigationItem.reactive.prompt <~ SignalProducer(pipeSignal) - - observer.send(value: firstChange) - expect(navigationItem.prompt) == firstChange - - observer.send(value: secondChange) - expect(navigationItem.prompt) == secondChange - - observer.send(value: nil) - expect(navigationItem.prompt).to(beNil()) - } + #if os(iOS) + it("should accept changes from bindings to its prompt value") { + let firstChange = "first" + let secondChange = "second" + + navigationItem.prompt = "" + + let (pipeSignal, observer) = Signal.pipe() + navigationItem.reactive.prompt <~ pipeSignal + + observer.send(value: firstChange) + expect(navigationItem.prompt) == firstChange + + observer.send(value: secondChange) + expect(navigationItem.prompt) == secondChange + + observer.send(value: nil) + expect(navigationItem.prompt).to(beNil()) + } + #endif } } diff --git a/ReactiveCocoaTests/UIKit/UITabBarItemSpec.swift b/ReactiveCocoaTests/UIKit/UITabBarItemSpec.swift index 84e874f994..8417ec9dcc 100644 --- a/ReactiveCocoaTests/UIKit/UITabBarItemSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITabBarItemSpec.swift @@ -27,7 +27,7 @@ class UITabBarSpec: QuickSpec { tabBarItem.badgeValue = "" let (pipeSignal, observer) = Signal.pipe() - tabBarItem.reactive.badgeValue <~ SignalProducer(pipeSignal) + tabBarItem.reactive.badgeValue <~ pipeSignal observer.send(value: firstChange) expect(tabBarItem.badgeValue) == firstChange @@ -39,7 +39,7 @@ class UITabBarSpec: QuickSpec { expect(tabBarItem.badgeValue).to(beNil()) } - if #available(iOS 10, *) { + if #available(iOS 10, *), #available(tvOS 10, *) { it("should accept changes from bindings to its badge color value") { let firstChange: UIColor = .red let secondChange: UIColor = .green @@ -47,7 +47,7 @@ class UITabBarSpec: QuickSpec { tabBarItem.badgeColor = .blue let (pipeSignal, observer) = Signal.pipe() - tabBarItem.reactive.badgeColor <~ SignalProducer(pipeSignal) + tabBarItem.reactive.badgeColor <~ pipeSignal observer.send(value: firstChange) expect(tabBarItem.badgeColor) == firstChange From 24c18cac464d5d3feb8cc3f96330f0562b5d2a48 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Wed, 5 Apr 2017 21:15:04 +0900 Subject: [PATCH 0765/1028] Update podspec --- ReactiveCocoa.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 6cebfdc648..602029c302 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -25,5 +25,5 @@ Pod::Spec.new do |s| s.dependency 'ReactiveSwift', '~> 1.1' - s.pod_target_xcconfig = {"OTHER_SWIFT_FLAGS[config=Release]" => "-suppress-warnings" } + s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "-suppress-warnings" } end From 6800645eca64dbcb4edab9d1fa622cc93d277d97 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Wed, 5 Apr 2017 22:52:23 +0900 Subject: [PATCH 0766/1028] Silence the warnings - Avoids passing optional values to string interpolation - Avoids using `class func initialize()` which should be unavailable in future Swift version - Replaces deprecated API usages --- ReactiveCocoa.xcodeproj/project.pbxproj | 8 ++++ .../NSObject+KeyValueObserving.swift | 8 ++-- ReactiveCocoaTests/InterceptingSpec.swift | 48 +++++++++---------- .../ReactiveCocoaTestsConfiguration.swift | 14 ++++++ .../SignalProducerNimbleMatchers.swift | 2 +- ...ol+EnableSendActionsForControlEvents.swift | 14 ++---- .../UIKit/UIDatePickerSpec.swift | 2 +- 7 files changed, 54 insertions(+), 42 deletions(-) create mode 100644 ReactiveCocoaTests/ReactiveCocoaTestsConfiguration.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 6ceb101d64..41be74ad71 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -245,6 +245,9 @@ CD0C45DF1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */; }; CD0C45E01CC9A288009F5BF0 /* DynamicProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */; }; CD0C45E11CC9A288009F5BF0 /* DynamicProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */; }; + CD42C69B1E951F6900AA9504 /* ReactiveCocoaTestsConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD42C69A1E951F6900AA9504 /* ReactiveCocoaTestsConfiguration.swift */; }; + CD42C69C1E951F6A00AA9504 /* ReactiveCocoaTestsConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD42C69A1E951F6900AA9504 /* ReactiveCocoaTestsConfiguration.swift */; }; + CD42C69D1E951F6A00AA9504 /* ReactiveCocoaTestsConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD42C69A1E951F6900AA9504 /* ReactiveCocoaTestsConfiguration.swift */; }; CD8401831CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8401821CEE8ED7009F0ABF /* CocoaActionSpec.swift */; }; CD8401841CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8401821CEE8ED7009F0ABF /* CocoaActionSpec.swift */; }; CD8401851CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8401821CEE8ED7009F0ABF /* CocoaActionSpec.swift */; }; @@ -448,6 +451,7 @@ BFCF77601DFAD9120058006E /* UISearchBarSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISearchBarSpec.swift; sourceTree = ""; }; BFE145881E43991A00208736 /* UIPickerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIPickerView.swift; sourceTree = ""; }; CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DynamicProperty.swift; sourceTree = ""; }; + CD42C69A1E951F6900AA9504 /* ReactiveCocoaTestsConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactiveCocoaTestsConfiguration.swift; sourceTree = ""; }; CD8401821CEE8ED7009F0ABF /* CocoaActionSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaActionSpec.swift; sourceTree = ""; }; CDC42E2E1AE7AB8B00965373 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D03766B119EDA60000A782A9 /* test-data.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "test-data.json"; sourceTree = ""; }; @@ -800,6 +804,7 @@ 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */, 9A24A8431DE1429600987AF9 /* SwizzlingSpec.swift */, 3B30EE8B1E7BE529007CC8EF /* DeprecationsSpec.swift */, + CD42C69A1E951F6900AA9504 /* ReactiveCocoaTestsConfiguration.swift */, D04725FA19E49ED7006002AA /* Supporting Files */, ); path = ReactiveCocoaTests; @@ -1234,6 +1239,7 @@ 9A1D063B1D93EA7E00ACF44C /* UIButtonSpec.swift in Sources */, 9A24A8471DE142A600987AF9 /* SwizzlingSpec.swift in Sources */, 9A1D064B1D93EA7E00ACF44C /* UISegmentedControlSpec.swift in Sources */, + CD42C69D1E951F6A00AA9504 /* ReactiveCocoaTestsConfiguration.swift in Sources */, 9A1D063F1D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift in Sources */, 9A1D06371D93EA7E00ACF44C /* UIActivityIndicatorViewSpec.swift in Sources */, 9A6AAA2F1DB903A40013AAEA /* ReusableComponentsSpec.swift in Sources */, @@ -1342,6 +1348,7 @@ CD8401831CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, 9A9DFEE91DA7EFB60039EE1B /* AssociationSpec.swift in Sources */, 3B30EE8C1E7BE529007CC8EF /* DeprecationsSpec.swift in Sources */, + CD42C69B1E951F6900AA9504 /* ReactiveCocoaTestsConfiguration.swift in Sources */, 9A1E72BA1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, 4ABEFE2E1DCFD01F0066A8C2 /* NSTableViewSpec.swift in Sources */, ); @@ -1423,6 +1430,7 @@ 9A892D901E8D19BE00EA35F3 /* DelegateProxySpec.swift in Sources */, 531866FA1DD7925600D1285F /* UIStepperSpec.swift in Sources */, 9A1D06361D93EA7E00ACF44C /* UIActivityIndicatorViewSpec.swift in Sources */, + CD42C69C1E951F6A00AA9504 /* ReactiveCocoaTestsConfiguration.swift in Sources */, 9A1D06461D93EA7E00ACF44C /* UILabelSpec.swift in Sources */, 3B30EE8D1E7BE529007CC8EF /* DeprecationsSpec.swift in Sources */, 9A9DFEEA1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */, diff --git a/ReactiveCocoa/NSObject+KeyValueObserving.swift b/ReactiveCocoa/NSObject+KeyValueObserving.swift index cd06fdd511..61aa93c6f2 100644 --- a/ReactiveCocoa/NSObject+KeyValueObserving.swift +++ b/ReactiveCocoa/NSObject+KeyValueObserving.swift @@ -264,7 +264,7 @@ internal struct PropertyAttributes { let _next = NSGetSizeAndAlignment(typeString, nil, nil) guard _next != typeString else { let string = String(validatingUTF8: attrString) - preconditionFailure("Could not read past type in attribute string: \(string).") + preconditionFailure("Could not read past type in attribute string: \(String(describing: string)).") } var next = UnsafeMutablePointer(mutating: _next) @@ -355,7 +355,7 @@ internal struct PropertyAttributes { case Code.Attribute.oldTypeEncoding: let string = String(validatingUTF8: attrString) - assertionFailure("Old-style type encoding is unsupported in attribute string \"\(string)\"") + assertionFailure("Old-style type encoding is unsupported in attribute string \"\(String(describing: string))\"") // skip over this type encoding while next.pointee != Code.comma && next.pointee != Code.nul { @@ -369,14 +369,14 @@ internal struct PropertyAttributes { let flag = String(validatingUTF8: pointer) let string = String(validatingUTF8: attrString) - preconditionFailure("ERROR: Unrecognized attribute string flag '\(flag)' in attribute string \"\(string)\".") + preconditionFailure("ERROR: Unrecognized attribute string flag '\(String(describing: flag))' in attribute string \"\(String(describing: string))\".") } } if next.pointee != Code.nul { let unparsedData = String(validatingUTF8: next) let string = String(validatingUTF8: attrString) - assertionFailure("Warning: Unparsed data \"\(unparsedData)\" in attribute string \"\(string)\".") + assertionFailure("Warning: Unparsed data \"\(String(describing: unparsedData))\" in attribute string \"\(String(describing: string))\".") } self.objectClass = objectClass diff --git a/ReactiveCocoaTests/InterceptingSpec.swift b/ReactiveCocoaTests/InterceptingSpec.swift index 56f5e61694..843c9e0299 100644 --- a/ReactiveCocoaTests/InterceptingSpec.swift +++ b/ReactiveCocoaTests/InterceptingSpec.swift @@ -8,6 +8,10 @@ import CoreGraphics class InterceptingSpec: QuickSpec { override func spec() { + beforeSuite { + ForwardInvocationTestObject._initialize() + } + describe("trigger(for:)") { var object: InterceptedObject! weak var _object: InterceptedObject? @@ -830,36 +834,28 @@ private class ForwardInvocationTestObject: InterceptedObject { var forwardedCount = 0 var forwardedSelector: Selector? - override open class func initialize() { - struct Static { - static var token: Int = { - let impl: @convention(c) (Any, Selector, AnyObject) -> Void = { object, _, invocation in - let object = object as! ForwardInvocationTestObject - object.forwardedCount += 1 - object.forwardedSelector = invocation.selector - } - - let success = class_addMethod(ForwardInvocationTestObject.self, - ObjCSelector.forwardInvocation, - unsafeBitCast(impl, to: IMP.self), - ObjCMethodEncoding.forwardInvocation) - - assert(success) - assert(ForwardInvocationTestObject.instancesRespond(to: ObjCSelector.forwardInvocation)) + static func _initialize() { + let impl: @convention(c) (Any, Selector, AnyObject) -> Void = { object, _, invocation in + let object = object as! ForwardInvocationTestObject + object.forwardedCount += 1 + object.forwardedSelector = invocation.selector + } - let success2 = class_addMethod(ForwardInvocationTestObject.self, - ForwardInvocationTestObject.forwardedSelector, - _rac_objc_msgForward, - ObjCMethodEncoding.forwardInvocation) + let success = class_addMethod(ForwardInvocationTestObject.self, + ObjCSelector.forwardInvocation, + unsafeBitCast(impl, to: IMP.self), + ObjCMethodEncoding.forwardInvocation) - assert(success2) - assert(ForwardInvocationTestObject.instancesRespond(to: ForwardInvocationTestObject.forwardedSelector)) + assert(success) + assert(ForwardInvocationTestObject.instancesRespond(to: ObjCSelector.forwardInvocation)) - return 0 - }() - } + let success2 = class_addMethod(ForwardInvocationTestObject.self, + ForwardInvocationTestObject.forwardedSelector, + _rac_objc_msgForward, + ObjCMethodEncoding.forwardInvocation) - _ = Static.token + assert(success2) + assert(ForwardInvocationTestObject.instancesRespond(to: ForwardInvocationTestObject.forwardedSelector)) } } diff --git a/ReactiveCocoaTests/ReactiveCocoaTestsConfiguration.swift b/ReactiveCocoaTests/ReactiveCocoaTestsConfiguration.swift new file mode 100644 index 0000000000..5c46cc3cf3 --- /dev/null +++ b/ReactiveCocoaTests/ReactiveCocoaTestsConfiguration.swift @@ -0,0 +1,14 @@ +import Quick +#if os(iOS) || os(tvOS) +import UIKit +#endif + +class ReactiveCocoaTestsConfiguration: QuickConfiguration { + override class func configure(_ configuration: Configuration) { + #if os(iOS) || os(tvOS) + configuration.beforeSuite { + UIControl._initialize() + } + #endif + } +} diff --git a/ReactiveCocoaTests/SignalProducerNimbleMatchers.swift b/ReactiveCocoaTests/SignalProducerNimbleMatchers.swift index 28f8316fa0..009684c2ea 100644 --- a/ReactiveCocoaTests/SignalProducerNimbleMatchers.swift +++ b/ReactiveCocoaTests/SignalProducerNimbleMatchers.swift @@ -11,7 +11,7 @@ public func sendValues(_ values: [T], sendError mayb return NonNilMatcherFunc { actualExpression, failureMessage in precondition(maybeSendError == nil || !complete, "Signals can't both send an error and complete") - failureMessage.postfixMessage = "Send values \(values). Send error \(maybeSendError). Complete: \(complete)" + failureMessage.postfixMessage = "Send values \(values). Send error \(String(describing: maybeSendError)). Complete: \(complete)" let maybeProducer = try actualExpression.evaluate() if let signalProducer = maybeProducer { diff --git a/ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift b/ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift index 886b336d55..7bacab3e50 100644 --- a/ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift +++ b/ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift @@ -1,6 +1,6 @@ import UIKit -private let rac_swizzleToken: Void = { +private func rac_swizzle() { let originalSelector = #selector(UIControl.sendAction(_:to:for:)) let swizzledSelector = #selector(UIControl.rac_sendAction(_:to:forEvent:)) @@ -20,9 +20,7 @@ private let rac_swizzleToken: Void = { } 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 @@ -30,12 +28,8 @@ private let rac_swizzleToken: Void = { /// in the future. To be able to test them, we're now using swizzling to manually invoke /// the pair target+action. extension UIControl { - override open class func initialize() { - guard self === UIControl.self else { - return - } - - _ = rac_swizzleToken + static func _initialize() { + rac_swizzle() } // MARK: - Method Swizzling diff --git a/ReactiveCocoaTests/UIKit/UIDatePickerSpec.swift b/ReactiveCocoaTests/UIKit/UIDatePickerSpec.swift index 99f43de451..7904b1760d 100644 --- a/ReactiveCocoaTests/UIKit/UIDatePickerSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIDatePickerSpec.swift @@ -25,7 +25,7 @@ class UIDatePickerSpec: QuickSpec { } it("should accept changes from bindings to its date value") { - picker.reactive.date.consume(date) + picker.reactive.date.action(date) expect(picker.date) == date } From 2674457cac3d192d9e059785f90c3ad19fb1a290 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Wed, 5 Apr 2017 22:53:36 +0900 Subject: [PATCH 0767/1028] Remove obsolete .swift-version which was used in RAC 4.x --- .swift-version | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .swift-version diff --git a/.swift-version b/.swift-version deleted file mode 100644 index 8c50098d8a..0000000000 --- a/.swift-version +++ /dev/null @@ -1 +0,0 @@ -3.1 From fd14f89433335544d5becab3d2434246559677e0 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Wed, 5 Apr 2017 23:23:17 +0900 Subject: [PATCH 0768/1028] Make it fileprivate as requested --- ReactiveCocoaTests/InterceptingSpec.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoaTests/InterceptingSpec.swift b/ReactiveCocoaTests/InterceptingSpec.swift index 843c9e0299..15e8b6dee7 100644 --- a/ReactiveCocoaTests/InterceptingSpec.swift +++ b/ReactiveCocoaTests/InterceptingSpec.swift @@ -834,7 +834,7 @@ private class ForwardInvocationTestObject: InterceptedObject { var forwardedCount = 0 var forwardedSelector: Selector? - static func _initialize() { + fileprivate static func _initialize() { let impl: @convention(c) (Any, Selector, AnyObject) -> Void = { object, _, invocation in let object = object as! ForwardInvocationTestObject object.forwardedCount += 1 From 14746024ca9d4fa71d2a74dbf9aa70eabac0f886 Mon Sep 17 00:00:00 2001 From: Marco Cancellieri Date: Wed, 5 Apr 2017 18:45:08 +0200 Subject: [PATCH 0769/1028] BindingTargets for UIFeedbackGenerator --- ReactiveCocoa.xcodeproj/project.pbxproj | 16 ++++++++++++++++ .../UIKit/iOS/UIFeedbackGenerator.swift | 12 ++++++++++++ ...2\200\213Feedback\342\200\213Generator.swift" | 12 ++++++++++++ ...2\200\213Feedback\342\200\213Generator.swift" | 10 ++++++++++ ...2\200\213Feedback\342\200\213Generator.swift" | 12 ++++++++++++ 5 files changed, 62 insertions(+) create mode 100644 ReactiveCocoa/UIKit/iOS/UIFeedbackGenerator.swift create mode 100644 "ReactiveCocoa/UIKit/iOS/UIImpact\342\200\213Feedback\342\200\213Generator.swift" create mode 100644 "ReactiveCocoa/UIKit/iOS/UINotification\342\200\213Feedback\342\200\213Generator.swift" create mode 100644 "ReactiveCocoa/UIKit/iOS/UISelection\342\200\213Feedback\342\200\213Generator.swift" diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 9918d38d2e..de5de194d2 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -231,6 +231,10 @@ A9EB3D2B1E94F3D9002A9BCC /* UITabBarItemSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D261E94F3C7002A9BCC /* UITabBarItemSpec.swift */; }; A9EB3D2C1E94F5A2002A9BCC /* UITabBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D241E94F335002A9BCC /* UITabBarItem.swift */; }; A9EB3D2D1E94F5A2002A9BCC /* UITabBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D241E94F335002A9BCC /* UITabBarItem.swift */; }; + A9EB3D811E955602002A9BCC /* UIFeedbackGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D7D1E955602002A9BCC /* UIFeedbackGenerator.swift */; }; + A9EB3D821E955602002A9BCC /* UIImpact​Feedback​Generator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D7E1E955602002A9BCC /* UIImpact​Feedback​Generator.swift */; }; + A9EB3D831E955602002A9BCC /* UINotification​Feedback​Generator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D7F1E955602002A9BCC /* UINotification​Feedback​Generator.swift */; }; + A9EB3D841E955602002A9BCC /* UISelection​Feedback​Generator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D801E955602002A9BCC /* UISelection​Feedback​Generator.swift */; }; B696FB811A7640C00075236D /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B696FB801A7640C00075236D /* TestError.swift */; }; B696FB821A7640C00075236D /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B696FB801A7640C00075236D /* TestError.swift */; }; BE330A0F1D634F1E00806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A0E1D634F1E00806963 /* ReactiveSwift.framework */; }; @@ -444,6 +448,10 @@ A9EB3D1E1E94ED84002A9BCC /* UINavigationItemSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UINavigationItemSpec.swift; sourceTree = ""; }; A9EB3D241E94F335002A9BCC /* UITabBarItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITabBarItem.swift; sourceTree = ""; }; A9EB3D261E94F3C7002A9BCC /* UITabBarItemSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITabBarItemSpec.swift; sourceTree = ""; }; + A9EB3D7D1E955602002A9BCC /* UIFeedbackGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIFeedbackGenerator.swift; sourceTree = ""; }; + A9EB3D7E1E955602002A9BCC /* UIImpact​Feedback​Generator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImpact​Feedback​Generator.swift"; sourceTree = ""; }; + A9EB3D7F1E955602002A9BCC /* UINotification​Feedback​Generator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UINotification​Feedback​Generator.swift"; sourceTree = ""; }; + A9EB3D801E955602002A9BCC /* UISelection​Feedback​Generator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UISelection​Feedback​Generator.swift"; sourceTree = ""; }; B696FB801A7640C00075236D /* TestError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestError.swift; sourceTree = ""; }; BE330A0E1D634F1E00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = build/Debug/ReactiveSwift.framework; sourceTree = ""; }; BE330A101D634F2900806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = build/Debug/ReactiveSwift.framework; sourceTree = ""; }; @@ -720,6 +728,10 @@ isa = PBXGroup; children = ( 9A1D05F21D93E9F100ACF44C /* UIDatePicker.swift */, + A9EB3D7D1E955602002A9BCC /* UIFeedbackGenerator.swift */, + A9EB3D7E1E955602002A9BCC /* UIImpact​Feedback​Generator.swift */, + A9EB3D7F1E955602002A9BCC /* UINotification​Feedback​Generator.swift */, + A9EB3D801E955602002A9BCC /* UISelection​Feedback​Generator.swift */, 9AAD49871DED2C350068EC9B /* UIKeyboard.swift */, BFE145881E43991A00208736 /* UIPickerView.swift */, 3BCAAC791DEE19BC00B30335 /* UIRefreshControl.swift */, @@ -1372,10 +1384,13 @@ buildActionMask = 2147483647; files = ( A9EB3D201E94F08A002A9BCC /* UINavigationItem.swift in Sources */, + A9EB3D841E955602002A9BCC /* UISelection​Feedback​Generator.swift in Sources */, 4ABEFE251DCFCF630066A8C2 /* UICollectionView.swift in Sources */, 9A1D06011D93EA0000ACF44C /* UIBarItem.swift in Sources */, + A9EB3D811E955602002A9BCC /* UIFeedbackGenerator.swift in Sources */, 9A1D060D1D93EA0000ACF44C /* UITextField.swift in Sources */, 9AB15C7B1E26CD9A00997378 /* Deprecations+Removals.swift in Sources */, + A9EB3D831E955602002A9BCC /* UINotification​Feedback​Generator.swift in Sources */, 9A6AAA231DB8F51C0013AAEA /* ReusableComponents.swift in Sources */, 9A7488491E3B8ACE00CD0317 /* DelegateProxy.swift in Sources */, 9A1D060E1D93EA0000ACF44C /* UITextView.swift in Sources */, @@ -1394,6 +1409,7 @@ 9A1D06041D93EA0000ACF44C /* UIControl.swift in Sources */, 9A1D06021D93EA0000ACF44C /* UIButton.swift in Sources */, 9AA0BD901DDE29F800531FCF /* NSObject+ObjCRuntime.swift in Sources */, + A9EB3D821E955602002A9BCC /* UIImpact​Feedback​Generator.swift in Sources */, 419139461DB910570043C9D1 /* UIGestureRecognizer.swift in Sources */, 9AA0BD7D1DDE03DE00531FCF /* ObjC+Runtime.swift in Sources */, BFE1458A1E439AB000208736 /* UIPickerView.swift in Sources */, diff --git a/ReactiveCocoa/UIKit/iOS/UIFeedbackGenerator.swift b/ReactiveCocoa/UIKit/iOS/UIFeedbackGenerator.swift new file mode 100644 index 0000000000..e64deda5da --- /dev/null +++ b/ReactiveCocoa/UIKit/iOS/UIFeedbackGenerator.swift @@ -0,0 +1,12 @@ +import ReactiveSwift +import UIKit + +@available(iOS 10.0, *) +extension Reactive where Base: UIFeedbackGenerator { + /// Prepares the feedback generator. + public var prepare: BindingTarget<()> { + return makeBindingTarget { generator, _ in + generator.prepare() + } + } +} diff --git "a/ReactiveCocoa/UIKit/iOS/UIImpact\342\200\213Feedback\342\200\213Generator.swift" "b/ReactiveCocoa/UIKit/iOS/UIImpact\342\200\213Feedback\342\200\213Generator.swift" new file mode 100644 index 0000000000..e3fe8959e4 --- /dev/null +++ "b/ReactiveCocoa/UIKit/iOS/UIImpact\342\200\213Feedback\342\200\213Generator.swift" @@ -0,0 +1,12 @@ +import ReactiveSwift +import UIKit + +@available(iOS 10.0, *) +extension Reactive where Base: UIImpactFeedbackGenerator { + /// Triggers the feedback. + public var impactOccurred: BindingTarget<()> { + return makeBindingTarget { generator, _ in + generator.impactOccurred() + } + } +} diff --git "a/ReactiveCocoa/UIKit/iOS/UINotification\342\200\213Feedback\342\200\213Generator.swift" "b/ReactiveCocoa/UIKit/iOS/UINotification\342\200\213Feedback\342\200\213Generator.swift" new file mode 100644 index 0000000000..dd4d328a2d --- /dev/null +++ "b/ReactiveCocoa/UIKit/iOS/UINotification\342\200\213Feedback\342\200\213Generator.swift" @@ -0,0 +1,10 @@ +import ReactiveSwift +import UIKit + +@available(iOS 10.0, *) +extension Reactive where Base: UINotificationFeedbackGenerator { + /// Triggers the feedback. + public var notificationOccurred: BindingTarget { + return makeBindingTarget { $0.notificationOccurred($1) } + } +} diff --git "a/ReactiveCocoa/UIKit/iOS/UISelection\342\200\213Feedback\342\200\213Generator.swift" "b/ReactiveCocoa/UIKit/iOS/UISelection\342\200\213Feedback\342\200\213Generator.swift" new file mode 100644 index 0000000000..07ad9327a4 --- /dev/null +++ "b/ReactiveCocoa/UIKit/iOS/UISelection\342\200\213Feedback\342\200\213Generator.swift" @@ -0,0 +1,12 @@ +import ReactiveSwift +import UIKit + +@available(iOS 10.0, *) +extension Reactive where Base: UISelectionFeedbackGenerator { + /// Triggers the feedback. + public var selectionChanged: BindingTarget<()> { + return makeBindingTarget { generator, _ in + generator.selectionChanged() + } + } +} From d5c5948c4cf95b890737947df5d645e5f47a111d Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Wed, 5 Apr 2017 21:41:28 -0400 Subject: [PATCH 0770/1028] Fix CI by specifying valid, specific simulators --- .travis.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3a851c935b..837717d339 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,8 +25,7 @@ matrix: env: - XCODE_SDK=iphonesimulator - XCODE_ACTION="build-for-testing test-without-building" - - XCODE_DESTINATION="platform=iOS Simulator,id=31E6604A-19AA-4B68-B560-C33C584BC80D" # iPhone 6s, iOS 10.2 -# - XCODE_DESTINATION="platform=iOS Simulator,name=iPhone 6s,OS=10.2" + - XCODE_DESTINATION="platform=iOS Simulator,name=iPhone 6s,OS=10.2" # - XCODE_PLAYGROUND_TARGET="x86_64-apple-ios9.3" # - PLAYGROUND="ReactiveCocoa-iOS.playground" - xcode_scheme: ReactiveCocoa-tvOS @@ -40,7 +39,7 @@ matrix: env: - XCODE_SDK=watchsimulator - XCODE_ACTION=build - - XCODE_DESTINATION="platform=watchOS Simulator,name=Apple Watch - 38mm" + - XCODE_DESTINATION="platform=watchOS Simulator,name=Apple Watch - 38mm,OS=3.2" - script: - carthage build --no-skip-current --platform mac env: From 4b53fed00b8f05e9f3f9e26dfb55c8b3d624ede7 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Wed, 5 Apr 2017 21:49:16 -0400 Subject: [PATCH 0771/1028] Specify an unambiguous simulator --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 837717d339..38f46e0283 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,7 +39,7 @@ matrix: env: - XCODE_SDK=watchsimulator - XCODE_ACTION=build - - XCODE_DESTINATION="platform=watchOS Simulator,name=Apple Watch - 38mm,OS=3.2" + - XCODE_DESTINATION="platform=watchOS Simulator,name=Apple Watch - 38mm,OS=2.2" - script: - carthage build --no-skip-current --platform mac env: From 378f662fbb904375b92cdb888b04a223368fb77f Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sun, 2 Apr 2017 19:24:45 +0800 Subject: [PATCH 0772/1028] New test cases: ActionProxySpec and DelegateProxySpec. --- ReactiveCocoa.xcodeproj/project.pbxproj | 4 + .../AppKit/ActionProxySpec.swift | 296 ++++++++++++++++++ ReactiveCocoaTests/DelegateProxySpec.swift | 187 ++++++++++- 3 files changed, 482 insertions(+), 5 deletions(-) create mode 100644 ReactiveCocoaTests/AppKit/ActionProxySpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 9918d38d2e..ec9bc06745 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -63,6 +63,7 @@ 834DE1121E4120340099F4E5 /* NSSegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 834DE1111E4120340099F4E5 /* NSSegmentedControl.swift */; }; 834DE1141E4122910099F4E5 /* NSSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 834DE1131E4122910099F4E5 /* NSSlider.swift */; }; 8392D8FD1DB93E5E00504ED4 /* NSImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8392D8FC1DB93E5E00504ED4 /* NSImageView.swift */; }; + 9A0726F31E912B610081F3F7 /* ActionProxySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A0726F21E912B610081F3F7 /* ActionProxySpec.swift */; }; 9A1D05E01D93E99100ACF44C /* NSObject+Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */; }; 9A1D05E11D93E99100ACF44C /* NSObject+Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */; }; 9A1D05E21D93E99100ACF44C /* NSObject+Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */; }; @@ -371,6 +372,7 @@ 834DE1111E4120340099F4E5 /* NSSegmentedControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSSegmentedControl.swift; sourceTree = ""; }; 834DE1131E4122910099F4E5 /* NSSlider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSSlider.swift; sourceTree = ""; }; 8392D8FC1DB93E5E00504ED4 /* NSImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSImageView.swift; sourceTree = ""; }; + 9A0726F21E912B610081F3F7 /* ActionProxySpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionProxySpec.swift; sourceTree = ""; }; 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Association.swift"; sourceTree = ""; }; 9A1D05EC1D93E9F100ACF44C /* UIActivityIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorView.swift; sourceTree = ""; }; 9A1D05ED1D93E9F100ACF44C /* UIBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIBarButtonItem.swift; sourceTree = ""; }; @@ -680,6 +682,7 @@ 9ADE4A8C1DA6D94C005C2AC8 /* AppKit */ = { isa = PBXGroup; children = ( + 9A0726F21E912B610081F3F7 /* ActionProxySpec.swift */, 4ABEFE311DCFD05F0066A8C2 /* NSCollectionViewSpec.swift */, 9ADE4A8D1DA6D965005C2AC8 /* NSControlSpec.swift */, 4ABEFE2C1DCFD0180066A8C2 /* NSTableViewSpec.swift */, @@ -1344,6 +1347,7 @@ buildActionMask = 2147483647; files = ( 834DE1011E4109750099F4E5 /* NSImageViewSpec.swift in Sources */, + 9A0726F31E912B610081F3F7 /* ActionProxySpec.swift in Sources */, 9A6AAA0E1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */, 9A54A2111DDF5B4D001739B3 /* InterceptingPerformanceTests.swift in Sources */, 538DCB7D1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift in Sources */, diff --git a/ReactiveCocoaTests/AppKit/ActionProxySpec.swift b/ReactiveCocoaTests/AppKit/ActionProxySpec.swift new file mode 100644 index 0000000000..887f4e82b1 --- /dev/null +++ b/ReactiveCocoaTests/AppKit/ActionProxySpec.swift @@ -0,0 +1,296 @@ +import Quick +import Nimble +import enum Result.NoError +import ReactiveSwift +@testable import ReactiveCocoa + +private class Object: NSObject, ActionMessageSending { + dynamic var objectValue: AnyObject? = nil + + dynamic weak var target: AnyObject? + dynamic var action: Selector? + + deinit { + target = nil + action = nil + } +} + +private class Receiver: NSObject { + var counter = 0 + + func foo() { + counter += 1 + } +} + +class ActionProxySpec: QuickSpec { + override func spec() { + describe("DelegateProxy") { + var object: Object! + var proxy: ActionProxy! + + beforeEach { + object = Object() + proxy = object.reactive.proxy + } + + afterEach { + weak var weakObject = object + + object = nil + expect(weakObject).to(beNil()) + } + + func sendMessage() { + _ = object.action.map { object.target?.perform($0, with: nil) } + } + + it("should be automatically set as the object's delegate.") { + expect(object.target).to(beIdenticalTo(proxy)) + expect(object.action) == #selector(proxy.invoke(_:)) + } + + it("should not be erased when the delegate is set with a new one.") { + object.target = nil + object.action = nil + + expect(object.target).to(beIdenticalTo(proxy)) + expect(object.action) == #selector(proxy.invoke(_:)) + + expect(proxy.target).to(beNil()) + expect(proxy.action).to(beNil()) + + let counter = Receiver() + object.target = counter + object.action = #selector(counter.foo) + + expect(object.target).to(beIdenticalTo(proxy)) + expect(object.action) == #selector(proxy.invoke(_:)) + + expect(proxy.target).to(beIdenticalTo(counter)) + expect(proxy.action) == #selector(counter.foo) + } + + it("should complete its signals when the object deinitializes") { + var isCompleted = false + proxy.invoked.observeCompleted { isCompleted = true } + + expect(isCompleted) == false + + object = nil + expect(isCompleted) == true + } + + it("should interrupt the observers if the object has already deinitialized") { + object = nil + + var isInterrupted = false + proxy.invoked.observeInterrupted { isInterrupted = true } + + expect(isInterrupted) == true + } + + it("should emit a `value` event whenever an action message is sent.") { + var fooCount = 0 + proxy.invoked.observeValues { _ in fooCount += 1 } + + expect(fooCount) == 0 + + sendMessage() + expect(fooCount) == 1 + + sendMessage() + expect(fooCount) == 2 + } + + it("should pass through the action message to the forwardee.") { + let receiver = Receiver() + proxy.target = receiver + proxy.action = #selector(receiver.foo) + + var fooCount = 0 + proxy.invoked.observeValues { _ in fooCount += 1 } + + expect(fooCount) == 0 + expect(receiver.counter) == 0 + + sendMessage() + expect(fooCount) == 1 + expect(receiver.counter) == 1 + + sendMessage() + expect(fooCount) == 2 + expect(receiver.counter) == 2 + } + + describe("interoperability") { + var object: Object! + var proxy: ActionProxy! + var invocationCount = 0 + + beforeEach { + object = Object() + invocationCount = 0 + } + + func setProxy() { + proxy = object.reactive.proxy + proxy.invoked.observeValues { _ in invocationCount += 1 } + } + + func sendMessage() { + _ = object.action.map { object.target?.perform($0, with: nil) } + } + + it("should not affect instances sharing the same runtime subclass") { + _ = object.reactive.producer(forKeyPath: #keyPath(Object.objectValue)).start() + setProxy() + expect(object.target).to(beIdenticalTo(proxy)) + + // Another object without RAC swizzling. + let object2 = Object() + _ = object2.reactive.producer(forKeyPath: #keyPath(Object.objectValue)).start() + + expect(object.objcClass).to(beIdenticalTo(object2.objcClass)) + + let className = NSStringFromClass(object_getClass(object)) + expect(className).to(beginWith("NSKVONotifying_")) + expect(className).toNot(endWith("_RACSwift")) + + object2.target = object + object2.action = #selector(AnyObject.perform(_:with:)) + + expect(object2.target).to(beIdenticalTo(object)) + expect(object2.action) == #selector(AnyObject.perform(_:with:)) + } + + it("should be automatically set as the object's delegate even if it has already been isa-swizzled by KVO.") { + _ = object.reactive.producer(forKeyPath: #keyPath(Object.objectValue)).start() + expect(object.target).to(beNil()) + + setProxy() + expect(object.target).to(beIdenticalTo(proxy)) + + sendMessage() + expect(invocationCount) == 1 + + object.target = nil + expect(object.target).to(beIdenticalTo(proxy)) + + sendMessage() + expect(invocationCount) == 2 + } + + it("should be automatically set as the object's delegate even if it has already been isa-swizzled by RAC.") { + _ = object.reactive.trigger(for: #selector(getter: Object.objectValue)) + expect(object.target).to(beNil()) + + setProxy() + expect(object.target).to(beIdenticalTo(proxy)) + + sendMessage() + expect(invocationCount) == 1 + + object.target = nil + expect(object.target).to(beIdenticalTo(proxy)) + + sendMessage() + expect(invocationCount) == 2 + } + + it("should be automatically set as the object's delegate even if it has already been isa-swizzled by RAC for intercepting the delegate setter.") { + var counter = 0 + + object.reactive + .trigger(for: #selector(setter: Object.target)) + .observeValues { counter += 1 } + expect(object.target).to(beNil()) + + setProxy() + expect(object.target).to(beIdenticalTo(proxy)) + expect(counter) == 1 + + sendMessage() + expect(invocationCount) == 1 + + object.target = nil + expect(object.target).to(beIdenticalTo(proxy)) + expect(counter) == 1 + + sendMessage() + expect(invocationCount) == 2 + } + + it("should be automatically set as the object's delegate even if it is subsequently isa-swizzled by RAC for intercepting the delegate setter.") { + expect(object.target).to(beNil()) + + setProxy() + expect(object.target).to(beIdenticalTo(proxy)) + + sendMessage() + expect(invocationCount) == 1 + + var counter = 0 + + object.reactive + .trigger(for: #selector(setter: Object.target)) + .observeValues { counter += 1 } + + object.target = nil + expect(object.target).to(beIdenticalTo(proxy)) + expect(counter) == 1 + + sendMessage() + expect(invocationCount) == 2 + } + + it("should be automatically set as the object's delegate even if it has already been isa-swizzled by KVO for observing the delegate key path.") { + var counter = 0 + + object.reactive + .signal(forKeyPath: #keyPath(Object.target)) + .observeValues { _ in counter += 1 } + expect(object.target).to(beNil()) + + setProxy() + expect(object.target).to(beIdenticalTo(proxy)) + expect(counter) == 0 + + sendMessage() + expect(invocationCount) == 1 + + object.target = nil + expect(object.target).to(beIdenticalTo(proxy)) + expect(counter) == 0 + + sendMessage() + expect(invocationCount) == 2 + } + + it("should be automatically set as the object's delegate even if it is subsequently isa-swizzled by KVO for observing the delegate key path.") { + expect(object.target).to(beNil()) + + setProxy() + expect(object.target).to(beIdenticalTo(proxy)) + + sendMessage() + expect(invocationCount) == 1 + + var counter = 0 + + object.reactive + .signal(forKeyPath: #keyPath(Object.target)) + .observeValues { _ in counter += 1 } + + object.target = nil + expect(object.target).to(beIdenticalTo(proxy)) + expect(counter) == 1 + + sendMessage() + expect(invocationCount) == 2 + } + } + } + } +} diff --git a/ReactiveCocoaTests/DelegateProxySpec.swift b/ReactiveCocoaTests/DelegateProxySpec.swift index 6754f8d8c3..ad11f5ce0d 100644 --- a/ReactiveCocoaTests/DelegateProxySpec.swift +++ b/ReactiveCocoaTests/DelegateProxySpec.swift @@ -4,17 +4,17 @@ import enum Result.NoError import ReactiveSwift @testable import ReactiveCocoa -@objc protocol ObjectDelegate: NSObjectProtocol { +@objc private protocol ObjectDelegate: NSObjectProtocol { func foo() @objc optional func bar() @objc optional func nop() } -class Object: NSObject { +private class Object: NSObject { var delegateSetCount = 0 var delegateSelectors: [Selector] = [] - weak var delegate: ObjectDelegate? { + dynamic weak var delegate: ObjectDelegate? { didSet { delegateSetCount += 1 delegateSelectors = Array() @@ -39,7 +39,7 @@ class Object: NSObject { } } -class ObjectDelegateCounter: NSObject, ObjectDelegate { +private class ObjectDelegateCounter: NSObject, ObjectDelegate { var fooCounter = 0 var nopCounter = 0 @@ -52,7 +52,7 @@ class ObjectDelegateCounter: NSObject, ObjectDelegate { } } -class ObjectDelegateProxy: DelegateProxy, ObjectDelegate { +private class ObjectDelegateProxy: DelegateProxy, ObjectDelegate { func foo() { forwardee?.foo() } @@ -86,6 +86,17 @@ class DelegateProxySpec: QuickSpec { expect(object.delegate).to(beIdenticalTo(proxy)) } + it("should not be erased when the delegate is set with a new one.") { + object.delegate = nil + expect(object.delegate).to(beIdenticalTo(proxy)) + expect(proxy.forwardee).to(beNil()) + + let counter = ObjectDelegateCounter() + object.delegate = counter + expect(object.delegate).to(beIdenticalTo(proxy)) + expect(proxy.forwardee).to(beIdenticalTo(counter)) + } + it("should respond to the protocol requirement checks.") { expect(proxy.responds(to: #selector(ObjectDelegate.foo))) == true expect(proxy.responds(to: #selector(ObjectDelegate.bar))) == true @@ -211,6 +222,172 @@ class DelegateProxySpec: QuickSpec { expect(object.delegateSelectors) == [#selector(ObjectDelegate.foo), #selector(ObjectDelegate.bar)] expect(object.delegate).to(beIdenticalTo(proxy)) } + + describe("interoperability") { + var object: Object! + var proxy: DelegateProxy! + var fooCounter = 0 + + beforeEach { + object = Object() + fooCounter = 0 + } + + func setProxy() { + proxy = ObjectDelegateProxy.proxy(for: object, + setter: #selector(setter: object.delegate), + getter: #selector(getter: object.delegate)) + + let signal: Signal<(), NoError> = proxy.intercept(#selector(ObjectDelegate.foo)) + signal.observeValues { fooCounter += 1 } + } + + it("should not affect instances sharing the same runtime subclass") { + _ = object.reactive.producer(forKeyPath: #keyPath(Object.delegateSetCount)).start() + setProxy() + expect(object.delegate).to(beIdenticalTo(proxy)) + + // Another object without RAC swizzling. + let object2 = Object() + _ = object2.reactive.producer(forKeyPath: #keyPath(Object.delegateSetCount)).start() + + expect(object.objcClass).to(beIdenticalTo(object2.objcClass)) + + let className = NSStringFromClass(object_getClass(object)) + expect(className).to(beginWith("NSKVONotifying_")) + expect(className).toNot(endWith("_RACSwift")) + + let delegate = ObjectDelegateCounter() + object2.delegate = delegate + expect(object2.delegate).to(beIdenticalTo(delegate)) + } + + it("should be automatically set as the object's delegate even if it has already been isa-swizzled by KVO.") { + _ = object.reactive.producer(forKeyPath: #keyPath(Object.delegateSetCount)).start() + expect(object.delegate).to(beNil()) + + setProxy() + expect(object.delegate).to(beIdenticalTo(proxy)) + + object.delegate?.foo() + expect(fooCounter) == 1 + + object.delegate = nil + expect(object.delegate).to(beIdenticalTo(proxy)) + + object.delegate?.foo() + expect(fooCounter) == 2 + } + + it("should be automatically set as the object's delegate even if it has already been isa-swizzled by RAC.") { + _ = object.reactive.trigger(for: #selector(getter: Object.delegateSetCount)) + expect(object.delegate).to(beNil()) + + setProxy() + expect(object.delegate).to(beIdenticalTo(proxy)) + + object.delegate?.foo() + expect(fooCounter) == 1 + + object.delegate = nil + expect(object.delegate).to(beIdenticalTo(proxy)) + + object.delegate?.foo() + expect(fooCounter) == 2 + } + + it("should be automatically set as the object's delegate even if it has already been isa-swizzled by RAC for intercepting the delegate setter.") { + var counter = 0 + + object.reactive + .trigger(for: #selector(setter: Object.delegate)) + .observeValues { counter += 1 } + expect(object.delegate).to(beNil()) + + setProxy() + expect(object.delegate).to(beIdenticalTo(proxy)) + expect(counter) == 1 + + object.delegate?.foo() + expect(fooCounter) == 1 + + object.delegate = nil + expect(object.delegate).to(beIdenticalTo(proxy)) + expect(counter) == 1 + + object.delegate?.foo() + expect(fooCounter) == 2 + } + + it("should be automatically set as the object's delegate even if it is subsequently isa-swizzled by RAC for intercepting the delegate setter.") { + expect(object.delegate).to(beNil()) + + setProxy() + expect(object.delegate).to(beIdenticalTo(proxy)) + + object.delegate?.foo() + expect(fooCounter) == 1 + + var counter = 0 + + object.reactive + .trigger(for: #selector(setter: Object.delegate)) + .observeValues { counter += 1 } + + object.delegate = nil + expect(object.delegate).to(beIdenticalTo(proxy)) + expect(counter) == 1 + + object.delegate?.foo() + expect(fooCounter) == 2 + } + + it("should be automatically set as the object's delegate even if it has already been isa-swizzled by KVO for observing the delegate key path.") { + var counter = 0 + + object.reactive + .signal(forKeyPath: #keyPath(Object.delegate)) + .observeValues { _ in counter += 1 } + expect(object.delegate).to(beNil()) + + setProxy() + expect(object.delegate).to(beIdenticalTo(proxy)) + expect(counter) == 0 + + object.delegate?.foo() + expect(fooCounter) == 1 + + object.delegate = nil + expect(object.delegate).to(beIdenticalTo(proxy)) + expect(counter) == 0 + + object.delegate?.foo() + expect(fooCounter) == 2 + } + + it("should be automatically set as the object's delegate even if it is subsequently isa-swizzled by KVO for observing the delegate key path.") { + expect(object.delegate).to(beNil()) + + setProxy() + expect(object.delegate).to(beIdenticalTo(proxy)) + + object.delegate?.foo() + expect(fooCounter) == 1 + + var counter = 0 + + object.reactive + .signal(forKeyPath: #keyPath(Object.delegate)) + .observeValues { _ in counter += 1 } + + object.delegate = nil + expect(object.delegate).to(beIdenticalTo(proxy)) + expect(counter) == 1 + + object.delegate?.foo() + expect(fooCounter) == 2 + } + } } } } From e8bfbfc40fade886f0458d07dfd7c45f7ab9cf11 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sun, 2 Apr 2017 19:39:03 +0800 Subject: [PATCH 0773/1028] Fixed an interoperability issue with KVO in the swizzling logic. The original implementation had not taken account of the existence of other swizzling users. --- ReactiveCocoa/AppKit/ActionProxy.swift | 25 ++++++++++++++++++++----- ReactiveCocoa/DelegateProxy.swift | 10 ++++++++-- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/ReactiveCocoa/AppKit/ActionProxy.swift b/ReactiveCocoa/AppKit/ActionProxy.swift index ff35ed2c22..0ac7111ed0 100644 --- a/ReactiveCocoa/AppKit/ActionProxy.swift +++ b/ReactiveCocoa/AppKit/ActionProxy.swift @@ -43,22 +43,37 @@ extension Reactive where Base: NSObject, Base: ActionMessageSending { } let proxy = ActionProxy(owner: base, lifetime: lifetime) - base.associations.setValue(proxy, forKey: key) proxy.target = base.target proxy.action = base.action + // The proxy must be associated after it is set as the target, since + // `base` may be an isa-swizzled instance that is using the injected + // setters below. base.target = proxy base.action = #selector(proxy.invoke(_:)) + base.associations.setValue(proxy, forKey: key) let newTargetSetterImpl: @convention(block) (NSObject, AnyObject?) -> Void = { object, target in - let proxy = object.associations.value(forKey: key)! - proxy.target = target + if let proxy = object.associations.value(forKey: key) { + proxy.target = target + } else { + typealias Setter = @convention(c) (NSObject, Selector, AnyObject?) -> Void + let impl = class_getMethodImplementation(object.objcClass, #selector(setter: ActionMessageSending.target)) + let targetSetter = unsafeBitCast(impl, to: Setter.self) + targetSetter(object, #selector(setter: ActionMessageSending.target), target) + } } let newActionSetterImpl: @convention(block) (NSObject, Selector?) -> Void = { object, selector in - let proxy = object.associations.value(forKey: key)! - proxy.action = selector + if let proxy = object.associations.value(forKey: key) { + proxy.action = selector + } else { + typealias Setter = @convention(c) (NSObject, Selector, Selector?) -> Void + let impl = class_getMethodImplementation(object.objcClass, #selector(setter: ActionMessageSending.action)) + let actionSetter = unsafeBitCast(impl, to: Setter.self) + actionSetter(object, #selector(setter: ActionMessageSending.action), selector) + } } // Swizzle the instance only after setting up the proxy. diff --git a/ReactiveCocoa/DelegateProxy.swift b/ReactiveCocoa/DelegateProxy.swift index 796ac288c5..d6f8795ebd 100644 --- a/ReactiveCocoa/DelegateProxy.swift +++ b/ReactiveCocoa/DelegateProxy.swift @@ -73,8 +73,14 @@ extension DelegateProxy { } let newSetterImpl: @convention(block) (NSObject, AnyObject?) -> Void = { object, delegate in - let proxy = object.associations.value(forKey: key)! - proxy.forwardee = (delegate as! Delegate?) + if let proxy = object.associations.value(forKey: key) { + proxy.forwardee = (delegate as! Delegate?) + } else { + typealias Setter = @convention(c) (NSObject, Selector, AnyObject?) -> Void + let impl = class_getMethodImplementation(object.objcClass, setter) + let delegateSetter = unsafeBitCast(impl, to: Setter.self) + delegateSetter(object, setter, delegate) + } } // Hide the original setter, and redirect subsequent delegate assignment From b774210cba5c0579693c301ae95f5aa2fea7bbfa Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sun, 2 Apr 2017 20:45:05 +0800 Subject: [PATCH 0774/1028] Identify delegate proxies by the setter selector. --- ReactiveCocoa/DelegateProxy.swift | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/ReactiveCocoa/DelegateProxy.swift b/ReactiveCocoa/DelegateProxy.swift index d6f8795ebd..ed0d1ace88 100644 --- a/ReactiveCocoa/DelegateProxy.swift +++ b/ReactiveCocoa/DelegateProxy.swift @@ -44,6 +44,7 @@ internal class DelegateProxy: NSObject { } private let hasSwizzledKey = AssociationKey(default: false) +private let proxyMapKey = AssociationKey<[(Selector, AnyObject)]>(default: []) extension DelegateProxy { // FIXME: This is a workaround to a compiler issue, where any use of `Self` @@ -53,27 +54,27 @@ extension DelegateProxy { internal static func proxy>( for instance: NSObject, setter: Selector, - getter: Selector, - _ key: StaticString = #function + getter: Selector ) -> P { - return _proxy(for: instance, setter: setter, getter: getter, key) as! P + return _proxy(for: instance, setter: setter, getter: getter) as! P } private static func _proxy( for instance: NSObject, setter: Selector, - getter: Selector, - _ key: StaticString = #function + getter: Selector ) -> AnyObject { - let key = AssociationKey?>(key) - return instance.synchronized { - if let proxy = instance.associations.value(forKey: key) { + let proxyMap = instance.associations.value(forKey: proxyMapKey) + + if let proxy = proxyMap.first(where: { $0.0 == setter })?.1 { return proxy } let newSetterImpl: @convention(block) (NSObject, AnyObject?) -> Void = { object, delegate in - if let proxy = object.associations.value(forKey: key) { + let proxyMap = object.associations.value(forKey: proxyMapKey) + + if let proxy = proxyMap.first(where: { $0.0 == setter })?.1 as! DelegateProxy? { proxy.forwardee = (delegate as! Delegate?) } else { typealias Setter = @convention(c) (NSObject, Selector, AnyObject?) -> Void @@ -103,7 +104,7 @@ extension DelegateProxy { unsafeBitCast(originalSetterImpl, to: Setter.self)(instance, setter, proxy) } - instance.associations.setValue(proxy, forKey: key) + instance.associations.setValue(proxyMap + [(setter, proxy)], forKey: proxyMapKey) // `proxy.forwardee` would invoke the original setter regardless of // `original` being `nil` or not. From d3b850e3c58302f796627d9ef38b0681e910f39b Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Thu, 6 Apr 2017 14:22:13 +0800 Subject: [PATCH 0775/1028] Refined the DelegateProxy implementation. --- ReactiveCocoa/DelegateProxy.swift | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/ReactiveCocoa/DelegateProxy.swift b/ReactiveCocoa/DelegateProxy.swift index ed0d1ace88..804dbcda15 100644 --- a/ReactiveCocoa/DelegateProxy.swift +++ b/ReactiveCocoa/DelegateProxy.swift @@ -71,16 +71,19 @@ extension DelegateProxy { return proxy } + let invokeOriginalSetter: @convention(c) (NSObject, Selector, AnyObject?) -> Void = { object, selector, delegate in + typealias Setter = @convention(c) (NSObject, Selector, AnyObject?) -> Void + let impl = class_getMethodImplementation(object.objcClass, selector) + unsafeBitCast(impl, to: Setter.self)(object, selector, delegate) + } + let newSetterImpl: @convention(block) (NSObject, AnyObject?) -> Void = { object, delegate in let proxyMap = object.associations.value(forKey: proxyMapKey) if let proxy = proxyMap.first(where: { $0.0 == setter })?.1 as! DelegateProxy? { proxy.forwardee = (delegate as! Delegate?) } else { - typealias Setter = @convention(c) (NSObject, Selector, AnyObject?) -> Void - let impl = class_getMethodImplementation(object.objcClass, setter) - let delegateSetter = unsafeBitCast(impl, to: Setter.self) - delegateSetter(object, setter, delegate) + invokeOriginalSetter(object, setter, delegate) } } @@ -88,29 +91,27 @@ extension DelegateProxy { // to the proxy. instance.swizzle((setter, newSetterImpl), key: hasSwizzledKey) - // Set the proxy as the delegate. - let realClass: AnyClass = class_getSuperclass(object_getClass(instance)) - let originalSetterImpl: IMP = class_getMethodImplementation(realClass, setter) - let getterImpl: IMP = class_getMethodImplementation(realClass, getter) - - typealias Setter = @convention(c) (NSObject, Selector, AnyObject?) -> Void - typealias Getter = @convention(c) (NSObject, Selector) -> AnyObject? - // As Objective-C classes may cache the information of their delegate at // the time the delegates are set, the information has to be "flushed" // whenever the proxy forwardee is replaced or a selector is intercepted. let proxy = self.init(lifetime: instance.reactive.lifetime) { [weak instance] proxy in guard let instance = instance else { return } - unsafeBitCast(originalSetterImpl, to: Setter.self)(instance, setter, proxy) + invokeOriginalSetter(instance, setter, proxy) } - instance.associations.setValue(proxyMap + [(setter, proxy)], forKey: proxyMapKey) + typealias Getter = @convention(c) (NSObject, Selector) -> AnyObject? + let getterImpl: IMP = class_getMethodImplementation(object_getClass(instance), getter) + let original = unsafeBitCast(getterImpl, to: Getter.self)(instance, getter) as! Delegate? // `proxy.forwardee` would invoke the original setter regardless of // `original` being `nil` or not. - let original = unsafeBitCast(getterImpl, to: Getter.self)(instance, getter) as! Delegate? proxy.forwardee = original + // The proxy must be associated after it is set as the target, since + // `base` may be an isa-swizzled instance that is using the injected + // setters above. + instance.associations.setValue(proxyMap + [(setter, proxy)], forKey: proxyMapKey) + return proxy } } From 434227c25add109440397e8acf1442ab18d1d063 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sun, 2 Apr 2017 21:26:23 +0800 Subject: [PATCH 0776/1028] Fixed an ActionProxy crash when `NSApp` is not present. --- ReactiveCocoa/AppKit/ActionProxy.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoa/AppKit/ActionProxy.swift b/ReactiveCocoa/AppKit/ActionProxy.swift index 0ac7111ed0..594b80af01 100644 --- a/ReactiveCocoa/AppKit/ActionProxy.swift +++ b/ReactiveCocoa/AppKit/ActionProxy.swift @@ -2,7 +2,7 @@ import AppKit import ReactiveSwift import enum Result.NoError -internal final class ActionProxy { +internal final class ActionProxy: NSObject { internal weak var target: AnyObject? internal var action: Selector? internal let invoked: Signal @@ -19,7 +19,11 @@ internal final class ActionProxy { // In AppKit, action messages always have only one parameter. @objc func invoke(_ sender: Any?) { if let action = action { - NSApp.sendAction(action, to: target, from: sender) + if let app = NSApp { + app.sendAction(action, to: target, from: sender) + } else { + _ = target?.perform(action, with: sender) + } } observer.send(value: owner) From a925d854aa60451b369dbccefd3b6291b0188550 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sun, 2 Apr 2017 21:45:25 +0800 Subject: [PATCH 0777/1028] Disabled four ActionProxy/DelegateProxy interop test cases. --- ReactiveCocoaTests/AppKit/ActionProxySpec.swift | 4 ++-- ReactiveCocoaTests/DelegateProxySpec.swift | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ReactiveCocoaTests/AppKit/ActionProxySpec.swift b/ReactiveCocoaTests/AppKit/ActionProxySpec.swift index 887f4e82b1..4ec972bf97 100644 --- a/ReactiveCocoaTests/AppKit/ActionProxySpec.swift +++ b/ReactiveCocoaTests/AppKit/ActionProxySpec.swift @@ -199,7 +199,7 @@ class ActionProxySpec: QuickSpec { expect(invocationCount) == 2 } - it("should be automatically set as the object's delegate even if it has already been isa-swizzled by RAC for intercepting the delegate setter.") { + xit("should be automatically set as the object's delegate even if it has already been isa-swizzled by RAC for intercepting the delegate setter.") { var counter = 0 object.reactive @@ -268,7 +268,7 @@ class ActionProxySpec: QuickSpec { expect(invocationCount) == 2 } - it("should be automatically set as the object's delegate even if it is subsequently isa-swizzled by KVO for observing the delegate key path.") { + xit("should be automatically set as the object's delegate even if it is subsequently isa-swizzled by KVO for observing the delegate key path.") { expect(object.target).to(beNil()) setProxy() diff --git a/ReactiveCocoaTests/DelegateProxySpec.swift b/ReactiveCocoaTests/DelegateProxySpec.swift index ad11f5ce0d..43d601faeb 100644 --- a/ReactiveCocoaTests/DelegateProxySpec.swift +++ b/ReactiveCocoaTests/DelegateProxySpec.swift @@ -296,7 +296,7 @@ class DelegateProxySpec: QuickSpec { expect(fooCounter) == 2 } - it("should be automatically set as the object's delegate even if it has already been isa-swizzled by RAC for intercepting the delegate setter.") { + xit("should be automatically set as the object's delegate even if it has already been isa-swizzled by RAC for intercepting the delegate setter.") { var counter = 0 object.reactive @@ -365,7 +365,7 @@ class DelegateProxySpec: QuickSpec { expect(fooCounter) == 2 } - it("should be automatically set as the object's delegate even if it is subsequently isa-swizzled by KVO for observing the delegate key path.") { + xit("should be automatically set as the object's delegate even if it is subsequently isa-swizzled by KVO for observing the delegate key path.") { expect(object.delegate).to(beNil()) setProxy() From af1d2366a2919250317e87be308cfcd80285d9b5 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Fri, 7 Apr 2017 00:29:56 +0800 Subject: [PATCH 0778/1028] Capture the superclass instead of relying on `+class`. The class hierarchy up to the subclass returned by `swizzleClass` is immutable. So instead of `+class`, the superclass can be obtained upfront. --- ReactiveCocoa/AppKit/ActionProxy.swift | 19 ++++++++++--------- ReactiveCocoa/DelegateProxy.swift | 10 ++++++---- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/ReactiveCocoa/AppKit/ActionProxy.swift b/ReactiveCocoa/AppKit/ActionProxy.swift index 594b80af01..18ad836387 100644 --- a/ReactiveCocoa/AppKit/ActionProxy.swift +++ b/ReactiveCocoa/AppKit/ActionProxy.swift @@ -46,8 +46,9 @@ extension Reactive where Base: NSObject, Base: ActionMessageSending { return proxy } - let proxy = ActionProxy(owner: base, lifetime: lifetime) + let superclass: AnyClass = class_getSuperclass(swizzleClass(base)) + let proxy = ActionProxy(owner: base, lifetime: lifetime) proxy.target = base.target proxy.action = base.action @@ -63,20 +64,20 @@ extension Reactive where Base: NSObject, Base: ActionMessageSending { proxy.target = target } else { typealias Setter = @convention(c) (NSObject, Selector, AnyObject?) -> Void - let impl = class_getMethodImplementation(object.objcClass, #selector(setter: ActionMessageSending.target)) - let targetSetter = unsafeBitCast(impl, to: Setter.self) - targetSetter(object, #selector(setter: ActionMessageSending.target), target) + let selector = #selector(setter: ActionMessageSending.target) + let impl = class_getMethodImplementation(superclass, selector) + unsafeBitCast(impl, to: Setter.self)(object, selector, target) } } - let newActionSetterImpl: @convention(block) (NSObject, Selector?) -> Void = { object, selector in + let newActionSetterImpl: @convention(block) (NSObject, Selector?) -> Void = { object, action in if let proxy = object.associations.value(forKey: key) { - proxy.action = selector + proxy.action = action } else { typealias Setter = @convention(c) (NSObject, Selector, Selector?) -> Void - let impl = class_getMethodImplementation(object.objcClass, #selector(setter: ActionMessageSending.action)) - let actionSetter = unsafeBitCast(impl, to: Setter.self) - actionSetter(object, #selector(setter: ActionMessageSending.action), selector) + let selector = #selector(setter: ActionMessageSending.action) + let impl = class_getMethodImplementation(superclass, selector) + unsafeBitCast(impl, to: Setter.self)(object, selector, action) } } diff --git a/ReactiveCocoa/DelegateProxy.swift b/ReactiveCocoa/DelegateProxy.swift index 804dbcda15..d522da731c 100644 --- a/ReactiveCocoa/DelegateProxy.swift +++ b/ReactiveCocoa/DelegateProxy.swift @@ -71,9 +71,11 @@ extension DelegateProxy { return proxy } - let invokeOriginalSetter: @convention(c) (NSObject, Selector, AnyObject?) -> Void = { object, selector, delegate in + let superclass: AnyClass = class_getSuperclass(swizzleClass(instance)) + + let invokeSuperSetter: @convention(c) (NSObject, AnyClass, Selector, AnyObject?) -> Void = { object, superclass, selector, delegate in typealias Setter = @convention(c) (NSObject, Selector, AnyObject?) -> Void - let impl = class_getMethodImplementation(object.objcClass, selector) + let impl = class_getMethodImplementation(superclass, selector) unsafeBitCast(impl, to: Setter.self)(object, selector, delegate) } @@ -83,7 +85,7 @@ extension DelegateProxy { if let proxy = proxyMap.first(where: { $0.0 == setter })?.1 as! DelegateProxy? { proxy.forwardee = (delegate as! Delegate?) } else { - invokeOriginalSetter(object, setter, delegate) + invokeSuperSetter(object, superclass, setter, delegate) } } @@ -96,7 +98,7 @@ extension DelegateProxy { // whenever the proxy forwardee is replaced or a selector is intercepted. let proxy = self.init(lifetime: instance.reactive.lifetime) { [weak instance] proxy in guard let instance = instance else { return } - invokeOriginalSetter(instance, setter, proxy) + invokeSuperSetter(instance, superclass, setter, proxy) } typealias Getter = @convention(c) (NSObject, Selector) -> AnyObject? From 7b0cddbeb638247a86192ab5015567054e1351c4 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Fri, 7 Apr 2017 16:15:52 +0800 Subject: [PATCH 0779/1028] Revert "Disabled four ActionProxy/DelegateProxy interop test cases." This reverts commit a925d854aa60451b369dbccefd3b6291b0188550. --- ReactiveCocoaTests/AppKit/ActionProxySpec.swift | 4 ++-- ReactiveCocoaTests/DelegateProxySpec.swift | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ReactiveCocoaTests/AppKit/ActionProxySpec.swift b/ReactiveCocoaTests/AppKit/ActionProxySpec.swift index 4ec972bf97..887f4e82b1 100644 --- a/ReactiveCocoaTests/AppKit/ActionProxySpec.swift +++ b/ReactiveCocoaTests/AppKit/ActionProxySpec.swift @@ -199,7 +199,7 @@ class ActionProxySpec: QuickSpec { expect(invocationCount) == 2 } - xit("should be automatically set as the object's delegate even if it has already been isa-swizzled by RAC for intercepting the delegate setter.") { + it("should be automatically set as the object's delegate even if it has already been isa-swizzled by RAC for intercepting the delegate setter.") { var counter = 0 object.reactive @@ -268,7 +268,7 @@ class ActionProxySpec: QuickSpec { expect(invocationCount) == 2 } - xit("should be automatically set as the object's delegate even if it is subsequently isa-swizzled by KVO for observing the delegate key path.") { + it("should be automatically set as the object's delegate even if it is subsequently isa-swizzled by KVO for observing the delegate key path.") { expect(object.target).to(beNil()) setProxy() diff --git a/ReactiveCocoaTests/DelegateProxySpec.swift b/ReactiveCocoaTests/DelegateProxySpec.swift index 43d601faeb..ad11f5ce0d 100644 --- a/ReactiveCocoaTests/DelegateProxySpec.swift +++ b/ReactiveCocoaTests/DelegateProxySpec.swift @@ -296,7 +296,7 @@ class DelegateProxySpec: QuickSpec { expect(fooCounter) == 2 } - xit("should be automatically set as the object's delegate even if it has already been isa-swizzled by RAC for intercepting the delegate setter.") { + it("should be automatically set as the object's delegate even if it has already been isa-swizzled by RAC for intercepting the delegate setter.") { var counter = 0 object.reactive @@ -365,7 +365,7 @@ class DelegateProxySpec: QuickSpec { expect(fooCounter) == 2 } - xit("should be automatically set as the object's delegate even if it is subsequently isa-swizzled by KVO for observing the delegate key path.") { + it("should be automatically set as the object's delegate even if it is subsequently isa-swizzled by KVO for observing the delegate key path.") { expect(object.delegate).to(beNil()) setProxy() From bb670fe0f1504d39fdca52b1a4ae1c186209ba5d Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Fri, 7 Apr 2017 16:38:57 +0800 Subject: [PATCH 0780/1028] Resolved a couple of interoperability issues of RAC method swizzling. Two boundary cases were resolved: proxying-then-KVO and interception-then-proxying. A circular invocation bug was also fixed, which would happen on RAC-intercepted, KVO-swizzled selectors when KVO creates its subclass on top of the RAC runtime subclass. Preconditions were also added to trap upon unsupported swizzling combinations. --- ReactiveCocoa/AppKit/ActionProxy.swift | 23 +++++----- ReactiveCocoa/NSObject+Intercepting.swift | 42 ++++++++++++++++--- ReactiveCocoa/ObjC+RuntimeSubclassing.swift | 8 +++- .../AppKit/ActionProxySpec.swift | 9 +++- ReactiveCocoaTests/DelegateProxySpec.swift | 7 +++- 5 files changed, 69 insertions(+), 20 deletions(-) diff --git a/ReactiveCocoa/AppKit/ActionProxy.swift b/ReactiveCocoa/AppKit/ActionProxy.swift index 18ad836387..5e445a03bb 100644 --- a/ReactiveCocoa/AppKit/ActionProxy.swift +++ b/ReactiveCocoa/AppKit/ActionProxy.swift @@ -55,18 +55,23 @@ extension Reactive where Base: NSObject, Base: ActionMessageSending { // The proxy must be associated after it is set as the target, since // `base` may be an isa-swizzled instance that is using the injected // setters below. - base.target = proxy - base.action = #selector(proxy.invoke(_:)) + typealias TargetSetter = @convention(c) (NSObject, Selector, AnyObject?) -> Void + typealias ActionSetter = @convention(c) (NSObject, Selector, Selector?) -> Void + + let setTargetImpl = class_getMethodImplementation(superclass, #selector(setter: base.target)) + unsafeBitCast(setTargetImpl, to: TargetSetter.self)(base, #selector(setter: base.target), proxy) + + let setActionImpl = class_getMethodImplementation(superclass, #selector(setter: base.action)) + unsafeBitCast(setActionImpl, to: ActionSetter.self)(base, #selector(setter: base.action), #selector(proxy.invoke(_:))) + base.associations.setValue(proxy, forKey: key) let newTargetSetterImpl: @convention(block) (NSObject, AnyObject?) -> Void = { object, target in if let proxy = object.associations.value(forKey: key) { proxy.target = target } else { - typealias Setter = @convention(c) (NSObject, Selector, AnyObject?) -> Void - let selector = #selector(setter: ActionMessageSending.target) - let impl = class_getMethodImplementation(superclass, selector) - unsafeBitCast(impl, to: Setter.self)(object, selector, target) + let impl = class_getMethodImplementation(superclass, #selector(setter: self.base.target)) + unsafeBitCast(impl, to: TargetSetter.self)(object, #selector(setter: self.base.target), target) } } @@ -74,10 +79,8 @@ extension Reactive where Base: NSObject, Base: ActionMessageSending { if let proxy = object.associations.value(forKey: key) { proxy.action = action } else { - typealias Setter = @convention(c) (NSObject, Selector, Selector?) -> Void - let selector = #selector(setter: ActionMessageSending.action) - let impl = class_getMethodImplementation(superclass, selector) - unsafeBitCast(impl, to: Setter.self)(object, selector, action) + let impl = class_getMethodImplementation(superclass, #selector(setter: self.base.action)) + unsafeBitCast(impl, to: ActionSetter.self)(object, #selector(setter: self.base.action), action) } } diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 41189e73f9..4e8b3a61b0 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -117,7 +117,8 @@ extension NSObject { .flatMap { $0 != _rac_objc_msgForward ? $0 : nil } if let impl = immediateImpl { - class_addMethod(subclass, interopAlias, impl, typeEncoding) + let succeeds = class_addMethod(subclass, interopAlias, impl, typeEncoding) + precondition(succeeds, "RAC attempts to swizzle a selector that has message forwarding enabled with a runtime injected implementation. This is unsupported in the current version.") } } } @@ -127,7 +128,7 @@ extension NSObject { // Start forwarding the messages of the selector. _ = class_replaceMethod(subclass, selector, _rac_objc_msgForward, typeEncoding) - + return state.signal } } @@ -166,10 +167,39 @@ private func enableMessageForwarding(_ realClass: AnyClass, _ selectorCache: Sel // // However, the IMP cache would be thrashed due to the swapping. - let interopImpl = class_getMethodImplementation(realClass, interopAlias) - let previousImpl = class_replaceMethod(realClass, selector, interopImpl, typeEncoding) - invocation.invoke() - _ = class_replaceMethod(realClass, selector, previousImpl, typeEncoding) + let topLevelClass: AnyClass = object_getClass(objectRef.takeUnretainedValue()) + + // The locking below prevents RAC swizzling attempts from intervening the + // invocation. + // + // Given the implementation of `swizzleClass`, `topLevelClass` can only be: + // (1) the same as `realClass`; or (2) a subclass of `realClass`. In other + // words, this would deadlock only if the locking order is not followed in + // other nested locking scenarios of these metaclasses at compile time. + + synchronized(topLevelClass) { + func swizzle() { + let interopImpl = class_getMethodImplementation(topLevelClass, interopAlias) + + let previousImpl = class_replaceMethod(topLevelClass, selector, interopImpl, typeEncoding) + invocation.invoke() + + _ = class_replaceMethod(topLevelClass, selector, previousImpl, typeEncoding) + } + + if topLevelClass != realClass { + synchronized(realClass) { + // In addition to swapping in the implementation, the message + // forwarding needs to be temporarily disabled to prevent circular + // invocation. + _ = class_replaceMethod(realClass, selector, nil, typeEncoding) + swizzle() + _ = class_replaceMethod(realClass, selector, _rac_objc_msgForward, typeEncoding) + } + } else { + swizzle() + } + } return } diff --git a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift index dead0d2cd6..a870724bfd 100644 --- a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift +++ b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift @@ -33,7 +33,13 @@ extension NSObject { let method = class_getInstanceMethod(subclass, selector) let typeEncoding = method_getTypeEncoding(method)! - class_replaceMethod(subclass, selector, imp_implementationWithBlock(body), typeEncoding) + if method_getImplementation(method) == _rac_objc_msgForward { + let succeeds = class_addMethod(subclass, selector.interopAlias, imp_implementationWithBlock(body), typeEncoding) + precondition(succeeds, "RAC attempts to swizzle a selector that has message forwarding enabled with a runtime injected implementation. This is unsupported in the current version.") + } else { + let succeeds = class_addMethod(subclass, selector, imp_implementationWithBlock(body), typeEncoding) + precondition(succeeds, "RAC attempts to swizzle a selector that has already a runtime injected implementation. This is unsupported in the current version.") + } } } } diff --git a/ReactiveCocoaTests/AppKit/ActionProxySpec.swift b/ReactiveCocoaTests/AppKit/ActionProxySpec.swift index 887f4e82b1..930853d8ff 100644 --- a/ReactiveCocoaTests/AppKit/ActionProxySpec.swift +++ b/ReactiveCocoaTests/AppKit/ActionProxySpec.swift @@ -26,7 +26,7 @@ private class Receiver: NSObject { class ActionProxySpec: QuickSpec { override func spec() { - describe("DelegateProxy") { + describe("ActionProxy") { var object: Object! var proxy: ActionProxy! @@ -208,8 +208,11 @@ class ActionProxySpec: QuickSpec { expect(object.target).to(beNil()) setProxy() + + // The assignment of the proxy should not be captured by the method + // interception logic. expect(object.target).to(beIdenticalTo(proxy)) - expect(counter) == 1 + expect(counter) == 0 sendMessage() expect(invocationCount) == 1 @@ -254,6 +257,8 @@ class ActionProxySpec: QuickSpec { expect(object.target).to(beNil()) setProxy() + + // The assignment of the proxy should not be captured by KVO. expect(object.target).to(beIdenticalTo(proxy)) expect(counter) == 0 diff --git a/ReactiveCocoaTests/DelegateProxySpec.swift b/ReactiveCocoaTests/DelegateProxySpec.swift index ad11f5ce0d..e608bda27a 100644 --- a/ReactiveCocoaTests/DelegateProxySpec.swift +++ b/ReactiveCocoaTests/DelegateProxySpec.swift @@ -305,8 +305,11 @@ class DelegateProxySpec: QuickSpec { expect(object.delegate).to(beNil()) setProxy() + + // The assignment of the proxy should not be captured by the method + // interception logic. expect(object.delegate).to(beIdenticalTo(proxy)) - expect(counter) == 1 + expect(counter) == 0 object.delegate?.foo() expect(fooCounter) == 1 @@ -351,6 +354,8 @@ class DelegateProxySpec: QuickSpec { expect(object.delegate).to(beNil()) setProxy() + + // The assignment of the proxy should not be captured by KVO. expect(object.delegate).to(beIdenticalTo(proxy)) expect(counter) == 0 From 78346b55202b4f688a0f846f4c6082912b7f23e7 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 8 Apr 2017 23:31:03 +0800 Subject: [PATCH 0781/1028] Use ObjC association directly for proxy storage. --- ReactiveCocoa/DelegateProxy.swift | 11 ++++------- ReactiveCocoa/ObjC+Selector.swift | 5 +++++ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/ReactiveCocoa/DelegateProxy.swift b/ReactiveCocoa/DelegateProxy.swift index d522da731c..8da519e14b 100644 --- a/ReactiveCocoa/DelegateProxy.swift +++ b/ReactiveCocoa/DelegateProxy.swift @@ -44,7 +44,6 @@ internal class DelegateProxy: NSObject { } private let hasSwizzledKey = AssociationKey(default: false) -private let proxyMapKey = AssociationKey<[(Selector, AnyObject)]>(default: []) extension DelegateProxy { // FIXME: This is a workaround to a compiler issue, where any use of `Self` @@ -65,9 +64,9 @@ extension DelegateProxy { getter: Selector ) -> AnyObject { return instance.synchronized { - let proxyMap = instance.associations.value(forKey: proxyMapKey) + let key = AssociationKey(setter.delegateProxyAlias) - if let proxy = proxyMap.first(where: { $0.0 == setter })?.1 { + if let proxy = instance.associations.value(forKey: key) { return proxy } @@ -80,9 +79,7 @@ extension DelegateProxy { } let newSetterImpl: @convention(block) (NSObject, AnyObject?) -> Void = { object, delegate in - let proxyMap = object.associations.value(forKey: proxyMapKey) - - if let proxy = proxyMap.first(where: { $0.0 == setter })?.1 as! DelegateProxy? { + if let proxy = object.associations.value(forKey: key) as! DelegateProxy? { proxy.forwardee = (delegate as! Delegate?) } else { invokeSuperSetter(object, superclass, setter, delegate) @@ -112,7 +109,7 @@ extension DelegateProxy { // The proxy must be associated after it is set as the target, since // `base` may be an isa-swizzled instance that is using the injected // setters above. - instance.associations.setValue(proxyMap + [(setter, proxy)], forKey: proxyMapKey) + instance.associations.setValue(proxy, forKey: key) return proxy } diff --git a/ReactiveCocoa/ObjC+Selector.swift b/ReactiveCocoa/ObjC+Selector.swift index a6dd4fe884..f2d126e682 100644 --- a/ReactiveCocoa/ObjC+Selector.swift +++ b/ReactiveCocoa/ObjC+Selector.swift @@ -17,6 +17,11 @@ extension Selector { return prefixing("rac1_") } + /// An alias of `self`, used for delegate proxies. + internal var delegateProxyAlias: Selector { + return prefixing("rac2_") + } + internal func prefixing(_ prefix: StaticString) -> Selector { let length = Int(strlen(utf8Start)) let prefixedLength = length + prefix.utf8CodeUnitCount From 58414f7cfc1b0e2f4feda9890e7ed32233245f35 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 8 Apr 2017 23:37:37 +0800 Subject: [PATCH 0782/1028] Updated a comment in ActionProxy. --- ReactiveCocoa/AppKit/ActionProxy.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ReactiveCocoa/AppKit/ActionProxy.swift b/ReactiveCocoa/AppKit/ActionProxy.swift index 5e445a03bb..12e65e05d5 100644 --- a/ReactiveCocoa/AppKit/ActionProxy.swift +++ b/ReactiveCocoa/AppKit/ActionProxy.swift @@ -52,9 +52,8 @@ extension Reactive where Base: NSObject, Base: ActionMessageSending { proxy.target = base.target proxy.action = base.action - // The proxy must be associated after it is set as the target, since - // `base` may be an isa-swizzled instance that is using the injected - // setters below. + // Set the proxy as the new delegate with all dynamic interception bypassed + // by directly invoking setters in the original class. typealias TargetSetter = @convention(c) (NSObject, Selector, AnyObject?) -> Void typealias ActionSetter = @convention(c) (NSObject, Selector, Selector?) -> Void From ce60a8656f67d5c12cbacbce98ac8a1a55e87f45 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Fri, 7 Apr 2017 21:35:23 +0800 Subject: [PATCH 0783/1028] Test cases for KVO on dependent keys. --- .../KeyValueObservingSpec.swift | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/ReactiveCocoaTests/KeyValueObservingSpec.swift b/ReactiveCocoaTests/KeyValueObservingSpec.swift index 0acc37b558..67c33550a4 100644 --- a/ReactiveCocoaTests/KeyValueObservingSpec.swift +++ b/ReactiveCocoaTests/KeyValueObservingSpec.swift @@ -172,6 +172,10 @@ fileprivate class KeyValueObservingSpecConfiguration: QuickConfiguration { func weakReferenceChanges(_ object: NSObject) -> SignalProducer { return observe(object, #keyPath(ObservableObject.weakTarget)) } + + func dependentKeyChanges(_ object: NSObject) -> SignalProducer { + return observe(object, #keyPath(ObservableObject.rac_value_plusOne)) + } } override class func configure(_ configuration: Configuration) { @@ -201,6 +205,26 @@ fileprivate class KeyValueObservingSpecConfiguration: QuickConfiguration { expect(values) == [0, 1, 1] } + it("should send new values for the dependent key path (even if the value remains unchanged)") { + let object = ObservableObject() + var values: [Int] = [] + + context.dependentKeyChanges(object).startWithValues { + values.append(($0 as! NSNumber).intValue) + } + + expect(values) == [] + + object.rac_value = 0 + expect(values) == [1] + + object.rac_value = 1 + expect(values) == [1, 2] + + object.rac_value = 1 + expect(values) == [1, 2, 2] + } + it("should not crash an Operation") { // Related issue: // https://github.com/ReactiveCocoa/ReactiveCocoa/issues/3329 @@ -561,6 +585,18 @@ private class ObservableObject: NSObject { dynamic var target: AnyObject? dynamic weak var weakTarget: AnyObject? + + dynamic var rac_value_plusOne: Int { + return rac_value + 1 + } + + override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set { + if key == "rac_value_plusOne" { + return Set([#keyPath(ObservableObject.rac_value)]) + } else { + return Set() + } + } } private class NestedObservableObject: NSObject { From e5b6f827062fcbeb4270e0d927d80250af756162 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sun, 9 Apr 2017 00:25:19 +0800 Subject: [PATCH 0784/1028] New KVO test case for #3443. The bug was fixed in #3439. It caused the KVO logic to observe the deallocation of object values of non-weak properties, causing various issues including but not limited to deadlocks and unexpected `nil`s. --- .../KeyValueObservingSpec.swift | 57 +++++++++++++++++-- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/ReactiveCocoaTests/KeyValueObservingSpec.swift b/ReactiveCocoaTests/KeyValueObservingSpec.swift index 67c33550a4..2a3ac4feb9 100644 --- a/ReactiveCocoaTests/KeyValueObservingSpec.swift +++ b/ReactiveCocoaTests/KeyValueObservingSpec.swift @@ -205,12 +205,61 @@ fileprivate class KeyValueObservingSpecConfiguration: QuickConfiguration { expect(values) == [0, 1, 1] } + it("should send new values for the dependent key path") { + // This variant wraps the setter invocations with an autoreleasepool, and + // intentionally avoids retaining the emitted value, so that a bug that + // emits `nil` inappropriately can be caught. + // + // Related: https://github.com/ReactiveCocoa/ReactiveCocoa/issues/3443#issuecomment-292721863 + // Fixed in https://github.com/ReactiveCocoa/ReactiveCocoa/pull/3439. + + let object = ObservableObject() + var expectedResults = [1, 2, 2] + var unexpectedResults: [NSDecimalNumber?] = [] + + var matches = true + + context.dependentKeyChanges(object).startWithValues { number in + let number = number as? NSDecimalNumber + + if number != NSDecimalNumber(value: expectedResults.removeFirst()) { + matches = false + unexpectedResults.append(number) + } + } + + expect(matches) == true + expect(unexpectedResults as NSArray) == [] + + autoreleasepool { + object.rac_value = 0 + } + + expect(matches) == true + expect(unexpectedResults as NSArray) == [] + + + autoreleasepool { + object.rac_value = 1 + } + + expect(matches) == true + expect(unexpectedResults as NSArray) == [] + + autoreleasepool { + object.rac_value = 1 + } + + expect(matches) == true + expect(unexpectedResults as NSArray) == [] + } + it("should send new values for the dependent key path (even if the value remains unchanged)") { let object = ObservableObject() - var values: [Int] = [] + var values: [NSDecimalNumber] = [] context.dependentKeyChanges(object).startWithValues { - values.append(($0 as! NSNumber).intValue) + values.append($0 as! NSDecimalNumber) } expect(values) == [] @@ -586,8 +635,8 @@ private class ObservableObject: NSObject { dynamic var target: AnyObject? dynamic weak var weakTarget: AnyObject? - dynamic var rac_value_plusOne: Int { - return rac_value + 1 + dynamic var rac_value_plusOne: NSDecimalNumber { + return NSDecimalNumber(value: rac_value + 1) } override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set { From 24af9360ae8894cf96eb31685dfa2e596db14229 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Wed, 5 Apr 2017 12:17:15 +0800 Subject: [PATCH 0785/1028] Continuous UITextField signals now react to all editing events. --- ReactiveCocoa/UIKit/UITextField.swift | 4 +-- .../UIKit/UITextFieldSpec.swift | 28 +++++++++++++------ 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/ReactiveCocoa/UIKit/UITextField.swift b/ReactiveCocoa/UIKit/UITextField.swift index 2701f441e1..b968227f51 100644 --- a/ReactiveCocoa/UIKit/UITextField.swift +++ b/ReactiveCocoa/UIKit/UITextField.swift @@ -20,7 +20,7 @@ extension Reactive where Base: UITextField { /// /// - note: To observe text values only when editing ends, see `textValues`. public var continuousTextValues: Signal { - return controlEvents(.editingChanged).map { $0.text } + return controlEvents(.allEditingEvents).map { $0.text } } /// Sets the attributed text of the text field. @@ -45,7 +45,7 @@ extension Reactive where Base: UITextField { /// /// - note: To observe attributed text values only when editing ends, see `attributedTextValues`. public var continuousAttributedTextValues: Signal { - return controlEvents(.editingChanged).map { $0.attributedText } + return controlEvents(.allEditingEvents).map { $0.attributedText } } /// Sets the secure text entry attribute on the text field. diff --git a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift index 428911d630..ff1eee0de0 100644 --- a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift @@ -37,15 +37,17 @@ class UITextFieldSpec: QuickSpec { } it("should emit user initiated changes to its text value continuously") { - textField.text = "Test" - var latestValue: String? textField.reactive.continuousTextValues.observeValues { text in latestValue = text } - textField.sendActions(for: .editingChanged) - expect(latestValue) == textField.text + for event in UIControlEvents.editingEvents { + textField.text = "Test \(event)" + + textField.sendActions(for: event) + expect(latestValue) == textField.text + } } it("should accept changes from bindings to its attributed text value") { @@ -77,15 +79,17 @@ class UITextFieldSpec: QuickSpec { } it("should emit user initiated changes to its attributed text value continuously") { - textField.attributedText = NSAttributedString(string: "Test") - var latestValue: NSAttributedString? textField.reactive.continuousAttributedTextValues.observeValues { attributedText in latestValue = attributedText } - - textField.sendActions(for: .editingChanged) - expect(latestValue?.string) == textField.attributedText?.string + + for event in UIControlEvents.editingEvents { + textField.attributedText = NSAttributedString(string: "Test \(event)") + + textField.sendActions(for: event) + expect(latestValue?.string) == textField.attributedText?.string + } } it("should accept changes from bindings to its secureTextEntry attribute") { @@ -111,3 +115,9 @@ class UITextFieldSpec: QuickSpec { } } } + +extension UIControlEvents { + fileprivate static var editingEvents: [UIControlEvents] { + return [.allEditingEvents, .editingDidBegin, .editingChanged, .editingDidEndOnExit, .editingDidEnd] + } +} From fdc02f188228666d56966fd4f82fb596f14576f1 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sun, 9 Apr 2017 01:36:34 +0800 Subject: [PATCH 0786/1028] 5.0.3 --- ReactiveCocoa.podspec | 2 +- ReactiveCocoa/Info.plist | 2 +- ReactiveCocoaTests/Info.plist | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 602029c302..9a00e4a7b5 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "5.0.2" + s.version = "5.0.3" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveCocoa/Info.plist b/ReactiveCocoa/Info.plist index ddc3040872..8614adfaeb 100644 --- a/ReactiveCocoa/Info.plist +++ b/ReactiveCocoa/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 5.0.2 + 5.0.3 CFBundleSignature ???? CFBundleVersion diff --git a/ReactiveCocoaTests/Info.plist b/ReactiveCocoaTests/Info.plist index b5296d04bb..a91394faff 100644 --- a/ReactiveCocoaTests/Info.plist +++ b/ReactiveCocoaTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 5.0.2 + 5.0.3 CFBundleSignature ???? CFBundleVersion From ce4f9c7ff7512d9496693a64f5c0d6eaba259da7 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Fri, 14 Apr 2017 17:46:10 +0800 Subject: [PATCH 0787/1028] Reentrance support in `CocoaTarget.invoke`. --- ReactiveCocoa/CocoaTarget.swift | 44 +++++++++++++++++-- ReactiveCocoa/UIKit/UIControl.swift | 4 +- ReactiveCocoa/UIKit/UIGestureRecognizer.swift | 4 +- .../UIKit/UITextFieldSpec.swift | 44 ++++++++++++++++++- 4 files changed, 87 insertions(+), 9 deletions(-) diff --git a/ReactiveCocoa/CocoaTarget.swift b/ReactiveCocoa/CocoaTarget.swift index f37b2f5620..1c50d06beb 100644 --- a/ReactiveCocoa/CocoaTarget.swift +++ b/ReactiveCocoa/CocoaTarget.swift @@ -4,16 +4,52 @@ import enum Result.NoError /// A target that accepts action messages. internal final class CocoaTarget: NSObject { + private enum State { + case idle + case sending + case invokeAfterSending + } + private let observer: Observer private let transform: (Any?) -> Value - + + private var state: State + internal init(_ observer: Observer, transform: @escaping (Any?) -> Value) { self.observer = observer self.transform = transform + self.state = .idle } - - @objc internal func sendNext(_ receiver: Any?) { - observer.send(value: transform(receiver)) + + /// Broadcast the action message to all observers. + /// + /// Reentrancy is supported, and the action message would be deferred until the + /// delivery of the current message has completed. Deferred messages would be + /// coalesced. + /// + /// - note: It should only be invoked on the main queue. + /// + /// - parameters: + /// - sender: The object which sends the action message. + @objc internal func invoke(_ sender: Any?) { + switch state { + case .idle: + state = .sending + observer.send(value: transform(sender)) + + while state == .invokeAfterSending { + state = .sending + observer.send(value: transform(sender)) + } + + state = .idle + + case .sending: + state = .invokeAfterSending + + case .invokeAfterSending: + break + } } } diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index b7d961795e..1cf29579a9 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -50,7 +50,7 @@ extension Reactive where Base: UIControl { return Signal { observer in let receiver = CocoaTarget(observer) { $0 as! Base } base.addTarget(receiver, - action: #selector(receiver.sendNext), + action: #selector(receiver.invoke), for: controlEvents) let disposable = lifetime.ended.observeCompleted(observer.sendCompleted) @@ -59,7 +59,7 @@ extension Reactive where Base: UIControl { disposable?.dispose() base?.removeTarget(receiver, - action: #selector(receiver.sendNext), + action: #selector(receiver.invoke), for: controlEvents) } } diff --git a/ReactiveCocoa/UIKit/UIGestureRecognizer.swift b/ReactiveCocoa/UIKit/UIGestureRecognizer.swift index 23a9470c10..9614e6fe14 100644 --- a/ReactiveCocoa/UIKit/UIGestureRecognizer.swift +++ b/ReactiveCocoa/UIKit/UIGestureRecognizer.swift @@ -11,13 +11,13 @@ extension Reactive where Base: UIGestureRecognizer { let receiver = CocoaTarget(observer) { gestureRecognizer in return gestureRecognizer as! Base } - base.addTarget(receiver, action: #selector(receiver.sendNext)) + base.addTarget(receiver, action: #selector(receiver.invoke)) let disposable = lifetime.ended.observeCompleted(observer.sendCompleted) return ActionDisposable { [weak base = self.base] in disposable?.dispose() - base?.removeTarget(receiver, action: #selector(receiver.sendNext)) + base?.removeTarget(receiver, action: #selector(receiver.invoke)) } } } diff --git a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift index ff1eee0de0..d51409e8de 100644 --- a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift @@ -21,7 +21,11 @@ class UITextFieldSpec: QuickSpec { autoreleasepool { textField = nil } - expect(_textField).to(beNil()) + + // For a unknown reason, the text field in the "should not deadlock" test case + // does not deallocate when the autorelease pool is drained, but only a while + // after. + expect(_textField).toEventually(beNil()) } it("should emit user initiated changes to its text value when the editing ends") { @@ -113,6 +117,44 @@ class UITextFieldSpec: QuickSpec { observer.send(value: UIColor.blue) expect(textField.textColor == UIColor.red) == false } + + it("should not deadlock when the text field is asked to resign first responder by any of its observers") { + UIView.setAnimationsEnabled(false) + defer { UIView.setAnimationsEnabled(true) } + + autoreleasepool { + let window = UIWindow(frame: .zero) + window.addSubview(textField) + + defer { + textField.removeFromSuperview() + expect(textField.superview).to(beNil()) + } + + expect(textField.becomeFirstResponder()) == true + expect(textField.isFirstResponder) == true + + var values: [String] = [] + + textField.reactive.continuousTextValues.observeValues { text in + values.append(text ?? "") + + if text == "2" { + textField.resignFirstResponder() + } + } + expect(values) == [] + + textField.text = "1" + textField.sendActions(for: .editingChanged) + expect(values) == ["1"] + + textField.text = "2" + textField.sendActions(for: .editingChanged) + expect(textField.isFirstResponder) == false + expect(values) == ["1", "2", "2"] + } + } } } From f7dd3766c5899a49479cedb366cc19f1ed3c9335 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Mon, 24 Apr 2017 23:27:47 +0800 Subject: [PATCH 0788/1028] Update .gitignore. [skip ci] --- .gitignore | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.gitignore b/.gitignore index d1ba8ca674..28f5624a0b 100644 --- a/.gitignore +++ b/.gitignore @@ -14,9 +14,18 @@ xcuserdata profile *.moved-aside PlaygroundUtility.remap +*.xctimeline +*.xcscmblueprint # SwiftPM .build +Packages # Carthage Carthage/Build + +# macOS +.DS_Store + +# Jazzy +docs From 8b4c180a5ff74388fa742ba45e95b5b318d03c52 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Tue, 25 Apr 2017 01:37:29 +0800 Subject: [PATCH 0789/1028] Fix iOS CI test, and add 32-bit iOS test. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 38f46e0283..42d1adbd3f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,7 @@ matrix: env: - XCODE_SDK=iphonesimulator - XCODE_ACTION="build-for-testing test-without-building" - - XCODE_DESTINATION="platform=iOS Simulator,name=iPhone 6s,OS=10.2" + - XCODE_DESTINATION="platform=iOS Simulator,name=iPhone 6s" # - XCODE_PLAYGROUND_TARGET="x86_64-apple-ios9.3" # - PLAYGROUND="ReactiveCocoa-iOS.playground" - xcode_scheme: ReactiveCocoa-tvOS From b608a5d6da7830cab727696723b934b213c630ee Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Mon, 24 Apr 2017 23:22:55 +0800 Subject: [PATCH 0790/1028] Fixed a bridging issue in `KeyboardChangeContext`. --- ReactiveCocoa/UIKit/iOS/UIKeyboard.swift | 3 ++- ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift | 12 ++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift b/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift index 4b57b96151..d50c4a4a26 100644 --- a/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift +++ b/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift @@ -19,7 +19,8 @@ public struct KeyboardChangeContext { /// The animation curve which the system keyboard will use to animate the /// change in its frame. public var animationCurve: UIViewAnimationCurve { - return base[UIKeyboardAnimationCurveUserInfoKey] as! UIViewAnimationCurve + let value = base[UIKeyboardAnimationCurveUserInfoKey] as! NSNumber + return UIViewAnimationCurve(rawValue: value.intValue)! } /// The duration in which the system keyboard expects to animate the change in diff --git a/ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift b/ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift index 35e0b0441c..1c6fdf59d9 100644 --- a/ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift @@ -15,13 +15,17 @@ class UIKeyboardSpec: QuickSpec { testCenter.reactive.keyboardChange .observeValues { context = $0 } - let dummyInfo: [AnyHashable: Any] = [ + var dummyInfo: [AnyHashable: Any] = [ UIKeyboardFrameBeginUserInfoKey: CGRect(x: 10, y: 10, width: 10, height: 10), UIKeyboardFrameEndUserInfoKey: CGRect(x: 20, y: 20, width: 20, height: 20), UIKeyboardAnimationDurationUserInfoKey: 1.0, - UIKeyboardAnimationCurveUserInfoKey: UIViewAnimationCurve.easeInOut + UIKeyboardAnimationCurveUserInfoKey: NSNumber(value: UIViewAnimationCurve.easeInOut.rawValue) ] + if #available(*, iOS 9.0) { + dummyInfo[UIKeyboardIsLocalUserInfoKey] = NSNumber(value: true) + } + testCenter.post(name: .UIKeyboardWillChangeFrame, object: nil, userInfo: dummyInfo) @@ -32,6 +36,10 @@ class UIKeyboardSpec: QuickSpec { expect(context?.endFrame) == CGRect(x: 20, y: 20, width: 20, height: 20) expect(context?.animationCurve) == .easeInOut expect(context?.animationDuration) == 1.0 + + if #available(*, iOS 9.0) { + expect(context?.isLocal) == true + } } } } From 96793fddc97539dba01cff5e7366c336bc0395c7 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Tue, 25 Apr 2017 23:18:03 +0800 Subject: [PATCH 0791/1028] Fixed a Swift 3.0 crash due to an errorous `Associations` overload signature. --- ReactiveCocoa/NSObject+Association.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa/NSObject+Association.swift b/ReactiveCocoa/NSObject+Association.swift index 421596bcd5..7740c47106 100644 --- a/ReactiveCocoa/NSObject+Association.swift +++ b/ReactiveCocoa/NSObject+Association.swift @@ -115,7 +115,7 @@ extension Associations { /// - parameters: /// - value: The value to be associated. /// - key: The key. - internal func setValue(_ value: Value?, forKey key: AssociationKey) { + internal func setValue(_ value: Value?, forKey key: AssociationKey) { objc_setAssociatedObject(base, key.address, value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } From ff5c12b0c3e000bade4a6209b45013a2514a8261 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Tue, 25 Apr 2017 23:24:21 +0800 Subject: [PATCH 0792/1028] Updated test cases to be compatible with Swift 3.0. NSNumber, NSValue and NSNull bridging was introduced since Swift 3.0.1. --- ReactiveCocoaTests/InterceptingSpec.swift | 64 +++++++++++++---------- ReactiveCocoaTests/SwizzlingSpec.swift | 4 +- 2 files changed, 37 insertions(+), 31 deletions(-) diff --git a/ReactiveCocoaTests/InterceptingSpec.swift b/ReactiveCocoaTests/InterceptingSpec.swift index 15e8b6dee7..b7a9cf05a3 100644 --- a/ReactiveCocoaTests/InterceptingSpec.swift +++ b/ReactiveCocoaTests/InterceptingSpec.swift @@ -530,10 +530,10 @@ class InterceptingSpec: QuickSpec { describe("two classes in the same hierarchy") { var superclassObj: InterceptedObject! - var superclassTuple: [Any?]? + var superclassTuple: [Any]! var subclassObj: InterceptedObjectSubclass! - var subclassTuple: [Any?]? + var subclassTuple: [Any]! beforeEach { superclassObj = InterceptedObject() @@ -547,51 +547,51 @@ class InterceptingSpec: QuickSpec { superclassObj.reactive .signal(for: #selector(InterceptedObject.foo)) .observeValues { args in - superclassTuple = args - } + superclassTuple = args.map { $0 ?? NSNull() } + } subclassObj .reactive .signal(for: #selector(InterceptedObject.foo)) .observeValues { args in - subclassTuple = args - } + subclassTuple = args.map { $0 ?? NSNull() } + } expect(superclassObj.foo(40, "foo")) == "Not Subclass 40 foo" let expectedValues = [40, "foo"] as NSArray - expect(superclassTuple as NSArray?) == expectedValues + expect(superclassTuple as NSArray) == expectedValues expect(subclassObj.foo(40, "foo")) == "Subclass 40 foo" - expect(subclassTuple as NSArray?) == expectedValues + expect(subclassTuple as NSArray) == expectedValues } it("should not collide when the superclass is invoked asynchronously") { superclassObj.reactive .signal(for: #selector(InterceptedObject.set(first:second:))) .observeValues { args in - superclassTuple = args + superclassTuple = args.map { $0 ?? NSNull() } } subclassObj .reactive .signal(for: #selector(InterceptedObject.set(first:second:))) .observeValues { args in - subclassTuple = args + subclassTuple = args.map { $0 ?? NSNull() } } superclassObj.set(first: "foo", second:"42") expect(superclassObj.hasInvokedSetObjectValueAndSecondObjectValue) == true let expectedValues = ["foo", "42"] as NSArray - expect(superclassTuple as NSArray?) == expectedValues + expect(superclassTuple as NSArray) == expectedValues subclassObj.set(first: "foo", second:"42") expect(subclassObj.hasInvokedSetObjectValueAndSecondObjectValue) == false expect(subclassObj.hasInvokedSetObjectValueAndSecondObjectValue).toEventually(beTruthy()) - expect(subclassTuple as NSArray?) == expectedValues + expect(subclassTuple as NSArray) == expectedValues } } @@ -668,19 +668,19 @@ class InterceptingSpec: QuickSpec { } func validate(arguments: [Any?], offset: UInt) { - expect(arguments[0] as? CChar) == CChar.max - CChar(offset) - expect(arguments[1] as? CShort) == CShort.max - CShort(offset) - expect(arguments[2] as? CInt) == CInt.max - CInt(offset) - expect(arguments[3] as? CLong) == CLong.max - CLong(offset) - expect(arguments[4] as? CLongLong) == CLongLong.max - CLongLong(offset) - expect(arguments[5] as? CUnsignedChar) == CUnsignedChar.max - CUnsignedChar(offset) - expect(arguments[6] as? CUnsignedShort) == CUnsignedShort.max - CUnsignedShort(offset) - expect(arguments[7] as? CUnsignedInt) == CUnsignedInt.max - CUnsignedInt(offset) - expect(arguments[8] as? CUnsignedLong) == CUnsignedLong.max - CUnsignedLong(offset) - expect(arguments[9] as? CUnsignedLongLong) == CUnsignedLongLong.max - CUnsignedLongLong(offset) - expect(arguments[10] as? CFloat) == CFloat.greatestFiniteMagnitude - CFloat(offset) - expect(arguments[11] as? CDouble) == CDouble.greatestFiniteMagnitude - CDouble(offset) - expect(arguments[12] as? CBool) == (offset % 2 == 0 ? true : false) + expect((arguments[0] as! NSNumber).int8Value) == CChar.max - CChar(offset) + expect((arguments[1] as! NSNumber).int16Value) == CShort.max - CShort(offset) + expect((arguments[2] as! NSNumber).int32Value) == CInt.max - CInt(offset) + expect((arguments[3] as! NSNumber).intValue) == CLong.max - CLong(offset) + expect((arguments[4] as! NSNumber).int64Value) == CLongLong.max - CLongLong(offset) + expect((arguments[5] as! NSNumber).uint8Value) == CUnsignedChar.max - CUnsignedChar(offset) + expect((arguments[6] as! NSNumber).uint16Value) == CUnsignedShort.max - CUnsignedShort(offset) + expect((arguments[7] as! NSNumber).uint32Value) == CUnsignedInt.max - CUnsignedInt(offset) + expect((arguments[8] as! NSNumber).uintValue) == CUnsignedLong.max - CUnsignedLong(offset) + expect((arguments[9] as! NSNumber).uint64Value) == CUnsignedLongLong.max - CUnsignedLongLong(offset) + expect((arguments[10] as! NSNumber).floatValue) == CFloat.greatestFiniteMagnitude - CFloat(offset) + expect((arguments[11] as! NSNumber).doubleValue) == CDouble.greatestFiniteMagnitude - CDouble(offset) + expect((arguments[12] as! NSNumber).boolValue) == (offset % 2 == 0 ? true : false) } call(offset: 0) @@ -755,10 +755,16 @@ class InterceptingSpec: QuickSpec { } func validate(arguments: [Any?], offset: CGFloat) { - expect((arguments[0] as! CGPoint)) == CGPoint(x: offset, y: offset) - expect((arguments[1] as! CGSize)) == CGSize(width: offset, height: offset) - expect((arguments[2] as! CGRect)) == CGRect(x: offset, y: offset, width: offset, height: offset) - expect((arguments[3] as! CGAffineTransform)) == CGAffineTransform(translationX: offset, y: offset) + #if os(macOS) + expect((arguments[0] as! NSValue).pointValue) == CGPoint(x: offset, y: offset) + expect((arguments[1] as! NSValue).sizeValue) == CGSize(width: offset, height: offset) + expect((arguments[2] as! NSValue).rectValue) == CGRect(x: offset, y: offset, width: offset, height: offset) + #else + expect((arguments[0] as! NSValue).cgPointValue) == CGPoint(x: offset, y: offset) + expect((arguments[1] as! NSValue).cgSizeValue) == CGSize(width: offset, height: offset) + expect((arguments[2] as! NSValue).cgRectValue) == CGRect(x: offset, y: offset, width: offset, height: offset) + expect((arguments[3] as! NSValue).cgAffineTransformValue) == CGAffineTransform(translationX: offset, y: offset) + #endif } call(offset: 0) diff --git a/ReactiveCocoaTests/SwizzlingSpec.swift b/ReactiveCocoaTests/SwizzlingSpec.swift index aaa4c6ede1..dc5b1693ee 100644 --- a/ReactiveCocoaTests/SwizzlingSpec.swift +++ b/ReactiveCocoaTests/SwizzlingSpec.swift @@ -14,9 +14,9 @@ class SwizzlingSpec: QuickSpec { let subclass: AnyClass = swizzleClass(object) expect(type(of: object)).to(beIdenticalTo(subclass)) - let objcClass = (object as AnyObject).objcClass + let objcClass: AnyClass = (object as AnyObject).objcClass expect(objcClass).to(beIdenticalTo(SwizzledObject.self)) - expect((objcClass as AnyObject).objcClass).to(beIdenticalTo(SwizzledObject.self)) + expect((objcClass as AnyObject).objcClass).to(beIdenticalTo(SwizzledObject.self as AnyClass)) expect(String(cString: class_getName(subclass))).to(contain("_RACSwift")) } From adcf6316016cdaa0280578a173d7cacf2ce989ed Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Tue, 25 Apr 2017 23:26:17 +0800 Subject: [PATCH 0793/1028] Disable UISwitch spec deallocation check for iOS prior to 10.2. --- ReactiveCocoaTests/UIKit/UISwitchSpec.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ReactiveCocoaTests/UIKit/UISwitchSpec.swift b/ReactiveCocoaTests/UIKit/UISwitchSpec.swift index b5dac2b9bf..a102a3ecdc 100644 --- a/ReactiveCocoaTests/UIKit/UISwitchSpec.swift +++ b/ReactiveCocoaTests/UIKit/UISwitchSpec.swift @@ -16,7 +16,9 @@ class UISwitchSpec: QuickSpec { afterEach { toggle = nil - expect(_toggle).to(beNil()) + if #available(*, iOS 10.2) { + expect(_toggle).to(beNil()) + } } it("should accept changes from bindings to its `isOn` state") { From 573a8783afea1cc4527fc053e90c0a667adc2cbc Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Wed, 26 Apr 2017 02:54:24 +0800 Subject: [PATCH 0794/1028] Restore the bridging casts in InterceptingSpec for Swift 3.1+. --- ReactiveCocoaTests/InterceptingSpec.swift | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/ReactiveCocoaTests/InterceptingSpec.swift b/ReactiveCocoaTests/InterceptingSpec.swift index b7a9cf05a3..f4a028b9da 100644 --- a/ReactiveCocoaTests/InterceptingSpec.swift +++ b/ReactiveCocoaTests/InterceptingSpec.swift @@ -668,6 +668,21 @@ class InterceptingSpec: QuickSpec { } func validate(arguments: [Any?], offset: UInt) { + #if swift(>=3.1) + expect((arguments[0] as! CChar)) == CChar.max - CChar(offset) + expect((arguments[1] as! CShort)) == CShort.max - CShort(offset) + expect((arguments[2] as! CInt)) == CInt.max - CInt(offset) + expect((arguments[3] as! CLong)) == CLong.max - CLong(offset) + expect((arguments[4] as! CLongLong)) == CLongLong.max - CLongLong(offset) + expect((arguments[5] as! CUnsignedChar)) == CUnsignedChar.max - CUnsignedChar(offset) + expect((arguments[6] as! CUnsignedShort)) == CUnsignedShort.max - CUnsignedShort(offset) + expect((arguments[7] as! CUnsignedInt)) == CUnsignedInt.max - CUnsignedInt(offset) + expect((arguments[8] as! CUnsignedLong)) == CUnsignedLong.max - CUnsignedLong(offset) + expect((arguments[9] as! CUnsignedLongLong)) == CUnsignedLongLong.max - CUnsignedLongLong(offset) + expect((arguments[10] as! CFloat)) == CFloat.greatestFiniteMagnitude - CFloat(offset) + expect((arguments[11] as! CDouble)) == CDouble.greatestFiniteMagnitude - CDouble(offset) + expect((arguments[12] as! Bool)) == (offset % 2 == 0 ? true : false) + #else expect((arguments[0] as! NSNumber).int8Value) == CChar.max - CChar(offset) expect((arguments[1] as! NSNumber).int16Value) == CShort.max - CShort(offset) expect((arguments[2] as! NSNumber).int32Value) == CInt.max - CInt(offset) @@ -681,6 +696,7 @@ class InterceptingSpec: QuickSpec { expect((arguments[10] as! NSNumber).floatValue) == CFloat.greatestFiniteMagnitude - CFloat(offset) expect((arguments[11] as! NSNumber).doubleValue) == CDouble.greatestFiniteMagnitude - CDouble(offset) expect((arguments[12] as! NSNumber).boolValue) == (offset % 2 == 0 ? true : false) + #endif } call(offset: 0) @@ -755,7 +771,12 @@ class InterceptingSpec: QuickSpec { } func validate(arguments: [Any?], offset: CGFloat) { - #if os(macOS) + #if swift(>=3.1) + expect((arguments[0] as! CGPoint)) == CGPoint(x: offset, y: offset) + expect((arguments[1] as! CGSize)) == CGSize(width: offset, height: offset) + expect((arguments[2] as! CGRect)) == CGRect(x: offset, y: offset, width: offset, height: offset) + expect((arguments[3] as! CGAffineTransform)) == CGAffineTransform(translationX: offset, y: offset) + #elseif os(macOS) expect((arguments[0] as! NSValue).pointValue) == CGPoint(x: offset, y: offset) expect((arguments[1] as! NSValue).sizeValue) == CGSize(width: offset, height: offset) expect((arguments[2] as! NSValue).rectValue) == CGRect(x: offset, y: offset, width: offset, height: offset) From 48db14738d726e84d496a82a6bf17019b683b296 Mon Sep 17 00:00:00 2001 From: Christopher Liscio Date: Wed, 26 Apr 2017 13:55:19 -0400 Subject: [PATCH 0795/1028] Fix bad productName values --- ReactiveCocoa.xcodeproj/project.pbxproj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 631c6b540b..9570007d91 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -977,7 +977,7 @@ dependencies = ( ); name = "ReactiveCocoa-tvOS"; - productName = ReactiveCocoa; + productName = "ReactiveCocoa-tvOS"; productReference = 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */; productType = "com.apple.product-type.framework"; }; @@ -1014,7 +1014,7 @@ dependencies = ( ); name = "ReactiveCocoa-watchOS"; - productName = ReactiveCocoa; + productName = "ReactiveCocoa-watchOS"; productReference = A9B315541B3940610001CB9C /* ReactiveCocoa.framework */; productType = "com.apple.product-type.framework"; }; @@ -1032,7 +1032,7 @@ dependencies = ( ); name = "ReactiveCocoa-macOS"; - productName = ReactiveCocoa; + productName = "ReactiveCocoa-macOS"; productReference = D04725EA19E49ED7006002AA /* ReactiveCocoa.framework */; productType = "com.apple.product-type.framework"; }; @@ -1050,7 +1050,7 @@ D04725F819E49ED7006002AA /* PBXTargetDependency */, ); name = "ReactiveCocoa-macOSTests"; - productName = ReactiveCocoaTests; + productName = "ReactiveCocoa-macOSTests"; productReference = D04725F519E49ED7006002AA /* ReactiveCocoaTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; @@ -1068,7 +1068,7 @@ dependencies = ( ); name = "ReactiveCocoa-iOS"; - productName = ReactiveCocoa; + productName = "ReactiveCocoa-iOS"; productReference = D047260C19E49F82006002AA /* ReactiveCocoa.framework */; productType = "com.apple.product-type.framework"; }; @@ -1087,7 +1087,7 @@ D047261919E49F82006002AA /* PBXTargetDependency */, ); name = "ReactiveCocoa-iOSTests"; - productName = ReactiveCocoaTests; + productName = "ReactiveCocoa-iOSTests"; productReference = D047261619E49F82006002AA /* ReactiveCocoaTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; From 4776f2911aebfab92fe3081e788d34f3703c0680 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Thu, 27 Apr 2017 13:05:30 +0800 Subject: [PATCH 0796/1028] Use unowned reference in `DynamicProperty`. --- ReactiveCocoa/DynamicProperty.swift | 56 ++++++++++---------- ReactiveCocoaTests/DynamicPropertySpec.swift | 43 ++++++++++----- 2 files changed, 57 insertions(+), 42 deletions(-) diff --git a/ReactiveCocoa/DynamicProperty.swift b/ReactiveCocoa/DynamicProperty.swift index a197ad8b52..8e76b3b8c8 100644 --- a/ReactiveCocoa/DynamicProperty.swift +++ b/ReactiveCocoa/DynamicProperty.swift @@ -2,31 +2,38 @@ import Foundation import ReactiveSwift import enum Result.NoError -/// Wraps a `dynamic` property, or one defined in Objective-C, using Key-Value -/// Coding and Key-Value Observing. +/// A typed mutable property view to a certain key path of an Objective-C object using +/// Key-Value Coding and Key-Value Observing. /// -/// Use this class only as a last resort! `MutableProperty` is generally better -/// unless KVC/KVO is required by the API you're using (for example, -/// `NSOperation`). +/// - warning: A `DynamicProperty` holds an unowned reference to the Objective-C object. +/// Any access of any property in a `DynamicProperty` after the deallocation +/// of the object would trap at runtime. public final class DynamicProperty: MutablePropertyProtocol { - private weak var object: NSObject? + private unowned let object: NSObject private let keyPath: String /// The current value of the property, as read and written using Key-Value /// Coding. - public var value: Value? { + public var value: Value { get { - return object?.value(forKeyPath: keyPath) as! Value + return object.value(forKeyPath: keyPath) as! Value } set(newValue) { - object?.setValue(newValue, forKeyPath: keyPath) + object.setValue(newValue, forKeyPath: keyPath) } } /// The lifetime of the property. public var lifetime: Lifetime { - return object?.reactive.lifetime ?? .empty + return object.reactive.lifetime + } + + /// The binding target of the property. + public var bindingTarget: BindingTarget { + return BindingTarget(lifetime: lifetime) { [weak object, keyPath] value in + object?.setValue(value, forKey: keyPath) + } } /// A producer that will create a Key-Value Observer for the given object, @@ -35,32 +42,23 @@ public final class DynamicProperty: MutablePropertyProtocol { /// /// - important: This only works if the object given to init() is KVO-compliant. /// Most UI controls are not! - public var producer: SignalProducer { - return (object.map { $0.reactive.producer(forKeyPath: keyPath) } ?? .empty) - .map { $0 as! Value } + public var producer: SignalProducer { + return object.reactive.producer(forKeyPath: keyPath).map { $0 as! Value } } - public private(set) lazy var signal: Signal = { - var signal: Signal! - self.producer.startWithSignal { innerSignal, _ in signal = innerSignal } - return signal - }() + public var signal: Signal { + return object.reactive.signal(forKeyPath: keyPath).map { $0 as! Value } + } - /// Initializes a property that will observe and set the given key path of - /// the given object. The generic type `Value` can be any Swift type, and will - /// be bridged to Objective-C via `Any`. - /// - /// - important: `object` must support weak references! + /// Create a typed mutable view to the given key path of the given Objective-C object. + /// The generic type `Value` can be any Swift type, and will be bridged to Objective-C + /// via `Any`. /// /// - parameters: - /// - object: An object to be observed. - /// - keyPath: Key path to observe on the object. + /// - object: The Objective-C object to be observed. + /// - keyPath: The key path to observe. public init(object: NSObject, keyPath: String) { self.object = object self.keyPath = keyPath - - /// A DynamicProperty will stay alive as long as its object is alive. - /// This is made possible by strong reference cycles. - _ = object.reactive.lifetime.ended.observeCompleted { _ = self } } } diff --git a/ReactiveCocoaTests/DynamicPropertySpec.swift b/ReactiveCocoaTests/DynamicPropertySpec.swift index 3db87f17e9..76eef29a7a 100644 --- a/ReactiveCocoaTests/DynamicPropertySpec.swift +++ b/ReactiveCocoaTests/DynamicPropertySpec.swift @@ -53,8 +53,7 @@ class DynamicPropertySpec: QuickSpec { it("should yield a producer that sends the current value and then the changes for the key path of the underlying object") { var values: [Int] = [] property.producer.startWithValues { value in - expect(value).notTo(beNil()) - values.append(value!) + values.append(value) } expect(values) == [ 0 ] @@ -69,8 +68,7 @@ class DynamicPropertySpec: QuickSpec { it("should yield a producer that sends the current value and then the changes for the key path of the underlying object, even if the value actually remains unchanged") { var values: [Int] = [] property.producer.startWithValues { value in - expect(value).notTo(beNil()) - values.append(value!) + values.append(value) } expect(values) == [ 0 ] @@ -86,7 +84,7 @@ class DynamicPropertySpec: QuickSpec { var values: [Int] = [] property.signal.observeValues { value in expect(value).notTo(beNil()) - values.append(value!) + values.append(value) } expect(values) == [] @@ -101,8 +99,7 @@ class DynamicPropertySpec: QuickSpec { it("should yield a signal that emits subsequent values for the key path of the underlying object, even if the value actually remains unchanged") { var values: [Int] = [] property.signal.observeValues { value in - expect(value).notTo(beNil()) - values.append(value!) + values.append(value) } expect(values) == [] @@ -132,7 +129,6 @@ class DynamicPropertySpec: QuickSpec { }() expect(completed).toEventually(beTruthy()) - expect(property.value).to(beNil()) } it("should have a completed signal when the underlying object deallocates") { @@ -153,16 +149,12 @@ class DynamicPropertySpec: QuickSpec { }() expect(completed).toEventually(beTruthy()) - expect(property.value).to(beNil()) } - it("should retain property while DynamicProperty's underlying object is retained"){ + it("should not be retained by its underlying object"){ weak var dynamicProperty: DynamicProperty? = property property = nil - expect(dynamicProperty).toNot(beNil()) - - object = nil expect(dynamicProperty).to(beNil()) } @@ -228,6 +220,31 @@ class DynamicPropertySpec: QuickSpec { property <~ source expect(object.rac_value) == 1 } + + it("should bridge values sent on a signal to Objective-C, even if the view has deinitialized") { + let (signal, observer) = Signal.pipe() + property <~ signal + property = nil + + observer.send(value: 1) + expect(object.rac_value) == 1 + } + + it("should bridge values sent on a signal producer to Objective-C, even if the view has deinitialized") { + let producer = SignalProducer(value: 1) + property <~ producer + property = nil + + expect(object.rac_value) == 1 + } + + it("should bridge values from a source property to Objective-C, even if the view has deinitialized") { + let source = MutableProperty(1) + property <~ source + property = nil + + expect(object.rac_value) == 1 + } } } } From e9d25cbe75e215e156dd81ace96c0e633e67839b Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 15 Apr 2017 14:39:36 +0800 Subject: [PATCH 0797/1028] Full buffering in `CocoaTarget` for reentrant calls. --- ReactiveCocoa/CocoaTarget.swift | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/ReactiveCocoa/CocoaTarget.swift b/ReactiveCocoa/CocoaTarget.swift index 1c50d06beb..85a92ed6d8 100644 --- a/ReactiveCocoa/CocoaTarget.swift +++ b/ReactiveCocoa/CocoaTarget.swift @@ -6,8 +6,7 @@ import enum Result.NoError internal final class CocoaTarget: NSObject { private enum State { case idle - case sending - case invokeAfterSending + case sending(queue: [Value]) } private let observer: Observer @@ -24,8 +23,7 @@ internal final class CocoaTarget: NSObject { /// Broadcast the action message to all observers. /// /// Reentrancy is supported, and the action message would be deferred until the - /// delivery of the current message has completed. Deferred messages would be - /// coalesced. + /// delivery of the current message has completed. /// /// - note: It should only be invoked on the main queue. /// @@ -34,21 +32,22 @@ internal final class CocoaTarget: NSObject { @objc internal func invoke(_ sender: Any?) { switch state { case .idle: - state = .sending + state = .sending(queue: []) observer.send(value: transform(sender)) - while state == .invokeAfterSending { - state = .sending - observer.send(value: transform(sender)) + while case let .sending(values) = state { + guard !values.isEmpty else { + break + } + + state = .sending(queue: Array(values.dropFirst())) + observer.send(value: values[0]) } state = .idle - case .sending: - state = .invokeAfterSending - - case .invokeAfterSending: - break + case let .sending(values): + state = .sending(queue: values + [transform(sender)]) } } } From b2c54aec9bc4b47d8e6f838492ec9ae521583a24 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Thu, 27 Apr 2017 13:06:37 +0800 Subject: [PATCH 0798/1028] Safe, read-only `DynamicProperty` as `Property`. --- ReactiveCocoa.xcodeproj/project.pbxproj | 8 + ReactiveCocoa/DynamicProperty.swift | 24 +-- .../NSObject+KeyValueObserving.swift | 40 ++++ ReactiveCocoaTests/KVOKVCExtensionSpec.swift | 192 ++++++++++++++++++ 4 files changed, 250 insertions(+), 14 deletions(-) create mode 100644 ReactiveCocoaTests/KVOKVCExtensionSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 631c6b540b..9ba534bf3b 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -222,6 +222,9 @@ 9AF0EA761D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; 9AF0EA771D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; 9AF0EA781D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; + 9AFCBFE31EB1ABC0004B4C74 /* KVOKVCExtensionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AFCBFE21EB1ABC0004B4C74 /* KVOKVCExtensionSpec.swift */; }; + 9AFCBFE41EB1ABC0004B4C74 /* KVOKVCExtensionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AFCBFE21EB1ABC0004B4C74 /* KVOKVCExtensionSpec.swift */; }; + 9AFCBFE51EB1ABC0004B4C74 /* KVOKVCExtensionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AFCBFE21EB1ABC0004B4C74 /* KVOKVCExtensionSpec.swift */; }; A9B315C91B3940980001CB9C /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; A9B315CA1B3940AB0001CB9C /* ReactiveCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; }; A9EB3D201E94F08A002A9BCC /* UINavigationItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D1C1E94ECAC002A9BCC /* UINavigationItem.swift */; }; @@ -444,6 +447,7 @@ 9AE7C2A31DDD7F5100F7534C /* ObjC+Messages.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObjC+Messages.swift"; sourceTree = ""; }; 9AED64C41E496A3700321004 /* ActionProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionProxy.swift; sourceTree = ""; }; 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+BindingTarget.swift"; sourceTree = ""; }; + 9AFCBFE21EB1ABC0004B4C74 /* KVOKVCExtensionSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KVOKVCExtensionSpec.swift; sourceTree = ""; }; A97451331B3A935E00F48E55 /* watchOS-Application.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Application.xcconfig"; sourceTree = ""; }; A97451341B3A935E00F48E55 /* watchOS-Base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Base.xcconfig"; sourceTree = ""; }; A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Framework.xcconfig"; sourceTree = ""; }; @@ -830,6 +834,7 @@ D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */, B696FB801A7640C00075236D /* TestError.swift */, 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */, + 9AFCBFE21EB1ABC0004B4C74 /* KVOKVCExtensionSpec.swift */, 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */, 9A54A2101DDF5B4D001739B3 /* InterceptingPerformanceTests.swift */, 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */, @@ -1285,6 +1290,7 @@ 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */, 9ADFE5A31DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */, 9A1D06411D93EA7E00ACF44C /* UIControlSpec.swift in Sources */, + 9AFCBFE51EB1ABC0004B4C74 /* KVOKVCExtensionSpec.swift in Sources */, 9A6AAA101DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */, 4ABEFE221DCFCF0A0066A8C2 /* UITableViewSpec.swift in Sources */, 9A1D06491D93EA7E00ACF44C /* UIProgressViewSpec.swift in Sources */, @@ -1372,6 +1378,7 @@ 9ADE4A8F1DA6DA20005C2AC8 /* NSControlSpec.swift in Sources */, D0A2260E1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */, 53A6BED61DD4BD2C0016C058 /* MKMapViewSpec.swift in Sources */, + 9AFCBFE31EB1ABC0004B4C74 /* KVOKVCExtensionSpec.swift in Sources */, 9ADFE5A11DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */, 4ABEFE331DCFD0630066A8C2 /* NSCollectionViewSpec.swift in Sources */, B696FB811A7640C00075236D /* TestError.swift in Sources */, @@ -1482,6 +1489,7 @@ 9A1D06421D93EA7E00ACF44C /* UIDatePickerSpec.swift in Sources */, 53AC46CF1DD6FC0000C799E1 /* UISliderSpec.swift in Sources */, 9A6AAA0F1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */, + 9AFCBFE41EB1ABC0004B4C74 /* KVOKVCExtensionSpec.swift in Sources */, BF4335681E02EF0600AC88DD /* UIScrollViewSpec.swift in Sources */, 3BCAAC7D1DEE1A2D00B30335 /* UIRefreshControlSpec.swift in Sources */, 53A6BED71DD4BD2C0016C058 /* MKMapViewSpec.swift in Sources */, diff --git a/ReactiveCocoa/DynamicProperty.swift b/ReactiveCocoa/DynamicProperty.swift index 8e76b3b8c8..ef2b71ebde 100644 --- a/ReactiveCocoa/DynamicProperty.swift +++ b/ReactiveCocoa/DynamicProperty.swift @@ -5,28 +5,23 @@ import enum Result.NoError /// A typed mutable property view to a certain key path of an Objective-C object using /// Key-Value Coding and Key-Value Observing. /// -/// - warning: A `DynamicProperty` holds an unowned reference to the Objective-C object. -/// Any access of any property in a `DynamicProperty` after the deallocation -/// of the object would trap at runtime. +/// Bindings towards a `DynamicProperty` would be directed to the underlying Objective-C +/// object, and would not be affected by the deinitialization of the `DynamicProperty`. public final class DynamicProperty: MutablePropertyProtocol { - private unowned let object: NSObject + private weak var object: NSObject? private let keyPath: String + private let cache: Property /// The current value of the property, as read and written using Key-Value /// Coding. public var value: Value { - get { - return object.value(forKeyPath: keyPath) as! Value - } - - set(newValue) { - object.setValue(newValue, forKeyPath: keyPath) - } + get { return cache.value } + set { object?.setValue(newValue, forKeyPath: keyPath) } } /// The lifetime of the property. public var lifetime: Lifetime { - return object.reactive.lifetime + return object?.reactive.lifetime ?? .empty } /// The binding target of the property. @@ -43,11 +38,11 @@ public final class DynamicProperty: MutablePropertyProtocol { /// - important: This only works if the object given to init() is KVO-compliant. /// Most UI controls are not! public var producer: SignalProducer { - return object.reactive.producer(forKeyPath: keyPath).map { $0 as! Value } + return cache.producer } public var signal: Signal { - return object.reactive.signal(forKeyPath: keyPath).map { $0 as! Value } + return cache.signal } /// Create a typed mutable view to the given key path of the given Objective-C object. @@ -60,5 +55,6 @@ public final class DynamicProperty: MutablePropertyProtocol { public init(object: NSObject, keyPath: String) { self.object = object self.keyPath = keyPath + self.cache = Property(object: object, keyPath: keyPath) } } diff --git a/ReactiveCocoa/NSObject+KeyValueObserving.swift b/ReactiveCocoa/NSObject+KeyValueObserving.swift index 0ec154e0d5..c1e69d96b2 100644 --- a/ReactiveCocoa/NSObject+KeyValueObserving.swift +++ b/ReactiveCocoa/NSObject+KeyValueObserving.swift @@ -52,6 +52,46 @@ extension Reactive where Base: NSObject { } } +extension Property { + /// Create a property that observes the given key path of the given object. The + /// generic type `Value` can be any Swift type that is Objective-C bridgeable. + /// + /// - parameters: + /// - object: An object to be observed. + /// - keyPath: The key path to observe. + public convenience init(object: NSObject, keyPath: String) { + // `Property(_:)` caches the latest value of the `DynamicProperty`, so it is + // saved to be used even after `object` deinitializes. + self.init(UnsafeKVOProperty(object: object, keyPath: keyPath)) + } + + // `Property(unsafeProducer:)` is private to ReactiveSwift. So the fact that + // `Property(_:)` uses only the producer is explioted here to achieve the same effect. + private final class UnsafeKVOProperty: PropertyProtocol { + var value: Value { fatalError() } + var signal: Signal { fatalError() } + let producer: SignalProducer + + init(object: NSObject, keyPath: String) { + self.producer = object.reactive.producer(forKeyPath: keyPath).map { $0 as! Value } + } + } +} + +extension BindingTarget { + /// Create a binding target that sets the given key path of the given object. The + /// generic type `Value` can be any Swift type that is Objective-C bridgeable. + /// + /// - parameters: + /// - object: An object to be observed. + /// - keyPath: The key path to set. + public init(object: NSObject, keyPath: String) { + self.init(lifetime: object.reactive.lifetime) { [weak object] value in + object?.setValue(value, forKey: keyPath) + } + } +} + internal final class KeyValueObserver: NSObject { typealias Action = (_ object: AnyObject?) -> Void private static let context = UnsafeMutableRawPointer.allocate(bytes: 1, alignedTo: 0) diff --git a/ReactiveCocoaTests/KVOKVCExtensionSpec.swift b/ReactiveCocoaTests/KVOKVCExtensionSpec.swift new file mode 100644 index 0000000000..45df5b7867 --- /dev/null +++ b/ReactiveCocoaTests/KVOKVCExtensionSpec.swift @@ -0,0 +1,192 @@ +import ReactiveSwift +import Result +import Nimble +import Quick +import ReactiveCocoa + +private let initialPropertyValue = "InitialValue" +private let subsequentPropertyValue = "SubsequentValue" +private let finalPropertyValue = "FinalValue" + +private let initialOtherPropertyValue = "InitialOtherValue" +private let subsequentOtherPropertyValue = "SubsequentOtherValue" +private let finalOtherPropertyValue = "FinalOtherValue" + +class KVOKVCExtensionSpec: QuickSpec { + override func spec() { + describe("Property(object:keyPath:)") { + var object: ObservableObject! + var property: Property! + + beforeEach { + object = ObservableObject() + expect(object.rac_value) == 0 + + property = Property(object: object, keyPath: "rac_value") + } + + afterEach { + object = nil + } + + it("should read the underlying object") { + expect(property.value) == 0 + + object.rac_value = 1 + expect(property.value) == 1 + } + + it("should yield a producer that sends the current value and then the changes for the key path of the underlying object") { + var values: [Int] = [] + property.producer.startWithValues { value in + values.append(value) + } + + expect(values) == [ 0 ] + + object.rac_value = 1 + expect(values) == [ 0, 1 ] + + object.rac_value = 2 + expect(values) == [ 0, 1, 2 ] + } + + it("should yield a producer that sends the current value and then the changes for the key path of the underlying object, even if the value actually remains unchanged") { + var values: [Int] = [] + property.producer.startWithValues { value in + values.append(value) + } + + expect(values) == [ 0 ] + + object.rac_value = 0 + expect(values) == [ 0, 0 ] + + object.rac_value = 0 + expect(values) == [ 0, 0, 0 ] + } + + it("should yield a signal that emits subsequent values for the key path of the underlying object") { + var values: [Int] = [] + property.signal.observeValues { value in + values.append(value) + } + + expect(values) == [] + + object.rac_value = 1 + expect(values) == [ 1 ] + + object.rac_value = 2 + expect(values) == [ 1, 2 ] + } + + it("should yield a signal that emits subsequent values for the key path of the underlying object, even if the value actually remains unchanged") { + var values: [Int] = [] + property.signal.observeValues { value in + values.append(value) + } + + expect(values) == [] + + object.rac_value = 0 + expect(values) == [ 0 ] + + object.rac_value = 0 + expect(values) == [ 0, 0 ] + } + + it("should have a completed producer when the underlying object deallocates") { + var completed = false + + property = { + // Use a closure so this object has a shorter lifetime. + let object = ObservableObject() + let property = Property(object: object, keyPath: "rac_value") + + property.producer.startWithCompleted { + completed = true + } + + expect(completed) == false + expect(property.value) == 0 + return property + }() + + expect(completed).toEventually(beTruthy()) + } + + it("should have a completed signal when the underlying object deallocates") { + var completed = false + + property = { + // Use a closure so this object has a shorter lifetime. + let object = ObservableObject() + let property = Property(object: object, keyPath: "rac_value") + + property.signal.observeCompleted { + completed = true + } + + expect(completed) == false + expect(property.value).notTo(beNil()) + return property + }() + + expect(completed).toEventually(beTruthy()) + } + + it("should not be retained by its underlying object"){ + weak var weakProperty: Property? = property + + property = nil + expect(weakProperty).to(beNil()) + } + + it("should be accessible even if the underlying object has deinitialized") { + object.rac_value = .max + object = nil + + expect(property.value) == Int.max + + var latestValue: Int? + property.producer.startWithValues { latestValue = $0 } + expect(latestValue) == .max + + var isInterrupted = false + property.signal.observeInterrupted { isInterrupted = true } + expect(isInterrupted) == true + } + + it("should support un-bridged reference types") { + let dynamicProperty = DynamicProperty(object: object, keyPath: "rac_reference") + dynamicProperty.value = UnbridgedObject("foo") + expect(object.rac_reference.value) == "foo" + } + + it("should support un-bridged value types") { + let dynamicProperty = DynamicProperty(object: object, keyPath: "rac_unbridged") + dynamicProperty.value = .changed + expect(object.rac_unbridged as? UnbridgedValue) == UnbridgedValue.changed + } + } + } +} + +private class ObservableObject: NSObject { + dynamic var rac_value: Int = 0 + dynamic var rac_reference: UnbridgedObject = UnbridgedObject("") + dynamic var rac_unbridged: Any = UnbridgedValue.starting +} + +private class UnbridgedObject: NSObject { + let value: String + init(_ value: String) { + self.value = value + } +} + +private enum UnbridgedValue { + case starting + case changed +} From 949d135e180238506151c6be2d2192f9efc89bae Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Thu, 25 May 2017 04:08:35 +0800 Subject: [PATCH 0799/1028] Introduce `take(duringLifetimeOf:)`. --- ReactiveCocoa.xcodeproj/project.pbxproj | 10 +++ ReactiveCocoa/ReactiveSwift+Lifetime.swift | 29 ++++++++ ReactiveCocoaTests/LifetimeSpec.swift | 86 ++++++++++++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 ReactiveCocoa/ReactiveSwift+Lifetime.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 631c6b540b..cf8040fe66 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -160,6 +160,10 @@ 9A892D8F1E8D19BE00EA35F3 /* DelegateProxySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A892D8E1E8D19BE00EA35F3 /* DelegateProxySpec.swift */; }; 9A892D901E8D19BE00EA35F3 /* DelegateProxySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A892D8E1E8D19BE00EA35F3 /* DelegateProxySpec.swift */; }; 9A892D911E8D19BE00EA35F3 /* DelegateProxySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A892D8E1E8D19BE00EA35F3 /* DelegateProxySpec.swift */; }; + 9A90374F1ED61C6300345D62 /* ReactiveSwift+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A90374E1ED61C6300345D62 /* ReactiveSwift+Lifetime.swift */; }; + 9A9037501ED61C6300345D62 /* ReactiveSwift+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A90374E1ED61C6300345D62 /* ReactiveSwift+Lifetime.swift */; }; + 9A9037511ED61C6300345D62 /* ReactiveSwift+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A90374E1ED61C6300345D62 /* ReactiveSwift+Lifetime.swift */; }; + 9A9037521ED61C6300345D62 /* ReactiveSwift+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A90374E1ED61C6300345D62 /* ReactiveSwift+Lifetime.swift */; }; 9A9A129A1DC7A97100D10223 /* UIGestureRecognizerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4191394B1DBA002C0043C9D1 /* UIGestureRecognizerSpec.swift */; }; 9A9DFEE51DA7B5500039EE1B /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; 9A9DFEE91DA7EFB60039EE1B /* AssociationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */; }; @@ -423,6 +427,7 @@ 9A6AAA291DB8F7F10013AAEA /* ReusableComponentsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponentsSpec.swift; sourceTree = ""; }; 9A7488471E3B8ACE00CD0317 /* DelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DelegateProxy.swift; sourceTree = ""; }; 9A892D8E1E8D19BE00EA35F3 /* DelegateProxySpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DelegateProxySpec.swift; sourceTree = ""; }; + 9A90374E1ED61C6300345D62 /* ReactiveSwift+Lifetime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ReactiveSwift+Lifetime.swift"; sourceTree = ""; }; 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssociationSpec.swift; sourceTree = ""; }; 9AA0BD771DDE03DE00531FCF /* ObjC+Runtime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObjC+Runtime.swift"; sourceTree = ""; }; 9AA0BD801DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObjC+RuntimeSubclassing.swift"; sourceTree = ""; }; @@ -796,6 +801,7 @@ 9A54A21A1DE00D09001739B3 /* ObjC+Selector.swift */, 9AA0BD801DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift */, 9AA0BD771DDE03DE00531FCF /* ObjC+Runtime.swift */, + 9A90374E1ED61C6300345D62 /* ReactiveSwift+Lifetime.swift */, 9A1D05E91D93E9F100ACF44C /* AppKit */, 9A1D05EB1D93E9F100ACF44C /* UIKit */, 538DCB761DCA5E1600332880 /* Shared */, @@ -1248,6 +1254,7 @@ 9AA0BD7F1DDE03DE00531FCF /* ObjC+Runtime.swift in Sources */, 9A2E42611DAA6737006D909F /* CocoaTarget.swift in Sources */, 53A6BED41DD4BCA90016C058 /* MKMapView.swift in Sources */, + 9A9037521ED61C6300345D62 /* ReactiveSwift+Lifetime.swift in Sources */, 9A1D06121D93EA0100ACF44C /* UIBarButtonItem.swift in Sources */, 9A1D06111D93EA0100ACF44C /* UIActivityIndicatorView.swift in Sources */, 9A1D061B1D93EA0100ACF44C /* UISegmentedControl.swift in Sources */, @@ -1314,6 +1321,7 @@ CD0C45E01CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9AA0BD911DDE29F800531FCF /* NSObject+ObjCRuntime.swift in Sources */, 9AF0EA771D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, + 9A9037511ED61C6300345D62 /* ReactiveSwift+Lifetime.swift in Sources */, 9A1D05E21D93E99100ACF44C /* NSObject+Association.swift in Sources */, 4A0E11011D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, 9AE7C2A61DDD7F5100F7534C /* ObjC+Messages.swift in Sources */, @@ -1339,6 +1347,7 @@ 9AA0BD8A1DDE153A00531FCF /* ObjC+Constants.swift in Sources */, 9AED64C51E496A3700321004 /* ActionProxy.swift in Sources */, 9A6AAA261DB8F5280013AAEA /* ReusableComponents.swift in Sources */, + 9A90374F1ED61C6300345D62 /* ReactiveSwift+Lifetime.swift in Sources */, 9AA0BD7C1DDE03DE00531FCF /* ObjC+Runtime.swift in Sources */, 9ADFE5A51DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */, 9ADE4A7C1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, @@ -1400,6 +1409,7 @@ 9A1D06011D93EA0000ACF44C /* UIBarItem.swift in Sources */, A9EB3D811E955602002A9BCC /* UIFeedbackGenerator.swift in Sources */, 9A1D060D1D93EA0000ACF44C /* UITextField.swift in Sources */, + 9A9037501ED61C6300345D62 /* ReactiveSwift+Lifetime.swift in Sources */, 9AB15C7B1E26CD9A00997378 /* Deprecations+Removals.swift in Sources */, A9EB3D831E955602002A9BCC /* UINotification​Feedback​Generator.swift in Sources */, 9A6AAA231DB8F51C0013AAEA /* ReusableComponents.swift in Sources */, diff --git a/ReactiveCocoa/ReactiveSwift+Lifetime.swift b/ReactiveCocoa/ReactiveSwift+Lifetime.swift new file mode 100644 index 0000000000..d42b2e3a49 --- /dev/null +++ b/ReactiveCocoa/ReactiveSwift+Lifetime.swift @@ -0,0 +1,29 @@ +import ReactiveSwift + +extension Signal { + /// Forward events from `self` until `object` deinitializes, at which point the + /// returned signal will complete. + /// + /// - parameters: + /// - object: An object of which the deinitialization would complete the returned + /// `Signal`. Both Objective-C and native Swift objects are supported. + /// + /// - returns: A signal that will deliver events until `object` deinitializes. + public func take(duringLifetimeOf object: AnyObject) -> Signal { + return take(during: lifetime(of: object)) + } +} + +extension SignalProducer { + /// Forward events from `self` until `object` deinitializes, at which point the + /// returned producer will complete. + /// + /// - parameters: + /// - object: An object of which the deinitialization would complete the returned + /// `Signal`. Both Objective-C and native Swift objects are supported. + /// + /// - returns: A producer that will deliver events until `object` deinitializes. + public func take(duringLifetimeOf object: AnyObject) -> SignalProducer { + return lift { $0.take(duringLifetimeOf: object) } + } +} diff --git a/ReactiveCocoaTests/LifetimeSpec.swift b/ReactiveCocoaTests/LifetimeSpec.swift index d3781839d1..4b4a0661c5 100644 --- a/ReactiveCocoaTests/LifetimeSpec.swift +++ b/ReactiveCocoaTests/LifetimeSpec.swift @@ -5,6 +5,8 @@ import enum Result.NoError import Quick import Nimble +private final class Token {} + class LifetimeSpec: QuickSpec { override func spec() { describe("NSObject.reactive.lifetime") { @@ -47,5 +49,89 @@ class LifetimeSpec: QuickSpec { } } } + + describe("Signal.take(duringLifetimeOf:)") { + it("should work with Objective-C objects") { + var object: NSObject? = NSObject() + weak var weakObject = object + var isCompleted = false + + let (signal, _) = Signal<(), NoError>.pipe() + + signal + .take(duringLifetimeOf: object!) + .observeCompleted { isCompleted = true } + + expect(weakObject).toNot(beNil()) + expect(isCompleted) == false + + object = nil + + expect(weakObject).to(beNil()) + expect(isCompleted) == true + } + + it("should work with native Swift objects") { + var object: Token? = Token() + weak var weakObject = object + var isCompleted = false + + let (signal, _) = Signal<(), NoError>.pipe() + + signal + .take(duringLifetimeOf: object!) + .observeCompleted { isCompleted = true } + + expect(weakObject).toNot(beNil()) + expect(isCompleted) == false + + object = nil + + expect(weakObject).to(beNil()) + expect(isCompleted) == true + } + } + + describe("SignalProducer.take(duringLifetimeOf:)") { + it("should work with Objective-C objects") { + var object: NSObject? = NSObject() + weak var weakObject = object + var isCompleted = false + + let (signal, _) = Signal<(), NoError>.pipe() + + SignalProducer(signal) + .take(duringLifetimeOf: object!) + .startWithCompleted { isCompleted = true } + + expect(weakObject).toNot(beNil()) + expect(isCompleted) == false + + object = nil + + expect(weakObject).to(beNil()) + expect(isCompleted) == true + } + + it("should work with native Swift objects") { + var object: Token? = Token() + weak var weakObject = object + var isCompleted = false + + let (signal, _) = Signal<(), NoError>.pipe() + + SignalProducer(signal) + .take(duringLifetimeOf: object!) + .startWithCompleted { isCompleted = true } + + expect(weakObject).toNot(beNil()) + expect(isCompleted) == false + + object = nil + + expect(weakObject).to(beNil()) + expect(isCompleted) == true + } + } } } From 5063f086d4b964432fc19297db771b6b469175a3 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 27 May 2017 17:00:27 +0800 Subject: [PATCH 0800/1028] Update the change log. [skip ci] --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32806b7984..1f89178136 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# master +*Please put new entries at the top. + +1. New operator: `take(duringLifetimeOf:)`. (#3466, kudos to @andersio) + It is available on `Signal` and `SignalProducer`, and supports both Objective-C and native Swift objects. + # 5.0 ### Table of Contents From 9d38b0ddbf226d31af05f5159c56245803b2a2ed Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sun, 4 Jun 2017 04:19:55 +0800 Subject: [PATCH 0801/1028] Update changelog. [skip ci] --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32806b7984..6f274df438 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# master +*Please put new entries at the top. + +1. Resigning first responder when reacting to a `UITextField` signal no longer deadlocks. (#3453) + # 5.0 ### Table of Contents From 46430da32aba3da605c829f4619ea4dbacf56072 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Mon, 5 Jun 2017 00:30:49 +0800 Subject: [PATCH 0802/1028] Introduce `mapControlEvents`. --- CHANGELOG.md | 2 +- ReactiveCocoa/UIKit/UIControl.swift | 18 +++++++++++++++--- ReactiveCocoa/UIKit/UISegmentedControl.swift | 2 +- ReactiveCocoa/UIKit/UITextField.swift | 8 ++++---- ReactiveCocoa/UIKit/iOS/UIDatePicker.swift | 2 +- ReactiveCocoa/UIKit/iOS/UISlider.swift | 2 +- ReactiveCocoa/UIKit/iOS/UIStepper.swift | 2 +- ReactiveCocoa/UIKit/iOS/UISwitch.swift | 2 +- ReactiveCocoaTests/UIKit/UITextFieldSpec.swift | 1 + 9 files changed, 26 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fab306383..ae7fb334c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # master *Please put new entries at the top. -1. Resigning first responder when reacting to a `UITextField` signal no longer deadlocks. (#3453) +1. Resigning first responder when reacting to a `UITextField` signal no longer deadlocks. (#3453, #3472) 1. New operator: `take(duringLifetimeOf:)`. (#3466, kudos to @andersio) It is available on `Signal` and `SignalProducer`, and supports both Objective-C and native Swift objects. diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index 1cf29579a9..0f14168c29 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -44,11 +44,23 @@ extension Reactive where Base: UIControl { /// - parameters: /// - controlEvents: The control event mask. /// - /// - returns: A signal that sends the control each time the control event - /// occurs. + /// - returns: A signal that sends the control each time the control event occurs. public func controlEvents(_ controlEvents: UIControlEvents) -> Signal { + return mapControlEvents(controlEvents, { $0 }) + } + + /// Create a signal which sends a `value` event for each of the specified + /// control events. + /// + /// - parameters: + /// - controlEvents: The control event mask. + /// - transform: A transform to reduce `Base`. + /// + /// - returns: A signal that sends the reduced value from the control each time the + /// control event occurs. + public func mapControlEvents(_ controlEvents: UIControlEvents, _ transform: @escaping (Base) -> Value) -> Signal { return Signal { observer in - let receiver = CocoaTarget(observer) { $0 as! Base } + let receiver = CocoaTarget(observer) { transform($0 as! Base) } base.addTarget(receiver, action: #selector(receiver.invoke), for: controlEvents) diff --git a/ReactiveCocoa/UIKit/UISegmentedControl.swift b/ReactiveCocoa/UIKit/UISegmentedControl.swift index db191a315a..33c4e4055e 100644 --- a/ReactiveCocoa/UIKit/UISegmentedControl.swift +++ b/ReactiveCocoa/UIKit/UISegmentedControl.swift @@ -10,6 +10,6 @@ extension Reactive where Base: UISegmentedControl { /// A signal of indexes of selections emitted by the segmented control. public var selectedSegmentIndexes: Signal { - return controlEvents(.valueChanged).map { $0.selectedSegmentIndex } + return mapControlEvents(.valueChanged) { $0.selectedSegmentIndex } } } diff --git a/ReactiveCocoa/UIKit/UITextField.swift b/ReactiveCocoa/UIKit/UITextField.swift index b968227f51..b278247e53 100644 --- a/ReactiveCocoa/UIKit/UITextField.swift +++ b/ReactiveCocoa/UIKit/UITextField.swift @@ -13,14 +13,14 @@ extension Reactive where Base: UITextField { /// - note: To observe text values that change on all editing events, /// see `continuousTextValues`. public var textValues: Signal { - return controlEvents(.editingDidEnd).map { $0.text } + return mapControlEvents(.editingDidEnd) { $0.text } } /// A signal of text values emitted by the text field upon any changes. /// /// - note: To observe text values only when editing ends, see `textValues`. public var continuousTextValues: Signal { - return controlEvents(.allEditingEvents).map { $0.text } + return mapControlEvents(.allEditingEvents) { $0.text } } /// Sets the attributed text of the text field. @@ -38,14 +38,14 @@ extension Reactive where Base: UITextField { /// - note: To observe attributed text values that change on all editing events, /// see `continuousAttributedTextValues`. public var attributedTextValues: Signal { - return controlEvents(.editingDidEnd).map { $0.attributedText } + return mapControlEvents(.editingDidEnd) { $0.attributedText } } /// A signal of attributed text values emitted by the text field upon any changes. /// /// - note: To observe attributed text values only when editing ends, see `attributedTextValues`. public var continuousAttributedTextValues: Signal { - return controlEvents(.allEditingEvents).map { $0.attributedText } + return mapControlEvents(.allEditingEvents) { $0.attributedText } } /// Sets the secure text entry attribute on the text field. diff --git a/ReactiveCocoa/UIKit/iOS/UIDatePicker.swift b/ReactiveCocoa/UIKit/iOS/UIDatePicker.swift index a1a558a728..356a1bf6bb 100644 --- a/ReactiveCocoa/UIKit/iOS/UIDatePicker.swift +++ b/ReactiveCocoa/UIKit/iOS/UIDatePicker.swift @@ -10,6 +10,6 @@ extension Reactive where Base: UIDatePicker { /// A signal of dates emitted by the date picker. public var dates: Signal { - return controlEvents(.valueChanged).map { $0.date } + return mapControlEvents(.valueChanged) { $0.date } } } diff --git a/ReactiveCocoa/UIKit/iOS/UISlider.swift b/ReactiveCocoa/UIKit/iOS/UISlider.swift index 8744660e35..161fb59a74 100644 --- a/ReactiveCocoa/UIKit/iOS/UISlider.swift +++ b/ReactiveCocoa/UIKit/iOS/UISlider.swift @@ -25,6 +25,6 @@ extension Reactive where Base: UISlider { /// - note: If slider's `isContinuous` property is `false` then values are /// sent only when user releases the slider. public var values: Signal { - return controlEvents(.valueChanged).map { $0.value } + return mapControlEvents(.valueChanged) { $0.value } } } diff --git a/ReactiveCocoa/UIKit/iOS/UIStepper.swift b/ReactiveCocoa/UIKit/iOS/UIStepper.swift index c0c88865d0..44e9a108e3 100644 --- a/ReactiveCocoa/UIKit/iOS/UIStepper.swift +++ b/ReactiveCocoa/UIKit/iOS/UIStepper.swift @@ -22,6 +22,6 @@ extension Reactive where Base: UIStepper { /// A signal of double values emitted by the stepper upon each user's /// interaction. public var values: Signal { - return controlEvents(.valueChanged).map { $0.value } + return mapControlEvents(.valueChanged) { $0.value } } } diff --git a/ReactiveCocoa/UIKit/iOS/UISwitch.swift b/ReactiveCocoa/UIKit/iOS/UISwitch.swift index dec9880a4b..937e4e3a13 100644 --- a/ReactiveCocoa/UIKit/iOS/UISwitch.swift +++ b/ReactiveCocoa/UIKit/iOS/UISwitch.swift @@ -25,6 +25,6 @@ extension Reactive where Base: UISwitch { /// A signal of on-off states in `Bool` emitted by the switch. public var isOnValues: Signal { - return controlEvents(.valueChanged).map { $0.isOn } + return mapControlEvents(.valueChanged) { $0.isOn } } } diff --git a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift index d51409e8de..33a1ad1835 100644 --- a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift @@ -141,6 +141,7 @@ class UITextFieldSpec: QuickSpec { if text == "2" { textField.resignFirstResponder() + textField.text = "3" } } expect(values) == [] From 13629e36af6f26805abab0ec6051854afce21cc9 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Fri, 9 Jun 2017 22:58:47 +0800 Subject: [PATCH 0803/1028] UITextField text signal now reacts to `editingDidEndOnExit`. --- ReactiveCocoa/UIKit/UITextField.swift | 4 +-- .../UIKit/UITextFieldSpec.swift | 26 ++++++++++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/ReactiveCocoa/UIKit/UITextField.swift b/ReactiveCocoa/UIKit/UITextField.swift index b968227f51..38dcf11ddc 100644 --- a/ReactiveCocoa/UIKit/UITextField.swift +++ b/ReactiveCocoa/UIKit/UITextField.swift @@ -13,7 +13,7 @@ extension Reactive where Base: UITextField { /// - note: To observe text values that change on all editing events, /// see `continuousTextValues`. public var textValues: Signal { - return controlEvents(.editingDidEnd).map { $0.text } + return controlEvents([.editingDidEnd, .editingDidEndOnExit]).map { $0.text } } /// A signal of text values emitted by the text field upon any changes. @@ -38,7 +38,7 @@ extension Reactive where Base: UITextField { /// - note: To observe attributed text values that change on all editing events, /// see `continuousAttributedTextValues`. public var attributedTextValues: Signal { - return controlEvents(.editingDidEnd).map { $0.attributedText } + return controlEvents([.editingDidEnd, .editingDidEndOnExit]).map { $0.attributedText } } /// A signal of attributed text values emitted by the text field upon any changes. diff --git a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift index d51409e8de..fa0a37da4d 100644 --- a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift @@ -40,6 +40,18 @@ class UITextFieldSpec: QuickSpec { expect(latestValue) == textField.text } + it("should emit user initiated changes to its text value when the editing ends as a reuslt of the return key being pressed") { + textField.text = "Test" + + var latestValue: String? + textField.reactive.textValues.observeValues { text in + latestValue = text + } + + textField.sendActions(for: .editingDidEndOnExit) + expect(latestValue) == textField.text + } + it("should emit user initiated changes to its text value continuously") { var latestValue: String? textField.reactive.continuousTextValues.observeValues { text in @@ -81,7 +93,19 @@ class UITextFieldSpec: QuickSpec { textField.sendActions(for: .editingDidEnd) expect(latestValue) == textField.attributedText } - + + it("should emit user initiated changes to its attributed text value when the editing ends as a reuslt of the return key being pressed") { + textField.attributedText = NSAttributedString(string: "Test") + + var latestValue: NSAttributedString? + textField.reactive.attributedTextValues.observeValues { attributedText in + latestValue = attributedText + } + + textField.sendActions(for: .editingDidEndOnExit) + expect(latestValue) == textField.attributedText + } + it("should emit user initiated changes to its attributed text value continuously") { var latestValue: NSAttributedString? textField.reactive.continuousAttributedTextValues.observeValues { attributedText in From 9ae308b59685ae8fc886831a029172c74538d423 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 10 Jun 2017 02:22:28 +0800 Subject: [PATCH 0804/1028] Beefed up the descriptions of control event signals. [skip ci] --- ReactiveCocoa/UIKit/UIControl.swift | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index 0f14168c29..0884f2e3a2 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -41,6 +41,9 @@ extension Reactive where Base: UIControl { /// Create a signal which sends a `value` event for each of the specified /// control events. /// + /// - note: If you mean to observe the **value** of `self` with regard to a particular + /// control event, `mapControlEvents(_:_:)` should be used instead. + /// /// - parameters: /// - controlEvents: The control event mask. /// @@ -52,6 +55,16 @@ extension Reactive where Base: UIControl { /// Create a signal which sends a `value` event for each of the specified /// control events. /// + /// - important: You should use `mapControlEvents` in general unless the state of + /// the control — e.g. `text`, `state` — is not concerned. In other + /// words, you should avoid using `map` on a control event signal to + /// extract the state from the control. + /// + /// - note: For observations that could potentially manipulate the first responder + /// status of `base`, `mapControlEvents(_:_:)` is made aware of the potential + /// recursion induced by UIKit and would collect the values for the control + /// events accordingly using the given transform. + /// /// - parameters: /// - controlEvents: The control event mask. /// - transform: A transform to reduce `Base`. From 07aefc0a91210f21adcdc10baf2cc8c57f12488b Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 10 Jun 2017 02:24:48 +0800 Subject: [PATCH 0805/1028] Update the changelog. [skip ci] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae7fb334c2..2297a6987f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # master *Please put new entries at the top. +1. Introduce `mapControlEvents(_:_:)` which is set to replace `controlEvents(_:_:)` in most cases. (#3472) + + You should use `mapControlEvents` in general unless the state of the control — e.g. `text`, `state` — is **not** concerned. In other words, you should avoid using `map` on a control event signal to extract the state from the control. + 1. Resigning first responder when reacting to a `UITextField` signal no longer deadlocks. (#3453, #3472) 1. New operator: `take(duringLifetimeOf:)`. (#3466, kudos to @andersio) From bdbe99fec5d2d11ce8dee45453b00f9d584c9760 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 10 Jun 2017 05:35:12 +0800 Subject: [PATCH 0806/1028] 5.0.4 --- CHANGELOG.md | 3 +++ ReactiveCocoa.podspec | 2 +- ReactiveCocoa/Info.plist | 2 +- ReactiveCocoaTests/Info.plist | 2 +- script/update-version | 24 ++++++++++++++++++++++++ 5 files changed, 30 insertions(+), 3 deletions(-) create mode 100755 script/update-version diff --git a/CHANGELOG.md b/CHANGELOG.md index 2297a6987f..6edfb73c3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # master *Please put new entries at the top. +# 5.0.4 +1. UITextField text signals now react to `editingDidEndOnExit`. (#3474) + 1. Introduce `mapControlEvents(_:_:)` which is set to replace `controlEvents(_:_:)` in most cases. (#3472) You should use `mapControlEvents` in general unless the state of the control — e.g. `text`, `state` — is **not** concerned. In other words, you should avoid using `map` on a control event signal to extract the state from the control. diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 9a00e4a7b5..ed829143a4 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "5.0.3" + s.version = "5.0.4" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveCocoa/Info.plist b/ReactiveCocoa/Info.plist index 8614adfaeb..78e7a15519 100644 --- a/ReactiveCocoa/Info.plist +++ b/ReactiveCocoa/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 5.0.3 + 5.0.4 CFBundleSignature ???? CFBundleVersion diff --git a/ReactiveCocoaTests/Info.plist b/ReactiveCocoaTests/Info.plist index a91394faff..7d55c1f9ce 100644 --- a/ReactiveCocoaTests/Info.plist +++ b/ReactiveCocoaTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 5.0.3 + 5.0.4 CFBundleSignature ???? CFBundleVersion diff --git a/script/update-version b/script/update-version new file mode 100755 index 0000000000..34dbb05892 --- /dev/null +++ b/script/update-version @@ -0,0 +1,24 @@ +#!/bin/bash + +if [[ -z "$1" ]]; then + echo "Please specify a version tag." + exit +fi + +PRERELEASE_STRIPPED=$(echo "$1" | perl -0777 -ne '/([0-9]+)\.([0-9]+)\.([0-9]+)(-.*)?/ and print "$1.$2.$3"') + +if [[ -z "$PRERELEASE_STRIPPED" ]]; then + echo "The version tag is not semver compliant." + exit +fi + +CURRENT_TAG=$(perl -0777 -ne '/s.version([\s]+)=([\s]+)"(.+)"/ and print $3' *.podspec) +echo "Current tag: $CURRENT_TAG" + +perl -0777 -i -pe 's/s.version([\s]+)=([\s]+)"'${CURRENT_TAG}'"/s.version$1=$2"'${1}'"/' *.podspec +perl -0777 -i -pe 's/g>'${CURRENT_TAG}'<\/str/g>'${PRERELEASE_STRIPPED}'<\/str/' */Info.plist +perl -0777 -i -pe 's/g>'${CURRENT_TAG}'<\/str/g>'${PRERELEASE_STRIPPED}'<\/str/' */*/Info.plist +sed -i '' '3i\ +\ +# '${1} CHANGELOG.md + From ae5fd00c193de080fe49d6582603fbd564d1f13c Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sun, 11 Jun 2017 23:09:03 +0800 Subject: [PATCH 0807/1028] Update the release roadmap. [skip ci] --- README.md | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 6a21aa30c0..11c5bcbc3f 100644 --- a/README.md +++ b/README.md @@ -135,23 +135,20 @@ If you need any help, please visit our [GitHub issues][] or [Stack Overflow][]. ## Release Roadmap **Current Stable Release:**
    [![GitHub release](https://img.shields.io/github/release/ReactiveCocoa/ReactiveCocoa.svg)](https://github.com/ReactiveCocoa/ReactiveCocoa/releases) +### In Development #### ReactiveCocoa 6.0 -It targets Swift 3.1.x and ReactiveSwift 2.0. The estimated schedule is Spring 2017. +It targets Swift 3.1 and ReactiveSwift 2.0. The estimated schedule is July 2017. -Since ReactiveSwift 2.0 would contain breaking changes, the public API of ReactiveCocoa is considered breaking too. +ReactiveCocoa 6.0 has continued the work in polishing the APIs, preparing for the eventual API freeze for library resilience. -As resilience would be enforced in Swift 4.0, it is important for us to have a clean and steady API to start with. The expectation is to **have the API reviewing to be concluded in ReactiveCocoa 6.0**, before we move on to ReactiveSwift 3.0 and Swift 4.0. Any contribution to help realising this goal is welcomed. +#### Swift 3.2 and Swift 4.0 compatibility +While the development would be Swift 3.2 and Swift 4.0 aware, ReactiveCocoa 6.0 would not declare official support until Swift 3.2 and Swift 4.0 launch. -#### ReactiveCocoa 7.0 -It targets Swift 4.0.x and ReactiveSwift 3.0. The estimated schedule is late 2017. - -The release may contain breaking changes due to changes in ReactiveSwift 3.0. +The official release for Swift 3.2 and Swift 4.0 is expected to be a minor 6.x release with full API compatibility. -ReactiveCocoa 7.0 would focus on three main goals: - -1. Swift 4.0 Resilience -1. Migration to ReactiveSwift 3.0 -1. Support new features introduced to AppKit, UIKit and Swift. +### Plan of Record +#### ReactiveCocoa 7.0 +ReactiveCocoa 7.0 is expected to declare library ABI stability as it adopts generics features arriving in a later Swift 4 release, e.g. conditional conformance. There is no ETA for now. [ReactiveSwift]: https://github.com/ReactiveCocoa/ReactiveSwift [ReactiveObjC]: https://github.com/ReactiveCocoa/ReactiveObjC From ec5bedbc80062e925d8ae303cbb6da38f489216c Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 10 Jun 2017 06:11:06 +0800 Subject: [PATCH 0808/1028] Update ReactiveSwift to 2.0.0 alpha 2, Result to 3.2.3, and Nimble to 7.0.1. --- Cartfile | 2 +- Cartfile.private | 2 +- Cartfile.resolved | 6 +++--- Carthage/Checkouts/Nimble | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- Carthage/Checkouts/Result | 2 +- ReactiveCocoa.podspec | 2 +- ReactiveCocoa/AppKit/ReusableComponents.swift | 2 +- ReactiveCocoa/CocoaTarget.swift | 15 ++++----------- ReactiveCocoa/NSObject+KeyValueObserving.swift | 15 ++++++++++----- ReactiveCocoa/UIKit/UIControl.swift | 2 +- ReactiveCocoa/UIKit/UIGestureRecognizer.swift | 2 +- ReactiveCocoaTests/KeyValueObservingSpec.swift | 6 +++--- ReactiveCocoaTests/Shared/MKMapViewSpec.swift | 4 +++- ReactiveCocoaTests/TestError.swift | 8 ++++---- ReactiveCocoaTests/UIKit/UITextFieldSpec.swift | 6 ++---- 16 files changed, 38 insertions(+), 40 deletions(-) diff --git a/Cartfile b/Cartfile index fee9fdb6a7..6a4eb7c5f0 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" ~> 1.1 +github "ReactiveCocoa/ReactiveSwift" "2.0.0-alpha.2" diff --git a/Cartfile.private b/Cartfile.private index 43b7f07f26..0d46b9e682 100644 --- a/Cartfile.private +++ b/Cartfile.private @@ -1,3 +1,3 @@ github "jspahrsummers/xcconfigs" "3d9d996" github "Quick/Quick" ~> 1.1 -github "Quick/Nimble" ~> 6.1 +github "Quick/Nimble" ~> 7.0.1 diff --git a/Cartfile.resolved b/Cartfile.resolved index d1ccb5c42b..34a7ed796f 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,5 +1,5 @@ -github "Quick/Nimble" "v6.1.0" +github "Quick/Nimble" "v7.0.1" github "Quick/Quick" "v1.1.0" -github "ReactiveCocoa/ReactiveSwift" "1.1.1" -github "antitypical/Result" "3.2.1" +github "ReactiveCocoa/ReactiveSwift" "2.0.0-alpha.2" +github "antitypical/Result" "3.2.3" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" diff --git a/Carthage/Checkouts/Nimble b/Carthage/Checkouts/Nimble index e3c1a78af3..39b6700230 160000 --- a/Carthage/Checkouts/Nimble +++ b/Carthage/Checkouts/Nimble @@ -1 +1 @@ -Subproject commit e3c1a78af33b3fa778207fcc6ba33221838c0788 +Subproject commit 39b67002306fda9de4c9fd1290a6295f97edd09e diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index 1556b846de..5fce558818 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit 1556b846de627cac0b96291ec2835fbc349c5e21 +Subproject commit 5fce5588184ef61ab4372281096fc62aca7e7930 diff --git a/Carthage/Checkouts/Result b/Carthage/Checkouts/Result index 2af7c14607..c844618523 160000 --- a/Carthage/Checkouts/Result +++ b/Carthage/Checkouts/Result @@ -1 +1 @@ -Subproject commit 2af7c146071c8d8fb3953f19924ecebf15c88ea7 +Subproject commit c8446185238659a2b27c0261f64ff1254291d07d diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index ed829143a4..758d7c5485 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -23,7 +23,7 @@ Pod::Spec.new do |s| s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_map = "ReactiveCocoa/module.modulemap" - s.dependency 'ReactiveSwift', '~> 1.1' + s.dependency 'ReactiveSwift', '2.0.0-alpha.2' s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "-suppress-warnings" } end diff --git a/ReactiveCocoa/AppKit/ReusableComponents.swift b/ReactiveCocoa/AppKit/ReusableComponents.swift index db2e5ec908..3656d9de9f 100644 --- a/ReactiveCocoa/AppKit/ReusableComponents.swift +++ b/ReactiveCocoa/AppKit/ReusableComponents.swift @@ -2,7 +2,7 @@ import AppKit import ReactiveSwift import enum Result.NoError -extension Reactive where Base: NSObject, Base: NSView { +extension Reactive where Base: NSView { public var prepareForReuse: Signal<(), NoError> { return trigger(for: #selector(base.prepareForReuse)) } diff --git a/ReactiveCocoa/CocoaTarget.swift b/ReactiveCocoa/CocoaTarget.swift index 85a92ed6d8..cfa2d9bd8d 100644 --- a/ReactiveCocoa/CocoaTarget.swift +++ b/ReactiveCocoa/CocoaTarget.swift @@ -9,12 +9,12 @@ internal final class CocoaTarget: NSObject { case sending(queue: [Value]) } - private let observer: Observer + private let observer: Signal.Observer private let transform: (Any?) -> Value private var state: State - internal init(_ observer: Observer, transform: @escaping (Any?) -> Value) { + internal init(_ observer: Signal.Observer, transform: @escaping (Any?) -> Value) { self.observer = observer self.transform = transform self.state = .idle @@ -52,15 +52,8 @@ internal final class CocoaTarget: NSObject { } } -internal protocol CocoaTargetProtocol: class { - associatedtype Value - init(_ observer: Observer, transform: @escaping (Any?) -> Value) -} - -extension CocoaTarget: CocoaTargetProtocol {} - -extension CocoaTargetProtocol where Value == Void { - internal init(_ observer: Observer<(), NoError>) { +extension CocoaTarget where Value == Void { + internal convenience init(_ observer: Signal<(), NoError>.Observer) { self.init(observer, transform: { _ in }) } } diff --git a/ReactiveCocoa/NSObject+KeyValueObserving.swift b/ReactiveCocoa/NSObject+KeyValueObserving.swift index 0ec154e0d5..4863f112c2 100644 --- a/ReactiveCocoa/NSObject+KeyValueObserving.swift +++ b/ReactiveCocoa/NSObject+KeyValueObserving.swift @@ -14,14 +14,19 @@ extension Reactive where Base: NSObject { /// - returns: A producer emitting values of the property specified by the /// key path. public func producer(forKeyPath keyPath: String) -> SignalProducer { - return SignalProducer { observer, disposable in - disposable += KeyValueObserver.observe( + return SignalProducer { observer, lifetime in + let disposable = KeyValueObserver.observe( self.base, keyPath: keyPath, options: [.initial, .new], action: observer.send ) - disposable += self.lifetime.observeEnded(observer.sendCompleted) + + lifetime.observeEnded(disposable.dispose) + + if let lifetimeDisposable = self.lifetime.observeEnded(observer.sendCompleted) { + lifetime.observeEnded(lifetimeDisposable.dispose) + } } } @@ -113,7 +118,7 @@ extension KeyValueObserver { keyPath: String, options: NSKeyValueObservingOptions, action: @escaping (_ value: AnyObject?) -> Void - ) -> ActionDisposable { + ) -> AnyDisposable { // Compute the key path head and tail. let components = keyPath.components(separatedBy: ".") precondition(!components.isEmpty, "Received an empty key path.") @@ -207,7 +212,7 @@ extension KeyValueObserver { } } - return ActionDisposable { + return AnyDisposable { observer.detach() headSerialDisposable.dispose() } diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index 0884f2e3a2..8fc347ceed 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -80,7 +80,7 @@ extension Reactive where Base: UIControl { let disposable = lifetime.ended.observeCompleted(observer.sendCompleted) - return ActionDisposable { [weak base = self.base] in + return AnyDisposable { [weak base = self.base] in disposable?.dispose() base?.removeTarget(receiver, diff --git a/ReactiveCocoa/UIKit/UIGestureRecognizer.swift b/ReactiveCocoa/UIKit/UIGestureRecognizer.swift index 9614e6fe14..d9e4b430a1 100644 --- a/ReactiveCocoa/UIKit/UIGestureRecognizer.swift +++ b/ReactiveCocoa/UIKit/UIGestureRecognizer.swift @@ -15,7 +15,7 @@ extension Reactive where Base: UIGestureRecognizer { let disposable = lifetime.ended.observeCompleted(observer.sendCompleted) - return ActionDisposable { [weak base = self.base] in + return AnyDisposable { [weak base = self.base] in disposable?.dispose() base?.removeTarget(receiver, action: #selector(receiver.invoke)) } diff --git a/ReactiveCocoaTests/KeyValueObservingSpec.swift b/ReactiveCocoaTests/KeyValueObservingSpec.swift index 2a3ac4feb9..5cc89d1f7b 100644 --- a/ReactiveCocoaTests/KeyValueObservingSpec.swift +++ b/ReactiveCocoaTests/KeyValueObservingSpec.swift @@ -83,7 +83,7 @@ class KeyValueObservingSpec: QuickSpec { it("should be able to classify weak references") { "weakProperty".withCString { cString in let propertyPointer = class_getProperty(type(of: object), cString) - expect(propertyPointer) != nil + expect(propertyPointer).toNot(beNil()) if let pointer = propertyPointer { let attributes = PropertyAttributes(property: pointer) @@ -98,7 +98,7 @@ class KeyValueObservingSpec: QuickSpec { it("should be able to classify blocks") { "block".withCString { cString in let propertyPointer = class_getProperty(type(of: object), cString) - expect(propertyPointer) != nil + expect(propertyPointer).toNot(beNil()) if let pointer = propertyPointer { let attributes = PropertyAttributes(property: pointer) @@ -113,7 +113,7 @@ class KeyValueObservingSpec: QuickSpec { it("should be able to classify non object properties") { "integer".withCString { cString in let propertyPointer = class_getProperty(type(of: object), cString) - expect(propertyPointer) != nil + expect(propertyPointer).toNot(beNil()) if let pointer = propertyPointer { let attributes = PropertyAttributes(property: pointer) diff --git a/ReactiveCocoaTests/Shared/MKMapViewSpec.swift b/ReactiveCocoaTests/Shared/MKMapViewSpec.swift index 506cdec6a3..f5e52640b2 100644 --- a/ReactiveCocoaTests/Shared/MKMapViewSpec.swift +++ b/ReactiveCocoaTests/Shared/MKMapViewSpec.swift @@ -17,7 +17,9 @@ class MKMapViewSpec: QuickSpec { } afterEach { - mapView = nil + autoreleasepool { + mapView = nil + } // FIXME: SDK_ISSUE // // Temporarily disabled since the expectation keeps failing with diff --git a/ReactiveCocoaTests/TestError.swift b/ReactiveCocoaTests/TestError.swift index 36067c76fc..2a89e4daba 100644 --- a/ReactiveCocoaTests/TestError.swift +++ b/ReactiveCocoaTests/TestError.swift @@ -12,21 +12,21 @@ extension TestError: Error { } -internal extension SignalProducerProtocol { +internal extension SignalProducer { /// Halts if an error is emitted in the receiver signal. /// This is useful in tests to be able to just use `startWithNext` /// in cases where we know that an error won't be emitted. func assumeNoErrors() -> SignalProducer { - return self.lift { $0.assumeNoErrors() } + return lift { $0.assumeNoErrors() } } } -internal extension SignalProtocol { +internal extension Signal { /// Halts if an error is emitted in the receiver signal. /// This is useful in tests to be able to just use `startWithNext` /// in cases where we know that an error won't be emitted. func assumeNoErrors() -> Signal { - return self.mapError { error in + return mapError { error in fatalError("Unexpected error: \(error)") () diff --git a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift index 5cbbb3bce4..2aa921c771 100644 --- a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift @@ -22,10 +22,8 @@ class UITextFieldSpec: QuickSpec { textField = nil } - // For a unknown reason, the text field in the "should not deadlock" test case - // does not deallocate when the autorelease pool is drained, but only a while - // after. - expect(_textField).toEventually(beNil()) + // FIXME: iOS 11.0 SDK beta 1 + // expect(_textField).toEventually(beNil()) } it("should emit user initiated changes to its text value when the editing ends") { From 48ac78f4d1b5cb9fc3cf596eedc726aee939f70a Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Mon, 12 Jun 2017 13:13:13 +0800 Subject: [PATCH 0809/1028] Verbose Pod spec repo update. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 42d1adbd3f..5cef17164e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,7 +58,7 @@ matrix: - JOB=CARTHAGE-watchOS - script: - gem install cocoapods - - pod repo update --silent + - pod repo update - pod lib lint ReactiveCocoa.podspec env: - JOB=PODSPEC From 13f30bb79fb37fa8c79ef1885d6f2e355dac3da0 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Mon, 12 Jun 2017 21:47:48 +0800 Subject: [PATCH 0810/1028] 6.0.0-alpha.1 --- CHANGELOG.md | 1 + ReactiveCocoa.podspec | 2 +- ReactiveCocoa/Info.plist | 2 +- ReactiveCocoaTests/Info.plist | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6edfb73c3c..e7743b094a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master *Please put new entries at the top. +# 6.0.0-alpha.1 # 5.0.4 1. UITextField text signals now react to `editingDidEndOnExit`. (#3474) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 758d7c5485..76d899bdef 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "5.0.4" + s.version = "6.0.0-alpha.1" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveCocoa/Info.plist b/ReactiveCocoa/Info.plist index 78e7a15519..cf1cd64e76 100644 --- a/ReactiveCocoa/Info.plist +++ b/ReactiveCocoa/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 5.0.4 + 6.0.0 CFBundleSignature ???? CFBundleVersion diff --git a/ReactiveCocoaTests/Info.plist b/ReactiveCocoaTests/Info.plist index 7d55c1f9ce..35cbe3fd57 100644 --- a/ReactiveCocoaTests/Info.plist +++ b/ReactiveCocoaTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 5.0.4 + 6.0.0 CFBundleSignature ???? CFBundleVersion From 11fe7305b76f43d1612ca1dd2c09ff6d7741b813 Mon Sep 17 00:00:00 2001 From: Greg Parker Date: Mon, 12 Jun 2017 15:42:58 -0700 Subject: [PATCH 0811/1028] Make a KeyValueObservingSpec test be independent of the iteration count --- ReactiveCocoaTests/KeyValueObservingSpec.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoaTests/KeyValueObservingSpec.swift b/ReactiveCocoaTests/KeyValueObservingSpec.swift index 5cc89d1f7b..8297aba756 100644 --- a/ReactiveCocoaTests/KeyValueObservingSpec.swift +++ b/ReactiveCocoaTests/KeyValueObservingSpec.swift @@ -556,7 +556,7 @@ fileprivate class KeyValueObservingSpecConfiguration: QuickConfiguration { testObject.rac_value = 2 - expect(atomicCounter).toEventually(equal(10000), timeout: 30.0) + expect(atomicCounter).toEventually(equal(Int64(numIterations * 2)), timeout: 30.0) } // ReactiveCocoa/ReactiveCocoa#1122 From 81da25817123b0565471d9b193af1392084ef842 Mon Sep 17 00:00:00 2001 From: Igor Palaguta Date: Tue, 13 Jun 2017 13:38:06 +0300 Subject: [PATCH 0812/1028] Add ability to monitor selectedRange for UITextView --- ReactiveCocoa/UIKit/UITextView.swift | 18 ++++++++++++++++++ ReactiveCocoaTests/UIKit/UITextViewSpec.swift | 14 ++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/ReactiveCocoa/UIKit/UITextView.swift b/ReactiveCocoa/UIKit/UITextView.swift index 7926d2adba..c0472942ea 100644 --- a/ReactiveCocoa/UIKit/UITextView.swift +++ b/ReactiveCocoa/UIKit/UITextView.swift @@ -2,7 +2,19 @@ import ReactiveSwift import UIKit import enum Result.NoError +private class TextViewDelegateProxy: DelegateProxy, UITextViewDelegate { + @objc func textViewDidChangeSelection(_ textView: UITextView) { + forwardee?.textViewDidChangeSelection?(textView) + } +} + extension Reactive where Base: UITextView { + private var proxy: TextViewDelegateProxy { + return .proxy(for: base, + setter: #selector(setter: base.delegate), + getter: #selector(getter: base.delegate)) + } + /// Sets the text of the text view. public var text: BindingTarget { return makeBindingTarget { $0.text = $1 } @@ -58,4 +70,10 @@ extension Reactive where Base: UITextView { public var continuousAttributedTextValues: Signal { return attributedTextValues(forName: .UITextViewTextDidChange) } + + /// A signal of range values emitted by the text view upon any selection change. + public var selectedRangeValues: Signal { + return proxy.intercept(#selector(UITextViewDelegate.textViewDidChangeSelection)) + .map { [unowned base] in base.selectedRange } + } } diff --git a/ReactiveCocoaTests/UIKit/UITextViewSpec.swift b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift index 413f0464f2..26a8af31f9 100644 --- a/ReactiveCocoaTests/UIKit/UITextViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift @@ -91,5 +91,19 @@ class UITextViewSpec: QuickSpec { NotificationCenter.default.post(name: .UITextViewTextDidChange, object: textView) expect(latestValue) == textView.attributedText } + + it("should emit user initiated changes for selection") { + var latestValue: NSRange! + textView.reactive.selectedRangeValues.observeValues { + latestValue = $0 + } + + textView.text = "Test" + textView.selectedRange = NSRange(location: 1, length: 2) + + textView.delegate!.textViewDidChangeSelection!(textView) + expect(latestValue.location) == 1 + expect(latestValue.length) == 2 + } } } From 1b588c05f21b6d8e50fb021bfeff2721c902e5b6 Mon Sep 17 00:00:00 2001 From: Andrei Kuzma Date: Fri, 16 Jun 2017 12:09:36 +0300 Subject: [PATCH 0813/1028] Fixed issue with UIButton pressed action on tvos --- ReactiveCocoa/UIKit/UIButton.swift | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoa/UIKit/UIButton.swift b/ReactiveCocoa/UIKit/UIButton.swift index 2c6486819d..b2c2b508ac 100644 --- a/ReactiveCocoa/UIKit/UIButton.swift +++ b/ReactiveCocoa/UIKit/UIButton.swift @@ -8,16 +8,24 @@ extension Reactive where Base: UIButton { get { return associatedAction.withValue { info in return info.flatMap { info in - return info.controlEvents == .touchUpInside ? info.action : nil + return info.controlEvents == pressEvent ? info.action : nil } } } nonmutating set { - setAction(newValue, for: .touchUpInside) + setAction(newValue, for: pressEvent) } } + private var pressEvent: UIControlEvents { + #if os(tvOS) + return .primaryActionTriggered + #else + return .touchUpInside + #endif + } + /// Sets the title of the button for its normal state. public var title: BindingTarget { return makeBindingTarget { $0.setTitle($1, for: .normal) } From a2af302541100de14ee05a9012627be1de4bf415 Mon Sep 17 00:00:00 2001 From: Andrei Kuzma Date: Sat, 17 Jun 2017 11:32:33 +0300 Subject: [PATCH 0814/1028] Updated UIButtonSpec --- ReactiveCocoa/UIKit/UIButton.swift | 10 +++++----- ReactiveCocoaTests/UIKit/UIButtonSpec.swift | 18 +++++++++++++++--- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/ReactiveCocoa/UIKit/UIButton.swift b/ReactiveCocoa/UIKit/UIButton.swift index b2c2b508ac..6c5594e0b3 100644 --- a/ReactiveCocoa/UIKit/UIButton.swift +++ b/ReactiveCocoa/UIKit/UIButton.swift @@ -19,11 +19,11 @@ extension Reactive where Base: UIButton { } private var pressEvent: UIControlEvents { - #if os(tvOS) - return .primaryActionTriggered - #else - return .touchUpInside - #endif + if #available(iOS 9.0, tvOS 9.0, *) { + return .primaryActionTriggered + } else { + return .touchUpInside + } } /// Sets the title of the button for its normal state. diff --git a/ReactiveCocoaTests/UIKit/UIButtonSpec.swift b/ReactiveCocoaTests/UIKit/UIButtonSpec.swift index a51b58354a..657b0b5436 100644 --- a/ReactiveCocoaTests/UIKit/UIButtonSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIButtonSpec.swift @@ -40,7 +40,7 @@ class UIButtonSpec: QuickSpec { expect(button.title(for: .selected)) == "" } - it("should execute the `pressed` action upon receiving a `touchUpInside` action message.") { + let pressedTest: (UIButton, UIControlEvents) -> Void = { button, event in button.isEnabled = true button.isUserInteractionEnabled = true @@ -53,9 +53,21 @@ class UIButtonSpec: QuickSpec { button.reactive.pressed = CocoaAction(action) expect(pressed.value) == false - - button.sendActions(for: .touchUpInside) + + button.sendActions(for: event) + expect(pressed.value) == true } + + if #available(iOS 9.0, tvOS 9.0, *) { + it("should execute the `pressed` action upon receiving a `primaryActionTriggered` action message.") { + pressedTest(button, .primaryActionTriggered) + } + } else { + it("should execute the `pressed` action upon receiving a `touchUpInside` action message.") { + pressedTest(button, .touchUpInside) + } + } } } + From 5266c1c14d82c5426e08dd06ead463b98a5b9e3c Mon Sep 17 00:00:00 2001 From: Charlotte Tortorella Date: Mon, 19 Jun 2017 13:24:50 +1000 Subject: [PATCH 0815/1028] Add scrollsToTop as a BindingTarget on UIScrollView --- ReactiveCocoa/UIKit/UIScrollView.swift | 5 +++++ ReactiveCocoaTests/UIKit/UIScrollViewSpec.swift | 13 +++++++++++++ 2 files changed, 18 insertions(+) diff --git a/ReactiveCocoa/UIKit/UIScrollView.swift b/ReactiveCocoa/UIKit/UIScrollView.swift index 384c9dc214..aac3ee3f30 100644 --- a/ReactiveCocoa/UIKit/UIScrollView.swift +++ b/ReactiveCocoa/UIKit/UIScrollView.swift @@ -31,4 +31,9 @@ extension Reactive where Base: UIScrollView { public var maximumZoomScale: BindingTarget { return makeBindingTarget { $0.maximumZoomScale = $1 } } + + /// Sets whether the scroll view scrolls to the top when the menu is tapped. + public var scrollsToTop: BindingTarget { + return makeBindingTarget { $0.scrollsToTop = $1 } + } } diff --git a/ReactiveCocoaTests/UIKit/UIScrollViewSpec.swift b/ReactiveCocoaTests/UIKit/UIScrollViewSpec.swift index 745a15bae4..0117ba1d65 100644 --- a/ReactiveCocoaTests/UIKit/UIScrollViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIScrollViewSpec.swift @@ -107,5 +107,18 @@ class UIScrollViewSpec: QuickSpec { observer.send(value: 0) expect(scrollView.maximumZoomScale) == 0 } + + it("should accept changes from bindings to its scrolls to top state") { + scrollView.scrollsToTop = true + + let (pipeSignal, observer) = Signal.pipe() + scrollView.reactive.scrollsToTop <~ SignalProducer(pipeSignal) + + observer.send(value: true) + expect(scrollView.scrollsToTop) == true + + observer.send(value: false) + expect(scrollView.scrollsToTop) == false + } } } From 23196b538db4b55179e94111aee2920e813daae5 Mon Sep 17 00:00:00 2001 From: Charlotte Tortorella Date: Wed, 21 Jun 2017 10:32:13 +1000 Subject: [PATCH 0816/1028] Add @unavailable annotation for tvOS --- ReactiveCocoa/UIKit/UIScrollView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/ReactiveCocoa/UIKit/UIScrollView.swift b/ReactiveCocoa/UIKit/UIScrollView.swift index aac3ee3f30..859f569ce7 100644 --- a/ReactiveCocoa/UIKit/UIScrollView.swift +++ b/ReactiveCocoa/UIKit/UIScrollView.swift @@ -33,6 +33,7 @@ extension Reactive where Base: UIScrollView { } /// Sets whether the scroll view scrolls to the top when the menu is tapped. + @available(tvOS, unavailable) public var scrollsToTop: BindingTarget { return makeBindingTarget { $0.scrollsToTop = $1 } } From 2b9d1ce6fd4b2413db97ceb282170c668ef10ddf Mon Sep 17 00:00:00 2001 From: Charlotte Tortorella Date: Wed, 21 Jun 2017 10:45:50 +1000 Subject: [PATCH 0817/1028] Change annotation to fit style --- ReactiveCocoa/UIKit/UIScrollView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa/UIKit/UIScrollView.swift b/ReactiveCocoa/UIKit/UIScrollView.swift index 859f569ce7..616d5c883c 100644 --- a/ReactiveCocoa/UIKit/UIScrollView.swift +++ b/ReactiveCocoa/UIKit/UIScrollView.swift @@ -33,7 +33,7 @@ extension Reactive where Base: UIScrollView { } /// Sets whether the scroll view scrolls to the top when the menu is tapped. - @available(tvOS, unavailable) + @available(iOS 2.0, *) public var scrollsToTop: BindingTarget { return makeBindingTarget { $0.scrollsToTop = $1 } } From 6f1c48a1c7773fb37997e5ade058d52cf058a7d4 Mon Sep 17 00:00:00 2001 From: Charlotte Tortorella Date: Wed, 21 Jun 2017 12:49:44 +1000 Subject: [PATCH 0818/1028] Change available to conditional compilation directive --- ReactiveCocoa/UIKit/UIScrollView.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoa/UIKit/UIScrollView.swift b/ReactiveCocoa/UIKit/UIScrollView.swift index 616d5c883c..cb087fd227 100644 --- a/ReactiveCocoa/UIKit/UIScrollView.swift +++ b/ReactiveCocoa/UIKit/UIScrollView.swift @@ -31,10 +31,11 @@ extension Reactive where Base: UIScrollView { public var maximumZoomScale: BindingTarget { return makeBindingTarget { $0.maximumZoomScale = $1 } } - + + #if os(iOS) /// Sets whether the scroll view scrolls to the top when the menu is tapped. - @available(iOS 2.0, *) public var scrollsToTop: BindingTarget { return makeBindingTarget { $0.scrollsToTop = $1 } } + #endif } From d1344dd2f84ad2e32358bea1f1a210f5040c74ab Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Tue, 27 Jun 2017 15:50:38 +0800 Subject: [PATCH 0819/1028] Update ReactiveSwift to 2.0.0-rc.1. --- Cartfile | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- ReactiveCocoa.podspec | 2 +- ReactiveCocoaTests/LifetimeSpec.swift | 80 +++++++++++++++------------ 5 files changed, 48 insertions(+), 40 deletions(-) diff --git a/Cartfile b/Cartfile index 6a4eb7c5f0..7c55560ad7 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" "2.0.0-alpha.2" +github "ReactiveCocoa/ReactiveSwift" "2.0.0-rc.1" diff --git a/Cartfile.resolved b/Cartfile.resolved index 34a7ed796f..df118d2cad 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,5 +1,5 @@ github "Quick/Nimble" "v7.0.1" github "Quick/Quick" "v1.1.0" -github "ReactiveCocoa/ReactiveSwift" "2.0.0-alpha.2" +github "ReactiveCocoa/ReactiveSwift" "2.0.0-rc.1" github "antitypical/Result" "3.2.3" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index 5fce558818..376c78aa58 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit 5fce5588184ef61ab4372281096fc62aca7e7930 +Subproject commit 376c78aa5850757b0654ea654a4cc70d1dad01ef diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 76d899bdef..ac3b7afcad 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -23,7 +23,7 @@ Pod::Spec.new do |s| s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_map = "ReactiveCocoa/module.modulemap" - s.dependency 'ReactiveSwift', '2.0.0-alpha.2' + s.dependency 'ReactiveSwift', '2.0.0-rc.1' s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "-suppress-warnings" } end diff --git a/ReactiveCocoaTests/LifetimeSpec.swift b/ReactiveCocoaTests/LifetimeSpec.swift index 4b4a0661c5..9e8f91bd39 100644 --- a/ReactiveCocoaTests/LifetimeSpec.swift +++ b/ReactiveCocoaTests/LifetimeSpec.swift @@ -56,19 +56,21 @@ class LifetimeSpec: QuickSpec { weak var weakObject = object var isCompleted = false - let (signal, _) = Signal<(), NoError>.pipe() + let (signal, observer) = Signal<(), NoError>.pipe() - signal - .take(duringLifetimeOf: object!) - .observeCompleted { isCompleted = true } + withExtendedLifetime(observer) { + signal + .take(duringLifetimeOf: object!) + .observeCompleted { isCompleted = true } - expect(weakObject).toNot(beNil()) - expect(isCompleted) == false + expect(weakObject).toNot(beNil()) + expect(isCompleted) == false - object = nil + object = nil - expect(weakObject).to(beNil()) - expect(isCompleted) == true + expect(weakObject).to(beNil()) + expect(isCompleted) == true + } } it("should work with native Swift objects") { @@ -76,19 +78,21 @@ class LifetimeSpec: QuickSpec { weak var weakObject = object var isCompleted = false - let (signal, _) = Signal<(), NoError>.pipe() + let (signal, observer) = Signal<(), NoError>.pipe() - signal - .take(duringLifetimeOf: object!) - .observeCompleted { isCompleted = true } + withExtendedLifetime(observer) { + signal + .take(duringLifetimeOf: object!) + .observeCompleted { isCompleted = true } - expect(weakObject).toNot(beNil()) - expect(isCompleted) == false + expect(weakObject).toNot(beNil()) + expect(isCompleted) == false - object = nil + object = nil - expect(weakObject).to(beNil()) - expect(isCompleted) == true + expect(weakObject).to(beNil()) + expect(isCompleted) == true + } } } @@ -98,19 +102,21 @@ class LifetimeSpec: QuickSpec { weak var weakObject = object var isCompleted = false - let (signal, _) = Signal<(), NoError>.pipe() + let (signal, observer) = Signal<(), NoError>.pipe() - SignalProducer(signal) - .take(duringLifetimeOf: object!) - .startWithCompleted { isCompleted = true } + withExtendedLifetime(observer) { + SignalProducer(signal) + .take(duringLifetimeOf: object!) + .startWithCompleted { isCompleted = true } - expect(weakObject).toNot(beNil()) - expect(isCompleted) == false + expect(weakObject).toNot(beNil()) + expect(isCompleted) == false - object = nil + object = nil - expect(weakObject).to(beNil()) - expect(isCompleted) == true + expect(weakObject).to(beNil()) + expect(isCompleted) == true + } } it("should work with native Swift objects") { @@ -118,19 +124,21 @@ class LifetimeSpec: QuickSpec { weak var weakObject = object var isCompleted = false - let (signal, _) = Signal<(), NoError>.pipe() + let (signal, observer) = Signal<(), NoError>.pipe() - SignalProducer(signal) - .take(duringLifetimeOf: object!) - .startWithCompleted { isCompleted = true } + withExtendedLifetime(observer) { + SignalProducer(signal) + .take(duringLifetimeOf: object!) + .startWithCompleted { isCompleted = true } - expect(weakObject).toNot(beNil()) - expect(isCompleted) == false + expect(weakObject).toNot(beNil()) + expect(isCompleted) == false - object = nil + object = nil - expect(weakObject).to(beNil()) - expect(isCompleted) == true + expect(weakObject).to(beNil()) + expect(isCompleted) == true + } } } } From 781241e246e29e5814f4f21f164b38fbaabe6c1f Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Wed, 28 Jun 2017 02:51:54 +0800 Subject: [PATCH 0820/1028] 6.0.0-rc.1 --- CHANGELOG.md | 1 + ReactiveCocoa.podspec | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7743b094a..e58d71f1f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master *Please put new entries at the top. +# 6.0.0-rc.1 # 6.0.0-alpha.1 # 5.0.4 1. UITextField text signals now react to `editingDidEndOnExit`. (#3474) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index ac3b7afcad..068e995feb 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "6.0.0-alpha.1" + s.version = "6.0.0-rc.1" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. From df54ea5943c1df8c021287b167e69c5935cc85ab Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Wed, 28 Jun 2017 03:03:02 +0800 Subject: [PATCH 0821/1028] Update changelog. [skip ci] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e58d71f1f5..bf65a6d957 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ *Please put new entries at the top. # 6.0.0-rc.1 +1. `UIButton.pressed` now reacts to the `primaryActionTriggered` control event on iOS 9.0+ and tvOS 9.0+. (#3480, kudos to @andrei-kuzma) + +1. New reactive extension: `UITextField.reactive.selectedRangeValues`. (#3479, kudos to @Igor-Palaguta) + # 6.0.0-alpha.1 # 5.0.4 1. UITextField text signals now react to `editingDidEndOnExit`. (#3474) From ff74f2d7cf2172976d7785e7253abf4d6223ae1f Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Wed, 28 Jun 2017 03:03:49 +0800 Subject: [PATCH 0822/1028] Fixed typos in the changelog. [skip ci] --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf65a6d957..7695df68a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ *Please put new entries at the top. # 6.0.0-rc.1 -1. `UIButton.pressed` now reacts to the `primaryActionTriggered` control event on iOS 9.0+ and tvOS 9.0+. (#3480, kudos to @andrei-kuzma) +1. `UIButton.reactive.pressed` now reacts to the `primaryActionTriggered` control event, instead of `touchUpInside`, on iOS 9.0+ and tvOS 9.0+. (#3480, kudos to @andrei-kuzma) 1. New reactive extension: `UITextField.reactive.selectedRangeValues`. (#3479, kudos to @Igor-Palaguta) From 7157f0779e084dd54afaa44e5d8d933d8a62a76c Mon Sep 17 00:00:00 2001 From: Nick Domenicali Date: Mon, 3 Jul 2017 12:29:05 -0400 Subject: [PATCH 0823/1028] Replace NSObject with NSObjectProtocol in extensions --- ReactiveCocoa/NSObject+Association.swift | 6 +++--- ReactiveCocoa/NSObject+BindingTarget.swift | 2 +- ReactiveCocoa/NSObject+Lifetime.swift | 2 +- ReactiveCocoa/NSObject+Synchronizing.swift | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ReactiveCocoa/NSObject+Association.swift b/ReactiveCocoa/NSObject+Association.swift index 7740c47106..06d35344c1 100644 --- a/ReactiveCocoa/NSObject+Association.swift +++ b/ReactiveCocoa/NSObject+Association.swift @@ -48,7 +48,7 @@ internal struct Associations { } } -extension Reactive where Base: NSObject { +extension Reactive where Base: NSObjectProtocol { /// Retrieve the associated value for the specified key. If the value does not /// exist, `initial` would be called and the returned value would be /// associated subsequently. @@ -72,8 +72,8 @@ extension Reactive where Base: NSObject { } } -extension NSObject { - @nonobjc internal var associations: Associations { +extension NSObjectProtocol { + @nonobjc internal var associations: Associations { return Associations(self) } } diff --git a/ReactiveCocoa/NSObject+BindingTarget.swift b/ReactiveCocoa/NSObject+BindingTarget.swift index c4640305f2..239075d262 100644 --- a/ReactiveCocoa/NSObject+BindingTarget.swift +++ b/ReactiveCocoa/NSObject+BindingTarget.swift @@ -1,7 +1,7 @@ import Foundation import ReactiveSwift -extension Reactive where Base: NSObject { +extension Reactive where Base: NSObjectProtocol { /// Creates a binding target which uses the lifetime of the object, and /// weakly references the object so that the supplied `action` is triggered /// only if the object has not deinitialized. diff --git a/ReactiveCocoa/NSObject+Lifetime.swift b/ReactiveCocoa/NSObject+Lifetime.swift index 92cae58338..c5daa70658 100644 --- a/ReactiveCocoa/NSObject+Lifetime.swift +++ b/ReactiveCocoa/NSObject+Lifetime.swift @@ -31,7 +31,7 @@ internal func lifetime(of object: AnyObject) -> Lifetime { } } -extension Reactive where Base: NSObject { +extension Reactive where Base: NSObjectProtocol { /// Returns a lifetime that ends when the object is deallocated. @nonobjc public var lifetime: Lifetime { return base.synchronized { diff --git a/ReactiveCocoa/NSObject+Synchronizing.swift b/ReactiveCocoa/NSObject+Synchronizing.swift index 02e6e8d8d2..310d9e78fa 100644 --- a/ReactiveCocoa/NSObject+Synchronizing.swift +++ b/ReactiveCocoa/NSObject+Synchronizing.swift @@ -1,5 +1,5 @@ -extension NSObject { - @nonobjc internal final func synchronized(execute: () throws -> Result) rethrows -> Result { +extension NSObjectProtocol { + internal final func synchronized(execute: () throws -> Result) rethrows -> Result { objc_sync_enter(self) defer { objc_sync_exit(self) } return try execute() From 4ca17d6233b4958ffac110a1047ae0fdb6905c6f Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Thu, 6 Jul 2017 03:03:48 +0800 Subject: [PATCH 0824/1028] Update ReactiveSwift to 2.0.0-rc.2. --- Cartfile | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- ReactiveCocoa.podspec | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cartfile b/Cartfile index 7c55560ad7..2e44b69704 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" "2.0.0-rc.1" +github "ReactiveCocoa/ReactiveSwift" "2.0.0-rc.2" diff --git a/Cartfile.resolved b/Cartfile.resolved index df118d2cad..fd11505856 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,5 +1,5 @@ github "Quick/Nimble" "v7.0.1" github "Quick/Quick" "v1.1.0" -github "ReactiveCocoa/ReactiveSwift" "2.0.0-rc.1" +github "ReactiveCocoa/ReactiveSwift" "2.0.0-rc.2" github "antitypical/Result" "3.2.3" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index 376c78aa58..cd4eb9e9a8 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit 376c78aa5850757b0654ea654a4cc70d1dad01ef +Subproject commit cd4eb9e9a86bffdb807b7027c02b9fb154ee6ba8 diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 068e995feb..daffea89b8 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -23,7 +23,7 @@ Pod::Spec.new do |s| s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_map = "ReactiveCocoa/module.modulemap" - s.dependency 'ReactiveSwift', '2.0.0-rc.1' + s.dependency 'ReactiveSwift', '2.0.0-rc.2' s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "-suppress-warnings" } end From 3d60e589ba8dba63595f2c15ce4d03d16b5d11b9 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Thu, 6 Jul 2017 03:11:01 +0800 Subject: [PATCH 0825/1028] Update changelog. [skip ci] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7695df68a5..763c622174 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # master *Please put new entries at the top. +1. `NSObject` reactive extensions now work in generic environments that are limited to `NSObjectProtocol`. (#3484, kudos to @nickdomenicali) + +1. New reactive extension for `UIScrollView`: `scrollsToTop`. (#3481, kudos to @Qata) + # 6.0.0-rc.1 1. `UIButton.reactive.pressed` now reacts to the `primaryActionTriggered` control event, instead of `touchUpInside`, on iOS 9.0+ and tvOS 9.0+. (#3480, kudos to @andrei-kuzma) From 836f742b35efd24fc1014d3a989efeb6ede06a80 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Thu, 6 Jul 2017 03:12:01 +0800 Subject: [PATCH 0826/1028] Copy the PR template from ReactiveSwift. [skip ci] --- .github/PULL_REQUEST_TEMPLATE.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..3ddb9d4205 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,3 @@ + +#### Checklist +- [ ] Updated CHANGELOG.md. From 5912f5376f9c19bb9f476873895d628002947590 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Thu, 6 Jul 2017 18:05:52 +0800 Subject: [PATCH 0827/1028] 6.0.0-rc.2 --- CHANGELOG.md | 1 + ReactiveCocoa.podspec | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 763c622174..41ed9c1b7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master *Please put new entries at the top. +# 6.0.0-rc.2 1. `NSObject` reactive extensions now work in generic environments that are limited to `NSObjectProtocol`. (#3484, kudos to @nickdomenicali) 1. New reactive extension for `UIScrollView`: `scrollsToTop`. (#3481, kudos to @Qata) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index daffea89b8..8c14683baa 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "6.0.0-rc.1" + s.version = "6.0.0-rc.2" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. From a5689a4f015bdcffa6adcde55b5a4c8b3b9ba2b5 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 8 Jul 2017 16:38:04 +0800 Subject: [PATCH 0828/1028] Add Xcode 9 CI tests. --- .travis.yml | 57 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5cef17164e..92a9d29704 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ language: objective-c osx_image: xcode8.3 -before_install: true +before_install: + - brew uninstall carthage + - HOMEBREW_NO_AUTO_UPDATE=1 brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/6ae4f69a652fb0ecb102b0c9216378679a4f1b92/Formula/carthage.rb # 0.22.0 install: true git: submodules: false @@ -12,8 +14,11 @@ before_script: script: - script/build xcode_workspace: ReactiveCocoa.xcworkspace +cache: + directories: Carthage/Build matrix: include: + # Xcode 8 - xcode_scheme: ReactiveCocoa-macOS env: - XCODE_SDK=macosx @@ -40,28 +45,54 @@ matrix: - XCODE_SDK=watchsimulator - XCODE_ACTION=build - XCODE_DESTINATION="platform=watchOS Simulator,name=Apple Watch - 38mm,OS=2.2" - - script: - - carthage build --no-skip-current --platform mac + # Xcode 9 + - xcode_scheme: ReactiveCocoa-macOS + osx_image: xcode9 env: - - JOB=CARTHAGE-macOS - - script: - - carthage build --no-skip-current --platform iOS + - XCODE_SDK=macosx + - XCODE_ACTION="build test" + - XCODE_DESTINATION="arch=x86_64" + - XCODE_PLAYGROUND_TARGET="x86_64-apple-macosx10.10" + - PLAYGROUND="ReactiveCocoa-macOS.playground" + - xcode_scheme: ReactiveCocoa-iOS + osx_image: xcode9 env: - - JOB=CARTHAGE-iOS - - script: - - carthage build --no-skip-current --platform tvOS + - XCODE_SDK=iphonesimulator + - XCODE_ACTION="build-for-testing test-without-building" + - XCODE_DESTINATION="platform=iOS Simulator,name=iPhone 6s" +# - XCODE_PLAYGROUND_TARGET="x86_64-apple-ios9.3" +# - PLAYGROUND="ReactiveCocoa-iOS.playground" + - xcode_scheme: ReactiveCocoa-tvOS + osx_image: xcode9 env: - - JOB=CARTHAGE-tvOS - - script: - - carthage build --no-skip-current --platform watchOS + - XCODE_SDK=appletvsimulator + - XCODE_ACTION="build-for-testing test-without-building" + - XCODE_DESTINATION="platform=tvOS Simulator,name=Apple TV 1080p" +# - XCODE_PLAYGROUND_TARGET="x86_64-apple-tvos9.3" +# - PLAYGROUND="ReactiveCocoa-tvOS.playground" + - xcode_scheme: ReactiveCocoa-watchOS + osx_image: xcode9 env: - - JOB=CARTHAGE-watchOS + - XCODE_SDK=watchsimulator + - XCODE_ACTION=build + - XCODE_DESTINATION="platform=watchOS Simulator,name=Apple Watch - 38mm,OS=2.2" - script: - gem install cocoapods - pod repo update - pod lib lint ReactiveCocoa.podspec env: - JOB=PODSPEC + - stage: prepare carthage cache + script: carthage build --cache-builds + - stage: carthage + script: + - carthage build --cache-builds --no-skip-current --platform mac + - script: + - carthage build --cache-builds --no-skip-current --platform iOS + - script: + - carthage build --cache-builds --no-skip-current --platform tvOS + - script: + - carthage build --cache-builds --no-skip-current --platform watchOS notifications: email: false slack: From d2ed0b880edee417c0013aa2dad8d850f8151053 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 8 Jul 2017 11:20:03 +0800 Subject: [PATCH 0829/1028] Reactive key path subscript. --- CHANGELOG.md | 7 ++++ ReactiveCocoa.xcodeproj/project.pbxproj | 8 ++++ ReactiveCocoa/NSObject+BindingTarget.swift | 26 ++++++++++++ ReactiveCocoa/NSObject+Lifetime.swift | 2 +- ReactiveCocoaTests/BindingTargetSpec.swift | 47 ++++++++++++++++++++++ 5 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 ReactiveCocoaTests/BindingTargetSpec.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 41ed9c1b7e..d5214d5963 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ # master *Please put new entries at the top. +1. Subscripting `reactive` with a key path now yields a corresponding `BindingTarget` under Swift 3.2+. (#3489, kudos to @andersio) + + Example: + ```swift + label.reactive[\.text] <~ viewModel.title + ``` + # 6.0.0-rc.2 1. `NSObject` reactive extensions now work in generic environments that are limited to `NSObjectProtocol`. (#3484, kudos to @nickdomenicali) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index cf8040fe66..6716692655 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -157,6 +157,9 @@ 9A7488491E3B8ACE00CD0317 /* DelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7488471E3B8ACE00CD0317 /* DelegateProxy.swift */; }; 9A74884A1E3B8ACE00CD0317 /* DelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7488471E3B8ACE00CD0317 /* DelegateProxy.swift */; }; 9A74884B1E3B8ACE00CD0317 /* DelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7488471E3B8ACE00CD0317 /* DelegateProxy.swift */; }; + 9A7990CE1F1085D8001493A3 /* BindingTargetSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7990CD1F1085D8001493A3 /* BindingTargetSpec.swift */; }; + 9A7990CF1F1085D8001493A3 /* BindingTargetSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7990CD1F1085D8001493A3 /* BindingTargetSpec.swift */; }; + 9A7990D01F1085D8001493A3 /* BindingTargetSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7990CD1F1085D8001493A3 /* BindingTargetSpec.swift */; }; 9A892D8F1E8D19BE00EA35F3 /* DelegateProxySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A892D8E1E8D19BE00EA35F3 /* DelegateProxySpec.swift */; }; 9A892D901E8D19BE00EA35F3 /* DelegateProxySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A892D8E1E8D19BE00EA35F3 /* DelegateProxySpec.swift */; }; 9A892D911E8D19BE00EA35F3 /* DelegateProxySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A892D8E1E8D19BE00EA35F3 /* DelegateProxySpec.swift */; }; @@ -426,6 +429,7 @@ 9A6AAA271DB8F7EB0013AAEA /* ReusableComponentsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponentsSpec.swift; sourceTree = ""; }; 9A6AAA291DB8F7F10013AAEA /* ReusableComponentsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponentsSpec.swift; sourceTree = ""; }; 9A7488471E3B8ACE00CD0317 /* DelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DelegateProxy.swift; sourceTree = ""; }; + 9A7990CD1F1085D8001493A3 /* BindingTargetSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BindingTargetSpec.swift; sourceTree = ""; }; 9A892D8E1E8D19BE00EA35F3 /* DelegateProxySpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DelegateProxySpec.swift; sourceTree = ""; }; 9A90374E1ED61C6300345D62 /* ReactiveSwift+Lifetime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ReactiveSwift+Lifetime.swift"; sourceTree = ""; }; 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssociationSpec.swift; sourceTree = ""; }; @@ -831,6 +835,7 @@ 9A1D06231D93EA7E00ACF44C /* UIKit */, 538DCB771DCA5E3200332880 /* Shared */, 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */, + 9A7990CD1F1085D8001493A3 /* BindingTargetSpec.swift */, CD8401821CEE8ED7009F0ABF /* CocoaActionSpec.swift */, 9A892D8E1E8D19BE00EA35F3 /* DelegateProxySpec.swift */, D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */, @@ -1277,6 +1282,7 @@ 9A1E72BC1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, 9A1D06451D93EA7E00ACF44C /* UIImageViewSpec.swift in Sources */, 9A1D06551D93EA7E00ACF44C /* UITextViewSpec.swift in Sources */, + 9A7990D01F1085D8001493A3 /* BindingTargetSpec.swift in Sources */, 9A1D063B1D93EA7E00ACF44C /* UIButtonSpec.swift in Sources */, 9A24A8471DE142A600987AF9 /* SwizzlingSpec.swift in Sources */, 9A1D064B1D93EA7E00ACF44C /* UISegmentedControlSpec.swift in Sources */, @@ -1386,6 +1392,7 @@ B696FB811A7640C00075236D /* TestError.swift in Sources */, 9A892D8F1E8D19BE00EA35F3 /* DelegateProxySpec.swift in Sources */, 004FD0071E26CDB300A03A82 /* NSButtonSpec.swift in Sources */, + 9A7990CE1F1085D8001493A3 /* BindingTargetSpec.swift in Sources */, D9558AB91DFF86C0003254E1 /* NSPopUpButtonSpec.swift in Sources */, BFA6B94D1A7604D400C846D1 /* SignalProducerNimbleMatchers.swift in Sources */, 9A24A8451DE142A400987AF9 /* SwizzlingSpec.swift in Sources */, @@ -1500,6 +1507,7 @@ 9A6AAA2E1DB903A20013AAEA /* ReusableComponentsSpec.swift in Sources */, 4ABEFE211DCFCF090066A8C2 /* UITableViewSpec.swift in Sources */, 9A1D06401D93EA7E00ACF44C /* UIControlSpec.swift in Sources */, + 9A7990CF1F1085D8001493A3 /* BindingTargetSpec.swift in Sources */, 9A1D06481D93EA7E00ACF44C /* UIProgressViewSpec.swift in Sources */, CD8401841CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, A9EB3D291E94F3D3002A9BCC /* UITabBarItemSpec.swift in Sources */, diff --git a/ReactiveCocoa/NSObject+BindingTarget.swift b/ReactiveCocoa/NSObject+BindingTarget.swift index 239075d262..b14de66f2a 100644 --- a/ReactiveCocoa/NSObject+BindingTarget.swift +++ b/ReactiveCocoa/NSObject+BindingTarget.swift @@ -21,3 +21,29 @@ extension Reactive where Base: NSObjectProtocol { } } } + +#if swift(>=3.2) +extension Reactive where Base: AnyObject { + /// Creates a binding target that writes to the object with the given key path on a + /// `UIScheduler`. + /// + /// - parameters: + /// - keyPath: The key path to be written to. + /// + /// - returns: A binding target. + public subscript(keyPath: ReferenceWritableKeyPath) -> BindingTarget { + return BindingTarget(on: UIScheduler(), lifetime: ReactiveCocoa.lifetime(of: base), object: base, keyPath: keyPath) + } + + /// Creates a binding target that writes to the object with the given key path. + /// + /// - parameters: + /// - keyPath: The key path to be written to. + /// - scheduler: The scheduler to perform the write on. + /// + /// - returns: A binding target. + public subscript(keyPath: ReferenceWritableKeyPath, on scheduler: Scheduler) -> BindingTarget { + return BindingTarget(on: scheduler, lifetime: ReactiveCocoa.lifetime(of: base), object: base, keyPath: keyPath) + } +} +#endif diff --git a/ReactiveCocoa/NSObject+Lifetime.swift b/ReactiveCocoa/NSObject+Lifetime.swift index c5daa70658..40e72c5b49 100644 --- a/ReactiveCocoa/NSObject+Lifetime.swift +++ b/ReactiveCocoa/NSObject+Lifetime.swift @@ -31,7 +31,7 @@ internal func lifetime(of object: AnyObject) -> Lifetime { } } -extension Reactive where Base: NSObjectProtocol { +extension Reactive where Base: AnyObject & NSObjectProtocol { /// Returns a lifetime that ends when the object is deallocated. @nonobjc public var lifetime: Lifetime { return base.synchronized { diff --git a/ReactiveCocoaTests/BindingTargetSpec.swift b/ReactiveCocoaTests/BindingTargetSpec.swift new file mode 100644 index 0000000000..9f2f527949 --- /dev/null +++ b/ReactiveCocoaTests/BindingTargetSpec.swift @@ -0,0 +1,47 @@ +import ReactiveSwift +import ReactiveCocoa +import Result +import Quick +import Nimble + +private class Object: NSObject { + var value: Int = 0 +} + +class BindingTargetSpec: QuickSpec { + override func spec() { + #if swift(>=3.2) + describe("key path binding target") { + it("should update the value") { + let object = Object() + expect(object.value) == 0 + + let property = MutableProperty(1) + object.reactive[\.value] <~ property + expect(object.value) == 1 + + property.value = 2 + expect(object.value) == 2 + } + + it("should update the value on the given scheduler") { + let scheduler = TestScheduler() + + let object = Object() + let property = MutableProperty(1) + object.reactive[\.value, on: scheduler] <~ property + expect(object.value) == 0 + + scheduler.run() + expect(object.value) == 1 + + property.value = 2 + expect(object.value) == 1 + + scheduler.run() + expect(object.value) == 2 + } + } + #endif + } +} From ef745ddc65a4cf7d4ea15ad902ea98ea5944290d Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Wed, 12 Jul 2017 16:00:57 +0800 Subject: [PATCH 0830/1028] Update ReactiveSwift to 2.0.0-rc.3. --- Cartfile | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- ReactiveCocoa.podspec | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cartfile b/Cartfile index 2e44b69704..c946a85baa 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" "2.0.0-rc.2" +github "ReactiveCocoa/ReactiveSwift" "2.0.0-rc.3" diff --git a/Cartfile.resolved b/Cartfile.resolved index fd11505856..8fb45c2626 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,5 +1,5 @@ github "Quick/Nimble" "v7.0.1" github "Quick/Quick" "v1.1.0" -github "ReactiveCocoa/ReactiveSwift" "2.0.0-rc.2" +github "ReactiveCocoa/ReactiveSwift" "2.0.0-rc.3" github "antitypical/Result" "3.2.3" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index cd4eb9e9a8..c4ca0982a2 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit cd4eb9e9a86bffdb807b7027c02b9fb154ee6ba8 +Subproject commit c4ca0982a23f5176e2a0b466aa3628647ead0b5c diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 8c14683baa..7269e27847 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -23,7 +23,7 @@ Pod::Spec.new do |s| s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_map = "ReactiveCocoa/module.modulemap" - s.dependency 'ReactiveSwift', '2.0.0-rc.2' + s.dependency 'ReactiveSwift', '2.0.0-rc.3' s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "-suppress-warnings" } end From f170ca03fab69b31d3b576ed11af5869485944dc Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Wed, 12 Jul 2017 17:42:23 +0800 Subject: [PATCH 0831/1028] 6.0.0-rc.3 --- CHANGELOG.md | 1 + ReactiveCocoa.podspec | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41ed9c1b7e..27fac6a29e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master *Please put new entries at the top. +# 6.0.0-rc.3 # 6.0.0-rc.2 1. `NSObject` reactive extensions now work in generic environments that are limited to `NSObjectProtocol`. (#3484, kudos to @nickdomenicali) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 7269e27847..4def4f1bf9 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "6.0.0-rc.2" + s.version = "6.0.0-rc.3" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. From b4ef5a55d5863b7ae13625c9a15a164079118f46 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 22 Jul 2017 00:40:17 +0800 Subject: [PATCH 0832/1028] Update ReactiveSwift to 2.0.0. --- Cartfile | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- ReactiveCocoa.podspec | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cartfile b/Cartfile index c946a85baa..229fab610c 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" "2.0.0-rc.3" +github "ReactiveCocoa/ReactiveSwift" ~> 2.0 diff --git a/Cartfile.resolved b/Cartfile.resolved index 8fb45c2626..df14250ab8 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,5 +1,5 @@ github "Quick/Nimble" "v7.0.1" github "Quick/Quick" "v1.1.0" -github "ReactiveCocoa/ReactiveSwift" "2.0.0-rc.3" +github "ReactiveCocoa/ReactiveSwift" "2.0.0" github "antitypical/Result" "3.2.3" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index c4ca0982a2..614757120e 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit c4ca0982a23f5176e2a0b466aa3628647ead0b5c +Subproject commit 614757120eb91a59a7421ad11c48614ca9ac04c0 diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 4def4f1bf9..9bf0c9053d 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -23,7 +23,7 @@ Pod::Spec.new do |s| s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_map = "ReactiveCocoa/module.modulemap" - s.dependency 'ReactiveSwift', '2.0.0-rc.3' + s.dependency 'ReactiveSwift', '~> 2.0' s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "-suppress-warnings" } end From bb31e7f94c5555ed9af0069cdc91662e8bee15e5 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 22 Jul 2017 01:01:34 +0800 Subject: [PATCH 0833/1028] 6.0.0 --- CHANGELOG.md | 1 + README.md | 11 +++-------- ReactiveCocoa.podspec | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27fac6a29e..ef1bfd85ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master *Please put new entries at the top. +# 6.0.0 # 6.0.0-rc.3 # 6.0.0-rc.2 1. `NSObject` reactive extensions now work in generic environments that are limited to `NSObjectProtocol`. (#3484, kudos to @nickdomenicali) diff --git a/README.md b/README.md index 11c5bcbc3f..50cf4c1e55 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](#carthage) [![CocoaPods compatible](https://img.shields.io/cocoapods/v/ReactiveCocoa.svg)](#cocoapods) [![GitHub release](https://img.shields.io/github/release/ReactiveCocoa/ReactiveCocoa.svg)](https://github.com/ReactiveCocoa/ReactiveCocoa/releases) ![Swift 3.0.x](https://img.shields.io/badge/Swift-3.0.x-orange.svg) ![platforms](https://img.shields.io/badge/platforms-iOS%20%7C%20OS%20X%20%7C%20watchOS%20%7C%20tvOS%20-lightgrey.svg) -⚠️ [Looking for the Objective-C API?][] ⚠️ [Still using Swift 2.x?][] +⚠️ [Looking for the Objective-C API?][] 🎉 [Migrating from RAC 4.x?][CHANGELOG] @@ -100,7 +100,7 @@ If you use [Carthage][] to manage your dependencies, simply add ReactiveCocoa to your `Cartfile`: ``` -github "ReactiveCocoa/ReactiveCocoa" ~> 5.0 +github "ReactiveCocoa/ReactiveCocoa" ~> 6.0 ``` If you use Carthage to build your dependencies, make sure you have added `ReactiveCocoa.framework`, `ReactiveSwift.framework`, and `Result.framework` to the "_Linked Frameworks and Libraries_" section of your target, and have included them in your Carthage framework copying build phase. @@ -111,7 +111,7 @@ If you use [CocoaPods][] to manage your dependencies, simply add ReactiveCocoa to your `Podfile`: ``` -pod 'ReactiveCocoa', '~> 5.0.0' +pod 'ReactiveCocoa', '~> 6.0' ``` #### Git submodule @@ -136,11 +136,6 @@ If you need any help, please visit our [GitHub issues][] or [Stack Overflow][]. **Current Stable Release:**
    [![GitHub release](https://img.shields.io/github/release/ReactiveCocoa/ReactiveCocoa.svg)](https://github.com/ReactiveCocoa/ReactiveCocoa/releases) ### In Development -#### ReactiveCocoa 6.0 -It targets Swift 3.1 and ReactiveSwift 2.0. The estimated schedule is July 2017. - -ReactiveCocoa 6.0 has continued the work in polishing the APIs, preparing for the eventual API freeze for library resilience. - #### Swift 3.2 and Swift 4.0 compatibility While the development would be Swift 3.2 and Swift 4.0 aware, ReactiveCocoa 6.0 would not declare official support until Swift 3.2 and Swift 4.0 launch. diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 9bf0c9053d..1fa6dea0f8 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "6.0.0-rc.3" + s.version = "6.0.0" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. From fa18d147aa765b6e0b72953c179e7759c84883a9 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Thu, 10 Aug 2017 00:46:23 +0800 Subject: [PATCH 0834/1028] Fixed an issue causing infinite recursion in the Swift runtime. --- CHANGELOG.md | 2 ++ ReactiveCocoa/ObjC+RuntimeSubclassing.swift | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef1bfd85ed..0fc3473b7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # master *Please put new entries at the top. +1. [Xcode 9 beta 5] Fixed an issue causing infinite recursion in the Swift runtime. (#3498, kudos to @andersio) + # 6.0.0 # 6.0.0-rc.3 # 6.0.0-rc.2 diff --git a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift index a870724bfd..de3fb05cfa 100644 --- a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift +++ b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift @@ -110,7 +110,7 @@ private func subclassName(of class: AnyClass) -> String { /// - class: The class to swizzle. /// - perceivedClass: The class to be reported by the methods. private func replaceGetClass(in class: AnyClass, decoy perceivedClass: AnyClass) { - let getClass: @convention(block) (Any) -> AnyClass = { _ in + let getClass: @convention(block) (UnsafeRawPointer?) -> AnyClass = { _ in return perceivedClass } From c8c4c765631ede4af5f13c2c9c0100e51bdfbb12 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Wed, 16 Aug 2017 00:38:31 +0800 Subject: [PATCH 0835/1028] Silent a CocoaPods warning. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 92a9d29704..dc15f5da15 100644 --- a/.travis.yml +++ b/.travis.yml @@ -79,7 +79,7 @@ matrix: - script: - gem install cocoapods - pod repo update - - pod lib lint ReactiveCocoa.podspec + - pod lib lint ReactiveCocoa.podspec --swift-version=3.0 env: - JOB=PODSPEC - stage: prepare carthage cache From 325a1ea77e2a9f4a5b37e2f603abd497aa6c9fab Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Wed, 16 Aug 2017 01:43:24 +0800 Subject: [PATCH 0836/1028] Silent a CocoaPods warning, take 2. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index dc15f5da15..383a446844 100644 --- a/.travis.yml +++ b/.travis.yml @@ -79,7 +79,7 @@ matrix: - script: - gem install cocoapods - pod repo update - - pod lib lint ReactiveCocoa.podspec --swift-version=3.0 + - pod lib lint ReactiveCocoa.podspec --allow-warnings env: - JOB=PODSPEC - stage: prepare carthage cache From f23eac8afb95f19c8ee83f64852905c9e6d2f38d Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Wed, 16 Aug 2017 12:08:03 +0800 Subject: [PATCH 0837/1028] Update ReactiveSwift to 2.0.1. --- Cartfile.resolved | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cartfile.resolved b/Cartfile.resolved index df14250ab8..cc05b87ab0 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,5 +1,5 @@ github "Quick/Nimble" "v7.0.1" github "Quick/Quick" "v1.1.0" -github "ReactiveCocoa/ReactiveSwift" "2.0.0" +github "ReactiveCocoa/ReactiveSwift" "2.0.1" github "antitypical/Result" "3.2.3" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index 614757120e..b9d5b350a4 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit 614757120eb91a59a7421ad11c48614ca9ac04c0 +Subproject commit b9d5b350a446b85704396ce332a1f9e4960cfc6b From afe5b34d384a86fec79766672b965de56005ba42 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Wed, 16 Aug 2017 13:52:05 +0800 Subject: [PATCH 0838/1028] 6.0.1 --- CHANGELOG.md | 1 + ReactiveCocoa.podspec | 2 +- ReactiveCocoa/Info.plist | 2 +- ReactiveCocoaTests/Info.plist | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fc3473b7c..8cb4348e15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master *Please put new entries at the top. +# 6.0.1 1. [Xcode 9 beta 5] Fixed an issue causing infinite recursion in the Swift runtime. (#3498, kudos to @andersio) # 6.0.0 diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 1fa6dea0f8..53b6e231e1 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "6.0.0" + s.version = "6.0.1" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveCocoa/Info.plist b/ReactiveCocoa/Info.plist index cf1cd64e76..9d8db4a43e 100644 --- a/ReactiveCocoa/Info.plist +++ b/ReactiveCocoa/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 6.0.0 + 6.0.1 CFBundleSignature ???? CFBundleVersion diff --git a/ReactiveCocoaTests/Info.plist b/ReactiveCocoaTests/Info.plist index 35cbe3fd57..56d0b7d127 100644 --- a/ReactiveCocoaTests/Info.plist +++ b/ReactiveCocoaTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 6.0.0 + 6.0.1 CFBundleSignature ???? CFBundleVersion From af7a360a9519342155d2e18ae5fd70b993fc51d8 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sun, 9 Jul 2017 14:05:36 +0800 Subject: [PATCH 0839/1028] KVO with Smart Key Path. --- CHANGELOG.md | 4 + ReactiveCocoa.xcodeproj/project.pbxproj | 8 + .../NSObject+KeyValueObserving.swift | 99 +++ .../KeyValueObservingSpec+Swift4.swift | 624 ++++++++++++++++++ 4 files changed, 735 insertions(+) create mode 100644 ReactiveCocoaTests/KeyValueObservingSpec+Swift4.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cb4348e15..9f139e4954 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,11 @@ # master *Please put new entries at the top. +1. KVO observations can now be made with Smart Key Path in Swift 3.2+, using `producer(for:)` and `signal(for:)` available on `NSObject.reactive`. (#3491, kudos to @andersio) + + # 6.0.1 + 1. [Xcode 9 beta 5] Fixed an issue causing infinite recursion in the Swift runtime. (#3498, kudos to @andersio) # 6.0.0 diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index cf8040fe66..e65f0d033a 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -189,6 +189,9 @@ 9AA0BD991DDE7A2200531FCF /* ObjCRuntimeAliases.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD971DDE7A2200531FCF /* ObjCRuntimeAliases.m */; }; 9AA0BD9A1DDE7A2200531FCF /* ObjCRuntimeAliases.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD971DDE7A2200531FCF /* ObjCRuntimeAliases.m */; }; 9AA0BD9B1DDE7A2200531FCF /* ObjCRuntimeAliases.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD971DDE7A2200531FCF /* ObjCRuntimeAliases.m */; }; + 9AA6A1E51F11F9B000CA2257 /* KeyValueObservingSpec+Swift4.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA6A1E31F11F9A800CA2257 /* KeyValueObservingSpec+Swift4.swift */; }; + 9AA6A1E61F11F9B100CA2257 /* KeyValueObservingSpec+Swift4.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA6A1E31F11F9A800CA2257 /* KeyValueObservingSpec+Swift4.swift */; }; + 9AA6A1E71F11F9B100CA2257 /* KeyValueObservingSpec+Swift4.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA6A1E31F11F9A800CA2257 /* KeyValueObservingSpec+Swift4.swift */; }; 9AAD49881DED2C350068EC9B /* UIKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AAD49871DED2C350068EC9B /* UIKeyboard.swift */; }; 9AAD498A1DED2F380068EC9B /* UIKeyboardSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AAD49891DED2F380068EC9B /* UIKeyboardSpec.swift */; }; 9AB15C7A1E26CD9A00997378 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AB15C791E26CD9A00997378 /* Deprecations+Removals.swift */; }; @@ -434,6 +437,7 @@ 9AA0BD891DDE153A00531FCF /* ObjC+Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObjC+Constants.swift"; sourceTree = ""; }; 9AA0BD8E1DDE29F800531FCF /* NSObject+ObjCRuntime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+ObjCRuntime.swift"; sourceTree = ""; }; 9AA0BD971DDE7A2200531FCF /* ObjCRuntimeAliases.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ObjCRuntimeAliases.m; sourceTree = ""; }; + 9AA6A1E31F11F9A800CA2257 /* KeyValueObservingSpec+Swift4.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KeyValueObservingSpec+Swift4.swift"; sourceTree = ""; }; 9AAD49871DED2C350068EC9B /* UIKeyboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIKeyboard.swift; sourceTree = ""; }; 9AAD49891DED2F380068EC9B /* UIKeyboardSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIKeyboardSpec.swift; sourceTree = ""; }; 9AB15C791E26CD9A00997378 /* Deprecations+Removals.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deprecations+Removals.swift"; sourceTree = ""; }; @@ -836,6 +840,7 @@ D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */, B696FB801A7640C00075236D /* TestError.swift */, 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */, + 9AA6A1E31F11F9A800CA2257 /* KeyValueObservingSpec+Swift4.swift */, 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */, 9A54A2101DDF5B4D001739B3 /* InterceptingPerformanceTests.swift */, 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */, @@ -1275,6 +1280,7 @@ 9A9DFEEB1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */, CD8401851CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, 9A1E72BC1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, + 9AA6A1E71F11F9B100CA2257 /* KeyValueObservingSpec+Swift4.swift in Sources */, 9A1D06451D93EA7E00ACF44C /* UIImageViewSpec.swift in Sources */, 9A1D06551D93EA7E00ACF44C /* UITextViewSpec.swift in Sources */, 9A1D063B1D93EA7E00ACF44C /* UIButtonSpec.swift in Sources */, @@ -1394,6 +1400,7 @@ 9A9DFEE91DA7EFB60039EE1B /* AssociationSpec.swift in Sources */, 3B30EE8C1E7BE529007CC8EF /* DeprecationsSpec.swift in Sources */, CD42C69B1E951F6900AA9504 /* ReactiveCocoaTestsConfiguration.swift in Sources */, + 9AA6A1E51F11F9B000CA2257 /* KeyValueObservingSpec+Swift4.swift in Sources */, 9A1E72BA1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, 4ABEFE2E1DCFD01F0066A8C2 /* NSTableViewSpec.swift in Sources */, ); @@ -1465,6 +1472,7 @@ files = ( A9EB3D211E94F0AF002A9BCC /* UINavigationItemSpec.swift in Sources */, 9A1D06581D93EA7E00ACF44C /* UIViewSpec.swift in Sources */, + 9AA6A1E61F11F9B100CA2257 /* KeyValueObservingSpec+Swift4.swift in Sources */, 9AAD498A1DED2F380068EC9B /* UIKeyboardSpec.swift in Sources */, BFCF77621DFAD9440058006E /* UISearchBarSpec.swift in Sources */, D0A2260F1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */, diff --git a/ReactiveCocoa/NSObject+KeyValueObserving.swift b/ReactiveCocoa/NSObject+KeyValueObserving.swift index 4863f112c2..519f59c150 100644 --- a/ReactiveCocoa/NSObject+KeyValueObserving.swift +++ b/ReactiveCocoa/NSObject+KeyValueObserving.swift @@ -55,6 +55,105 @@ extension Reactive where Base: NSObject { return disposable } } + + #if swift(>=3.2) + private func producer( + for keyPath: KeyPath, + transform: @escaping (Any?) -> U + ) -> SignalProducer { + return SignalProducer { observer, lifetime in + let disposable = KeyValueObserver.observe( + self.base, + keyPath: keyPath._kvcKeyPathString!, + options: [.initial, .new], + action: { observer.send(value: transform($0)) } + ) + + lifetime.observeEnded(disposable.dispose) + + if let lifetimeDisposable = self.lifetime.observeEnded(observer.sendCompleted) { + lifetime.observeEnded(lifetimeDisposable.dispose) + } + } + } + + private func signal( + for keyPath: KeyPath, + transform: @escaping (Any?) -> U + ) -> Signal { + return Signal { observer in + let disposable = CompositeDisposable() + disposable += KeyValueObserver.observe( + self.base, + keyPath: keyPath._kvcKeyPathString!, + options: [.new], + action: { observer.send(value: transform($0)) } + ) + disposable += self.lifetime.observeEnded(observer.sendCompleted) + return disposable + } + } + + /// Create a producer which sends the current value and all the subsequent + /// changes of the property specified by the key path. + /// + /// The producer completes when the object deinitializes. + /// + /// - parameters: + /// - keyPath: The key path of the property to be observed. + /// + /// - returns: A producer emitting values of the property specified by the + /// key path. + public func producer(for keyPath: KeyPath) -> SignalProducer { + return producer(for: keyPath) { $0 as! U? } + } + + /// Create a signal all changes of the property specified by the key path. + /// + /// The signal completes when the object deinitializes. + /// + /// - note: + /// Does not send the initial value. See `producer(forKeyPath:)`. + /// + /// - parameters: + /// - keyPath: The key path of the property to be observed. + /// + /// - returns: A producer emitting values of the property specified by the + /// key path. + public func signal(for keyPath: KeyPath) -> Signal { + return signal(for: keyPath) { $0 as! U? } + } + + /// Create a producer which sends the current value and all the subsequent + /// changes of the property specified by the key path. + /// + /// The producer completes when the object deinitializes. + /// + /// - parameters: + /// - keyPath: The key path of the property to be observed. + /// + /// - returns: A producer emitting values of the property specified by the + /// key path. + public func producer(for keyPath: KeyPath) -> SignalProducer { + return producer(for: keyPath) { $0 as! U } + } + + /// Create a signal all changes of the property specified by the key path. + /// + /// The signal completes when the object deinitializes. + /// + /// - note: + /// Does not send the initial value. See `producer(forKeyPath:)`. + /// + /// - parameters: + /// - keyPath: The key path of the property to be observed. + /// + /// - returns: A producer emitting values of the property specified by the + /// key path. + public func signal(for keyPath: KeyPath) -> Signal { + return signal(for: keyPath) { $0 as! U } + } + #endif } internal final class KeyValueObserver: NSObject { diff --git a/ReactiveCocoaTests/KeyValueObservingSpec+Swift4.swift b/ReactiveCocoaTests/KeyValueObservingSpec+Swift4.swift new file mode 100644 index 0000000000..fd4ea4ceef --- /dev/null +++ b/ReactiveCocoaTests/KeyValueObservingSpec+Swift4.swift @@ -0,0 +1,624 @@ +import Foundation +@testable import ReactiveCocoa +import ReactiveSwift +import enum Result.NoError +import Quick +import Nimble + +#if swift(>=3.2) +class KeyValueObservingSwift4Spec: QuickSpec { + override func spec() { + describe("NSObject.signal(forKeyPath:)") { + it("should not send the initial value") { + let object = ObservableObject() + var values: [Int] = [] + + object.reactive + .signal(for: \.rac_value) + .observeValues { values.append($0) } + + expect(values) == [] + } + + itBehavesLike("a reactive key value observer using Swift 4 Smart Key Path") { + ["observe": "signal"] + } + } + + describe("NSObject.producer(forKeyPath:)") { + it("should send the initial value") { + let object = ObservableObject() + var values: [Int] = [] + + object.reactive + .producer(for: \.rac_value) + .startWithValues { value in + values.append(value) + } + + expect(values) == [0] + } + + it("should send the initial value for nested key path") { + let parentObject = NestedObservableObject() + var values: [Int] = [] + + parentObject + .reactive + .producer(for: \.rac_object.rac_value) + .startWithValues { values.append($0) } + + expect(values) == [0] + } + + // NOTE: Xcode 9.0 b2 does not support optional chaining in key paths yet. + /* + it("should send the initial value for weak nested key path") { + let parentObject = NestedObservableObject() + let innerObject = Optional(ObservableObject()) + parentObject.rac_weakObject = innerObject + var values: [Int] = [] + + parentObject + .reactive + .producer(forKeyPath: "rac_weakObject.rac_value") + .startWithValues { values.append(($0 as! NSNumber).intValue) } + + expect(values) == [0] + } + */ + + itBehavesLike("a reactive key value observer using Swift 4 Smart Key Path") { + ["observe": "producer"] + } + } + } +} + +// Shared examples to ensure both `signal(forKeyPath:)` and `producer(forKeyPath:)` +// share common behavior. +fileprivate class KeyValueObservingSwift4SpecConfiguration: QuickConfiguration { + class Context { + let context: [String: Any] + + init(_ context: [String: Any]) { + self.context = context + } + + func observe(_ object: Object, _ keyPath: KeyPath) -> SignalProducer { + switch context["observe"] { + case let context as String where context == "signal": + return SignalProducer(object.reactive.signal(for: keyPath)) + + case let context as String where context == "producer": + return object.reactive.producer(for: keyPath).skip(first: 1) + + default: + fatalError("Unknown test config.") + } + } + + func observe(_ object: Object, _ keyPath: KeyPath) -> SignalProducer { + switch context["observe"] { + case let context as String where context == "signal": + return SignalProducer(object.reactive.signal(for: keyPath)) + + case let context as String where context == "producer": + return object.reactive.producer(for: keyPath).skip(first: 1) + + default: + fatalError("Unknown test config.") + } + } + + func isFinished(_ object: Operation) -> SignalProducer { + return observe(object, \.isFinished) + } + + func changes(_ object: ObservableObject) -> SignalProducer { + return observe(object, \.rac_value) + } + + func nestedChanges(_ object: NestedObservableObject) -> SignalProducer { + return observe(object, \.rac_object.rac_value) + } + + // NOTE: Xcode 9.0 b2 does not support optional chaining in key paths yet. + // + // func weakNestedChanges(_ object: NestedObservableObject) -> SignalProducer { + // return observe(object, \.rac_weakObject?.rac_value) + // } + + func strongReferenceChanges(_ object: ObservableObject) -> SignalProducer { + return observe(object, \.target) + } + + // NOTE: Compiler segfault with key path literals refering to weak + // properties. + // + // func weakReferenceChanges(_ object: ObservableObject) -> SignalProducer { + // return observe(object, \.weakTarget) + //} + + func dependentKeyChanges(_ object: ObservableObject) -> SignalProducer { + return observe(object, \.rac_value_plusOne) + } + } + + override class func configure(_ configuration: Configuration) { + sharedExamples("a reactive key value observer using Swift 4 Smart Key Path") { (sharedExampleContext: @escaping SharedExampleContext) in + var context: Context! + + beforeEach { context = Context(sharedExampleContext()) } + afterEach { context = nil } + + it("should send new values for the key path (even if the value remains unchanged)") { + let object = ObservableObject() + var values: [Int] = [] + + context.changes(object).startWithValues { + values.append($0) + } + + expect(values) == [] + + object.rac_value = 0 + expect(values) == [0] + + object.rac_value = 1 + expect(values) == [0, 1] + + object.rac_value = 1 + expect(values) == [0, 1, 1] + } + + it("should send new values for the dependent key path") { + // This variant wraps the setter invocations with an autoreleasepool, and + // intentionally avoids retaining the emitted value, so that a bug that + // emits `nil` inappropriately can be caught. + // + // Related: https://github.com/ReactiveCocoa/ReactiveCocoa/issues/3443#issuecomment-292721863 + // Fixed in https://github.com/ReactiveCocoa/ReactiveCocoa/pull/3439. + + let object = ObservableObject() + var expectedResults = [1, 2, 2] + var unexpectedResults: [NSDecimalNumber?] = [] + + var matches = true + + context.dependentKeyChanges(object).startWithValues { number in + if number != NSDecimalNumber(value: expectedResults.removeFirst()) { + matches = false + unexpectedResults.append(number) + } + } + + expect(matches) == true + expect(unexpectedResults as NSArray) == [] + + autoreleasepool { + object.rac_value = 0 + } + + expect(matches) == true + expect(unexpectedResults as NSArray) == [] + + + autoreleasepool { + object.rac_value = 1 + } + + expect(matches) == true + expect(unexpectedResults as NSArray) == [] + + autoreleasepool { + object.rac_value = 1 + } + + expect(matches) == true + expect(unexpectedResults as NSArray) == [] + } + + it("should send new values for the dependent key path (even if the value remains unchanged)") { + let object = ObservableObject() + var values: [NSDecimalNumber] = [] + + context.dependentKeyChanges(object).startWithValues { + values.append($0) + } + + expect(values) == [] + + object.rac_value = 0 + expect(values) == [1] + + object.rac_value = 1 + expect(values) == [1, 2] + + object.rac_value = 1 + expect(values) == [1, 2, 2] + } + + it("should not crash an Operation") { + // Related issue: + // https://github.com/ReactiveCocoa/ReactiveCocoa/issues/3329 + func invoke() { + let op = Operation() + context.isFinished(op).start() + } + + invoke() + } + + describe("signal behavior") { + it("should complete when the object deallocates") { + var completed = false + + _ = { + // Use a closure so this object has a shorter lifetime. + let object = ObservableObject() + + context.changes(object).startWithCompleted { + completed = true + } + + expect(completed) == false + }() + + expect(completed).toEventually(beTruthy()) + } + + it("should support native Swift objects") { + let object = ObservableObject() + var value: Any? + + context + .strongReferenceChanges(object) + .startWithValues { value = $0 } + + expect(value).to(beNil()) + + let token = Token() + object.target = token + expect(value).to(beIdenticalTo(token)) + } + + // NOTE: Compiler segfault with key path literals refering to weak + // properties. + /* + it("should emit a `nil` when the key path is being cleared due to the deallocation of the Objective-C object it held.") { + let object = ObservableObject() + let null = ObjectIdentifier(NSNull()) + var ids: [ObjectIdentifier] = [] + + context + .weakReferenceChanges(object) + .startWithValues { ids.append($0.map { ObjectIdentifier($0 as AnyObject) } ?? null) } + + expect(ids) == [] + + var token: NSObject? = NSObject() + let tokenId = ObjectIdentifier(token!) + + // KVO would create autoreleasing references of the values being + // passed. So we have to ensure that they are cleared before + // we move on. + autoreleasepool { + object.weakTarget = token + } + + expect(ids) == [tokenId] + + token = nil + + expect(ids) == [tokenId, null] + expect(object.weakTarget).to(beNil()) + } + + it("should emit a `nil` when the key path is being cleared due to the deallocation of the native Swift object it held.") { + let object = ObservableObject() + let null = ObjectIdentifier(NSNull()) + var ids: [ObjectIdentifier] = [] + + context + .weakReferenceChanges(object) + .startWithValues { ids.append($0.map { ObjectIdentifier($0 as AnyObject) } ?? null) } + + expect(ids) == [] + + var token: Token? = Token() + let tokenId = ObjectIdentifier(token!) + + // KVO would create autoreleasing references of the values being + // passed. So we have to ensure that they are cleared before + // we move on. + autoreleasepool { + object.weakTarget = token + } + + expect(ids) == [tokenId] + + token = nil + + expect(ids) == [tokenId, null] + expect(object.weakTarget).to(beNil()) + } + */ + } + + describe("nested key paths") { + it("should observe changes in a nested key path") { + let parentObject = NestedObservableObject() + var values: [Int] = [] + + context.nestedChanges(parentObject).startWithValues { + values.append($0) + } + + expect(values) == [] + + parentObject.rac_object.rac_value = 1 + expect(values) == [1] + + let oldInnerObject = parentObject.rac_object + + let newInnerObject = ObservableObject() + parentObject.rac_object = newInnerObject + expect(values) == [1, 0] + + parentObject.rac_object.rac_value = 10 + oldInnerObject.rac_value = 2 + expect(values) == [1, 0, 10] + } + + // NOTE: Xcode 9.0 b2 does not support optional chaining in key paths yet. + /* + it("should observe changes in a nested weak key path") { + let parentObject = NestedObservableObject() + var innerObject = Optional(ObservableObject()) + parentObject.rac_weakObject = innerObject + var values: [Int] = [] + + context.weakNestedChanges(parentObject).startWithValues { + values.append($0) + } + + expect(values) == [] + + innerObject?.rac_value = 1 + expect(values) == [1] + + autoreleasepool { + innerObject = nil + } + + // NOTE: [1] or [Optional(1), nil]? + expect(values) == [1] + + innerObject = ObservableObject() + parentObject.rac_weakObject = innerObject + expect(values) == [1, 0] + + innerObject?.rac_value = 10 + expect(values) == [1, 0, 10] + } + */ + + it("should not retain replaced value in a nested key path") { + // NOTE: The producer version of this test cases somehow + // fails when the spec is being run alone. + let parentObject = NestedObservableObject() + + weak var weakOriginalInner: ObservableObject? = parentObject.rac_object + expect(weakOriginalInner).toNot(beNil()) + + autoreleasepool { + _ = context + .nestedChanges(parentObject) + .start() + } + + autoreleasepool { + parentObject.rac_object = ObservableObject() + } + + expect(weakOriginalInner).to(beNil()) + } + } + + describe("thread safety") { + var concurrentQueue: DispatchQueue! + + beforeEach { + concurrentQueue = DispatchQueue( + label: "org.reactivecocoa.ReactiveCocoa.DynamicPropertySpec.concurrentQueue", + attributes: .concurrent + ) + } + + it("should handle changes being made on another queue") { + let object = ObservableObject() + var observedValue = 0 + + context.changes(object) + .take(first: 1) + .startWithValues { observedValue = $0 } + + concurrentQueue.async { + object.rac_value = 2 + } + + concurrentQueue.sync(flags: .barrier) {} + expect(observedValue).toEventually(equal(2)) + } + + it("should handle changes being made on another queue using deliverOn") { + let object = ObservableObject() + var observedValue = 0 + + context.changes(object) + .take(first: 1) + .observe(on: UIScheduler()) + .startWithValues { observedValue = $0 } + + concurrentQueue.async { + object.rac_value = 2 + } + + concurrentQueue.sync(flags: .barrier) {} + expect(observedValue).toEventually(equal(2)) + } + + it("async disposal of target") { + var object: ObservableObject? = ObservableObject() + var observedValue = 0 + + context.changes(object!) + .observe(on: UIScheduler()) + .startWithValues { observedValue = $0 } + + concurrentQueue.async { + object!.rac_value = 2 + object = nil + } + + concurrentQueue.sync(flags: .barrier) {} + expect(observedValue).toEventually(equal(2)) + } + } + + describe("stress tests") { + let numIterations = 5000 + + var testObject: ObservableObject! + var iterationQueue: DispatchQueue! + var concurrentQueue: DispatchQueue! + + beforeEach { + testObject = ObservableObject() + iterationQueue = DispatchQueue( + label: "org.reactivecocoa.ReactiveCocoa.RACKVOProxySpec.iterationQueue", + attributes: .concurrent + ) + concurrentQueue = DispatchQueue( + label: "org.reactivecocoa.ReactiveCocoa.DynamicPropertySpec.concurrentQueue", + attributes: .concurrent + ) + } + + it("attach observers") { + let deliveringObserver: QueueScheduler + if #available(*, OSX 10.10) { + deliveringObserver = QueueScheduler(name: "\(#file):\(#line)") + } else { + deliveringObserver = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) + } + + var atomicCounter = Int64(0) + + DispatchQueue.concurrentPerform(iterations: numIterations) { index in + context.changes(testObject) + .observe(on: deliveringObserver) + .startWithValues { value in + OSAtomicAdd64(Int64(value), &atomicCounter) + } + } + + testObject.rac_value = 2 + + expect(atomicCounter).toEventually(equal(Int64(numIterations * 2)), timeout: 30.0) + } + + // ReactiveCocoa/ReactiveCocoa#1122 + it("async disposal of observer") { + let serialDisposable = SerialDisposable() + + iterationQueue.async { + DispatchQueue.concurrentPerform(iterations: numIterations) { index in + let disposable = context.changes(testObject) + .startWithCompleted {} + + serialDisposable.inner = disposable + + concurrentQueue.async { + testObject.rac_value = index + } + } + } + + iterationQueue.sync(flags: .barrier) { + serialDisposable.dispose() + } + } + + it("async disposal of signal with in-flight changes") { + let otherScheduler: QueueScheduler + + var token = Optional(Lifetime.Token()) + let lifetime = Lifetime(token!) + + if #available(*, OSX 10.10) { + otherScheduler = QueueScheduler(name: "\(#file):\(#line)") + } else { + otherScheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) + } + + let replayProducer = context.changes(testObject) + .map { $0 % 2 == 0 } + .observe(on: otherScheduler) + .take(during: lifetime) + .replayLazily(upTo: 1) + + replayProducer.start() + + iterationQueue.suspend() + + let half = numIterations / 2 + + for index in 0 ..< numIterations { + iterationQueue.async { + testObject.rac_value = index + } + + if index == half { + iterationQueue.async(flags: .barrier) { + token = nil + expect(replayProducer.last()).toNot(beNil()) + } + } + } + + iterationQueue.resume() + iterationQueue.sync(flags: .barrier, execute: {}) + } + } + } + } +} + +private final class Token {} + +private class ObservableObject: NSObject { + dynamic var rac_value: Int = 0 + + dynamic var target: AnyObject? + dynamic weak var weakTarget: AnyObject? + + dynamic var rac_value_plusOne: NSDecimalNumber { + return NSDecimalNumber(value: rac_value + 1) + } + + override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set { + if key == "rac_value_plusOne" { + return Set([#keyPath(ObservableObject.rac_value)]) + } else { + return Set() + } + } +} + +private class NestedObservableObject: NSObject { + dynamic var rac_object: ObservableObject = ObservableObject() + dynamic weak var rac_weakObject: ObservableObject? +} +#endif From 43deb9cf6c37d15a6f2e963a0b5e6fcc02b2e3d7 Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Wed, 16 Aug 2017 15:09:59 +0200 Subject: [PATCH 0840/1028] Fix changelog formating: Without extra newline, the header for 4.0 is not rendered as header --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cb4348e15..45e9bf72d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -349,6 +349,7 @@ public final class MyController { + # 4.0 If you’re new to the Swift API and migrating from RAC 2, start with the [3.0 changes](#30). This section only covers the differences between `3.0` and `4.0`. From db39cc1e46034766345b745bdd41a234400fa696 Mon Sep 17 00:00:00 2001 From: Alberto Salas Date: Sun, 27 Aug 2017 19:28:46 +0200 Subject: [PATCH 0841/1028] Added cancelButtonClick Signal to UISearchBar --- ReactiveCocoa/UIKit/iOS/UISearchBar.swift | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ReactiveCocoa/UIKit/iOS/UISearchBar.swift b/ReactiveCocoa/UIKit/iOS/UISearchBar.swift index c89bdbff77..f2902f201c 100644 --- a/ReactiveCocoa/UIKit/iOS/UISearchBar.swift +++ b/ReactiveCocoa/UIKit/iOS/UISearchBar.swift @@ -10,6 +10,10 @@ private class SearchBarDelegateProxy: DelegateProxy, UISear @objc func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { forwardee?.searchBar?(searchBar, textDidChange: searchText) } + + @objc func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { + forwardee?.searchBarCancelButtonClicked?(searchBar) + } } extension Reactive where Base: UISearchBar { @@ -41,4 +45,10 @@ extension Reactive where Base: UISearchBar { .map { [unowned base] in base.text } } + /// A void signal emitted by the search bar upon any click on the cancel button + public var cancelButtonClicked: Signal { + return proxy.intercept(#selector(UISearchBarDelegate.searchBarCancelButtonClicked)) + .map { () -> Void in } + } + } From a5394c2cdfc2b91aaab27d21005b1e99b7f1b949 Mon Sep 17 00:00:00 2001 From: Alberto Salas Date: Sun, 27 Aug 2017 19:52:12 +0200 Subject: [PATCH 0842/1028] =?UTF-8?q?Added=20unit=20test=20for=20UISearchB?= =?UTF-8?q?ar=E2=80=99s=20cancelButtonClick?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ReactiveCocoaTests/UIKit/UISearchBarSpec.swift | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift b/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift index 546eb685c7..9f9d71b893 100644 --- a/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift +++ b/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift @@ -105,6 +105,18 @@ class UISearchBarSpec: QuickSpec { searchBar.delegate!.searchBarSearchButtonClicked!(searchBar) expect(receiver.searchButtonClickedCounter) == 1 } + + it("should pass through the unintercepted calls") { + searchBar.reactive.continuousTextValues.observe { _ in } + + let receiver = SearchBarDelegateReceiver() + searchBar.delegate = receiver + expect(receiver.searchBarCancelButtonClickedCounter) == 0 + + searchBar.delegate!.searchBarCancelButtonClicked!(searchBar) + expect(receiver.searchBarCancelButtonClickedCounter) == 1 + } + } } @@ -112,6 +124,7 @@ class SearchBarDelegateReceiver: NSObject, UISearchBarDelegate { var textDidChangeCounter = 0 var textDidEndEditingCounter = 0 var searchButtonClickedCounter = 0 + var searchBarCancelButtonClickedCounter = 0 func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { searchButtonClickedCounter += 1 @@ -124,4 +137,8 @@ class SearchBarDelegateReceiver: NSObject, UISearchBarDelegate { func searchBarTextDidEndEditing(_ searchBar: UISearchBar) { textDidEndEditingCounter += 1 } + + func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { + searchBarCancelButtonClickedCounter += 1 + } } From f00bc611c1f256dc3f97c743817020cd1a558c8f Mon Sep 17 00:00:00 2001 From: Alberto Salas Date: Sun, 27 Aug 2017 19:57:35 +0200 Subject: [PATCH 0843/1028] Added change to CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e84c9a965b..65bbaf018f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master *Please put new entries at the top. +1. Added `cancelButtonClicked` signal to `UISearchBar`. 1. Subscripting `reactive` with a key path now yields a corresponding `BindingTarget` under Swift 3.2+. (#3489, kudos to @andersio) Example: From 5dd7c69657b6d729ed857b201c6a978931176202 Mon Sep 17 00:00:00 2001 From: Alberto Salas Date: Mon, 28 Aug 2017 21:59:50 +0200 Subject: [PATCH 0844/1028] Removed unnecessary map --- ReactiveCocoa/UIKit/iOS/UISearchBar.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/ReactiveCocoa/UIKit/iOS/UISearchBar.swift b/ReactiveCocoa/UIKit/iOS/UISearchBar.swift index f2902f201c..c785d17cd1 100644 --- a/ReactiveCocoa/UIKit/iOS/UISearchBar.swift +++ b/ReactiveCocoa/UIKit/iOS/UISearchBar.swift @@ -48,7 +48,6 @@ extension Reactive where Base: UISearchBar { /// A void signal emitted by the search bar upon any click on the cancel button public var cancelButtonClicked: Signal { return proxy.intercept(#selector(UISearchBarDelegate.searchBarCancelButtonClicked)) - .map { () -> Void in } } } From 17a2ad0b48cd704127562312caca42641f10c8a2 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 16 Sep 2017 02:08:45 +0800 Subject: [PATCH 0845/1028] Update ReactiveSwift to 2.1.0 alpha 1. --- Cartfile | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- ReactiveCocoa.podspec | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cartfile b/Cartfile index 229fab610c..775c46db62 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" ~> 2.0 +github "ReactiveCocoa/ReactiveSwift" "2.1.0-alpha.1" diff --git a/Cartfile.resolved b/Cartfile.resolved index cc05b87ab0..a9baf3f9c3 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,5 +1,5 @@ github "Quick/Nimble" "v7.0.1" github "Quick/Quick" "v1.1.0" -github "ReactiveCocoa/ReactiveSwift" "2.0.1" +github "ReactiveCocoa/ReactiveSwift" "2.1.0-alpha.1" github "antitypical/Result" "3.2.3" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index b9d5b350a4..2f202643e9 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit b9d5b350a446b85704396ce332a1f9e4960cfc6b +Subproject commit 2f202643e9e9266cc07469e74f43c0b614d9bcd2 diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 53b6e231e1..36c602220c 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -23,7 +23,7 @@ Pod::Spec.new do |s| s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_map = "ReactiveCocoa/module.modulemap" - s.dependency 'ReactiveSwift', '~> 2.0' + s.dependency 'ReactiveSwift', '2.1.0-alpha.1' s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "-suppress-warnings" } end From e5d074cb8fec4364ba3b9c213773efe605dc3091 Mon Sep 17 00:00:00 2001 From: NachoSoto Date: Fri, 15 Sep 2017 14:49:47 -0700 Subject: [PATCH 0846/1028] iOS + tvOS: disable code coverage to fix app submission with Xcode 9 See https://github.com/Carthage/Carthage/issues/2056. --- CHANGELOG.md | 1 + .../xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme | 3 +-- .../xcshareddata/xcschemes/ReactiveCocoa-tvOS.xcscheme | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65bbaf018f..10071ca209 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master *Please put new entries at the top. +1. Disabled code coverage data to allow app submissions with Xcode 9.0 (see https://github.com/Carthage/Carthage/issues/2056, kudos to @NachoSoto) 1. Added `cancelButtonClicked` signal to `UISearchBar`. 1. Subscripting `reactive` with a key path now yields a corresponding `BindingTarget` under Swift 3.2+. (#3489, kudos to @andersio) diff --git a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme index 2e8560ffbe..afada7a200 100644 --- a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme +++ b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme @@ -96,8 +96,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES" - codeCoverageEnabled = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES"> diff --git a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-tvOS.xcscheme b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-tvOS.xcscheme index b54308f5fd..521a4a4e20 100644 --- a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-tvOS.xcscheme +++ b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-tvOS.xcscheme @@ -82,8 +82,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES" - codeCoverageEnabled = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES"> From a3441090234c537808546ce93aeb14567312aa98 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 16 Sep 2017 17:53:03 +0800 Subject: [PATCH 0847/1028] Update ReactiveSwift to 2.1.0 alpha 2. --- Cartfile | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- ReactiveCocoa.podspec | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cartfile b/Cartfile index 775c46db62..76e23db613 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" "2.1.0-alpha.1" +github "ReactiveCocoa/ReactiveSwift" "2.1.0-alpha.2" diff --git a/Cartfile.resolved b/Cartfile.resolved index a9baf3f9c3..98acb00b48 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,5 +1,5 @@ github "Quick/Nimble" "v7.0.1" github "Quick/Quick" "v1.1.0" -github "ReactiveCocoa/ReactiveSwift" "2.1.0-alpha.1" +github "ReactiveCocoa/ReactiveSwift" "2.1.0-alpha.2" github "antitypical/Result" "3.2.3" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index 2f202643e9..999cead2e7 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit 2f202643e9e9266cc07469e74f43c0b614d9bcd2 +Subproject commit 999cead2e77dbcb8267ee8fe97042c320c11c142 diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 36c602220c..374afe15d4 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -23,7 +23,7 @@ Pod::Spec.new do |s| s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_map = "ReactiveCocoa/module.modulemap" - s.dependency 'ReactiveSwift', '2.1.0-alpha.1' + s.dependency 'ReactiveSwift', '2.1.0-alpha.2' s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "-suppress-warnings" } end From d78d775672716c4584eaec9b48104e5c0b96d1e8 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 16 Sep 2017 22:14:29 +0800 Subject: [PATCH 0848/1028] 6.1.0-alpha.1 --- CHANGELOG.md | 1 + ReactiveCocoa.podspec | 2 +- ReactiveCocoa/Info.plist | 2 +- ReactiveCocoaTests/Info.plist | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10071ca209..510277fe35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master *Please put new entries at the top. +# 6.1.0-alpha.1 1. Disabled code coverage data to allow app submissions with Xcode 9.0 (see https://github.com/Carthage/Carthage/issues/2056, kudos to @NachoSoto) 1. Added `cancelButtonClicked` signal to `UISearchBar`. 1. Subscripting `reactive` with a key path now yields a corresponding `BindingTarget` under Swift 3.2+. (#3489, kudos to @andersio) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 374afe15d4..8e71e8cb05 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "6.0.1" + s.version = "6.1.0-alpha.1" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveCocoa/Info.plist b/ReactiveCocoa/Info.plist index 9d8db4a43e..a18cd934e5 100644 --- a/ReactiveCocoa/Info.plist +++ b/ReactiveCocoa/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 6.0.1 + 6.1.0 CFBundleSignature ???? CFBundleVersion diff --git a/ReactiveCocoaTests/Info.plist b/ReactiveCocoaTests/Info.plist index 56d0b7d127..1554da65db 100644 --- a/ReactiveCocoaTests/Info.plist +++ b/ReactiveCocoaTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 6.0.1 + 6.1.0 CFBundleSignature ???? CFBundleVersion From 63a4652e3e1bcea62fda7897e7e3a2a9cc005117 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 16 Sep 2017 22:41:34 +0800 Subject: [PATCH 0849/1028] Disable code coverage for the macOS target. --- .../xcshareddata/xcschemes/ReactiveCocoa-macOS.xcscheme | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-macOS.xcscheme b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-macOS.xcscheme index 9b570d656e..11355638da 100644 --- a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-macOS.xcscheme +++ b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-macOS.xcscheme @@ -96,8 +96,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES" - codeCoverageEnabled = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES"> From 6ec62e82ff5167f933dd88556b329b35d1c8ddf7 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Thu, 21 Sep 2017 02:23:03 +0800 Subject: [PATCH 0850/1028] Add `.swift-version` for CocoaPods. --- .swift-version | 1 + 1 file changed, 1 insertion(+) create mode 100644 .swift-version diff --git a/.swift-version b/.swift-version new file mode 100644 index 0000000000..8c50098d8a --- /dev/null +++ b/.swift-version @@ -0,0 +1 @@ +3.1 From 39d4f2a4d0010d7435dcc0ca54b37dcc2a22e521 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Thu, 21 Sep 2017 02:23:03 +0800 Subject: [PATCH 0851/1028] Add `.swift-version` for CocoaPods. --- .swift-version | 1 + 1 file changed, 1 insertion(+) create mode 100644 .swift-version diff --git a/.swift-version b/.swift-version new file mode 100644 index 0000000000..8c50098d8a --- /dev/null +++ b/.swift-version @@ -0,0 +1 @@ +3.1 From 5582d370591b3240996c6a6ade054b7759b57fbd Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 16 Sep 2017 22:41:34 +0800 Subject: [PATCH 0852/1028] Disable code coverage for the macOS target. --- .../xcshareddata/xcschemes/ReactiveCocoa-macOS.xcscheme | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-macOS.xcscheme b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-macOS.xcscheme index 9b570d656e..11355638da 100644 --- a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-macOS.xcscheme +++ b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-macOS.xcscheme @@ -96,8 +96,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES" - codeCoverageEnabled = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES"> From aa189b1fec0daf0918725bae780e97ab3a1102b7 Mon Sep 17 00:00:00 2001 From: NachoSoto Date: Fri, 15 Sep 2017 14:49:47 -0700 Subject: [PATCH 0853/1028] iOS + tvOS: disable code coverage to fix app submission with Xcode 9 See https://github.com/Carthage/Carthage/issues/2056. --- .../xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme | 3 +-- .../xcshareddata/xcschemes/ReactiveCocoa-tvOS.xcscheme | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme index 2e8560ffbe..afada7a200 100644 --- a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme +++ b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme @@ -96,8 +96,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES" - codeCoverageEnabled = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES"> diff --git a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-tvOS.xcscheme b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-tvOS.xcscheme index b54308f5fd..521a4a4e20 100644 --- a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-tvOS.xcscheme +++ b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-tvOS.xcscheme @@ -82,8 +82,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES" - codeCoverageEnabled = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES"> From 339e7bdbc44f3c0e68ebc7fbbe7a59a259686703 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Thu, 21 Sep 2017 14:24:11 +0800 Subject: [PATCH 0854/1028] 6.0.2 --- CHANGELOG.md | 3 +++ ReactiveCocoa.podspec | 2 +- ReactiveCocoa/Info.plist | 2 +- ReactiveCocoaTests/Info.plist | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cb4348e15..327fff2762 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # master *Please put new entries at the top. +# 6.0.2 +1. Disabled code coverage data to allow app submissions with Xcode 9.0 (see https://github.com/Carthage/Carthage/issues/2056, kudos to @NachoSoto) + # 6.0.1 1. [Xcode 9 beta 5] Fixed an issue causing infinite recursion in the Swift runtime. (#3498, kudos to @andersio) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 53b6e231e1..097524f259 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "6.0.1" + s.version = "6.0.2" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveCocoa/Info.plist b/ReactiveCocoa/Info.plist index 9d8db4a43e..6e7980dbec 100644 --- a/ReactiveCocoa/Info.plist +++ b/ReactiveCocoa/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 6.0.1 + 6.0.2 CFBundleSignature ???? CFBundleVersion diff --git a/ReactiveCocoaTests/Info.plist b/ReactiveCocoaTests/Info.plist index 56d0b7d127..3eb2ddb5a4 100644 --- a/ReactiveCocoaTests/Info.plist +++ b/ReactiveCocoaTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 6.0.1 + 6.0.2 CFBundleSignature ???? CFBundleVersion From 59ddcab4b9f0b13be2527410b6a3561bb553a5c0 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Thu, 21 Sep 2017 14:30:37 +0800 Subject: [PATCH 0855/1028] Unmute CocoaPods warnings. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 383a446844..92a9d29704 100644 --- a/.travis.yml +++ b/.travis.yml @@ -79,7 +79,7 @@ matrix: - script: - gem install cocoapods - pod repo update - - pod lib lint ReactiveCocoa.podspec --allow-warnings + - pod lib lint ReactiveCocoa.podspec env: - JOB=PODSPEC - stage: prepare carthage cache From 426737b74b93bb1f2a98f68d445014f60fb72d15 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Thu, 21 Sep 2017 14:32:05 +0800 Subject: [PATCH 0856/1028] 6.1.0-alpha.2 --- CHANGELOG.md | 1 + ReactiveCocoa.podspec | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 510277fe35..fb99ee3187 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master *Please put new entries at the top. +# 6.1.0-alpha.2 # 6.1.0-alpha.1 1. Disabled code coverage data to allow app submissions with Xcode 9.0 (see https://github.com/Carthage/Carthage/issues/2056, kudos to @NachoSoto) 1. Added `cancelButtonClicked` signal to `UISearchBar`. diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 8e71e8cb05..5b9a521c37 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "6.1.0-alpha.1" + s.version = "6.1.0-alpha.2" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. From 6b5a1619f3848d15cbb77b5a3f637d2722b16d2a Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Thu, 28 Sep 2017 13:59:57 +0800 Subject: [PATCH 0857/1028] ReactiveMapKit. --- .travis.yml | 3 +- CHANGELOG.md | 6 + ReactiveCocoa.xcodeproj/project.pbxproj | 736 +++++++++++++++++- .../xcschemes/ReactiveCocoa-iOS.xcscheme | 12 + .../xcschemes/ReactiveCocoa-macOS.xcscheme | 12 + .../xcschemes/ReactiveCocoa-tvOS.xcscheme | 12 + .../xcschemes/ReactiveMapKit.xcscheme | 101 +++ ReactiveMapKit.podspec | 23 + ReactiveMapKit/Info.plist | 24 + .../Shared => ReactiveMapKit}/MKMapView.swift | 3 + ReactiveMapKitTests/Info.plist | 22 + .../MKMapViewSpec.swift | 1 + 12 files changed, 911 insertions(+), 44 deletions(-) create mode 100644 ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveMapKit.xcscheme create mode 100644 ReactiveMapKit.podspec create mode 100644 ReactiveMapKit/Info.plist rename {ReactiveCocoa/Shared => ReactiveMapKit}/MKMapView.swift (92%) create mode 100644 ReactiveMapKitTests/Info.plist rename {ReactiveCocoaTests/Shared => ReactiveMapKitTests}/MKMapViewSpec.swift (99%) diff --git a/.travis.yml b/.travis.yml index 383a446844..f73ca4d553 100644 --- a/.travis.yml +++ b/.travis.yml @@ -79,7 +79,8 @@ matrix: - script: - gem install cocoapods - pod repo update - - pod lib lint ReactiveCocoa.podspec --allow-warnings + - pod lib lint ReactiveCocoa.podspec + - pod lib lint ReactiveMapKit.podspec env: - JOB=PODSPEC - stage: prepare carthage cache diff --git a/CHANGELOG.md b/CHANGELOG.md index fb99ee3187..728b7a21f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # master *Please put new entries at the top. +1. MapKit reactive bindings have been moved to a new **ReactiveMapKit** framework. (#3524) + + Sources that use the MapKit bindings are now required to import ReactiveMapKit. + + For all Xcode project users (including Carthage), targets need to be configured to link against ReactiveMapKit. For CocoaPods users, the framework is offered as a standalone podspec, so the Podfile needs to be updated with a new entry. + # 6.1.0-alpha.2 # 6.1.0-alpha.1 1. Disabled code coverage data to allow app submissions with Xcode 9.0 (see https://github.com/Carthage/Carthage/issues/2056, kudos to @NachoSoto) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 4b5cd34ca9..6c29c7538e 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -41,12 +41,6 @@ 538DCB7D1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB7C1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift */; }; 538DCB7E1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB7C1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift */; }; 538DCB7F1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB7C1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift */; }; - 53A6BED21DD4BCA90016C058 /* MKMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED11DD4BCA90016C058 /* MKMapView.swift */; }; - 53A6BED31DD4BCA90016C058 /* MKMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED11DD4BCA90016C058 /* MKMapView.swift */; }; - 53A6BED41DD4BCA90016C058 /* MKMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED11DD4BCA90016C058 /* MKMapView.swift */; }; - 53A6BED61DD4BD2C0016C058 /* MKMapViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED51DD4BD2C0016C058 /* MKMapViewSpec.swift */; }; - 53A6BED71DD4BD2C0016C058 /* MKMapViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED51DD4BD2C0016C058 /* MKMapViewSpec.swift */; }; - 53A6BED81DD4BD2C0016C058 /* MKMapViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED51DD4BD2C0016C058 /* MKMapViewSpec.swift */; }; 53AC46CC1DD6F97400C799E1 /* UISlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53AC46CB1DD6F97400C799E1 /* UISlider.swift */; }; 53AC46CF1DD6FC0000C799E1 /* UISliderSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53AC46CE1DD6FC0000C799E1 /* UISliderSpec.swift */; }; 57A4D2081BA13D7A00F7D4B1 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; @@ -64,6 +58,23 @@ 834DE1141E4122910099F4E5 /* NSSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 834DE1131E4122910099F4E5 /* NSSlider.swift */; }; 8392D8FD1DB93E5E00504ED4 /* NSImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8392D8FC1DB93E5E00504ED4 /* NSImageView.swift */; }; 9A0726F31E912B610081F3F7 /* ActionProxySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A0726F21E912B610081F3F7 /* ActionProxySpec.swift */; }; + 9A16754B1F80C3E300B63650 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D04725EA19E49ED7006002AA /* ReactiveCocoa.framework */; }; + 9A16754C1F80C3EC00B63650 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9AC03A631F7CC6B300EC33C1 /* ReactiveSwift.framework */; }; + 9A1675501F80C40C00B63650 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A16754F1F80C40200B63650 /* Result.framework */; }; + 9A1675511F80C41F00B63650 /* ReactiveCocoa.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = D04725EA19E49ED7006002AA /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 9A1675521F80C41F00B63650 /* Result.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = 9A16754F1F80C40200B63650 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 9A1675531F80C41F00B63650 /* ReactiveSwift.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = 9AC03A631F7CC6B300EC33C1 /* ReactiveSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 9A1675571F80C44F00B63650 /* Nimble.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = 9A1675561F80C44100B63650 /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 9A1675581F80C45300B63650 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A1675561F80C44100B63650 /* Nimble.framework */; }; + 9A1675591F80DDDE00B63650 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9AC03A631F7CC6B300EC33C1 /* ReactiveSwift.framework */; }; + 9A16755A1F80DDE100B63650 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9AC03A631F7CC6B300EC33C1 /* ReactiveSwift.framework */; }; + 9A16755B1F80DDE400B63650 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9AC03A631F7CC6B300EC33C1 /* ReactiveSwift.framework */; }; + 9A16755C1F80DDE700B63650 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9AC03A631F7CC6B300EC33C1 /* ReactiveSwift.framework */; }; + 9A16755D1F80DE1C00B63650 /* MKMapViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED51DD4BD2C0016C058 /* MKMapViewSpec.swift */; }; + 9A16755E1F80DE2F00B63650 /* ReactiveMapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9AC03A571F7CC3BF00EC33C1 /* ReactiveMapKit.framework */; }; + 9A16755F1F80DE3900B63650 /* ReactiveMapKit.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = 9AC03A571F7CC3BF00EC33C1 /* ReactiveMapKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 9A1675601F80DF9600B63650 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; }; + 9A1675641F80DFAB00B63650 /* Quick.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = 9A1675631F80DFA400B63650 /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 9A1D05E01D93E99100ACF44C /* NSObject+Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */; }; 9A1D05E11D93E99100ACF44C /* NSObject+Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */; }; 9A1D05E21D93E99100ACF44C /* NSObject+Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */; }; @@ -198,6 +209,10 @@ 9AB15C7B1E26CD9A00997378 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AB15C791E26CD9A00997378 /* Deprecations+Removals.swift */; }; 9AB15C7C1E26CD9A00997378 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AB15C791E26CD9A00997378 /* Deprecations+Removals.swift */; }; 9AB15C7D1E26CD9A00997378 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AB15C791E26CD9A00997378 /* Deprecations+Removals.swift */; }; + 9AC03A611F7CC5E300EC33C1 /* MKMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED11DD4BCA90016C058 /* MKMapView.swift */; }; + 9AC03A621F7CC6B300EC33C1 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9AC03A631F7CC6B300EC33C1 /* ReactiveSwift.framework */; }; + 9AC03A641F7CC6B300EC33C1 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D04725EA19E49ED7006002AA /* ReactiveCocoa.framework */; }; + 9AC03A651F7CC6B300EC33C1 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; 9AD0F06B1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; 9AD0F06C1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; @@ -245,13 +260,6 @@ A9EB3D841E955602002A9BCC /* UISelection​Feedback​Generator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D801E955602002A9BCC /* UISelection​Feedback​Generator.swift */; }; B696FB811A7640C00075236D /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B696FB801A7640C00075236D /* TestError.swift */; }; B696FB821A7640C00075236D /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B696FB801A7640C00075236D /* TestError.swift */; }; - BE330A0F1D634F1E00806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A0E1D634F1E00806963 /* ReactiveSwift.framework */; }; - BE330A111D634F2900806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A101D634F2900806963 /* ReactiveSwift.framework */; }; - BE330A131D634F2E00806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A121D634F2E00806963 /* ReactiveSwift.framework */; }; - BE330A151D634F4000806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A141D634F4000806963 /* ReactiveSwift.framework */; }; - BE330A171D634F4E00806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A161D634F4E00806963 /* ReactiveSwift.framework */; }; - BE330A191D634F5900806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A181D634F5900806963 /* ReactiveSwift.framework */; }; - BE330A1B1D634F5F00806963 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE330A1A1D634F5F00806963 /* ReactiveSwift.framework */; }; BEE020661D637B0000DF261F /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B696FB801A7640C00075236D /* TestError.swift */; }; BF4335651E02AC7600AC88DD /* UIScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF4335641E02AC7600AC88DD /* UIScrollView.swift */; }; BF4335681E02EF0600AC88DD /* UIScrollViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF4335661E02EEDE00AC88DD /* UIScrollViewSpec.swift */; }; @@ -304,6 +312,13 @@ remoteGlobalIDString = 57A4D1AF1BA13D7A00F7D4B1; remoteInfo = "ReactiveCocoa-tvOS"; }; + 9A1675651F813EAA00B63650 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D04725E119E49ED7006002AA /* Project object */; + proxyType = 1; + remoteGlobalIDString = 9AC03A561F7CC3BF00EC33C1; + remoteInfo = ReactiveMapKit; + }; D04725F719E49ED7006002AA /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D04725E119E49ED7006002AA /* Project object */; @@ -335,6 +350,22 @@ name = "Copy Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; + 9A16754A1F80C3AC00B63650 /* Copy Files */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 9A1675641F80DFAB00B63650 /* Quick.framework in Copy Files */, + 9A1675571F80C44F00B63650 /* Nimble.framework in Copy Files */, + 9A1675511F80C41F00B63650 /* ReactiveCocoa.framework in Copy Files */, + 9A1675521F80C41F00B63650 /* Result.framework in Copy Files */, + 9A1675531F80C41F00B63650 /* ReactiveSwift.framework in Copy Files */, + 9A16755F1F80DE3900B63650 /* ReactiveMapKit.framework in Copy Files */, + ); + name = "Copy Files"; + runOnlyForDeploymentPostprocessing = 0; + }; D01B7B6119EDD8F600D26E01 /* Copy Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -387,6 +418,11 @@ 834DE1131E4122910099F4E5 /* NSSlider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSSlider.swift; sourceTree = ""; }; 8392D8FC1DB93E5E00504ED4 /* NSImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSImageView.swift; sourceTree = ""; }; 9A0726F21E912B610081F3F7 /* ActionProxySpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionProxySpec.swift; sourceTree = ""; }; + 9A16753D1F80C35100B63650 /* ReactiveMapKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveMapKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 9A1675411F80C35100B63650 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 9A16754F1F80C40200B63650 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 9A1675561F80C44100B63650 /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Nimble.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 9A1675631F80DFA400B63650 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Quick.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Association.swift"; sourceTree = ""; }; 9A1D05EC1D93E9F100ACF44C /* UIActivityIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorView.swift; sourceTree = ""; }; 9A1D05ED1D93E9F100ACF44C /* UIBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIBarButtonItem.swift; sourceTree = ""; }; @@ -441,6 +477,9 @@ 9AAD49871DED2C350068EC9B /* UIKeyboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIKeyboard.swift; sourceTree = ""; }; 9AAD49891DED2F380068EC9B /* UIKeyboardSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIKeyboardSpec.swift; sourceTree = ""; }; 9AB15C791E26CD9A00997378 /* Deprecations+Removals.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deprecations+Removals.swift"; sourceTree = ""; }; + 9AC03A571F7CC3BF00EC33C1 /* ReactiveMapKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveMapKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 9AC03A5A1F7CC3BF00EC33C1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 9AC03A631F7CC6B300EC33C1 /* ReactiveSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ReactiveSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+KeyValueObserving.swift"; sourceTree = ""; }; 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaAction.swift; sourceTree = ""; }; 9ADE4A881DA6D206005C2AC8 /* NSControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSControl.swift; sourceTree = ""; }; @@ -467,13 +506,6 @@ A9EB3D7F1E955602002A9BCC /* UINotification​Feedback​Generator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UINotification​Feedback​Generator.swift"; sourceTree = ""; }; A9EB3D801E955602002A9BCC /* UISelection​Feedback​Generator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UISelection​Feedback​Generator.swift"; sourceTree = ""; }; B696FB801A7640C00075236D /* TestError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestError.swift; sourceTree = ""; }; - BE330A0E1D634F1E00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = build/Debug/ReactiveSwift.framework; sourceTree = ""; }; - BE330A101D634F2900806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = build/Debug/ReactiveSwift.framework; sourceTree = ""; }; - BE330A121D634F2E00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-iphoneos/ReactiveSwift.framework"; sourceTree = ""; }; - BE330A141D634F4000806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-iphoneos/ReactiveSwift.framework"; sourceTree = ""; }; - BE330A161D634F4E00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-watchos/ReactiveSwift.framework"; sourceTree = ""; }; - BE330A181D634F5900806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-appletvos/ReactiveSwift.framework"; sourceTree = ""; }; - BE330A1A1D634F5F00806963 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = "build/Debug-appletvos/ReactiveSwift.framework"; sourceTree = ""; }; BF4335641E02AC7600AC88DD /* UIScrollView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIScrollView.swift; sourceTree = ""; }; BF4335661E02EEDE00AC88DD /* UIScrollViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIScrollViewSpec.swift; sourceTree = ""; }; BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalProducerNimbleMatchers.swift; sourceTree = ""; }; @@ -523,8 +555,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - BE330A191D634F5900806963 /* ReactiveSwift.framework in Frameworks */, 57A4D2081BA13D7A00F7D4B1 /* Result.framework in Frameworks */, + 9A16755C1F80DDE700B63650 /* ReactiveSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -532,19 +564,41 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - BE330A1B1D634F5F00806963 /* ReactiveSwift.framework in Frameworks */, CDF066CA1CDC1CA200199626 /* Nimble.framework in Frameworks */, CDF066CB1CDC1CA200199626 /* Quick.framework in Frameworks */, 7DFBED081CDB8C9500EE435B /* ReactiveCocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; + 9A16753A1F80C35100B63650 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9A16755E1F80DE2F00B63650 /* ReactiveMapKit.framework in Frameworks */, + 9A1675581F80C45300B63650 /* Nimble.framework in Frameworks */, + 9A16754B1F80C3E300B63650 /* ReactiveCocoa.framework in Frameworks */, + 9A16754C1F80C3EC00B63650 /* ReactiveSwift.framework in Frameworks */, + 9A1675501F80C40C00B63650 /* Result.framework in Frameworks */, + 9A1675601F80DF9600B63650 /* Quick.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9AC03A531F7CC3BF00EC33C1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9AC03A641F7CC6B300EC33C1 /* ReactiveCocoa.framework in Frameworks */, + 9AC03A621F7CC6B300EC33C1 /* ReactiveSwift.framework in Frameworks */, + 9AC03A651F7CC6B300EC33C1 /* Result.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; A9B315501B3940610001CB9C /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - BE330A171D634F4E00806963 /* ReactiveSwift.framework in Frameworks */, A9B315C91B3940980001CB9C /* Result.framework in Frameworks */, + 9A16755B1F80DDE400B63650 /* ReactiveSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -552,8 +606,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - BE330A0F1D634F1E00806963 /* ReactiveSwift.framework in Frameworks */, CDC42E2F1AE7AB8B00965373 /* Result.framework in Frameworks */, + 9A1675591F80DDDE00B63650 /* ReactiveSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -561,7 +615,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - BE330A111D634F2900806963 /* ReactiveSwift.framework in Frameworks */, CDC42E301AE7AB8B00965373 /* Result.framework in Frameworks */, D05E662519EDD82000904ACA /* Nimble.framework in Frameworks */, D037672D19EDA75D00A782A9 /* Quick.framework in Frameworks */, @@ -573,8 +626,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - BE330A131D634F2E00806963 /* ReactiveSwift.framework in Frameworks */, CDC42E311AE7AB8B00965373 /* Result.framework in Frameworks */, + 9A16755A1F80DDE100B63650 /* ReactiveSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -582,7 +635,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - BE330A151D634F4000806963 /* ReactiveSwift.framework in Frameworks */, D05E662619EDD83000904ACA /* Nimble.framework in Frameworks */, D037672F19EDA78B00A782A9 /* Quick.framework in Frameworks */, D047261719E49F82006002AA /* ReactiveCocoa.framework in Frameworks */, @@ -596,7 +648,6 @@ isa = PBXGroup; children = ( 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */, - 53A6BED11DD4BCA90016C058 /* MKMapView.swift */, ); path = Shared; sourceTree = ""; @@ -605,7 +656,6 @@ isa = PBXGroup; children = ( 538DCB7C1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift */, - 53A6BED51DD4BD2C0016C058 /* MKMapViewSpec.swift */, ); path = Shared; sourceTree = ""; @@ -621,6 +671,15 @@ path = tvOS; sourceTree = ""; }; + 9A16753E1F80C35100B63650 /* ReactiveMapKitTests */ = { + isa = PBXGroup; + children = ( + 53A6BED51DD4BD2C0016C058 /* MKMapViewSpec.swift */, + 9A1675411F80C35100B63650 /* Info.plist */, + ); + path = ReactiveMapKitTests; + sourceTree = ""; + }; 9A1D05E91D93E9F100ACF44C /* AppKit */ = { isa = PBXGroup; children = ( @@ -700,6 +759,15 @@ path = UIKit; sourceTree = ""; }; + 9AC03A581F7CC3BF00EC33C1 /* ReactiveMapKit */ = { + isa = PBXGroup; + children = ( + 53A6BED11DD4BCA90016C058 /* MKMapView.swift */, + 9AC03A5A1F7CC3BF00EC33C1 /* Info.plist */, + ); + path = ReactiveMapKit; + sourceTree = ""; + }; 9ADE4A8C1DA6D94C005C2AC8 /* AppKit */ = { isa = PBXGroup; children = ( @@ -729,13 +797,10 @@ BE330A0D1D634F1E00806963 /* Frameworks */ = { isa = PBXGroup; children = ( - BE330A1A1D634F5F00806963 /* ReactiveSwift.framework */, - BE330A181D634F5900806963 /* ReactiveSwift.framework */, - BE330A161D634F4E00806963 /* ReactiveSwift.framework */, - BE330A141D634F4000806963 /* ReactiveSwift.framework */, - BE330A121D634F2E00806963 /* ReactiveSwift.framework */, - BE330A101D634F2900806963 /* ReactiveSwift.framework */, - BE330A0E1D634F1E00806963 /* ReactiveSwift.framework */, + 9A1675631F80DFA400B63650 /* Quick.framework */, + 9A1675561F80C44100B63650 /* Nimble.framework */, + 9A16754F1F80C40200B63650 /* Result.framework */, + 9AC03A631F7CC6B300EC33C1 /* ReactiveSwift.framework */, ); name = Frameworks; sourceTree = ""; @@ -765,6 +830,8 @@ D04725EC19E49ED7006002AA /* ReactiveCocoa */, D04725F919E49ED7006002AA /* ReactiveCocoaTests */, D047262519E49FE8006002AA /* Configuration */, + 9AC03A581F7CC3BF00EC33C1 /* ReactiveMapKit */, + 9A16753E1F80C35100B63650 /* ReactiveMapKitTests */, D04725EB19E49ED7006002AA /* Products */, BE330A0D1D634F1E00806963 /* Frameworks */, ); @@ -781,6 +848,8 @@ A9B315541B3940610001CB9C /* ReactiveCocoa.framework */, 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */, 7DFBED031CDB8C9500EE435B /* ReactiveCocoaTests.xctest */, + 9AC03A571F7CC3BF00EC33C1 /* ReactiveMapKit.framework */, + 9A16753D1F80C35100B63650 /* ReactiveMapKitTests.xctest */, ); name = Products; sourceTree = ""; @@ -944,6 +1013,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 9AC03A541F7CC3BF00EC33C1 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; A9B315511B3940610001CB9C /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -1011,6 +1087,43 @@ productReference = 7DFBED031CDB8C9500EE435B /* ReactiveCocoaTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; + 9A16753C1F80C35100B63650 /* ReactiveMapKitTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9A1675451F80C35100B63650 /* Build configuration list for PBXNativeTarget "ReactiveMapKitTests" */; + buildPhases = ( + 9A1675391F80C35100B63650 /* Sources */, + 9A16753A1F80C35100B63650 /* Frameworks */, + 9A16753B1F80C35100B63650 /* Resources */, + 9A16754A1F80C3AC00B63650 /* Copy Files */, + ); + buildRules = ( + ); + dependencies = ( + 9A1675661F813EAA00B63650 /* PBXTargetDependency */, + ); + name = ReactiveMapKitTests; + productName = ReactiveMapKitTests; + productReference = 9A16753D1F80C35100B63650 /* ReactiveMapKitTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 9AC03A561F7CC3BF00EC33C1 /* ReactiveMapKit */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9AC03A5C1F7CC3BF00EC33C1 /* Build configuration list for PBXNativeTarget "ReactiveMapKit" */; + buildPhases = ( + 9AC03A521F7CC3BF00EC33C1 /* Sources */, + 9AC03A531F7CC3BF00EC33C1 /* Frameworks */, + 9AC03A541F7CC3BF00EC33C1 /* Headers */, + 9AC03A551F7CC3BF00EC33C1 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ReactiveMapKit; + productName = ReactiveMapKit; + productReference = 9AC03A571F7CC3BF00EC33C1 /* ReactiveMapKit.framework */; + productType = "com.apple.product-type.framework"; + }; A9B315531B3940610001CB9C /* ReactiveCocoa-watchOS */ = { isa = PBXNativeTarget; buildConfigurationList = A9B3155D1B3940610001CB9C /* Build configuration list for PBXNativeTarget "ReactiveCocoa-watchOS" */; @@ -1108,7 +1221,7 @@ D04725E119E49ED7006002AA /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 0730; + LastSwiftUpdateCheck = 0900; LastUpgradeCheck = 0800; ORGANIZATIONNAME = GitHub; TargetAttributes = { @@ -1119,6 +1232,13 @@ CreatedOnToolsVersion = 7.3.1; LastSwiftMigration = 0800; }; + 9A16753C1F80C35100B63650 = { + CreatedOnToolsVersion = 9.0; + ProvisioningStyle = Automatic; + }; + 9AC03A561F7CC3BF00EC33C1 = { + CreatedOnToolsVersion = 9.0; + }; A9B315531B3940610001CB9C = { CreatedOnToolsVersion = 7.0; LastSwiftMigration = 0810; @@ -1160,6 +1280,8 @@ A9B315531B3940610001CB9C /* ReactiveCocoa-watchOS */, 57A4D1AF1BA13D7A00F7D4B1 /* ReactiveCocoa-tvOS */, 7DFBED021CDB8C9500EE435B /* ReactiveCocoa-tvOSTests */, + 9AC03A561F7CC3BF00EC33C1 /* ReactiveMapKit */, + 9A16753C1F80C35100B63650 /* ReactiveMapKitTests */, ); }; /* End PBXProject section */ @@ -1180,6 +1302,20 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 9A16753B1F80C35100B63650 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9AC03A551F7CC3BF00EC33C1 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; A9B315521B3940610001CB9C /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1258,7 +1394,6 @@ 9A1D061A1D93EA0100ACF44C /* UIProgressView.swift in Sources */, 9AA0BD7F1DDE03DE00531FCF /* ObjC+Runtime.swift in Sources */, 9A2E42611DAA6737006D909F /* CocoaTarget.swift in Sources */, - 53A6BED41DD4BCA90016C058 /* MKMapView.swift in Sources */, 9A9037521ED61C6300345D62 /* ReactiveSwift+Lifetime.swift in Sources */, 9A1D06121D93EA0100ACF44C /* UIBarButtonItem.swift in Sources */, 9A1D06111D93EA0100ACF44C /* UIActivityIndicatorView.swift in Sources */, @@ -1274,7 +1409,6 @@ A9EB3D231E94F314002A9BCC /* UINavigationItemSpec.swift in Sources */, 9A9A129A1DC7A97100D10223 /* UIGestureRecognizerSpec.swift in Sources */, 9A1D06591D93EA7E00ACF44C /* UIViewSpec.swift in Sources */, - 53A6BED81DD4BD2C0016C058 /* MKMapViewSpec.swift in Sources */, 3B30EE8E1E7BE529007CC8EF /* DeprecationsSpec.swift in Sources */, 7DFBED281CDB8DE300EE435B /* DynamicPropertySpec.swift in Sources */, 9A9DFEEB1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */, @@ -1308,6 +1442,22 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 9A1675391F80C35100B63650 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9A16755D1F80DE1C00B63650 /* MKMapViewSpec.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9AC03A521F7CC3BF00EC33C1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9AC03A611F7CC5E300EC33C1 /* MKMapView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; A9B3154F1B3940610001CB9C /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1368,7 +1518,6 @@ 834DE1141E4122910099F4E5 /* NSSlider.swift in Sources */, 9ADE4A911DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, 834DE1121E4120340099F4E5 /* NSSegmentedControl.swift in Sources */, - 53A6BED21DD4BCA90016C058 /* MKMapView.swift in Sources */, 9A1D065B1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, 4A0E10FF1D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, 9AA0BD811DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift in Sources */, @@ -1386,7 +1535,6 @@ 538DCB7D1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift in Sources */, 9ADE4A8F1DA6DA20005C2AC8 /* NSControlSpec.swift in Sources */, D0A2260E1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */, - 53A6BED61DD4BD2C0016C058 /* MKMapViewSpec.swift in Sources */, 9ADFE5A11DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */, 4ABEFE331DCFD0630066A8C2 /* NSCollectionViewSpec.swift in Sources */, B696FB811A7640C00075236D /* TestError.swift in Sources */, @@ -1445,7 +1593,6 @@ 9A1D065C1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, 9A1D06071D93EA0000ACF44C /* UILabel.swift in Sources */, BF4335651E02AC7600AC88DD /* UIScrollView.swift in Sources */, - 53A6BED31DD4BCA90016C058 /* MKMapView.swift in Sources */, 4A0E11001D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, 9ADE4A921DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, 9ADFE5A61DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */, @@ -1501,7 +1648,6 @@ 9A6AAA0F1DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */, BF4335681E02EF0600AC88DD /* UIScrollViewSpec.swift in Sources */, 3BCAAC7D1DEE1A2D00B30335 /* UIRefreshControlSpec.swift in Sources */, - 53A6BED71DD4BD2C0016C058 /* MKMapViewSpec.swift in Sources */, 9A1D06381D93EA7E00ACF44C /* UIBarButtonItemSpec.swift in Sources */, 9A1E72BB1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, 9A6AAA2E1DB903A20013AAEA /* ReusableComponentsSpec.swift in Sources */, @@ -1524,6 +1670,11 @@ target = 57A4D1AF1BA13D7A00F7D4B1 /* ReactiveCocoa-tvOS */; targetProxy = 7DFBED091CDB8C9500EE435B /* PBXContainerItemProxy */; }; + 9A1675661F813EAA00B63650 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 9AC03A561F7CC3BF00EC33C1 /* ReactiveMapKit */; + targetProxy = 9A1675651F813EAA00B63650 /* PBXContainerItemProxy */; + }; D04725F819E49ED7006002AA /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = D04725E919E49ED7006002AA /* ReactiveCocoa-macOS */; @@ -1657,6 +1808,479 @@ }; name = Profile; }; + 9A1675461F80C35100B63650 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SUPPORTED_PLATFORMS = "macosx iphonesimulator appletvsimulator watchsimulator"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 9A1675471F80C35100B63650 /* Test */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SUPPORTED_PLATFORMS = "macosx iphonesimulator appletvsimulator watchsimulator"; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + }; + name = Test; + }; + 9A1675481F80C35100B63650 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SUPPORTED_PLATFORMS = "macosx iphonesimulator appletvsimulator watchsimulator"; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + }; + name = Release; + }; + 9A1675491F80C35100B63650 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SUPPORTED_PLATFORMS = "macosx iphonesimulator appletvsimulator watchsimulator"; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + }; + name = Profile; + }; + 9AC03A5D1F7CC3BF00EC33C1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = ReactiveMapKit/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @executable_path/../Frameworks @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "macosx appletvsimulator watchsimulator appletvos watchos iphoneos iphonesimulator"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VALID_ARCHS = "i386 x86_64 armv7s arm64 armv7 armv7k"; + }; + name = Debug; + }; + 9AC03A5E1F7CC3BF00EC33C1 /* Test */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = ReactiveMapKit/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @executable_path/../Frameworks @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "macosx appletvsimulator watchsimulator appletvos watchos iphoneos iphonesimulator"; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + VALIDATE_PRODUCT = YES; + VALID_ARCHS = "i386 x86_64 armv7s arm64 armv7 armv7k"; + }; + name = Test; + }; + 9AC03A5F1F7CC3BF00EC33C1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = ReactiveMapKit/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @executable_path/../Frameworks @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "macosx appletvsimulator watchsimulator appletvos watchos iphoneos iphonesimulator"; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + VALIDATE_PRODUCT = YES; + VALID_ARCHS = "i386 x86_64 armv7s arm64 armv7 armv7k"; + }; + name = Release; + }; + 9AC03A601F7CC3BF00EC33C1 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = ReactiveMapKit/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @executable_path/../Frameworks @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "macosx appletvsimulator watchsimulator appletvos watchos iphoneos iphonesimulator"; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + VALIDATE_PRODUCT = YES; + VALID_ARCHS = "i386 x86_64 armv7s arm64 armv7 armv7k"; + }; + name = Profile; + }; A9B315591B3940610001CB9C /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */; @@ -1725,6 +2349,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047262919E49FE8006002AA /* Debug.xcconfig */; buildSettings = { + APPLETVOS_DEPLOYMENT_TARGET = 9.0; BITCODE_GENERATION_MODE = bitcode; CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; @@ -1745,6 +2370,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047262B19E49FE8006002AA /* Release.xcconfig */; buildSettings = { + APPLETVOS_DEPLOYMENT_TARGET = 9.0; BITCODE_GENERATION_MODE = bitcode; CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; @@ -1865,6 +2491,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047262A19E49FE8006002AA /* Profile.xcconfig */; buildSettings = { + APPLETVOS_DEPLOYMENT_TARGET = 9.0; BITCODE_GENERATION_MODE = bitcode; CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; @@ -1934,6 +2561,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047262C19E49FE8006002AA /* Test.xcconfig */; buildSettings = { + APPLETVOS_DEPLOYMENT_TARGET = 9.0; BITCODE_GENERATION_MODE = bitcode; CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; @@ -2024,6 +2652,28 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 9A1675451F80C35100B63650 /* Build configuration list for PBXNativeTarget "ReactiveMapKitTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9A1675461F80C35100B63650 /* Debug */, + 9A1675471F80C35100B63650 /* Test */, + 9A1675481F80C35100B63650 /* Release */, + 9A1675491F80C35100B63650 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 9AC03A5C1F7CC3BF00EC33C1 /* Build configuration list for PBXNativeTarget "ReactiveMapKit" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9AC03A5D1F7CC3BF00EC33C1 /* Debug */, + 9AC03A5E1F7CC3BF00EC33C1 /* Test */, + 9AC03A5F1F7CC3BF00EC33C1 /* Release */, + 9AC03A601F7CC3BF00EC33C1 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; A9B3155D1B3940610001CB9C /* Build configuration list for PBXNativeTarget "ReactiveCocoa-watchOS" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme index afada7a200..f91892c9ed 100644 --- a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme +++ b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme @@ -96,6 +96,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "" shouldUseLaunchSchemeArgsEnv = "YES"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec new file mode 100644 index 0000000000..a30b31ddde --- /dev/null +++ b/ReactiveMapKit.podspec @@ -0,0 +1,23 @@ +Pod::Spec.new do |s| + s.name = "ReactiveMapKit" + s.version = "6.1.0-alpha.2" + s.summary = "MapKit bindings for ReactiveCocoa." + s.description = <<-DESC + Provide MapKit bindings for ReactiveCocoa. ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. + DESC + s.homepage = "https://github.com/ReactiveCocoa/ReactiveCocoa" + s.license = { :type => "MIT", :file => "LICENSE.md" } + s.author = "ReactiveCocoa" + + s.osx.deployment_target = "10.9" + s.ios.deployment_target = "8.0" + s.tvos.deployment_target = "9.0" + s.watchos.deployment_target = "2.0" + + s.source = { :git => "https://github.com/ReactiveCocoa/ReactiveCocoa.git", :tag => "#{s.version}" } + s.source_files = "ReactiveMapKit/*.{swift,h,m}" + + s.dependency 'ReactiveCocoa', "#{s.version}" + + s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "-suppress-warnings" } +end diff --git a/ReactiveMapKit/Info.plist b/ReactiveMapKit/Info.plist new file mode 100644 index 0000000000..63568f1c11 --- /dev/null +++ b/ReactiveMapKit/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 6.0.1 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/ReactiveCocoa/Shared/MKMapView.swift b/ReactiveMapKit/MKMapView.swift similarity index 92% rename from ReactiveCocoa/Shared/MKMapView.swift rename to ReactiveMapKit/MKMapView.swift index 3e206bc388..a19f386d46 100644 --- a/ReactiveCocoa/Shared/MKMapView.swift +++ b/ReactiveMapKit/MKMapView.swift @@ -1,6 +1,8 @@ import ReactiveSwift +import ReactiveCocoa import MapKit +#if os(iOS) || os(tvOS) || os(macOS) @available(tvOS 9.2, *) extension Reactive where Base: MKMapView { @@ -31,3 +33,4 @@ extension Reactive where Base: MKMapView { } #endif } +#endif diff --git a/ReactiveMapKitTests/Info.plist b/ReactiveMapKitTests/Info.plist new file mode 100644 index 0000000000..6c40a6cd0c --- /dev/null +++ b/ReactiveMapKitTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/ReactiveCocoaTests/Shared/MKMapViewSpec.swift b/ReactiveMapKitTests/MKMapViewSpec.swift similarity index 99% rename from ReactiveCocoaTests/Shared/MKMapViewSpec.swift rename to ReactiveMapKitTests/MKMapViewSpec.swift index f5e52640b2..1039aae897 100644 --- a/ReactiveCocoaTests/Shared/MKMapViewSpec.swift +++ b/ReactiveMapKitTests/MKMapViewSpec.swift @@ -1,5 +1,6 @@ import ReactiveSwift import ReactiveCocoa +import ReactiveMapKit import Quick import Nimble import enum Result.NoError From 99fc9f8d3daad02cd544bbb0fee25cbfe9afd82c Mon Sep 17 00:00:00 2001 From: Sho Ikeda Date: Thu, 5 Oct 2017 23:25:54 +0900 Subject: [PATCH 0858/1028] Update Quick and Nimble --- Cartfile.private | 4 ++-- Cartfile.resolved | 4 ++-- Carthage/Checkouts/Nimble | 2 +- Carthage/Checkouts/Quick | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cartfile.private b/Cartfile.private index 0d46b9e682..d3790f5a79 100644 --- a/Cartfile.private +++ b/Cartfile.private @@ -1,3 +1,3 @@ github "jspahrsummers/xcconfigs" "3d9d996" -github "Quick/Quick" ~> 1.1 -github "Quick/Nimble" ~> 7.0.1 +github "Quick/Quick" ~> 1.2 +github "Quick/Nimble" ~> 7.0.2 diff --git a/Cartfile.resolved b/Cartfile.resolved index 98acb00b48..3adf0bca5e 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,5 +1,5 @@ -github "Quick/Nimble" "v7.0.1" -github "Quick/Quick" "v1.1.0" +github "Quick/Nimble" "v7.0.2" +github "Quick/Quick" "v1.2.0" github "ReactiveCocoa/ReactiveSwift" "2.1.0-alpha.2" github "antitypical/Result" "3.2.3" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" diff --git a/Carthage/Checkouts/Nimble b/Carthage/Checkouts/Nimble index 39b6700230..38c9ab0846 160000 --- a/Carthage/Checkouts/Nimble +++ b/Carthage/Checkouts/Nimble @@ -1 +1 @@ -Subproject commit 39b67002306fda9de4c9fd1290a6295f97edd09e +Subproject commit 38c9ab0846a3fbec308eb2aa9ef68b10a7434eb4 diff --git a/Carthage/Checkouts/Quick b/Carthage/Checkouts/Quick index e4fa1e85c0..0ff81f2c66 160000 --- a/Carthage/Checkouts/Quick +++ b/Carthage/Checkouts/Quick @@ -1 +1 @@ -Subproject commit e4fa1e85c0305ba4e0866f25812d3fa398f3a048 +Subproject commit 0ff81f2c665b4381f526bd656f8708dd52a9ea2f From 430b253d50cd7193ca25461a054c3b4344a0e6fc Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 7 Oct 2017 00:56:15 +0800 Subject: [PATCH 0859/1028] Update ReactiveMapKit build settings. --- ReactiveCocoa.xcodeproj/project.pbxproj | 194 +----------------------- 1 file changed, 8 insertions(+), 186 deletions(-) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 6c29c7538e..cb7862fa92 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -1810,6 +1810,7 @@ }; 9A1675461F80C35100B63650 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = D047262E19E49FE8006002AA /* Application.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -1872,6 +1873,7 @@ }; 9A1675471F80C35100B63650 /* Test */ = { isa = XCBuildConfiguration; + baseConfigurationReference = D047262E19E49FE8006002AA /* Application.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -1927,6 +1929,7 @@ }; 9A1675481F80C35100B63650 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = D047262E19E49FE8006002AA /* Application.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -1982,6 +1985,7 @@ }; 9A1675491F80C35100B63650 /* Profile */ = { isa = XCBuildConfiguration; + baseConfigurationReference = D047262E19E49FE8006002AA /* Application.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -2037,246 +2041,64 @@ }; 9AC03A5D1F7CC3BF00EC33C1 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = D047262F19E49FE8006002AA /* Framework.xcconfig */; buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = ReactiveMapKit/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @executable_path/../Frameworks @executable_path/Frameworks @loader_path/Frameworks"; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = macosx; - SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = "macosx appletvsimulator watchsimulator appletvos watchos iphoneos iphonesimulator"; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; VALID_ARCHS = "i386 x86_64 armv7s arm64 armv7 armv7k"; }; name = Debug; }; 9AC03A5E1F7CC3BF00EC33C1 /* Test */ = { isa = XCBuildConfiguration; + baseConfigurationReference = D047262F19E49FE8006002AA /* Framework.xcconfig */; buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = ReactiveMapKit/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @executable_path/../Frameworks @executable_path/Frameworks @loader_path/Frameworks"; - MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = macosx; - SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = "macosx appletvsimulator watchsimulator appletvos watchos iphoneos iphonesimulator"; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - VALIDATE_PRODUCT = YES; VALID_ARCHS = "i386 x86_64 armv7s arm64 armv7 armv7k"; }; name = Test; }; 9AC03A5F1F7CC3BF00EC33C1 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = D047262F19E49FE8006002AA /* Framework.xcconfig */; buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = ReactiveMapKit/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @executable_path/../Frameworks @executable_path/Frameworks @loader_path/Frameworks"; - MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = macosx; - SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = "macosx appletvsimulator watchsimulator appletvos watchos iphoneos iphonesimulator"; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - VALIDATE_PRODUCT = YES; VALID_ARCHS = "i386 x86_64 armv7s arm64 armv7 armv7k"; }; name = Release; }; 9AC03A601F7CC3BF00EC33C1 /* Profile */ = { isa = XCBuildConfiguration; + baseConfigurationReference = D047262F19E49FE8006002AA /* Framework.xcconfig */; buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = ReactiveMapKit/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @executable_path/../Frameworks @executable_path/Frameworks @loader_path/Frameworks"; - MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = macosx; - SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = "macosx appletvsimulator watchsimulator appletvos watchos iphoneos iphonesimulator"; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - VALIDATE_PRODUCT = YES; VALID_ARCHS = "i386 x86_64 armv7s arm64 armv7 armv7k"; }; name = Profile; From a9edd503b086fe9e6ebbfae50baccf9124e946da Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 7 Oct 2017 16:25:07 +0800 Subject: [PATCH 0860/1028] 7.0.0-alpha.1 --- CHANGELOG.md | 2 ++ ReactiveCocoa.podspec | 2 +- ReactiveMapKit.podspec | 2 +- script/update-version | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6133779974..b9d71d69bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # master *Please put new entries at the top. +# 7.0.0-alpha.1 +# 7.0.0-alpha.1 1. MapKit reactive bindings have been moved to a new **ReactiveMapKit** framework. (#3524) Sources that use the MapKit bindings are now required to import ReactiveMapKit. diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 5b9a521c37..aa9e088a5a 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "6.1.0-alpha.2" + s.version = "7.0.0-alpha.1" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index a30b31ddde..5cb8f6485a 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveMapKit" - s.version = "6.1.0-alpha.2" + s.version = "7.0.0-alpha.1" s.summary = "MapKit bindings for ReactiveCocoa." s.description = <<-DESC Provide MapKit bindings for ReactiveCocoa. ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/script/update-version b/script/update-version index 34dbb05892..71d5b41a25 100755 --- a/script/update-version +++ b/script/update-version @@ -12,7 +12,7 @@ if [[ -z "$PRERELEASE_STRIPPED" ]]; then exit fi -CURRENT_TAG=$(perl -0777 -ne '/s.version([\s]+)=([\s]+)"(.+)"/ and print $3' *.podspec) +CURRENT_TAG=$(perl -0777 -ne '/s.version([\s]+)=([\s]+)"(.+)"/ and print $3 and last' *.podspec) echo "Current tag: $CURRENT_TAG" perl -0777 -i -pe 's/s.version([\s]+)=([\s]+)"'${CURRENT_TAG}'"/s.version$1=$2"'${1}'"/' *.podspec From dac9c428d1e42401b7b9643194ec572618d78c44 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Wed, 4 Oct 2017 13:40:02 +0800 Subject: [PATCH 0861/1028] Swift 4. --- CHANGELOG.md | 3 +- ReactiveCocoa.xcodeproj/project.pbxproj | 4 ++ ReactiveCocoa/AppKit/ActionProxy.swift | 2 +- ReactiveCocoa/AppKit/NSButton.swift | 12 ++++++ ReactiveCocoa/AppKit/NSControl.swift | 8 ++++ ReactiveCocoa/AppKit/NSTextField.swift | 8 +++- ReactiveCocoa/DelegateProxy.swift | 4 +- ReactiveCocoa/NSObject+Intercepting.swift | 21 +++++----- ReactiveCocoa/NSObject+Lifetime.swift | 10 +++-- ReactiveCocoa/NSObject+Synchronizing.swift | 2 +- ReactiveCocoa/ObjC+Runtime.swift | 12 +++--- ReactiveCocoa/ObjC+RuntimeSubclassing.swift | 2 +- .../AppKit/ActionProxySpec.swift | 10 ++--- ReactiveCocoaTests/AppKit/NSButtonSpec.swift | 40 +++++++++---------- ReactiveCocoaTests/AppKit/NSControlSpec.swift | 6 ++- .../AppKit/NSImageViewSpec.swift | 4 ++ .../AppKit/Swift4TestInteroperability.swift | 13 ++++++ ReactiveCocoaTests/DelegateProxySpec.swift | 6 +-- ReactiveCocoaTests/DeprecationsSpec.swift | 2 +- ReactiveCocoaTests/DynamicPropertySpec.swift | 6 +-- .../InterceptingPerformanceTests.swift | 12 +++--- ReactiveCocoaTests/InterceptingSpec.swift | 32 +++++++-------- .../KeyValueObservingSpec.swift | 12 +++--- ...ol+EnableSendActionsForControlEvents.swift | 10 ++--- ReactiveCocoaTests/UIKit/UITextViewSpec.swift | 8 ++++ 25 files changed, 159 insertions(+), 90 deletions(-) create mode 100644 ReactiveCocoaTests/AppKit/Swift4TestInteroperability.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index b9d71d69bf..4e4175fd19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ # master *Please put new entries at the top. -# 7.0.0-alpha.1 +1. ReactiveCocoa is now compatible with the Swift 4.0 language mode, in addition to the Swift 3.2 compatibility mode. (#3526, kudos to @andersio) + # 7.0.0-alpha.1 1. MapKit reactive bindings have been moved to a new **ReactiveMapKit** framework. (#3524) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index cb7862fa92..2afcd1b91a 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -205,6 +205,7 @@ 9AA0BD9B1DDE7A2200531FCF /* ObjCRuntimeAliases.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD971DDE7A2200531FCF /* ObjCRuntimeAliases.m */; }; 9AAD49881DED2C350068EC9B /* UIKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AAD49871DED2C350068EC9B /* UIKeyboard.swift */; }; 9AAD498A1DED2F380068EC9B /* UIKeyboardSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AAD49891DED2F380068EC9B /* UIKeyboardSpec.swift */; }; + 9AADB6F41F84AECB00EFFD19 /* Swift4TestInteroperability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AADB6F31F84AECB00EFFD19 /* Swift4TestInteroperability.swift */; }; 9AB15C7A1E26CD9A00997378 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AB15C791E26CD9A00997378 /* Deprecations+Removals.swift */; }; 9AB15C7B1E26CD9A00997378 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AB15C791E26CD9A00997378 /* Deprecations+Removals.swift */; }; 9AB15C7C1E26CD9A00997378 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AB15C791E26CD9A00997378 /* Deprecations+Removals.swift */; }; @@ -476,6 +477,7 @@ 9AA0BD971DDE7A2200531FCF /* ObjCRuntimeAliases.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ObjCRuntimeAliases.m; sourceTree = ""; }; 9AAD49871DED2C350068EC9B /* UIKeyboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIKeyboard.swift; sourceTree = ""; }; 9AAD49891DED2F380068EC9B /* UIKeyboardSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIKeyboardSpec.swift; sourceTree = ""; }; + 9AADB6F31F84AECB00EFFD19 /* Swift4TestInteroperability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Swift4TestInteroperability.swift; sourceTree = ""; }; 9AB15C791E26CD9A00997378 /* Deprecations+Removals.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deprecations+Removals.swift"; sourceTree = ""; }; 9AC03A571F7CC3BF00EC33C1 /* ReactiveMapKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveMapKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9AC03A5A1F7CC3BF00EC33C1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -779,6 +781,7 @@ D9558AB51DFF7B90003254E1 /* NSPopUpButtonSpec.swift */, 004FD0061E26CDB300A03A82 /* NSButtonSpec.swift */, 834DE1001E4109750099F4E5 /* NSImageViewSpec.swift */, + 9AADB6F31F84AECB00EFFD19 /* Swift4TestInteroperability.swift */, ); path = AppKit; sourceTree = ""; @@ -1547,6 +1550,7 @@ 9A6AAA2B1DB8F85C0013AAEA /* ReusableComponentsSpec.swift in Sources */, CD8401831CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, 9A9DFEE91DA7EFB60039EE1B /* AssociationSpec.swift in Sources */, + 9AADB6F41F84AECB00EFFD19 /* Swift4TestInteroperability.swift in Sources */, 3B30EE8C1E7BE529007CC8EF /* DeprecationsSpec.swift in Sources */, CD42C69B1E951F6900AA9504 /* ReactiveCocoaTestsConfiguration.swift in Sources */, 9A1E72BA1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, diff --git a/ReactiveCocoa/AppKit/ActionProxy.swift b/ReactiveCocoa/AppKit/ActionProxy.swift index 12e65e05d5..be5e150d47 100644 --- a/ReactiveCocoa/AppKit/ActionProxy.swift +++ b/ReactiveCocoa/AppKit/ActionProxy.swift @@ -46,7 +46,7 @@ extension Reactive where Base: NSObject, Base: ActionMessageSending { return proxy } - let superclass: AnyClass = class_getSuperclass(swizzleClass(base)) + let superclass: AnyClass = class_getSuperclass(swizzleClass(base))! let proxy = ActionProxy(owner: base, lifetime: lifetime) proxy.target = base.target diff --git a/ReactiveCocoa/AppKit/NSButton.swift b/ReactiveCocoa/AppKit/NSButton.swift index 677efbf2d8..33aa8e776e 100644 --- a/ReactiveCocoa/AppKit/NSButton.swift +++ b/ReactiveCocoa/AppKit/NSButton.swift @@ -27,6 +27,17 @@ extension Reactive where Base: NSButton { } } + #if swift(>=4.0) + /// A signal of integer states (On, Off, Mixed), emitted by the button. + public var states: Signal { + return proxy.invoked.map { $0.state } + } + + /// Sets the button's state + public var state: BindingTarget { + return makeBindingTarget { $0.state = $1 } + } + #else /// A signal of integer states (On, Off, Mixed), emitted by the button. public var states: Signal { return proxy.invoked.map { $0.state } @@ -36,6 +47,7 @@ extension Reactive where Base: NSButton { public var state: BindingTarget { return makeBindingTarget { $0.state = $1 } } + #endif /// Sets the button's image public var image: BindingTarget { diff --git a/ReactiveCocoa/AppKit/NSControl.swift b/ReactiveCocoa/AppKit/NSControl.swift index 1e29888a45..f73f4393d2 100644 --- a/ReactiveCocoa/AppKit/NSControl.swift +++ b/ReactiveCocoa/AppKit/NSControl.swift @@ -22,12 +22,20 @@ extension Reactive where Base: NSControl { /// Sets the value of the control with a `Bool`. public var boolValue: BindingTarget { + #if swift(>=4.0) + return makeBindingTarget { $0.integerValue = $1 ? NSControl.StateValue.on.rawValue : NSControl.StateValue.off.rawValue } + #else return makeBindingTarget { $0.integerValue = $1 ? NSOnState : NSOffState } + #endif } /// A signal of values in `Bool`, emitted by the control. public var boolValues: Signal { + #if swift(>=4.0) + return proxy.invoked.map { $0.integerValue != NSControl.StateValue.off.rawValue } + #else return proxy.invoked.map { $0.integerValue != NSOffState } + #endif } /// Sets the value of the control with a `Double`. diff --git a/ReactiveCocoa/AppKit/NSTextField.swift b/ReactiveCocoa/AppKit/NSTextField.swift index 3baf51fd70..e8a27095be 100644 --- a/ReactiveCocoa/AppKit/NSTextField.swift +++ b/ReactiveCocoa/AppKit/NSTextField.swift @@ -4,9 +4,15 @@ import enum Result.NoError extension Reactive where Base: NSTextField { private var notifications: Signal { + #if swift(>=4.0) + let name = NSControl.textDidChangeNotification + #else + let name = Notification.Name.NSControlTextDidChange + #endif + return NotificationCenter.default .reactive - .notifications(forName: .NSControlTextDidChange, object: base) + .notifications(forName: name, object: base) .take(during: lifetime) } diff --git a/ReactiveCocoa/DelegateProxy.swift b/ReactiveCocoa/DelegateProxy.swift index 8da519e14b..639cf5d9c8 100644 --- a/ReactiveCocoa/DelegateProxy.swift +++ b/ReactiveCocoa/DelegateProxy.swift @@ -70,7 +70,7 @@ extension DelegateProxy { return proxy } - let superclass: AnyClass = class_getSuperclass(swizzleClass(instance)) + let superclass: AnyClass = class_getSuperclass(swizzleClass(instance))! let invokeSuperSetter: @convention(c) (NSObject, AnyClass, Selector, AnyObject?) -> Void = { object, superclass, selector, delegate in typealias Setter = @convention(c) (NSObject, Selector, AnyObject?) -> Void @@ -99,7 +99,7 @@ extension DelegateProxy { } typealias Getter = @convention(c) (NSObject, Selector) -> AnyObject? - let getterImpl: IMP = class_getMethodImplementation(object_getClass(instance), getter) + let getterImpl: IMP = class_getMethodImplementation(object_getClass(instance), getter)! let original = unsafeBitCast(getterImpl, to: Getter.self)(instance, getter) as! Delegate? // `proxy.forwardee` would invoke the original setter regardless of diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 4e8b3a61b0..1aa2176ef5 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -12,6 +12,8 @@ fileprivate let signatureCacheKey = AssociationKey() /// Holds the method selector cache of the runtime subclass. fileprivate let selectorCacheKey = AssociationKey() +internal let noImplementation: IMP = unsafeBitCast(Int(0), to: IMP.self) + extension Reactive where Base: NSObject { /// Create a signal which sends a `next` event at the end of every /// invocation of `selector` on the object. @@ -139,7 +141,7 @@ extension NSObject { /// - parameters: /// - realClass: The runtime subclass to be swizzled. private func enableMessageForwarding(_ realClass: AnyClass, _ selectorCache: SelectorCache) { - let perceivedClass: AnyClass = class_getSuperclass(realClass) + let perceivedClass: AnyClass = class_getSuperclass(realClass)! typealias ForwardInvocationImpl = @convention(block) (Unmanaged, AnyObject) -> Void let newForwardInvocation: ForwardInvocationImpl = { objectRef, invocation in @@ -167,7 +169,7 @@ private func enableMessageForwarding(_ realClass: AnyClass, _ selectorCache: Sel // // However, the IMP cache would be thrashed due to the swapping. - let topLevelClass: AnyClass = object_getClass(objectRef.takeUnretainedValue()) + let topLevelClass: AnyClass = object_getClass(objectRef.takeUnretainedValue())! // The locking below prevents RAC swizzling attempts from intervening the // invocation. @@ -179,12 +181,12 @@ private func enableMessageForwarding(_ realClass: AnyClass, _ selectorCache: Sel synchronized(topLevelClass) { func swizzle() { - let interopImpl = class_getMethodImplementation(topLevelClass, interopAlias) + let interopImpl = class_getMethodImplementation(topLevelClass, interopAlias)! let previousImpl = class_replaceMethod(topLevelClass, selector, interopImpl, typeEncoding) invocation.invoke() - _ = class_replaceMethod(topLevelClass, selector, previousImpl, typeEncoding) + _ = class_replaceMethod(topLevelClass, selector, previousImpl ?? noImplementation, typeEncoding) } if topLevelClass != realClass { @@ -192,7 +194,7 @@ private func enableMessageForwarding(_ realClass: AnyClass, _ selectorCache: Sel // In addition to swapping in the implementation, the message // forwarding needs to be temporarily disabled to prevent circular // invocation. - _ = class_replaceMethod(realClass, selector, nil, typeEncoding) + _ = class_replaceMethod(realClass, selector, noImplementation, typeEncoding) swizzle() _ = class_replaceMethod(realClass, selector, _rac_objc_msgForward, typeEncoding) } @@ -204,7 +206,8 @@ private func enableMessageForwarding(_ realClass: AnyClass, _ selectorCache: Sel return } - if let impl = method_getImplementation(method), impl != _rac_objc_msgForward { + let impl = method_getImplementation(method) + if impl != _rac_objc_msgForward { // The perceived class, or its ancestors, responds to the selector. // // The implementation is invoked through the selector alias, which @@ -227,8 +230,8 @@ private func enableMessageForwarding(_ realClass: AnyClass, _ selectorCache: Sel // inheritance hierarchy, or the default handler returned by the runtime // if it finds no implementation. typealias SuperForwardInvocation = @convention(c) (Unmanaged, Selector, AnyObject) -> Void - let impl = class_getMethodImplementation(perceivedClass, ObjCSelector.forwardInvocation) - let forwardInvocation = unsafeBitCast(impl, to: SuperForwardInvocation.self) + let forwardInvocationImpl = class_getMethodImplementation(perceivedClass, ObjCSelector.forwardInvocation) + let forwardInvocation = unsafeBitCast(forwardInvocationImpl, to: SuperForwardInvocation.self) forwardInvocation(objectRef, ObjCSelector.forwardInvocation, invocation) } @@ -245,7 +248,7 @@ private func enableMessageForwarding(_ realClass: AnyClass, _ selectorCache: Sel /// - realClass: The runtime subclass to be swizzled. /// - signatureCache: The method signature cache. private func setupMethodSignatureCaching(_ realClass: AnyClass, _ signatureCache: SignatureCache) { - let perceivedClass: AnyClass = class_getSuperclass(realClass) + let perceivedClass: AnyClass = class_getSuperclass(realClass)! let newMethodSignatureForSelector: @convention(block) (Unmanaged, Selector) -> AnyObject? = { objectRef, selector in if let signature = signatureCache[selector] { diff --git a/ReactiveCocoa/NSObject+Lifetime.swift b/ReactiveCocoa/NSObject+Lifetime.swift index 40e72c5b49..ddc19a4337 100644 --- a/ReactiveCocoa/NSObject+Lifetime.swift +++ b/ReactiveCocoa/NSObject+Lifetime.swift @@ -44,7 +44,11 @@ extension Reactive where Base: AnyObject & NSObjectProtocol { let objcClass: AnyClass = (base as AnyObject).objcClass let objcClassAssociations = Associations(objcClass as AnyObject) + #if swift(>=4.0) + let deallocSelector = sel_registerName("dealloc") + #else let deallocSelector = sel_registerName("dealloc")! + #endif // Swizzle `-dealloc` so that the lifetime token is released at the // beginning of the deallocation chain, and only after the KVO `-dealloc`. @@ -70,8 +74,8 @@ extension Reactive where Base: AnyObject & NSObjectProtocol { if let existingImpl = existingImpl { impl = existingImpl } else { - let superclass: AnyClass = class_getSuperclass(objcClass) - impl = class_getMethodImplementation(superclass, deallocSelector) + let superclass: AnyClass = class_getSuperclass(objcClass)! + impl = class_getMethodImplementation(superclass, deallocSelector)! } typealias Impl = @convention(c) (UnsafeRawPointer, Selector) -> Void @@ -82,7 +86,7 @@ extension Reactive where Base: AnyObject & NSObjectProtocol { if !class_addMethod(objcClass, deallocSelector, newImpl, "v@:") { // The class has an existing `dealloc`. Preserve that as `existingImpl`. - let deallocMethod = class_getInstanceMethod(objcClass, deallocSelector) + let deallocMethod = class_getInstanceMethod(objcClass, deallocSelector)! // Store the existing implementation to `existingImpl` to ensure it is // available before our version is swapped in. diff --git a/ReactiveCocoa/NSObject+Synchronizing.swift b/ReactiveCocoa/NSObject+Synchronizing.swift index 310d9e78fa..2816bf1a0f 100644 --- a/ReactiveCocoa/NSObject+Synchronizing.swift +++ b/ReactiveCocoa/NSObject+Synchronizing.swift @@ -1,5 +1,5 @@ extension NSObjectProtocol { - internal final func synchronized(execute: () throws -> Result) rethrows -> Result { + internal func synchronized(execute: () throws -> Result) rethrows -> Result { objc_sync_enter(self) defer { objc_sync_exit(self) } return try execute() diff --git a/ReactiveCocoa/ObjC+Runtime.swift b/ReactiveCocoa/ObjC+Runtime.swift index de29eb7a21..420f8332f6 100644 --- a/ReactiveCocoa/ObjC+Runtime.swift +++ b/ReactiveCocoa/ObjC+Runtime.swift @@ -7,15 +7,17 @@ /// /// - returns: The matching method, or `nil` if none is found. internal func class_getImmediateMethod(_ `class`: AnyClass, _ selector: Selector) -> Method? { - if let buffer = class_copyMethodList(`class`, nil) { - defer { free(buffer) } + var total: UInt32 = 0 + + if let methods = class_copyMethodList(`class`, &total) { + defer { free(methods) } + + for index in 0 ..< Int(total) { + let method = methods[index] - var iterator = buffer - while let method = iterator.pointee { if method_getName(method) == selector { return method } - iterator = iterator.advanced(by: 1) } } diff --git a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift index de3fb05cfa..11609d473b 100644 --- a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift +++ b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift @@ -30,7 +30,7 @@ extension NSObject { subclassAssociations.setValue(true, forKey: hasSwizzledKey) for (selector, body) in pairs { - let method = class_getInstanceMethod(subclass, selector) + let method = class_getInstanceMethod(subclass, selector)! let typeEncoding = method_getTypeEncoding(method)! if method_getImplementation(method) == _rac_objc_msgForward { diff --git a/ReactiveCocoaTests/AppKit/ActionProxySpec.swift b/ReactiveCocoaTests/AppKit/ActionProxySpec.swift index 930853d8ff..f7b5c6f0d9 100644 --- a/ReactiveCocoaTests/AppKit/ActionProxySpec.swift +++ b/ReactiveCocoaTests/AppKit/ActionProxySpec.swift @@ -5,10 +5,10 @@ import ReactiveSwift @testable import ReactiveCocoa private class Object: NSObject, ActionMessageSending { - dynamic var objectValue: AnyObject? = nil + @objc dynamic var objectValue: AnyObject? = nil - dynamic weak var target: AnyObject? - dynamic var action: Selector? + @objc dynamic weak var target: AnyObject? + @objc dynamic var action: Selector? deinit { target = nil @@ -19,7 +19,7 @@ private class Object: NSObject, ActionMessageSending { private class Receiver: NSObject { var counter = 0 - func foo() { + @objc func foo() { counter += 1 } } @@ -154,7 +154,7 @@ class ActionProxySpec: QuickSpec { expect(object.objcClass).to(beIdenticalTo(object2.objcClass)) - let className = NSStringFromClass(object_getClass(object)) + let className = NSStringFromClass(object_getClass(object)!) expect(className).to(beginWith("NSKVONotifying_")) expect(className).toNot(endWith("_RACSwift")) diff --git a/ReactiveCocoaTests/AppKit/NSButtonSpec.swift b/ReactiveCocoaTests/AppKit/NSButtonSpec.swift index bbfdeb0e4f..7bcab96947 100644 --- a/ReactiveCocoaTests/AppKit/NSButtonSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSButtonSpec.swift @@ -42,45 +42,45 @@ class NSButtonSpec: QuickSpec { it("should accept changes from bindings to its state") { button.allowsMixedState = true - button.state = NSOffState + button.state = RACNSOffState - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() button.reactive.state <~ SignalProducer(pipeSignal) - observer.send(value: NSOffState) - expect(button.state) == NSOffState + observer.send(value: RACNSOffState) + expect(button.state) == RACNSOffState - observer.send(value: NSMixedState) - expect(button.state) == NSMixedState + observer.send(value: RACNSMixedState) + expect(button.state) == RACNSMixedState - observer.send(value: NSOnState) - expect(button.state) == NSOnState + observer.send(value: RACNSOnState) + expect(button.state) == RACNSOnState } it("should send along state changes") { button.setButtonType(.pushOnPushOff) button.allowsMixedState = false - button.state = NSOffState + button.state = RACNSOffState - let state = MutableProperty(NSOffState) + let state = MutableProperty(RACNSOffState) state <~ button.reactive.states button.performClick(nil) - expect(state.value) == NSOnState + expect(state.value) == RACNSOnState button.performClick(nil) - expect(state.value) == NSOffState + expect(state.value) == RACNSOffState button.allowsMixedState = true button.performClick(nil) - expect(state.value) == NSMixedState + expect(state.value) == RACNSMixedState button.performClick(nil) - expect(state.value) == NSOnState + expect(state.value) == RACNSOnState button.performClick(nil) - expect(state.value) == NSOffState + expect(state.value) == RACNSOffState } @@ -94,11 +94,11 @@ class NSButtonSpec: QuickSpec { button1.setButtonType(.pushOnPushOff) button1.allowsMixedState = false - button1.state = NSOffState + button1.state = RACNSOffState button2.setButtonType(.pushOnPushOff) button2.allowsMixedState = false - button2.state = NSOnState + button2.state = RACNSOnState let stackView = NSStackView() stackView.addArrangedSubview(button1) @@ -106,15 +106,15 @@ class NSButtonSpec: QuickSpec { window.contentView?.addSubview(stackView) - let state = MutableProperty(NSOffState) + let state = MutableProperty(RACNSOffState) state <~ button1.reactive.states state <~ button2.reactive.states button1.performClick(nil) - expect(state.value) == NSOnState + expect(state.value) == RACNSOnState button2.performClick(nil) - expect(state.value) == NSOffState + expect(state.value) == RACNSOffState autoreleasepool { button1.removeFromSuperview() diff --git a/ReactiveCocoaTests/AppKit/NSControlSpec.swift b/ReactiveCocoaTests/AppKit/NSControlSpec.swift index d34aaab27f..e95b95cdd9 100644 --- a/ReactiveCocoaTests/AppKit/NSControlSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSControlSpec.swift @@ -17,7 +17,11 @@ class NSControlSpec: QuickSpec { control = NSButton(frame: .zero) control.setButtonType(.onOff) - control.state = NSOffState + #if swift(>=4.0) + control.state = .off + #else + control.state = RACNSOffState + #endif _control = control diff --git a/ReactiveCocoaTests/AppKit/NSImageViewSpec.swift b/ReactiveCocoaTests/AppKit/NSImageViewSpec.swift index 055a268dc6..f424235e22 100644 --- a/ReactiveCocoaTests/AppKit/NSImageViewSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSImageViewSpec.swift @@ -38,7 +38,11 @@ class NSImageViewSpec: QuickSpec { let (pipeSignal, observer) = Signal.pipe() imageView.reactive.image <~ SignalProducer(pipeSignal) + #if swift(>=4.0) + let theImage = NSImage(named: .user) + #else let theImage = NSImage(named: NSImageNameUser) + #endif observer.send(value: theImage) expect(imageView.image) == theImage diff --git a/ReactiveCocoaTests/AppKit/Swift4TestInteroperability.swift b/ReactiveCocoaTests/AppKit/Swift4TestInteroperability.swift new file mode 100644 index 0000000000..4bc1a31b7e --- /dev/null +++ b/ReactiveCocoaTests/AppKit/Swift4TestInteroperability.swift @@ -0,0 +1,13 @@ +import AppKit + +#if swift(>=4.0) +internal typealias RACNSControlState = NSControl.StateValue +internal let RACNSOnState = NSControl.StateValue.on +internal let RACNSOffState = NSControl.StateValue.off +internal let RACNSMixedState = NSControl.StateValue.mixed +#else +internal typealias RACNSControlState = Int +internal let RACNSOnState = NSOnState +internal let RACNSOffState = NSOffState +internal let RACNSMixedState = NSMixedState +#endif diff --git a/ReactiveCocoaTests/DelegateProxySpec.swift b/ReactiveCocoaTests/DelegateProxySpec.swift index e608bda27a..fa20e9cab2 100644 --- a/ReactiveCocoaTests/DelegateProxySpec.swift +++ b/ReactiveCocoaTests/DelegateProxySpec.swift @@ -11,10 +11,10 @@ import ReactiveSwift } private class Object: NSObject { - var delegateSetCount = 0 + @objc var delegateSetCount = 0 var delegateSelectors: [Selector] = [] - dynamic weak var delegate: ObjectDelegate? { + @objc dynamic weak var delegate: ObjectDelegate? { didSet { delegateSetCount += 1 delegateSelectors = Array() @@ -253,7 +253,7 @@ class DelegateProxySpec: QuickSpec { expect(object.objcClass).to(beIdenticalTo(object2.objcClass)) - let className = NSStringFromClass(object_getClass(object)) + let className = NSStringFromClass(object_getClass(object)!) expect(className).to(beginWith("NSKVONotifying_")) expect(className).toNot(endWith("_RACSwift")) diff --git a/ReactiveCocoaTests/DeprecationsSpec.swift b/ReactiveCocoaTests/DeprecationsSpec.swift index d3bfa48a5f..56bfbb7312 100644 --- a/ReactiveCocoaTests/DeprecationsSpec.swift +++ b/ReactiveCocoaTests/DeprecationsSpec.swift @@ -9,7 +9,7 @@ class DeprecationsSpec: QuickSpec { override func spec() { describe("NSObject.reactive.values(forKeyPath:)") { class TestKVOObject: NSObject { - dynamic var value: Int = 0 + @objc dynamic var value: Int = 0 } it("should observe the initial value and changes for the key path") { diff --git a/ReactiveCocoaTests/DynamicPropertySpec.swift b/ReactiveCocoaTests/DynamicPropertySpec.swift index 3db87f17e9..1e73f06408 100644 --- a/ReactiveCocoaTests/DynamicPropertySpec.swift +++ b/ReactiveCocoaTests/DynamicPropertySpec.swift @@ -234,9 +234,9 @@ class DynamicPropertySpec: QuickSpec { } private class ObservableObject: NSObject { - dynamic var rac_value: Int = 0 - dynamic var rac_reference: UnbridgedObject = UnbridgedObject("") - dynamic var rac_unbridged: Any = UnbridgedValue.starting + @objc dynamic var rac_value: Int = 0 + @objc dynamic var rac_reference: UnbridgedObject = UnbridgedObject("") + @objc dynamic var rac_unbridged: Any = UnbridgedValue.starting } private class UnbridgedObject: NSObject { diff --git a/ReactiveCocoaTests/InterceptingPerformanceTests.swift b/ReactiveCocoaTests/InterceptingPerformanceTests.swift index 17ff223c77..df1a5f5d4a 100644 --- a/ReactiveCocoaTests/InterceptingPerformanceTests.swift +++ b/ReactiveCocoaTests/InterceptingPerformanceTests.swift @@ -5,27 +5,27 @@ import ReactiveSwift private let iterationCount = 500 private final class Receiver1: NSObject { - dynamic func message() {} + @objc dynamic func message() {} } private final class Receiver2: NSObject { - dynamic var value: Int = 0 + @objc dynamic var value: Int = 0 } private final class Receiver3: NSObject { - dynamic var value: Int = 0 + @objc dynamic var value: Int = 0 } private final class Receiver4: NSObject { - dynamic var value: Int = 0 + @objc dynamic var value: Int = 0 } private final class Receiver5: NSObject { - dynamic var value: Int = 0 + @objc dynamic var value: Int = 0 } private final class Receiver6: NSObject { - dynamic var value: Int = 0 + @objc dynamic var value: Int = 0 } class InterceptingTests: XCTestCase { diff --git a/ReactiveCocoaTests/InterceptingSpec.swift b/ReactiveCocoaTests/InterceptingSpec.swift index f4a028b9da..bab40c1246 100644 --- a/ReactiveCocoaTests/InterceptingSpec.swift +++ b/ReactiveCocoaTests/InterceptingSpec.swift @@ -353,7 +353,7 @@ class InterceptingSpec: QuickSpec { let original = class_replaceMethod(originalClass, swizzledSelector, _rac_objc_msgForward, - typeEncoding) + typeEncoding) ?? noImplementation defer { _ = class_replaceMethod(originalClass, swizzledSelector, @@ -375,7 +375,7 @@ class InterceptingSpec: QuickSpec { let original2 = class_replaceMethod(originalClass, ObjCSelector.forwardInvocation, imp_implementationWithBlock(forwardInvocationBlock as Any), - typeEncoding2) + typeEncoding2) ?? noImplementation defer { _ = class_replaceMethod(originalClass, ObjCSelector.forwardInvocation, @@ -399,7 +399,7 @@ class InterceptingSpec: QuickSpec { let original = class_replaceMethod(originalClass, swizzledSelector, _rac_objc_msgForward, - typeEncoding) + typeEncoding) ?? noImplementation defer { _ = class_replaceMethod(originalClass, swizzledSelector, @@ -421,7 +421,7 @@ class InterceptingSpec: QuickSpec { let original2 = class_replaceMethod(originalClass, ObjCSelector.forwardInvocation, imp_implementationWithBlock(forwardInvocationBlock as Any), - typeEncoding2) + typeEncoding2) ?? noImplementation defer { _ = class_replaceMethod(originalClass, ObjCSelector.forwardInvocation, @@ -438,7 +438,7 @@ class InterceptingSpec: QuickSpec { let swizzledSelector = #selector(object.lifeIsGood) - let lifeIsGoodBlock: @convention(block) (AnyObject, AnyObject) -> Void = { _ in + let lifeIsGoodBlock: @convention(block) (AnyObject, AnyObject) -> Void = { _, _ in expect(invoked) == false invoked = true } @@ -449,7 +449,7 @@ class InterceptingSpec: QuickSpec { let original = class_replaceMethod(originalClass, swizzledSelector, imp_implementationWithBlock(lifeIsGoodBlock as Any), - typeEncoding) + typeEncoding) ?? noImplementation defer { _ = class_replaceMethod(originalClass, swizzledSelector, @@ -907,27 +907,27 @@ private class InterceptedObjectSubclass: InterceptedObject { private class InterceptedObject: NSObject { var counter = 0 - dynamic var hasInvokedSetObjectValueAndSecondObjectValue = false - dynamic var objectValue: Any? - dynamic var secondObjectValue: Any? + @objc dynamic var hasInvokedSetObjectValueAndSecondObjectValue = false + @objc dynamic var objectValue: Any? + @objc dynamic var secondObjectValue: Any? - dynamic func increment() { + @objc dynamic func increment() { counter += 1 } - dynamic func foo(_ number: Int, _ string: String) -> String { + @objc dynamic func foo(_ number: Int, _ string: String) -> String { return "Not Subclass \(number) \(string)" } - dynamic func lifeIsGood(_ value: Any?) {} - dynamic func set(first: Any?, second: Any?) { + @objc dynamic func lifeIsGood(_ value: Any?) {} + @objc dynamic func set(first: Any?, second: Any?) { objectValue = first secondObjectValue = second hasInvokedSetObjectValueAndSecondObjectValue = true } - dynamic func testNumericValues(c: CChar, s: CShort, i: CInt, l: CLong, ll: CLongLong, uc: CUnsignedChar, us: CUnsignedShort, ui: CUnsignedInt, ul: CUnsignedLong, ull: CUnsignedLongLong, f: CFloat, d: CDouble, b: CBool) {} - dynamic func testReferences(nonnull: NSObject, nullable: NSObject?, iuo: NSObject!, class: AnyClass, nullableClass: AnyClass?, iuoClass: AnyClass!) {} - dynamic func testBridgedStructs(p: CGPoint, s: CGSize, r: CGRect, a: CGAffineTransform) {} + @objc dynamic func testNumericValues(c: CChar, s: CShort, i: CInt, l: CLong, ll: CLongLong, uc: CUnsignedChar, us: CUnsignedShort, ui: CUnsignedInt, ul: CUnsignedLong, ull: CUnsignedLongLong, f: CFloat, d: CDouble, b: CBool) {} + @objc dynamic func testReferences(nonnull: NSObject, nullable: NSObject?, iuo: NSObject!, class: AnyClass, nullableClass: AnyClass?, iuoClass: AnyClass!) {} + @objc dynamic func testBridgedStructs(p: CGPoint, s: CGSize, r: CGRect, a: CGAffineTransform) {} } diff --git a/ReactiveCocoaTests/KeyValueObservingSpec.swift b/ReactiveCocoaTests/KeyValueObservingSpec.swift index 8297aba756..7e3a368108 100644 --- a/ReactiveCocoaTests/KeyValueObservingSpec.swift +++ b/ReactiveCocoaTests/KeyValueObservingSpec.swift @@ -630,12 +630,12 @@ fileprivate class KeyValueObservingSpecConfiguration: QuickConfiguration { private final class Token {} private class ObservableObject: NSObject { - dynamic var rac_value: Int = 0 + @objc dynamic var rac_value: Int = 0 - dynamic var target: AnyObject? - dynamic weak var weakTarget: AnyObject? + @objc dynamic var target: AnyObject? + @objc dynamic weak var weakTarget: AnyObject? - dynamic var rac_value_plusOne: NSDecimalNumber { + @objc dynamic var rac_value_plusOne: NSDecimalNumber { return NSDecimalNumber(value: rac_value + 1) } @@ -649,8 +649,8 @@ private class ObservableObject: NSObject { } private class NestedObservableObject: NSObject { - dynamic var rac_object: ObservableObject = ObservableObject() - dynamic weak var rac_weakObject: ObservableObject? + @objc dynamic var rac_object: ObservableObject = ObservableObject() + @objc dynamic weak var rac_weakObject: ObservableObject? } private class TestAttributeQueryObject: NSObject { diff --git a/ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift b/ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift index 7bacab3e50..c771d7a2f8 100644 --- a/ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift +++ b/ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift @@ -4,19 +4,19 @@ private func rac_swizzle() { let originalSelector = #selector(UIControl.sendAction(_:to:for:)) let swizzledSelector = #selector(UIControl.rac_sendAction(_:to:forEvent:)) - let originalMethod = class_getInstanceMethod(UIControl.self, originalSelector) - let swizzledMethod = class_getInstanceMethod(UIControl.self, swizzledSelector) + 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)) + method_getTypeEncoding(swizzledMethod)!) if didAddMethod { class_replaceMethod(UIControl.self, swizzledSelector, method_getImplementation(originalMethod), - method_getTypeEncoding(originalMethod)) + method_getTypeEncoding(originalMethod)!) } else { method_exchangeImplementations(originalMethod, swizzledMethod) } @@ -34,7 +34,7 @@ extension UIControl { // MARK: - Method Swizzling - func rac_sendAction(_ action: Selector, to target: AnyObject?, forEvent event: UIEvent?) { + @objc func rac_sendAction(_ action: Selector, to target: AnyObject?, forEvent event: UIEvent?) { _ = target?.perform(action, with: self) } } diff --git a/ReactiveCocoaTests/UIKit/UITextViewSpec.swift b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift index 26a8af31f9..f35058c87d 100644 --- a/ReactiveCocoaTests/UIKit/UITextViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift @@ -9,10 +9,18 @@ class UITextViewSpec: QuickSpec { override func spec() { var textView: UITextView! weak var _textView: UITextView? + + #if swift(>=4.0) + let attributes: [NSAttributedStringKey: Any] = [ + .font: UIFont.systemFont(ofSize: 18), + .foregroundColor: UIColor.red + ] + #else let attributes = [ NSFontAttributeName: UIFont.systemFont(ofSize: 18), NSForegroundColorAttributeName: UIColor.red ] + #endif beforeEach { autoreleasepool { From 58cb849558c43d975996d4c7a158e1408aec82af Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sun, 8 Oct 2017 15:37:14 +0800 Subject: [PATCH 0862/1028] Update Travis CI configuration. --- .travis.yml | 117 +++++++++++++++------------------------------------ script/build | 14 +++--- 2 files changed, 39 insertions(+), 92 deletions(-) diff --git a/.travis.yml b/.travis.yml index f73ca4d553..41900d5a25 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,100 +1,51 @@ language: objective-c -osx_image: xcode8.3 +osx_image: xcode9 before_install: - brew uninstall carthage - HOMEBREW_NO_AUTO_UPDATE=1 brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/6ae4f69a652fb0ecb102b0c9216378679a4f1b92/Formula/carthage.rb # 0.22.0 install: true -git: - submodules: false branches: only: - master -before_script: - - git submodule update --init --recursive -script: - - script/build -xcode_workspace: ReactiveCocoa.xcworkspace + - /^(\d+\.\d+\.\d+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/ + - /^hotfix-(\d+\.\d+\.\d+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/ cache: - directories: Carthage/Build -matrix: + directories: + - $HOME/Library/Caches/org.carthage.CarthageKit/dependencies + - Carthage/Build +jobs: include: - # Xcode 8 - - xcode_scheme: ReactiveCocoa-macOS - env: - - XCODE_SDK=macosx - - XCODE_ACTION="build test" - - XCODE_DESTINATION="arch=x86_64" - - XCODE_PLAYGROUND_TARGET="x86_64-apple-macosx10.10" - - PLAYGROUND="ReactiveCocoa-macOS.playground" - - xcode_scheme: ReactiveCocoa-iOS - env: - - XCODE_SDK=iphonesimulator - - XCODE_ACTION="build-for-testing test-without-building" - - XCODE_DESTINATION="platform=iOS Simulator,name=iPhone 6s" -# - XCODE_PLAYGROUND_TARGET="x86_64-apple-ios9.3" -# - PLAYGROUND="ReactiveCocoa-iOS.playground" - - xcode_scheme: ReactiveCocoa-tvOS - env: - - XCODE_SDK=appletvsimulator - - XCODE_ACTION="build-for-testing test-without-building" - - XCODE_DESTINATION="platform=tvOS Simulator,name=Apple TV 1080p" -# - XCODE_PLAYGROUND_TARGET="x86_64-apple-tvos9.3" -# - PLAYGROUND="ReactiveCocoa-tvOS.playground" - - xcode_scheme: ReactiveCocoa-watchOS - env: - - XCODE_SDK=watchsimulator - - XCODE_ACTION=build - - XCODE_DESTINATION="platform=watchOS Simulator,name=Apple Watch - 38mm,OS=2.2" - # Xcode 9 - - xcode_scheme: ReactiveCocoa-macOS - osx_image: xcode9 - env: - - XCODE_SDK=macosx - - XCODE_ACTION="build test" - - XCODE_DESTINATION="arch=x86_64" - - XCODE_PLAYGROUND_TARGET="x86_64-apple-macosx10.10" - - PLAYGROUND="ReactiveCocoa-macOS.playground" - - xcode_scheme: ReactiveCocoa-iOS - osx_image: xcode9 - env: - - XCODE_SDK=iphonesimulator - - XCODE_ACTION="build-for-testing test-without-building" - - XCODE_DESTINATION="platform=iOS Simulator,name=iPhone 6s" -# - XCODE_PLAYGROUND_TARGET="x86_64-apple-ios9.3" -# - PLAYGROUND="ReactiveCocoa-iOS.playground" - - xcode_scheme: ReactiveCocoa-tvOS - osx_image: xcode9 - env: - - XCODE_SDK=appletvsimulator - - XCODE_ACTION="build-for-testing test-without-building" - - XCODE_DESTINATION="platform=tvOS Simulator,name=Apple TV 1080p" -# - XCODE_PLAYGROUND_TARGET="x86_64-apple-tvos9.3" -# - PLAYGROUND="ReactiveCocoa-tvOS.playground" - - xcode_scheme: ReactiveCocoa-watchOS + - stage: unit tests osx_image: xcode9 - env: - - XCODE_SDK=watchsimulator - - XCODE_ACTION=build - - XCODE_DESTINATION="platform=watchOS Simulator,name=Apple Watch - 38mm,OS=2.2" - - script: + script: + - XCODE_SCHEME=ReactiveCocoa-macOS + XCODE_SDK=macosx + XCODE_ACTION="build test" + XCODE_DESTINATION="arch=x86_64" + XCODE_PLAYGROUND_TARGET="x86_64-apple-macosx10.10" + PLAYGROUND="ReactiveCocoa-macOS.playground" + script/build + - XCODE_SCHEME=ReactiveCocoa-iOS + XCODE_SDK=iphonesimulator + XCODE_ACTION="build-for-testing test-without-building" + XCODE_DESTINATION="platform=iOS Simulator,name=iPhone 6s" + script/build + - XCODE_SCHEME=ReactiveCocoa-tvOS + XCODE_SDK=appletvsimulator + XCODE_ACTION="build-for-testing test-without-building" + XCODE_DESTINATION="platform=tvOS Simulator,name=Apple TV 1080p" + script/build + - XCODE_SCHEME=ReactiveCocoa-watchOS + XCODE_SDK=watchsimulator + XCODE_ACTION=build + XCODE_DESTINATION="platform=watchOS Simulator,name=Apple Watch - 38mm,OS=2.2" + script/build + - stage: package manager tests + script: - gem install cocoapods - pod repo update - pod lib lint ReactiveCocoa.podspec - pod lib lint ReactiveMapKit.podspec env: - JOB=PODSPEC - - stage: prepare carthage cache - script: carthage build --cache-builds - - stage: carthage - script: - - carthage build --cache-builds --no-skip-current --platform mac - - script: - - carthage build --cache-builds --no-skip-current --platform iOS - - script: - - carthage build --cache-builds --no-skip-current --platform tvOS - - script: - - carthage build --cache-builds --no-skip-current --platform watchOS -notifications: - email: false - slack: - secure: C9QTry5wUG9CfeH3rm3Z19R5rDWqDO7EhHAqHDXBxT6CpGRkTPFliJexpjBYB4sroJ8CiY5ZgTI2sjRBiAdGoE5ZQkfnwSoKQhWXkwo19TnbSnufr3cKO2SZkUhBqOlZcA+mgfjZ7rm2wm7RhpCR/4z8oBXDN4/xv0U5R2fLCLE= + - script: carthage build --cache-builds --no-skip-current diff --git a/script/build b/script/build index 6b1f69cf79..fffabcd56b 100755 --- a/script/build +++ b/script/build @@ -2,14 +2,10 @@ BUILD_DIRECTORY="build" CONFIGURATION=Release +XCODE_WORKSPACE=ReactiveCocoa.xcworkspace -if [[ -z $TRAVIS_XCODE_WORKSPACE ]]; then - echo "Error: \$TRAVIS_XCODE_WORKSPACE is not set." - exit 1 -fi - -if [[ -z $TRAVIS_XCODE_SCHEME ]]; then - echo "Error: \$TRAVIS_XCODE_SCHEME is not set!" +if [[ -z $XCODE_SCHEME ]]; then + echo "Error: \$XCODE_SCHEME is not set!" exit 1 fi @@ -30,8 +26,8 @@ fi set -o pipefail xcodebuild $XCODE_ACTION \ - -workspace "$TRAVIS_XCODE_WORKSPACE" \ - -scheme "$TRAVIS_XCODE_SCHEME" \ + -workspace "$XCODE_WORKSPACE" \ + -scheme "$XCODE_SCHEME" \ -sdk "$XCODE_SDK" \ -destination "$XCODE_DESTINATION" \ -derivedDataPath "${BUILD_DIRECTORY}" \ From 5456f498dbe11802a9202e6fa02df4f4b3a3d0e0 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sun, 8 Oct 2017 15:42:04 +0800 Subject: [PATCH 0863/1028] Build ReactiveCocoa with Swift 4.0. --- .swift-version | 2 +- ReactiveCocoa.xcodeproj/project.pbxproj | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.swift-version b/.swift-version index 8c50098d8a..5186d07068 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -3.1 +4.0 diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 2afcd1b91a..9975fb6ba4 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -2184,7 +2184,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -2205,7 +2205,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -2325,7 +2325,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -2395,7 +2395,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)-Tests"; PRODUCT_NAME = "$(PROJECT_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; From fc28cff022363ad49de20101a78bd7e70626d938 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Tue, 10 Oct 2017 09:50:45 +0800 Subject: [PATCH 0864/1028] Temporarily override the Swift version for ReactiveMapKit pod linting. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 41900d5a25..f1654de18e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,7 +45,7 @@ jobs: - gem install cocoapods - pod repo update - pod lib lint ReactiveCocoa.podspec - - pod lib lint ReactiveMapKit.podspec + - pod lib lint ReactiveMapKit.podspec --swift-version=3.2 env: - JOB=PODSPEC - script: carthage build --cache-builds --no-skip-current From eff0fc3f3e63fa35c2deca34f70b7b18732b4d9d Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Fri, 20 Oct 2017 02:04:46 +0800 Subject: [PATCH 0865/1028] Update ReactiveSwift to 3.0.0 alpha 1. --- CHANGELOG.md | 2 ++ Cartfile | 2 +- Cartfile.resolved | 4 ++-- Carthage/Checkouts/ReactiveSwift | 2 +- Carthage/Checkouts/Result | 2 +- ReactiveCocoa.podspec | 2 +- ReactiveCocoa/NSObject+KeyValueObserving.swift | 8 +++----- ReactiveCocoa/UIKit/UIControl.swift | 4 ++-- ReactiveCocoa/UIKit/UIGestureRecognizer.swift | 4 ++-- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e4175fd19..e32f749cfc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # master *Please put new entries at the top. +1. Requires ReactiveSwift 3.0.0 alpha 1. + 1. ReactiveCocoa is now compatible with the Swift 4.0 language mode, in addition to the Swift 3.2 compatibility mode. (#3526, kudos to @andersio) # 7.0.0-alpha.1 diff --git a/Cartfile b/Cartfile index 76e23db613..6319e8edde 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" "2.1.0-alpha.2" +github "ReactiveCocoa/ReactiveSwift" "3.0.0-alpha.1" diff --git a/Cartfile.resolved b/Cartfile.resolved index 3adf0bca5e..af4f101667 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,5 +1,5 @@ github "Quick/Nimble" "v7.0.2" github "Quick/Quick" "v1.2.0" -github "ReactiveCocoa/ReactiveSwift" "2.1.0-alpha.2" -github "antitypical/Result" "3.2.3" +github "ReactiveCocoa/ReactiveSwift" "3.0.0-alpha.1" +github "antitypical/Result" "3.2.4" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index 999cead2e7..eeb62fcae7 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit 999cead2e77dbcb8267ee8fe97042c320c11c142 +Subproject commit eeb62fcae787221f57ef35d359bcacb936bc33c2 diff --git a/Carthage/Checkouts/Result b/Carthage/Checkouts/Result index c844618523..7477584259 160000 --- a/Carthage/Checkouts/Result +++ b/Carthage/Checkouts/Result @@ -1 +1 @@ -Subproject commit c8446185238659a2b27c0261f64ff1254291d07d +Subproject commit 7477584259bfce2560a19e06ad9f71db441fff11 diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index aa9e088a5a..e8e5f2ecaf 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -23,7 +23,7 @@ Pod::Spec.new do |s| s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_map = "ReactiveCocoa/module.modulemap" - s.dependency 'ReactiveSwift', '2.1.0-alpha.2' + s.dependency 'ReactiveSwift', '3.0.0-alpha.1' s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "-suppress-warnings" } end diff --git a/ReactiveCocoa/NSObject+KeyValueObserving.swift b/ReactiveCocoa/NSObject+KeyValueObserving.swift index 4863f112c2..0eed8fb261 100644 --- a/ReactiveCocoa/NSObject+KeyValueObserving.swift +++ b/ReactiveCocoa/NSObject+KeyValueObserving.swift @@ -43,16 +43,14 @@ extension Reactive where Base: NSObject { /// - returns: A producer emitting values of the property specified by the /// key path. public func signal(forKeyPath keyPath: String) -> Signal { - return Signal { observer in - let disposable = CompositeDisposable() - disposable += KeyValueObserver.observe( + return Signal { observer, signalLifetime in + signalLifetime += KeyValueObserver.observe( self.base, keyPath: keyPath, options: [.new], action: observer.send ) - disposable += self.lifetime.observeEnded(observer.sendCompleted) - return disposable + signalLifetime += lifetime.observeEnded(observer.sendCompleted) } } } diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index 8fc347ceed..e443543535 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -72,7 +72,7 @@ extension Reactive where Base: UIControl { /// - returns: A signal that sends the reduced value from the control each time the /// control event occurs. public func mapControlEvents(_ controlEvents: UIControlEvents, _ transform: @escaping (Base) -> Value) -> Signal { - return Signal { observer in + return Signal { observer, signalLifetime in let receiver = CocoaTarget(observer) { transform($0 as! Base) } base.addTarget(receiver, action: #selector(receiver.invoke), @@ -80,7 +80,7 @@ extension Reactive where Base: UIControl { let disposable = lifetime.ended.observeCompleted(observer.sendCompleted) - return AnyDisposable { [weak base = self.base] in + signalLifetime.observeEnded { [weak base] in disposable?.dispose() base?.removeTarget(receiver, diff --git a/ReactiveCocoa/UIKit/UIGestureRecognizer.swift b/ReactiveCocoa/UIKit/UIGestureRecognizer.swift index d9e4b430a1..bfab11ad91 100644 --- a/ReactiveCocoa/UIKit/UIGestureRecognizer.swift +++ b/ReactiveCocoa/UIKit/UIGestureRecognizer.swift @@ -7,7 +7,7 @@ extension Reactive where Base: UIGestureRecognizer { /// /// - returns: A trigger signal. public var stateChanged: Signal { - return Signal { observer in + return Signal { observer, signalLifetime in let receiver = CocoaTarget(observer) { gestureRecognizer in return gestureRecognizer as! Base } @@ -15,7 +15,7 @@ extension Reactive where Base: UIGestureRecognizer { let disposable = lifetime.ended.observeCompleted(observer.sendCompleted) - return AnyDisposable { [weak base = self.base] in + signalLifetime.observeEnded { [weak base] in disposable?.dispose() base?.removeTarget(receiver, action: #selector(receiver.invoke)) } From e9a2b5285d614ba93f3105372946676b3e3a7fb7 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 21 Oct 2017 04:31:44 +0800 Subject: [PATCH 0866/1028] 7.0.0-alpha.2 --- CHANGELOG.md | 1 + ReactiveCocoa.podspec | 2 +- ReactiveMapKit.podspec | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e32f749cfc..4d004310dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master *Please put new entries at the top. +# 7.0.0-alpha.2 1. Requires ReactiveSwift 3.0.0 alpha 1. 1. ReactiveCocoa is now compatible with the Swift 4.0 language mode, in addition to the Swift 3.2 compatibility mode. (#3526, kudos to @andersio) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index e8e5f2ecaf..8b6867539a 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "7.0.0-alpha.1" + s.version = "7.0.0-alpha.2" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index 5cb8f6485a..5cc394deb6 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveMapKit" - s.version = "7.0.0-alpha.1" + s.version = "7.0.0-alpha.2" s.summary = "MapKit bindings for ReactiveCocoa." s.description = <<-DESC Provide MapKit bindings for ReactiveCocoa. ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. From 8b9263f79efe3410cacb2cc49e86d7a816150342 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sun, 22 Oct 2017 19:55:50 +0800 Subject: [PATCH 0867/1028] UISearchBar bindings. --- CHANGELOG.md | 6 + ReactiveCocoa/UIKit/iOS/UISearchBar.swift | 49 +++++- .../UIKit/UISearchBarSpec.swift | 149 +++++++++++++----- 3 files changed, 160 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d004310dd..e9e2a7d946 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # master *Please put new entries at the top. +1. UISearchBar has gained more reactive bindings and signals. (#3531, kudos to @andersio) + + **Signals:** Search Button Clicked, Bookmark Button Clicked, Results List Clicked, Selected Scope Button Index + + **Binding Target:** Selected Scope Button Indices. + # 7.0.0-alpha.2 1. Requires ReactiveSwift 3.0.0 alpha 1. diff --git a/ReactiveCocoa/UIKit/iOS/UISearchBar.swift b/ReactiveCocoa/UIKit/iOS/UISearchBar.swift index c785d17cd1..0b78138f11 100644 --- a/ReactiveCocoa/UIKit/iOS/UISearchBar.swift +++ b/ReactiveCocoa/UIKit/iOS/UISearchBar.swift @@ -14,6 +14,22 @@ private class SearchBarDelegateProxy: DelegateProxy, UISear @objc func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { forwardee?.searchBarCancelButtonClicked?(searchBar) } + + @objc func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { + forwardee?.searchBarSearchButtonClicked?(searchBar) + } + + @objc func searchBarBookmarkButtonClicked(_ searchBar: UISearchBar) { + forwardee?.searchBarBookmarkButtonClicked?(searchBar) + } + + @objc func searchBarResultsListButtonClicked(_ searchBar: UISearchBar) { + forwardee?.searchBarResultsListButtonClicked?(searchBar) + } + + @objc func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) { + forwardee?.searchBar?(searchBar, selectedScopeButtonIndexDidChange: selectedScope) + } } extension Reactive where Base: UISearchBar { @@ -28,6 +44,11 @@ extension Reactive where Base: UISearchBar { return makeBindingTarget { $0.text = $1 } } + /// Sets the selected scope button index of the search bar. + public var selectedScopeButtonIndex: BindingTarget { + return makeBindingTarget { $0.selectedScopeButtonIndex = $1 } + } + /// A signal of text values emitted by the search bar upon end of editing. /// /// - note: To observe text values that change on all editing events, @@ -41,13 +62,33 @@ extension Reactive where Base: UISearchBar { /// /// - note: To observe text values only when editing ends, see `textValues`. public var continuousTextValues: Signal { - return proxy.intercept(#selector(UISearchBarDelegate.searchBar(_:textDidChange:))) + return proxy.intercept(#selector(proxy.searchBar(_:textDidChange:))) .map { [unowned base] in base.text } } - + + /// A signal of the latest selected scope button index upon any user selection. + public var selectedScopeButtonIndices: Signal { + return proxy.intercept(#selector(proxy.searchBar(_:selectedScopeButtonIndexDidChange:))) + .map { $0[1] as! Int } + } + /// A void signal emitted by the search bar upon any click on the cancel button public var cancelButtonClicked: Signal { - return proxy.intercept(#selector(UISearchBarDelegate.searchBarCancelButtonClicked)) + return proxy.intercept(#selector(proxy.searchBarCancelButtonClicked)) + } + + /// A void signal emitted by the search bar upon any click on the search button + public var searchButtonClicked: Signal { + return proxy.intercept(#selector(proxy.searchBarSearchButtonClicked(_:))) + } + + /// A void signal emitted by the search bar upon any click on the bookmark button + public var bookmarkButtonClicked: Signal { + return proxy.intercept(#selector(proxy.searchBarBookmarkButtonClicked)) + } + + /// A void signal emitted by the search bar upon any click on the bookmark button + public var resultsListButtonClicked: Signal { + return proxy.intercept(#selector(proxy.searchBarResultsListButtonClicked)) } - } diff --git a/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift b/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift index 9f9d71b893..b711d3d353 100644 --- a/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift +++ b/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift @@ -9,12 +9,18 @@ class UISearchBarSpec: QuickSpec { override func spec() { var searchBar: UISearchBar! weak var _searchBar: UISearchBar? - + var receiver: SearchBarDelegateReceiver! beforeEach { autoreleasepool { + receiver = SearchBarDelegateReceiver() searchBar = UISearchBar(frame: .zero) _searchBar = searchBar + + _ = searchBar.reactive.textValues + searchBar.delegate = receiver + + expect(searchBar.delegate).toNot(beIdenticalTo(receiver)) } } @@ -49,9 +55,13 @@ class UISearchBarSpec: QuickSpec { latestValue = text } + expect(latestValue).to(beNil()) + expect(receiver.endEditingTexts.isEmpty) == true + searchBar.delegate!.searchBarTextDidEndEditing!(searchBar) expect(latestValue) == searchBar.text + expect(receiver.endEditingTexts.last) == searchBar.text } it("should emit user initiated changes to its text value continuously") { @@ -62,83 +72,142 @@ class UISearchBarSpec: QuickSpec { latestValue = text } + expect(latestValue).to(beNil()) + expect(receiver.texts.isEmpty) == true + searchBar.delegate!.searchBar!(searchBar, textDidChange: "newValue") expect(latestValue) == "newValue" + expect(receiver.texts.last) == "newValue" } - it("should pass through the intercepted calls") { - searchBar.text = "newValue" + it("should accept changes from bindings to its scope button index") { + let firstChange = 1 + let secondChange = 2 - var latestValue: String? - searchBar.reactive.continuousTextValues.observeValues { text in + searchBar.scopeButtonTitles = ["First", "Second", "Third"] + + let (pipeSignal, observer) = Signal.pipe() + searchBar.reactive.selectedScopeButtonIndex <~ SignalProducer(pipeSignal) + + observer.send(value: firstChange) + expect(searchBar.selectedScopeButtonIndex) == firstChange + + observer.send(value: secondChange) + expect(searchBar.selectedScopeButtonIndex) == secondChange + } + + it("should emit user initiated changes to its text value when the editing ends") { + searchBar.scopeButtonTitles = ["First", "Second", "Third"] + + var latestValue: Int? + searchBar.reactive.selectedScopeButtonIndices.observeValues { text in latestValue = text } - let receiver = SearchBarDelegateReceiver() - searchBar.delegate = receiver - expect(receiver.textDidChangeCounter) == 0 + expect(latestValue).to(beNil()) + expect(receiver.selectedScopeButtonIndices.isEmpty) == true - searchBar.delegate!.searchBar!(searchBar, textDidChange: "newValue") - expect(latestValue) == "newValue" - expect(receiver.textDidChangeCounter) == 1 + searchBar.delegate!.searchBar!(searchBar, selectedScopeButtonIndexDidChange: 1) + + expect(latestValue) == 1 + expect(receiver.selectedScopeButtonIndices.last) == 1 + + searchBar.delegate!.searchBar!(searchBar, selectedScopeButtonIndexDidChange: 2) + + expect(latestValue) == 2 + expect(receiver.selectedScopeButtonIndices.last) == 2 } - it("should pass through the unintercepted calls") { - searchBar.reactive.continuousTextValues.observe { _ in } + it("should notify when the cancel button is clicked") { + var isClicked: Bool? + searchBar.reactive.cancelButtonClicked + .observeValues { isClicked = true } - let receiver = SearchBarDelegateReceiver() - searchBar.delegate = receiver - expect(receiver.searchButtonClickedCounter) == 0 + expect(isClicked).to(beNil()) + expect(receiver.cancelButtonClickedCounter) == 0 - searchBar.delegate!.searchBarSearchButtonClicked!(searchBar) - expect(receiver.searchButtonClickedCounter) == 1 + searchBar.delegate!.searchBarCancelButtonClicked!(searchBar) + expect(isClicked) == true + expect(receiver.cancelButtonClickedCounter) == 1 } - it("should preserve the original delegate, and pass through the unintercepted calls") { - let receiver = SearchBarDelegateReceiver() - searchBar.delegate = receiver - expect(receiver.searchButtonClickedCounter) == 0 + it("should notify when the search button is clicked") { + var isClicked: Bool? + searchBar.reactive.searchButtonClicked + .observeValues { isClicked = true } - searchBar.reactive.continuousTextValues.observe { _ in } + expect(isClicked).to(beNil()) expect(receiver.searchButtonClickedCounter) == 0 searchBar.delegate!.searchBarSearchButtonClicked!(searchBar) + expect(isClicked) == true expect(receiver.searchButtonClickedCounter) == 1 } - - it("should pass through the unintercepted calls") { - searchBar.reactive.continuousTextValues.observe { _ in } - - let receiver = SearchBarDelegateReceiver() - searchBar.delegate = receiver - expect(receiver.searchBarCancelButtonClickedCounter) == 0 - - searchBar.delegate!.searchBarCancelButtonClicked!(searchBar) - expect(receiver.searchBarCancelButtonClickedCounter) == 1 + + + it("should notify when the bookmark button is clicked") { + var isClicked: Bool? + searchBar.reactive.bookmarkButtonClicked + .observeValues { isClicked = true } + + expect(isClicked).to(beNil()) + expect(receiver.bookmarkButtonClickedCounter) == 0 + + searchBar.delegate!.searchBarBookmarkButtonClicked!(searchBar) + expect(isClicked) == true + expect(receiver.bookmarkButtonClickedCounter) == 1 + } + + + it("should notify when the results list button is clicked") { + var isClicked: Bool? + searchBar.reactive.resultsListButtonClicked + .observeValues { isClicked = true } + + expect(isClicked).to(beNil()) + expect(receiver.resultsListButtonClickedCounter) == 0 + + searchBar.delegate!.searchBarResultsListButtonClicked!(searchBar) + expect(isClicked) == true + expect(receiver.resultsListButtonClickedCounter) == 1 } - } } class SearchBarDelegateReceiver: NSObject, UISearchBarDelegate { - var textDidChangeCounter = 0 - var textDidEndEditingCounter = 0 + var texts: [String] = [] + var endEditingTexts: [String] = [] var searchButtonClickedCounter = 0 - var searchBarCancelButtonClickedCounter = 0 + var cancelButtonClickedCounter = 0 + var bookmarkButtonClickedCounter = 0 + var resultsListButtonClickedCounter = 0 + var selectedScopeButtonIndices: [Int] = [] func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { searchButtonClickedCounter += 1 } func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { - textDidChangeCounter += 1 + texts.append(searchText) } func searchBarTextDidEndEditing(_ searchBar: UISearchBar) { - textDidEndEditingCounter += 1 + endEditingTexts.append(searchBar.text ?? "") } func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { - searchBarCancelButtonClickedCounter += 1 + cancelButtonClickedCounter += 1 + } + + func searchBarResultsListButtonClicked(_ searchBar: UISearchBar) { + resultsListButtonClickedCounter += 1 + } + + func searchBarBookmarkButtonClicked(_ searchBar: UISearchBar) { + bookmarkButtonClickedCounter += 1 + } + + func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) { + selectedScopeButtonIndices.append(selectedScope) } } From 8ff3a228f88ee6683e6e8683ab044f573d035b13 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Mon, 30 Oct 2017 15:03:28 +0800 Subject: [PATCH 0868/1028] Update ReactiveSwift to 3.0.0-rc.1. --- Cartfile | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- ReactiveCocoa.podspec | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cartfile b/Cartfile index 6319e8edde..6b84d19213 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" "3.0.0-alpha.1" +github "ReactiveCocoa/ReactiveSwift" "3.0.0-rc.1" diff --git a/Cartfile.resolved b/Cartfile.resolved index af4f101667..26be3403e4 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,5 +1,5 @@ github "Quick/Nimble" "v7.0.2" github "Quick/Quick" "v1.2.0" -github "ReactiveCocoa/ReactiveSwift" "3.0.0-alpha.1" +github "ReactiveCocoa/ReactiveSwift" "3.0.0-rc.1" github "antitypical/Result" "3.2.4" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index eeb62fcae7..f15a072862 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit eeb62fcae787221f57ef35d359bcacb936bc33c2 +Subproject commit f15a072862ff3d588104b475c6266c313eed8b8d diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 8b6867539a..04c7b4ce00 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -23,7 +23,7 @@ Pod::Spec.new do |s| s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_map = "ReactiveCocoa/module.modulemap" - s.dependency 'ReactiveSwift', '3.0.0-alpha.1' + s.dependency 'ReactiveSwift', '3.0.0-rc.1' s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "-suppress-warnings" } end From ab508849556f3f3bfd1b2737022fa8fa8ae5f77b Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Tue, 31 Oct 2017 12:20:03 +0800 Subject: [PATCH 0869/1028] 7.0.0-rc.1 --- CHANGELOG.md | 1 + ReactiveCocoa.podspec | 2 +- ReactiveMapKit.podspec | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9e2a7d946..02dc257d99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master *Please put new entries at the top. +# 7.0.0-rc.1 1. UISearchBar has gained more reactive bindings and signals. (#3531, kudos to @andersio) **Signals:** Search Button Clicked, Bookmark Button Clicked, Results List Clicked, Selected Scope Button Index diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 04c7b4ce00..ede39466a6 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "7.0.0-alpha.2" + s.version = "7.0.0-rc.1" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index 5cc394deb6..0f63097144 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveMapKit" - s.version = "7.0.0-alpha.2" + s.version = "7.0.0-rc.1" s.summary = "MapKit bindings for ReactiveCocoa." s.description = <<-DESC Provide MapKit bindings for ReactiveCocoa. ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. From 33a01857ff9e914da1b70adcb381e572597b6059 Mon Sep 17 00:00:00 2001 From: Ronald Martin Date: Sat, 11 Nov 2017 21:51:36 -0800 Subject: [PATCH 0870/1028] Add 'placeholder' BindingTarget to UITextField --- CHANGELOG.md | 1 + ReactiveCocoa/UIKit/UITextField.swift | 5 +++++ ReactiveCocoaTests/UIKit/UITextFieldSpec.swift | 16 +++++++++++++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02dc257d99..97873b7007 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ *Please put new entries at the top. # 7.0.0-rc.1 +1. Add `placeholder` binding target to `UITextField` 1. UISearchBar has gained more reactive bindings and signals. (#3531, kudos to @andersio) **Signals:** Search Button Clicked, Bookmark Button Clicked, Results List Clicked, Selected Scope Button Index diff --git a/ReactiveCocoa/UIKit/UITextField.swift b/ReactiveCocoa/UIKit/UITextField.swift index 608a69cec8..0e658bba65 100644 --- a/ReactiveCocoa/UIKit/UITextField.swift +++ b/ReactiveCocoa/UIKit/UITextField.swift @@ -27,6 +27,11 @@ extension Reactive where Base: UITextField { public var attributedText: BindingTarget { return makeBindingTarget { $0.attributedText = $1 } } + + /// Sets the placeholder text of the text field. + public var placeholder: BindingTarget { + return makeBindingTarget { $0.placeholder = $1 } + } /// Sets the textColor of the text field. public var textColor: BindingTarget { diff --git a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift index 2aa921c771..e77486b0c3 100644 --- a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift @@ -92,7 +92,7 @@ class UITextFieldSpec: QuickSpec { expect(latestValue) == textField.attributedText } - it("should emit user initiated changes to its attributed text value when the editing ends as a reuslt of the return key being pressed") { + it("should emit user initiated changes to its attributed text value when the editing ends as a result of the return key being pressed") { textField.attributedText = NSAttributedString(string: "Test") var latestValue: NSAttributedString? @@ -118,6 +118,20 @@ class UITextFieldSpec: QuickSpec { } } + it("should accept changes from bindings to its placeholder attribute") { + let (pipeSignal, observer) = Signal.pipe() + textField.reactive.placeholder <~ pipeSignal + + observer.send(value: "A placeholder") + expect(textField.placeholder).to(equal("A placeholder")) + + observer.send(value: nil) + expect(textField.placeholder).to(beNil()) + + observer.send(value: "Another placeholder") + expect(textField.placeholder).to(equal("Another placeholder")) + } + it("should accept changes from bindings to its secureTextEntry attribute") { let (pipeSignal, observer) = Signal.pipe() textField.reactive.isSecureTextEntry <~ pipeSignal From f7b49724973494b9f68f3bcc38eb027ab92c45d8 Mon Sep 17 00:00:00 2001 From: Ronald Martin Date: Sun, 12 Nov 2017 07:49:21 -0800 Subject: [PATCH 0871/1028] Update CHANGELOG.md Per PR feedback (https://github.com/ReactiveCocoa/ReactiveCocoa/pull/3536#discussion_r150406925) --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97873b7007..3269d5df72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,9 @@ # master *Please put new entries at the top. +1. Added `placeholder` binding target to `UITextField`. (#3536) + # 7.0.0-rc.1 -1. Add `placeholder` binding target to `UITextField` 1. UISearchBar has gained more reactive bindings and signals. (#3531, kudos to @andersio) **Signals:** Search Button Clicked, Bookmark Button Clicked, Results List Clicked, Selected Scope Button Index From c37151ced3349fd58327839b66912e7f18ca9c86 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Fri, 17 Nov 2017 12:17:14 +0800 Subject: [PATCH 0872/1028] Update ReactiveSwift to 3.0. --- CHANGELOG.md | 2 ++ Cartfile | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3269d5df72..f8722865e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # master *Please put new entries at the top. +1. Update ReactiveSwift to 3.0. + 1. Added `placeholder` binding target to `UITextField`. (#3536) # 7.0.0-rc.1 diff --git a/Cartfile b/Cartfile index 6b84d19213..ecb3846b48 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" "3.0.0-rc.1" +github "ReactiveCocoa/ReactiveSwift" ~> 3.0 diff --git a/Cartfile.resolved b/Cartfile.resolved index 26be3403e4..ac9466c6ed 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,5 +1,5 @@ github "Quick/Nimble" "v7.0.2" github "Quick/Quick" "v1.2.0" -github "ReactiveCocoa/ReactiveSwift" "3.0.0-rc.1" +github "ReactiveCocoa/ReactiveSwift" "3.0.0" github "antitypical/Result" "3.2.4" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index f15a072862..2ec944c43e 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit f15a072862ff3d588104b475c6266c313eed8b8d +Subproject commit 2ec944c43ef9cf6b1380629e3ea483f62a7afaef From a302b8a4bee5cef96b952e02be7cde50b97d0ac8 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Fri, 17 Nov 2017 13:25:06 +0800 Subject: [PATCH 0873/1028] 7.0.0 --- CHANGELOG.md | 1 + README.md | 13 ++++--------- ReactiveCocoa.podspec | 2 +- ReactiveMapKit.podspec | 2 +- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8722865e9..45932d5f1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master *Please put new entries at the top. +# 7.0.0 1. Update ReactiveSwift to 3.0. 1. Added `placeholder` binding target to `UITextField`. (#3536) diff --git a/README.md b/README.md index 50cf4c1e55..5bc296c4e0 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ If you use [Carthage][] to manage your dependencies, simply add ReactiveCocoa to your `Cartfile`: ``` -github "ReactiveCocoa/ReactiveCocoa" ~> 6.0 +github "ReactiveCocoa/ReactiveCocoa" ~> 7.0 ``` If you use Carthage to build your dependencies, make sure you have added `ReactiveCocoa.framework`, `ReactiveSwift.framework`, and `Result.framework` to the "_Linked Frameworks and Libraries_" section of your target, and have included them in your Carthage framework copying build phase. @@ -111,7 +111,7 @@ If you use [CocoaPods][] to manage your dependencies, simply add ReactiveCocoa to your `Podfile`: ``` -pod 'ReactiveCocoa', '~> 6.0' +pod 'ReactiveCocoa', '~> 7.0' ``` #### Git submodule @@ -136,14 +136,9 @@ If you need any help, please visit our [GitHub issues][] or [Stack Overflow][]. **Current Stable Release:**
    [![GitHub release](https://img.shields.io/github/release/ReactiveCocoa/ReactiveCocoa.svg)](https://github.com/ReactiveCocoa/ReactiveCocoa/releases) ### In Development -#### Swift 3.2 and Swift 4.0 compatibility -While the development would be Swift 3.2 and Swift 4.0 aware, ReactiveCocoa 6.0 would not declare official support until Swift 3.2 and Swift 4.0 launch. - -The official release for Swift 3.2 and Swift 4.0 is expected to be a minor 6.x release with full API compatibility. - ### Plan of Record -#### ReactiveCocoa 7.0 -ReactiveCocoa 7.0 is expected to declare library ABI stability as it adopts generics features arriving in a later Swift 4 release, e.g. conditional conformance. There is no ETA for now. +#### ABI stability release +ReactiveCocoa is expected to declare library ABI stability when Swift rolls out resilence support. Until then, ReactiveCocoa would incrementally adopt new language features that help move towards to goal. The ETA is Swift 5. [ReactiveSwift]: https://github.com/ReactiveCocoa/ReactiveSwift [ReactiveObjC]: https://github.com/ReactiveCocoa/ReactiveObjC diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index ede39466a6..cb2d82cc8d 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "7.0.0-rc.1" + s.version = "7.0.0" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index 0f63097144..2478f3d00a 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveMapKit" - s.version = "7.0.0-rc.1" + s.version = "7.0.0" s.summary = "MapKit bindings for ReactiveCocoa." s.description = <<-DESC Provide MapKit bindings for ReactiveCocoa. ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. From de8498d979b4a617ce1145fa5f3c5d424f81cc98 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Fri, 17 Nov 2017 16:07:20 +0800 Subject: [PATCH 0874/1028] Update the podspec. --- ReactiveCocoa.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index cb2d82cc8d..609a8e9e67 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -23,7 +23,7 @@ Pod::Spec.new do |s| s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_map = "ReactiveCocoa/module.modulemap" - s.dependency 'ReactiveSwift', '3.0.0-rc.1' + s.dependency 'ReactiveSwift', '~> 3.0' s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "-suppress-warnings" } end From 8d462953eca827154f10abcd26cffd20a6121d26 Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Sat, 18 Nov 2017 07:25:33 +0100 Subject: [PATCH 0875/1028] Add tintColor BindingTarget for UIView (#3542) --- CHANGELOG.md | 2 ++ ReactiveCocoa/UIKit/UIButton.swift | 1 + ReactiveCocoa/UIKit/UIView.swift | 5 +++++ ReactiveCocoaTests/UIKit/UIViewSpec.swift | 16 ++++++++++++++++ 4 files changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 45932d5f1a..bcc71017ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # master *Please put new entries at the top. +1. Added `tintColor` binding target to `UIView`. (#3542) + # 7.0.0 1. Update ReactiveSwift to 3.0. diff --git a/ReactiveCocoa/UIKit/UIButton.swift b/ReactiveCocoa/UIKit/UIButton.swift index 6c5594e0b3..0bf4f6ec1c 100644 --- a/ReactiveCocoa/UIKit/UIButton.swift +++ b/ReactiveCocoa/UIKit/UIButton.swift @@ -41,6 +41,7 @@ extension Reactive where Base: UIButton { return makeBindingTarget { $0.setImage($1, for: state) } } + /// Sets the image of the button for the .normal state public var image: BindingTarget { return image(for: .normal) } diff --git a/ReactiveCocoa/UIKit/UIView.swift b/ReactiveCocoa/UIKit/UIView.swift index d9094369f7..85f3046fd3 100644 --- a/ReactiveCocoa/UIKit/UIView.swift +++ b/ReactiveCocoa/UIKit/UIView.swift @@ -21,4 +21,9 @@ extension Reactive where Base: UIView { public var backgroundColor: BindingTarget { return makeBindingTarget { $0.backgroundColor = $1 } } + + /// Sets the tintColor of the view + public var tintColor: BindingTarget { + return makeBindingTarget { $0.tintColor = $1 } + } } diff --git a/ReactiveCocoaTests/UIKit/UIViewSpec.swift b/ReactiveCocoaTests/UIKit/UIViewSpec.swift index e42bdd57f3..7be1422df9 100644 --- a/ReactiveCocoaTests/UIKit/UIViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIViewSpec.swift @@ -77,5 +77,21 @@ class UIViewSpec: QuickSpec { observer.send(value: .red) expect(view.backgroundColor) == .red } + + it("should accept changes from bindings to its tint color") { + view.tintColor = .white + + let (pipeSignal, observer) = Signal.pipe() + view.reactive.tintColor <~ SignalProducer(pipeSignal) + + observer.send(value: .yellow) + expect(view.tintColor) == .yellow + + observer.send(value: .green) + expect(view.tintColor) == .green + + observer.send(value: .red) + expect(view.tintColor) == .red + } } } From 6086d35533c55f0eed90d636228cc3520009d296 Mon Sep 17 00:00:00 2001 From: Kare Morstol Date: Sun, 19 Nov 2017 15:24:57 +0100 Subject: [PATCH 0876/1028] Readme: clarify section on ABI stability release. (#3544) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5bc296c4e0..6429f81787 100644 --- a/README.md +++ b/README.md @@ -138,7 +138,7 @@ If you need any help, please visit our [GitHub issues][] or [Stack Overflow][]. ### In Development ### Plan of Record #### ABI stability release -ReactiveCocoa is expected to declare library ABI stability when Swift rolls out resilence support. Until then, ReactiveCocoa would incrementally adopt new language features that help move towards to goal. The ETA is Swift 5. +ReactiveCocoa is expected to declare library ABI stability when Swift rolls out resilence support in Swift 5. Until then, ReactiveCocoa will incrementally adopt new language features. [ReactiveSwift]: https://github.com/ReactiveCocoa/ReactiveSwift [ReactiveObjC]: https://github.com/ReactiveCocoa/ReactiveObjC From bbfec828067b43bcb06b2162ab46f55fe0c8a936 Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Fri, 24 Nov 2017 08:22:26 +0100 Subject: [PATCH 0877/1028] DynamicProperty of optional property (#3548) * Fix DynamicProperty for optional properties. #3547 * Use a stored transform instead of another setter override --- CHANGELOG.md | 3 +- ReactiveCocoa/DynamicProperty.swift | 31 ++++++++-- .../NSObject+KeyValueObserving.swift | 46 ++++++++++---- ReactiveCocoaTests/DynamicPropertySpec.swift | 60 ++++++++++++++++++- 4 files changed, 122 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bcc71017ff..ff2dd0627e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ # master *Please put new entries at the top. -1. Added `tintColor` binding target to `UIView`. (#3542) +1. Added `tintColor` binding target to `UIView`. (#3542, kudos to @iv-mexx) +1. Fixed `DynamicProperty` for optional properties. (#3548, kudos to @iv-mexx) # 7.0.0 1. Update ReactiveSwift to 3.0. diff --git a/ReactiveCocoa/DynamicProperty.swift b/ReactiveCocoa/DynamicProperty.swift index ef2b71ebde..08362c04d9 100644 --- a/ReactiveCocoa/DynamicProperty.swift +++ b/ReactiveCocoa/DynamicProperty.swift @@ -11,12 +11,13 @@ public final class DynamicProperty: MutablePropertyProtocol { private weak var object: NSObject? private let keyPath: String private let cache: Property + private let transform: (Value) -> Any? /// The current value of the property, as read and written using Key-Value /// Coding. public var value: Value { get { return cache.value } - set { object?.setValue(newValue, forKeyPath: keyPath) } + set { object?.setValue(transform(newValue), forKeyPath: keyPath) } } /// The lifetime of the property. @@ -45,16 +46,34 @@ public final class DynamicProperty: MutablePropertyProtocol { return cache.signal } - /// Create a typed mutable view to the given key path of the given Objective-C object. + internal init(object: NSObject, keyPath: String, cache: Property, transform: @escaping (Value) -> Any?) { + self.object = object + self.keyPath = keyPath + self.cache = cache + self.transform = transform + } + + /// Create a typed mutable view to the given key path of the given Objective-C object. /// The generic type `Value` can be any Swift type, and will be bridged to Objective-C /// via `Any`. /// /// - parameters: /// - object: The Objective-C object to be observed. /// - keyPath: The key path to observe. - public init(object: NSObject, keyPath: String) { - self.object = object - self.keyPath = keyPath - self.cache = Property(object: object, keyPath: keyPath) + public convenience init(object: NSObject, keyPath: String) { + self.init(object: object, keyPath: keyPath, cache: Property(object: object, keyPath: keyPath), transform: { $0 }) + } +} + +extension DynamicProperty where Value: OptionalProtocol { + /// Create a typed mutable view to the given key path of the given Objective-C object. + /// The generic type `Value` can be any Swift type, and will be bridged to Objective-C + /// via `Any`. + /// + /// - parameters: + /// - object: The Objective-C object to be observed. + /// - keyPath: The key path to observe. + public convenience init(object: NSObject, keyPath: String) { + self.init(object: object, keyPath: keyPath, cache: Property(object: object, keyPath: keyPath), transform: { $0.optional }) } } diff --git a/ReactiveCocoa/NSObject+KeyValueObserving.swift b/ReactiveCocoa/NSObject+KeyValueObserving.swift index fccf531372..0144001412 100644 --- a/ReactiveCocoa/NSObject+KeyValueObserving.swift +++ b/ReactiveCocoa/NSObject+KeyValueObserving.swift @@ -55,6 +55,20 @@ extension Reactive where Base: NSObject { } } +extension Property where Value: OptionalProtocol { + /// Create a property that observes the given key path of the given object. The + /// generic type `Value` can be any Swift type that is Objective-C bridgeable. + /// + /// - parameters: + /// - object: An object to be observed. + /// - keyPath: The key path to observe. + public convenience init(object: NSObject, keyPath: String) { + // `Property(_:)` caches the latest value of the `DynamicProperty`, so it is + // saved to be used even after `object` deinitializes. + self.init(UnsafeKVOProperty(object: object, optionalAttributeKeyPath: keyPath)) + } +} + extension Property { /// Create a property that observes the given key path of the given object. The /// generic type `Value` can be any Swift type that is Objective-C bridgeable. @@ -65,19 +79,31 @@ extension Property { public convenience init(object: NSObject, keyPath: String) { // `Property(_:)` caches the latest value of the `DynamicProperty`, so it is // saved to be used even after `object` deinitializes. - self.init(UnsafeKVOProperty(object: object, keyPath: keyPath)) + self.init(UnsafeKVOProperty(object: object, nonOptionalAttributeKeyPath: keyPath)) } +} - // `Property(unsafeProducer:)` is private to ReactiveSwift. So the fact that - // `Property(_:)` uses only the producer is explioted here to achieve the same effect. - private final class UnsafeKVOProperty: PropertyProtocol { - var value: Value { fatalError() } - var signal: Signal { fatalError() } - let producer: SignalProducer +// `Property(unsafeProducer:)` is private to ReactiveSwift. So the fact that +// `Property(_:)` uses only the producer is explioted here to achieve the same effect. +private final class UnsafeKVOProperty: PropertyProtocol { + var value: Value { fatalError() } + var signal: Signal { fatalError() } + let producer: SignalProducer + + init(producer: SignalProducer) { + self.producer = producer + } + + convenience init(object: NSObject, nonOptionalAttributeKeyPath keyPath: String) { + self.init(producer: object.reactive.producer(forKeyPath: keyPath).map { $0 as! Value }) + } +} - init(object: NSObject, keyPath: String) { - self.producer = object.reactive.producer(forKeyPath: keyPath).map { $0 as! Value } - } +private extension UnsafeKVOProperty where Value: OptionalProtocol { + convenience init(object: NSObject, optionalAttributeKeyPath keyPath: String) { + self.init(producer: object.reactive.producer(forKeyPath: keyPath).map { + return Value(reconstructing: $0.optional as? Value.Wrapped) + }) } } diff --git a/ReactiveCocoaTests/DynamicPropertySpec.swift b/ReactiveCocoaTests/DynamicPropertySpec.swift index 54789b2cf6..4e901b3184 100644 --- a/ReactiveCocoaTests/DynamicPropertySpec.swift +++ b/ReactiveCocoaTests/DynamicPropertySpec.swift @@ -14,6 +14,63 @@ private let finalOtherPropertyValue = "FinalOtherValue" class DynamicPropertySpec: QuickSpec { override func spec() { + + describe("DynamicProperty with optional value") { + var object: ObservableObject! + var property: DynamicProperty! + + beforeEach { + object = ObservableObject() + expect(object.rac_optional_value) == 0 + + property = DynamicProperty(object: object, keyPath: "rac_optional_value") + } + + afterEach { + object = nil + } + + let propertyValue: () -> NSNumber? = { + if let value: Any = property?.value { + return value as? NSNumber + } else { + return nil + } + } + + it("should read the underlying object") { + expect(propertyValue()) == 0 + + object.rac_optional_value = nil + expect(propertyValue()).to(beNil()) + } + + it("should write the underlying object") { + property.value = nil + expect(object.rac_optional_value).to(beNil()) + expect(propertyValue()).to(beNil()) + } + + it("should yield a producer that sends the current value and then the changes for the key path of the underlying object") { + var values: [NSNumber?] = [] + property.producer.startWithValues { value in + values.append(value) + } + + expect(values).to(equal([0])) + + property.value = nil + expect(values).to(equal([0, nil])) + + object.rac_optional_value = 2 + expect(values).to(equal([0, nil, 2])) + + object.rac_optional_value = nil + expect(values).to(equal([0, nil, 2, nil])) + print(values) + } + } + describe("DynamicProperty") { var object: ObservableObject! var property: DynamicProperty! @@ -153,7 +210,7 @@ class DynamicPropertySpec: QuickSpec { it("should not be retained by its underlying object"){ weak var dynamicProperty: DynamicProperty? = property - + property = nil expect(dynamicProperty).to(beNil()) } @@ -252,6 +309,7 @@ class DynamicPropertySpec: QuickSpec { private class ObservableObject: NSObject { @objc dynamic var rac_value: Int = 0 + @objc dynamic var rac_optional_value: NSNumber? = 0 @objc dynamic var rac_reference: UnbridgedObject = UnbridgedObject("") @objc dynamic var rac_unbridged: Any = UnbridgedValue.starting } From 5024e99885da8478335fc4d051c436e0966d3b58 Mon Sep 17 00:00:00 2001 From: Kim Burge Strand Date: Fri, 24 Nov 2017 08:22:56 +0100 Subject: [PATCH 0878/1028] =?UTF-8?q?Make=20`Reactive#makeBindingTarget(?= =?UTF-8?q?=E2=80=A6)`=20available=20on=20`AnyObject`,=20not=20just=20NSOb?= =?UTF-8?q?ject=20(#3545)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Make `Reactive#makeBindingTarget(…)` available on `AnyObject`, not just `NSObject`. * Update CHANGELOG --- CHANGELOG.md | 2 + ReactiveCocoa/NSObject+BindingTarget.swift | 4 +- ReactiveCocoaTests/BindingTargetSpec.swift | 113 +++++++++++++++++++++ 3 files changed, 117 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff2dd0627e..5b79cbcbc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ 1. Added `tintColor` binding target to `UIView`. (#3542, kudos to @iv-mexx) 1. Fixed `DynamicProperty` for optional properties. (#3548, kudos to @iv-mexx) +1. Made `makeBindingTarget` available on Reactive extensions on all objects, not just `NSObject`. (#3545) + # 7.0.0 1. Update ReactiveSwift to 3.0. diff --git a/ReactiveCocoa/NSObject+BindingTarget.swift b/ReactiveCocoa/NSObject+BindingTarget.swift index b14de66f2a..b898c77b33 100644 --- a/ReactiveCocoa/NSObject+BindingTarget.swift +++ b/ReactiveCocoa/NSObject+BindingTarget.swift @@ -1,7 +1,7 @@ import Foundation import ReactiveSwift -extension Reactive where Base: NSObjectProtocol { +extension Reactive where Base: AnyObject { /// Creates a binding target which uses the lifetime of the object, and /// weakly references the object so that the supplied `action` is triggered /// only if the object has not deinitialized. @@ -14,7 +14,7 @@ extension Reactive where Base: NSObjectProtocol { /// - returns: A binding target that holds no strong references to the /// object. public func makeBindingTarget(on scheduler: Scheduler = UIScheduler(), _ action: @escaping (Base, U) -> Void) -> BindingTarget { - return BindingTarget(on: scheduler, lifetime: lifetime) { [weak base = self.base] value in + return BindingTarget(on: scheduler, lifetime: ReactiveCocoa.lifetime(of: base)) { [weak base = self.base] value in if let base = base { action(base, value) } diff --git a/ReactiveCocoaTests/BindingTargetSpec.swift b/ReactiveCocoaTests/BindingTargetSpec.swift index 9f2f527949..5baaf709d9 100644 --- a/ReactiveCocoaTests/BindingTargetSpec.swift +++ b/ReactiveCocoaTests/BindingTargetSpec.swift @@ -6,10 +6,111 @@ import Nimble private class Object: NSObject { var value: Int = 0 + + func increment() { + value += 1 + } +} + +private class NativeObject: ReactiveExtensionsProvider { + var value: Int = 0 + + func increment() { + value += 1 + } } class BindingTargetSpec: QuickSpec { override func spec() { + describe("arbitrary binding target on Objective-C object") { + it("should call the action") { + let object = Object() + let target = object.reactive.makeBindingTarget { (object: Object, nothing: Void) -> Void in + object.increment() + } + expect(object.value) == 0 + + let (signal, observer) = Signal<(), NoError>.pipe() + target <~ signal + expect(object.value) == 0 + + observer.send(value: ()) + expect(object.value) == 1 + + observer.send(value: ()) + expect(object.value) == 2 + } + + it("should call the action on the given scheduler") { + let scheduler = TestScheduler() + + let object = Object() + let target = object.reactive.makeBindingTarget(on: scheduler) { (object: Object, nothing: Void) -> Void in + object.increment() + } + expect(object.value) == 0 + + let (signal, observer) = Signal<(), NoError>.pipe() + target <~ signal + observer.send(value: ()) + expect(object.value) == 0 + + scheduler.run() + expect(object.value) == 1 + + observer.send(value: ()) + expect(object.value) == 1 + + scheduler.run() + expect(object.value) == 2 + } + } + + describe("arbitrary binding target on native object") { + it("should call the action") { + let object = NativeObject() + let target = object.reactive.makeBindingTarget { (object: NativeObject, nothing: Void) -> Void in + object.increment() + } + expect(object.value) == 0 + + let (signal, observer) = Signal<(), NoError>.pipe() + target <~ signal + expect(object.value) == 0 + + observer.send(value: ()) + expect(object.value) == 1 + + observer.send(value: ()) + expect(object.value) == 2 + } + + it("should call the action on the given scheduler") { + let scheduler = TestScheduler() + + let object = NativeObject() + let target = object.reactive.makeBindingTarget(on: scheduler) { (object: NativeObject, nothing: Void) -> Void in + object.increment() + } + expect(object.value) == 0 + + let (signal, observer) = Signal<(), NoError>.pipe() + target <~ signal + observer.send(value: ()) + expect(object.value) == 0 + + scheduler.run() + expect(object.value) == 1 + + observer.send(value: ()) + expect(object.value) == 1 + + scheduler.run() + expect(object.value) == 2 + } + } + + #if swift(>=3.2) describe("key path binding target") { it("should update the value") { @@ -41,6 +142,18 @@ class BindingTargetSpec: QuickSpec { scheduler.run() expect(object.value) == 2 } + + it("should work for native swift objects") { + let object = NativeObject() + expect(object.value) == 0 + + let property = MutableProperty(1) + object.reactive[\.value] <~ property + expect(object.value) == 1 + + property.value = 2 + expect(object.value) == 2 + } } #endif } From f21566e445e31fdeb4550a61986a86d2df103ed1 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Fri, 24 Nov 2017 15:28:26 +0800 Subject: [PATCH 0879/1028] 7.0.1 --- CHANGELOG.md | 4 +++- ReactiveCocoa.podspec | 2 +- ReactiveMapKit.podspec | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b79cbcbc8..f0c13b47af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,12 @@ # master *Please put new entries at the top. +# 7.0.1 1. Added `tintColor` binding target to `UIView`. (#3542, kudos to @iv-mexx) + 1. Fixed `DynamicProperty` for optional properties. (#3548, kudos to @iv-mexx) -1. Made `makeBindingTarget` available on Reactive extensions on all objects, not just `NSObject`. (#3545) +1. Made `makeBindingTarget` available on Reactive extensions on all objects, not just `NSObject`. (#3545, kudos to @Burgestrand) # 7.0.0 1. Update ReactiveSwift to 3.0. diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 609a8e9e67..5a3b2ec02c 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "7.0.0" + s.version = "7.0.1" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index 2478f3d00a..ae96ae8875 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveMapKit" - s.version = "7.0.0" + s.version = "7.0.1" s.summary = "MapKit bindings for ReactiveCocoa." s.description = <<-DESC Provide MapKit bindings for ReactiveCocoa. ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. From 8269faf1c10c6854533cd3c672996aac7b3a14ab Mon Sep 17 00:00:00 2001 From: Romain Pouclet Date: Mon, 27 Nov 2017 12:53:35 -0500 Subject: [PATCH 0880/1028] Add extension for NSTextView --- ReactiveCocoa.xcodeproj/project.pbxproj | 4 ++++ ReactiveCocoa/AppKit/NSTextView.swift | 28 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 ReactiveCocoa/AppKit/NSTextView.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 0b37c08e54..294eda8fac 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -45,6 +45,7 @@ 53AC46CF1DD6FC0000C799E1 /* UISliderSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53AC46CE1DD6FC0000C799E1 /* UISliderSpec.swift */; }; 57A4D2081BA13D7A00F7D4B1 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; 57A4D20A1BA13D7A00F7D4B1 /* ReactiveCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7A8BA0FA1FCC86FC003241C7 /* NSTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8BA0F91FCC86FC003241C7 /* NSTextView.swift */; }; 7DFBED081CDB8C9500EE435B /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */; }; 7DFBED141CDB8CE600EE435B /* test-data.json in Resources */ = {isa = PBXBuildFile; fileRef = D03766B119EDA60000A782A9 /* test-data.json */; }; 7DFBED1E1CDB8D7000EE435B /* ReactiveCocoa.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -416,6 +417,7 @@ 57A4D2451BA13F9700F7D4B1 /* tvOS-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Base.xcconfig"; sourceTree = ""; }; 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Framework.xcconfig"; sourceTree = ""; }; 57A4D2471BA13F9700F7D4B1 /* tvOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-StaticLibrary.xcconfig"; sourceTree = ""; }; + 7A8BA0F91FCC86FC003241C7 /* NSTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSTextView.swift; sourceTree = ""; }; 7DFBED031CDB8C9500EE435B /* ReactiveCocoaTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveCocoaTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 834DE1001E4109750099F4E5 /* NSImageViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSImageViewSpec.swift; sourceTree = ""; }; 834DE1111E4120340099F4E5 /* NSSegmentedControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSSegmentedControl.swift; sourceTree = ""; }; @@ -700,6 +702,7 @@ 834DE1111E4120340099F4E5 /* NSSegmentedControl.swift */, 834DE1131E4122910099F4E5 /* NSSlider.swift */, 8392D8FC1DB93E5E00504ED4 /* NSImageView.swift */, + 7A8BA0F91FCC86FC003241C7 /* NSTextView.swift */, ); path = AppKit; sourceTree = ""; @@ -1518,6 +1521,7 @@ 9ADE4A7C1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, 9ADE4A961DA6F018005C2AC8 /* NSTextField.swift in Sources */, 9AA0BD981DDE7A2200531FCF /* ObjCRuntimeAliases.m in Sources */, + 7A8BA0FA1FCC86FC003241C7 /* NSTextView.swift in Sources */, 9A2E425E1DAA6737006D909F /* CocoaTarget.swift in Sources */, 9AB15C7A1E26CD9A00997378 /* Deprecations+Removals.swift in Sources */, 8392D8FD1DB93E5E00504ED4 /* NSImageView.swift in Sources */, diff --git a/ReactiveCocoa/AppKit/NSTextView.swift b/ReactiveCocoa/AppKit/NSTextView.swift new file mode 100644 index 0000000000..d843062d20 --- /dev/null +++ b/ReactiveCocoa/AppKit/NSTextView.swift @@ -0,0 +1,28 @@ +import ReactiveSwift +import AppKit +import enum Result.NoError + +extension Reactive where Base: NSTextView { + private var notifications: Signal { + let name = NSTextView.didChangeNotification + + return NotificationCenter.default + .reactive + .notifications(forName: name, object: base) + .take(during: lifetime) + } + + /// A signal of values in `String` from the text field upon any changes. + public var continuousStringValues: Signal { + return notifications + .map { ($0.object as! NSTextView).string } + } + + /// A signal of values in `NSAttributedString` from the text field upon any + /// changes. + public var continuousAttributedStringValues: Signal { + return notifications + .map { ($0.object as! NSTextView).attributedString() } + } + +} From a6b54fdb19efb1b2c7d97fc6a57817de83617750 Mon Sep 17 00:00:00 2001 From: Romain Pouclet Date: Mon, 27 Nov 2017 13:14:27 -0500 Subject: [PATCH 0881/1028] Update Changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0c13b47af..b08ab77794 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # master *Please put new entries at the top. +1. Added reactive extension for AppKit's NSTextView + # 7.0.1 1. Added `tintColor` binding target to `UIView`. (#3542, kudos to @iv-mexx) From 2b60932130d2840f485846c355a8797b6f036244 Mon Sep 17 00:00:00 2001 From: Sho Ikeda Date: Sat, 9 Dec 2017 18:56:10 +0900 Subject: [PATCH 0882/1028] [CI] Use Xcode 9.2 image (#3553) * [CI] Use Xcode 9.2 image * [CI] Remove now-unnecessary Carthage re-installation * [CI] Update some xcodebuild destinations --- .swift-version | 2 +- .travis.yml | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/.swift-version b/.swift-version index 5186d07068..c4e41f9459 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -4.0 +4.0.3 diff --git a/.travis.yml b/.travis.yml index f1654de18e..435de9a46d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,6 @@ language: objective-c -osx_image: xcode9 -before_install: - - brew uninstall carthage - - HOMEBREW_NO_AUTO_UPDATE=1 brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/6ae4f69a652fb0ecb102b0c9216378679a4f1b92/Formula/carthage.rb # 0.22.0 +osx_image: xcode9.2 +before_install: true install: true branches: only: @@ -16,7 +14,7 @@ cache: jobs: include: - stage: unit tests - osx_image: xcode9 + osx_image: xcode9.2 script: - XCODE_SCHEME=ReactiveCocoa-macOS XCODE_SDK=macosx @@ -33,12 +31,12 @@ jobs: - XCODE_SCHEME=ReactiveCocoa-tvOS XCODE_SDK=appletvsimulator XCODE_ACTION="build-for-testing test-without-building" - XCODE_DESTINATION="platform=tvOS Simulator,name=Apple TV 1080p" + XCODE_DESTINATION="platform=tvOS Simulator,name=Apple TV" script/build - XCODE_SCHEME=ReactiveCocoa-watchOS XCODE_SDK=watchsimulator XCODE_ACTION=build - XCODE_DESTINATION="platform=watchOS Simulator,name=Apple Watch - 38mm,OS=2.2" + XCODE_DESTINATION="platform=watchOS Simulator,name=Apple Watch Series 3 - 38mm" script/build - stage: package manager tests script: From 54d79473a0c0ef042372c815dcc9f0fff49a3b91 Mon Sep 17 00:00:00 2001 From: Klaas Date: Mon, 11 Dec 2017 18:44:16 +0100 Subject: [PATCH 0883/1028] =?UTF-8?q?Remove=20no=20longer=20needed=20?= =?UTF-8?q?=E2=80=98try!=E2=80=99=20(#3554)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ReactiveCocoa/NSObject+Intercepting.swift | 3 +-- ReactiveCocoa/ObjC+RuntimeSubclassing.swift | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 1aa2176ef5..a758e606f6 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -78,8 +78,7 @@ extension NSObject { let subclass: AnyClass = swizzleClass(self) let subclassAssociations = Associations(subclass as AnyObject) - // FIXME: Compiler asks to handle a mysterious throw. - try! ReactiveCocoa.synchronized(subclass) { + ReactiveCocoa.synchronized(subclass) { let isSwizzled = subclassAssociations.value(forKey: interceptedKey) let signatureCache: SignatureCache diff --git a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift index 11609d473b..ed6e836f17 100644 --- a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift +++ b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift @@ -23,7 +23,7 @@ extension NSObject { internal func swizzle(_ pairs: (Selector, Any)..., key hasSwizzledKey: AssociationKey) { let subclass: AnyClass = swizzleClass(self) - try! ReactiveCocoa.synchronized(subclass) { + ReactiveCocoa.synchronized(subclass) { let subclassAssociations = Associations(subclass as AnyObject) if !subclassAssociations.value(forKey: hasSwizzledKey) { From f07f46220d62786a0eb6e52eb2797a24d0a0babe Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 23 Dec 2017 12:30:43 +0800 Subject: [PATCH 0884/1028] Update ReactiveSwift to 3.1.0-rc.1. (#3555) --- CHANGELOG.md | 4 +++- Cartfile | 2 +- Cartfile.resolved | 4 ++-- Carthage/Checkouts/Nimble | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- ReactiveCocoa.podspec | 2 +- 6 files changed, 9 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b08ab77794..ed53411769 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,9 @@ # master *Please put new entries at the top. -1. Added reactive extension for AppKit's NSTextView +1. Requires ReactiveSwift 3.1.0 Release Candidate 1. (#3555) + +1. Added reactive extension for AppKit's NSTextView. (#3549, kudos to @Palleas) # 7.0.1 1. Added `tintColor` binding target to `UIView`. (#3542, kudos to @iv-mexx) diff --git a/Cartfile b/Cartfile index ecb3846b48..1289996575 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" ~> 3.0 +github "ReactiveCocoa/ReactiveSwift" "3.1.0-rc.1" diff --git a/Cartfile.resolved b/Cartfile.resolved index ac9466c6ed..b7e1635faf 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,5 +1,5 @@ -github "Quick/Nimble" "v7.0.2" +github "Quick/Nimble" "v7.0.3" github "Quick/Quick" "v1.2.0" -github "ReactiveCocoa/ReactiveSwift" "3.0.0" +github "ReactiveCocoa/ReactiveSwift" "3.1.0-rc.1" github "antitypical/Result" "3.2.4" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" diff --git a/Carthage/Checkouts/Nimble b/Carthage/Checkouts/Nimble index 38c9ab0846..22800b0954 160000 --- a/Carthage/Checkouts/Nimble +++ b/Carthage/Checkouts/Nimble @@ -1 +1 @@ -Subproject commit 38c9ab0846a3fbec308eb2aa9ef68b10a7434eb4 +Subproject commit 22800b0954c89344bb8c87f8ab93378076716fb7 diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index 2ec944c43e..85ee0406ac 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit 2ec944c43ef9cf6b1380629e3ea483f62a7afaef +Subproject commit 85ee0406ac173a4be7719d6228d6f7a0ab357e5e diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 5a3b2ec02c..7a7adefd9d 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -23,7 +23,7 @@ Pod::Spec.new do |s| s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_map = "ReactiveCocoa/module.modulemap" - s.dependency 'ReactiveSwift', '~> 3.0' + s.dependency 'ReactiveSwift', '3.1.0-rc.1' s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "-suppress-warnings" } end From 9eee050a9915247fd1e4d72bc5e0f705f97f154d Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 23 Dec 2017 17:00:11 +0800 Subject: [PATCH 0885/1028] 7.1.0-rc.1 --- CHANGELOG.md | 1 + ReactiveCocoa.podspec | 2 +- ReactiveMapKit.podspec | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed53411769..626a20c49c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master *Please put new entries at the top. +# 7.1.0-rc.1 1. Requires ReactiveSwift 3.1.0 Release Candidate 1. (#3555) 1. Added reactive extension for AppKit's NSTextView. (#3549, kudos to @Palleas) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 7a7adefd9d..a3c49eab47 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "7.0.1" + s.version = "7.1.0-rc.1" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index ae96ae8875..d9a421e5eb 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveMapKit" - s.version = "7.0.1" + s.version = "7.1.0-rc.1" s.summary = "MapKit bindings for ReactiveCocoa." s.description = <<-DESC Provide MapKit bindings for ReactiveCocoa. ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. From 30a08cc6b7bc6aafc737124bb7c235cbb1650d2a Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sun, 24 Dec 2017 15:33:57 +0800 Subject: [PATCH 0886/1028] Fix broken Swift 3.2 build. (#3556) --- .travis.yml | 3 ++- CHANGELOG.md | 2 ++ ReactiveCocoa/AppKit/NSTextView.swift | 13 ++++++++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 435de9a46d..88bb3b0e6b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,7 +43,8 @@ jobs: - gem install cocoapods - pod repo update - pod lib lint ReactiveCocoa.podspec - - pod lib lint ReactiveMapKit.podspec --swift-version=3.2 + - pod lib lint ReactiveCocoa.podspec --swift-version=3.2 + - pod lib lint ReactiveMapKit.podspec env: - JOB=PODSPEC - script: carthage build --cache-builds --no-skip-current diff --git a/CHANGELOG.md b/CHANGELOG.md index 626a20c49c..ba81245b92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # master *Please put new entries at the top. +1. Fix an issue preventing ReactiveCocoa from being built with the Swift 3.2 language mode. (#3556) + # 7.1.0-rc.1 1. Requires ReactiveSwift 3.1.0 Release Candidate 1. (#3555) diff --git a/ReactiveCocoa/AppKit/NSTextView.swift b/ReactiveCocoa/AppKit/NSTextView.swift index d843062d20..f1c5362ffb 100644 --- a/ReactiveCocoa/AppKit/NSTextView.swift +++ b/ReactiveCocoa/AppKit/NSTextView.swift @@ -4,7 +4,11 @@ import enum Result.NoError extension Reactive where Base: NSTextView { private var notifications: Signal { + #if swift(>=4.0) let name = NSTextView.didChangeNotification + #else + let name = Notification.Name.NSControlTextDidChange + #endif return NotificationCenter.default .reactive @@ -15,7 +19,14 @@ extension Reactive where Base: NSTextView { /// A signal of values in `String` from the text field upon any changes. public var continuousStringValues: Signal { return notifications - .map { ($0.object as! NSTextView).string } + .map { notification in + let textView = notification.object as! NSTextView + #if swift(>=4.0) + return textView.string + #else + return textView.string ?? "" + #endif + } } /// A signal of values in `NSAttributedString` from the text field upon any From c390de926cba5a9eab406a20b31368cb64e66d20 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Mon, 25 Dec 2017 00:28:31 +0800 Subject: [PATCH 0887/1028] 7.1.0-rc.2 --- CHANGELOG.md | 1 + ReactiveCocoa.podspec | 2 +- ReactiveMapKit.podspec | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba81245b92..e7c7c230ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master *Please put new entries at the top. +# 7.1.0-rc.2 1. Fix an issue preventing ReactiveCocoa from being built with the Swift 3.2 language mode. (#3556) # 7.1.0-rc.1 diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index a3c49eab47..6949e3cee1 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "7.1.0-rc.1" + s.version = "7.1.0-rc.2" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index d9a421e5eb..0f20632258 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveMapKit" - s.version = "7.1.0-rc.1" + s.version = "7.1.0-rc.2" s.summary = "MapKit bindings for ReactiveCocoa." s.description = <<-DESC Provide MapKit bindings for ReactiveCocoa. ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. From cef77fbdbe90ea91fea2c231e7a76bd300b8e25c Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 13 Jan 2018 09:43:27 +0000 Subject: [PATCH 0888/1028] Update ReactiveSwift to 3.1. (#3561) --- Cartfile | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- ReactiveCocoa.podspec | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cartfile b/Cartfile index 1289996575..6e9a77789f 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" "3.1.0-rc.1" +github "ReactiveCocoa/ReactiveSwift" ~> 3.1 diff --git a/Cartfile.resolved b/Cartfile.resolved index b7e1635faf..c643618547 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,5 +1,5 @@ github "Quick/Nimble" "v7.0.3" github "Quick/Quick" "v1.2.0" -github "ReactiveCocoa/ReactiveSwift" "3.1.0-rc.1" +github "ReactiveCocoa/ReactiveSwift" "3.1.0" github "antitypical/Result" "3.2.4" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index 85ee0406ac..46fb4d4a82 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit 85ee0406ac173a4be7719d6228d6f7a0ab357e5e +Subproject commit 46fb4d4a8285286e54929add1d12f384675895c6 diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 6949e3cee1..0b8457c206 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -23,7 +23,7 @@ Pod::Spec.new do |s| s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_map = "ReactiveCocoa/module.modulemap" - s.dependency 'ReactiveSwift', '3.1.0-rc.1' + s.dependency 'ReactiveSwift', '~> 3.1' s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "-suppress-warnings" } end From e5532fc81474ced9908965c5e4d5a91135fb2e2d Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 13 Jan 2018 16:21:24 +0000 Subject: [PATCH 0889/1028] 7.1.0 --- CHANGELOG.md | 1 + ReactiveCocoa.podspec | 2 +- ReactiveMapKit.podspec | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7c7c230ba..a8685f879e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master *Please put new entries at the top. +# 7.1.0 # 7.1.0-rc.2 1. Fix an issue preventing ReactiveCocoa from being built with the Swift 3.2 language mode. (#3556) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 0b8457c206..ed8ef79927 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "7.1.0-rc.2" + s.version = "7.1.0" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index 0f20632258..509ec8717b 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveMapKit" - s.version = "7.1.0-rc.2" + s.version = "7.1.0" s.summary = "MapKit bindings for ReactiveCocoa." s.description = <<-DESC Provide MapKit bindings for ReactiveCocoa. ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. From 390ea8efe2aed4b12f18c57d8d973a8d77891a55 Mon Sep 17 00:00:00 2001 From: BAN Jun Date: Wed, 24 Jan 2018 11:57:19 +0900 Subject: [PATCH 0890/1028] add reactive extension for UISearchBar allowing access to starts and ends of editing, and the cancel button --- ReactiveCocoa/UIKit/iOS/UISearchBar.swift | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/ReactiveCocoa/UIKit/iOS/UISearchBar.swift b/ReactiveCocoa/UIKit/iOS/UISearchBar.swift index 0b78138f11..994be130c8 100644 --- a/ReactiveCocoa/UIKit/iOS/UISearchBar.swift +++ b/ReactiveCocoa/UIKit/iOS/UISearchBar.swift @@ -3,6 +3,10 @@ import enum Result.NoError import UIKit private class SearchBarDelegateProxy: DelegateProxy, UISearchBarDelegate { + @objc func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) { + forwardee?.searchBarTextDidBeginEditing?(searchBar) + } + @objc func searchBarTextDidEndEditing(_ searchBar: UISearchBar) { forwardee?.searchBarTextDidEndEditing?(searchBar) } @@ -91,4 +95,19 @@ extension Reactive where Base: UISearchBar { public var resultsListButtonClicked: Signal { return proxy.intercept(#selector(proxy.searchBarResultsListButtonClicked)) } + + /// A void signal emitted by the search bar upon start of editing + public var textDidBeginEditing: Signal { + return proxy.intercept(#selector(proxy.searchBarTextDidBeginEditing)) + } + + /// A void signal emitted by the search bar upon end of editing + public var textDidEndEditing: Signal { + return proxy.intercept(#selector(proxy.searchBarTextDidEndEditing)) + } + + /// Shows and hides the cancel button of the search bar + public var showsCancelButton: BindingTarget { + return makeBindingTarget { $0.showsCancelButton = $1 } + } } From e8b0bb91d000e9a2001ed655ab2046bfe9fba6f3 Mon Sep 17 00:00:00 2001 From: BAN Jun Date: Wed, 24 Jan 2018 12:38:56 +0900 Subject: [PATCH 0891/1028] add specs for observing starts and ends of editing, and binding to the cancel button --- .../UIKit/UISearchBarSpec.swift | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift b/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift index b711d3d353..60c8474a61 100644 --- a/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift +++ b/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift @@ -171,11 +171,54 @@ class UISearchBarSpec: QuickSpec { expect(isClicked) == true expect(receiver.resultsListButtonClickedCounter) == 1 } + + + it("should notify when started editing") { + var didBegin: Bool? + searchBar.reactive.textDidBeginEditing + .observeValues { didBegin = true } + + expect(didBegin).to(beNil()) + expect(receiver.beginEditingCounter) == 0 + + searchBar.delegate!.searchBarTextDidBeginEditing!(searchBar) + expect(didBegin) == true + expect(receiver.beginEditingCounter) == 1 + } + + + it("should notify when ended editing") { + var didEnd: Bool? + searchBar.reactive.textDidEndEditing + .observeValues { didEnd = true } + + expect(didEnd).to(beNil()) + expect(receiver.endEditingTexts.isEmpty) == true + + searchBar.delegate!.searchBarTextDidEndEditing!(searchBar) + expect(didEnd) == true + expect(receiver.endEditingTexts.count) == 1 + } + + + it("should accept changes from bindings to its hidden state of the cancel button") { + searchBar.showsCancelButton = false + + let (pipeSignal, observer) = Signal.pipe() + searchBar.reactive.showsCancelButton <~ SignalProducer(pipeSignal) + + observer.send(value: true) + expect(searchBar.showsCancelButton) == true + + observer.send(value: false) + expect(searchBar.showsCancelButton) == false + } } } class SearchBarDelegateReceiver: NSObject, UISearchBarDelegate { var texts: [String] = [] + var beginEditingCounter = 0 var endEditingTexts: [String] = [] var searchButtonClickedCounter = 0 var cancelButtonClickedCounter = 0 @@ -191,6 +234,10 @@ class SearchBarDelegateReceiver: NSObject, UISearchBarDelegate { texts.append(searchText) } + func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) { + beginEditingCounter += 1 + } + func searchBarTextDidEndEditing(_ searchBar: UISearchBar) { endEditingTexts.append(searchBar.text ?? "") } From 0afb2cea9218826d301ab4b258104b8368e29772 Mon Sep 17 00:00:00 2001 From: BAN Jun Date: Wed, 24 Jan 2018 13:59:28 +0900 Subject: [PATCH 0892/1028] update CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8685f879e..695e054402 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # master *Please put new entries at the top. +1. Add `showsCancelButton`, `textDidBeginEditing` and `textDidEndEditing` extensions to `UISearchBar` (#3565) + # 7.1.0 # 7.1.0-rc.2 1. Fix an issue preventing ReactiveCocoa from being built with the Swift 3.2 language mode. (#3556) From adaf999db396d2eef7cb20d940319aa9e1bea6be Mon Sep 17 00:00:00 2001 From: Ryo Aoyama Date: Wed, 31 Jan 2018 00:16:36 +0900 Subject: [PATCH 0893/1028] Observe the keyboard change context for specified Events --- ReactiveCocoa/UIKit/iOS/UIKeyboard.swift | 49 ++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift b/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift index d50c4a4a26..6aff222dc7 100644 --- a/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift +++ b/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift @@ -2,9 +2,40 @@ import UIKit import ReactiveSwift import enum Result.NoError +/// The type of system keyboard events. +public enum KeyboardEvent { + case willShow + case didShow + case willHide + case didHide + case willChangeFrame + case didChangeFrame + + /// The name of the notification to observe system keyboard events. + var notificationName: Notification.Name { + switch self { + case .willShow: + return .UIKeyboardWillShow + case .didShow: + return .UIKeyboardDidShow + case .willHide: + return .UIKeyboardWillHide + case .didHide: + return .UIKeyboardDidHide + case .willChangeFrame: + return .UIKeyboardWillChangeFrame + case .didChangeFrame: + return .UIKeyboardDidChangeFrame + } + } +} + /// The context of an upcoming change in the frame of the system keyboard. public struct KeyboardChangeContext { private let base: [AnyHashable: Any] + + /// The event type of the system keyboard. + public let event: KeyboardEvent /// The current frame of the system keyboard. public var beginFrame: CGRect { @@ -37,19 +68,31 @@ public struct KeyboardChangeContext { return base[UIKeyboardIsLocalUserInfoKey] as! Bool } - fileprivate init(_ userInfo: [AnyHashable: Any]) { + fileprivate init(userInfo: [AnyHashable: Any], event: KeyboardEvent) { base = userInfo + self.event = event } } extension Reactive where Base: NotificationCenter { + /// Create a `Signal` that notifies whenever the system keyboard announces specified events. + /// + /// - returns: A `Signal` that emits the context of system keyboard events. + public func keyboard(_ events: KeyboardEvent...) -> Signal { + return .merge( + events.map { event in + notifications(forName: event.notificationName) + .map { notification in KeyboardChangeContext(userInfo: notification.userInfo!, event: event) } + } + ) + } + /// Create a `Signal` that notifies whenever the system keyboard announces an /// upcoming change in its frame. /// /// - returns: A `Signal` that emits the context of every change in the /// system keyboard's frame. public var keyboardChange: Signal { - return notifications(forName: .UIKeyboardWillChangeFrame) - .map { notification in KeyboardChangeContext(notification.userInfo!) } + return keyboard(.willChangeFrame) } } From 246cb70f4779701bd7ae87d8ac6c363cfb35daa3 Mon Sep 17 00:00:00 2001 From: Ryo Aoyama Date: Wed, 31 Jan 2018 00:28:07 +0900 Subject: [PATCH 0894/1028] Add spec --- ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift | 113 +++++++++++++++++- 1 file changed, 112 insertions(+), 1 deletion(-) diff --git a/ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift b/ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift index 1c6fdf59d9..e004ffaf6a 100644 --- a/ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift @@ -6,6 +6,116 @@ import Nimble class UIKeyboardSpec: QuickSpec { override func spec() { + describe("NotificationCenter.reactive.keyboard(_:)") { + it("should emit a `value` event when the notification is posted") { + var context: KeyboardChangeContext? + + let testCenter = NotificationCenter() + + testCenter.reactive.keyboard(.willShow, .didShow, .willHide, .didHide, .willChangeFrame, .didChangeFrame) + .observeValues { context = $0 } + + func makeDummyInfo(beginFrameHeight: CGFloat) -> [AnyHashable: Any] { + var dummyInfo: [AnyHashable: Any] = [ + UIKeyboardFrameBeginUserInfoKey: CGRect(x: 10, y: 10, width: 10, height: beginFrameHeight), + UIKeyboardFrameEndUserInfoKey: CGRect(x: 20, y: 20, width: 20, height: 20), + UIKeyboardAnimationDurationUserInfoKey: 1.0, + UIKeyboardAnimationCurveUserInfoKey: NSNumber(value: UIViewAnimationCurve.easeInOut.rawValue) + ] + if #available(*, iOS 9.0) { + dummyInfo[UIKeyboardIsLocalUserInfoKey] = NSNumber(value: true) + } + return dummyInfo + } + + testCenter.post(name: .UIKeyboardWillShow, + object: nil, + userInfo: makeDummyInfo(beginFrameHeight: 10)) + + expect(context).toNot(beNil()) + + expect(context?.event) == .willShow + expect(context?.beginFrame) == CGRect(x: 10, y: 10, width: 10, height: 10) + expect(context?.endFrame) == CGRect(x: 20, y: 20, width: 20, height: 20) + expect(context?.animationCurve) == .easeInOut + expect(context?.animationDuration) == 1.0 + + if #available(*, iOS 9.0) { + expect(context?.isLocal) == true + } + + testCenter.post(name: .UIKeyboardDidShow, + object: nil, + userInfo: makeDummyInfo(beginFrameHeight: 20)) + + expect(context?.event) == .didShow + expect(context?.beginFrame) == CGRect(x: 10, y: 10, width: 10, height: 20) + expect(context?.endFrame) == CGRect(x: 20, y: 20, width: 20, height: 20) + expect(context?.animationCurve) == .easeInOut + expect(context?.animationDuration) == 1.0 + + if #available(*, iOS 9.0) { + expect(context?.isLocal) == true + } + + testCenter.post(name: .UIKeyboardWillHide, + object: nil, + userInfo: makeDummyInfo(beginFrameHeight: 30)) + + expect(context?.event) == .willHide + expect(context?.beginFrame) == CGRect(x: 10, y: 10, width: 10, height: 30) + expect(context?.endFrame) == CGRect(x: 20, y: 20, width: 20, height: 20) + expect(context?.animationCurve) == .easeInOut + expect(context?.animationDuration) == 1.0 + + if #available(*, iOS 9.0) { + expect(context?.isLocal) == true + } + + testCenter.post(name: .UIKeyboardDidHide, + object: nil, + userInfo: makeDummyInfo(beginFrameHeight: 40)) + + expect(context?.event) == .didHide + expect(context?.beginFrame) == CGRect(x: 10, y: 10, width: 10, height: 40) + expect(context?.endFrame) == CGRect(x: 20, y: 20, width: 20, height: 20) + expect(context?.animationCurve) == .easeInOut + expect(context?.animationDuration) == 1.0 + + if #available(*, iOS 9.0) { + expect(context?.isLocal) == true + } + + testCenter.post(name: .UIKeyboardWillChangeFrame, + object: nil, + userInfo: makeDummyInfo(beginFrameHeight: 50)) + + expect(context?.event) == .willChangeFrame + expect(context?.beginFrame) == CGRect(x: 10, y: 10, width: 10, height: 50) + expect(context?.endFrame) == CGRect(x: 20, y: 20, width: 20, height: 20) + expect(context?.animationCurve) == .easeInOut + expect(context?.animationDuration) == 1.0 + + if #available(*, iOS 9.0) { + expect(context?.isLocal) == true + } + + testCenter.post(name: .UIKeyboardDidChangeFrame, + object: nil, + userInfo: makeDummyInfo(beginFrameHeight: 60)) + + expect(context?.event) == .didChangeFrame + expect(context?.beginFrame) == CGRect(x: 10, y: 10, width: 10, height: 60) + expect(context?.endFrame) == CGRect(x: 20, y: 20, width: 20, height: 20) + expect(context?.animationCurve) == .easeInOut + expect(context?.animationDuration) == 1.0 + + if #available(*, iOS 9.0) { + expect(context?.isLocal) == true + } + } + } + describe("NotificationCenter.reactive.keyboardChange") { it("should emit a `value` event when the notification is posted") { var context: KeyboardChangeContext? @@ -31,7 +141,8 @@ class UIKeyboardSpec: QuickSpec { userInfo: dummyInfo) expect(context).toNot(beNil()) - + + expect(context?.event) == .willChangeFrame expect(context?.beginFrame) == CGRect(x: 10, y: 10, width: 10, height: 10) expect(context?.endFrame) == CGRect(x: 20, y: 20, width: 20, height: 20) expect(context?.animationCurve) == .easeInOut From ca30a11b0b0781a5ffa0ac3ab6a6d557cd356eb9 Mon Sep 17 00:00:00 2001 From: Ryo Aoyama Date: Wed, 31 Jan 2018 00:51:39 +0900 Subject: [PATCH 0895/1028] Add fileprivate to KeyboardEvent.notificationName --- ReactiveCocoa/UIKit/iOS/UIKeyboard.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift b/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift index 6aff222dc7..ce028cc7f0 100644 --- a/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift +++ b/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift @@ -12,7 +12,7 @@ public enum KeyboardEvent { case didChangeFrame /// The name of the notification to observe system keyboard events. - var notificationName: Notification.Name { + fileprivate var notificationName: Notification.Name { switch self { case .willShow: return .UIKeyboardWillShow From 88cea8e9ad5766089452029d83e97a55c03510e3 Mon Sep 17 00:00:00 2001 From: Ryo Aoyama Date: Wed, 31 Jan 2018 01:29:48 +0900 Subject: [PATCH 0896/1028] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8685f879e..e274a81a6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # master *Please put new entries at the top. +1. `NotificationCenter.reactive.keyboard(_:)` for system keyboard notification by the event types. (#3566, kudos to @ra1028) + # 7.1.0 # 7.1.0-rc.2 1. Fix an issue preventing ReactiveCocoa from being built with the Swift 3.2 language mode. (#3556) From cebe276ffcec9df969b44f27cfeecce19ceaac7f Mon Sep 17 00:00:00 2001 From: Ryo Aoyama Date: Fri, 2 Feb 2018 19:42:46 +0900 Subject: [PATCH 0897/1028] Force to required to pass at least one event --- ReactiveCocoa/UIKit/iOS/UIKeyboard.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift b/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift index ce028cc7f0..885634ae00 100644 --- a/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift +++ b/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift @@ -78,7 +78,8 @@ extension Reactive where Base: NotificationCenter { /// Create a `Signal` that notifies whenever the system keyboard announces specified events. /// /// - returns: A `Signal` that emits the context of system keyboard events. - public func keyboard(_ events: KeyboardEvent...) -> Signal { + public func keyboard(_ first: KeyboardEvent, _ tail: KeyboardEvent...) -> Signal { + let events = [first] + tail return .merge( events.map { event in notifications(forName: event.notificationName) From 7563702f192d44f413a4ebdb1561258ef5122133 Mon Sep 17 00:00:00 2001 From: Ryo Aoyama Date: Fri, 2 Feb 2018 19:49:13 +0900 Subject: [PATCH 0898/1028] Refactor --- ReactiveCocoa/UIKit/iOS/UIKeyboard.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift b/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift index 885634ae00..92b5af69d4 100644 --- a/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift +++ b/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift @@ -80,12 +80,7 @@ extension Reactive where Base: NotificationCenter { /// - returns: A `Signal` that emits the context of system keyboard events. public func keyboard(_ first: KeyboardEvent, _ tail: KeyboardEvent...) -> Signal { let events = [first] + tail - return .merge( - events.map { event in - notifications(forName: event.notificationName) - .map { notification in KeyboardChangeContext(userInfo: notification.userInfo!, event: event) } - } - ) + return .merge(events.map(_keyboard)) } /// Create a `Signal` that notifies whenever the system keyboard announces an @@ -94,6 +89,11 @@ extension Reactive where Base: NotificationCenter { /// - returns: A `Signal` that emits the context of every change in the /// system keyboard's frame. public var keyboardChange: Signal { - return keyboard(.willChangeFrame) + return _keyboard(.willChangeFrame) + } + + private func _keyboard(_ event: KeyboardEvent) -> Signal { + return notifications(forName: event.notificationName) + .map { notification in KeyboardChangeContext(userInfo: notification.userInfo!, event: event) } } } From 99c8a614e570b4cc7cd45090a6ef98fb751d4009 Mon Sep 17 00:00:00 2001 From: Ryo Aoyama Date: Fri, 2 Feb 2018 22:06:53 +0900 Subject: [PATCH 0899/1028] Refactor --- ReactiveCocoa/UIKit/iOS/UIKeyboard.swift | 29 +++++++++----- ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift | 40 ++++++++++++++++++- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift b/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift index 92b5af69d4..2d8a6c8abf 100644 --- a/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift +++ b/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift @@ -75,12 +75,28 @@ public struct KeyboardChangeContext { } extension Reactive where Base: NotificationCenter { + /// Create a `Signal` that notifies whenever the system keyboard announce specified event. + /// + /// - parameters: + /// - event: The type of system keyboard event to observe. + /// + /// - returns: A `Signal` that emits the context of system keyboard event. + public func keyboard(_ event: KeyboardEvent) -> Signal { + return notifications(forName: event.notificationName) + .map { notification in KeyboardChangeContext(userInfo: notification.userInfo!, event: event) } + } + /// Create a `Signal` that notifies whenever the system keyboard announces specified events. /// + /// - parameters: + /// - first: First type of system keyboard event to observe. + /// - second: Second type of system keyboard event to observe. + /// - tail: Rest of the types of system keyboard events to observe. + /// /// - returns: A `Signal` that emits the context of system keyboard events. - public func keyboard(_ first: KeyboardEvent, _ tail: KeyboardEvent...) -> Signal { - let events = [first] + tail - return .merge(events.map(_keyboard)) + public func keyboard(_ first: KeyboardEvent, _ second: KeyboardEvent, _ tail: KeyboardEvent...) -> Signal { + let events = [first, second] + tail + return .merge(events.map(keyboard)) } /// Create a `Signal` that notifies whenever the system keyboard announces an @@ -89,11 +105,6 @@ extension Reactive where Base: NotificationCenter { /// - returns: A `Signal` that emits the context of every change in the /// system keyboard's frame. public var keyboardChange: Signal { - return _keyboard(.willChangeFrame) - } - - private func _keyboard(_ event: KeyboardEvent) -> Signal { - return notifications(forName: event.notificationName) - .map { notification in KeyboardChangeContext(userInfo: notification.userInfo!, event: event) } + return keyboard(.willChangeFrame) } } diff --git a/ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift b/ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift index e004ffaf6a..b27bf2ec21 100644 --- a/ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift @@ -6,7 +6,45 @@ import Nimble class UIKeyboardSpec: QuickSpec { override func spec() { - describe("NotificationCenter.reactive.keyboard(_:)") { + describe("NotificationCenter.reactive.keyboard(_;)") { + it("should emit a `value` event when the notification is posted") { + var context: KeyboardChangeContext? + + let testCenter = NotificationCenter() + + testCenter.reactive.keyboard(.willShow) + .observeValues { context = $0 } + + var dummyInfo: [AnyHashable: Any] = [ + UIKeyboardFrameBeginUserInfoKey: CGRect(x: 10, y: 10, width: 10, height: 10), + UIKeyboardFrameEndUserInfoKey: CGRect(x: 20, y: 20, width: 20, height: 20), + UIKeyboardAnimationDurationUserInfoKey: 1.0, + UIKeyboardAnimationCurveUserInfoKey: NSNumber(value: UIViewAnimationCurve.easeInOut.rawValue) + ] + + if #available(*, iOS 9.0) { + dummyInfo[UIKeyboardIsLocalUserInfoKey] = NSNumber(value: true) + } + + testCenter.post(name: .UIKeyboardWillShow, + object: nil, + userInfo: dummyInfo) + + expect(context).toNot(beNil()) + + expect(context?.event) == .willShow + expect(context?.beginFrame) == CGRect(x: 10, y: 10, width: 10, height: 10) + expect(context?.endFrame) == CGRect(x: 20, y: 20, width: 20, height: 20) + expect(context?.animationCurve) == .easeInOut + expect(context?.animationDuration) == 1.0 + + if #available(*, iOS 9.0) { + expect(context?.isLocal) == true + } + } + } + + describe("NotificationCenter.reactive.keyboard(_:_:_;)") { it("should emit a `value` event when the notification is posted") { var context: KeyboardChangeContext? From 8fc732d1513511673de90c2ef6ec53d4ceb7ef6e Mon Sep 17 00:00:00 2001 From: Marco Cancellieri Date: Fri, 2 Mar 2018 23:33:00 +0100 Subject: [PATCH 0900/1028] Reactive extension for MKLocalSearchRequest (#3571) * Reactive extension for MKLocalSearchRequest * use trailing closure * indentation --- ReactiveCocoa.xcodeproj/project.pbxproj | 4 ++++ ReactiveMapKit/MKLocalSearchRequest.swift | 28 +++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 ReactiveMapKit/MKLocalSearchRequest.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 294eda8fac..2f2b1f5e5f 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -249,6 +249,7 @@ 9AFCBFE31EB1ABC0004B4C74 /* KVOKVCExtensionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AFCBFE21EB1ABC0004B4C74 /* KVOKVCExtensionSpec.swift */; }; 9AFCBFE41EB1ABC0004B4C74 /* KVOKVCExtensionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AFCBFE21EB1ABC0004B4C74 /* KVOKVCExtensionSpec.swift */; }; 9AFCBFE51EB1ABC0004B4C74 /* KVOKVCExtensionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AFCBFE21EB1ABC0004B4C74 /* KVOKVCExtensionSpec.swift */; }; + A91244E820389AEA0001BBCB /* MKLocalSearchRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91244E720389AEA0001BBCB /* MKLocalSearchRequest.swift */; }; A9B315C91B3940980001CB9C /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; A9B315CA1B3940AB0001CB9C /* ReactiveCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; }; A9EB3D201E94F08A002A9BCC /* UINavigationItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D1C1E94ECAC002A9BCC /* UINavigationItem.swift */; }; @@ -500,6 +501,7 @@ 9AED64C41E496A3700321004 /* ActionProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionProxy.swift; sourceTree = ""; }; 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+BindingTarget.swift"; sourceTree = ""; }; 9AFCBFE21EB1ABC0004B4C74 /* KVOKVCExtensionSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KVOKVCExtensionSpec.swift; sourceTree = ""; }; + A91244E720389AEA0001BBCB /* MKLocalSearchRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MKLocalSearchRequest.swift; sourceTree = ""; }; A97451331B3A935E00F48E55 /* watchOS-Application.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Application.xcconfig"; sourceTree = ""; }; A97451341B3A935E00F48E55 /* watchOS-Base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Base.xcconfig"; sourceTree = ""; }; A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Framework.xcconfig"; sourceTree = ""; }; @@ -772,6 +774,7 @@ isa = PBXGroup; children = ( 53A6BED11DD4BCA90016C058 /* MKMapView.swift */, + A91244E720389AEA0001BBCB /* MKLocalSearchRequest.swift */, 9AC03A5A1F7CC3BF00EC33C1 /* Info.plist */, ); path = ReactiveMapKit; @@ -1467,6 +1470,7 @@ buildActionMask = 2147483647; files = ( 9AC03A611F7CC5E300EC33C1 /* MKMapView.swift in Sources */, + A91244E820389AEA0001BBCB /* MKLocalSearchRequest.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ReactiveMapKit/MKLocalSearchRequest.swift b/ReactiveMapKit/MKLocalSearchRequest.swift new file mode 100644 index 0000000000..680eab356b --- /dev/null +++ b/ReactiveMapKit/MKLocalSearchRequest.swift @@ -0,0 +1,28 @@ +import ReactiveSwift +import ReactiveCocoa +import Result +import MapKit + +#if os(iOS) || os(tvOS) || os(macOS) + private let defaultLocalSearchError = NSError(domain: "org.reactivecocoa.ReactiveCocoa.Reactivity.MKLocalSearchRequest", + code: 1, + userInfo: nil) + @available(tvOS 9.2, *) + extension Reactive where Base: MKLocalSearchRequest { + /// A SignalProducer which performs an `MKLocalSearch`. + public var search: SignalProducer { + return SignalProducer {[base = self.base] observer, lifetime in + let search = MKLocalSearch(request: base) + search.start { response, error in + if let response = response { + observer.send(value: response) + observer.sendCompleted() + } else { + observer.send(error: AnyError(error ?? defaultLocalSearchError)) + } + } + lifetime.observeEnded(search.cancel) + } + } + } +#endif From 970f04a0c168357f5b5c49cf27b46429cdca0f2b Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Thu, 15 Feb 2018 22:58:01 +0000 Subject: [PATCH 0901/1028] Handle forwarded selectors without any implementation. --- CHANGELOG.md | 2 + ReactiveCocoa.xcodeproj/project.pbxproj | 298 ++---------------- ReactiveCocoa/NSObject+Intercepting.swift | 16 +- ReactiveCocoa/ObjC+Messages.swift | 5 +- ReactiveCocoaTests/InterceptingSpec.swift | 19 ++ ReactiveCocoaTests/MessageForwardingEntity.h | 5 + ReactiveCocoaTests/MessageForwardingEntity.m | 35 ++ .../ReactiveCocoaTests-Bridging-Header.h | 1 + 8 files changed, 107 insertions(+), 274 deletions(-) create mode 100644 ReactiveCocoaTests/MessageForwardingEntity.h create mode 100644 ReactiveCocoaTests/MessageForwardingEntity.m create mode 100644 ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 264267e733..a90d6480a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # master *Please put new entries at the top. +1. Improved the interoperability of method interception. (#3570, kudos to @andersio) + 1. Add `showsCancelButton`, `textDidBeginEditing` and `textDidEndEditing` extensions to `UISearchBar` (#3566) 1. `NotificationCenter.reactive.keyboard(_:)` for system keyboard notification by the event types. (#3566, kudos to @ra1028) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 294eda8fac..bfd3a858e4 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -219,6 +219,9 @@ 9AD0F06B1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; 9AD0F06C1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; 9AD0F06D1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; + 9AD841DC204C29B90040F0C0 /* MessageForwardingEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AD841DB204C29B90040F0C0 /* MessageForwardingEntity.m */; }; + 9AD841DD204C29B90040F0C0 /* MessageForwardingEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AD841DB204C29B90040F0C0 /* MessageForwardingEntity.m */; }; + 9AD841DE204C29B90040F0C0 /* MessageForwardingEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AD841DB204C29B90040F0C0 /* MessageForwardingEntity.m */; }; 9ADE4A7C1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */; }; 9ADE4A7D1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */; }; 9ADE4A7E1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */; }; @@ -488,6 +491,9 @@ 9AC03A5A1F7CC3BF00EC33C1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9AC03A631F7CC6B300EC33C1 /* ReactiveSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ReactiveSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+KeyValueObserving.swift"; sourceTree = ""; }; + 9AD841D6204C29B90040F0C0 /* ReactiveCocoaTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ReactiveCocoaTests-Bridging-Header.h"; sourceTree = ""; }; + 9AD841DA204C29B90040F0C0 /* MessageForwardingEntity.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MessageForwardingEntity.h; sourceTree = ""; }; + 9AD841DB204C29B90040F0C0 /* MessageForwardingEntity.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MessageForwardingEntity.m; sourceTree = ""; }; 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaAction.swift; sourceTree = ""; }; 9ADE4A881DA6D206005C2AC8 /* NSControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSControl.swift; sourceTree = ""; }; 9ADE4A8D1DA6D965005C2AC8 /* NSControlSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSControlSpec.swift; sourceTree = ""; }; @@ -928,6 +934,9 @@ 3B30EE8B1E7BE529007CC8EF /* DeprecationsSpec.swift */, CD42C69A1E951F6900AA9504 /* ReactiveCocoaTestsConfiguration.swift */, D04725FA19E49ED7006002AA /* Supporting Files */, + 9AD841DA204C29B90040F0C0 /* MessageForwardingEntity.h */, + 9AD841DB204C29B90040F0C0 /* MessageForwardingEntity.m */, + 9AD841D6204C29B90040F0C0 /* ReactiveCocoaTests-Bridging-Header.h */, ); path = ReactiveCocoaTests; sourceTree = ""; @@ -1241,10 +1250,11 @@ }; 7DFBED021CDB8C9500EE435B = { CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 0800; + LastSwiftMigration = 0920; }; 9A16753C1F80C35100B63650 = { CreatedOnToolsVersion = 9.0; + LastSwiftMigration = 0920; ProvisioningStyle = Automatic; }; 9AC03A561F7CC3BF00EC33C1 = { @@ -1260,7 +1270,7 @@ }; D04725F419E49ED7006002AA = { CreatedOnToolsVersion = 6.1; - LastSwiftMigration = 0800; + LastSwiftMigration = 0920; }; D047260B19E49F82006002AA = { CreatedOnToolsVersion = 6.1; @@ -1268,7 +1278,7 @@ }; D047261519E49F82006002AA = { CreatedOnToolsVersion = 6.1; - LastSwiftMigration = 0800; + LastSwiftMigration = 0920; }; }; }; @@ -1439,6 +1449,7 @@ 9A892D911E8D19BE00EA35F3 /* DelegateProxySpec.swift in Sources */, 9A1D06471D93EA7E00ACF44C /* UILabelSpec.swift in Sources */, 9A1D06391D93EA7E00ACF44C /* UIBarButtonItemSpec.swift in Sources */, + 9AD841DE204C29B90040F0C0 /* MessageForwardingEntity.m in Sources */, A9EB3D2B1E94F3D9002A9BCC /* UITabBarItemSpec.swift in Sources */, 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */, 9ADFE5A31DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */, @@ -1554,6 +1565,7 @@ B696FB811A7640C00075236D /* TestError.swift in Sources */, 9A892D8F1E8D19BE00EA35F3 /* DelegateProxySpec.swift in Sources */, 004FD0071E26CDB300A03A82 /* NSButtonSpec.swift in Sources */, + 9AD841DC204C29B90040F0C0 /* MessageForwardingEntity.m in Sources */, 9A7990CE1F1085D8001493A3 /* BindingTargetSpec.swift in Sources */, D9558AB91DFF86C0003254E1 /* NSPopUpButtonSpec.swift in Sources */, BFA6B94D1A7604D400C846D1 /* SignalProducerNimbleMatchers.swift in Sources */, @@ -1665,6 +1677,7 @@ BF4335681E02EF0600AC88DD /* UIScrollViewSpec.swift in Sources */, 3BCAAC7D1DEE1A2D00B30335 /* UIRefreshControlSpec.swift in Sources */, 9A1D06381D93EA7E00ACF44C /* UIBarButtonItemSpec.swift in Sources */, + 9AD841DD204C29B90040F0C0 /* MessageForwardingEntity.m in Sources */, 9A1E72BB1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, 9A6AAA2E1DB903A20013AAEA /* ReusableComponentsSpec.swift in Sources */, 4ABEFE211DCFCF090066A8C2 /* UITableViewSpec.swift in Sources */, @@ -1711,10 +1724,6 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; }; @@ -1727,10 +1736,6 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; }; @@ -1743,10 +1748,6 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; }; @@ -1759,10 +1760,6 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; }; @@ -1772,13 +1769,9 @@ isa = XCBuildConfiguration; baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; buildSettings = { - CODE_SIGN_IDENTITY = ""; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + SWIFT_OBJC_BRIDGING_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h"; }; name = Debug; }; @@ -1786,13 +1779,9 @@ isa = XCBuildConfiguration; baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; buildSettings = { - CODE_SIGN_IDENTITY = ""; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + SWIFT_OBJC_BRIDGING_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h"; }; name = Test; }; @@ -1800,13 +1789,9 @@ isa = XCBuildConfiguration; baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; buildSettings = { - CODE_SIGN_IDENTITY = ""; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + SWIFT_OBJC_BRIDGING_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h"; }; name = Release; }; @@ -1814,13 +1799,9 @@ isa = XCBuildConfiguration; baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; buildSettings = { - CODE_SIGN_IDENTITY = ""; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + SWIFT_OBJC_BRIDGING_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h"; }; name = Profile; }; @@ -1828,62 +1809,12 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047262E19E49FE8006002AA /* Application.xcconfig */; buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "-"; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; SUPPORTED_PLATFORMS = "macosx iphonesimulator appletvsimulator watchsimulator"; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; @@ -1891,55 +1822,12 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047262E19E49FE8006002AA /* Application.xcconfig */; buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "-"; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; - MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; SUPPORTED_PLATFORMS = "macosx iphonesimulator appletvsimulator watchsimulator"; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; }; name = Test; }; @@ -1947,55 +1835,12 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047262E19E49FE8006002AA /* Application.xcconfig */; buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "-"; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; - MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; SUPPORTED_PLATFORMS = "macosx iphonesimulator appletvsimulator watchsimulator"; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; }; name = Release; }; @@ -2003,55 +1848,12 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047262E19E49FE8006002AA /* Application.xcconfig */; buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "-"; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; - MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; SUPPORTED_PLATFORMS = "macosx iphonesimulator appletvsimulator watchsimulator"; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; }; name = Profile; }; @@ -2061,7 +1863,6 @@ buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = ReactiveMapKit/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; @@ -2077,7 +1878,6 @@ buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = ReactiveMapKit/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; @@ -2093,7 +1893,6 @@ buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = ReactiveMapKit/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; @@ -2109,7 +1908,6 @@ buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = ReactiveMapKit/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; @@ -2126,10 +1924,6 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; }; @@ -2142,10 +1936,6 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; }; @@ -2158,10 +1948,6 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; }; @@ -2174,10 +1960,6 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "DTRACE_PROBES_DISABLED=1", - ); INFOPLIST_FILE = ReactiveCocoa/Info.plist; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; }; @@ -2253,12 +2035,9 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(DEVELOPER_FRAMEWORKS_DIR)", - "$(inherited)", - ); INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + SWIFT_OBJC_BRIDGING_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h"; }; name = Debug; }; @@ -2266,12 +2045,9 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(DEVELOPER_FRAMEWORKS_DIR)", - "$(inherited)", - ); INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + SWIFT_OBJC_BRIDGING_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h"; }; name = Release; }; @@ -2303,12 +2079,9 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + SWIFT_OBJC_BRIDGING_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h"; }; name = Debug; }; @@ -2316,12 +2089,9 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + SWIFT_OBJC_BRIDGING_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h"; }; name = Release; }; @@ -2361,12 +2131,9 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(DEVELOPER_FRAMEWORKS_DIR)", - "$(inherited)", - ); INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + SWIFT_OBJC_BRIDGING_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h"; }; name = Profile; }; @@ -2386,12 +2153,9 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + SWIFT_OBJC_BRIDGING_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h"; }; name = Profile; }; @@ -2431,12 +2195,9 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(DEVELOPER_FRAMEWORKS_DIR)", - "$(inherited)", - ); INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + SWIFT_OBJC_BRIDGING_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h"; }; name = Test; }; @@ -2456,12 +2217,9 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; + SWIFT_OBJC_BRIDGING_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h"; }; name = Test; }; diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index a758e606f6..688cb951c8 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -155,8 +155,18 @@ private func enableMessageForwarding(_ realClass: AnyClass, _ selectorCache: Sel } } - let method = class_getInstanceMethod(perceivedClass, selector)! - let typeEncoding = method_getTypeEncoding(method) + let method = class_getInstanceMethod(perceivedClass, selector) + let typeEncoding: String + + if let runtimeTypeEncoding = method.flatMap(method_getTypeEncoding) { + typeEncoding = String(cString: runtimeTypeEncoding) + } else { + let methodSignature = (objectRef.takeUnretainedValue() as AnyObject) + .methodSignature(for: selector) + let encodings = (0 ..< methodSignature.numberOfArguments!) + .map { UInt8(methodSignature.getArgumentType(at: $0).pointee) } + typeEncoding = String(bytes: encodings, encoding: .ascii)! + } if class_respondsToSelector(realClass, interopAlias) { // RAC has preserved an immediate implementation found in the runtime @@ -205,7 +215,7 @@ private func enableMessageForwarding(_ realClass: AnyClass, _ selectorCache: Sel return } - let impl = method_getImplementation(method) + let impl = method.map(method_getImplementation) ?? _rac_objc_msgForward if impl != _rac_objc_msgForward { // The perceived class, or its ancestors, responds to the selector. // diff --git a/ReactiveCocoa/ObjC+Messages.swift b/ReactiveCocoa/ObjC+Messages.swift index c0c76a7a3f..6efad7f813 100644 --- a/ReactiveCocoa/ObjC+Messages.swift +++ b/ReactiveCocoa/ObjC+Messages.swift @@ -11,9 +11,12 @@ internal let NSMethodSignature: AnyClass = NSClassFromString("NSMethodSignature" // Signatures defined in `@objc` protocols would be available for ObjC message // sending via `AnyObject`. @objc internal protocol ObjCClassReporting { -// An alias for `-class`, which is unavailable in Swift. + // An alias for `-class`, which is unavailable in Swift. @objc(class) var objcClass: AnyClass! { get } + + @objc(methodSignatureForSelector:) + func methodSignature(for selector: Selector) -> AnyObject } // Methods of `NSInvocation`. diff --git a/ReactiveCocoaTests/InterceptingSpec.swift b/ReactiveCocoaTests/InterceptingSpec.swift index bab40c1246..5d94567e11 100644 --- a/ReactiveCocoaTests/InterceptingSpec.swift +++ b/ReactiveCocoaTests/InterceptingSpec.swift @@ -852,6 +852,25 @@ class InterceptingSpec: QuickSpec { } } } + + describe("classes utilising the message forwarding mechanism") { + it("should receive the message without needing to implement it in the runtime") { + let entity = MessageForwardingEntity() + expect(entity.hasInvoked) == false + + var latestValue: Bool? + entity.reactive + .signal(for: #selector(setter: entity.hasInvoked)) + .observeValues { latestValue = $0[0] as? Bool } + + expect(entity.hasInvoked) == false + expect(latestValue).to(beNil()) + + entity.perform(Selector(("_rac_test_forwarding"))) + expect(entity.hasInvoked) == true + expect(latestValue) == true + } + } } } diff --git a/ReactiveCocoaTests/MessageForwardingEntity.h b/ReactiveCocoaTests/MessageForwardingEntity.h new file mode 100644 index 0000000000..f807fa6821 --- /dev/null +++ b/ReactiveCocoaTests/MessageForwardingEntity.h @@ -0,0 +1,5 @@ +#import + +@interface MessageForwardingEntity : NSObject +@property(nonatomic) BOOL hasInvoked; +@end diff --git a/ReactiveCocoaTests/MessageForwardingEntity.m b/ReactiveCocoaTests/MessageForwardingEntity.m new file mode 100644 index 0000000000..261c779d5f --- /dev/null +++ b/ReactiveCocoaTests/MessageForwardingEntity.m @@ -0,0 +1,35 @@ +#import "MessageForwardingEntity.h" +#pragma GCC diagnostic ignored "-Wundeclared-selector" + +@implementation MessageForwardingEntity + +- (instancetype) init { + if (self = [super init]) { + self.hasInvoked = NO; + } + return self; +} + +- (BOOL) respondsToSelector:(SEL)aSelector { + if (aSelector == @selector(_rac_test_forwarding)) { + return YES; + } + return [super respondsToSelector:aSelector]; +} + +- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { + if (aSelector == @selector(_rac_test_forwarding)) { + return [NSMethodSignature signatureWithObjCTypes:"v@:"]; + } + return [super methodSignatureForSelector:aSelector]; +} + +- (void)forwardInvocation:(NSInvocation *)anInvocation { + if (anInvocation.selector == @selector(_rac_test_forwarding)) { + [self setHasInvoked:YES]; + return; + } + return [super forwardInvocation:anInvocation]; +} + +@end diff --git a/ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h b/ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h new file mode 100644 index 0000000000..d8af552026 --- /dev/null +++ b/ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h @@ -0,0 +1 @@ +#import "MessageForwardingEntity.h" From 44a363d0b7f1ac867878fc62f9cabf6aba9df500 Mon Sep 17 00:00:00 2001 From: Ellen Teapot Date: Wed, 21 Mar 2018 15:17:56 -0700 Subject: [PATCH 0902/1028] Add reactive extensions for all UINavigationItem properties --- ReactiveCocoa/UIKit/UINavigationItem.swift | 75 ++++++++++++++++++++-- 1 file changed, 68 insertions(+), 7 deletions(-) diff --git a/ReactiveCocoa/UIKit/UINavigationItem.swift b/ReactiveCocoa/UIKit/UINavigationItem.swift index 3db6460624..78e295c645 100644 --- a/ReactiveCocoa/UIKit/UINavigationItem.swift +++ b/ReactiveCocoa/UIKit/UINavigationItem.swift @@ -6,11 +6,72 @@ extension Reactive where Base: UINavigationItem { public var title: BindingTarget { return makeBindingTarget { $0.title = $1 } } - - #if os(iOS) - /// Sets the prompt of the navigation item. - public var prompt: BindingTarget { - return makeBindingTarget { $0.prompt = $1 } - } - #endif + + /// Sets the title view of the navigation item. + public var titleView: BindingTarget { + return makeBindingTarget { $0.titleView = $1 } + } + +#if os(iOS) + /// Sets the prompt of the navigation item. + public var prompt: BindingTarget { + return makeBindingTarget { $0.prompt = $1 } + } + + /// Sets the back button item of the navigation item. + public var backBarButtonItem: BindingTarget { + return makeBindingTarget { $0.backBarButtonItem = $1 } + } + + /// Sets the `hidesBackButton` property of the navigation item. + public var hidesBackButton: BindingTarget { + return makeBindingTarget { $0.hidesBackButton = $1 } + } +#endif + + /// Sets the left bar button items of the navigation item. + public var leftBarButtonItems: BindingTarget<[UIBarButtonItem]?> { + return makeBindingTarget { $0.leftBarButtonItems = $1 } + } + + /// Sets the right bar button items of the navigation item. + public var rightBarButtonItems: BindingTarget<[UIBarButtonItem]?> { + return makeBindingTarget { $0.rightBarButtonItems = $1 } + } + + /// Sets the left bar button item of the navigation item. + public var leftBarButtonItem: BindingTarget { + return makeBindingTarget { $0.leftBarButtonItem = $1 } + } + + /// Sets the right bar button item of the navigation item. + public var rightBarButtonItem: BindingTarget { + return makeBindingTarget { $0.rightBarButtonItem = $1 } + } + +#if os(iOS) + /// Sets the `leftItemsSupplementBackButton` property of the navigation item. + @available(iOS 5.0, *) + public var leftItemsSupplementBackButton: BindingTarget { + return makeBindingTarget { $0.leftItemsSupplementBackButton = $1 } + } + + /// Sets the large title display mode of the navigation item. + @available(iOS 11.0, *) + public var largeTitleDisplayMode: BindingTarget { + return makeBindingTarget { $0.largeTitleDisplayMode = $1 } + } + + /// Sets the search controller of the navigation item. + @available(iOS 11.0, *) + public var searchController: BindingTarget { + return makeBindingTarget { $0.searchController = $1 } + } + + /// Sets the `hidesSearchBarWhenScrolling` property of the navigation item. + @available(iOS 11.0, *) + public var hidesSearchBarWhenScrolling: BindingTarget { + return makeBindingTarget { $0.hidesSearchBarWhenScrolling = $1 } + } +#endif } From 6a59299e690b409520b492bd0ebb84ce14de3f47 Mon Sep 17 00:00:00 2001 From: Ellen Teapot Date: Wed, 21 Mar 2018 15:19:29 -0700 Subject: [PATCH 0903/1028] Add CHANGELOG.md entry --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a90d6480a5..ea5aec2483 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ 1. `NotificationCenter.reactive.keyboard(_:)` for system keyboard notification by the event types. (#3566, kudos to @ra1028) +1. Add extensions for several properties on `UINavigationItem`. + # 7.1.0 # 7.1.0-rc.2 1. Fix an issue preventing ReactiveCocoa from being built with the Swift 3.2 language mode. (#3556) From 72dd2096c04e66bb10a5dd0c230c7a7ceef467e9 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Fri, 30 Mar 2018 14:07:16 +0100 Subject: [PATCH 0904/1028] Append `objc` prefixes to all ObjC messaging placeholders. (#3580) * Append `objc` prefixes to all ObjC messaging placeholders. * Fixed a few call sites that weren't using the prefixed placeholders. --- CHANGELOG.md | 2 ++ ReactiveCocoa/NSObject+Intercepting.swift | 22 +++++++++++----------- ReactiveCocoa/ObjC+Messages.swift | 18 ++++++++++-------- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a90d6480a5..7c36c28d87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # master *Please put new entries at the top. +1. Fixed a compilation issue related to [SR-7299](https://bugs.swift.org/browse/SR-7299). (#3580) + 1. Improved the interoperability of method interception. (#3570, kudos to @andersio) 1. Add `showsCancelButton`, `textDidBeginEditing` and `textDidEndEditing` extensions to `UISearchBar` (#3566) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 688cb951c8..a2560e3e99 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -102,7 +102,7 @@ extension NSObject { selectorCache.cache(selector) if signatureCache[selector] == nil { - let signature = NSMethodSignature.signature(withObjCTypes: typeEncoding) + let signature = NSMethodSignature.objcSignature(withObjCTypes: typeEncoding) signatureCache[selector] = signature } @@ -162,9 +162,9 @@ private func enableMessageForwarding(_ realClass: AnyClass, _ selectorCache: Sel typeEncoding = String(cString: runtimeTypeEncoding) } else { let methodSignature = (objectRef.takeUnretainedValue() as AnyObject) - .methodSignature(for: selector) - let encodings = (0 ..< methodSignature.numberOfArguments!) - .map { UInt8(methodSignature.getArgumentType(at: $0).pointee) } + .objcMethodSignature(for: selector) + let encodings = (0 ..< methodSignature.objcNumberOfArguments!) + .map { UInt8(methodSignature.objcArgumentType(at: $0).pointee) } typeEncoding = String(bytes: encodings, encoding: .ascii)! } @@ -193,7 +193,7 @@ private func enableMessageForwarding(_ realClass: AnyClass, _ selectorCache: Sel let interopImpl = class_getMethodImplementation(topLevelClass, interopAlias)! let previousImpl = class_replaceMethod(topLevelClass, selector, interopImpl, typeEncoding) - invocation.invoke() + invocation.objcInvoke() _ = class_replaceMethod(topLevelClass, selector, previousImpl ?? noImplementation, typeEncoding) } @@ -229,8 +229,8 @@ private func enableMessageForwarding(_ realClass: AnyClass, _ selectorCache: Sel _ = class_replaceMethod(realClass, alias, impl, typeEncoding) } - invocation.setSelector(alias) - invocation.invoke() + invocation.objcSetSelector(alias) + invocation.objcInvoke() return } @@ -396,14 +396,14 @@ private func checkTypeEncoding(_ types: UnsafePointer) -> Bool { private func unpackInvocation(_ invocation: AnyObject) -> [Any?] { let invocation = invocation as AnyObject let methodSignature = invocation.objcMethodSignature! - let count = UInt(methodSignature.numberOfArguments!) + let count = methodSignature.objcNumberOfArguments! var bridged = [Any?]() bridged.reserveCapacity(Int(count - 2)) // Ignore `self` and `_cmd` at index 0 and 1. for position in 2 ..< count { - let rawEncoding = methodSignature.argumentType(at: position) + let rawEncoding = methodSignature.objcArgumentType(at: position) let encoding = ObjCTypeEncoding(rawValue: rawEncoding.pointee) ?? .undefined func extract(_ type: U.Type) -> U { @@ -414,7 +414,7 @@ private func unpackInvocation(_ invocation: AnyObject) -> [Any?] { alignedTo: MemoryLayout.alignment) } - invocation.copy(to: pointer, forArgumentAt: Int(position)) + invocation.objcCopy(to: pointer, forArgumentAt: Int(position)) return pointer.assumingMemoryBound(to: type).pointee } @@ -459,7 +459,7 @@ private func unpackInvocation(_ invocation: AnyObject) -> [Any?] { let buffer = UnsafeMutableRawPointer.allocate(bytes: size, alignedTo: alignment) defer { buffer.deallocate(bytes: size, alignedTo: alignment) } - invocation.copy(to: buffer, forArgumentAt: Int(position)) + invocation.objcCopy(to: buffer, forArgumentAt: Int(position)) value = NSValue(bytes: buffer, objCType: rawEncoding) } diff --git a/ReactiveCocoa/ObjC+Messages.swift b/ReactiveCocoa/ObjC+Messages.swift index 6efad7f813..d494a45883 100644 --- a/ReactiveCocoa/ObjC+Messages.swift +++ b/ReactiveCocoa/ObjC+Messages.swift @@ -16,33 +16,35 @@ internal let NSMethodSignature: AnyClass = NSClassFromString("NSMethodSignature" var objcClass: AnyClass! { get } @objc(methodSignatureForSelector:) - func methodSignature(for selector: Selector) -> AnyObject + func objcMethodSignature(for selector: Selector) -> AnyObject } // Methods of `NSInvocation`. @objc internal protocol ObjCInvocation { @objc(setSelector:) - func setSelector(_ selector: Selector) + func objcSetSelector(_ selector: Selector) @objc(methodSignature) var objcMethodSignature: AnyObject { get } @objc(getArgument:atIndex:) - func copy(to buffer: UnsafeMutableRawPointer?, forArgumentAt index: Int) + func objcCopy(to buffer: UnsafeMutableRawPointer?, forArgumentAt index: Int) - func invoke() + @objc(invoke) + func objcInvoke() @objc(invocationWithMethodSignature:) - static func invocation(withMethodSignature signature: AnyObject) -> AnyObject + static func objcInvocation(withMethodSignature signature: AnyObject) -> AnyObject } // Methods of `NSMethodSignature`. @objc internal protocol ObjCMethodSignature { - var numberOfArguments: UInt { get } + @objc(numberOfArguments) + var objcNumberOfArguments: UInt { get } @objc(getArgumentTypeAtIndex:) - func argumentType(at index: UInt) -> UnsafePointer + func objcArgumentType(at index: UInt) -> UnsafePointer @objc(signatureWithObjCTypes:) - static func signature(withObjCTypes typeEncoding: UnsafePointer) -> AnyObject + static func objcSignature(withObjCTypes typeEncoding: UnsafePointer) -> AnyObject } From ec0c85152850024ad94cd4b7af0342a4f37566bc Mon Sep 17 00:00:00 2001 From: Ellen Teapot Date: Mon, 2 Apr 2018 10:04:22 -0700 Subject: [PATCH 0905/1028] Add spec cases for new UINavigationItem extensions --- .../UIKit/UINavigationItemSpec.swift | 253 ++++++++++++++++-- 1 file changed, 237 insertions(+), 16 deletions(-) diff --git a/ReactiveCocoaTests/UIKit/UINavigationItemSpec.swift b/ReactiveCocoaTests/UIKit/UINavigationItemSpec.swift index e9e5127427..b9f0b73a90 100644 --- a/ReactiveCocoaTests/UIKit/UINavigationItemSpec.swift +++ b/ReactiveCocoaTests/UIKit/UINavigationItemSpec.swift @@ -38,26 +38,247 @@ class UINavigationItemSpec: QuickSpec { observer.send(value: nil) expect(navigationItem.title).to(beNil()) } + + it("should accept changes from bindings to its titleView value") { + let firstChange = UIView() + firstChange.accessibilityIdentifier = "first" + + let secondChange = UIView() + secondChange.accessibilityIdentifier = "second" + + navigationItem.titleView = nil + + let (pipeSignal, observer) = Signal.pipe() + navigationItem.reactive.titleView <~ pipeSignal + + observer.send(value: firstChange) + expect(navigationItem.titleView) == firstChange + + observer.send(value: secondChange) + expect(navigationItem.titleView) == secondChange + + observer.send(value: nil) + expect(navigationItem.titleView).to(beNil()) + } - #if os(iOS) - it("should accept changes from bindings to its prompt value") { - let firstChange = "first" - let secondChange = "second" - - navigationItem.prompt = "" - - let (pipeSignal, observer) = Signal.pipe() - navigationItem.reactive.prompt <~ pipeSignal - +#if os(iOS) + it("should accept changes from bindings to its prompt value") { + let firstChange = "first" + let secondChange = "second" + + navigationItem.prompt = "" + + let (pipeSignal, observer) = Signal.pipe() + navigationItem.reactive.prompt <~ pipeSignal + + observer.send(value: firstChange) + expect(navigationItem.prompt) == firstChange + + observer.send(value: secondChange) + expect(navigationItem.prompt) == secondChange + + observer.send(value: nil) + expect(navigationItem.prompt).to(beNil()) + } + + it("should accept changes from bindings to its backBarButtonItem value") { + let firstChange = UIBarButtonItem(title: "first", style: .plain, target: nil, action: nil) + let secondChange = UIBarButtonItem(title: "second", style: .plain, target: nil, action: nil) + + navigationItem.backBarButtonItem = nil + + let (pipeSignal, observer) = Signal.pipe() + navigationItem.reactive.backBarButtonItem <~ pipeSignal + + observer.send(value: firstChange) + expect(navigationItem.backBarButtonItem) == firstChange + + observer.send(value: secondChange) + expect(navigationItem.backBarButtonItem) == secondChange + + observer.send(value: nil) + expect(navigationItem.backBarButtonItem).to(beNil()) + } + + it("should accept changes from bindings to its hidesBackButton value") { + let firstChange = true + let secondChange = false + + navigationItem.hidesBackButton = false + + let (pipeSignal, observer) = Signal.pipe() + navigationItem.reactive.hidesBackButton <~ pipeSignal + + observer.send(value: firstChange) + expect(navigationItem.hidesBackButton) == firstChange + + observer.send(value: secondChange) + expect(navigationItem.hidesBackButton) == secondChange + } +#endif + + it("should accept changes from bindings to its leftBarButtonItems value") { + let firstChange = [ + UIBarButtonItem(barButtonSystemItem: .action, target: nil, action: nil), + UIBarButtonItem(title: "first", style: .plain, target: nil, action: nil) + ] + + let secondChange = [ + UIBarButtonItem(barButtonSystemItem: .action, target: nil, action: nil), + UIBarButtonItem(title: "second", style: .plain, target: nil, action: nil) + ] + + navigationItem.leftBarButtonItems = nil + + let (pipeSignal, observer) = Signal<[UIBarButtonItem]?, NoError>.pipe() + navigationItem.reactive.leftBarButtonItems <~ pipeSignal + + observer.send(value: firstChange) + expect(navigationItem.leftBarButtonItems) == firstChange + + observer.send(value: secondChange) + expect(navigationItem.leftBarButtonItems) == secondChange + + observer.send(value: nil) + expect(navigationItem.leftBarButtonItems).to(beNil()) + } + + it("should accept changes from bindings to its rightBarButtonItems value") { + let firstChange = [ + UIBarButtonItem(barButtonSystemItem: .action, target: nil, action: nil), + UIBarButtonItem(title: "first", style: .plain, target: nil, action: nil) + ] + + let secondChange = [ + UIBarButtonItem(barButtonSystemItem: .action, target: nil, action: nil), + UIBarButtonItem(title: "second", style: .plain, target: nil, action: nil) + ] + + navigationItem.rightBarButtonItems = nil + + let (pipeSignal, observer) = Signal<[UIBarButtonItem]?, NoError>.pipe() + navigationItem.reactive.rightBarButtonItems <~ pipeSignal + + observer.send(value: firstChange) + expect(navigationItem.rightBarButtonItems) == firstChange + + observer.send(value: secondChange) + expect(navigationItem.rightBarButtonItems) == secondChange + + observer.send(value: nil) + expect(navigationItem.rightBarButtonItems).to(beNil()) + } + + it("should accept changes from bindings to its leftBarButtonItem value") { + let firstChange = UIBarButtonItem(title: "first", style: .plain, target: nil, action: nil) + let secondChange = UIBarButtonItem(barButtonSystemItem: .action, target: nil, action: nil) + + navigationItem.leftBarButtonItem = nil + + let (pipeSignal, observer) = Signal.pipe() + navigationItem.reactive.leftBarButtonItem <~ pipeSignal + + observer.send(value: firstChange) + expect(navigationItem.leftBarButtonItem) == firstChange + + observer.send(value: secondChange) + expect(navigationItem.leftBarButtonItem) == secondChange + + observer.send(value: nil) + expect(navigationItem.leftBarButtonItem).to(beNil()) + } + + it("should accept changes from bindings to its rightBarButtonItem value") { + let firstChange = UIBarButtonItem(title: "first", style: .plain, target: nil, action: nil) + let secondChange = UIBarButtonItem(title: "second", style: .plain, target: nil, action: nil) + + navigationItem.rightBarButtonItem = nil + + let (pipeSignal, observer) = Signal.pipe() + navigationItem.reactive.rightBarButtonItem <~ pipeSignal + + observer.send(value: firstChange) + expect(navigationItem.rightBarButtonItem) == firstChange + + observer.send(value: secondChange) + expect(navigationItem.rightBarButtonItem) == secondChange + + observer.send(value: nil) + expect(navigationItem.rightBarButtonItem).to(beNil()) + } + +#if os(iOS) + it("should accept changes from bindings to its leftItemsSupplementBackButton value") { + let firstChange = true + let secondChange = false + + navigationItem.leftItemsSupplementBackButton = false + + let (pipeSignal, observer) = Signal.pipe() + navigationItem.reactive.leftItemsSupplementBackButton <~ pipeSignal + + observer.send(value: firstChange) + expect(navigationItem.leftItemsSupplementBackButton) == firstChange + + observer.send(value: secondChange) + expect(navigationItem.leftItemsSupplementBackButton) == secondChange + } + + if #available(iOS 11.0, *) { + it("should accept changes from bindings to its largeTitleDisplayMode value") { + let firstChange = UINavigationItem.LargeTitleDisplayMode.always + let secondChange = UINavigationItem.LargeTitleDisplayMode.automatic + + navigationItem.largeTitleDisplayMode = .automatic + + let (pipeSignal, observer) = Signal.pipe() + navigationItem.reactive.largeTitleDisplayMode <~ pipeSignal + + observer.send(value: firstChange) + expect(navigationItem.largeTitleDisplayMode) == firstChange + + observer.send(value: secondChange) + expect(navigationItem.largeTitleDisplayMode) == secondChange + } + + it("should accept changes from bindings to its searchController value") { + let firstChange = UISearchController() + firstChange.view.accessibilityIdentifier = "firstChange" + + let secondChange = UISearchController() + secondChange.view.accessibilityIdentifier = "firstChange" + + navigationItem.searchController = nil + + let (pipeSignal, observer) = Signal.pipe() + navigationItem.reactive.searchController <~ pipeSignal + observer.send(value: firstChange) - expect(navigationItem.prompt) == firstChange - + expect(navigationItem.searchController) == firstChange + observer.send(value: secondChange) - expect(navigationItem.prompt) == secondChange - + expect(navigationItem.searchController) == secondChange + observer.send(value: nil) - expect(navigationItem.prompt).to(beNil()) + expect(navigationItem.searchController).to(beNil()) + } + + it("should accept changes from bindings to its hidesSearchBarWhenScrolling value") { + let firstChange = true + let secondChange = false + + navigationItem.hidesSearchBarWhenScrolling = false + + let (pipeSignal, observer) = Signal.pipe() + navigationItem.reactive.hidesSearchBarWhenScrolling <~ pipeSignal + + observer.send(value: firstChange) + expect(navigationItem.hidesSearchBarWhenScrolling) == firstChange + + observer.send(value: secondChange) + expect(navigationItem.hidesSearchBarWhenScrolling) == secondChange } - #endif + } +#endif } } From 498c9546535808a6fd977919c4e464cb017cbdcb Mon Sep 17 00:00:00 2001 From: Ellen Teapot Date: Mon, 2 Apr 2018 10:05:02 -0700 Subject: [PATCH 0906/1028] sign my work --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea5aec2483..f94ebcacff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ 1. `NotificationCenter.reactive.keyboard(_:)` for system keyboard notification by the event types. (#3566, kudos to @ra1028) -1. Add extensions for several properties on `UINavigationItem`. +1. Add extensions for several properties on `UINavigationItem` (#3576, kudos to @asmallteapot). # 7.1.0 # 7.1.0-rc.2 From 30ee38bcc0ddf6a9d3e63d86af0529be0809b391 Mon Sep 17 00:00:00 2001 From: Zheng Li Date: Sat, 7 Apr 2018 03:24:37 +0800 Subject: [PATCH 0907/1028] Avoid flag override by adding `$(inherited)` to `OTHER_SWIFT_FLAGS[config=Release]`. (#3581) --- ReactiveCocoa.podspec | 2 +- ReactiveMapKit.podspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index ed8ef79927..2ef619f8ae 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -25,5 +25,5 @@ Pod::Spec.new do |s| s.dependency 'ReactiveSwift', '~> 3.1' - s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "-suppress-warnings" } + s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "$(inherited) -suppress-warnings" } end diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index 509ec8717b..72e91b2110 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -19,5 +19,5 @@ Pod::Spec.new do |s| s.dependency 'ReactiveCocoa', "#{s.version}" - s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "-suppress-warnings" } + s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "$(inherited) -suppress-warnings" } end From 526fa93e1ef79255d0849ee7872965404e82d0f5 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sun, 8 Apr 2018 11:11:35 +0100 Subject: [PATCH 0908/1028] Mitigate a regression in type inference. (#3583) --- ReactiveCocoa/NSObject+Intercepting.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index a2560e3e99..fb55375674 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -215,7 +215,7 @@ private func enableMessageForwarding(_ realClass: AnyClass, _ selectorCache: Sel return } - let impl = method.map(method_getImplementation) ?? _rac_objc_msgForward + let impl: IMP = method.map(method_getImplementation) ?? _rac_objc_msgForward if impl != _rac_objc_msgForward { // The perceived class, or its ancestors, responds to the selector. // From b6802382015aeeeb4fe1aa9d88a2c46771072587 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sun, 8 Apr 2018 13:47:54 +0100 Subject: [PATCH 0909/1028] 7.2.0 --- CHANGELOG.md | 1 + ReactiveCocoa.podspec | 2 +- ReactiveMapKit.podspec | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7bc25ed320..554880e495 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master *Please put new entries at the top. +# 7.2.0 1. Fixed a compilation issue related to [SR-7299](https://bugs.swift.org/browse/SR-7299). (#3580) 1. Improved the interoperability of method interception. (#3570, kudos to @andersio) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 2ef619f8ae..bb730e7e59 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "7.1.0" + s.version = "7.2.0" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index 72e91b2110..cecf759c7e 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveMapKit" - s.version = "7.1.0" + s.version = "7.2.0" s.summary = "MapKit bindings for ReactiveCocoa." s.description = <<-DESC Provide MapKit bindings for ReactiveCocoa. ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. From 0d414bd826df18fda96d93c94b79858a559365a3 Mon Sep 17 00:00:00 2001 From: BAN Jun Date: Mon, 9 Apr 2018 14:15:23 +0900 Subject: [PATCH 0910/1028] correct CHANGELOG refs at 7.2.0 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 554880e495..eecd5b9c20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ 1. Improved the interoperability of method interception. (#3570, kudos to @andersio) -1. Add `showsCancelButton`, `textDidBeginEditing` and `textDidEndEditing` extensions to `UISearchBar` (#3566) +1. Add `showsCancelButton`, `textDidBeginEditing` and `textDidEndEditing` extensions to `UISearchBar` (#3565, kudos to @banjun) 1. `NotificationCenter.reactive.keyboard(_:)` for system keyboard notification by the event types. (#3566, kudos to @ra1028) From 570dad3cfda69e446ed5b61b62c6005857c56bd1 Mon Sep 17 00:00:00 2001 From: Marco Cancellieri Date: Tue, 10 Apr 2018 13:42:14 +0200 Subject: [PATCH 0911/1028] Add UIResponder extension --- ReactiveCocoa.xcodeproj/project.pbxproj | 12 +++++++++++ ReactiveCocoa/UIKit/UIResponder.swift | 14 +++++++++++++ .../UIKit/UIResponderSpec.swift | 21 +++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 ReactiveCocoa/UIKit/UIResponder.swift create mode 100644 ReactiveCocoaTests/UIKit/UIResponderSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index e71f290ef0..06704c1f8c 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -255,6 +255,10 @@ A91244E820389AEA0001BBCB /* MKLocalSearchRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91244E720389AEA0001BBCB /* MKLocalSearchRequest.swift */; }; A9B315C91B3940980001CB9C /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; A9B315CA1B3940AB0001CB9C /* ReactiveCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9D8BA71207CD7090031733D /* UIResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D8BA70207CD7090031733D /* UIResponder.swift */; }; + A9D8BA72207CD7090031733D /* UIResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D8BA70207CD7090031733D /* UIResponder.swift */; }; + A9D8BA74207CD8430031733D /* UIResponderSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D8BA73207CD8430031733D /* UIResponderSpec.swift */; }; + A9D8BA75207CD8430031733D /* UIResponderSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D8BA73207CD8430031733D /* UIResponderSpec.swift */; }; A9EB3D201E94F08A002A9BCC /* UINavigationItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D1C1E94ECAC002A9BCC /* UINavigationItem.swift */; }; A9EB3D211E94F0AF002A9BCC /* UINavigationItemSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D1E1E94ED84002A9BCC /* UINavigationItemSpec.swift */; }; A9EB3D221E94F308002A9BCC /* UINavigationItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D1C1E94ECAC002A9BCC /* UINavigationItem.swift */; }; @@ -513,6 +517,8 @@ A97451351B3A935E00F48E55 /* watchOS-Framework.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-Framework.xcconfig"; sourceTree = ""; }; A97451361B3A935E00F48E55 /* watchOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "watchOS-StaticLibrary.xcconfig"; sourceTree = ""; }; A9B315541B3940610001CB9C /* ReactiveCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + A9D8BA70207CD7090031733D /* UIResponder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIResponder.swift; sourceTree = ""; }; + A9D8BA73207CD8430031733D /* UIResponderSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIResponderSpec.swift; sourceTree = ""; }; A9EB3D1C1E94ECAC002A9BCC /* UINavigationItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UINavigationItem.swift; sourceTree = ""; }; A9EB3D1E1E94ED84002A9BCC /* UINavigationItemSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UINavigationItemSpec.swift; sourceTree = ""; }; A9EB3D241E94F335002A9BCC /* UITabBarItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITabBarItem.swift; sourceTree = ""; }; @@ -738,6 +744,7 @@ 9A1D05FA1D93E9F100ACF44C /* UITextField.swift */, 9A1D05FB1D93E9F100ACF44C /* UITextView.swift */, 9A1D05FC1D93E9F100ACF44C /* UIView.swift */, + A9D8BA70207CD7090031733D /* UIResponder.swift */, ); path = UIKit; sourceTree = ""; @@ -772,6 +779,7 @@ 9A1D06351D93EA7E00ACF44C /* UIViewSpec.swift */, 9AAD49891DED2F380068EC9B /* UIKeyboardSpec.swift */, 3BCAAC7B1DEE1A2300B30335 /* UIRefreshControlSpec.swift */, + A9D8BA73207CD8430031733D /* UIResponderSpec.swift */, ); path = UIKit; sourceTree = ""; @@ -1412,6 +1420,7 @@ 9A1D06191D93EA0100ACF44C /* UILabel.swift in Sources */, 4A0E11021D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, 9ADFE5A81DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */, + A9D8BA72207CD7090031733D /* UIResponder.swift in Sources */, CD0C45E11CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, A9EB3D2D1E94F5A2002A9BCC /* UITabBarItem.swift in Sources */, 9AA0BD921DDE29F800531FCF /* NSObject+ObjCRuntime.swift in Sources */, @@ -1461,6 +1470,7 @@ 9A6AAA101DB6A4CF0013AAEA /* InterceptingSpec.swift in Sources */, 4ABEFE221DCFCF0A0066A8C2 /* UITableViewSpec.swift in Sources */, 9A1D06491D93EA7E00ACF44C /* UIProgressViewSpec.swift in Sources */, + A9D8BA75207CD8430031733D /* UIResponderSpec.swift in Sources */, 4ABEFE291DCFCFA90066A8C2 /* UICollectionViewSpec.swift in Sources */, 9A54A2131DDF5B4D001739B3 /* InterceptingPerformanceTests.swift in Sources */, BEE020661D637B0000DF261F /* TestError.swift in Sources */, @@ -1612,6 +1622,7 @@ 9AA0BD821DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift in Sources */, 9A1D060F1D93EA0000ACF44C /* UIView.swift in Sources */, 9A1D06051D93EA0000ACF44C /* UIDatePicker.swift in Sources */, + A9D8BA71207CD7090031733D /* UIResponder.swift in Sources */, 9A1D05E11D93E99100ACF44C /* NSObject+Association.swift in Sources */, 9A1D06041D93EA0000ACF44C /* UIControl.swift in Sources */, 9A1D06021D93EA0000ACF44C /* UIButton.swift in Sources */, @@ -1673,6 +1684,7 @@ 3B30EE8D1E7BE529007CC8EF /* DeprecationsSpec.swift in Sources */, 9A9DFEEA1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */, 9A54A2121DDF5B4D001739B3 /* InterceptingPerformanceTests.swift in Sources */, + A9D8BA74207CD8430031733D /* UIResponderSpec.swift in Sources */, 9ADFE5A21DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */, 9A1D06421D93EA7E00ACF44C /* UIDatePickerSpec.swift in Sources */, 53AC46CF1DD6FC0000C799E1 /* UISliderSpec.swift in Sources */, diff --git a/ReactiveCocoa/UIKit/UIResponder.swift b/ReactiveCocoa/UIKit/UIResponder.swift new file mode 100644 index 0000000000..f085559e98 --- /dev/null +++ b/ReactiveCocoa/UIKit/UIResponder.swift @@ -0,0 +1,14 @@ +import ReactiveSwift +import UIKit + +extension Reactive where Base: UIResponder { + /// Asks UIKit to make this object the first responder in its window. + public var becomeFirstResponder: BindingTarget<()> { + return makeBindingTarget { base, _ in base.becomeFirstResponder() } + } + + /// Notifies this object that it has been asked to relinquish its status as first responder in its window. + public var resignFirstResponder: BindingTarget<()> { + return makeBindingTarget { base, _ in base.resignFirstResponder() } + } +} diff --git a/ReactiveCocoaTests/UIKit/UIResponderSpec.swift b/ReactiveCocoaTests/UIKit/UIResponderSpec.swift new file mode 100644 index 0000000000..a0ad21b182 --- /dev/null +++ b/ReactiveCocoaTests/UIKit/UIResponderSpec.swift @@ -0,0 +1,21 @@ +import Quick +import Nimble +import ReactiveSwift +import ReactiveCocoa + +class UIResponderSpec: QuickSpec { + override func spec() { + it("should become and resign first responder") { + let window = UIWindow(frame: .zero) + let textField = UITextField(frame: .zero) + window.addSubview(textField) + + expect(textField.isFirstResponder).to(beFalse()) + textField.reactive.becomeFirstResponder <~ SignalProducer(value: ()) + expect(textField.isFirstResponder).to(beTrue()) + textField.reactive.resignFirstResponder <~ SignalProducer(value: ()) + expect(textField.isFirstResponder).to(beFalse()) + + } + } +} From 1e6d70b96c71f3148fb3f8832a68fa1ecb73e09d Mon Sep 17 00:00:00 2001 From: Marco Cancellieri Date: Tue, 10 Apr 2018 13:59:15 +0200 Subject: [PATCH 0912/1028] add changelog --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eecd5b9c20..e29a98d728 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # master *Please put new entries at the top. +1. Add `becomeFirstResponder` and `resignFirstResponder` extensions to `UIResponder` (#3585, kudos to @Marcocanc) + # 7.2.0 1. Fixed a compilation issue related to [SR-7299](https://bugs.swift.org/browse/SR-7299). (#3580) @@ -10,7 +12,9 @@ 1. `NotificationCenter.reactive.keyboard(_:)` for system keyboard notification by the event types. (#3566, kudos to @ra1028) -1. Add extensions for several properties on `UINavigationItem` (#3576, kudos to @asmallteapot). +1. Add extensions for several properties on `UINavigationItem` (#3576, kudos to @asmallteapot) + +1. Add extension for `search` on MKLocalSearchRequest (#3571, kudos to @Marcocanc) # 7.1.0 # 7.1.0-rc.2 From 7625047d9c64b18032608d2bfd3df39eb9617590 Mon Sep 17 00:00:00 2001 From: Marco Cancellieri Date: Tue, 10 Apr 2018 14:01:56 +0200 Subject: [PATCH 0913/1028] remove trailing empty line --- ReactiveCocoaTests/UIKit/UIResponderSpec.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/ReactiveCocoaTests/UIKit/UIResponderSpec.swift b/ReactiveCocoaTests/UIKit/UIResponderSpec.swift index a0ad21b182..830fb5f0ee 100644 --- a/ReactiveCocoaTests/UIKit/UIResponderSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIResponderSpec.swift @@ -15,7 +15,6 @@ class UIResponderSpec: QuickSpec { expect(textField.isFirstResponder).to(beTrue()) textField.reactive.resignFirstResponder <~ SignalProducer(value: ()) expect(textField.isFirstResponder).to(beFalse()) - } } } From e14dc513658de1bc891a45ed7b0247e6076daa6b Mon Sep 17 00:00:00 2001 From: Marco Cancellieri Date: Tue, 10 Apr 2018 14:04:18 +0200 Subject: [PATCH 0914/1028] make punctuation more consistent --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e29a98d728..3b26859abf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,20 +1,20 @@ # master *Please put new entries at the top. -1. Add `becomeFirstResponder` and `resignFirstResponder` extensions to `UIResponder` (#3585, kudos to @Marcocanc) +1. Add `becomeFirstResponder` and `resignFirstResponder` extensions to `UIResponder`. (#3585, kudos to @Marcocanc) # 7.2.0 1. Fixed a compilation issue related to [SR-7299](https://bugs.swift.org/browse/SR-7299). (#3580) 1. Improved the interoperability of method interception. (#3570, kudos to @andersio) -1. Add `showsCancelButton`, `textDidBeginEditing` and `textDidEndEditing` extensions to `UISearchBar` (#3565, kudos to @banjun) +1. Add `showsCancelButton`, `textDidBeginEditing` and `textDidEndEditing` extensions to `UISearchBar`. (#3565, kudos to @banjun) 1. `NotificationCenter.reactive.keyboard(_:)` for system keyboard notification by the event types. (#3566, kudos to @ra1028) -1. Add extensions for several properties on `UINavigationItem` (#3576, kudos to @asmallteapot) +1. Add extensions for several properties on `UINavigationItem`. (#3576, kudos to @asmallteapot) -1. Add extension for `search` on MKLocalSearchRequest (#3571, kudos to @Marcocanc) +1. Add extension for `search` on MKLocalSearchRequest. (#3571, kudos to @Marcocanc) # 7.1.0 # 7.1.0-rc.2 From 4e416191cf7b279edae1f74763c1ec5a188741f9 Mon Sep 17 00:00:00 2001 From: Jonathan Baker Date: Wed, 25 Apr 2018 13:31:50 -0400 Subject: [PATCH 0915/1028] Add UIViewController.title BindingTarget. --- ReactiveCocoa.xcodeproj/project.pbxproj | 12 ++++++ ReactiveCocoa/UIKit/UIViewController.swift | 9 ++++ .../UIKit/UIViewControllerSpec.swift | 42 +++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 ReactiveCocoa/UIKit/UIViewController.swift create mode 100644 ReactiveCocoaTests/UIKit/UIViewControllerSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index e71f290ef0..65a43d5fd9 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -33,6 +33,10 @@ 4ABEFE2E1DCFD01F0066A8C2 /* NSTableViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE2C1DCFD0180066A8C2 /* NSTableViewSpec.swift */; }; 4ABEFE301DCFD0530066A8C2 /* NSCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE2F1DCFD0530066A8C2 /* NSCollectionView.swift */; }; 4ABEFE331DCFD0630066A8C2 /* NSCollectionViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE311DCFD05F0066A8C2 /* NSCollectionViewSpec.swift */; }; + 4EE6372E2090EEFA00ECD02A /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE6372D2090EEFA00ECD02A /* UIViewController.swift */; }; + 4EE6372F2090EEFA00ECD02A /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE6372D2090EEFA00ECD02A /* UIViewController.swift */; }; + 4EE637332090EFDD00ECD02A /* UIViewControllerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE637302090EFD300ECD02A /* UIViewControllerSpec.swift */; }; + 4EE637342090EFDF00ECD02A /* UIViewControllerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE637302090EFD300ECD02A /* UIViewControllerSpec.swift */; }; 531866F81DD7920400D1285F /* UIStepper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531866F71DD7920400D1285F /* UIStepper.swift */; }; 531866FA1DD7925600D1285F /* UIStepperSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531866F91DD7925600D1285F /* UIStepperSpec.swift */; }; 538DCB791DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */; }; @@ -408,6 +412,8 @@ 4ABEFE2C1DCFD0180066A8C2 /* NSTableViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSTableViewSpec.swift; sourceTree = ""; }; 4ABEFE2F1DCFD0530066A8C2 /* NSCollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSCollectionView.swift; sourceTree = ""; }; 4ABEFE311DCFD05F0066A8C2 /* NSCollectionViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSCollectionViewSpec.swift; sourceTree = ""; }; + 4EE6372D2090EEFA00ECD02A /* UIViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = ""; }; + 4EE637302090EFD300ECD02A /* UIViewControllerSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewControllerSpec.swift; sourceTree = ""; }; 531866F71DD7920400D1285F /* UIStepper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIStepper.swift; sourceTree = ""; }; 531866F91DD7925600D1285F /* UIStepperSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIStepperSpec.swift; sourceTree = ""; }; 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSLayoutConstraint.swift; sourceTree = ""; }; @@ -738,6 +744,7 @@ 9A1D05FA1D93E9F100ACF44C /* UITextField.swift */, 9A1D05FB1D93E9F100ACF44C /* UITextView.swift */, 9A1D05FC1D93E9F100ACF44C /* UIView.swift */, + 4EE6372D2090EEFA00ECD02A /* UIViewController.swift */, ); path = UIKit; sourceTree = ""; @@ -770,6 +777,7 @@ 9A1D06321D93EA7E00ACF44C /* UITextFieldSpec.swift */, 9A1D06331D93EA7E00ACF44C /* UITextViewSpec.swift */, 9A1D06351D93EA7E00ACF44C /* UIViewSpec.swift */, + 4EE637302090EFD300ECD02A /* UIViewControllerSpec.swift */, 9AAD49891DED2F380068EC9B /* UIKeyboardSpec.swift */, 3BCAAC7B1DEE1A2300B30335 /* UIRefreshControlSpec.swift */, ); @@ -1398,6 +1406,7 @@ 9A1D06181D93EA0100ACF44C /* UIImageView.swift in Sources */, 9AA0BD841DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift in Sources */, 9AD0F06D1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, + 4EE6372F2090EEFA00ECD02A /* UIViewController.swift in Sources */, 9A1D06211D93EA0100ACF44C /* UIView.swift in Sources */, 538DCB7B1DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */, 9AA0BD9B1DDE7A2200531FCF /* ObjCRuntimeAliases.m in Sources */, @@ -1447,6 +1456,7 @@ CD42C69D1E951F6A00AA9504 /* ReactiveCocoaTestsConfiguration.swift in Sources */, 9A1D063F1D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift in Sources */, 9A1D06371D93EA7E00ACF44C /* UIActivityIndicatorViewSpec.swift in Sources */, + 4EE637332090EFDD00ECD02A /* UIViewControllerSpec.swift in Sources */, 9A6AAA2F1DB903A40013AAEA /* ReusableComponentsSpec.swift in Sources */, 538DCB7F1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift in Sources */, 9A892D911E8D19BE00EA35F3 /* DelegateProxySpec.swift in Sources */, @@ -1615,6 +1625,7 @@ 9A1D05E11D93E99100ACF44C /* NSObject+Association.swift in Sources */, 9A1D06041D93EA0000ACF44C /* UIControl.swift in Sources */, 9A1D06021D93EA0000ACF44C /* UIButton.swift in Sources */, + 4EE6372E2090EEFA00ECD02A /* UIViewController.swift in Sources */, 9AA0BD901DDE29F800531FCF /* NSObject+ObjCRuntime.swift in Sources */, A9EB3D821E955602002A9BCC /* UIImpact​Feedback​Generator.swift in Sources */, 419139461DB910570043C9D1 /* UIGestureRecognizer.swift in Sources */, @@ -1692,6 +1703,7 @@ A9EB3D291E94F3D3002A9BCC /* UITabBarItemSpec.swift in Sources */, 538DCB7E1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift in Sources */, 9A1D06521D93EA7E00ACF44C /* UITextFieldSpec.swift in Sources */, + 4EE637342090EFDF00ECD02A /* UIViewControllerSpec.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ReactiveCocoa/UIKit/UIViewController.swift b/ReactiveCocoa/UIKit/UIViewController.swift new file mode 100644 index 0000000000..69a8bca306 --- /dev/null +++ b/ReactiveCocoa/UIKit/UIViewController.swift @@ -0,0 +1,9 @@ +import ReactiveSwift +import UIKit + +extension Reactive where Base: UIViewController { + /// Set's the title of the view controller. + public var title: BindingTarget { + return makeBindingTarget({ $0.title = $1 }) + } +} diff --git a/ReactiveCocoaTests/UIKit/UIViewControllerSpec.swift b/ReactiveCocoaTests/UIKit/UIViewControllerSpec.swift new file mode 100644 index 0000000000..067ae68405 --- /dev/null +++ b/ReactiveCocoaTests/UIKit/UIViewControllerSpec.swift @@ -0,0 +1,42 @@ +import ReactiveSwift +import ReactiveCocoa +import UIKit +import Quick +import Nimble +import enum Result.NoError + +class UIViewControllerSpec: QuickSpec { + override func spec() { + var viewController: UIViewController! + weak var _viewController: UIViewController? + + beforeEach { + viewController = UIViewController() + _viewController = viewController + } + + afterEach { + viewController = nil + expect(_viewController).to(beNil()) + } + + it("should accept changes from bindings to its title value") { + let firstChange = "first" + let secondChange = "second" + + viewController.title = "" + + let (pipeSignal, observer) = Signal.pipe() + viewController.reactive.title <~ pipeSignal + + observer.send(value: firstChange) + expect(viewController.title) == firstChange + + observer.send(value: secondChange) + expect(viewController.title) == secondChange + + observer.send(value: nil) + expect(viewController.title).to(beNil()) + } + } +} From a074e4b0d19cbded71b30dbffcb8947b7ed8212c Mon Sep 17 00:00:00 2001 From: Jonathan Baker Date: Wed, 25 Apr 2018 13:45:34 -0400 Subject: [PATCH 0916/1028] Add view lifecycle trigger signal extensions. --- ReactiveCocoa/UIKit/UIViewController.swift | 31 ++++++++ .../UIKit/UIViewControllerSpec.swift | 72 +++++++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/ReactiveCocoa/UIKit/UIViewController.swift b/ReactiveCocoa/UIKit/UIViewController.swift index 69a8bca306..0b94554a1f 100644 --- a/ReactiveCocoa/UIKit/UIViewController.swift +++ b/ReactiveCocoa/UIKit/UIViewController.swift @@ -1,9 +1,40 @@ import ReactiveSwift import UIKit +import enum Result.NoError extension Reactive where Base: UIViewController { /// Set's the title of the view controller. public var title: BindingTarget { return makeBindingTarget({ $0.title = $1 }) } + + /// A signal that sends a value event every time `viewWillAppear` is invoked. + public var viewWillAppear: Signal { + return trigger(for: #selector(Base.viewWillAppear)) + } + + /// A signal that sends a value event every time `viewDidAppear` is invoked. + public var viewDidAppear: Signal { + return trigger(for: #selector(Base.viewDidAppear)) + } + + /// A signal that sends a value event every time `viewWillDisappear` is invoked. + public var viewWillDisappear: Signal { + return trigger(for: #selector(Base.viewWillDisappear)) + } + + /// A signal that sends a value event every time `viewDidDisappear` is invoked. + public var viewDidDisappear: Signal { + return trigger(for: #selector(Base.viewDidDisappear)) + } + + /// A signal that sends a value event every time `viewWillLayoutSubviews` is invoked. + public var viewWillLayoutSubviews: Signal { + return trigger(for: #selector(Base.viewWillLayoutSubviews)) + } + + /// A signal that sends a value event every time `viewDidLayoutSubviews` is invoked. + public var viewDidLayoutSubviews: Signal { + return trigger(for: #selector(Base.viewDidLayoutSubviews)) + } } diff --git a/ReactiveCocoaTests/UIKit/UIViewControllerSpec.swift b/ReactiveCocoaTests/UIKit/UIViewControllerSpec.swift index 067ae68405..0a2a128b04 100644 --- a/ReactiveCocoaTests/UIKit/UIViewControllerSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIViewControllerSpec.swift @@ -38,5 +38,77 @@ class UIViewControllerSpec: QuickSpec { observer.send(value: nil) expect(viewController.title).to(beNil()) } + + it("should send a `value` event when `viewWillAppear` is invoked") { + var isInvoked = false + viewController.reactive.viewWillAppear.observeValues { + isInvoked = true + } + + expect(isInvoked) == false + + viewController.viewWillAppear(false) + expect(isInvoked) == true + } + + it("should send a `value` event when `viewDidAppear` is invoked") { + var isInvoked = false + viewController.reactive.viewDidAppear.observeValues { + isInvoked = true + } + + expect(isInvoked) == false + + viewController.viewDidAppear(false) + expect(isInvoked) == true + } + + it("should send a `value` event when `viewWillDisappear` is invoked") { + var isInvoked = false + viewController.reactive.viewWillDisappear.observeValues { + isInvoked = true + } + + expect(isInvoked) == false + + viewController.viewWillDisappear(false) + expect(isInvoked) == true + } + + it("should send a `value` event when `viewDidDisappear` is invoked") { + var isInvoked = false + viewController.reactive.viewDidDisappear.observeValues { + isInvoked = true + } + + expect(isInvoked) == false + + viewController.viewDidDisappear(false) + expect(isInvoked) == true + } + + it("should send a `value` event when `viewWillLayoutSubviews` is invoked") { + var isInvoked = false + viewController.reactive.viewWillLayoutSubviews.observeValues { + isInvoked = true + } + + expect(isInvoked) == false + + viewController.viewWillLayoutSubviews() + expect(isInvoked) == true + } + + it("should send a `value` event when `viewDidLayoutSubviews` is invoked") { + var isInvoked = false + viewController.reactive.viewDidLayoutSubviews.observeValues { + isInvoked = true + } + + expect(isInvoked) == false + + viewController.viewDidLayoutSubviews() + expect(isInvoked) == true + } } } From 1822523579628552819ee4e4d8ad3e7e89257801 Mon Sep 17 00:00:00 2001 From: Jonathan Baker Date: Wed, 25 Apr 2018 13:54:56 -0400 Subject: [PATCH 0917/1028] Updated CHANGELOG.md. --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eecd5b9c20..cacff963ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # master *Please put new entries at the top. +# 7.X.0 +1. Added `title` binding target to `UIViewController` (#3588, kudos to @cocoahero). +2. Added several trigger signals for view lifecycle events to `UIViewController` (#3588, kudos to @cocoahero). + # 7.2.0 1. Fixed a compilation issue related to [SR-7299](https://bugs.swift.org/browse/SR-7299). (#3580) From 3fee4e6d4f10ce4985e397b0691b0bf6031ac969 Mon Sep 17 00:00:00 2001 From: Jonathan Baker Date: Wed, 25 Apr 2018 14:00:24 -0400 Subject: [PATCH 0918/1028] Add reactive extension for UIApplication.applicationIconBadgeNumber. --- ReactiveCocoa.xcodeproj/project.pbxproj | 6 ++++++ ReactiveCocoa/UIKit/UIApplication.swift | 9 +++++++++ 2 files changed, 15 insertions(+) create mode 100644 ReactiveCocoa/UIKit/UIApplication.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index e71f290ef0..780879cfda 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -33,6 +33,8 @@ 4ABEFE2E1DCFD01F0066A8C2 /* NSTableViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE2C1DCFD0180066A8C2 /* NSTableViewSpec.swift */; }; 4ABEFE301DCFD0530066A8C2 /* NSCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE2F1DCFD0530066A8C2 /* NSCollectionView.swift */; }; 4ABEFE331DCFD0630066A8C2 /* NSCollectionViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE311DCFD05F0066A8C2 /* NSCollectionViewSpec.swift */; }; + 4EE637362090F92600ECD02A /* UIApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE637352090F92600ECD02A /* UIApplication.swift */; }; + 4EE637372090F92600ECD02A /* UIApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE637352090F92600ECD02A /* UIApplication.swift */; }; 531866F81DD7920400D1285F /* UIStepper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531866F71DD7920400D1285F /* UIStepper.swift */; }; 531866FA1DD7925600D1285F /* UIStepperSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531866F91DD7925600D1285F /* UIStepperSpec.swift */; }; 538DCB791DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */; }; @@ -408,6 +410,7 @@ 4ABEFE2C1DCFD0180066A8C2 /* NSTableViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSTableViewSpec.swift; sourceTree = ""; }; 4ABEFE2F1DCFD0530066A8C2 /* NSCollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSCollectionView.swift; sourceTree = ""; }; 4ABEFE311DCFD05F0066A8C2 /* NSCollectionViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSCollectionViewSpec.swift; sourceTree = ""; }; + 4EE637352090F92600ECD02A /* UIApplication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIApplication.swift; sourceTree = ""; }; 531866F71DD7920400D1285F /* UIStepper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIStepper.swift; sourceTree = ""; }; 531866F91DD7925600D1285F /* UIStepperSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIStepperSpec.swift; sourceTree = ""; }; 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSLayoutConstraint.swift; sourceTree = ""; }; @@ -721,6 +724,7 @@ CD91E3D41DDAC67700FA70D0 /* iOS */, 9A6AAA221DB8F51C0013AAEA /* ReusableComponents.swift */, 9A1D05EC1D93E9F100ACF44C /* UIActivityIndicatorView.swift */, + 4EE637352090F92600ECD02A /* UIApplication.swift */, 9A1D05ED1D93E9F100ACF44C /* UIBarButtonItem.swift */, 9A1D05EE1D93E9F100ACF44C /* UIBarItem.swift */, 9A1D05EF1D93E9F100ACF44C /* UIButton.swift */, @@ -1392,6 +1396,7 @@ 9AB15C7D1E26CD9A00997378 /* Deprecations+Removals.swift in Sources */, 9ADE4A7F1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, 9AE7C2A71DDD7F5100F7534C /* ObjC+Messages.swift in Sources */, + 4EE637372090F92600ECD02A /* UIApplication.swift in Sources */, 9A1D06201D93EA0100ACF44C /* UITextView.swift in Sources */, 9A6AAA241DB8F51C0013AAEA /* ReusableComponents.swift in Sources */, 4ABEFE201DCFCEF80066A8C2 /* UITableView.swift in Sources */, @@ -1609,6 +1614,7 @@ 9A2E425F1DAA6737006D909F /* CocoaTarget.swift in Sources */, 9AAD49881DED2C350068EC9B /* UIKeyboard.swift in Sources */, 53AC46CC1DD6F97400C799E1 /* UISlider.swift in Sources */, + 4EE637362090F92600ECD02A /* UIApplication.swift in Sources */, 9AA0BD821DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift in Sources */, 9A1D060F1D93EA0000ACF44C /* UIView.swift in Sources */, 9A1D06051D93EA0000ACF44C /* UIDatePicker.swift in Sources */, diff --git a/ReactiveCocoa/UIKit/UIApplication.swift b/ReactiveCocoa/UIKit/UIApplication.swift new file mode 100644 index 0000000000..57175a4c60 --- /dev/null +++ b/ReactiveCocoa/UIKit/UIApplication.swift @@ -0,0 +1,9 @@ +import ReactiveSwift +import UIKit + +extension Reactive where Base: UIApplication { + /// Sets the number as the badge of the app icon in Springboard. + public var applicationIconBadgeNumber: BindingTarget { + return makeBindingTarget({ $0.applicationIconBadgeNumber = $1 }) + } +} From 8cc7c5714fa7ef59d9e16665fbded67ecfed608e Mon Sep 17 00:00:00 2001 From: Jonathan Baker Date: Wed, 25 Apr 2018 14:08:11 -0400 Subject: [PATCH 0919/1028] Add spec for UIApplication extension. --- ReactiveCocoa.xcodeproj/project.pbxproj | 6 +++++ .../UIKit/UIApplicationSpec.swift | 25 +++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 ReactiveCocoaTests/UIKit/UIApplicationSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 780879cfda..a50d923916 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -35,6 +35,8 @@ 4ABEFE331DCFD0630066A8C2 /* NSCollectionViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE311DCFD05F0066A8C2 /* NSCollectionViewSpec.swift */; }; 4EE637362090F92600ECD02A /* UIApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE637352090F92600ECD02A /* UIApplication.swift */; }; 4EE637372090F92600ECD02A /* UIApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE637352090F92600ECD02A /* UIApplication.swift */; }; + 4EE637392090F9F600ECD02A /* UIApplicationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE637382090F9F600ECD02A /* UIApplicationSpec.swift */; }; + 4EE6373A2090F9F600ECD02A /* UIApplicationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE637382090F9F600ECD02A /* UIApplicationSpec.swift */; }; 531866F81DD7920400D1285F /* UIStepper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531866F71DD7920400D1285F /* UIStepper.swift */; }; 531866FA1DD7925600D1285F /* UIStepperSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531866F91DD7925600D1285F /* UIStepperSpec.swift */; }; 538DCB791DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */; }; @@ -411,6 +413,7 @@ 4ABEFE2F1DCFD0530066A8C2 /* NSCollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSCollectionView.swift; sourceTree = ""; }; 4ABEFE311DCFD05F0066A8C2 /* NSCollectionViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSCollectionViewSpec.swift; sourceTree = ""; }; 4EE637352090F92600ECD02A /* UIApplication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIApplication.swift; sourceTree = ""; }; + 4EE637382090F9F600ECD02A /* UIApplicationSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIApplicationSpec.swift; sourceTree = ""; }; 531866F71DD7920400D1285F /* UIStepper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIStepper.swift; sourceTree = ""; }; 531866F91DD7925600D1285F /* UIStepperSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIStepperSpec.swift; sourceTree = ""; }; 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSLayoutConstraint.swift; sourceTree = ""; }; @@ -751,6 +754,7 @@ children = ( 9A6AAA291DB8F7F10013AAEA /* ReusableComponentsSpec.swift */, 9A1D06241D93EA7E00ACF44C /* UIActivityIndicatorViewSpec.swift */, + 4EE637382090F9F600ECD02A /* UIApplicationSpec.swift */, 9A1D06251D93EA7E00ACF44C /* UIBarButtonItemSpec.swift */, 9A1D06261D93EA7E00ACF44C /* UIButtonSpec.swift */, 4ABEFE271DCFCFA90066A8C2 /* UICollectionViewSpec.swift */, @@ -1460,6 +1464,7 @@ 9AD841DE204C29B90040F0C0 /* MessageForwardingEntity.m in Sources */, A9EB3D2B1E94F3D9002A9BCC /* UITabBarItemSpec.swift in Sources */, 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */, + 4EE6373A2090F9F600ECD02A /* UIApplicationSpec.swift in Sources */, 9ADFE5A31DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */, 9A1D06411D93EA7E00ACF44C /* UIControlSpec.swift in Sources */, 9AFCBFE51EB1ABC0004B4C74 /* KVOKVCExtensionSpec.swift in Sources */, @@ -1679,6 +1684,7 @@ 3B30EE8D1E7BE529007CC8EF /* DeprecationsSpec.swift in Sources */, 9A9DFEEA1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */, 9A54A2121DDF5B4D001739B3 /* InterceptingPerformanceTests.swift in Sources */, + 4EE637392090F9F600ECD02A /* UIApplicationSpec.swift in Sources */, 9ADFE5A21DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */, 9A1D06421D93EA7E00ACF44C /* UIDatePickerSpec.swift in Sources */, 53AC46CF1DD6FC0000C799E1 /* UISliderSpec.swift in Sources */, diff --git a/ReactiveCocoaTests/UIKit/UIApplicationSpec.swift b/ReactiveCocoaTests/UIKit/UIApplicationSpec.swift new file mode 100644 index 0000000000..288d178f57 --- /dev/null +++ b/ReactiveCocoaTests/UIKit/UIApplicationSpec.swift @@ -0,0 +1,25 @@ +import ReactiveSwift +import ReactiveCocoa +import UIKit +import Quick +import Nimble +import enum Result.NoError + +class UIApplicationSpec: QuickSpec { + override func spec() { + it("should accept changes from bindings to its applicationIconBadgeNumber value") { + let application = UIApplication.shared + + application.applicationIconBadgeNumber = 0 + + let (pipeSignal, observer) = Signal.pipe() + application.reactive.applicationIconBadgeNumber <~ pipeSignal + + observer.send(value: 1) + expect(application.applicationIconBadgeNumber) == 1 + + observer.send(value: 1337) + expect(application.applicationIconBadgeNumber) == 1337 + } + } +} From 4f61855d900498644c8d564a1f2dacd326b9b04f Mon Sep 17 00:00:00 2001 From: Jonathan Baker Date: Wed, 25 Apr 2018 14:10:49 -0400 Subject: [PATCH 0920/1028] Update CHANGELOG. --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eecd5b9c20..7ec6d4f965 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # master *Please put new entries at the top. +# 7.X.0 +1. Added `applicationIconBadgeNumber` binding target to `UIApplication` (#3589, kudos to @cocoahero). + # 7.2.0 1. Fixed a compilation issue related to [SR-7299](https://bugs.swift.org/browse/SR-7299). (#3580) From 041d35e38d145a250aa4b284801c9e1942f75359 Mon Sep 17 00:00:00 2001 From: Michael Gray Date: Fri, 27 Apr 2018 11:04:09 -0400 Subject: [PATCH 0921/1028] support for static libs on cocoapods --- ReactiveCocoa.podspec | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index bb730e7e59..24ee63284a 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -16,12 +16,14 @@ Pod::Spec.new do |s| s.source = { :git => "https://github.com/ReactiveCocoa/ReactiveCocoa.git", :tag => "#{s.version}" } s.source_files = "ReactiveCocoa/*.{swift,h,m}", "ReactiveCocoa/Shared/*.{swift}" - s.private_header_files = "ReactiveCocoa/ObjCRuntimeAliases.h" + s.public_header_files = "ReactiveCocoa/ObjCRuntimeAliases.h" s.osx.source_files = "ReactiveCocoa/AppKit/*.{swift}" s.ios.source_files = "ReactiveCocoa/UIKit/*.{swift}", "ReactiveCocoa/UIKit/iOS/*.{swift}" s.tvos.source_files = "ReactiveCocoa/UIKit/*.{swift}" s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" - s.module_map = "ReactiveCocoa/module.modulemap" + # s.module_map = "ReactiveCocoa/module.modulemap" + s.static_framework = true + s.module_name = 'ReactiveCocoa' s.dependency 'ReactiveSwift', '~> 3.1' From 13fa4c7c428aca0b5c2779178dd7867a2e83ed67 Mon Sep 17 00:00:00 2001 From: Michael Gray Date: Fri, 27 Apr 2018 11:09:16 -0400 Subject: [PATCH 0922/1028] fixes for allowing cocoapod to generate static framework --- ReactiveCocoa.podspec | 2 -- 1 file changed, 2 deletions(-) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 24ee63284a..160f9725fd 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -21,8 +21,6 @@ Pod::Spec.new do |s| s.ios.source_files = "ReactiveCocoa/UIKit/*.{swift}", "ReactiveCocoa/UIKit/iOS/*.{swift}" s.tvos.source_files = "ReactiveCocoa/UIKit/*.{swift}" s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" - # s.module_map = "ReactiveCocoa/module.modulemap" - s.static_framework = true s.module_name = 'ReactiveCocoa' s.dependency 'ReactiveSwift', '~> 3.1' From 8e824f90465d57ad8dee96ce508fb65cd5d89d87 Mon Sep 17 00:00:00 2001 From: Michael Gray Date: Fri, 27 Apr 2018 11:11:31 -0400 Subject: [PATCH 0923/1028] changelog.md --- CHANGELOG.md | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b26859abf..f080268de5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # master *Please put new entries at the top. +1. Add support for Cocoapods 1.5.0 static frameworks (#3590, kudos to @mishagray) + 1. Add `becomeFirstResponder` and `resignFirstResponder` extensions to `UIResponder`. (#3585, kudos to @Marcocanc) # 7.2.0 @@ -54,7 +56,7 @@ Sources that use the MapKit bindings are now required to import ReactiveMapKit. - For all Xcode project users (including Carthage), targets need to be configured to link against ReactiveMapKit. For CocoaPods users, the framework is offered as a standalone podspec, so the Podfile needs to be updated with a new entry. + For all Xcode project users (including Carthage), targets need to be configured to link against ReactiveMapKit. For CocoaPods users, the framework is offered as a standalone podspec, so the Podfile needs to be updated with a new entry. # 6.1.0-alpha.2 # 6.1.0-alpha.1 @@ -160,16 +162,16 @@ Lots has changed, but if you're already migrating to Swift 3 then that should no #### Foundation: Object Interception RAC 5.0 includes a few object interception tools from ReactiveObjC, remastered for ReactiveSwift. - + 1. **Method Call Interception** Create signals that are sourced by intercepting Objective-C objects. - + ```swift // Notify after every time `viewWillAppear(_:)` is called. let appearing = viewController.reactive.trigger(for: #selector(UIViewController.viewWillAppear(_:))) ``` - + 1. **Object Lifetime** Obtain a `Lifetime` token for any `NSObject` to observe their deinitialization. @@ -183,7 +185,7 @@ RAC 5.0 includes a few object interception tools from ReactiveObjC, remastered f Establish key-value observations in the form of [`SignalProducer`][]s and strong-typed `DynamicProperty`s, and enjoy the inherited composability. - + ```swift // A producer that sends the current value of `keyPath`, followed by // subsequent changes. @@ -191,7 +193,7 @@ RAC 5.0 includes a few object interception tools from ReactiveObjC, remastered f // Terminate the KVO observation if the lifetime of `self` ends. let producer = object.reactive.values(forKeyPath: #keyPath(key)) .take(during: self.reactive.lifetime) - + // A parameterized property that represents the supplied key path of the // wrapped object. It holds a weak reference to the wrapped object. let property = DynamicProperty(object: person, @@ -218,19 +220,19 @@ UI components now expose a collection of binding targets to which can be bound f Interactive UI components expose [`Signal`][]s for control events and updates in the control value upon user interactions. - + A selected set of controls provide a convenience, expressive binding API for [`Action`][]s. - - + + ```swift // Update `allowsCookies` whenever the toggle is flipped. - preferences.allowsCookies <~ toggle.reactive.isOnValues - + preferences.allowsCookies <~ toggle.reactive.isOnValues + // Compute live character counts from the continuous stream of user initiated // changes in the text. textField.reactive.continuousTextValues.map { $0.characters.count } - + // Trigger `commit` whenever the button is pressed. button.reactive.pressed = CocoaAction(viewModel.commit) ``` @@ -285,7 +287,7 @@ let old = atomicCount.modify { value in The new `BindingTargetProtocol` protocol has been formally introduced to represent an entity to which can form a unidirectional binding using the `<~` operator. A new type `BindingTarget` has also been introduced to represent non-observable targets that are expected to only be written to. ```swift -// The `UIControl` exposes a `isEnabled` binding target. +// The `UIControl` exposes a `isEnabled` binding target. control.isEnabled <~ viewModel.isEnabled ``` @@ -297,7 +299,7 @@ control.isEnabled <~ viewModel.isEnabled public final class MyController { private let token = Lifetime.Token() public let lifetime: Lifetime - + public init() { lifetime = Lifetime(token) } From eccf055acfb9d65fcb0765ad22372195cfc61de1 Mon Sep 17 00:00:00 2001 From: Michael Gray Date: Fri, 27 Apr 2018 11:29:39 -0400 Subject: [PATCH 0924/1028] what's up with the whitespace in CHANGELOG.md? reverting atom's changes --- CHANGELOG.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f080268de5..6232469dc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,7 +56,7 @@ Sources that use the MapKit bindings are now required to import ReactiveMapKit. - For all Xcode project users (including Carthage), targets need to be configured to link against ReactiveMapKit. For CocoaPods users, the framework is offered as a standalone podspec, so the Podfile needs to be updated with a new entry. + For all Xcode project users (including Carthage), targets need to be configured to link against ReactiveMapKit. For CocoaPods users, the framework is offered as a standalone podspec, so the Podfile needs to be updated with a new entry. # 6.1.0-alpha.2 # 6.1.0-alpha.1 @@ -162,16 +162,16 @@ Lots has changed, but if you're already migrating to Swift 3 then that should no #### Foundation: Object Interception RAC 5.0 includes a few object interception tools from ReactiveObjC, remastered for ReactiveSwift. - + 1. **Method Call Interception** Create signals that are sourced by intercepting Objective-C objects. - + ```swift // Notify after every time `viewWillAppear(_:)` is called. let appearing = viewController.reactive.trigger(for: #selector(UIViewController.viewWillAppear(_:))) ``` - + 1. **Object Lifetime** Obtain a `Lifetime` token for any `NSObject` to observe their deinitialization. @@ -185,7 +185,7 @@ RAC 5.0 includes a few object interception tools from ReactiveObjC, remastered f Establish key-value observations in the form of [`SignalProducer`][]s and strong-typed `DynamicProperty`s, and enjoy the inherited composability. - + ```swift // A producer that sends the current value of `keyPath`, followed by // subsequent changes. @@ -193,7 +193,7 @@ RAC 5.0 includes a few object interception tools from ReactiveObjC, remastered f // Terminate the KVO observation if the lifetime of `self` ends. let producer = object.reactive.values(forKeyPath: #keyPath(key)) .take(during: self.reactive.lifetime) - + // A parameterized property that represents the supplied key path of the // wrapped object. It holds a weak reference to the wrapped object. let property = DynamicProperty(object: person, @@ -220,19 +220,19 @@ UI components now expose a collection of binding targets to which can be bound f Interactive UI components expose [`Signal`][]s for control events and updates in the control value upon user interactions. - + A selected set of controls provide a convenience, expressive binding API for [`Action`][]s. - - + + ```swift // Update `allowsCookies` whenever the toggle is flipped. - preferences.allowsCookies <~ toggle.reactive.isOnValues - + preferences.allowsCookies <~ toggle.reactive.isOnValues + // Compute live character counts from the continuous stream of user initiated // changes in the text. textField.reactive.continuousTextValues.map { $0.characters.count } - + // Trigger `commit` whenever the button is pressed. button.reactive.pressed = CocoaAction(viewModel.commit) ``` @@ -287,7 +287,7 @@ let old = atomicCount.modify { value in The new `BindingTargetProtocol` protocol has been formally introduced to represent an entity to which can form a unidirectional binding using the `<~` operator. A new type `BindingTarget` has also been introduced to represent non-observable targets that are expected to only be written to. ```swift -// The `UIControl` exposes a `isEnabled` binding target. +// The `UIControl` exposes a `isEnabled` binding target. control.isEnabled <~ viewModel.isEnabled ``` @@ -299,7 +299,7 @@ control.isEnabled <~ viewModel.isEnabled public final class MyController { private let token = Lifetime.Token() public let lifetime: Lifetime - + public init() { lifetime = Lifetime(token) } From 65085e94ccfa00bbb4479a1341b4742d3b888001 Mon Sep 17 00:00:00 2001 From: Christopher Liscio Date: Mon, 4 Jun 2018 11:31:29 -0400 Subject: [PATCH 0925/1028] Pick up the nimble matchers from ReactiveSwift This resolves a few build warnings that were already resolved in the ReactiveSwift project by merely copying the same source file. --- .../SignalProducerNimbleMatchers.swift | 83 +++++++++++-------- 1 file changed, 50 insertions(+), 33 deletions(-) diff --git a/ReactiveCocoaTests/SignalProducerNimbleMatchers.swift b/ReactiveCocoaTests/SignalProducerNimbleMatchers.swift index 009684c2ea..6fadb214ef 100644 --- a/ReactiveCocoaTests/SignalProducerNimbleMatchers.swift +++ b/ReactiveCocoaTests/SignalProducerNimbleMatchers.swift @@ -1,49 +1,66 @@ +// +// SignalProducerNimbleMatchers.swift +// ReactiveSwift +// +// Created by Javier Soto on 1/25/15. +// Copyright (c) 2015 GitHub. All rights reserved. +// + import Foundation -import ReactiveCocoa + import ReactiveSwift import Nimble -public func sendValue(_ value: T?, sendError: E?, complete: Bool) -> NonNilMatcherFunc> { +public func sendValue(_ value: T?, sendError: E?, complete: Bool) -> Predicate> { return sendValues(value.map { [$0] } ?? [], sendError: sendError, complete: complete) } -public func sendValues(_ values: [T], sendError maybeSendError: E?, complete: Bool) -> NonNilMatcherFunc> { - return NonNilMatcherFunc { actualExpression, failureMessage in +public func sendValues(_ values: [T], sendError maybeSendError: E?, complete: Bool) -> Predicate> { + return Predicate> { actualExpression in precondition(maybeSendError == nil || !complete, "Signals can't both send an error and complete") + guard let signalProducer = try actualExpression.evaluate() else { + let message = ExpectationMessage.fail("The SignalProducer was not created.") + .appendedBeNilHint() + return PredicateResult(status: .fail, message: message) + } - failureMessage.postfixMessage = "Send values \(values). Send error \(String(describing: maybeSendError)). Complete: \(complete)" - let maybeProducer = try actualExpression.evaluate() - - if let signalProducer = maybeProducer { - var sentValues: [T] = [] - var sentError: E? - var signalCompleted = false - - signalProducer.start { event in - switch event { - case let .value(value): - sentValues.append(value) - case .completed: - signalCompleted = true - case let .failed(error): - sentError = error - default: - break - } - } + var sentValues: [T] = [] + var sentError: E? + var signalCompleted = false - if sentValues != values { - return false - } - - if sentError != maybeSendError { - return false + signalProducer.start { event in + switch event { + case let .value(value): + sentValues.append(value) + case .completed: + signalCompleted = true + case let .failed(error): + sentError = error + default: + break } + } - return signalCompleted == complete + if sentValues != values { + let message = ExpectationMessage.expectedCustomValueTo( + "send values <\(values)>", + "<\(sentValues)>" + ) + return PredicateResult(status: .doesNotMatch, message: message) } - else { - return false + + if sentError != maybeSendError { + let message = ExpectationMessage.expectedCustomValueTo( + "send error <\(String(describing: maybeSendError))>", + "<\(String(describing: sentError))>" + ) + return PredicateResult(status: .doesNotMatch, message: message) } + + let completeMessage = complete ? + "complete, but the producer did not complete" : + "not to complete, but the producer completed" + let message = ExpectationMessage.expectedTo(completeMessage) + return PredicateResult(bool: signalCompleted == complete, message: message) } } From 49f825a51ce7fdfd1536f62068e72df5cb739369 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Mon, 4 Jun 2018 23:05:59 +0100 Subject: [PATCH 0926/1028] Update ReactiveSwift to 4.0.0-rc.1. (#3597) * Update ReactiveSwift to 4.0.0-rc.1. * Use Xcode 9.3 for CI. --- .travis.yml | 5 ++--- Cartfile | 2 +- Cartfile.resolved | 8 ++++---- Carthage/Checkouts/Nimble | 2 +- Carthage/Checkouts/Quick | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- Carthage/Checkouts/Result | 2 +- ReactiveCocoa.podspec | 2 +- 8 files changed, 12 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index 88bb3b0e6b..4b9310f5b0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: objective-c -osx_image: xcode9.2 +osx_image: xcode9.3 before_install: true install: true branches: @@ -14,7 +14,7 @@ cache: jobs: include: - stage: unit tests - osx_image: xcode9.2 + osx_image: xcode9.3 script: - XCODE_SCHEME=ReactiveCocoa-macOS XCODE_SDK=macosx @@ -43,7 +43,6 @@ jobs: - gem install cocoapods - pod repo update - pod lib lint ReactiveCocoa.podspec - - pod lib lint ReactiveCocoa.podspec --swift-version=3.2 - pod lib lint ReactiveMapKit.podspec env: - JOB=PODSPEC diff --git a/Cartfile b/Cartfile index 6e9a77789f..61e1b318e8 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" ~> 3.1 +github "ReactiveCocoa/ReactiveSwift" "4.0.0-rc.1" diff --git a/Cartfile.resolved b/Cartfile.resolved index c643618547..4b1e33d34f 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,5 +1,5 @@ -github "Quick/Nimble" "v7.0.3" -github "Quick/Quick" "v1.2.0" -github "ReactiveCocoa/ReactiveSwift" "3.1.0" -github "antitypical/Result" "3.2.4" +github "Quick/Nimble" "v7.1.1" +github "Quick/Quick" "v1.3.0" +github "ReactiveCocoa/ReactiveSwift" "4.0.0-rc.1" +github "antitypical/Result" "4.0.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" diff --git a/Carthage/Checkouts/Nimble b/Carthage/Checkouts/Nimble index 22800b0954..21f4fed205 160000 --- a/Carthage/Checkouts/Nimble +++ b/Carthage/Checkouts/Nimble @@ -1 +1 @@ -Subproject commit 22800b0954c89344bb8c87f8ab93378076716fb7 +Subproject commit 21f4fed2052cea480f5f1d2044d45aa25fdfb988 diff --git a/Carthage/Checkouts/Quick b/Carthage/Checkouts/Quick index 0ff81f2c66..3e3023569c 160000 --- a/Carthage/Checkouts/Quick +++ b/Carthage/Checkouts/Quick @@ -1 +1 @@ -Subproject commit 0ff81f2c665b4381f526bd656f8708dd52a9ea2f +Subproject commit 3e3023569c8d4c4a0d000f58db765df53041117f diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index 46fb4d4a82..7c25bc2e28 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit 46fb4d4a8285286e54929add1d12f384675895c6 +Subproject commit 7c25bc2e284ba54a8d81d3843a59cdd933c8f823 diff --git a/Carthage/Checkouts/Result b/Carthage/Checkouts/Result index 7477584259..8fc088dcf7 160000 --- a/Carthage/Checkouts/Result +++ b/Carthage/Checkouts/Result @@ -1 +1 @@ -Subproject commit 7477584259bfce2560a19e06ad9f71db441fff11 +Subproject commit 8fc088dcf72802801efeecba76ea8fb041fb773d diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 160f9725fd..ee70c38912 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -23,7 +23,7 @@ Pod::Spec.new do |s| s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_name = 'ReactiveCocoa' - s.dependency 'ReactiveSwift', '~> 3.1' + s.dependency 'ReactiveSwift', '4.0.0-rc.1' s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "$(inherited) -suppress-warnings" } end From 5cf8ac4f5dbcc4e41ab44be4f3316dde739b6c83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=82=85=E7=AB=8B=E4=B8=9A?= <17433201@qq.com> Date: Wed, 6 Jun 2018 00:03:07 +0800 Subject: [PATCH 0927/1028] Added a test case for issue #3596. --- .../KeyValueObservingSpec.swift | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/ReactiveCocoaTests/KeyValueObservingSpec.swift b/ReactiveCocoaTests/KeyValueObservingSpec.swift index 7e3a368108..cc5a36600c 100644 --- a/ReactiveCocoaTests/KeyValueObservingSpec.swift +++ b/ReactiveCocoaTests/KeyValueObservingSpec.swift @@ -452,6 +452,44 @@ fileprivate class KeyValueObservingSpecConfiguration: QuickConfiguration { expect(weakOriginalInner).to(beNil()) } + + it("should not observe changes on a replaced inner object in a nested key path") { + let parentObject = NestedObservableObject() + + // This test case requires a nil value which `rac_object` doesn't + // allow, so we are going to use `rac_weakObject` instead. + // The tested inner objects are not meant to be weak in any way. + let oldInnerObject = ObservableObject() + parentObject.rac_weakObject = oldInnerObject + + var values: [Int?] = [] + + context.weakNestedChanges(parentObject).startWithValues { + values.append($0 as! Int?) + } + + expect(values) == [] + + oldInnerObject.rac_value = 1 + expect(values) == [1] + + parentObject.rac_weakObject = nil + expect(values) == [1, nil] + + oldInnerObject.rac_value = 2 + expect(values) == [1, nil] + + let newInnerObject = ObservableObject() + parentObject.rac_weakObject = newInnerObject + + expect(values) == [1, nil, 0] + + oldInnerObject.rac_value = 3 + expect(values) == [1, nil, 0] + + newInnerObject.rac_value = 4 + expect(values) == [1, nil, 0, 4] + } } describe("thread safety") { From d3d56e2752f49d8ca80b9aaccd4fccddd1d127ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=82=85=E7=AB=8B=E4=B8=9A?= <17433201@qq.com> Date: Wed, 6 Jun 2018 00:03:44 +0800 Subject: [PATCH 0928/1028] Fix for issue #3596. --- ReactiveCocoa/NSObject+KeyValueObserving.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ReactiveCocoa/NSObject+KeyValueObserving.swift b/ReactiveCocoa/NSObject+KeyValueObserving.swift index 0144001412..31b75d4597 100644 --- a/ReactiveCocoa/NSObject+KeyValueObserving.swift +++ b/ReactiveCocoa/NSObject+KeyValueObserving.swift @@ -223,6 +223,11 @@ extension KeyValueObserver { if isNested { observer = KeyValueObserver(observing: object, key: keyPathHead, options: options.union(.initial)) { object in guard let value = object?.value(forKey: keyPathHead) as! NSObject? else { + if let headDisposable = headSerialDisposable.inner { + headDisposable.dispose() + headSerialDisposable.inner = nil + } + action(nil) return } From f6a60a786503a91ef5f82dcd4bc1c5e90844f87e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=82=85=E7=AB=8B=E4=B8=9A?= <17433201@qq.com> Date: Mon, 11 Jun 2018 10:25:14 +0800 Subject: [PATCH 0929/1028] Simplified fix for issue #3596. --- ReactiveCocoa/NSObject+KeyValueObserving.swift | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/ReactiveCocoa/NSObject+KeyValueObserving.swift b/ReactiveCocoa/NSObject+KeyValueObserving.swift index 31b75d4597..373182a88e 100644 --- a/ReactiveCocoa/NSObject+KeyValueObserving.swift +++ b/ReactiveCocoa/NSObject+KeyValueObserving.swift @@ -223,11 +223,7 @@ extension KeyValueObserver { if isNested { observer = KeyValueObserver(observing: object, key: keyPathHead, options: options.union(.initial)) { object in guard let value = object?.value(forKey: keyPathHead) as! NSObject? else { - if let headDisposable = headSerialDisposable.inner { - headDisposable.dispose() - headSerialDisposable.inner = nil - } - + headSerialDisposable.inner = nil action(nil) return } From 320c7ced80604524a546a3a874ecf95b4018c866 Mon Sep 17 00:00:00 2001 From: Sho Ikeda Date: Tue, 12 Jun 2018 23:01:10 +0900 Subject: [PATCH 0930/1028] MapKit is not available on watchOS --- ReactiveCocoa.xcodeproj/project.pbxproj | 8 ++--- ReactiveMapKit.podspec | 1 - ReactiveMapKit/MKLocalSearchRequest.swift | 36 +++++++++++------------ ReactiveMapKit/MKMapView.swift | 2 -- 4 files changed, 21 insertions(+), 26 deletions(-) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 4d6d9c85bf..dc1257c3b1 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -1895,7 +1895,7 @@ PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = macosx; - SUPPORTED_PLATFORMS = "macosx appletvsimulator watchsimulator appletvos watchos iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "macosx appletvsimulator appletvos iphoneos iphonesimulator"; VALID_ARCHS = "i386 x86_64 armv7s arm64 armv7 armv7k"; }; name = Debug; @@ -1910,7 +1910,7 @@ PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = macosx; - SUPPORTED_PLATFORMS = "macosx appletvsimulator watchsimulator appletvos watchos iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "macosx appletvsimulator appletvos iphoneos iphonesimulator"; VALID_ARCHS = "i386 x86_64 armv7s arm64 armv7 armv7k"; }; name = Test; @@ -1925,7 +1925,7 @@ PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = macosx; - SUPPORTED_PLATFORMS = "macosx appletvsimulator watchsimulator appletvos watchos iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "macosx appletvsimulator appletvos iphoneos iphonesimulator"; VALID_ARCHS = "i386 x86_64 armv7s arm64 armv7 armv7k"; }; name = Release; @@ -1940,7 +1940,7 @@ PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = macosx; - SUPPORTED_PLATFORMS = "macosx appletvsimulator watchsimulator appletvos watchos iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "macosx appletvsimulator appletvos iphoneos iphonesimulator"; VALID_ARCHS = "i386 x86_64 armv7s arm64 armv7 armv7k"; }; name = Profile; diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index cecf759c7e..29c0f8ddb6 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -12,7 +12,6 @@ Pod::Spec.new do |s| s.osx.deployment_target = "10.9" s.ios.deployment_target = "8.0" s.tvos.deployment_target = "9.0" - s.watchos.deployment_target = "2.0" s.source = { :git => "https://github.com/ReactiveCocoa/ReactiveCocoa.git", :tag => "#{s.version}" } s.source_files = "ReactiveMapKit/*.{swift,h,m}" diff --git a/ReactiveMapKit/MKLocalSearchRequest.swift b/ReactiveMapKit/MKLocalSearchRequest.swift index 680eab356b..2cee6f343d 100644 --- a/ReactiveMapKit/MKLocalSearchRequest.swift +++ b/ReactiveMapKit/MKLocalSearchRequest.swift @@ -3,26 +3,24 @@ import ReactiveCocoa import Result import MapKit -#if os(iOS) || os(tvOS) || os(macOS) - private let defaultLocalSearchError = NSError(domain: "org.reactivecocoa.ReactiveCocoa.Reactivity.MKLocalSearchRequest", - code: 1, - userInfo: nil) - @available(tvOS 9.2, *) - extension Reactive where Base: MKLocalSearchRequest { - /// A SignalProducer which performs an `MKLocalSearch`. - public var search: SignalProducer { - return SignalProducer {[base = self.base] observer, lifetime in - let search = MKLocalSearch(request: base) - search.start { response, error in - if let response = response { - observer.send(value: response) - observer.sendCompleted() - } else { - observer.send(error: AnyError(error ?? defaultLocalSearchError)) - } +private let defaultLocalSearchError = NSError(domain: "org.reactivecocoa.ReactiveCocoa.Reactivity.MKLocalSearchRequest", + code: 1, + userInfo: nil) +@available(tvOS 9.2, *) +extension Reactive where Base: MKLocalSearchRequest { + /// A SignalProducer which performs an `MKLocalSearch`. + public var search: SignalProducer { + return SignalProducer {[base = self.base] observer, lifetime in + let search = MKLocalSearch(request: base) + search.start { response, error in + if let response = response { + observer.send(value: response) + observer.sendCompleted() + } else { + observer.send(error: AnyError(error ?? defaultLocalSearchError)) } - lifetime.observeEnded(search.cancel) } + lifetime.observeEnded(search.cancel) } } -#endif +} diff --git a/ReactiveMapKit/MKMapView.swift b/ReactiveMapKit/MKMapView.swift index a19f386d46..152cf9871a 100644 --- a/ReactiveMapKit/MKMapView.swift +++ b/ReactiveMapKit/MKMapView.swift @@ -2,7 +2,6 @@ import ReactiveSwift import ReactiveCocoa import MapKit -#if os(iOS) || os(tvOS) || os(macOS) @available(tvOS 9.2, *) extension Reactive where Base: MKMapView { @@ -33,4 +32,3 @@ extension Reactive where Base: MKMapView { } #endif } -#endif From 33e76543c52638735226e80911754dc5f794f8eb Mon Sep 17 00:00:00 2001 From: Sho Ikeda Date: Tue, 12 Jun 2018 23:02:02 +0900 Subject: [PATCH 0931/1028] Xcode 9.4: Update to recommended settings --- ReactiveCocoa.xcodeproj/project.pbxproj | 19 ++++++++++++++++++- .../xcschemes/ReactiveCocoa-iOS.xcscheme | 4 +--- .../xcschemes/ReactiveCocoa-macOS.xcscheme | 4 +--- .../xcschemes/ReactiveCocoa-tvOS.xcscheme | 4 +--- .../xcschemes/ReactiveCocoa-watchOS.xcscheme | 2 +- .../xcschemes/ReactiveMapKit.xcscheme | 4 +--- 6 files changed, 23 insertions(+), 14 deletions(-) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index dc1257c3b1..b82adc0259 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -1261,7 +1261,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0900; - LastUpgradeCheck = 0800; + LastUpgradeCheck = 0930; ORGANIZATIONNAME = GitHub; TargetAttributes = { 57A4D1AF1BA13D7A00F7D4B1 = { @@ -1999,6 +1999,10 @@ buildSettings = { APPLETVOS_DEPLOYMENT_TARGET = 9.0; BITCODE_GENERATION_MODE = bitcode; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; ENABLE_TESTABILITY = YES; @@ -2020,6 +2024,10 @@ buildSettings = { APPLETVOS_DEPLOYMENT_TARGET = 9.0; BITCODE_GENERATION_MODE = bitcode; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; GCC_OPTIMIZATION_LEVEL = 0; @@ -2027,6 +2035,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; + SWIFT_COMPILATION_MODE = wholemodule; SWIFT_VERSION = 4.0; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; @@ -2129,6 +2138,10 @@ buildSettings = { APPLETVOS_DEPLOYMENT_TARGET = 9.0; BITCODE_GENERATION_MODE = bitcode; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; IPHONEOS_DEPLOYMENT_TARGET = 8.0; @@ -2193,6 +2206,10 @@ buildSettings = { APPLETVOS_DEPLOYMENT_TARGET = 9.0; BITCODE_GENERATION_MODE = bitcode; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; IPHONEOS_DEPLOYMENT_TARGET = 8.0; diff --git a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme index f91892c9ed..7c759ab667 100644 --- a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme +++ b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme @@ -1,6 +1,6 @@ Date: Tue, 12 Jun 2018 23:14:23 +0900 Subject: [PATCH 0932/1028] [ReactiveMapKit] Remove `armv7k` from `VALID_ARCHS` build setting which was for watchOS --- ReactiveCocoa.xcodeproj/project.pbxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index b82adc0259..b9df13cf42 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -1896,7 +1896,7 @@ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = macosx; SUPPORTED_PLATFORMS = "macosx appletvsimulator appletvos iphoneos iphonesimulator"; - VALID_ARCHS = "i386 x86_64 armv7s arm64 armv7 armv7k"; + VALID_ARCHS = "i386 x86_64 armv7s arm64 armv7"; }; name = Debug; }; @@ -1911,7 +1911,7 @@ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = macosx; SUPPORTED_PLATFORMS = "macosx appletvsimulator appletvos iphoneos iphonesimulator"; - VALID_ARCHS = "i386 x86_64 armv7s arm64 armv7 armv7k"; + VALID_ARCHS = "i386 x86_64 armv7s arm64 armv7"; }; name = Test; }; @@ -1926,7 +1926,7 @@ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = macosx; SUPPORTED_PLATFORMS = "macosx appletvsimulator appletvos iphoneos iphonesimulator"; - VALID_ARCHS = "i386 x86_64 armv7s arm64 armv7 armv7k"; + VALID_ARCHS = "i386 x86_64 armv7s arm64 armv7"; }; name = Release; }; @@ -1941,7 +1941,7 @@ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = macosx; SUPPORTED_PLATFORMS = "macosx appletvsimulator appletvos iphoneos iphonesimulator"; - VALID_ARCHS = "i386 x86_64 armv7s arm64 armv7 armv7k"; + VALID_ARCHS = "i386 x86_64 armv7s arm64 armv7"; }; name = Profile; }; From 31abe12ba67f7395bf096a9e4ff2544c4907d0fb Mon Sep 17 00:00:00 2001 From: Sho Ikeda Date: Tue, 12 Jun 2018 23:50:46 +0900 Subject: [PATCH 0933/1028] Revert SWIFT_COMPILATION_MODE for now as requested --- ReactiveCocoa.xcodeproj/project.pbxproj | 1 - 1 file changed, 1 deletion(-) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index b9df13cf42..3f13dcee6c 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -2035,7 +2035,6 @@ MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; - SWIFT_COMPILATION_MODE = wholemodule; SWIFT_VERSION = 4.0; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; From 872272c04382cf22867d07d3d608bb251f928f22 Mon Sep 17 00:00:00 2001 From: NachoSoto Date: Mon, 11 Jun 2018 18:35:31 -0700 Subject: [PATCH 0934/1028] Updated ReactiveSwift for Xcode 10 --- Cartfile | 2 +- Cartfile.resolved | 4 ++-- Carthage/Checkouts/Nimble | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cartfile b/Cartfile index 61e1b318e8..e477c4293b 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" "4.0.0-rc.1" +github "ReactiveCocoa/ReactiveSwift" "4.0.0-rc.2" diff --git a/Cartfile.resolved b/Cartfile.resolved index 4b1e33d34f..eb63ea4e32 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,5 +1,5 @@ -github "Quick/Nimble" "v7.1.1" +github "Quick/Nimble" "v7.1.2" github "Quick/Quick" "v1.3.0" -github "ReactiveCocoa/ReactiveSwift" "4.0.0-rc.1" +github "ReactiveCocoa/ReactiveSwift" "4.0.0-rc.2" github "antitypical/Result" "4.0.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" diff --git a/Carthage/Checkouts/Nimble b/Carthage/Checkouts/Nimble index 21f4fed205..8023e3980d 160000 --- a/Carthage/Checkouts/Nimble +++ b/Carthage/Checkouts/Nimble @@ -1 +1 @@ -Subproject commit 21f4fed2052cea480f5f1d2044d45aa25fdfb988 +Subproject commit 8023e3980d91b470ad073d6da843b73f2eeb1844 diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index 7c25bc2e28..750387211b 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit 7c25bc2e284ba54a8d81d3843a59cdd933c8f823 +Subproject commit 750387211ba66d9acc498bd75f017769c9158122 From fe7a4d48fba0b3631eb674cf332c31a6b4363f7f Mon Sep 17 00:00:00 2001 From: Andrey Yastrebov Date: Tue, 3 Jul 2018 16:28:53 +0300 Subject: [PATCH 0935/1028] Update ReactiveCocoa.podspec (#3607) - point to 4.0.0-rc.2 --- ReactiveCocoa.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index ee70c38912..aca9062bff 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -23,7 +23,7 @@ Pod::Spec.new do |s| s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_name = 'ReactiveCocoa' - s.dependency 'ReactiveSwift', '4.0.0-rc.1' + s.dependency 'ReactiveSwift', '4.0.0-rc.2' s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "$(inherited) -suppress-warnings" } end From a6762c44bf227a8858d1fa06be45fae7bf73b9da Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Tue, 3 Jul 2018 21:12:04 +0100 Subject: [PATCH 0936/1028] 8.0.0-rc.1 --- CHANGELOG.md | 1 + ReactiveCocoa.podspec | 2 +- ReactiveMapKit.podspec | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae21db80c2..cdfb63b084 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master *Please put new entries at the top. +# 8.0.0-rc.1 1. Add support for Cocoapods 1.5.0 static frameworks (#3590, kudos to @mishagray) 1. Add `becomeFirstResponder` and `resignFirstResponder` extensions to `UIResponder`. (#3585, kudos to @Marcocanc) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index aca9062bff..922a3518ee 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "7.2.0" + s.version = "8.0.0-rc.1" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index 29c0f8ddb6..0b137a4b15 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveMapKit" - s.version = "7.2.0" + s.version = "8.0.0-rc.1" s.summary = "MapKit bindings for ReactiveCocoa." s.description = <<-DESC Provide MapKit bindings for ReactiveCocoa. ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. From 81be4ce4f3469a55078dc59bd93db7f346bdc09a Mon Sep 17 00:00:00 2001 From: Ellen Teapot Date: Tue, 24 Apr 2018 13:46:57 -0700 Subject: [PATCH 0937/1028] Add UIBarButtonItem extensions --- CHANGELOG.md | 1 + ReactiveCocoa/UIKit/UIBarButtonItem.swift | 20 +++++++ .../UIKit/UIBarButtonItemSpec.swift | 59 +++++++++++++++++++ 3 files changed, 80 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cdfb63b084..2294b4dd3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ 1. Add `becomeFirstResponder` and `resignFirstResponder` extensions to `UIResponder`. (#3585, kudos to @Marcocanc) 2. Added `title` binding target to `UIViewController` (#3588, kudos to @cocoahero). 3. Added several trigger signals for view lifecycle events to `UIViewController` (#3588, kudos to @cocoahero). +4. Add extensions for several properties on `UIBarButtonItem` (#3586, kudos to @asmallteapot). # 7.2.0 1. Fixed a compilation issue related to [SR-7299](https://bugs.swift.org/browse/SR-7299). (#3580) diff --git a/ReactiveCocoa/UIKit/UIBarButtonItem.swift b/ReactiveCocoa/UIKit/UIBarButtonItem.swift index 57960651fb..b03fde331e 100644 --- a/ReactiveCocoa/UIKit/UIBarButtonItem.swift +++ b/ReactiveCocoa/UIKit/UIBarButtonItem.swift @@ -26,4 +26,24 @@ extension Reactive where Base: UIBarButtonItem { .disposable?.dispose() } } + + /// Sets the style of the bar button item. + public var style: BindingTarget { + return makeBindingTarget { $0.style = $1 } + } + + /// Sets the width of the bar button item. + public var width: BindingTarget { + return makeBindingTarget { $0.width = $1 } + } + + /// Sets the possible titles of the bar button item. + public var possibleTitles: BindingTarget?> { + return makeBindingTarget { $0.possibleTitles = $1 } + } + + /// Sets the custom view of the bar button item. + public var customView: BindingTarget { + return makeBindingTarget { $0.customView = $1 } + } } diff --git a/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift b/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift index 32fc762f8c..c7c82a6658 100644 --- a/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift @@ -60,5 +60,64 @@ class UIBarButtonItemSpec: QuickSpec { observer.send(value: nil) expect(barButtonItem.image).to(beNil()) } + + it("should accept changes from bindings to its style") { + let (pipeSignal, observer) = Signal.pipe() + barButtonItem.reactive.style <~ SignalProducer(pipeSignal) + + observer.send(value: .done) + expect(barButtonItem.style) == .done + + observer.send(value: .plain) + expect(barButtonItem.style) == .plain + } + + it("should accept changes from bindings to its width") { + let (pipeSignal, observer) = Signal.pipe() + barButtonItem.reactive.width <~ SignalProducer(pipeSignal) + + observer.send(value: 42.0) + expect(barButtonItem.width) == 42.0 + + observer.send(value: 320.0) + expect(barButtonItem.width) == 320.0 + + observer.send(value: 0.0) + expect(barButtonItem.width) == 0.0 + } + + it("should accept changes from bindings to its possible titles") { + let (pipeSignal, observer) = Signal?, NoError>.pipe() + barButtonItem.reactive.possibleTitles <~ SignalProducer(pipeSignal) + + let possibleTitles = Set(["Unread (123,456,789)", "Unread"]) + observer.send(value: possibleTitles) + expect(barButtonItem.possibleTitles) == possibleTitles + + observer.send(value: nil) + expect(barButtonItem.possibleTitles).to(beNil()) + } + + it("should accept changes from bindings to its custom view") { + let firstChange = UIView() + firstChange.accessibilityIdentifier = "first" + + let secondChange = UIView() + secondChange.accessibilityIdentifier = "second" + + barButtonItem.customView = nil + + let (pipeSignal, observer) = Signal.pipe() + barButtonItem.reactive.customView <~ pipeSignal + + observer.send(value: firstChange) + expect(barButtonItem.customView) == firstChange + + observer.send(value: secondChange) + expect(barButtonItem.customView) == secondChange + + observer.send(value: nil) + expect(barButtonItem.customView).to(beNil()) + } } } From c3745b5500e5eda71437e15302f22553ec0eb781 Mon Sep 17 00:00:00 2001 From: Ellen Teapot Date: Tue, 17 Jul 2018 12:33:35 -0700 Subject: [PATCH 0938/1028] Fix CHANGELOG entry --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2294b4dd3b..1db574e47f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,15 @@ # master *Please put new entries at the top. +# 8.0.0-rc.2 +1. Add extensions for several properties on `UIBarButtonItem` (#3586, kudos to @asmallteapot). + # 8.0.0-rc.1 1. Add support for Cocoapods 1.5.0 static frameworks (#3590, kudos to @mishagray) 1. Add `becomeFirstResponder` and `resignFirstResponder` extensions to `UIResponder`. (#3585, kudos to @Marcocanc) 2. Added `title` binding target to `UIViewController` (#3588, kudos to @cocoahero). 3. Added several trigger signals for view lifecycle events to `UIViewController` (#3588, kudos to @cocoahero). -4. Add extensions for several properties on `UIBarButtonItem` (#3586, kudos to @asmallteapot). # 7.2.0 1. Fixed a compilation issue related to [SR-7299](https://bugs.swift.org/browse/SR-7299). (#3580) From 925d30759ae10d8b858391d5410366864629a265 Mon Sep 17 00:00:00 2001 From: Ellen Teapot Date: Tue, 17 Jul 2018 13:48:01 -0700 Subject: [PATCH 0939/1028] Remove unreleased version from CHANGELOG --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1db574e47f..309f0d1f99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,6 @@ # master *Please put new entries at the top. -# 8.0.0-rc.2 1. Add extensions for several properties on `UIBarButtonItem` (#3586, kudos to @asmallteapot). # 8.0.0-rc.1 From d31219bb0cdb9a733ede47c6c41c88b48e22dac3 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Mon, 23 Jul 2018 21:32:12 +0100 Subject: [PATCH 0940/1028] Upgrade to ReactiveSwift 4.0. (#3610) --- Cartfile | 2 +- Cartfile.resolved | 6 +++--- Carthage/Checkouts/Nimble | 2 +- Carthage/Checkouts/Quick | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- ReactiveCocoa.podspec | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cartfile b/Cartfile index e477c4293b..f73f84fd6a 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" "4.0.0-rc.2" +github "ReactiveCocoa/ReactiveSwift" ~> 4.0 diff --git a/Cartfile.resolved b/Cartfile.resolved index eb63ea4e32..d42e390188 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,5 +1,5 @@ -github "Quick/Nimble" "v7.1.2" -github "Quick/Quick" "v1.3.0" -github "ReactiveCocoa/ReactiveSwift" "4.0.0-rc.2" +github "Quick/Nimble" "v7.1.3" +github "Quick/Quick" "v1.3.1" +github "ReactiveCocoa/ReactiveSwift" "4.0.0" github "antitypical/Result" "4.0.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" diff --git a/Carthage/Checkouts/Nimble b/Carthage/Checkouts/Nimble index 8023e3980d..9c1379fdcd 160000 --- a/Carthage/Checkouts/Nimble +++ b/Carthage/Checkouts/Nimble @@ -1 +1 @@ -Subproject commit 8023e3980d91b470ad073d6da843b73f2eeb1844 +Subproject commit 9c1379fdcd58c4f2278aea5e029394ba9a2b8f07 diff --git a/Carthage/Checkouts/Quick b/Carthage/Checkouts/Quick index 3e3023569c..b060679e70 160000 --- a/Carthage/Checkouts/Quick +++ b/Carthage/Checkouts/Quick @@ -1 +1 @@ -Subproject commit 3e3023569c8d4c4a0d000f58db765df53041117f +Subproject commit b060679e70d13c3c7dcd124201b5b1b34ce6f340 diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index 750387211b..4f6a12ae67 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit 750387211ba66d9acc498bd75f017769c9158122 +Subproject commit 4f6a12ae6762e825b0e19a4f7076eafa43847e6e diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 922a3518ee..1bb7baec07 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -23,7 +23,7 @@ Pod::Spec.new do |s| s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" s.module_name = 'ReactiveCocoa' - s.dependency 'ReactiveSwift', '4.0.0-rc.2' + s.dependency 'ReactiveSwift', '~> 4.0' s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "$(inherited) -suppress-warnings" } end From a240cb549f5557a2c4a0f651bc0740c96de57121 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Wed, 25 Jul 2018 10:29:03 +0100 Subject: [PATCH 0941/1028] 8.0.0 --- CHANGELOG.md | 1 + ReactiveCocoa.podspec | 2 +- ReactiveMapKit.podspec | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 309f0d1f99..34930db511 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master *Please put new entries at the top. +# 8.0.0 1. Add extensions for several properties on `UIBarButtonItem` (#3586, kudos to @asmallteapot). # 8.0.0-rc.1 diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 1bb7baec07..244d837f5b 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "8.0.0-rc.1" + s.version = "8.0.0" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index 0b137a4b15..ef4deb89a0 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveMapKit" - s.version = "8.0.0-rc.1" + s.version = "8.0.0" s.summary = "MapKit bindings for ReactiveCocoa." s.description = <<-DESC Provide MapKit bindings for ReactiveCocoa. ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. From afa9f189c00ff93983be3c3f14eb63dcaf394d36 Mon Sep 17 00:00:00 2001 From: Ryo Aoyama Date: Mon, 27 Aug 2018 19:05:59 +0900 Subject: [PATCH 0942/1028] Add public constructor for retrieves the lifetime of AnyObject. (#3614) * Unify synchronizing method * Make the constructor that create Lifetime with AnyObject public * Remove NSObjectProtocol type constraints for Reactive extension of lifetime of AnyObject * Add doc * Add tests * Update CHANGELOG * Fix compiling error on macOS * Fix the api documents * Refactor * Fix CHANGELOG --- CHANGELOG.md | 2 + ReactiveCocoa.xcodeproj/project.pbxproj | 40 +++++------ ...ifetime.swift => AnyObject+Lifetime.swift} | 72 ++++++++++++------- ReactiveCocoa/AppKit/ActionProxy.swift | 2 +- ReactiveCocoa/DelegateProxy.swift | 2 +- ReactiveCocoa/NSObject+BindingTarget.swift | 6 +- ReactiveCocoa/NSObject+Intercepting.swift | 2 +- .../NSObject+KeyValueObserving.swift | 2 +- ReactiveCocoa/NSObject+Synchronizing.swift | 13 ---- ReactiveCocoa/ReactiveSwift+Lifetime.swift | 2 +- ReactiveCocoa/Synchronizing.swift | 5 ++ ReactiveCocoaTests/LifetimeSpec.swift | 47 +++++++++++- 12 files changed, 125 insertions(+), 70 deletions(-) rename ReactiveCocoa/{NSObject+Lifetime.swift => AnyObject+Lifetime.swift} (62%) delete mode 100644 ReactiveCocoa/NSObject+Synchronizing.swift create mode 100644 ReactiveCocoa/Synchronizing.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 34930db511..6c3ba981b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # master *Please put new entries at the top. +1. Introduce Lifetime.of(_:) which retrieves the lifetime of any Objective-C or Swift native object. (#3614, kudos to @ra1028) + # 8.0.0 1. Add extensions for several properties on `UIBarButtonItem` (#3586, kudos to @asmallteapot). diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 3f13dcee6c..e070e7f997 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -17,10 +17,10 @@ 419139461DB910570043C9D1 /* UIGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 419139431DB910570043C9D1 /* UIGestureRecognizer.swift */; }; 419139491DB910570043C9D1 /* UIGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 419139431DB910570043C9D1 /* UIGestureRecognizer.swift */; }; 4191394E1DBA01A00043C9D1 /* UIGestureRecognizerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4191394B1DBA002C0043C9D1 /* UIGestureRecognizerSpec.swift */; }; - 4A0E10FF1D2A92720065D310 /* NSObject+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */; }; - 4A0E11001D2A92720065D310 /* NSObject+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */; }; - 4A0E11011D2A92720065D310 /* NSObject+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */; }; - 4A0E11021D2A92720065D310 /* NSObject+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */; }; + 4A0E10FF1D2A92720065D310 /* AnyObject+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* AnyObject+Lifetime.swift */; }; + 4A0E11001D2A92720065D310 /* AnyObject+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* AnyObject+Lifetime.swift */; }; + 4A0E11011D2A92720065D310 /* AnyObject+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* AnyObject+Lifetime.swift */; }; + 4A0E11021D2A92720065D310 /* AnyObject+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* AnyObject+Lifetime.swift */; }; 4ABEFE1F1DCFCEF60066A8C2 /* UITableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFDE11DCFC8560066A8C2 /* UITableView.swift */; }; 4ABEFE201DCFCEF80066A8C2 /* UITableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFDE11DCFC8560066A8C2 /* UITableView.swift */; }; 4ABEFE211DCFCF090066A8C2 /* UITableViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFDE31DCFCCD70066A8C2 /* UITableViewSpec.swift */; }; @@ -240,10 +240,10 @@ 9ADFE5A11DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */; }; 9ADFE5A21DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */; }; 9ADFE5A31DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */; }; - 9ADFE5A51DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronizing.swift */; }; - 9ADFE5A61DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronizing.swift */; }; - 9ADFE5A71DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronizing.swift */; }; - 9ADFE5A81DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronizing.swift */; }; + 9ADFE5A51DC0001C001E11F7 /* Synchronizing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A41DC0001C001E11F7 /* Synchronizing.swift */; }; + 9ADFE5A61DC0001C001E11F7 /* Synchronizing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A41DC0001C001E11F7 /* Synchronizing.swift */; }; + 9ADFE5A71DC0001C001E11F7 /* Synchronizing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A41DC0001C001E11F7 /* Synchronizing.swift */; }; + 9ADFE5A81DC0001C001E11F7 /* Synchronizing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A41DC0001C001E11F7 /* Synchronizing.swift */; }; 9AE7C2A41DDD7F5100F7534C /* ObjC+Messages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE7C2A31DDD7F5100F7534C /* ObjC+Messages.swift */; }; 9AE7C2A51DDD7F5100F7534C /* ObjC+Messages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE7C2A31DDD7F5100F7534C /* ObjC+Messages.swift */; }; 9AE7C2A61DDD7F5100F7534C /* ObjC+Messages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE7C2A31DDD7F5100F7534C /* ObjC+Messages.swift */; }; @@ -407,7 +407,7 @@ 3BCAAC7B1DEE1A2300B30335 /* UIRefreshControlSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIRefreshControlSpec.swift; sourceTree = ""; }; 419139431DB910570043C9D1 /* UIGestureRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIGestureRecognizer.swift; sourceTree = ""; }; 4191394B1DBA002C0043C9D1 /* UIGestureRecognizerSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIGestureRecognizerSpec.swift; sourceTree = ""; }; - 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Lifetime.swift"; sourceTree = ""; }; + 4A0E10FE1D2A92720065D310 /* AnyObject+Lifetime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AnyObject+Lifetime.swift"; sourceTree = ""; }; 4ABEFDE11DCFC8560066A8C2 /* UITableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableView.swift; sourceTree = ""; }; 4ABEFDE31DCFCCD70066A8C2 /* UITableViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewSpec.swift; sourceTree = ""; }; 4ABEFE231DCFCF5C0066A8C2 /* UICollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UICollectionView.swift; sourceTree = ""; }; @@ -511,7 +511,7 @@ 9ADE4A901DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+ReactiveExtensionsProvider.swift"; sourceTree = ""; }; 9ADE4A951DA6F018005C2AC8 /* NSTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSTextField.swift; sourceTree = ""; }; 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LifetimeSpec.swift; sourceTree = ""; }; - 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronizing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Synchronizing.swift"; sourceTree = ""; }; + 9ADFE5A41DC0001C001E11F7 /* Synchronizing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Synchronizing.swift; sourceTree = ""; }; 9AE7C2A21DDD768500F7534C /* module.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = ""; }; 9AE7C2A31DDD7F5100F7534C /* ObjC+Messages.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObjC+Messages.swift"; sourceTree = ""; }; 9AED64C41E496A3700321004 /* ActionProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionProxy.swift; sourceTree = ""; }; @@ -896,13 +896,12 @@ 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */, CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */, 9A7488471E3B8ACE00CD0317 /* DelegateProxy.swift */, + 4A0E10FE1D2A92720065D310 /* AnyObject+Lifetime.swift */, 9ADE4A901DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift */, 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */, 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */, - 4A0E10FE1D2A92720065D310 /* NSObject+Lifetime.swift */, 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */, 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */, - 9ADFE5A41DC0001C001E11F7 /* NSObject+Synchronizing.swift */, 9AA0BD8E1DDE29F800531FCF /* NSObject+ObjCRuntime.swift */, 9AE7C2A31DDD7F5100F7534C /* ObjC+Messages.swift */, 9AA0BD891DDE153A00531FCF /* ObjC+Constants.swift */, @@ -910,6 +909,7 @@ 9AA0BD801DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift */, 9AA0BD771DDE03DE00531FCF /* ObjC+Runtime.swift */, 9A90374E1ED61C6300345D62 /* ReactiveSwift+Lifetime.swift */, + 9ADFE5A41DC0001C001E11F7 /* Synchronizing.swift */, 9A1D05E91D93E9F100ACF44C /* AppKit */, 9A1D05EB1D93E9F100ACF44C /* UIKit */, 538DCB761DCA5E1600332880 /* Shared */, @@ -1427,8 +1427,8 @@ 9AA0BD8D1DDE153A00531FCF /* ObjC+Constants.swift in Sources */, 9AF0EA781D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, 9A1D06191D93EA0100ACF44C /* UILabel.swift in Sources */, - 4A0E11021D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, - 9ADFE5A81DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */, + 4A0E11021D2A92720065D310 /* AnyObject+Lifetime.swift in Sources */, + 9ADFE5A81DC0001C001E11F7 /* Synchronizing.swift in Sources */, A9D8BA72207CD7090031733D /* UIResponder.swift in Sources */, CD0C45E11CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, A9EB3D2D1E94F5A2002A9BCC /* UITabBarItem.swift in Sources */, @@ -1518,7 +1518,7 @@ 9AA0BD9A1DDE7A2200531FCF /* ObjCRuntimeAliases.m in Sources */, 9ADE4A931DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, 9AA0BD8C1DDE153A00531FCF /* ObjC+Constants.swift in Sources */, - 9ADFE5A71DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */, + 9ADFE5A71DC0001C001E11F7 /* Synchronizing.swift in Sources */, 9ADE4A7E1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, 9AA0BD831DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift in Sources */, CD0C45E01CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, @@ -1526,7 +1526,7 @@ 9AF0EA771D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, 9A9037511ED61C6300345D62 /* ReactiveSwift+Lifetime.swift in Sources */, 9A1D05E21D93E99100ACF44C /* NSObject+Association.swift in Sources */, - 4A0E11011D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, + 4A0E11011D2A92720065D310 /* AnyObject+Lifetime.swift in Sources */, 9AE7C2A61DDD7F5100F7534C /* ObjC+Messages.swift in Sources */, 9AD0F06C1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, ); @@ -1552,7 +1552,7 @@ 9A6AAA261DB8F5280013AAEA /* ReusableComponents.swift in Sources */, 9A90374F1ED61C6300345D62 /* ReactiveSwift+Lifetime.swift in Sources */, 9AA0BD7C1DDE03DE00531FCF /* ObjC+Runtime.swift in Sources */, - 9ADFE5A51DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */, + 9ADFE5A51DC0001C001E11F7 /* Synchronizing.swift in Sources */, 9ADE4A7C1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, 9ADE4A961DA6F018005C2AC8 /* NSTextField.swift in Sources */, 9AA0BD981DDE7A2200531FCF /* ObjCRuntimeAliases.m in Sources */, @@ -1567,7 +1567,7 @@ 9ADE4A911DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, 834DE1121E4120340099F4E5 /* NSSegmentedControl.swift in Sources */, 9A1D065B1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, - 4A0E10FF1D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, + 4A0E10FF1D2A92720065D310 /* AnyObject+Lifetime.swift in Sources */, 9AA0BD811DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1646,9 +1646,9 @@ 9A1D065C1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, 9A1D06071D93EA0000ACF44C /* UILabel.swift in Sources */, BF4335651E02AC7600AC88DD /* UIScrollView.swift in Sources */, - 4A0E11001D2A92720065D310 /* NSObject+Lifetime.swift in Sources */, + 4A0E11001D2A92720065D310 /* AnyObject+Lifetime.swift in Sources */, 9ADE4A921DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, - 9ADFE5A61DC0001C001E11F7 /* NSObject+Synchronizing.swift in Sources */, + 9ADFE5A61DC0001C001E11F7 /* Synchronizing.swift in Sources */, 9AD0F06B1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, 4ABEFE1F1DCFCEF60066A8C2 /* UITableView.swift in Sources */, 9A1D06081D93EA0000ACF44C /* UIProgressView.swift in Sources */, diff --git a/ReactiveCocoa/NSObject+Lifetime.swift b/ReactiveCocoa/AnyObject+Lifetime.swift similarity index 62% rename from ReactiveCocoa/NSObject+Lifetime.swift rename to ReactiveCocoa/AnyObject+Lifetime.swift index ddc19a4337..998475e168 100644 --- a/ReactiveCocoa/NSObject+Lifetime.swift +++ b/ReactiveCocoa/AnyObject+Lifetime.swift @@ -2,46 +2,59 @@ import Foundation import ReactiveSwift /// Holds the `Lifetime` of the object. -fileprivate let isSwizzledKey = AssociationKey(default: false) +private let isSwizzledKey = AssociationKey(default: false) /// Holds the `Lifetime` of the object. -fileprivate let lifetimeKey = AssociationKey(default: nil) +private let lifetimeKey = AssociationKey(default: nil) /// Holds the `Lifetime.Token` of the object. -fileprivate let lifetimeTokenKey = AssociationKey(default: nil) - -internal func lifetime(of object: AnyObject) -> Lifetime { - if let object = object as? NSObject { - return object.reactive.lifetime - } +private let lifetimeTokenKey = AssociationKey(default: nil) + +public extension Lifetime { + /// Retrive the associated lifetime of given object. + /// The lifetime ends when the given object is deinitialized. + /// + /// - parameters: + /// - object: The object for which the lifetime is obtained. + /// + /// - returns: The lifetime ends when the given object is deinitialized. + public static func of(_ object: AnyObject) -> Lifetime { + if let object = object as? NSObject { + return .of(object) + } - return synchronized(object) { - let associations = Associations(object) + return synchronized(object) { + let associations = Associations(object) - if let lifetime = associations.value(forKey: lifetimeKey) { - return lifetime - } + if let lifetime = associations.value(forKey: lifetimeKey) { + return lifetime + } - let (lifetime, token) = Lifetime.make() + let (lifetime, token) = Lifetime.make() - associations.setValue(token, forKey: lifetimeTokenKey) - associations.setValue(lifetime, forKey: lifetimeKey) + associations.setValue(token, forKey: lifetimeTokenKey) + associations.setValue(lifetime, forKey: lifetimeKey) - return lifetime + return lifetime + } } -} -extension Reactive where Base: AnyObject & NSObjectProtocol { - /// Returns a lifetime that ends when the object is deallocated. - @nonobjc public var lifetime: Lifetime { - return base.synchronized { - if let lifetime = base.associations.value(forKey: lifetimeKey) { + /// Retrive the associated lifetime of given object. + /// The lifetime ends when the given object is deinitialized. + /// + /// - parameters: + /// - object: The object for which the lifetime is obtained. + /// + /// - returns: The lifetime ends when the given object is deinitialized. + public static func of(_ object: NSObject) -> Lifetime { + return synchronized(object) { + if let lifetime = object.associations.value(forKey: lifetimeKey) { return lifetime } let (lifetime, token) = Lifetime.make() - let objcClass: AnyClass = (base as AnyObject).objcClass + let objcClass: AnyClass = (object as AnyObject).objcClass let objcClassAssociations = Associations(objcClass as AnyObject) #if swift(>=4.0) @@ -99,10 +112,17 @@ extension Reactive where Base: AnyObject & NSObjectProtocol { } } - base.associations.setValue(token, forKey: lifetimeTokenKey) - base.associations.setValue(lifetime, forKey: lifetimeKey) + object.associations.setValue(token, forKey: lifetimeTokenKey) + object.associations.setValue(lifetime, forKey: lifetimeKey) return lifetime } } } + +extension Reactive where Base: AnyObject { + /// Returns a lifetime that ends when the object is deallocated. + @nonobjc public var lifetime: Lifetime { + return .of(base) + } +} diff --git a/ReactiveCocoa/AppKit/ActionProxy.swift b/ReactiveCocoa/AppKit/ActionProxy.swift index be5e150d47..6b95d7fe63 100644 --- a/ReactiveCocoa/AppKit/ActionProxy.swift +++ b/ReactiveCocoa/AppKit/ActionProxy.swift @@ -41,7 +41,7 @@ extension Reactive where Base: NSObject, Base: ActionMessageSending { internal var proxy: ActionProxy { let key = AssociationKey?>((#function as StaticString)) - return base.synchronized { + return synchronized(base) { if let proxy = base.associations.value(forKey: key) { return proxy } diff --git a/ReactiveCocoa/DelegateProxy.swift b/ReactiveCocoa/DelegateProxy.swift index 639cf5d9c8..c9ee6d6f73 100644 --- a/ReactiveCocoa/DelegateProxy.swift +++ b/ReactiveCocoa/DelegateProxy.swift @@ -63,7 +63,7 @@ extension DelegateProxy { setter: Selector, getter: Selector ) -> AnyObject { - return instance.synchronized { + return synchronized(instance) { let key = AssociationKey(setter.delegateProxyAlias) if let proxy = instance.associations.value(forKey: key) { diff --git a/ReactiveCocoa/NSObject+BindingTarget.swift b/ReactiveCocoa/NSObject+BindingTarget.swift index b898c77b33..f55ac5441a 100644 --- a/ReactiveCocoa/NSObject+BindingTarget.swift +++ b/ReactiveCocoa/NSObject+BindingTarget.swift @@ -14,7 +14,7 @@ extension Reactive where Base: AnyObject { /// - returns: A binding target that holds no strong references to the /// object. public func makeBindingTarget(on scheduler: Scheduler = UIScheduler(), _ action: @escaping (Base, U) -> Void) -> BindingTarget { - return BindingTarget(on: scheduler, lifetime: ReactiveCocoa.lifetime(of: base)) { [weak base = self.base] value in + return BindingTarget(on: scheduler, lifetime: Lifetime.of(base)) { [weak base = self.base] value in if let base = base { action(base, value) } @@ -32,7 +32,7 @@ extension Reactive where Base: AnyObject { /// /// - returns: A binding target. public subscript(keyPath: ReferenceWritableKeyPath) -> BindingTarget { - return BindingTarget(on: UIScheduler(), lifetime: ReactiveCocoa.lifetime(of: base), object: base, keyPath: keyPath) + return BindingTarget(on: UIScheduler(), lifetime: Lifetime.of(base), object: base, keyPath: keyPath) } /// Creates a binding target that writes to the object with the given key path. @@ -43,7 +43,7 @@ extension Reactive where Base: AnyObject { /// /// - returns: A binding target. public subscript(keyPath: ReferenceWritableKeyPath, on scheduler: Scheduler) -> BindingTarget { - return BindingTarget(on: scheduler, lifetime: ReactiveCocoa.lifetime(of: base), object: base, keyPath: keyPath) + return BindingTarget(on: scheduler, lifetime: Lifetime.of(base), object: base, keyPath: keyPath) } } #endif diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index fb55375674..963a7b100e 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -66,7 +66,7 @@ extension NSObject { let typeEncoding = method_getTypeEncoding(method)! assert(checkTypeEncoding(typeEncoding)) - return synchronized { + return synchronized(self) { let alias = selector.alias let stateKey = AssociationKey(alias) let interopAlias = selector.interopAlias diff --git a/ReactiveCocoa/NSObject+KeyValueObserving.swift b/ReactiveCocoa/NSObject+KeyValueObserving.swift index 373182a88e..489948d92a 100644 --- a/ReactiveCocoa/NSObject+KeyValueObserving.swift +++ b/ReactiveCocoa/NSObject+KeyValueObserving.swift @@ -265,7 +265,7 @@ extension KeyValueObserver { // For a direct key path, the deinitialization needs to be // observed only if the key path is a weak property. if shouldObserveDeinit && isWeak { - let disposable = lifetime(of: value).observeEnded { + let disposable = Lifetime.of(value).observeEnded { action(nil) } diff --git a/ReactiveCocoa/NSObject+Synchronizing.swift b/ReactiveCocoa/NSObject+Synchronizing.swift deleted file mode 100644 index 2816bf1a0f..0000000000 --- a/ReactiveCocoa/NSObject+Synchronizing.swift +++ /dev/null @@ -1,13 +0,0 @@ -extension NSObjectProtocol { - internal func synchronized(execute: () throws -> Result) rethrows -> Result { - objc_sync_enter(self) - defer { objc_sync_exit(self) } - return try execute() - } -} - -internal func synchronized(_ token: AnyObject, execute: () throws -> Result) rethrows -> Result { - objc_sync_enter(token) - defer { objc_sync_exit(token) } - return try execute() -} diff --git a/ReactiveCocoa/ReactiveSwift+Lifetime.swift b/ReactiveCocoa/ReactiveSwift+Lifetime.swift index d42b2e3a49..e7ce35126b 100644 --- a/ReactiveCocoa/ReactiveSwift+Lifetime.swift +++ b/ReactiveCocoa/ReactiveSwift+Lifetime.swift @@ -10,7 +10,7 @@ extension Signal { /// /// - returns: A signal that will deliver events until `object` deinitializes. public func take(duringLifetimeOf object: AnyObject) -> Signal { - return take(during: lifetime(of: object)) + return take(during: Lifetime.of(object)) } } diff --git a/ReactiveCocoa/Synchronizing.swift b/ReactiveCocoa/Synchronizing.swift new file mode 100644 index 0000000000..fc1dc21292 --- /dev/null +++ b/ReactiveCocoa/Synchronizing.swift @@ -0,0 +1,5 @@ +internal func synchronized(_ token: AnyObject, execute: () throws -> Result) rethrows -> Result { + objc_sync_enter(token) + defer { objc_sync_exit(token) } + return try execute() +} diff --git a/ReactiveCocoaTests/LifetimeSpec.swift b/ReactiveCocoaTests/LifetimeSpec.swift index 9e8f91bd39..b159284807 100644 --- a/ReactiveCocoaTests/LifetimeSpec.swift +++ b/ReactiveCocoaTests/LifetimeSpec.swift @@ -11,16 +11,16 @@ class LifetimeSpec: QuickSpec { override func spec() { describe("NSObject.reactive.lifetime") { var object: NSObject! - weak var _object: NSObject? + weak var weakObject: NSObject? beforeEach { object = NSObject() - _object = object + weakObject = object } afterEach { object = nil - expect(_object).to(beNil()) + expect(weakObject).to(beNil()) } it("should not deadlock") { @@ -50,6 +50,47 @@ class LifetimeSpec: QuickSpec { } } + describe("Lifetime.of(_:)") { + var object: Token! + weak var weakObject: Token? + + beforeEach { + object = Token() + weakObject = object + } + + afterEach { + object = nil + expect(weakObject).to(beNil()) + } + + it("should not deadlock") { + for _ in 1 ... 10 { + var isDeadlocked = true + + func createQueue() -> DispatchQueue { + if #available(*, macOS 10.10) { + return .global(qos: .userInitiated) + } else { + return .global(priority: .high) + } + } + + createQueue().async { + _ = Lifetime.of(object) + + createQueue().async { + _ = Lifetime.of(object) + + isDeadlocked = false + } + } + + expect(isDeadlocked).toEventually(beFalsy()) + } + } + } + describe("Signal.take(duringLifetimeOf:)") { it("should work with Objective-C objects") { var object: NSObject? = NSObject() From c9c0ab9d21b1301439c0f6a323944c3f38c70fc9 Mon Sep 17 00:00:00 2001 From: Sho Ikeda Date: Sat, 8 Sep 2018 15:16:20 +0900 Subject: [PATCH 0943/1028] [CI] Use Xcode 9.4 image --- .travis.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4b9310f5b0..873bbda5f5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: objective-c -osx_image: xcode9.3 +osx_image: xcode9.4 before_install: true install: true branches: @@ -14,7 +14,7 @@ cache: jobs: include: - stage: unit tests - osx_image: xcode9.3 + osx_image: xcode9.4 script: - XCODE_SCHEME=ReactiveCocoa-macOS XCODE_SDK=macosx @@ -40,7 +40,6 @@ jobs: script/build - stage: package manager tests script: - - gem install cocoapods - pod repo update - pod lib lint ReactiveCocoa.podspec - pod lib lint ReactiveMapKit.podspec From 6ce3f317f9f509c2aef5e10e0223a11315151b79 Mon Sep 17 00:00:00 2001 From: Sho Ikeda Date: Sat, 8 Sep 2018 15:20:44 +0900 Subject: [PATCH 0944/1028] Update .swift-version --- .swift-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.swift-version b/.swift-version index c4e41f9459..4d0dcda01c 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -4.0.3 +4.1.2 From cc79cbd5e0fc696cd30e88df1455725a371488e2 Mon Sep 17 00:00:00 2001 From: Sho Ikeda Date: Sat, 8 Sep 2018 15:21:46 +0900 Subject: [PATCH 0945/1028] Xcode 9.4: Update to recommended settings --- ReactiveCocoa.xcodeproj/project.pbxproj | 3 ++- .../xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme | 2 +- .../xcshareddata/xcschemes/ReactiveCocoa-macOS.xcscheme | 2 +- .../xcshareddata/xcschemes/ReactiveCocoa-tvOS.xcscheme | 2 +- .../xcshareddata/xcschemes/ReactiveCocoa-watchOS.xcscheme | 2 +- .../xcshareddata/xcschemes/ReactiveMapKit.xcscheme | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index e070e7f997..c702d79007 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -1261,7 +1261,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0900; - LastUpgradeCheck = 0930; + LastUpgradeCheck = 0940; ORGANIZATIONNAME = GitHub; TargetAttributes = { 57A4D1AF1BA13D7A00F7D4B1 = { @@ -2035,6 +2035,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; + SWIFT_COMPILATION_MODE = wholemodule; SWIFT_VERSION = 4.0; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; diff --git a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme index 7c759ab667..fb13d3c42e 100644 --- a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme +++ b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme @@ -1,6 +1,6 @@ Date: Sat, 6 Oct 2018 16:31:01 +0200 Subject: [PATCH 0946/1028] Add swift version to podspecs (#3622) * Add `swift_version` to ReactiveCocoa.podspec * Add `swift_version` to ReactiveMapKit.podspec * Update CHANGELOG.md * Update CHANGELOG.md * Set correct swift version for ReactiveCocoa.podspec * Set correct swift_version for ReactiveMapkit.podspec --- CHANGELOG.md | 1 + ReactiveCocoa.podspec | 1 + ReactiveMapKit.podspec | 1 + 3 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c3ba981b5..bf98fb97bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master *Please put new entries at the top. +1. Add `swift_version` to podspecs (#3622, kudos to @olejnjak) 1. Introduce Lifetime.of(_:) which retrieves the lifetime of any Objective-C or Swift native object. (#3614, kudos to @ra1028) # 8.0.0 diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 244d837f5b..c193827409 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -26,4 +26,5 @@ Pod::Spec.new do |s| s.dependency 'ReactiveSwift', '~> 4.0' s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "$(inherited) -suppress-warnings" } + s.swift_version = '4.1.2' end diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index ef4deb89a0..380700736e 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -19,4 +19,5 @@ Pod::Spec.new do |s| s.dependency 'ReactiveCocoa', "#{s.version}" s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "$(inherited) -suppress-warnings" } + s.swift_version = '4.1.2' end From 221ccceee48d0a3db801f0cb4ae298ceecbf03f1 Mon Sep 17 00:00:00 2001 From: andrei-kuzma Date: Sat, 6 Oct 2018 17:35:30 +0300 Subject: [PATCH 0947/1028] fixed memory issue (#3615) --- ReactiveCocoa/ReactiveSwift+Lifetime.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa/ReactiveSwift+Lifetime.swift b/ReactiveCocoa/ReactiveSwift+Lifetime.swift index e7ce35126b..ea3f85a619 100644 --- a/ReactiveCocoa/ReactiveSwift+Lifetime.swift +++ b/ReactiveCocoa/ReactiveSwift+Lifetime.swift @@ -24,6 +24,6 @@ extension SignalProducer { /// /// - returns: A producer that will deliver events until `object` deinitializes. public func take(duringLifetimeOf object: AnyObject) -> SignalProducer { - return lift { $0.take(duringLifetimeOf: object) } + return take(during: Lifetime.of(object)) } } From 37a072cb09ea7b2d27a266a0b7e0ed1016a109d1 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 6 Oct 2018 15:38:27 +0100 Subject: [PATCH 0948/1028] Update the changelog to cover #3615. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf98fb97bb..270aaf1951 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ 1. Add `swift_version` to podspecs (#3622, kudos to @olejnjak) 1. Introduce Lifetime.of(_:) which retrieves the lifetime of any Objective-C or Swift native object. (#3614, kudos to @ra1028) +1. Fixed an issue of `SignalProducer.take(duringLifetimeOf:)` incorrectly retaining its argument. (#3615, kudos to @andrei-kuzma) # 8.0.0 1. Add extensions for several properties on `UIBarButtonItem` (#3586, kudos to @asmallteapot). From c2759ee63d452175c1394e4015fca29e00adf47e Mon Sep 17 00:00:00 2001 From: Yoshikuni Kato Date: Sun, 7 Oct 2018 01:13:07 +0900 Subject: [PATCH 0949/1028] Binding targets for WKInterfaceLabel & WKInterfaceButton (#3616) * Add binding targets for WKInterfaceLabel, WKInterfaceButton * Update podspec * Update CHANGELOG --- CHANGELOG.md | 1 + ReactiveCocoa.podspec | 1 + ReactiveCocoa.xcodeproj/project.pbxproj | 16 ++++++++ .../WatchKit/WKInterfaceButton.swift | 39 +++++++++++++++++++ ReactiveCocoa/WatchKit/WKInterfaceLabel.swift | 19 +++++++++ 5 files changed, 76 insertions(+) create mode 100644 ReactiveCocoa/WatchKit/WKInterfaceButton.swift create mode 100644 ReactiveCocoa/WatchKit/WKInterfaceLabel.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 270aaf1951..ee7745fa74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master *Please put new entries at the top. +1. Add extensions for several properties on `WKInterfaceLabel` and `WKInterfaceButton`. (#3616, kudos to @yoching) 1. Add `swift_version` to podspecs (#3622, kudos to @olejnjak) 1. Introduce Lifetime.of(_:) which retrieves the lifetime of any Objective-C or Swift native object. (#3614, kudos to @ra1028) 1. Fixed an issue of `SignalProducer.take(duringLifetimeOf:)` incorrectly retaining its argument. (#3615, kudos to @andrei-kuzma) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index c193827409..b20de034e7 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -21,6 +21,7 @@ Pod::Spec.new do |s| s.ios.source_files = "ReactiveCocoa/UIKit/*.{swift}", "ReactiveCocoa/UIKit/iOS/*.{swift}" s.tvos.source_files = "ReactiveCocoa/UIKit/*.{swift}" s.watchos.exclude_files = "ReactiveCocoa/Shared/*.{swift}" + s.watchos.source_files = "ReactiveCocoa/WatchKit/*.{swift}" s.module_name = 'ReactiveCocoa' s.dependency 'ReactiveSwift', '~> 4.0' diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index c702d79007..1b03cee7e6 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -319,6 +319,8 @@ D0A2260F1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */; }; D9558AB81DFF805A003254E1 /* NSPopUpButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9558AB71DFF805A003254E1 /* NSPopUpButton.swift */; }; D9558AB91DFF86C0003254E1 /* NSPopUpButtonSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9558AB51DFF7B90003254E1 /* NSPopUpButtonSpec.swift */; }; + E3AA54C12142918B0077B206 /* WKInterfaceLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3AA54C02142918B0077B206 /* WKInterfaceLabel.swift */; }; + E3AA54C3214292550077B206 /* WKInterfaceButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3AA54C2214292540077B206 /* WKInterfaceButton.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -576,6 +578,8 @@ D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = DynamicPropertySpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; D9558AB51DFF7B90003254E1 /* NSPopUpButtonSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSPopUpButtonSpec.swift; sourceTree = ""; }; D9558AB71DFF805A003254E1 /* NSPopUpButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSPopUpButton.swift; sourceTree = ""; }; + E3AA54C02142918B0077B206 /* WKInterfaceLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WKInterfaceLabel.swift; sourceTree = ""; }; + E3AA54C2214292540077B206 /* WKInterfaceButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WKInterfaceButton.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -910,6 +914,7 @@ 9AA0BD771DDE03DE00531FCF /* ObjC+Runtime.swift */, 9A90374E1ED61C6300345D62 /* ReactiveSwift+Lifetime.swift */, 9ADFE5A41DC0001C001E11F7 /* Synchronizing.swift */, + E3AA54BF214291590077B206 /* WatchKit */, 9A1D05E91D93E9F100ACF44C /* AppKit */, 9A1D05EB1D93E9F100ACF44C /* UIKit */, 538DCB761DCA5E1600332880 /* Shared */, @@ -1040,6 +1045,15 @@ path = "Mac OS X"; sourceTree = ""; }; + E3AA54BF214291590077B206 /* WatchKit */ = { + isa = PBXGroup; + children = ( + E3AA54C02142918B0077B206 /* WKInterfaceLabel.swift */, + E3AA54C2214292540077B206 /* WKInterfaceButton.swift */, + ); + path = WatchKit; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -1510,6 +1524,7 @@ buildActionMask = 2147483647; files = ( 9A2E42601DAA6737006D909F /* CocoaTarget.swift in Sources */, + E3AA54C3214292550077B206 /* WKInterfaceButton.swift in Sources */, 9A74884A1E3B8ACE00CD0317 /* DelegateProxy.swift in Sources */, 9A9DFEE51DA7B5500039EE1B /* NSObject+Intercepting.swift in Sources */, 9A54A21D1DE00D09001739B3 /* ObjC+Selector.swift in Sources */, @@ -1521,6 +1536,7 @@ 9ADFE5A71DC0001C001E11F7 /* Synchronizing.swift in Sources */, 9ADE4A7E1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, 9AA0BD831DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift in Sources */, + E3AA54C12142918B0077B206 /* WKInterfaceLabel.swift in Sources */, CD0C45E01CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9AA0BD911DDE29F800531FCF /* NSObject+ObjCRuntime.swift in Sources */, 9AF0EA771D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, diff --git a/ReactiveCocoa/WatchKit/WKInterfaceButton.swift b/ReactiveCocoa/WatchKit/WKInterfaceButton.swift new file mode 100644 index 0000000000..2d1e4a7cd1 --- /dev/null +++ b/ReactiveCocoa/WatchKit/WKInterfaceButton.swift @@ -0,0 +1,39 @@ +import ReactiveSwift +import WatchKit + +extension Reactive where Base: WKInterfaceButton { + /// Sets the title of the button. + public var text: BindingTarget { + return makeBindingTarget { $0.setTitle($1) } + } + + /// Sets the attributed title of the button. + public var attributedTitle: BindingTarget { + return makeBindingTarget { $0.setAttributedTitle($1) } + } + + /// Sets the background color of the button. + public var backgroundColor: BindingTarget { + return makeBindingTarget { $0.setBackgroundColor($1) } + } + + /// Sets the background image of the button. + public var backgroundImage: BindingTarget { + return makeBindingTarget { $0.setBackgroundImage($1) } + } + + /// Sets the background image data of the button. + public var backgroundImageData: BindingTarget { + return makeBindingTarget { $0.setBackgroundImageData($1) } + } + + /// Sets the background named image of the button. + public var backgroundImageNamed: BindingTarget { + return makeBindingTarget { $0.setBackgroundImageNamed($1) } + } + + /// Sets whether the button is enabled. + public var isEnabled: BindingTarget { + return makeBindingTarget { $0.setEnabled($1) } + } +} diff --git a/ReactiveCocoa/WatchKit/WKInterfaceLabel.swift b/ReactiveCocoa/WatchKit/WKInterfaceLabel.swift new file mode 100644 index 0000000000..61c5a54009 --- /dev/null +++ b/ReactiveCocoa/WatchKit/WKInterfaceLabel.swift @@ -0,0 +1,19 @@ +import ReactiveSwift +import WatchKit + +extension Reactive where Base: WKInterfaceLabel { + /// Sets the text of the label. + public var text: BindingTarget { + return makeBindingTarget { $0.setText($1) } + } + + /// Sets the attributed text of the label. + public var attributedText: BindingTarget { + return makeBindingTarget { $0.setAttributedText($1) } + } + + /// Sets the color of the text of the label. + public var textColor: BindingTarget { + return makeBindingTarget { $0.setTextColor($1) } + } +} From a63c4f6fb92235e9ea1b90ef5a57d82916489bdf Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 6 Oct 2018 17:20:35 +0100 Subject: [PATCH 0950/1028] 8.0.1 --- CHANGELOG.md | 1 + ReactiveCocoa.podspec | 2 +- ReactiveMapKit.podspec | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee7745fa74..4076ec18b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master *Please put new entries at the top. +# 8.0.1 1. Add extensions for several properties on `WKInterfaceLabel` and `WKInterfaceButton`. (#3616, kudos to @yoching) 1. Add `swift_version` to podspecs (#3622, kudos to @olejnjak) 1. Introduce Lifetime.of(_:) which retrieves the lifetime of any Objective-C or Swift native object. (#3614, kudos to @ra1028) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index b20de034e7..5168915241 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "8.0.0" + s.version = "8.0.1" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index 380700736e..0ee346f5d2 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveMapKit" - s.version = "8.0.0" + s.version = "8.0.1" s.summary = "MapKit bindings for ReactiveCocoa." s.description = <<-DESC Provide MapKit bindings for ReactiveCocoa. ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. From 1dfb3ceb7c07657f77bf3240e77740962f54a4c8 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sun, 14 Oct 2018 20:01:20 +0100 Subject: [PATCH 0951/1028] Split the ReactiveMapKit target up. (#3625) * Split the ReactiveMapKit target up. Move CI to Xcode 10. * Update the changelog. * Adapt a swizzling test case to Swift 4.2 runtime changes. * Fix the tvOS scheme. * Fix the linkage of macOS tests. * Use `build-for-testing` for macOS tests. * Use `--use-libraries` when validating pod specs. --- .travis.yml | 10 +- CHANGELOG.md | 2 + ReactiveCocoa.xcodeproj/project.pbxproj | 714 +++++++++++++++--- .../xcschemes/ReactiveCocoa-iOS.xcscheme | 16 +- .../xcschemes/ReactiveCocoa-macOS.xcscheme | 32 +- .../xcschemes/ReactiveCocoa-tvOS.xcscheme | 38 +- .../xcschemes/ReactiveMapKit-iOS.xcscheme | 169 +++++ .../xcschemes/ReactiveMapKit-macOS.xcscheme | 169 +++++ .../xcschemes/ReactiveMapKit-tvOS.xcscheme | 169 +++++ .../xcschemes/ReactiveMapKit.xcscheme | 99 --- ReactiveCocoaTests/SwizzlingSpec.swift | 6 +- 11 files changed, 1211 insertions(+), 213 deletions(-) create mode 100644 ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveMapKit-iOS.xcscheme create mode 100644 ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveMapKit-macOS.xcscheme create mode 100644 ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveMapKit-tvOS.xcscheme delete mode 100644 ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveMapKit.xcscheme diff --git a/.travis.yml b/.travis.yml index 873bbda5f5..4914342a87 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: objective-c -osx_image: xcode9.4 +osx_image: xcode10 before_install: true install: true branches: @@ -14,11 +14,11 @@ cache: jobs: include: - stage: unit tests - osx_image: xcode9.4 + osx_image: xcode10 script: - XCODE_SCHEME=ReactiveCocoa-macOS XCODE_SDK=macosx - XCODE_ACTION="build test" + XCODE_ACTION="build-for-testing test-without-building" XCODE_DESTINATION="arch=x86_64" XCODE_PLAYGROUND_TARGET="x86_64-apple-macosx10.10" PLAYGROUND="ReactiveCocoa-macOS.playground" @@ -41,8 +41,8 @@ jobs: - stage: package manager tests script: - pod repo update - - pod lib lint ReactiveCocoa.podspec - - pod lib lint ReactiveMapKit.podspec + - pod lib lint ReactiveCocoa.podspec --use-libraries + - pod lib lint ReactiveMapKit.podspec --use-libraries env: - JOB=PODSPEC - script: carthage build --cache-builds --no-skip-current diff --git a/CHANGELOG.md b/CHANGELOG.md index 4076ec18b7..60b00d1f1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # master *Please put new entries at the top. +1. ReactiveMapKit has now platform specific build targets and schemes. (#3625, kudos to @andersio) + # 8.0.1 1. Add extensions for several properties on `WKInterfaceLabel` and `WKInterfaceButton`. (#3616, kudos to @yoching) 1. Add `swift_version` to podspecs (#3622, kudos to @olejnjak) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 1b03cee7e6..4635adc097 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -63,23 +63,9 @@ 834DE1141E4122910099F4E5 /* NSSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 834DE1131E4122910099F4E5 /* NSSlider.swift */; }; 8392D8FD1DB93E5E00504ED4 /* NSImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8392D8FC1DB93E5E00504ED4 /* NSImageView.swift */; }; 9A0726F31E912B610081F3F7 /* ActionProxySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A0726F21E912B610081F3F7 /* ActionProxySpec.swift */; }; - 9A16754B1F80C3E300B63650 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D04725EA19E49ED7006002AA /* ReactiveCocoa.framework */; }; - 9A16754C1F80C3EC00B63650 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9AC03A631F7CC6B300EC33C1 /* ReactiveSwift.framework */; }; - 9A1675501F80C40C00B63650 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A16754F1F80C40200B63650 /* Result.framework */; }; 9A1675511F80C41F00B63650 /* ReactiveCocoa.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = D04725EA19E49ED7006002AA /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 9A1675521F80C41F00B63650 /* Result.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = 9A16754F1F80C40200B63650 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 9A1675531F80C41F00B63650 /* ReactiveSwift.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = 9AC03A631F7CC6B300EC33C1 /* ReactiveSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 9A1675571F80C44F00B63650 /* Nimble.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = 9A1675561F80C44100B63650 /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 9A1675581F80C45300B63650 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A1675561F80C44100B63650 /* Nimble.framework */; }; - 9A1675591F80DDDE00B63650 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9AC03A631F7CC6B300EC33C1 /* ReactiveSwift.framework */; }; - 9A16755A1F80DDE100B63650 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9AC03A631F7CC6B300EC33C1 /* ReactiveSwift.framework */; }; - 9A16755B1F80DDE400B63650 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9AC03A631F7CC6B300EC33C1 /* ReactiveSwift.framework */; }; - 9A16755C1F80DDE700B63650 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9AC03A631F7CC6B300EC33C1 /* ReactiveSwift.framework */; }; 9A16755D1F80DE1C00B63650 /* MKMapViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED51DD4BD2C0016C058 /* MKMapViewSpec.swift */; }; - 9A16755E1F80DE2F00B63650 /* ReactiveMapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9AC03A571F7CC3BF00EC33C1 /* ReactiveMapKit.framework */; }; 9A16755F1F80DE3900B63650 /* ReactiveMapKit.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = 9AC03A571F7CC3BF00EC33C1 /* ReactiveMapKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 9A1675601F80DF9600B63650 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; }; - 9A1675641F80DFAB00B63650 /* Quick.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = 9A1675631F80DFA400B63650 /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 9A1D05E01D93E99100ACF44C /* NSObject+Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */; }; 9A1D05E11D93E99100ACF44C /* NSObject+Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */; }; 9A1D05E21D93E99100ACF44C /* NSObject+Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */; }; @@ -149,6 +135,10 @@ 9A2E425F1DAA6737006D909F /* CocoaTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */; }; 9A2E42601DAA6737006D909F /* CocoaTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */; }; 9A2E42611DAA6737006D909F /* CocoaTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */; }; + 9A3C54EB21726A1200E98207 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A3C54EA21726A1200E98207 /* Nimble.framework */; }; + 9A3C54ED21726A1200E98207 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A3C54EC21726A1200E98207 /* Quick.framework */; }; + 9A3C54F121726BD200E98207 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A3C54F021726BD200E98207 /* Nimble.framework */; }; + 9A3C54F321726BD200E98207 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A3C54F221726BD200E98207 /* Quick.framework */; }; 9A54A2111DDF5B4D001739B3 /* InterceptingPerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A54A2101DDF5B4D001739B3 /* InterceptingPerformanceTests.swift */; }; 9A54A2121DDF5B4D001739B3 /* InterceptingPerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A54A2101DDF5B4D001739B3 /* InterceptingPerformanceTests.swift */; }; 9A54A2131DDF5B4D001739B3 /* InterceptingPerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A54A2101DDF5B4D001739B3 /* InterceptingPerformanceTests.swift */; }; @@ -169,6 +159,51 @@ 9A6AAA2B1DB8F85C0013AAEA /* ReusableComponentsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA271DB8F7EB0013AAEA /* ReusableComponentsSpec.swift */; }; 9A6AAA2E1DB903A20013AAEA /* ReusableComponentsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA291DB8F7F10013AAEA /* ReusableComponentsSpec.swift */; }; 9A6AAA2F1DB903A40013AAEA /* ReusableComponentsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA291DB8F7F10013AAEA /* ReusableComponentsSpec.swift */; }; + 9A73DFAE216D3C550069AD76 /* MKMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED11DD4BCA90016C058 /* MKMapView.swift */; }; + 9A73DFAF216D3C550069AD76 /* MKLocalSearchRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91244E720389AEA0001BBCB /* MKLocalSearchRequest.swift */; }; + 9A73DFBF216D3C570069AD76 /* MKMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED11DD4BCA90016C058 /* MKMapView.swift */; }; + 9A73DFC0216D3C570069AD76 /* MKLocalSearchRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91244E720389AEA0001BBCB /* MKLocalSearchRequest.swift */; }; + 9A73DFE3216D3CEB0069AD76 /* MKMapViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED51DD4BD2C0016C058 /* MKMapViewSpec.swift */; }; + 9A73DFEF216D3CEB0069AD76 /* ReactiveCocoa.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = D04725EA19E49ED7006002AA /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 9A73DFF2216D3CEB0069AD76 /* ReactiveMapKit.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = 9AC03A571F7CC3BF00EC33C1 /* ReactiveMapKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 9A73DFFE216D3CEE0069AD76 /* MKMapViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED51DD4BD2C0016C058 /* MKMapViewSpec.swift */; }; + 9A73E00A216D3CEE0069AD76 /* ReactiveCocoa.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = D04725EA19E49ED7006002AA /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 9A73E00D216D3CEE0069AD76 /* ReactiveMapKit.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = 9AC03A571F7CC3BF00EC33C1 /* ReactiveMapKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 9A73E015216D3E660069AD76 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D04725EA19E49ED7006002AA /* ReactiveCocoa.framework */; }; + 9A73E01C216D3E700069AD76 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D047260C19E49F82006002AA /* ReactiveCocoa.framework */; }; + 9A73E030216D3FAF0069AD76 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; + 9A73E036216D3FC50069AD76 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */; }; + 9A73E038216D3FC50069AD76 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E037216D3FC50069AD76 /* Result.framework */; }; + 9A73E039216D3FEB0069AD76 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */; }; + 9A73E03A216D3FF10069AD76 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */; }; + 9A73E03B216D3FF50069AD76 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */; }; + 9A73E03C216D3FFC0069AD76 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */; }; + 9A73E03D216D40070069AD76 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */; }; + 9A73E03E216D400D0069AD76 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */; }; + 9A73E03F216D400D0069AD76 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E037216D3FC50069AD76 /* Result.framework */; }; + 9A73E040216D40170069AD76 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */; }; + 9A73E041216D40170069AD76 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E037216D3FC50069AD76 /* Result.framework */; }; + 9A73E042216D40280069AD76 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */; }; + 9A73E043216D40280069AD76 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E037216D3FC50069AD76 /* Result.framework */; }; + 9A73E049216D40800069AD76 /* ReactiveMapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73DFCC216D3C570069AD76 /* ReactiveMapKit.framework */; }; + 9A73E04A216D40850069AD76 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */; }; + 9A73E04C216D408D0069AD76 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E04B216D408D0069AD76 /* Nimble.framework */; }; + 9A73E04E216D408D0069AD76 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E04D216D408D0069AD76 /* Quick.framework */; }; + 9A73E04F216D40980069AD76 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E04B216D408D0069AD76 /* Nimble.framework */; }; + 9A73E050216D40980069AD76 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E04D216D408D0069AD76 /* Quick.framework */; }; + 9A73E051216D40A20069AD76 /* ReactiveMapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73DFBB216D3C550069AD76 /* ReactiveMapKit.framework */; }; + 9A73E052216D40AC0069AD76 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D047260C19E49F82006002AA /* ReactiveCocoa.framework */; }; + 9A73E053216D40C80069AD76 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D04725EA19E49ED7006002AA /* ReactiveCocoa.framework */; }; + 9A73E054216D40CD0069AD76 /* ReactiveMapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9AC03A571F7CC3BF00EC33C1 /* ReactiveMapKit.framework */; }; + 9A73E055216D40D10069AD76 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */; }; + 9A73E056216D40D10069AD76 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E037216D3FC50069AD76 /* Result.framework */; }; + 9A73E059216D40E30069AD76 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */; }; + 9A73E05A216D40E30069AD76 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E037216D3FC50069AD76 /* Result.framework */; }; + 9A73E05B216D40EC0069AD76 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */; }; + 9A73E05C216D40EC0069AD76 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E037216D3FC50069AD76 /* Result.framework */; }; + 9A73E05D216D40F70069AD76 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */; }; + 9A73E05E216D40F70069AD76 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E037216D3FC50069AD76 /* Result.framework */; }; + 9A73E05F216D41FA0069AD76 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */; }; 9A7488481E3B8ACE00CD0317 /* DelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7488471E3B8ACE00CD0317 /* DelegateProxy.swift */; }; 9A7488491E3B8ACE00CD0317 /* DelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7488471E3B8ACE00CD0317 /* DelegateProxy.swift */; }; 9A74884A1E3B8ACE00CD0317 /* DelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7488471E3B8ACE00CD0317 /* DelegateProxy.swift */; }; @@ -216,9 +251,6 @@ 9AB15C7C1E26CD9A00997378 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AB15C791E26CD9A00997378 /* Deprecations+Removals.swift */; }; 9AB15C7D1E26CD9A00997378 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AB15C791E26CD9A00997378 /* Deprecations+Removals.swift */; }; 9AC03A611F7CC5E300EC33C1 /* MKMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED11DD4BCA90016C058 /* MKMapView.swift */; }; - 9AC03A621F7CC6B300EC33C1 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9AC03A631F7CC6B300EC33C1 /* ReactiveSwift.framework */; }; - 9AC03A641F7CC6B300EC33C1 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D04725EA19E49ED7006002AA /* ReactiveCocoa.framework */; }; - 9AC03A651F7CC6B300EC33C1 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; 9AD0F06B1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; 9AD0F06C1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; @@ -297,7 +329,6 @@ CD8401841CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8401821CEE8ED7009F0ABF /* CocoaActionSpec.swift */; }; CD8401851CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8401821CEE8ED7009F0ABF /* CocoaActionSpec.swift */; }; CDC42E2F1AE7AB8B00965373 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; - CDC42E301AE7AB8B00965373 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; CDC42E311AE7AB8B00965373 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; CDC42E331AE7AC6D00965373 /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; CDF066CA1CDC1CA200199626 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; }; @@ -308,12 +339,10 @@ D037666419EDA43C00A782A9 /* ReactiveCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; }; D037671719EDA60000A782A9 /* test-data.json in Resources */ = {isa = PBXBuildFile; fileRef = D03766B119EDA60000A782A9 /* test-data.json */; }; D037671819EDA60000A782A9 /* test-data.json in Resources */ = {isa = PBXBuildFile; fileRef = D03766B119EDA60000A782A9 /* test-data.json */; }; - D037672D19EDA75D00A782A9 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; }; D037672F19EDA78B00A782A9 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; }; D04725F019E49ED7006002AA /* ReactiveCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; }; D04725F619E49ED7006002AA /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D04725EA19E49ED7006002AA /* ReactiveCocoa.framework */; }; D047261719E49F82006002AA /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D047260C19E49F82006002AA /* ReactiveCocoa.framework */; }; - D05E662519EDD82000904ACA /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; }; D05E662619EDD83000904ACA /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; }; D0A2260E1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */; }; D0A2260F1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */; }; @@ -331,13 +360,27 @@ remoteGlobalIDString = 57A4D1AF1BA13D7A00F7D4B1; remoteInfo = "ReactiveCocoa-tvOS"; }; - 9A1675651F813EAA00B63650 /* PBXContainerItemProxy */ = { + 9A73DFE1216D3CEB0069AD76 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D04725E119E49ED7006002AA /* Project object */; proxyType = 1; remoteGlobalIDString = 9AC03A561F7CC3BF00EC33C1; remoteInfo = ReactiveMapKit; }; + 9A73E045216D404D0069AD76 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D04725E119E49ED7006002AA /* Project object */; + proxyType = 1; + remoteGlobalIDString = 9A73DFAC216D3C550069AD76; + remoteInfo = "ReactiveMapKit-iOS"; + }; + 9A73E047216D40550069AD76 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D04725E119E49ED7006002AA /* Project object */; + proxyType = 1; + remoteGlobalIDString = 9A73DFBD216D3C570069AD76; + remoteInfo = "ReactiveMapKit-tvOS"; + }; D04725F719E49ED7006002AA /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D04725E119E49ED7006002AA /* Project object */; @@ -375,16 +418,36 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 9A1675641F80DFAB00B63650 /* Quick.framework in Copy Files */, - 9A1675571F80C44F00B63650 /* Nimble.framework in Copy Files */, 9A1675511F80C41F00B63650 /* ReactiveCocoa.framework in Copy Files */, - 9A1675521F80C41F00B63650 /* Result.framework in Copy Files */, - 9A1675531F80C41F00B63650 /* ReactiveSwift.framework in Copy Files */, 9A16755F1F80DE3900B63650 /* ReactiveMapKit.framework in Copy Files */, ); name = "Copy Files"; runOnlyForDeploymentPostprocessing = 0; }; + 9A73DFEC216D3CEB0069AD76 /* Copy Files */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 9A73DFEF216D3CEB0069AD76 /* ReactiveCocoa.framework in Copy Files */, + 9A73DFF2216D3CEB0069AD76 /* ReactiveMapKit.framework in Copy Files */, + ); + name = "Copy Files"; + runOnlyForDeploymentPostprocessing = 0; + }; + 9A73E007216D3CEE0069AD76 /* Copy Files */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 9A73E00A216D3CEE0069AD76 /* ReactiveCocoa.framework in Copy Files */, + 9A73E00D216D3CEE0069AD76 /* ReactiveMapKit.framework in Copy Files */, + ); + name = "Copy Files"; + runOnlyForDeploymentPostprocessing = 0; + }; D01B7B6119EDD8F600D26E01 /* Copy Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -442,9 +505,6 @@ 9A0726F21E912B610081F3F7 /* ActionProxySpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionProxySpec.swift; sourceTree = ""; }; 9A16753D1F80C35100B63650 /* ReactiveMapKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveMapKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 9A1675411F80C35100B63650 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 9A16754F1F80C40200B63650 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 9A1675561F80C44100B63650 /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Nimble.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 9A1675631F80DFA400B63650 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Quick.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Association.swift"; sourceTree = ""; }; 9A1D05EC1D93E9F100ACF44C /* UIActivityIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorView.swift; sourceTree = ""; }; 9A1D05ED1D93E9F100ACF44C /* UIBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIBarButtonItem.swift; sourceTree = ""; }; @@ -479,6 +539,10 @@ 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyValueObservingSpec.swift; sourceTree = ""; }; 9A24A8431DE1429600987AF9 /* SwizzlingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwizzlingSpec.swift; sourceTree = ""; }; 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaTarget.swift; sourceTree = ""; }; + 9A3C54EA21726A1200E98207 /* Nimble.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Nimble.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 9A3C54EC21726A1200E98207 /* Quick.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Quick.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 9A3C54F021726BD200E98207 /* Nimble.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Nimble.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 9A3C54F221726BD200E98207 /* Quick.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Quick.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9A54A2101DDF5B4D001739B3 /* InterceptingPerformanceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterceptingPerformanceTests.swift; sourceTree = ""; }; 9A54A21A1DE00D09001739B3 /* ObjC+Selector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObjC+Selector.swift"; sourceTree = ""; }; 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterceptingSpec.swift; sourceTree = ""; }; @@ -486,6 +550,14 @@ 9A6AAA251DB8F5280013AAEA /* ReusableComponents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponents.swift; sourceTree = ""; }; 9A6AAA271DB8F7EB0013AAEA /* ReusableComponentsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponentsSpec.swift; sourceTree = ""; }; 9A6AAA291DB8F7F10013AAEA /* ReusableComponentsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponentsSpec.swift; sourceTree = ""; }; + 9A73DFBB216D3C550069AD76 /* ReactiveMapKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveMapKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 9A73DFCC216D3C570069AD76 /* ReactiveMapKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveMapKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 9A73DFF8216D3CEB0069AD76 /* ReactiveMapKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveMapKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 9A73E013216D3CEE0069AD76 /* ReactiveMapKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveMapKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ReactiveSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 9A73E037216D3FC50069AD76 /* Result.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Result.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 9A73E04B216D408D0069AD76 /* Nimble.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Nimble.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 9A73E04D216D408D0069AD76 /* Quick.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Quick.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9A7488471E3B8ACE00CD0317 /* DelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DelegateProxy.swift; sourceTree = ""; }; 9A7990CD1F1085D8001493A3 /* BindingTargetSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BindingTargetSpec.swift; sourceTree = ""; }; 9A892D8E1E8D19BE00EA35F3 /* DelegateProxySpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DelegateProxySpec.swift; sourceTree = ""; }; @@ -502,7 +574,6 @@ 9AB15C791E26CD9A00997378 /* Deprecations+Removals.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deprecations+Removals.swift"; sourceTree = ""; }; 9AC03A571F7CC3BF00EC33C1 /* ReactiveMapKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveMapKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9AC03A5A1F7CC3BF00EC33C1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 9AC03A631F7CC6B300EC33C1 /* ReactiveSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ReactiveSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+KeyValueObserving.swift"; sourceTree = ""; }; 9AD841D6204C29B90040F0C0 /* ReactiveCocoaTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ReactiveCocoaTests-Bridging-Header.h"; sourceTree = ""; }; 9AD841DA204C29B90040F0C0 /* MessageForwardingEntity.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MessageForwardingEntity.h; sourceTree = ""; }; @@ -588,7 +659,7 @@ buildActionMask = 2147483647; files = ( 57A4D2081BA13D7A00F7D4B1 /* Result.framework in Frameworks */, - 9A16755C1F80DDE700B63650 /* ReactiveSwift.framework in Frameworks */, + 9A73E03C216D3FFC0069AD76 /* ReactiveSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -599,6 +670,8 @@ CDF066CA1CDC1CA200199626 /* Nimble.framework in Frameworks */, CDF066CB1CDC1CA200199626 /* Quick.framework in Frameworks */, 7DFBED081CDB8C9500EE435B /* ReactiveCocoa.framework in Frameworks */, + 9A73E059216D40E30069AD76 /* ReactiveSwift.framework in Frameworks */, + 9A73E05A216D40E30069AD76 /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -606,12 +679,58 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9A16755E1F80DE2F00B63650 /* ReactiveMapKit.framework in Frameworks */, - 9A1675581F80C45300B63650 /* Nimble.framework in Frameworks */, - 9A16754B1F80C3E300B63650 /* ReactiveCocoa.framework in Frameworks */, - 9A16754C1F80C3EC00B63650 /* ReactiveSwift.framework in Frameworks */, - 9A1675501F80C40C00B63650 /* Result.framework in Frameworks */, - 9A1675601F80DF9600B63650 /* Quick.framework in Frameworks */, + 9A73E043216D40280069AD76 /* Result.framework in Frameworks */, + 9A73E042216D40280069AD76 /* ReactiveSwift.framework in Frameworks */, + 9A73E052216D40AC0069AD76 /* ReactiveCocoa.framework in Frameworks */, + 9A73E051216D40A20069AD76 /* ReactiveMapKit.framework in Frameworks */, + 9A73E04F216D40980069AD76 /* Nimble.framework in Frameworks */, + 9A73E050216D40980069AD76 /* Quick.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9A73DFB0216D3C550069AD76 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9A73E01C216D3E700069AD76 /* ReactiveCocoa.framework in Frameworks */, + 9A73E03E216D400D0069AD76 /* ReactiveSwift.framework in Frameworks */, + 9A73E03F216D400D0069AD76 /* Result.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9A73DFC1216D3C570069AD76 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9A73E05F216D41FA0069AD76 /* ReactiveCocoa.framework in Frameworks */, + 9A73E036216D3FC50069AD76 /* ReactiveSwift.framework in Frameworks */, + 9A73E038216D3FC50069AD76 /* Result.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9A73DFE4216D3CEB0069AD76 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9A3C54F121726BD200E98207 /* Nimble.framework in Frameworks */, + 9A3C54F321726BD200E98207 /* Quick.framework in Frameworks */, + 9A73E056216D40D10069AD76 /* Result.framework in Frameworks */, + 9A73E055216D40D10069AD76 /* ReactiveSwift.framework in Frameworks */, + 9A73E053216D40C80069AD76 /* ReactiveCocoa.framework in Frameworks */, + 9A73E054216D40CD0069AD76 /* ReactiveMapKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9A73DFFF216D3CEE0069AD76 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9A73E041216D40170069AD76 /* Result.framework in Frameworks */, + 9A73E040216D40170069AD76 /* ReactiveSwift.framework in Frameworks */, + 9A73E04A216D40850069AD76 /* ReactiveCocoa.framework in Frameworks */, + 9A73E049216D40800069AD76 /* ReactiveMapKit.framework in Frameworks */, + 9A73E04C216D408D0069AD76 /* Nimble.framework in Frameworks */, + 9A73E04E216D408D0069AD76 /* Quick.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -619,9 +738,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9AC03A641F7CC6B300EC33C1 /* ReactiveCocoa.framework in Frameworks */, - 9AC03A621F7CC6B300EC33C1 /* ReactiveSwift.framework in Frameworks */, - 9AC03A651F7CC6B300EC33C1 /* Result.framework in Frameworks */, + 9A73E030216D3FAF0069AD76 /* Result.framework in Frameworks */, + 9A73E03D216D40070069AD76 /* ReactiveSwift.framework in Frameworks */, + 9A73E015216D3E660069AD76 /* ReactiveCocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -630,7 +749,7 @@ buildActionMask = 2147483647; files = ( A9B315C91B3940980001CB9C /* Result.framework in Frameworks */, - 9A16755B1F80DDE400B63650 /* ReactiveSwift.framework in Frameworks */, + 9A73E03B216D3FF50069AD76 /* ReactiveSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -639,7 +758,7 @@ buildActionMask = 2147483647; files = ( CDC42E2F1AE7AB8B00965373 /* Result.framework in Frameworks */, - 9A1675591F80DDDE00B63650 /* ReactiveSwift.framework in Frameworks */, + 9A73E039216D3FEB0069AD76 /* ReactiveSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -647,10 +766,11 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - CDC42E301AE7AB8B00965373 /* Result.framework in Frameworks */, - D05E662519EDD82000904ACA /* Nimble.framework in Frameworks */, - D037672D19EDA75D00A782A9 /* Quick.framework in Frameworks */, + 9A3C54EB21726A1200E98207 /* Nimble.framework in Frameworks */, + 9A3C54ED21726A1200E98207 /* Quick.framework in Frameworks */, D04725F619E49ED7006002AA /* ReactiveCocoa.framework in Frameworks */, + 9A73E05D216D40F70069AD76 /* ReactiveSwift.framework in Frameworks */, + 9A73E05E216D40F70069AD76 /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -659,7 +779,7 @@ buildActionMask = 2147483647; files = ( CDC42E311AE7AB8B00965373 /* Result.framework in Frameworks */, - 9A16755A1F80DDE100B63650 /* ReactiveSwift.framework in Frameworks */, + 9A73E03A216D3FF10069AD76 /* ReactiveSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -670,6 +790,8 @@ D05E662619EDD83000904ACA /* Nimble.framework in Frameworks */, D037672F19EDA78B00A782A9 /* Quick.framework in Frameworks */, D047261719E49F82006002AA /* ReactiveCocoa.framework in Frameworks */, + 9A73E05B216D40EC0069AD76 /* ReactiveSwift.framework in Frameworks */, + 9A73E05C216D40EC0069AD76 /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -796,6 +918,21 @@ path = UIKit; sourceTree = ""; }; + 9A73E026216D3F670069AD76 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 9A3C54F021726BD200E98207 /* Nimble.framework */, + 9A3C54F221726BD200E98207 /* Quick.framework */, + 9A3C54EA21726A1200E98207 /* Nimble.framework */, + 9A3C54EC21726A1200E98207 /* Quick.framework */, + 9A73E04B216D408D0069AD76 /* Nimble.framework */, + 9A73E04D216D408D0069AD76 /* Quick.framework */, + 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */, + 9A73E037216D3FC50069AD76 /* Result.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; 9AC03A581F7CC3BF00EC33C1 /* ReactiveMapKit */ = { isa = PBXGroup; children = ( @@ -833,17 +970,6 @@ path = watchOS; sourceTree = ""; }; - BE330A0D1D634F1E00806963 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 9A1675631F80DFA400B63650 /* Quick.framework */, - 9A1675561F80C44100B63650 /* Nimble.framework */, - 9A16754F1F80C40200B63650 /* Result.framework */, - 9AC03A631F7CC6B300EC33C1 /* ReactiveSwift.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; CD91E3D41DDAC67700FA70D0 /* iOS */ = { isa = PBXGroup; children = ( @@ -872,7 +998,7 @@ 9AC03A581F7CC3BF00EC33C1 /* ReactiveMapKit */, 9A16753E1F80C35100B63650 /* ReactiveMapKitTests */, D04725EB19E49ED7006002AA /* Products */, - BE330A0D1D634F1E00806963 /* Frameworks */, + 9A73E026216D3F670069AD76 /* Frameworks */, ); sourceTree = ""; usesTabs = 1; @@ -889,6 +1015,10 @@ 7DFBED031CDB8C9500EE435B /* ReactiveCocoaTests.xctest */, 9AC03A571F7CC3BF00EC33C1 /* ReactiveMapKit.framework */, 9A16753D1F80C35100B63650 /* ReactiveMapKitTests.xctest */, + 9A73DFBB216D3C550069AD76 /* ReactiveMapKit.framework */, + 9A73DFCC216D3C570069AD76 /* ReactiveMapKit.framework */, + 9A73DFF8216D3CEB0069AD76 /* ReactiveMapKitTests.xctest */, + 9A73E013216D3CEE0069AD76 /* ReactiveMapKitTests.xctest */, ); name = Products; sourceTree = ""; @@ -1066,6 +1196,20 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 9A73DFB4216D3C550069AD76 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9A73DFC5216D3C570069AD76 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 9AC03A541F7CC3BF00EC33C1 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -1140,9 +1284,9 @@ productReference = 7DFBED031CDB8C9500EE435B /* ReactiveCocoaTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; - 9A16753C1F80C35100B63650 /* ReactiveMapKitTests */ = { + 9A16753C1F80C35100B63650 /* ReactiveMapKitTests-iOS */ = { isa = PBXNativeTarget; - buildConfigurationList = 9A1675451F80C35100B63650 /* Build configuration list for PBXNativeTarget "ReactiveMapKitTests" */; + buildConfigurationList = 9A1675451F80C35100B63650 /* Build configuration list for PBXNativeTarget "ReactiveMapKitTests-iOS" */; buildPhases = ( 9A1675391F80C35100B63650 /* Sources */, 9A16753A1F80C35100B63650 /* Frameworks */, @@ -1152,16 +1296,90 @@ buildRules = ( ); dependencies = ( - 9A1675661F813EAA00B63650 /* PBXTargetDependency */, + 9A73E046216D404D0069AD76 /* PBXTargetDependency */, ); - name = ReactiveMapKitTests; + name = "ReactiveMapKitTests-iOS"; productName = ReactiveMapKitTests; productReference = 9A16753D1F80C35100B63650 /* ReactiveMapKitTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; - 9AC03A561F7CC3BF00EC33C1 /* ReactiveMapKit */ = { + 9A73DFAC216D3C550069AD76 /* ReactiveMapKit-iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9A73DFB6216D3C550069AD76 /* Build configuration list for PBXNativeTarget "ReactiveMapKit-iOS" */; + buildPhases = ( + 9A73DFAD216D3C550069AD76 /* Sources */, + 9A73DFB0216D3C550069AD76 /* Frameworks */, + 9A73DFB4216D3C550069AD76 /* Headers */, + 9A73DFB5216D3C550069AD76 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "ReactiveMapKit-iOS"; + productName = ReactiveMapKit; + productReference = 9A73DFBB216D3C550069AD76 /* ReactiveMapKit.framework */; + productType = "com.apple.product-type.framework"; + }; + 9A73DFBD216D3C570069AD76 /* ReactiveMapKit-tvOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9A73DFC7216D3C570069AD76 /* Build configuration list for PBXNativeTarget "ReactiveMapKit-tvOS" */; + buildPhases = ( + 9A73DFBE216D3C570069AD76 /* Sources */, + 9A73DFC1216D3C570069AD76 /* Frameworks */, + 9A73DFC5216D3C570069AD76 /* Headers */, + 9A73DFC6216D3C570069AD76 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "ReactiveMapKit-tvOS"; + productName = ReactiveMapKit; + productReference = 9A73DFCC216D3C570069AD76 /* ReactiveMapKit.framework */; + productType = "com.apple.product-type.framework"; + }; + 9A73DFDF216D3CEB0069AD76 /* ReactiveMapKitTests-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9A73DFF3216D3CEB0069AD76 /* Build configuration list for PBXNativeTarget "ReactiveMapKitTests-macOS" */; + buildPhases = ( + 9A73DFE2216D3CEB0069AD76 /* Sources */, + 9A73DFE4216D3CEB0069AD76 /* Frameworks */, + 9A73DFEB216D3CEB0069AD76 /* Resources */, + 9A73DFEC216D3CEB0069AD76 /* Copy Files */, + ); + buildRules = ( + ); + dependencies = ( + 9A73DFE0216D3CEB0069AD76 /* PBXTargetDependency */, + ); + name = "ReactiveMapKitTests-macOS"; + productName = ReactiveMapKitTests; + productReference = 9A73DFF8216D3CEB0069AD76 /* ReactiveMapKitTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 9A73DFFA216D3CEE0069AD76 /* ReactiveMapKitTests-tvOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9A73E00E216D3CEE0069AD76 /* Build configuration list for PBXNativeTarget "ReactiveMapKitTests-tvOS" */; + buildPhases = ( + 9A73DFFD216D3CEE0069AD76 /* Sources */, + 9A73DFFF216D3CEE0069AD76 /* Frameworks */, + 9A73E006216D3CEE0069AD76 /* Resources */, + 9A73E007216D3CEE0069AD76 /* Copy Files */, + ); + buildRules = ( + ); + dependencies = ( + 9A73E048216D40550069AD76 /* PBXTargetDependency */, + ); + name = "ReactiveMapKitTests-tvOS"; + productName = ReactiveMapKitTests; + productReference = 9A73E013216D3CEE0069AD76 /* ReactiveMapKitTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 9AC03A561F7CC3BF00EC33C1 /* ReactiveMapKit-macOS */ = { isa = PBXNativeTarget; - buildConfigurationList = 9AC03A5C1F7CC3BF00EC33C1 /* Build configuration list for PBXNativeTarget "ReactiveMapKit" */; + buildConfigurationList = 9AC03A5C1F7CC3BF00EC33C1 /* Build configuration list for PBXNativeTarget "ReactiveMapKit-macOS" */; buildPhases = ( 9AC03A521F7CC3BF00EC33C1 /* Sources */, 9AC03A531F7CC3BF00EC33C1 /* Frameworks */, @@ -1172,7 +1390,7 @@ ); dependencies = ( ); - name = ReactiveMapKit; + name = "ReactiveMapKit-macOS"; productName = ReactiveMapKit; productReference = 9AC03A571F7CC3BF00EC33C1 /* ReactiveMapKit.framework */; productType = "com.apple.product-type.framework"; @@ -1334,8 +1552,12 @@ A9B315531B3940610001CB9C /* ReactiveCocoa-watchOS */, 57A4D1AF1BA13D7A00F7D4B1 /* ReactiveCocoa-tvOS */, 7DFBED021CDB8C9500EE435B /* ReactiveCocoa-tvOSTests */, - 9AC03A561F7CC3BF00EC33C1 /* ReactiveMapKit */, - 9A16753C1F80C35100B63650 /* ReactiveMapKitTests */, + 9AC03A561F7CC3BF00EC33C1 /* ReactiveMapKit-macOS */, + 9A73DFDF216D3CEB0069AD76 /* ReactiveMapKitTests-macOS */, + 9A73DFAC216D3C550069AD76 /* ReactiveMapKit-iOS */, + 9A16753C1F80C35100B63650 /* ReactiveMapKitTests-iOS */, + 9A73DFBD216D3C570069AD76 /* ReactiveMapKit-tvOS */, + 9A73DFFA216D3CEE0069AD76 /* ReactiveMapKitTests-tvOS */, ); }; /* End PBXProject section */ @@ -1363,6 +1585,34 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 9A73DFB5216D3C550069AD76 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9A73DFC6216D3C570069AD76 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9A73DFEB216D3CEB0069AD76 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9A73E006216D3CEE0069AD76 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 9AC03A551F7CC3BF00EC33C1 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1510,6 +1760,40 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 9A73DFAD216D3C550069AD76 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9A73DFAE216D3C550069AD76 /* MKMapView.swift in Sources */, + 9A73DFAF216D3C550069AD76 /* MKLocalSearchRequest.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9A73DFBE216D3C570069AD76 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9A73DFBF216D3C570069AD76 /* MKMapView.swift in Sources */, + 9A73DFC0216D3C570069AD76 /* MKLocalSearchRequest.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9A73DFE2216D3CEB0069AD76 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9A73DFE3216D3CEB0069AD76 /* MKMapViewSpec.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9A73DFFD216D3CEE0069AD76 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9A73DFFE216D3CEE0069AD76 /* MKMapViewSpec.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 9AC03A521F7CC3BF00EC33C1 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1743,10 +2027,20 @@ target = 57A4D1AF1BA13D7A00F7D4B1 /* ReactiveCocoa-tvOS */; targetProxy = 7DFBED091CDB8C9500EE435B /* PBXContainerItemProxy */; }; - 9A1675661F813EAA00B63650 /* PBXTargetDependency */ = { + 9A73DFE0216D3CEB0069AD76 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 9AC03A561F7CC3BF00EC33C1 /* ReactiveMapKit-macOS */; + targetProxy = 9A73DFE1216D3CEB0069AD76 /* PBXContainerItemProxy */; + }; + 9A73E046216D404D0069AD76 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = 9AC03A561F7CC3BF00EC33C1 /* ReactiveMapKit */; - targetProxy = 9A1675651F813EAA00B63650 /* PBXContainerItemProxy */; + target = 9A73DFAC216D3C550069AD76 /* ReactiveMapKit-iOS */; + targetProxy = 9A73E045216D404D0069AD76 /* PBXContainerItemProxy */; + }; + 9A73E048216D40550069AD76 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 9A73DFBD216D3C570069AD76 /* ReactiveMapKit-tvOS */; + targetProxy = 9A73E047216D40550069AD76 /* PBXContainerItemProxy */; }; D04725F819E49ED7006002AA /* PBXTargetDependency */ = { isa = PBXTargetDependency; @@ -1856,9 +2150,8 @@ INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = macosx; - SUPPORTED_PLATFORMS = "macosx iphonesimulator appletvsimulator watchsimulator"; + PRODUCT_NAME = ReactiveMapKitTests; + SDKROOT = iphoneos; }; name = Debug; }; @@ -1869,9 +2162,8 @@ INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = macosx; - SUPPORTED_PLATFORMS = "macosx iphonesimulator appletvsimulator watchsimulator"; + PRODUCT_NAME = ReactiveMapKitTests; + SDKROOT = iphoneos; }; name = Test; }; @@ -1882,9 +2174,8 @@ INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = macosx; - SUPPORTED_PLATFORMS = "macosx iphonesimulator appletvsimulator watchsimulator"; + PRODUCT_NAME = ReactiveMapKitTests; + SDKROOT = iphoneos; }; name = Release; }; @@ -1895,9 +2186,208 @@ INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; - PRODUCT_NAME = "$(TARGET_NAME)"; + PRODUCT_NAME = ReactiveMapKitTests; + SDKROOT = iphoneos; + }; + name = Profile; + }; + 9A73DFB7216D3C550069AD76 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047262F19E49FE8006002AA /* Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + INFOPLIST_FILE = ReactiveMapKit/Info.plist; + PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; + PRODUCT_NAME = ReactiveMapKit; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 9A73DFB8216D3C550069AD76 /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047262F19E49FE8006002AA /* Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + INFOPLIST_FILE = ReactiveMapKit/Info.plist; + PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; + PRODUCT_NAME = ReactiveMapKit; + SDKROOT = iphoneos; + }; + name = Test; + }; + 9A73DFB9216D3C550069AD76 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047262F19E49FE8006002AA /* Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + INFOPLIST_FILE = ReactiveMapKit/Info.plist; + PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; + PRODUCT_NAME = ReactiveMapKit; + SDKROOT = iphoneos; + }; + name = Release; + }; + 9A73DFBA216D3C550069AD76 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047262F19E49FE8006002AA /* Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + INFOPLIST_FILE = ReactiveMapKit/Info.plist; + PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; + PRODUCT_NAME = ReactiveMapKit; + SDKROOT = iphoneos; + }; + name = Profile; + }; + 9A73DFC8216D3C570069AD76 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047262F19E49FE8006002AA /* Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + INFOPLIST_FILE = ReactiveMapKit/Info.plist; + PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; + PRODUCT_NAME = ReactiveMapKit; + SDKROOT = appletvos; + }; + name = Debug; + }; + 9A73DFC9216D3C570069AD76 /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047262F19E49FE8006002AA /* Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + INFOPLIST_FILE = ReactiveMapKit/Info.plist; + PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; + PRODUCT_NAME = ReactiveMapKit; + SDKROOT = appletvos; + }; + name = Test; + }; + 9A73DFCA216D3C570069AD76 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047262F19E49FE8006002AA /* Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + INFOPLIST_FILE = ReactiveMapKit/Info.plist; + PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; + PRODUCT_NAME = ReactiveMapKit; + SDKROOT = appletvos; + }; + name = Release; + }; + 9A73DFCB216D3C570069AD76 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047262F19E49FE8006002AA /* Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + INFOPLIST_FILE = ReactiveMapKit/Info.plist; + PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; + PRODUCT_NAME = ReactiveMapKit; + SDKROOT = appletvos; + }; + name = Profile; + }; + 9A73DFF4216D3CEB0069AD76 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047262E19E49FE8006002AA /* Application.xcconfig */; + buildSettings = { + INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; + PRODUCT_NAME = ReactiveMapKitTests; + SDKROOT = macosx; + }; + name = Debug; + }; + 9A73DFF5216D3CEB0069AD76 /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047262E19E49FE8006002AA /* Application.xcconfig */; + buildSettings = { + INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; + PRODUCT_NAME = ReactiveMapKitTests; + SDKROOT = macosx; + }; + name = Test; + }; + 9A73DFF6216D3CEB0069AD76 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047262E19E49FE8006002AA /* Application.xcconfig */; + buildSettings = { + INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; + PRODUCT_NAME = ReactiveMapKitTests; + SDKROOT = macosx; + }; + name = Release; + }; + 9A73DFF7216D3CEB0069AD76 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047262E19E49FE8006002AA /* Application.xcconfig */; + buildSettings = { + INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; + PRODUCT_NAME = ReactiveMapKitTests; SDKROOT = macosx; - SUPPORTED_PLATFORMS = "macosx iphonesimulator appletvsimulator watchsimulator"; + }; + name = Profile; + }; + 9A73E00F216D3CEE0069AD76 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047262E19E49FE8006002AA /* Application.xcconfig */; + buildSettings = { + INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; + PRODUCT_NAME = ReactiveMapKitTests; + SDKROOT = appletvos; + }; + name = Debug; + }; + 9A73E010216D3CEE0069AD76 /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047262E19E49FE8006002AA /* Application.xcconfig */; + buildSettings = { + INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; + PRODUCT_NAME = ReactiveMapKitTests; + SDKROOT = appletvos; + }; + name = Test; + }; + 9A73E011216D3CEE0069AD76 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047262E19E49FE8006002AA /* Application.xcconfig */; + buildSettings = { + INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; + PRODUCT_NAME = ReactiveMapKitTests; + SDKROOT = appletvos; + }; + name = Release; + }; + 9A73E012216D3CEE0069AD76 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047262E19E49FE8006002AA /* Application.xcconfig */; + buildSettings = { + INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; + PRODUCT_NAME = ReactiveMapKitTests; + SDKROOT = appletvos; }; name = Profile; }; @@ -1909,10 +2399,8 @@ DYLIB_CURRENT_VERSION = 1; INFOPLIST_FILE = ReactiveMapKit/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PRODUCT_NAME = ReactiveMapKit; SDKROOT = macosx; - SUPPORTED_PLATFORMS = "macosx appletvsimulator appletvos iphoneos iphonesimulator"; - VALID_ARCHS = "i386 x86_64 armv7s arm64 armv7"; }; name = Debug; }; @@ -1924,10 +2412,8 @@ DYLIB_CURRENT_VERSION = 1; INFOPLIST_FILE = ReactiveMapKit/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PRODUCT_NAME = ReactiveMapKit; SDKROOT = macosx; - SUPPORTED_PLATFORMS = "macosx appletvsimulator appletvos iphoneos iphonesimulator"; - VALID_ARCHS = "i386 x86_64 armv7s arm64 armv7"; }; name = Test; }; @@ -1939,10 +2425,8 @@ DYLIB_CURRENT_VERSION = 1; INFOPLIST_FILE = ReactiveMapKit/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PRODUCT_NAME = ReactiveMapKit; SDKROOT = macosx; - SUPPORTED_PLATFORMS = "macosx appletvsimulator appletvos iphoneos iphonesimulator"; - VALID_ARCHS = "i386 x86_64 armv7s arm64 armv7"; }; name = Release; }; @@ -1954,10 +2438,8 @@ DYLIB_CURRENT_VERSION = 1; INFOPLIST_FILE = ReactiveMapKit/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PRODUCT_NAME = ReactiveMapKit; SDKROOT = macosx; - SUPPORTED_PLATFORMS = "macosx appletvsimulator appletvos iphoneos iphonesimulator"; - VALID_ARCHS = "i386 x86_64 armv7s arm64 armv7"; }; name = Profile; }; @@ -2309,7 +2791,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 9A1675451F80C35100B63650 /* Build configuration list for PBXNativeTarget "ReactiveMapKitTests" */ = { + 9A1675451F80C35100B63650 /* Build configuration list for PBXNativeTarget "ReactiveMapKitTests-iOS" */ = { isa = XCConfigurationList; buildConfigurations = ( 9A1675461F80C35100B63650 /* Debug */, @@ -2320,7 +2802,51 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 9AC03A5C1F7CC3BF00EC33C1 /* Build configuration list for PBXNativeTarget "ReactiveMapKit" */ = { + 9A73DFB6216D3C550069AD76 /* Build configuration list for PBXNativeTarget "ReactiveMapKit-iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9A73DFB7216D3C550069AD76 /* Debug */, + 9A73DFB8216D3C550069AD76 /* Test */, + 9A73DFB9216D3C550069AD76 /* Release */, + 9A73DFBA216D3C550069AD76 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 9A73DFC7216D3C570069AD76 /* Build configuration list for PBXNativeTarget "ReactiveMapKit-tvOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9A73DFC8216D3C570069AD76 /* Debug */, + 9A73DFC9216D3C570069AD76 /* Test */, + 9A73DFCA216D3C570069AD76 /* Release */, + 9A73DFCB216D3C570069AD76 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 9A73DFF3216D3CEB0069AD76 /* Build configuration list for PBXNativeTarget "ReactiveMapKitTests-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9A73DFF4216D3CEB0069AD76 /* Debug */, + 9A73DFF5216D3CEB0069AD76 /* Test */, + 9A73DFF6216D3CEB0069AD76 /* Release */, + 9A73DFF7216D3CEB0069AD76 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 9A73E00E216D3CEE0069AD76 /* Build configuration list for PBXNativeTarget "ReactiveMapKitTests-tvOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9A73E00F216D3CEE0069AD76 /* Debug */, + 9A73E010216D3CEE0069AD76 /* Test */, + 9A73E011216D3CEE0069AD76 /* Release */, + 9A73E012216D3CEE0069AD76 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 9AC03A5C1F7CC3BF00EC33C1 /* Build configuration list for PBXNativeTarget "ReactiveMapKit-macOS" */ = { isa = XCConfigurationList; buildConfigurations = ( 9AC03A5D1F7CC3BF00EC33C1 /* Debug */, diff --git a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme index fb13d3c42e..37e90d8e80 100644 --- a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme +++ b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme @@ -90,6 +90,20 @@ ReferencedContainer = "container:ReactiveCocoa.xcodeproj">
    + + + +
    diff --git a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-macOS.xcscheme b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-macOS.xcscheme index 791da0a5cd..803cd43de8 100644 --- a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-macOS.xcscheme +++ b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-macOS.xcscheme @@ -90,6 +90,34 @@ ReferencedContainer = "container:ReactiveCocoa.xcodeproj"> + + + + + + + +
    diff --git a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-tvOS.xcscheme b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-tvOS.xcscheme index 4acf2d6ef8..79cce96149 100644 --- a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-tvOS.xcscheme +++ b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-tvOS.xcscheme @@ -76,6 +76,34 @@ ReferencedContainer = "container:Carthage/Checkouts/Quick/Quick.xcodeproj"> + + + + + + + +
    - - - -
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveMapKit-macOS.xcscheme b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveMapKit-macOS.xcscheme new file mode 100644 index 0000000000..3050c7a836 --- /dev/null +++ b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveMapKit-macOS.xcscheme @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveMapKit-tvOS.xcscheme b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveMapKit-tvOS.xcscheme new file mode 100644 index 0000000000..8061768f12 --- /dev/null +++ b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveMapKit-tvOS.xcscheme @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveMapKit.xcscheme b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveMapKit.xcscheme deleted file mode 100644 index ef013f3f2f..0000000000 --- a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveMapKit.xcscheme +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ReactiveCocoaTests/SwizzlingSpec.swift b/ReactiveCocoaTests/SwizzlingSpec.swift index dc5b1693ee..9f47161f51 100644 --- a/ReactiveCocoaTests/SwizzlingSpec.swift +++ b/ReactiveCocoaTests/SwizzlingSpec.swift @@ -12,11 +12,13 @@ class SwizzlingSpec: QuickSpec { expect(type(of: object)).to(beIdenticalTo(SwizzledObject.self)) let subclass: AnyClass = swizzleClass(object) - expect(type(of: object)).to(beIdenticalTo(subclass)) + expect(type(of: object)).to(beIdenticalTo(SwizzledObject.self)) + expect(object.objcClass).to(beIdenticalTo(SwizzledObject.self)) + expect(object_getClass(object)).to(beIdenticalTo(subclass)) let objcClass: AnyClass = (object as AnyObject).objcClass expect(objcClass).to(beIdenticalTo(SwizzledObject.self)) - expect((objcClass as AnyObject).objcClass).to(beIdenticalTo(SwizzledObject.self as AnyClass)) + expect((objcClass as AnyObject).objcClass as Any?).to(beIdenticalTo(SwizzledObject.self as AnyClass)) expect(String(cString: class_getName(subclass))).to(contain("_RACSwift")) } From 4a0d33f8df6c3feed25aba0d2cee97e571123c8c Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Tue, 16 Oct 2018 12:36:37 +0100 Subject: [PATCH 0952/1028] 8.0.2 --- CHANGELOG.md | 1 + ReactiveCocoa.podspec | 2 +- ReactiveMapKit.podspec | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60b00d1f1e..7fa1033e52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master *Please put new entries at the top. +# 8.0.2 1. ReactiveMapKit has now platform specific build targets and schemes. (#3625, kudos to @andersio) # 8.0.1 diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 5168915241..ac5fbc66c0 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "8.0.1" + s.version = "8.0.2" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index 0ee346f5d2..05c7298c3a 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveMapKit" - s.version = "8.0.1" + s.version = "8.0.2" s.summary = "MapKit bindings for ReactiveCocoa." s.description = <<-DESC Provide MapKit bindings for ReactiveCocoa. ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. From db44b6824d05a9fcfb57ac8fbdc5bc05c9d6a20e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josip=20=C4=86avar?= Date: Fri, 19 Oct 2018 16:43:11 +0200 Subject: [PATCH 0953/1028] Change to latest method name `values` was renamed to `producer` --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6429f81787..80bbecef64 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ __ReactiveCocoa__ wraps various aspects of Cocoa frameworks with the declarative // subsequent changes. // // Terminate the KVO observation if the lifetime of `self` ends. - let producer = object.reactive.values(forKeyPath: #keyPath(key)) + let producer = object.reactive.producer(forKeyPath: #keyPath(key)) .take(during: self.reactive.lifetime) // A parameterized property that represents the supplied key path of the From b4db0dc58b5424209daa75b8eccfe0a8dd1bade0 Mon Sep 17 00:00:00 2001 From: Raphael Oliveira Date: Fri, 2 Nov 2018 10:11:07 +1100 Subject: [PATCH 0954/1028] Update README.md Update Carthage and CocoaPods instructions for version 8.0 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 80bbecef64..a8d74f5bf6 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ If you use [Carthage][] to manage your dependencies, simply add ReactiveCocoa to your `Cartfile`: ``` -github "ReactiveCocoa/ReactiveCocoa" ~> 7.0 +github "ReactiveCocoa/ReactiveCocoa" ~> 8.0 ``` If you use Carthage to build your dependencies, make sure you have added `ReactiveCocoa.framework`, `ReactiveSwift.framework`, and `Result.framework` to the "_Linked Frameworks and Libraries_" section of your target, and have included them in your Carthage framework copying build phase. @@ -111,7 +111,7 @@ If you use [CocoaPods][] to manage your dependencies, simply add ReactiveCocoa to your `Podfile`: ``` -pod 'ReactiveCocoa', '~> 7.0' +pod 'ReactiveCocoa', '~> 8.0' ``` #### Git submodule From d19ac696d4511d65c17e32b48b64870b17ac72e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eimantas=20Vaic=CC=8Ciu=CC=84nas?= Date: Sat, 24 Nov 2018 11:59:34 +0200 Subject: [PATCH 0955/1028] Add `isHidden` property binding to `NSView` --- ReactiveCocoa.xcodeproj/project.pbxproj | 24 ++++++++++++------- ReactiveCocoa/AppKit/NSView.swift | 9 +++++++ ReactiveCocoaTests/AppKit/NSViewSpec.swift | 28 ++++++++++++++++++++++ 3 files changed, 53 insertions(+), 8 deletions(-) create mode 100644 ReactiveCocoa/AppKit/NSView.swift create mode 100644 ReactiveCocoaTests/AppKit/NSViewSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 4635adc097..316087be8d 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -39,6 +39,8 @@ 4EE637342090EFDF00ECD02A /* UIViewControllerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE637302090EFD300ECD02A /* UIViewControllerSpec.swift */; }; 531866F81DD7920400D1285F /* UIStepper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531866F71DD7920400D1285F /* UIStepper.swift */; }; 531866FA1DD7925600D1285F /* UIStepperSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531866F91DD7925600D1285F /* UIStepperSpec.swift */; }; + 537EC08121A950CA00D6EE18 /* NSView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537EC08021A950CA00D6EE18 /* NSView.swift */; }; + 537EC08321A9557400D6EE18 /* NSViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537EC08221A9557400D6EE18 /* NSViewSpec.swift */; }; 538DCB791DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */; }; 538DCB7A1DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */; }; 538DCB7B1DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */; }; @@ -485,6 +487,8 @@ 4EE637302090EFD300ECD02A /* UIViewControllerSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewControllerSpec.swift; sourceTree = ""; }; 531866F71DD7920400D1285F /* UIStepper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIStepper.swift; sourceTree = ""; }; 531866F91DD7925600D1285F /* UIStepperSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIStepperSpec.swift; sourceTree = ""; }; + 537EC08021A950CA00D6EE18 /* NSView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSView.swift; sourceTree = ""; }; + 537EC08221A9557400D6EE18 /* NSViewSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSViewSpec.swift; sourceTree = ""; }; 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSLayoutConstraint.swift; sourceTree = ""; }; 538DCB7C1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSLayoutConstraintSpec.swift; sourceTree = ""; }; 53A6BED11DD4BCA90016C058 /* MKMapView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MKMapView.swift; sourceTree = ""; }; @@ -838,17 +842,18 @@ isa = PBXGroup; children = ( 9AED64C41E496A3700321004 /* ActionProxy.swift */, + 006518751E26865800C3139A /* NSButton.swift */, 4ABEFE2F1DCFD0530066A8C2 /* NSCollectionView.swift */, 9ADE4A881DA6D206005C2AC8 /* NSControl.swift */, - 4ABEFE2A1DCFD0030066A8C2 /* NSTableView.swift */, - 9ADE4A951DA6F018005C2AC8 /* NSTextField.swift */, - 9A6AAA251DB8F5280013AAEA /* ReusableComponents.swift */, + 8392D8FC1DB93E5E00504ED4 /* NSImageView.swift */, D9558AB71DFF805A003254E1 /* NSPopUpButton.swift */, - 006518751E26865800C3139A /* NSButton.swift */, 834DE1111E4120340099F4E5 /* NSSegmentedControl.swift */, 834DE1131E4122910099F4E5 /* NSSlider.swift */, - 8392D8FC1DB93E5E00504ED4 /* NSImageView.swift */, + 4ABEFE2A1DCFD0030066A8C2 /* NSTableView.swift */, + 9ADE4A951DA6F018005C2AC8 /* NSTextField.swift */, 7A8BA0F91FCC86FC003241C7 /* NSTextView.swift */, + 537EC08021A950CA00D6EE18 /* NSView.swift */, + 9A6AAA251DB8F5280013AAEA /* ReusableComponents.swift */, ); path = AppKit; sourceTree = ""; @@ -947,13 +952,14 @@ isa = PBXGroup; children = ( 9A0726F21E912B610081F3F7 /* ActionProxySpec.swift */, + 004FD0061E26CDB300A03A82 /* NSButtonSpec.swift */, 4ABEFE311DCFD05F0066A8C2 /* NSCollectionViewSpec.swift */, 9ADE4A8D1DA6D965005C2AC8 /* NSControlSpec.swift */, + 834DE1001E4109750099F4E5 /* NSImageViewSpec.swift */, + D9558AB51DFF7B90003254E1 /* NSPopUpButtonSpec.swift */, 4ABEFE2C1DCFD0180066A8C2 /* NSTableViewSpec.swift */, + 537EC08221A9557400D6EE18 /* NSViewSpec.swift */, 9A6AAA271DB8F7EB0013AAEA /* ReusableComponentsSpec.swift */, - D9558AB51DFF7B90003254E1 /* NSPopUpButtonSpec.swift */, - 004FD0061E26CDB300A03A82 /* NSButtonSpec.swift */, - 834DE1001E4109750099F4E5 /* NSImageViewSpec.swift */, 9AADB6F31F84AECB00EFFD19 /* Swift4TestInteroperability.swift */, ); path = AppKit; @@ -1839,6 +1845,7 @@ 4ABEFE2B1DCFD0030066A8C2 /* NSTableView.swift in Sources */, CD0C45DE1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 4ABEFE301DCFD0530066A8C2 /* NSCollectionView.swift in Sources */, + 537EC08121A950CA00D6EE18 /* NSView.swift in Sources */, 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, 9AA0BD8F1DDE29F800531FCF /* NSObject+ObjCRuntime.swift in Sources */, 9A1D05E01D93E99100ACF44C /* NSObject+Association.swift in Sources */, @@ -1884,6 +1891,7 @@ 9ADE4A8F1DA6DA20005C2AC8 /* NSControlSpec.swift in Sources */, D0A2260E1A72F16D00D33B74 /* DynamicPropertySpec.swift in Sources */, 9AFCBFE31EB1ABC0004B4C74 /* KVOKVCExtensionSpec.swift in Sources */, + 537EC08321A9557400D6EE18 /* NSViewSpec.swift in Sources */, 9ADFE5A11DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */, 4ABEFE331DCFD0630066A8C2 /* NSCollectionViewSpec.swift in Sources */, B696FB811A7640C00075236D /* TestError.swift in Sources */, diff --git a/ReactiveCocoa/AppKit/NSView.swift b/ReactiveCocoa/AppKit/NSView.swift new file mode 100644 index 0000000000..172c048342 --- /dev/null +++ b/ReactiveCocoa/AppKit/NSView.swift @@ -0,0 +1,9 @@ +import ReactiveSwift +import AppKit +import enum Result.NoError + +extension Reactive where Base: NSView { + public var isHidden: BindingTarget { + return makeBindingTarget { $0.isHidden = $1 } + } +} diff --git a/ReactiveCocoaTests/AppKit/NSViewSpec.swift b/ReactiveCocoaTests/AppKit/NSViewSpec.swift new file mode 100644 index 0000000000..07320c65c0 --- /dev/null +++ b/ReactiveCocoaTests/AppKit/NSViewSpec.swift @@ -0,0 +1,28 @@ +import Quick +import Nimble +import Result +import ReactiveSwift +import ReactiveCocoa +import AppKit + +class NSViewSpec: QuickSpec { + override func spec() { + describe("NSView") { + var view: NSView! + + beforeEach { + view = NSView() + } + + it("should allow binding of `isHidden` property") { + let (hSignal, hSink) = Signal.pipe() + expect(view.isHidden).to(beFalse()) + + view.reactive.isHidden <~ hSignal + hSink.send(value: true) + + expect(view.isHidden).to(beTrue()) + } + } + } +} From 89e428d218cb4efcd27b2b2d18bbc3ce076873fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eimantas=20Vaic=CC=8Ciu=CC=84nas?= Date: Sat, 24 Nov 2018 12:04:04 +0200 Subject: [PATCH 0956/1028] Update `CHANGELOG.md` --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fa1033e52..61929a0df2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # master *Please put new entries at the top. +1. Add extension for `isHidden` property of `NSView` class. (#3621, kudos to @eimantas) # 8.0.2 1. ReactiveMapKit has now platform specific build targets and schemes. (#3625, kudos to @andersio) From fdc3e9b207d45f2788c148d95fb6b3e0917023dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eimantas=20Vaic=CC=8Ciu=CC=84nas?= Date: Sat, 24 Nov 2018 12:05:26 +0200 Subject: [PATCH 0957/1028] Set proper PR# --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61929a0df2..bb694ef12f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # master *Please put new entries at the top. -1. Add extension for `isHidden` property of `NSView` class. (#3621, kudos to @eimantas) +1. Add extension for `isHidden` property of `NSView` class. (#3634, kudos to @eimantas) # 8.0.2 1. ReactiveMapKit has now platform specific build targets and schemes. (#3625, kudos to @andersio) From 4976da53482e4b4c47eb6e44dbb4c3cb88e0e1ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eimantas=20Vaic=CC=8Ciu=CC=84nas?= Date: Mon, 26 Nov 2018 20:30:07 +0200 Subject: [PATCH 0958/1028] Add a little documentation for `NSView`'s bindings --- ReactiveCocoa/AppKit/NSView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/ReactiveCocoa/AppKit/NSView.swift b/ReactiveCocoa/AppKit/NSView.swift index 172c048342..28f7258398 100644 --- a/ReactiveCocoa/AppKit/NSView.swift +++ b/ReactiveCocoa/AppKit/NSView.swift @@ -3,6 +3,7 @@ import AppKit import enum Result.NoError extension Reactive where Base: NSView { + /// Sets the visibility of the view. public var isHidden: BindingTarget { return makeBindingTarget { $0.isHidden = $1 } } From 04716f23c28d22e8d451d74da7065ec8a3108ced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eimantas=20Vaic=CC=8Ciu=CC=84nas?= Date: Mon, 26 Nov 2018 20:53:53 +0200 Subject: [PATCH 0959/1028] Add bindings for `alphaValue` --- ReactiveCocoa/AppKit/NSView.swift | 5 +++++ ReactiveCocoaTests/AppKit/NSViewSpec.swift | 14 ++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoa/AppKit/NSView.swift b/ReactiveCocoa/AppKit/NSView.swift index 172c048342..7e0704f8ad 100644 --- a/ReactiveCocoa/AppKit/NSView.swift +++ b/ReactiveCocoa/AppKit/NSView.swift @@ -6,4 +6,9 @@ extension Reactive where Base: NSView { public var isHidden: BindingTarget { return makeBindingTarget { $0.isHidden = $1 } } + + /// Sets the alpha value of the view. + public var alphaValue: BindingTarget { + return makeBindingTarget { $0.alphaValue = $1 } + } } diff --git a/ReactiveCocoaTests/AppKit/NSViewSpec.swift b/ReactiveCocoaTests/AppKit/NSViewSpec.swift index 07320c65c0..e06af13f35 100644 --- a/ReactiveCocoaTests/AppKit/NSViewSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSViewSpec.swift @@ -16,12 +16,22 @@ class NSViewSpec: QuickSpec { it("should allow binding of `isHidden` property") { let (hSignal, hSink) = Signal.pipe() - expect(view.isHidden).to(beFalse()) + expect(view.isHidden) == false view.reactive.isHidden <~ hSignal hSink.send(value: true) - expect(view.isHidden).to(beTrue()) + expect(view.isHidden) == true + } + + it("should allow binding of `alphaValue` property") { + let (avSignal, avSink) = Signal.pipe() + expect(view.alphaValue) == 1.0 + + view.reactive.alphaValue <~ avSignal + avSink.send(value: 0.5) + + expect(view.alphaValue) == 0.5 } } } From 0018adbf02bf1d2af1b9e2451f3daa0610fe643e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eimantas=20Vaic=CC=8Ciu=CC=84nas?= Date: Mon, 26 Nov 2018 20:55:31 +0200 Subject: [PATCH 0960/1028] Update `CHANGELOG.md` --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb694ef12f..ed5655b69b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # master *Please put new entries at the top. +1. Add extension for `alphaValue` property of `NSView` class. (#3636, kuds to @eimantas) 1. Add extension for `isHidden` property of `NSView` class. (#3634, kudos to @eimantas) # 8.0.2 From deee083658e1fa79c3a070422c7ea1c5f48f9a45 Mon Sep 17 00:00:00 2001 From: Jonathan Baker Date: Wed, 23 Jan 2019 12:00:17 -0500 Subject: [PATCH 0961/1028] Remove UIApplicationSpec for now. --- ReactiveCocoa.xcodeproj/project.pbxproj | 6 ----- .../UIKit/UIApplicationSpec.swift | 25 ------------------- 2 files changed, 31 deletions(-) delete mode 100644 ReactiveCocoaTests/UIKit/UIApplicationSpec.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index a50d923916..780879cfda 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -35,8 +35,6 @@ 4ABEFE331DCFD0630066A8C2 /* NSCollectionViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE311DCFD05F0066A8C2 /* NSCollectionViewSpec.swift */; }; 4EE637362090F92600ECD02A /* UIApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE637352090F92600ECD02A /* UIApplication.swift */; }; 4EE637372090F92600ECD02A /* UIApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE637352090F92600ECD02A /* UIApplication.swift */; }; - 4EE637392090F9F600ECD02A /* UIApplicationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE637382090F9F600ECD02A /* UIApplicationSpec.swift */; }; - 4EE6373A2090F9F600ECD02A /* UIApplicationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE637382090F9F600ECD02A /* UIApplicationSpec.swift */; }; 531866F81DD7920400D1285F /* UIStepper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531866F71DD7920400D1285F /* UIStepper.swift */; }; 531866FA1DD7925600D1285F /* UIStepperSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531866F91DD7925600D1285F /* UIStepperSpec.swift */; }; 538DCB791DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */; }; @@ -413,7 +411,6 @@ 4ABEFE2F1DCFD0530066A8C2 /* NSCollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSCollectionView.swift; sourceTree = ""; }; 4ABEFE311DCFD05F0066A8C2 /* NSCollectionViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSCollectionViewSpec.swift; sourceTree = ""; }; 4EE637352090F92600ECD02A /* UIApplication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIApplication.swift; sourceTree = ""; }; - 4EE637382090F9F600ECD02A /* UIApplicationSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIApplicationSpec.swift; sourceTree = ""; }; 531866F71DD7920400D1285F /* UIStepper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIStepper.swift; sourceTree = ""; }; 531866F91DD7925600D1285F /* UIStepperSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIStepperSpec.swift; sourceTree = ""; }; 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSLayoutConstraint.swift; sourceTree = ""; }; @@ -754,7 +751,6 @@ children = ( 9A6AAA291DB8F7F10013AAEA /* ReusableComponentsSpec.swift */, 9A1D06241D93EA7E00ACF44C /* UIActivityIndicatorViewSpec.swift */, - 4EE637382090F9F600ECD02A /* UIApplicationSpec.swift */, 9A1D06251D93EA7E00ACF44C /* UIBarButtonItemSpec.swift */, 9A1D06261D93EA7E00ACF44C /* UIButtonSpec.swift */, 4ABEFE271DCFCFA90066A8C2 /* UICollectionViewSpec.swift */, @@ -1464,7 +1460,6 @@ 9AD841DE204C29B90040F0C0 /* MessageForwardingEntity.m in Sources */, A9EB3D2B1E94F3D9002A9BCC /* UITabBarItemSpec.swift in Sources */, 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */, - 4EE6373A2090F9F600ECD02A /* UIApplicationSpec.swift in Sources */, 9ADFE5A31DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */, 9A1D06411D93EA7E00ACF44C /* UIControlSpec.swift in Sources */, 9AFCBFE51EB1ABC0004B4C74 /* KVOKVCExtensionSpec.swift in Sources */, @@ -1684,7 +1679,6 @@ 3B30EE8D1E7BE529007CC8EF /* DeprecationsSpec.swift in Sources */, 9A9DFEEA1DA7EFB60039EE1B /* AssociationSpec.swift in Sources */, 9A54A2121DDF5B4D001739B3 /* InterceptingPerformanceTests.swift in Sources */, - 4EE637392090F9F600ECD02A /* UIApplicationSpec.swift in Sources */, 9ADFE5A21DBFFBCF001E11F7 /* LifetimeSpec.swift in Sources */, 9A1D06421D93EA7E00ACF44C /* UIDatePickerSpec.swift in Sources */, 53AC46CF1DD6FC0000C799E1 /* UISliderSpec.swift in Sources */, diff --git a/ReactiveCocoaTests/UIKit/UIApplicationSpec.swift b/ReactiveCocoaTests/UIKit/UIApplicationSpec.swift deleted file mode 100644 index 288d178f57..0000000000 --- a/ReactiveCocoaTests/UIKit/UIApplicationSpec.swift +++ /dev/null @@ -1,25 +0,0 @@ -import ReactiveSwift -import ReactiveCocoa -import UIKit -import Quick -import Nimble -import enum Result.NoError - -class UIApplicationSpec: QuickSpec { - override func spec() { - it("should accept changes from bindings to its applicationIconBadgeNumber value") { - let application = UIApplication.shared - - application.applicationIconBadgeNumber = 0 - - let (pipeSignal, observer) = Signal.pipe() - application.reactive.applicationIconBadgeNumber <~ pipeSignal - - observer.send(value: 1) - expect(application.applicationIconBadgeNumber) == 1 - - observer.send(value: 1337) - expect(application.applicationIconBadgeNumber) == 1337 - } - } -} From c9ae5c6e0ab4dddc81d29de8213e7de436d72054 Mon Sep 17 00:00:00 2001 From: Jonathan Baker Date: Thu, 24 Jan 2019 08:53:23 -0500 Subject: [PATCH 0962/1028] Update CHANGELOG.md --- CHANGELOG.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d37c67c206..49b1f7922a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # master *Please put new entries at the top. +1. Added `applicationIconBadgeNumber` binding target to `UIApplication` (#3589, kudos to @cocoahero). 1. Add extension for `alphaValue` property of `NSView` class. (#3636, kuds to @eimantas) 1. Add extension for `isHidden` property of `NSView` class. (#3634, kudos to @eimantas) @@ -22,9 +23,6 @@ 2. Added `title` binding target to `UIViewController` (#3588, kudos to @cocoahero). 3. Added several trigger signals for view lifecycle events to `UIViewController` (#3588, kudos to @cocoahero). -# 7.X.0 -1. Added `applicationIconBadgeNumber` binding target to `UIApplication` (#3589, kudos to @cocoahero). - # 7.2.0 1. Fixed a compilation issue related to [SR-7299](https://bugs.swift.org/browse/SR-7299). (#3580) From 8b5248753594125081a8f45600c258c3459b6f20 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 2 Feb 2019 16:02:53 +0000 Subject: [PATCH 0963/1028] Reenable disabled test cases. --- .../KeyValueObservingSpec+Swift4.swift | 27 +++++-------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/ReactiveCocoaTests/KeyValueObservingSpec+Swift4.swift b/ReactiveCocoaTests/KeyValueObservingSpec+Swift4.swift index 35baa74023..613d7c506b 100644 --- a/ReactiveCocoaTests/KeyValueObservingSpec+Swift4.swift +++ b/ReactiveCocoaTests/KeyValueObservingSpec+Swift4.swift @@ -51,8 +51,6 @@ class KeyValueObservingSwift4Spec: QuickSpec { expect(values) == [0] } - // NOTE: Xcode 9.0 b2 does not support optional chaining in key paths yet. - /* it("should send the initial value for weak nested key path") { let parentObject = NestedObservableObject() let innerObject = Optional(ObservableObject()) @@ -66,7 +64,6 @@ class KeyValueObservingSwift4Spec: QuickSpec { expect(values) == [0] } - */ itBehavesLike("a reactive key value observer using Swift 4 Smart Key Path") { ["observe": "producer"] @@ -123,22 +120,17 @@ fileprivate class KeyValueObservingSwift4SpecConfiguration: QuickConfiguration { return observe(object, \.rac_object.rac_value) } - // NOTE: Xcode 9.0 b2 does not support optional chaining in key paths yet. - // - // func weakNestedChanges(_ object: NestedObservableObject) -> SignalProducer { - // return observe(object, \.rac_weakObject?.rac_value) - // } + func weakNestedChanges(_ object: NestedObservableObject) -> SignalProducer { + return observe(object, \.rac_weakObject?.rac_value) + } func strongReferenceChanges(_ object: ObservableObject) -> SignalProducer { return observe(object, \.target) } - // NOTE: Compiler segfault with key path literals refering to weak - // properties. - // - // func weakReferenceChanges(_ object: ObservableObject) -> SignalProducer { - // return observe(object, \.weakTarget) - //} + func weakReferenceChanges(_ object: ObservableObject) -> SignalProducer { + return observe(object, \.weakTarget) + } func dependentKeyChanges(_ object: ObservableObject) -> SignalProducer { return observe(object, \.rac_value_plusOne) @@ -285,7 +277,6 @@ fileprivate class KeyValueObservingSwift4SpecConfiguration: QuickConfiguration { // NOTE: Compiler segfault with key path literals refering to weak // properties. - /* it("should emit a `nil` when the key path is being cleared due to the deallocation of the Objective-C object it held.") { let object = ObservableObject() let null = ObjectIdentifier(NSNull()) @@ -343,7 +334,6 @@ fileprivate class KeyValueObservingSwift4SpecConfiguration: QuickConfiguration { expect(ids) == [tokenId, null] expect(object.weakTarget).to(beNil()) } - */ } describe("nested key paths") { @@ -371,13 +361,11 @@ fileprivate class KeyValueObservingSwift4SpecConfiguration: QuickConfiguration { expect(values) == [1, 0, 10] } - // NOTE: Xcode 9.0 b2 does not support optional chaining in key paths yet. - /* it("should observe changes in a nested weak key path") { let parentObject = NestedObservableObject() var innerObject = Optional(ObservableObject()) parentObject.rac_weakObject = innerObject - var values: [Int] = [] + var values: [Int?] = [] context.weakNestedChanges(parentObject).startWithValues { values.append($0) @@ -402,7 +390,6 @@ fileprivate class KeyValueObservingSwift4SpecConfiguration: QuickConfiguration { innerObject?.rac_value = 10 expect(values) == [1, 0, 10] } - */ it("should not retain replaced value in a nested key path") { // NOTE: The producer version of this test cases somehow From fecd084032630be5fe3cd3c8e84ffa9d15712923 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 2 Feb 2019 16:17:55 +0000 Subject: [PATCH 0964/1028] Fix build configurations of ReactiveMapKit. --- ReactiveCocoa.xcodeproj/project.pbxproj | 99 +++++++++++-------------- 1 file changed, 45 insertions(+), 54 deletions(-) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 90374bef20..7f679dd37f 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -33,12 +33,12 @@ 4ABEFE2E1DCFD01F0066A8C2 /* NSTableViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE2C1DCFD0180066A8C2 /* NSTableViewSpec.swift */; }; 4ABEFE301DCFD0530066A8C2 /* NSCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE2F1DCFD0530066A8C2 /* NSCollectionView.swift */; }; 4ABEFE331DCFD0630066A8C2 /* NSCollectionViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE311DCFD05F0066A8C2 /* NSCollectionViewSpec.swift */; }; - 4EE637362090F92600ECD02A /* UIApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE637352090F92600ECD02A /* UIApplication.swift */; }; - 4EE637372090F92600ECD02A /* UIApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE637352090F92600ECD02A /* UIApplication.swift */; }; 4EE6372E2090EEFA00ECD02A /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE6372D2090EEFA00ECD02A /* UIViewController.swift */; }; 4EE6372F2090EEFA00ECD02A /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE6372D2090EEFA00ECD02A /* UIViewController.swift */; }; 4EE637332090EFDD00ECD02A /* UIViewControllerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE637302090EFD300ECD02A /* UIViewControllerSpec.swift */; }; 4EE637342090EFDF00ECD02A /* UIViewControllerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE637302090EFD300ECD02A /* UIViewControllerSpec.swift */; }; + 4EE637362090F92600ECD02A /* UIApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE637352090F92600ECD02A /* UIApplication.swift */; }; + 4EE637372090F92600ECD02A /* UIApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE637352090F92600ECD02A /* UIApplication.swift */; }; 531866F81DD7920400D1285F /* UIStepper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531866F71DD7920400D1285F /* UIStepper.swift */; }; 531866FA1DD7925600D1285F /* UIStepperSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531866F91DD7925600D1285F /* UIStepperSpec.swift */; }; 537EC08121A950CA00D6EE18 /* NSView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537EC08021A950CA00D6EE18 /* NSView.swift */; }; @@ -53,6 +53,9 @@ 53AC46CF1DD6FC0000C799E1 /* UISliderSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53AC46CE1DD6FC0000C799E1 /* UISliderSpec.swift */; }; 57A4D2081BA13D7A00F7D4B1 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; 57A4D20A1BA13D7A00F7D4B1 /* ReactiveCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 654DE7B02205F9DE0048FE14 /* ReactiveCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D04725EA19E49ED7006002AA /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 654DE7B22205FA0A0048FE14 /* ReactiveCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D047260C19E49F82006002AA /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 654DE7B32205FA200048FE14 /* ReactiveCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 7A8BA0FA1FCC86FC003241C7 /* NSTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8BA0F91FCC86FC003241C7 /* NSTextView.swift */; }; 7DFBED081CDB8C9500EE435B /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */; }; 7DFBED141CDB8CE600EE435B /* test-data.json in Resources */ = {isa = PBXBuildFile; fileRef = D03766B119EDA60000A782A9 /* test-data.json */; }; @@ -67,9 +70,7 @@ 834DE1141E4122910099F4E5 /* NSSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 834DE1131E4122910099F4E5 /* NSSlider.swift */; }; 8392D8FD1DB93E5E00504ED4 /* NSImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8392D8FC1DB93E5E00504ED4 /* NSImageView.swift */; }; 9A0726F31E912B610081F3F7 /* ActionProxySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A0726F21E912B610081F3F7 /* ActionProxySpec.swift */; }; - 9A1675511F80C41F00B63650 /* ReactiveCocoa.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = D04725EA19E49ED7006002AA /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 9A16755D1F80DE1C00B63650 /* MKMapViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED51DD4BD2C0016C058 /* MKMapViewSpec.swift */; }; - 9A16755F1F80DE3900B63650 /* ReactiveMapKit.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = 9AC03A571F7CC3BF00EC33C1 /* ReactiveMapKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 9A1D05E01D93E99100ACF44C /* NSObject+Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */; }; 9A1D05E11D93E99100ACF44C /* NSObject+Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */; }; 9A1D05E21D93E99100ACF44C /* NSObject+Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */; }; @@ -168,11 +169,7 @@ 9A73DFBF216D3C570069AD76 /* MKMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED11DD4BCA90016C058 /* MKMapView.swift */; }; 9A73DFC0216D3C570069AD76 /* MKLocalSearchRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91244E720389AEA0001BBCB /* MKLocalSearchRequest.swift */; }; 9A73DFE3216D3CEB0069AD76 /* MKMapViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED51DD4BD2C0016C058 /* MKMapViewSpec.swift */; }; - 9A73DFEF216D3CEB0069AD76 /* ReactiveCocoa.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = D04725EA19E49ED7006002AA /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 9A73DFF2216D3CEB0069AD76 /* ReactiveMapKit.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = 9AC03A571F7CC3BF00EC33C1 /* ReactiveMapKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 9A73DFFE216D3CEE0069AD76 /* MKMapViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED51DD4BD2C0016C058 /* MKMapViewSpec.swift */; }; - 9A73E00A216D3CEE0069AD76 /* ReactiveCocoa.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = D04725EA19E49ED7006002AA /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 9A73E00D216D3CEE0069AD76 /* ReactiveMapKit.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = 9AC03A571F7CC3BF00EC33C1 /* ReactiveMapKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 9A73E015216D3E660069AD76 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D04725EA19E49ED7006002AA /* ReactiveCocoa.framework */; }; 9A73E01C216D3E700069AD76 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D047260C19E49F82006002AA /* ReactiveCocoa.framework */; }; 9A73E030216D3FAF0069AD76 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; @@ -402,54 +399,48 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ - 7DFBED151CDB8CEC00EE435B /* Copy Frameworks */ = { + 654DE7AE2205F9C30048FE14 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( - 7DFBED211CDB8D8300EE435B /* Result.framework in Copy Frameworks */, - 7DFBED201CDB8D7D00EE435B /* Nimble.framework in Copy Frameworks */, - 7DFBED1F1CDB8D7800EE435B /* Quick.framework in Copy Frameworks */, - 7DFBED1E1CDB8D7000EE435B /* ReactiveCocoa.framework in Copy Frameworks */, + 654DE7B32205FA200048FE14 /* ReactiveCocoa.framework in CopyFiles */, ); - name = "Copy Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; - 9A16754A1F80C3AC00B63650 /* Copy Files */ = { + 654DE7AF2205F9D60048FE14 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( - 9A1675511F80C41F00B63650 /* ReactiveCocoa.framework in Copy Files */, - 9A16755F1F80DE3900B63650 /* ReactiveMapKit.framework in Copy Files */, + 654DE7B02205F9DE0048FE14 /* ReactiveCocoa.framework in CopyFiles */, ); - name = "Copy Files"; runOnlyForDeploymentPostprocessing = 0; }; - 9A73DFEC216D3CEB0069AD76 /* Copy Files */ = { + 654DE7B12205F9F60048FE14 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( - 9A73DFEF216D3CEB0069AD76 /* ReactiveCocoa.framework in Copy Files */, - 9A73DFF2216D3CEB0069AD76 /* ReactiveMapKit.framework in Copy Files */, + 654DE7B22205FA0A0048FE14 /* ReactiveCocoa.framework in CopyFiles */, ); - name = "Copy Files"; runOnlyForDeploymentPostprocessing = 0; }; - 9A73E007216D3CEE0069AD76 /* Copy Files */ = { + 7DFBED151CDB8CEC00EE435B /* Copy Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( - 9A73E00A216D3CEE0069AD76 /* ReactiveCocoa.framework in Copy Files */, - 9A73E00D216D3CEE0069AD76 /* ReactiveMapKit.framework in Copy Files */, + 7DFBED211CDB8D8300EE435B /* Result.framework in Copy Frameworks */, + 7DFBED201CDB8D7D00EE435B /* Nimble.framework in Copy Frameworks */, + 7DFBED1F1CDB8D7800EE435B /* Quick.framework in Copy Frameworks */, + 7DFBED1E1CDB8D7000EE435B /* ReactiveCocoa.framework in Copy Frameworks */, ); - name = "Copy Files"; + name = "Copy Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; D01B7B6119EDD8F600D26E01 /* Copy Frameworks */ = { @@ -485,9 +476,9 @@ 4ABEFE2C1DCFD0180066A8C2 /* NSTableViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSTableViewSpec.swift; sourceTree = ""; }; 4ABEFE2F1DCFD0530066A8C2 /* NSCollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSCollectionView.swift; sourceTree = ""; }; 4ABEFE311DCFD05F0066A8C2 /* NSCollectionViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSCollectionViewSpec.swift; sourceTree = ""; }; - 4EE637352090F92600ECD02A /* UIApplication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIApplication.swift; sourceTree = ""; }; 4EE6372D2090EEFA00ECD02A /* UIViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = ""; }; 4EE637302090EFD300ECD02A /* UIViewControllerSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewControllerSpec.swift; sourceTree = ""; }; + 4EE637352090F92600ECD02A /* UIApplication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIApplication.swift; sourceTree = ""; }; 531866F71DD7920400D1285F /* UIStepper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIStepper.swift; sourceTree = ""; }; 531866F91DD7925600D1285F /* UIStepperSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIStepperSpec.swift; sourceTree = ""; }; 537EC08021A950CA00D6EE18 /* NSView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSView.swift; sourceTree = ""; }; @@ -1301,7 +1292,7 @@ 9A1675391F80C35100B63650 /* Sources */, 9A16753A1F80C35100B63650 /* Frameworks */, 9A16753B1F80C35100B63650 /* Resources */, - 9A16754A1F80C3AC00B63650 /* Copy Files */, + 654DE7B12205F9F60048FE14 /* CopyFiles */, ); buildRules = ( ); @@ -1356,7 +1347,7 @@ 9A73DFE2216D3CEB0069AD76 /* Sources */, 9A73DFE4216D3CEB0069AD76 /* Frameworks */, 9A73DFEB216D3CEB0069AD76 /* Resources */, - 9A73DFEC216D3CEB0069AD76 /* Copy Files */, + 654DE7AF2205F9D60048FE14 /* CopyFiles */, ); buildRules = ( ); @@ -1375,7 +1366,7 @@ 9A73DFFD216D3CEE0069AD76 /* Sources */, 9A73DFFF216D3CEE0069AD76 /* Frameworks */, 9A73E006216D3CEE0069AD76 /* Resources */, - 9A73E007216D3CEE0069AD76 /* Copy Files */, + 654DE7AE2205F9C30048FE14 /* CopyFiles */, ); buildRules = ( ); @@ -2159,7 +2150,7 @@ }; 9A1675461F80C35100B63650 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047262E19E49FE8006002AA /* Application.xcconfig */; + baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; @@ -2171,7 +2162,7 @@ }; 9A1675471F80C35100B63650 /* Test */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047262E19E49FE8006002AA /* Application.xcconfig */; + baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; @@ -2183,7 +2174,7 @@ }; 9A1675481F80C35100B63650 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047262E19E49FE8006002AA /* Application.xcconfig */; + baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; @@ -2195,7 +2186,7 @@ }; 9A1675491F80C35100B63650 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047262E19E49FE8006002AA /* Application.xcconfig */; + baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; @@ -2207,7 +2198,7 @@ }; 9A73DFB7216D3C550069AD76 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047262F19E49FE8006002AA /* Framework.xcconfig */; + baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -2220,7 +2211,7 @@ }; 9A73DFB8216D3C550069AD76 /* Test */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047262F19E49FE8006002AA /* Framework.xcconfig */; + baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -2233,7 +2224,7 @@ }; 9A73DFB9216D3C550069AD76 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047262F19E49FE8006002AA /* Framework.xcconfig */; + baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -2246,7 +2237,7 @@ }; 9A73DFBA216D3C550069AD76 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047262F19E49FE8006002AA /* Framework.xcconfig */; + baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -2259,7 +2250,7 @@ }; 9A73DFC8216D3C570069AD76 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047262F19E49FE8006002AA /* Framework.xcconfig */; + baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -2272,7 +2263,7 @@ }; 9A73DFC9216D3C570069AD76 /* Test */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047262F19E49FE8006002AA /* Framework.xcconfig */; + baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -2285,7 +2276,7 @@ }; 9A73DFCA216D3C570069AD76 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047262F19E49FE8006002AA /* Framework.xcconfig */; + baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -2298,7 +2289,7 @@ }; 9A73DFCB216D3C570069AD76 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047262F19E49FE8006002AA /* Framework.xcconfig */; + baseConfigurationReference = 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */; buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -2311,7 +2302,7 @@ }; 9A73DFF4216D3CEB0069AD76 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047262E19E49FE8006002AA /* Application.xcconfig */; + baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; @@ -2323,7 +2314,7 @@ }; 9A73DFF5216D3CEB0069AD76 /* Test */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047262E19E49FE8006002AA /* Application.xcconfig */; + baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; @@ -2335,7 +2326,7 @@ }; 9A73DFF6216D3CEB0069AD76 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047262E19E49FE8006002AA /* Application.xcconfig */; + baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; @@ -2347,7 +2338,7 @@ }; 9A73DFF7216D3CEB0069AD76 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047262E19E49FE8006002AA /* Application.xcconfig */; + baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; @@ -2359,7 +2350,7 @@ }; 9A73E00F216D3CEE0069AD76 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047262E19E49FE8006002AA /* Application.xcconfig */; + baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; @@ -2371,7 +2362,7 @@ }; 9A73E010216D3CEE0069AD76 /* Test */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047262E19E49FE8006002AA /* Application.xcconfig */; + baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; @@ -2383,7 +2374,7 @@ }; 9A73E011216D3CEE0069AD76 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047262E19E49FE8006002AA /* Application.xcconfig */; + baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; @@ -2395,7 +2386,7 @@ }; 9A73E012216D3CEE0069AD76 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047262E19E49FE8006002AA /* Application.xcconfig */; + baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; @@ -2407,7 +2398,7 @@ }; 9AC03A5D1F7CC3BF00EC33C1 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047262F19E49FE8006002AA /* Framework.xcconfig */; + baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -2420,7 +2411,7 @@ }; 9AC03A5E1F7CC3BF00EC33C1 /* Test */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047262F19E49FE8006002AA /* Framework.xcconfig */; + baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -2433,7 +2424,7 @@ }; 9AC03A5F1F7CC3BF00EC33C1 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047262F19E49FE8006002AA /* Framework.xcconfig */; + baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -2446,7 +2437,7 @@ }; 9AC03A601F7CC3BF00EC33C1 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047262F19E49FE8006002AA /* Framework.xcconfig */; + baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; From a621cc2a55a56591a4478beff64b2c8b63e880f9 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 2 Feb 2019 16:22:21 +0000 Subject: [PATCH 0965/1028] Move the changelog entry to the appropriate place. --- CHANGELOG.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 950c7862df..ef9ba2056c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # master *Please put new entries at the top. +1. KVO observations can now be made with Smart Key Path in Swift 3.2+, using `producer(for:)` and `signal(for:)` available on `NSObject.reactive`. (#3491, kudos to @andersio) 1. Added `applicationIconBadgeNumber` binding target to `UIApplication` (#3589, kudos to @cocoahero). 1. Add extension for `alphaValue` property of `NSView` class. (#3636, kuds to @eimantas) 1. Add extension for `isHidden` property of `NSView` class. (#3634, kudos to @eimantas) @@ -89,11 +90,7 @@ # 6.0.2 1. Disabled code coverage data to allow app submissions with Xcode 9.0 (see https://github.com/Carthage/Carthage/issues/2056, kudos to @NachoSoto) -1. KVO observations can now be made with Smart Key Path in Swift 3.2+, using `producer(for:)` and `signal(for:)` available on `NSObject.reactive`. (#3491, kudos to @andersio) - - # 6.0.1 - 1. [Xcode 9 beta 5] Fixed an issue causing infinite recursion in the Swift runtime. (#3498, kudos to @andersio) # 6.0.0 From 38aa40906f95ede471ab82623e48a194e632e062 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Mon, 4 Feb 2019 17:55:00 +0800 Subject: [PATCH 0966/1028] Use Xcode 10.1 for CI. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4914342a87..c329280f7b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: objective-c -osx_image: xcode10 +osx_image: xcode10.1 before_install: true install: true branches: @@ -14,7 +14,7 @@ cache: jobs: include: - stage: unit tests - osx_image: xcode10 + osx_image: xcode10.1 script: - XCODE_SCHEME=ReactiveCocoa-macOS XCODE_SDK=macosx From c21e6e1a96f532449017ed035df40e84407a41cd Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Mon, 4 Feb 2019 22:07:17 +0800 Subject: [PATCH 0967/1028] Fix the KVO test case which cannot be run in isolation. --- ReactiveCocoaTests/KeyValueObservingSpec.swift | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/ReactiveCocoaTests/KeyValueObservingSpec.swift b/ReactiveCocoaTests/KeyValueObservingSpec.swift index cc5a36600c..71cf505086 100644 --- a/ReactiveCocoaTests/KeyValueObservingSpec.swift +++ b/ReactiveCocoaTests/KeyValueObservingSpec.swift @@ -433,20 +433,19 @@ fileprivate class KeyValueObservingSpecConfiguration: QuickConfiguration { } it("should not retain replaced value in a nested key path") { - // NOTE: The producer version of this test cases somehow - // fails when the spec is being run alone. + weak var weakOriginalInner: ObservableObject? let parentObject = NestedObservableObject() - weak var weakOriginalInner: ObservableObject? = parentObject.rac_object - expect(weakOriginalInner).toNot(beNil()) - autoreleasepool { + parentObject.rac_object = ObservableObject() + weakOriginalInner = parentObject.rac_object + + expect(weakOriginalInner).toNot(beNil()) + _ = context .nestedChanges(parentObject) .start() - } - autoreleasepool { parentObject.rac_object = ObservableObject() } From aaaedbc9695d78991f341503881cd712ae8be816 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Tue, 5 Feb 2019 16:36:31 +0800 Subject: [PATCH 0968/1028] Fixed an intermittent crash in a ObjC-ported KVO stress test. --- ReactiveCocoaTests/KeyValueObservingSpec.swift | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoaTests/KeyValueObservingSpec.swift b/ReactiveCocoaTests/KeyValueObservingSpec.swift index 71cf505086..b1a89ac23f 100644 --- a/ReactiveCocoaTests/KeyValueObservingSpec.swift +++ b/ReactiveCocoaTests/KeyValueObservingSpec.swift @@ -1,6 +1,6 @@ import Foundation @testable import ReactiveCocoa -import ReactiveSwift +@testable import ReactiveSwift import enum Result.NoError import Quick import Nimble @@ -596,9 +596,10 @@ fileprivate class KeyValueObservingSpecConfiguration: QuickConfiguration { expect(atomicCounter).toEventually(equal(Int64(numIterations * 2)), timeout: 30.0) } - // ReactiveCocoa/ReactiveCocoa#1122 + // Direct port of https://github.com/ReactiveCocoa/ReactiveObjC/blob/3.1.0/ReactiveObjCTests/RACKVOProxySpec.m#L196 it("async disposal of observer") { let serialDisposable = SerialDisposable() + let lock = Lock.make() iterationQueue.async { DispatchQueue.concurrentPerform(iterations: numIterations) { index in @@ -608,7 +609,11 @@ fileprivate class KeyValueObservingSpecConfiguration: QuickConfiguration { serialDisposable.inner = disposable concurrentQueue.async { + // TestObject in the ObjC version has manual getter, setter and KVO notification. Here + // we just wrap the call with a `Lock` to emulate the effect. + lock.lock() testObject.rac_value = index + lock.unlock() } } } @@ -618,6 +623,7 @@ fileprivate class KeyValueObservingSpecConfiguration: QuickConfiguration { } } + // Direct port of https://github.com/ReactiveCocoa/ReactiveObjC/blob/3.1.0/ReactiveObjCTests/RACKVOProxySpec.m#L196 it("async disposal of signal with in-flight changes") { let otherScheduler: QueueScheduler From 65ca56b9a5095312cc99775613c8b13e4e316215 Mon Sep 17 00:00:00 2001 From: Marco Cancellieri Date: Fri, 15 Mar 2019 14:33:29 +0100 Subject: [PATCH 0969/1028] Update ReactiveSwift to 5.0 --- CHANGELOG.md | 1 + Cartfile | 2 +- Cartfile.resolved | 4 ++-- Carthage/Checkouts/ReactiveSwift | 2 +- Carthage/Checkouts/Result | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49b1f7922a..da19c3a4c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # master *Please put new entries at the top. +1. Update ReactiveSwift to 5.0. 1. Added `applicationIconBadgeNumber` binding target to `UIApplication` (#3589, kudos to @cocoahero). 1. Add extension for `alphaValue` property of `NSView` class. (#3636, kuds to @eimantas) 1. Add extension for `isHidden` property of `NSView` class. (#3634, kudos to @eimantas) diff --git a/Cartfile b/Cartfile index f73f84fd6a..e6233eb34c 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" ~> 4.0 +github "ReactiveCocoa/ReactiveSwift" ~> 5.0 diff --git a/Cartfile.resolved b/Cartfile.resolved index d42e390188..a206c114e2 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,5 +1,5 @@ github "Quick/Nimble" "v7.1.3" github "Quick/Quick" "v1.3.1" -github "ReactiveCocoa/ReactiveSwift" "4.0.0" -github "antitypical/Result" "4.0.0" +github "ReactiveCocoa/ReactiveSwift" "5.0.0" +github "antitypical/Result" "4.1.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index 4f6a12ae67..e285cea17c 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit 4f6a12ae6762e825b0e19a4f7076eafa43847e6e +Subproject commit e285cea17cca8e23928d1e85a235638184e39ac9 diff --git a/Carthage/Checkouts/Result b/Carthage/Checkouts/Result index 8fc088dcf7..2ca499ba45 160000 --- a/Carthage/Checkouts/Result +++ b/Carthage/Checkouts/Result @@ -1 +1 @@ -Subproject commit 8fc088dcf72802801efeecba76ea8fb041fb773d +Subproject commit 2ca499ba456795616fbc471561ff1d963e6ae160 From 4b09c3fe776dae4888d8863b704c5d8d66b8b964 Mon Sep 17 00:00:00 2001 From: Christopher Liscio Date: Fri, 15 Mar 2019 11:51:48 -0400 Subject: [PATCH 0970/1028] Fix use of deprecated functions, other warnings --- Carthage/Checkouts/Nimble | 2 +- Carthage/Checkouts/Quick | 2 +- ReactiveCocoa.xcodeproj/project.pbxproj | 38 ++++++++++++++----- .../xcschemes/ReactiveCocoa-iOS.xcscheme | 2 +- .../xcschemes/ReactiveCocoa-macOS.xcscheme | 2 +- .../xcschemes/ReactiveCocoa-tvOS.xcscheme | 2 +- .../xcschemes/ReactiveCocoa-watchOS.xcscheme | 2 +- .../xcschemes/ReactiveMapKit-iOS.xcscheme | 2 +- .../xcschemes/ReactiveMapKit-macOS.xcscheme | 2 +- .../xcschemes/ReactiveMapKit-tvOS.xcscheme | 2 +- ReactiveCocoa/AnyObject+Lifetime.swift | 4 +- ReactiveCocoa/NSObject+Intercepting.swift | 11 +++--- .../NSObject+KeyValueObserving.swift | 8 ++-- ReactiveCocoa/ObjC+Selector.swift | 4 +- 14 files changed, 50 insertions(+), 33 deletions(-) diff --git a/Carthage/Checkouts/Nimble b/Carthage/Checkouts/Nimble index 9c1379fdcd..43304bf2b1 160000 --- a/Carthage/Checkouts/Nimble +++ b/Carthage/Checkouts/Nimble @@ -1 +1 @@ -Subproject commit 9c1379fdcd58c4f2278aea5e029394ba9a2b8f07 +Subproject commit 43304bf2b1579fd555f2fdd51742771c1e4f2b98 diff --git a/Carthage/Checkouts/Quick b/Carthage/Checkouts/Quick index b060679e70..0b4ed6c706 160000 --- a/Carthage/Checkouts/Quick +++ b/Carthage/Checkouts/Quick @@ -1 +1 @@ -Subproject commit b060679e70d13c3c7dcd124201b5b1b34ce6f340 +Subproject commit 0b4ed6c706dd0cce923b5019a605a9bcc6b1b600 diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 7f679dd37f..129e6717ad 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -1494,52 +1494,66 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0900; - LastUpgradeCheck = 0940; + LastUpgradeCheck = 1020; ORGANIZATIONNAME = GitHub; TargetAttributes = { 57A4D1AF1BA13D7A00F7D4B1 = { - LastSwiftMigration = 0810; + LastSwiftMigration = 1020; }; 7DFBED021CDB8C9500EE435B = { CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 0920; + LastSwiftMigration = 1020; }; 9A16753C1F80C35100B63650 = { CreatedOnToolsVersion = 9.0; - LastSwiftMigration = 0920; + LastSwiftMigration = 1020; ProvisioningStyle = Automatic; }; + 9A73DFAC216D3C550069AD76 = { + LastSwiftMigration = 1020; + }; + 9A73DFBD216D3C570069AD76 = { + LastSwiftMigration = 1020; + }; + 9A73DFDF216D3CEB0069AD76 = { + LastSwiftMigration = 1020; + }; + 9A73DFFA216D3CEE0069AD76 = { + LastSwiftMigration = 1020; + }; 9AC03A561F7CC3BF00EC33C1 = { CreatedOnToolsVersion = 9.0; + LastSwiftMigration = 1020; }; A9B315531B3940610001CB9C = { CreatedOnToolsVersion = 7.0; - LastSwiftMigration = 0810; + LastSwiftMigration = 1020; }; D04725E919E49ED7006002AA = { CreatedOnToolsVersion = 6.1; - LastSwiftMigration = 0810; + LastSwiftMigration = 1020; }; D04725F419E49ED7006002AA = { CreatedOnToolsVersion = 6.1; - LastSwiftMigration = 0920; + LastSwiftMigration = 1020; }; D047260B19E49F82006002AA = { CreatedOnToolsVersion = 6.1; - LastSwiftMigration = 0810; + LastSwiftMigration = 1020; }; D047261519E49F82006002AA = { CreatedOnToolsVersion = 6.1; - LastSwiftMigration = 0920; + LastSwiftMigration = 1020; }; }; }; buildConfigurationList = D04725E419E49ED7006002AA /* Build configuration list for PBXProject "ReactiveCocoa" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, + Base, ); mainGroup = D04725E019E49ED7006002AA; productRefGroup = D04725EB19E49ED7006002AA /* Products */; @@ -2502,6 +2516,7 @@ buildSettings = { APPLETVOS_DEPLOYMENT_TARGET = 9.0; BITCODE_GENERATION_MODE = bitcode; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -2527,6 +2542,7 @@ buildSettings = { APPLETVOS_DEPLOYMENT_TARGET = 9.0; BITCODE_GENERATION_MODE = bitcode; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -2641,6 +2657,7 @@ buildSettings = { APPLETVOS_DEPLOYMENT_TARGET = 9.0; BITCODE_GENERATION_MODE = bitcode; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -2709,6 +2726,7 @@ buildSettings = { APPLETVOS_DEPLOYMENT_TARGET = 9.0; BITCODE_GENERATION_MODE = bitcode; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; diff --git a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme index 37e90d8e80..fd7a186857 100644 --- a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme +++ b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme @@ -1,6 +1,6 @@ Lifetime { + static func of(_ object: AnyObject) -> Lifetime { if let object = object as? NSObject { return .of(object) } @@ -46,7 +46,7 @@ public extension Lifetime { /// - object: The object for which the lifetime is obtained. /// /// - returns: The lifetime ends when the given object is deinitialized. - public static func of(_ object: NSObject) -> Lifetime { + static func of(_ object: NSObject) -> Lifetime { return synchronized(object) { if let lifetime = object.associations.value(forKey: lifetimeKey) { return lifetime diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index 963a7b100e..df3a70e190 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -407,11 +407,10 @@ private func unpackInvocation(_ invocation: AnyObject) -> [Any?] { let encoding = ObjCTypeEncoding(rawValue: rawEncoding.pointee) ?? .undefined func extract(_ type: U.Type) -> U { - let pointer = UnsafeMutableRawPointer.allocate(bytes: MemoryLayout.size, - alignedTo: MemoryLayout.alignment) + let pointer = UnsafeMutableRawPointer.allocate(byteCount: MemoryLayout.size, + alignment: MemoryLayout.alignment) defer { - pointer.deallocate(bytes: MemoryLayout.size, - alignedTo: MemoryLayout.alignment) + pointer.deallocate() } invocation.objcCopy(to: pointer, forArgumentAt: Int(position)) @@ -456,8 +455,8 @@ private func unpackInvocation(_ invocation: AnyObject) -> [Any?] { case .undefined: var size = 0, alignment = 0 NSGetSizeAndAlignment(rawEncoding, &size, &alignment) - let buffer = UnsafeMutableRawPointer.allocate(bytes: size, alignedTo: alignment) - defer { buffer.deallocate(bytes: size, alignedTo: alignment) } + let buffer = UnsafeMutableRawPointer.allocate(byteCount: size, alignment: alignment) + defer { buffer.deallocate() } invocation.objcCopy(to: buffer, forArgumentAt: Int(position)) value = NSValue(bytes: buffer, objCType: rawEncoding) diff --git a/ReactiveCocoa/NSObject+KeyValueObserving.swift b/ReactiveCocoa/NSObject+KeyValueObserving.swift index 489948d92a..a3d7bdcc4c 100644 --- a/ReactiveCocoa/NSObject+KeyValueObserving.swift +++ b/ReactiveCocoa/NSObject+KeyValueObserving.swift @@ -123,7 +123,7 @@ extension BindingTarget { internal final class KeyValueObserver: NSObject { typealias Action = (_ object: AnyObject?) -> Void - private static let context = UnsafeMutableRawPointer.allocate(bytes: 1, alignedTo: 0) + private static let context = UnsafeMutableRawPointer.allocate(byteCount: 1, alignment: 0) unowned(unsafe) let unsafeObject: NSObject let key: String @@ -367,7 +367,7 @@ internal struct PropertyAttributes { objectClass = objc_getClass(name) as! AnyClass? name.deinitialize(count: length + 1) - name.deallocate(capacity: length + 1) + name.deallocate() } } @@ -379,8 +379,8 @@ internal struct PropertyAttributes { let emptyString = UnsafeMutablePointer.allocate(capacity: 1) emptyString.initialize(to: Code.nul) defer { - emptyString.deinitialize() - emptyString.deallocate(capacity: 1) + emptyString.deinitialize(count: 1) + emptyString.deallocate() } var isWeak = false diff --git a/ReactiveCocoa/ObjC+Selector.swift b/ReactiveCocoa/ObjC+Selector.swift index f2d126e682..b34dce9e71 100644 --- a/ReactiveCocoa/ObjC+Selector.swift +++ b/ReactiveCocoa/ObjC+Selector.swift @@ -30,8 +30,8 @@ extension Selector { let cString = UnsafeMutablePointer.allocate(capacity: prefixedLength + 1) defer { - cString.deinitialize() - cString.deallocate(capacity: prefixedLength + 1) + cString.deinitialize(count: prefixedLength + 1) + cString.deallocate() } cString.initialize(from: asciiPrefix, count: prefix.utf8CodeUnitCount) From 5364056a5f0fb6a208a0144bced237276c85213f Mon Sep 17 00:00:00 2001 From: Christopher Liscio Date: Fri, 15 Mar 2019 11:58:25 -0400 Subject: [PATCH 0971/1028] Update CHANGELOG, bump test deployment target The test deployment targets were bumped because of changes to the Quick & Nimble deployment targets. --- CHANGELOG.md | 2 ++ ReactiveCocoa.xcodeproj/project.pbxproj | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index da19c3a4c9..c5d330d262 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # master *Please put new entries at the top. +1. Fix warnings for deprecated use of Swift's allocate/deallocate methods. +1. Update Quick (2.0.0) and Nimble (8.0.1). 1. Update ReactiveSwift to 5.0. 1. Added `applicationIconBadgeNumber` binding target to `UIApplication` (#3589, kudos to @cocoahero). 1. Add extension for `alphaValue` property of `NSView` class. (#3636, kuds to @eimantas) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 129e6717ad..6a0fa6c100 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -2320,6 +2320,7 @@ buildSettings = { INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; PRODUCT_NAME = ReactiveMapKitTests; SDKROOT = macosx; @@ -2332,6 +2333,7 @@ buildSettings = { INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; PRODUCT_NAME = ReactiveMapKitTests; SDKROOT = macosx; @@ -2344,6 +2346,7 @@ buildSettings = { INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; PRODUCT_NAME = ReactiveMapKitTests; SDKROOT = macosx; @@ -2356,6 +2359,7 @@ buildSettings = { INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; PRODUCT_NAME = ReactiveMapKitTests; SDKROOT = macosx; @@ -2592,6 +2596,7 @@ baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; + MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; SWIFT_OBJC_BRIDGING_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h"; }; @@ -2602,6 +2607,7 @@ baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; + MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; SWIFT_OBJC_BRIDGING_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h"; }; @@ -2693,6 +2699,7 @@ baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; + MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; SWIFT_OBJC_BRIDGING_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h"; }; @@ -2762,6 +2769,7 @@ baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; + MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; SWIFT_OBJC_BRIDGING_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h"; }; From 2395878b254b1236364e3dff5d36f56414f2a142 Mon Sep 17 00:00:00 2001 From: Christopher Liscio Date: Fri, 15 Mar 2019 17:58:39 -0400 Subject: [PATCH 0972/1028] Oops! Forgot the Cartfiles. --- Cartfile.private | 4 ++-- Cartfile.resolved | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cartfile.private b/Cartfile.private index d3790f5a79..7d29fbe4ec 100644 --- a/Cartfile.private +++ b/Cartfile.private @@ -1,3 +1,3 @@ github "jspahrsummers/xcconfigs" "3d9d996" -github "Quick/Quick" ~> 1.2 -github "Quick/Nimble" ~> 7.0.2 +github "Quick/Quick" ~> 2.0 +github "Quick/Nimble" ~> 8.0.1 diff --git a/Cartfile.resolved b/Cartfile.resolved index a206c114e2..9d55212049 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,5 +1,5 @@ -github "Quick/Nimble" "v7.1.3" -github "Quick/Quick" "v1.3.1" +github "Quick/Nimble" "v8.0.1" +github "Quick/Quick" "v2.0.0" github "ReactiveCocoa/ReactiveSwift" "5.0.0" github "antitypical/Result" "4.1.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" From 202b00dabbfc391db0cbb884930363d582ed5558 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 16 Mar 2019 16:01:42 +0000 Subject: [PATCH 0973/1028] Drop `swift(>=3.2)` compile time condition. Fix an error message typo. --- ReactiveCocoa/NSObject+KeyValueObserving.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ReactiveCocoa/NSObject+KeyValueObserving.swift b/ReactiveCocoa/NSObject+KeyValueObserving.swift index 51c9097a91..5111d024ac 100644 --- a/ReactiveCocoa/NSObject+KeyValueObserving.swift +++ b/ReactiveCocoa/NSObject+KeyValueObserving.swift @@ -54,13 +54,12 @@ extension Reactive where Base: NSObject { } } - #if swift(>=3.2) private func producer( for keyPath: KeyPath, transform: @escaping (Any?) -> U ) -> SignalProducer { guard let kvcKeyPath = keyPath._kvcKeyPathString else { - fatalError("Cannot use `signal(for:)` on a non Objective-C property.") + fatalError("Cannot use `producer(for:)` on a non Objective-C property.") } return SignalProducer { observer, lifetime in @@ -153,7 +152,6 @@ extension Reactive where Base: NSObject { public func signal(for keyPath: KeyPath) -> Signal { return signal(for: keyPath) { $0 as! U } } - #endif } extension Property where Value: OptionalProtocol { From f2f1fa5676eaf3b0c23f823495c98255bad18db5 Mon Sep 17 00:00:00 2001 From: Marco Cancellieri Date: Fri, 15 Mar 2019 18:23:08 +0100 Subject: [PATCH 0974/1028] Update podspec to ReactiveSwift 5.0 --- ReactiveCocoa.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index ac5fbc66c0..d13d890d13 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -24,7 +24,7 @@ Pod::Spec.new do |s| s.watchos.source_files = "ReactiveCocoa/WatchKit/*.{swift}" s.module_name = 'ReactiveCocoa' - s.dependency 'ReactiveSwift', '~> 4.0' + s.dependency 'ReactiveSwift', '~> 5.0' s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "$(inherited) -suppress-warnings" } s.swift_version = '4.1.2' From 26edb81cef5be6ade32d0253b8b4a3962b344fdb Mon Sep 17 00:00:00 2001 From: Marco Cancellieri Date: Fri, 15 Mar 2019 19:07:34 +0100 Subject: [PATCH 0975/1028] Update CI image to Xcode 10.2 --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c329280f7b..4386205141 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: objective-c -osx_image: xcode10.1 +osx_image: xcode10.2 before_install: true install: true branches: @@ -14,7 +14,7 @@ cache: jobs: include: - stage: unit tests - osx_image: xcode10.1 + osx_image: xcode10.2 script: - XCODE_SCHEME=ReactiveCocoa-macOS XCODE_SDK=macosx From 97d4a994770afd1af28547a6fd2fa50ed819b1a8 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Fri, 22 Mar 2019 07:24:05 -0400 Subject: [PATCH 0976/1028] Adopt Swift 4.2 --- ReactiveCocoa.xcodeproj/project.pbxproj | 8 ++-- ReactiveCocoa/UIKit/UIBarButtonItem.swift | 2 +- ReactiveCocoa/UIKit/UIButton.swift | 6 +-- ReactiveCocoa/UIKit/UIControl.swift | 10 ++-- ReactiveCocoa/UIKit/UITextView.swift | 8 ++-- ReactiveCocoa/UIKit/iOS/UIKeyboard.swift | 26 +++++------ ...00\213Feedback\342\200\213Generator.swift" | 2 +- .../AppKit/NSImageViewSpec.swift | 6 +-- ReactiveCocoaTests/DeprecationsSpec.swift | 2 +- .../UIKit/UIBarButtonItemSpec.swift | 2 +- ReactiveCocoaTests/UIKit/UIButtonSpec.swift | 6 +-- .../UIKit/UIGestureRecognizerSpec.swift | 8 ++-- ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift | 46 +++++++++---------- .../UIKit/UISegmentedControlSpec.swift | 4 +- .../UIKit/UITableViewSpec.swift | 2 +- .../UIKit/UITextFieldSpec.swift | 8 ++-- ReactiveCocoaTests/UIKit/UITextViewSpec.swift | 10 ++-- ReactiveMapKit/MKLocalSearchRequest.swift | 4 +- 18 files changed, 78 insertions(+), 82 deletions(-) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 5c246da08a..f931c52c09 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -2540,7 +2540,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -2567,7 +2567,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -2682,7 +2682,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -2752,7 +2752,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)-Tests"; PRODUCT_NAME = "$(PROJECT_NAME)"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; diff --git a/ReactiveCocoa/UIKit/UIBarButtonItem.swift b/ReactiveCocoa/UIKit/UIBarButtonItem.swift index b03fde331e..6628b1576a 100644 --- a/ReactiveCocoa/UIKit/UIBarButtonItem.swift +++ b/ReactiveCocoa/UIKit/UIBarButtonItem.swift @@ -28,7 +28,7 @@ extension Reactive where Base: UIBarButtonItem { } /// Sets the style of the bar button item. - public var style: BindingTarget { + public var style: BindingTarget { return makeBindingTarget { $0.style = $1 } } diff --git a/ReactiveCocoa/UIKit/UIButton.swift b/ReactiveCocoa/UIKit/UIButton.swift index 0bf4f6ec1c..f8f49964b5 100644 --- a/ReactiveCocoa/UIKit/UIButton.swift +++ b/ReactiveCocoa/UIKit/UIButton.swift @@ -18,7 +18,7 @@ extension Reactive where Base: UIButton { } } - private var pressEvent: UIControlEvents { + private var pressEvent: UIControl.Event { if #available(iOS 9.0, tvOS 9.0, *) { return .primaryActionTriggered } else { @@ -32,12 +32,12 @@ extension Reactive where Base: UIButton { } /// Sets the title of the button for the specified state. - public func title(for state: UIControlState) -> BindingTarget { + public func title(for state: UIControl.State) -> BindingTarget { return makeBindingTarget { $0.setTitle($1, for: state) } } /// Sets the image of the button for the specified state. - public func image(for state: UIControlState) -> BindingTarget { + public func image(for state: UIControl.State) -> BindingTarget { return makeBindingTarget { $0.setImage($1, for: state) } } diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index e443543535..7be2d7e919 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -5,7 +5,7 @@ import enum Result.NoError extension Reactive where Base: UIControl { /// The current associated action of `self`, with its registered event mask /// and its disposable. - internal var associatedAction: Atomic<(action: CocoaAction, controlEvents: UIControlEvents, disposable: Disposable)?> { + internal var associatedAction: Atomic<(action: CocoaAction, controlEvents: UIControl.Event, disposable: Disposable)?> { return associatedValue { _ in Atomic(nil) } } @@ -17,7 +17,7 @@ extension Reactive where Base: UIControl { /// - controlEvents: The control event mask. /// - disposable: An outside disposable that will be bound to the scope of /// the given `action`. - internal func setAction(_ action: CocoaAction?, for controlEvents: UIControlEvents, disposable: Disposable? = nil) { + internal func setAction(_ action: CocoaAction?, for controlEvents: UIControl.Event, disposable: Disposable? = nil) { associatedAction.modify { associatedAction in associatedAction?.disposable.dispose() @@ -48,7 +48,7 @@ extension Reactive where Base: UIControl { /// - controlEvents: The control event mask. /// /// - returns: A signal that sends the control each time the control event occurs. - public func controlEvents(_ controlEvents: UIControlEvents) -> Signal { + public func controlEvents(_ controlEvents: UIControl.Event) -> Signal { return mapControlEvents(controlEvents, { $0 }) } @@ -71,7 +71,7 @@ extension Reactive where Base: UIControl { /// /// - returns: A signal that sends the reduced value from the control each time the /// control event occurs. - public func mapControlEvents(_ controlEvents: UIControlEvents, _ transform: @escaping (Base) -> Value) -> Signal { + public func mapControlEvents(_ controlEvents: UIControl.Event, _ transform: @escaping (Base) -> Value) -> Signal { return Signal { observer, signalLifetime in let receiver = CocoaTarget(observer) { transform($0 as! Base) } base.addTarget(receiver, @@ -91,7 +91,7 @@ extension Reactive where Base: UIControl { } @available(*, unavailable, renamed: "controlEvents(_:)") - public func trigger(for controlEvents: UIControlEvents) -> Signal<(), NoError> { + public func trigger(for controlEvents: UIControl.Event) -> Signal<(), NoError> { fatalError() } diff --git a/ReactiveCocoa/UIKit/UITextView.swift b/ReactiveCocoa/UIKit/UITextView.swift index c0472942ea..50c5ad3cd1 100644 --- a/ReactiveCocoa/UIKit/UITextView.swift +++ b/ReactiveCocoa/UIKit/UITextView.swift @@ -33,14 +33,14 @@ extension Reactive where Base: UITextView { /// - note: To observe text values that change on all editing events, /// see `continuousTextValues`. public var textValues: Signal { - return textValues(forName: .UITextViewTextDidEndEditing) + return textValues(forName: UITextView.textDidEndEditingNotification) } /// A signal of text values emitted by the text view upon any changes. /// /// - note: To observe text values only when editing ends, see `textValues`. public var continuousTextValues: Signal { - return textValues(forName: .UITextViewTextDidChange) + return textValues(forName: UITextView.textDidChangeNotification) } /// Sets the attributed text of the text view. @@ -61,14 +61,14 @@ extension Reactive where Base: UITextView { /// - note: To observe attributed text values that change on all editing events, /// see `continuousAttributedTextValues`. public var attributedTextValues: Signal { - return attributedTextValues(forName: .UITextViewTextDidEndEditing) + return attributedTextValues(forName: UITextView.textDidEndEditingNotification) } /// A signal of attributed text values emitted by the text view upon any changes. /// /// - note: To observe text values only when editing ends, see `attributedTextValues`. public var continuousAttributedTextValues: Signal { - return attributedTextValues(forName: .UITextViewTextDidChange) + return attributedTextValues(forName: UITextView.textDidChangeNotification) } /// A signal of range values emitted by the text view upon any selection change. diff --git a/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift b/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift index 2d8a6c8abf..6d363c5994 100644 --- a/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift +++ b/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift @@ -15,17 +15,17 @@ public enum KeyboardEvent { fileprivate var notificationName: Notification.Name { switch self { case .willShow: - return .UIKeyboardWillShow + return UIResponder.keyboardWillShowNotification case .didShow: - return .UIKeyboardDidShow + return UIResponder.keyboardDidShowNotification case .willHide: - return .UIKeyboardWillHide + return UIResponder.keyboardWillHideNotification case .didHide: - return .UIKeyboardDidHide + return UIResponder.keyboardDidHideNotification case .willChangeFrame: - return .UIKeyboardWillChangeFrame + return UIResponder.keyboardWillChangeFrameNotification case .didChangeFrame: - return .UIKeyboardDidChangeFrame + return UIResponder.keyboardDidChangeFrameNotification } } } @@ -39,25 +39,25 @@ public struct KeyboardChangeContext { /// The current frame of the system keyboard. public var beginFrame: CGRect { - return base[UIKeyboardFrameBeginUserInfoKey] as! CGRect + return base[UIResponder.keyboardFrameBeginUserInfoKey] as! CGRect } /// The final frame of the system keyboard. public var endFrame: CGRect { - return base[UIKeyboardFrameEndUserInfoKey] as! CGRect + return base[UIResponder.keyboardFrameEndUserInfoKey] as! CGRect } /// The animation curve which the system keyboard will use to animate the /// change in its frame. - public var animationCurve: UIViewAnimationCurve { - let value = base[UIKeyboardAnimationCurveUserInfoKey] as! NSNumber - return UIViewAnimationCurve(rawValue: value.intValue)! + public var animationCurve: UIView.AnimationCurve { + let value = base[UIResponder.keyboardAnimationCurveUserInfoKey] as! NSNumber + return UIView.AnimationCurve(rawValue: value.intValue)! } /// The duration in which the system keyboard expects to animate the change in /// its frame. public var animationDuration: Double { - return base[UIKeyboardAnimationDurationUserInfoKey] as! Double + return base[UIResponder.keyboardAnimationDurationUserInfoKey] as! Double } /// Indicates whether the change is triggered locally. Used in iPad @@ -65,7 +65,7 @@ public struct KeyboardChangeContext { /// in the system keyboard's frame. @available(iOS 9.0, *) public var isLocal: Bool { - return base[UIKeyboardIsLocalUserInfoKey] as! Bool + return base[UIResponder.keyboardIsLocalUserInfoKey] as! Bool } fileprivate init(userInfo: [AnyHashable: Any], event: KeyboardEvent) { diff --git "a/ReactiveCocoa/UIKit/iOS/UINotification\342\200\213Feedback\342\200\213Generator.swift" "b/ReactiveCocoa/UIKit/iOS/UINotification\342\200\213Feedback\342\200\213Generator.swift" index dd4d328a2d..9b5cbd511c 100644 --- "a/ReactiveCocoa/UIKit/iOS/UINotification\342\200\213Feedback\342\200\213Generator.swift" +++ "b/ReactiveCocoa/UIKit/iOS/UINotification\342\200\213Feedback\342\200\213Generator.swift" @@ -4,7 +4,7 @@ import UIKit @available(iOS 10.0, *) extension Reactive where Base: UINotificationFeedbackGenerator { /// Triggers the feedback. - public var notificationOccurred: BindingTarget { + public var notificationOccurred: BindingTarget { return makeBindingTarget { $0.notificationOccurred($1) } } } diff --git a/ReactiveCocoaTests/AppKit/NSImageViewSpec.swift b/ReactiveCocoaTests/AppKit/NSImageViewSpec.swift index f424235e22..887f7868a9 100644 --- a/ReactiveCocoaTests/AppKit/NSImageViewSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSImageViewSpec.swift @@ -38,11 +38,7 @@ class NSImageViewSpec: QuickSpec { let (pipeSignal, observer) = Signal.pipe() imageView.reactive.image <~ SignalProducer(pipeSignal) - #if swift(>=4.0) - let theImage = NSImage(named: .user) - #else - let theImage = NSImage(named: NSImageNameUser) - #endif + let theImage = NSImage(named: NSImage.userName) observer.send(value: theImage) expect(imageView.image) == theImage diff --git a/ReactiveCocoaTests/DeprecationsSpec.swift b/ReactiveCocoaTests/DeprecationsSpec.swift index 56bfbb7312..16f8abbe84 100644 --- a/ReactiveCocoaTests/DeprecationsSpec.swift +++ b/ReactiveCocoaTests/DeprecationsSpec.swift @@ -16,7 +16,7 @@ class DeprecationsSpec: QuickSpec { let object = TestKVOObject() var values: [Int] = [] - object.reactive.values(forKeyPath: #keyPath(TestKVOObject.value)).startWithValues { value in + object.reactive.producer(forKeyPath: #keyPath(TestKVOObject.value)).startWithValues { value in values.append(value as! Int) } diff --git a/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift b/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift index c7c82a6658..eed8d6ce66 100644 --- a/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift @@ -62,7 +62,7 @@ class UIBarButtonItemSpec: QuickSpec { } it("should accept changes from bindings to its style") { - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() barButtonItem.reactive.style <~ SignalProducer(pipeSignal) observer.send(value: .done) diff --git a/ReactiveCocoaTests/UIKit/UIButtonSpec.swift b/ReactiveCocoaTests/UIKit/UIButtonSpec.swift index 657b0b5436..bd61699d87 100644 --- a/ReactiveCocoaTests/UIKit/UIButtonSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIButtonSpec.swift @@ -30,17 +30,17 @@ class UIButtonSpec: QuickSpec { button.setTitle("", for: .highlighted) observer.send(value: firstTitle) - expect(button.title(for: UIControlState())) == firstTitle + expect(button.title(for: UIControl.State())) == firstTitle expect(button.title(for: .highlighted)) == "" expect(button.title(for: .selected)) == "" observer.send(value: secondTitle) - expect(button.title(for: UIControlState())) == secondTitle + expect(button.title(for: UIControl.State())) == secondTitle expect(button.title(for: .highlighted)) == "" expect(button.title(for: .selected)) == "" } - let pressedTest: (UIButton, UIControlEvents) -> Void = { button, event in + let pressedTest: (UIButton, UIControl.Event) -> Void = { button, event in button.isEnabled = true button.isUserInteractionEnabled = true diff --git a/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift b/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift index 62acba2ae4..33bc630ac6 100644 --- a/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift @@ -82,18 +82,18 @@ private final class TestTapGestureRecognizer: UITapGestureRecognizer { } private var targetActionPair: TargetActionPair? - private var forceState: UIGestureRecognizerState = .ended + private var forceState: UIGestureRecognizer.State = .ended - fileprivate override var state: UIGestureRecognizerState { + fileprivate override var state: UIGestureRecognizer.State { get { return forceState } - set { self.state = newValue } + set { } } fileprivate override func addTarget(_ target: Any, action: Selector) { targetActionPair = TargetActionPair(target: target as AnyObject, action: action) } - fileprivate func fireGestureEvent(_ state: UIGestureRecognizerState) { + fileprivate func fireGestureEvent(_ state: UIGestureRecognizer.State) { guard let targetAction = self.targetActionPair else { return } forceState = state _ = targetAction.target.perform(targetAction.action, with: self) diff --git a/ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift b/ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift index b27bf2ec21..51bb6602cb 100644 --- a/ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift @@ -16,17 +16,17 @@ class UIKeyboardSpec: QuickSpec { .observeValues { context = $0 } var dummyInfo: [AnyHashable: Any] = [ - UIKeyboardFrameBeginUserInfoKey: CGRect(x: 10, y: 10, width: 10, height: 10), - UIKeyboardFrameEndUserInfoKey: CGRect(x: 20, y: 20, width: 20, height: 20), - UIKeyboardAnimationDurationUserInfoKey: 1.0, - UIKeyboardAnimationCurveUserInfoKey: NSNumber(value: UIViewAnimationCurve.easeInOut.rawValue) + UIResponder.keyboardFrameBeginUserInfoKey: CGRect(x: 10, y: 10, width: 10, height: 10), + UIResponder.keyboardFrameEndUserInfoKey: CGRect(x: 20, y: 20, width: 20, height: 20), + UIResponder.keyboardAnimationDurationUserInfoKey: 1.0, + UIResponder.keyboardAnimationCurveUserInfoKey: NSNumber(value: UIView.AnimationCurve.easeInOut.rawValue) ] if #available(*, iOS 9.0) { - dummyInfo[UIKeyboardIsLocalUserInfoKey] = NSNumber(value: true) + dummyInfo[UIResponder.keyboardIsLocalUserInfoKey] = NSNumber(value: true) } - testCenter.post(name: .UIKeyboardWillShow, + testCenter.post(name: UIResponder.keyboardWillShowNotification, object: nil, userInfo: dummyInfo) @@ -55,18 +55,18 @@ class UIKeyboardSpec: QuickSpec { func makeDummyInfo(beginFrameHeight: CGFloat) -> [AnyHashable: Any] { var dummyInfo: [AnyHashable: Any] = [ - UIKeyboardFrameBeginUserInfoKey: CGRect(x: 10, y: 10, width: 10, height: beginFrameHeight), - UIKeyboardFrameEndUserInfoKey: CGRect(x: 20, y: 20, width: 20, height: 20), - UIKeyboardAnimationDurationUserInfoKey: 1.0, - UIKeyboardAnimationCurveUserInfoKey: NSNumber(value: UIViewAnimationCurve.easeInOut.rawValue) + UIResponder.keyboardFrameBeginUserInfoKey: CGRect(x: 10, y: 10, width: 10, height: beginFrameHeight), + UIResponder.keyboardFrameEndUserInfoKey: CGRect(x: 20, y: 20, width: 20, height: 20), + UIResponder.keyboardAnimationDurationUserInfoKey: 1.0, + UIResponder.keyboardAnimationCurveUserInfoKey: NSNumber(value: UIView.AnimationCurve.easeInOut.rawValue) ] if #available(*, iOS 9.0) { - dummyInfo[UIKeyboardIsLocalUserInfoKey] = NSNumber(value: true) + dummyInfo[UIResponder.keyboardIsLocalUserInfoKey] = NSNumber(value: true) } return dummyInfo } - testCenter.post(name: .UIKeyboardWillShow, + testCenter.post(name: UIResponder.keyboardWillShowNotification, object: nil, userInfo: makeDummyInfo(beginFrameHeight: 10)) @@ -82,7 +82,7 @@ class UIKeyboardSpec: QuickSpec { expect(context?.isLocal) == true } - testCenter.post(name: .UIKeyboardDidShow, + testCenter.post(name: UIResponder.keyboardDidShowNotification, object: nil, userInfo: makeDummyInfo(beginFrameHeight: 20)) @@ -96,7 +96,7 @@ class UIKeyboardSpec: QuickSpec { expect(context?.isLocal) == true } - testCenter.post(name: .UIKeyboardWillHide, + testCenter.post(name: UIResponder.keyboardWillHideNotification, object: nil, userInfo: makeDummyInfo(beginFrameHeight: 30)) @@ -110,7 +110,7 @@ class UIKeyboardSpec: QuickSpec { expect(context?.isLocal) == true } - testCenter.post(name: .UIKeyboardDidHide, + testCenter.post(name: UIResponder.keyboardDidHideNotification, object: nil, userInfo: makeDummyInfo(beginFrameHeight: 40)) @@ -124,7 +124,7 @@ class UIKeyboardSpec: QuickSpec { expect(context?.isLocal) == true } - testCenter.post(name: .UIKeyboardWillChangeFrame, + testCenter.post(name: UIResponder.keyboardWillChangeFrameNotification, object: nil, userInfo: makeDummyInfo(beginFrameHeight: 50)) @@ -138,7 +138,7 @@ class UIKeyboardSpec: QuickSpec { expect(context?.isLocal) == true } - testCenter.post(name: .UIKeyboardDidChangeFrame, + testCenter.post(name: UIResponder.keyboardDidChangeFrameNotification, object: nil, userInfo: makeDummyInfo(beginFrameHeight: 60)) @@ -164,17 +164,17 @@ class UIKeyboardSpec: QuickSpec { .observeValues { context = $0 } var dummyInfo: [AnyHashable: Any] = [ - UIKeyboardFrameBeginUserInfoKey: CGRect(x: 10, y: 10, width: 10, height: 10), - UIKeyboardFrameEndUserInfoKey: CGRect(x: 20, y: 20, width: 20, height: 20), - UIKeyboardAnimationDurationUserInfoKey: 1.0, - UIKeyboardAnimationCurveUserInfoKey: NSNumber(value: UIViewAnimationCurve.easeInOut.rawValue) + UIResponder.keyboardFrameBeginUserInfoKey: CGRect(x: 10, y: 10, width: 10, height: 10), + UIResponder.keyboardFrameEndUserInfoKey: CGRect(x: 20, y: 20, width: 20, height: 20), + UIResponder.keyboardAnimationDurationUserInfoKey: 1.0, + UIResponder.keyboardAnimationCurveUserInfoKey: NSNumber(value: UIView.AnimationCurve.easeInOut.rawValue) ] if #available(*, iOS 9.0) { - dummyInfo[UIKeyboardIsLocalUserInfoKey] = NSNumber(value: true) + dummyInfo[UIResponder.keyboardIsLocalUserInfoKey] = NSNumber(value: true) } - testCenter.post(name: .UIKeyboardWillChangeFrame, + testCenter.post(name: UIResponder.keyboardWillChangeFrameNotification, object: nil, userInfo: dummyInfo) diff --git a/ReactiveCocoaTests/UIKit/UISegmentedControlSpec.swift b/ReactiveCocoaTests/UIKit/UISegmentedControlSpec.swift index a56331ce5d..51d4c5b21e 100644 --- a/ReactiveCocoaTests/UIKit/UISegmentedControlSpec.swift +++ b/ReactiveCocoaTests/UIKit/UISegmentedControlSpec.swift @@ -8,13 +8,13 @@ class UISegmentedControlSpec: QuickSpec { override func spec() { it("should accept changes from bindings to its selected segment index") { let s = UISegmentedControl(items: ["0", "1", "2"]) - s.selectedSegmentIndex = UISegmentedControlNoSegment + s.selectedSegmentIndex = UISegmentedControl.noSegment expect(s.numberOfSegments) == 3 let (pipeSignal, observer) = Signal.pipe() s.reactive.selectedSegmentIndex <~ SignalProducer(pipeSignal) - expect(s.selectedSegmentIndex) == UISegmentedControlNoSegment + expect(s.selectedSegmentIndex) == UISegmentedControl.noSegment observer.send(value: 1) expect(s.selectedSegmentIndex) == 1 diff --git a/ReactiveCocoaTests/UIKit/UITableViewSpec.swift b/ReactiveCocoaTests/UIKit/UITableViewSpec.swift index c7c0203b4b..9aa8c23259 100644 --- a/ReactiveCocoaTests/UIKit/UITableViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITableViewSpec.swift @@ -46,7 +46,7 @@ private final class TestTableView: UITableView { let reloadDataSignal: Signal<(), NoError> private let reloadDataObserver: Signal<(), NoError>.Observer - override init(frame: CGRect, style: UITableViewStyle) { + override init(frame: CGRect, style: UITableView.Style) { (reloadDataSignal, reloadDataObserver) = Signal.pipe() super.init(frame: frame, style: style) } diff --git a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift index e77486b0c3..4a26416de5 100644 --- a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift @@ -56,7 +56,7 @@ class UITextFieldSpec: QuickSpec { latestValue = text } - for event in UIControlEvents.editingEvents { + for event in UIControl.Event.editingEvents { textField.text = "Test \(event)" textField.sendActions(for: event) @@ -110,7 +110,7 @@ class UITextFieldSpec: QuickSpec { latestValue = attributedText } - for event in UIControlEvents.editingEvents { + for event in UIControl.Event.editingEvents { textField.attributedText = NSAttributedString(string: "Test \(event)") textField.sendActions(for: event) @@ -195,8 +195,8 @@ class UITextFieldSpec: QuickSpec { } } -extension UIControlEvents { - fileprivate static var editingEvents: [UIControlEvents] { +extension UIControl.Event { + fileprivate static var editingEvents: [UIControl.Event] { return [.allEditingEvents, .editingDidBegin, .editingChanged, .editingDidEndOnExit, .editingDidEnd] } } diff --git a/ReactiveCocoaTests/UIKit/UITextViewSpec.swift b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift index f35058c87d..fb51107d4f 100644 --- a/ReactiveCocoaTests/UIKit/UITextViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift @@ -11,7 +11,7 @@ class UITextViewSpec: QuickSpec { weak var _textView: UITextView? #if swift(>=4.0) - let attributes: [NSAttributedStringKey: Any] = [ + let attributes: [NSAttributedString.Key: Any] = [ .font: UIFont.systemFont(ofSize: 18), .foregroundColor: UIColor.red ] @@ -44,7 +44,7 @@ class UITextViewSpec: QuickSpec { latestValue = text } - NotificationCenter.default.post(name: .UITextViewTextDidEndEditing, object: textView) + NotificationCenter.default.post(name: UITextView.textDidEndEditingNotification, object: textView) expect(latestValue) == textView.text } @@ -56,7 +56,7 @@ class UITextViewSpec: QuickSpec { latestValue = text } - NotificationCenter.default.post(name: .UITextViewTextDidChange, object: textView) + NotificationCenter.default.post(name: UITextView.textDidChangeNotification, object: textView) expect(latestValue) == textView.text } @@ -84,7 +84,7 @@ class UITextViewSpec: QuickSpec { latestValue = attributedText } - NotificationCenter.default.post(name: .UITextViewTextDidEndEditing, object: textView) + NotificationCenter.default.post(name: UITextView.textDidEndEditingNotification, object: textView) expect(latestValue) == textView.attributedText } @@ -96,7 +96,7 @@ class UITextViewSpec: QuickSpec { latestValue = attributedText } - NotificationCenter.default.post(name: .UITextViewTextDidChange, object: textView) + NotificationCenter.default.post(name: UITextView.textDidChangeNotification, object: textView) expect(latestValue) == textView.attributedText } diff --git a/ReactiveMapKit/MKLocalSearchRequest.swift b/ReactiveMapKit/MKLocalSearchRequest.swift index 2cee6f343d..7a9650713e 100644 --- a/ReactiveMapKit/MKLocalSearchRequest.swift +++ b/ReactiveMapKit/MKLocalSearchRequest.swift @@ -7,9 +7,9 @@ private let defaultLocalSearchError = NSError(domain: "org.reactivecocoa.Reactiv code: 1, userInfo: nil) @available(tvOS 9.2, *) -extension Reactive where Base: MKLocalSearchRequest { +extension Reactive where Base: MKLocalSearch.Request { /// A SignalProducer which performs an `MKLocalSearch`. - public var search: SignalProducer { + public var search: SignalProducer { return SignalProducer {[base = self.base] observer, lifetime in let search = MKLocalSearch(request: base) search.start { response, error in From d839842e6418977755a5bb08aa45d29498c4236c Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Fri, 22 Mar 2019 08:22:12 -0400 Subject: [PATCH 0977/1028] Update the other Swift versions --- .swift-version | 2 +- ReactiveCocoa.podspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.swift-version b/.swift-version index 4d0dcda01c..bf77d54968 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -4.1.2 +4.2 diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index d13d890d13..9c79962ec7 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -27,5 +27,5 @@ Pod::Spec.new do |s| s.dependency 'ReactiveSwift', '~> 5.0' s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "$(inherited) -suppress-warnings" } - s.swift_version = '4.1.2' + s.swift_version = '4.2' end From 13a761f1c5be14e54d7ec0c9821b3c5466b7f949 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Fri, 22 Mar 2019 10:15:52 -0400 Subject: [PATCH 0978/1028] Update ReactiveMapKit.podspec --- ReactiveMapKit.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index 05c7298c3a..7b68222635 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -19,5 +19,5 @@ Pod::Spec.new do |s| s.dependency 'ReactiveCocoa', "#{s.version}" s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "$(inherited) -suppress-warnings" } - s.swift_version = '4.1.2' + s.swift_version = '4.2' end From 7747b42d987eadfb885ad03ad24ff1386582610f Mon Sep 17 00:00:00 2001 From: Marco Cancellieri Date: Wed, 2 May 2018 17:02:11 +0200 Subject: [PATCH 0979/1028] make text and attributedText non-optional --- ReactiveCocoa/UIKit/UITextField.swift | 8 ++++---- ReactiveCocoa/UIKit/UITextView.swift | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ReactiveCocoa/UIKit/UITextField.swift b/ReactiveCocoa/UIKit/UITextField.swift index 0e658bba65..9d411ce655 100644 --- a/ReactiveCocoa/UIKit/UITextField.swift +++ b/ReactiveCocoa/UIKit/UITextField.swift @@ -12,15 +12,15 @@ extension Reactive where Base: UITextField { /// /// - note: To observe text values that change on all editing events, /// see `continuousTextValues`. - public var textValues: Signal { - return mapControlEvents([.editingDidEnd, .editingDidEndOnExit]) { $0.text } + public var textValues: Signal { + return mapControlEvents([.editingDidEnd, .editingDidEndOnExit]) { $0.text ?? "" } } /// A signal of text values emitted by the text field upon any changes. /// /// - note: To observe text values only when editing ends, see `textValues`. - public var continuousTextValues: Signal { - return mapControlEvents(.allEditingEvents) { $0.text } + public var continuousTextValues: Signal { + return mapControlEvents(.allEditingEvents) { $0.text ?? "" } } /// Sets the attributed text of the text field. diff --git a/ReactiveCocoa/UIKit/UITextView.swift b/ReactiveCocoa/UIKit/UITextView.swift index c0472942ea..1981359bef 100644 --- a/ReactiveCocoa/UIKit/UITextView.swift +++ b/ReactiveCocoa/UIKit/UITextView.swift @@ -20,7 +20,7 @@ extension Reactive where Base: UITextView { return makeBindingTarget { $0.text = $1 } } - private func textValues(forName name: NSNotification.Name) -> Signal { + private func textValues(forName name: NSNotification.Name) -> Signal { return NotificationCenter.default .reactive .notifications(forName: name, object: base) @@ -32,14 +32,14 @@ extension Reactive where Base: UITextView { /// /// - note: To observe text values that change on all editing events, /// see `continuousTextValues`. - public var textValues: Signal { + public var textValues: Signal { return textValues(forName: .UITextViewTextDidEndEditing) } /// A signal of text values emitted by the text view upon any changes. /// /// - note: To observe text values only when editing ends, see `textValues`. - public var continuousTextValues: Signal { + public var continuousTextValues: Signal { return textValues(forName: .UITextViewTextDidChange) } @@ -48,7 +48,7 @@ extension Reactive where Base: UITextView { return makeBindingTarget { $0.attributedText = $1 } } - private func attributedTextValues(forName name: NSNotification.Name) -> Signal { + private func attributedTextValues(forName name: NSNotification.Name) -> Signal { return NotificationCenter.default .reactive .notifications(forName: name, object: base) @@ -60,14 +60,14 @@ extension Reactive where Base: UITextView { /// /// - note: To observe attributed text values that change on all editing events, /// see `continuousAttributedTextValues`. - public var attributedTextValues: Signal { + public var attributedTextValues: Signal { return attributedTextValues(forName: .UITextViewTextDidEndEditing) } /// A signal of attributed text values emitted by the text view upon any changes. /// /// - note: To observe text values only when editing ends, see `attributedTextValues`. - public var continuousAttributedTextValues: Signal { + public var continuousAttributedTextValues: Signal { return attributedTextValues(forName: .UITextViewTextDidChange) } From 17a7980ded6bb35d4c0a1ba84f6068693737b960 Mon Sep 17 00:00:00 2001 From: Marco Cancellieri Date: Wed, 4 Jul 2018 14:42:11 +0200 Subject: [PATCH 0980/1028] non-optional nsattributedstring --- ReactiveCocoa/UIKit/UITextField.swift | 8 ++++---- ReactiveCocoaTests/UIKit/UITextFieldSpec.swift | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ReactiveCocoa/UIKit/UITextField.swift b/ReactiveCocoa/UIKit/UITextField.swift index 9d411ce655..e6be8d9507 100644 --- a/ReactiveCocoa/UIKit/UITextField.swift +++ b/ReactiveCocoa/UIKit/UITextField.swift @@ -42,15 +42,15 @@ extension Reactive where Base: UITextField { /// /// - note: To observe attributed text values that change on all editing events, /// see `continuousAttributedTextValues`. - public var attributedTextValues: Signal { - return mapControlEvents([.editingDidEnd, .editingDidEndOnExit]) { $0.attributedText } + public var attributedTextValues: Signal { + return mapControlEvents([.editingDidEnd, .editingDidEndOnExit]) { $0.attributedText ?? NSAttributedString() } } /// A signal of attributed text values emitted by the text field upon any changes. /// /// - note: To observe attributed text values only when editing ends, see `attributedTextValues`. - public var continuousAttributedTextValues: Signal { - return mapControlEvents(.allEditingEvents) { $0.attributedText } + public var continuousAttributedTextValues: Signal { + return mapControlEvents(.allEditingEvents) { $0.attributedText ?? NSAttributedString() } } /// Sets the secure text entry attribute on the text field. diff --git a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift index e77486b0c3..56db8480ff 100644 --- a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift @@ -173,7 +173,7 @@ class UITextFieldSpec: QuickSpec { var values: [String] = [] textField.reactive.continuousTextValues.observeValues { text in - values.append(text ?? "") + values.append(text) if text == "2" { textField.resignFirstResponder() From b1a6209bbd7ede5b113fa4c8d4d21fc0f74e00bc Mon Sep 17 00:00:00 2001 From: Marco Cancellieri Date: Mon, 25 Mar 2019 09:27:14 +0100 Subject: [PATCH 0981/1028] Update Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index efe0e678bd..4f5289c040 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # master *Please put new entries at the top. +1. Make UITextField and UITextView text and attributedText values non-optional. (#3591, kudos to @Marcocanc) 1. KVO observations can now be made with Smart Key Path in Swift 3.2+, using `producer(for:)` and `signal(for:)` available on `NSObject.reactive`. (#3491, kudos to @andersio) 1. Fix warnings for deprecated use of Swift's allocate/deallocate methods. 1. Update Quick (2.0.0) and Nimble (8.0.1). From a6376320f011e654669b6518a3311e2fc72b763c Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Wed, 27 Mar 2019 08:41:22 -0400 Subject: [PATCH 0982/1028] 9.0.0 --- CHANGELOG.md | 2 ++ ReactiveCocoa.podspec | 2 +- ReactiveMapKit.podspec | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f5289c040..196bac2606 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # master *Please put new entries at the top. + +# 9.0.0 1. Make UITextField and UITextView text and attributedText values non-optional. (#3591, kudos to @Marcocanc) 1. KVO observations can now be made with Smart Key Path in Swift 3.2+, using `producer(for:)` and `signal(for:)` available on `NSObject.reactive`. (#3491, kudos to @andersio) 1. Fix warnings for deprecated use of Swift's allocate/deallocate methods. diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 9c79962ec7..8b2baf781b 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "8.0.2" + s.version = "9.0.0" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index 7b68222635..c004e058c6 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveMapKit" - s.version = "8.0.2" + s.version = "9.0.0" s.summary = "MapKit bindings for ReactiveCocoa." s.description = <<-DESC Provide MapKit bindings for ReactiveCocoa. ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. From bde24a319600dae3e66a1635e1967b55d8834809 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Wed, 17 Apr 2019 10:40:12 -0700 Subject: [PATCH 0983/1028] Update to ReactiveSwift 6.0 and remove antitypical/Result (#3661) * Update to ReactiveSwift 6.0 and remove antitypical/Result * Remove references of Result from README. * Remove remaining Result.framework references * Update ReactiveCocoa.podspec --- .gitmodules | 3 -- CHANGELOG.md | 3 ++ Cartfile | 2 +- Cartfile.resolved | 3 +- Carthage/Checkouts/ReactiveSwift | 2 +- Carthage/Checkouts/Result | 1 - README.md | 9 ++--- .../Sandbox.xcplaygroundpage/Contents.swift | 2 -- .../Sandbox.xcplaygroundpage/Contents.swift | 2 -- .../Sandbox.xcplaygroundpage/Contents.swift | 2 -- ReactiveCocoa.podspec | 2 +- ReactiveCocoa.xcodeproj/project.pbxproj | 34 ------------------- .../xcschemes/ReactiveCocoa-iOS.xcscheme | 14 -------- .../xcschemes/ReactiveCocoa-macOS.xcscheme | 14 -------- .../xcschemes/ReactiveCocoa-tvOS.xcscheme | 14 -------- .../xcschemes/ReactiveMapKit-iOS.xcscheme | 14 -------- .../xcschemes/ReactiveMapKit-macOS.xcscheme | 14 -------- .../xcschemes/ReactiveMapKit-tvOS.xcscheme | 14 -------- .../contents.xcworkspacedata | 3 -- ReactiveCocoa/AppKit/ActionProxy.swift | 7 ++-- ReactiveCocoa/AppKit/NSButton.swift | 5 ++- ReactiveCocoa/AppKit/NSControl.swift | 17 +++++----- ReactiveCocoa/AppKit/NSImageView.swift | 1 - ReactiveCocoa/AppKit/NSPopUpButton.swift | 9 +++-- ReactiveCocoa/AppKit/NSSegmentedControl.swift | 5 ++- ReactiveCocoa/AppKit/NSSlider.swift | 3 +- ReactiveCocoa/AppKit/NSTextField.swift | 7 ++-- ReactiveCocoa/AppKit/NSTextView.swift | 7 ++-- ReactiveCocoa/AppKit/NSView.swift | 1 - ReactiveCocoa/AppKit/ReusableComponents.swift | 5 ++- ReactiveCocoa/CocoaAction.swift | 1 - ReactiveCocoa/CocoaTarget.swift | 7 ++-- ReactiveCocoa/DelegateProxy.swift | 5 ++- ReactiveCocoa/Deprecations+Removals.swift | 3 +- ReactiveCocoa/DynamicProperty.swift | 5 ++- ReactiveCocoa/NSObject+Intercepting.swift | 9 +++-- .../NSObject+KeyValueObserving.swift | 23 ++++++------- ReactiveCocoa/UIKit/ReusableComponents.swift | 3 +- ReactiveCocoa/UIKit/UIControl.swift | 7 ++-- ReactiveCocoa/UIKit/UIGestureRecognizer.swift | 3 +- ReactiveCocoa/UIKit/UISegmentedControl.swift | 3 +- ReactiveCocoa/UIKit/UITextField.swift | 9 +++-- ReactiveCocoa/UIKit/UITextView.swift | 15 ++++---- ReactiveCocoa/UIKit/UIViewController.swift | 13 ++++--- ReactiveCocoa/UIKit/iOS/UIDatePicker.swift | 3 +- ReactiveCocoa/UIKit/iOS/UIKeyboard.swift | 7 ++-- ReactiveCocoa/UIKit/iOS/UIPickerView.swift | 3 +- .../UIKit/iOS/UIRefreshControl.swift | 1 - ReactiveCocoa/UIKit/iOS/UISearchBar.swift | 19 +++++------ ReactiveCocoa/UIKit/iOS/UISlider.swift | 3 +- ReactiveCocoa/UIKit/iOS/UIStepper.swift | 3 +- ReactiveCocoa/UIKit/iOS/UISwitch.swift | 3 +- .../AppKit/ActionProxySpec.swift | 1 - ReactiveCocoaTests/AppKit/NSButtonSpec.swift | 9 +++-- .../AppKit/NSCollectionViewSpec.swift | 11 +++--- ReactiveCocoaTests/AppKit/NSControlSpec.swift | 1 - .../AppKit/NSImageViewSpec.swift | 5 ++- .../AppKit/NSPopUpButtonSpec.swift | 7 ++-- .../AppKit/NSTableViewSpec.swift | 11 +++--- ReactiveCocoaTests/AppKit/NSViewSpec.swift | 5 ++- .../AppKit/ReusableComponentsSpec.swift | 1 - ReactiveCocoaTests/AssociationSpec.swift | 1 - ReactiveCocoaTests/BindingTargetSpec.swift | 9 +++-- ReactiveCocoaTests/CocoaActionSpec.swift | 3 +- ReactiveCocoaTests/DelegateProxySpec.swift | 15 ++++---- ReactiveCocoaTests/DeprecationsSpec.swift | 1 - ReactiveCocoaTests/DynamicPropertySpec.swift | 9 +++-- ReactiveCocoaTests/InterceptingSpec.swift | 1 - ReactiveCocoaTests/KVOKVCExtensionSpec.swift | 1 - .../KeyValueObservingSpec+Swift4.swift | 19 +++++------ .../KeyValueObservingSpec.swift | 21 ++++++------ ReactiveCocoaTests/LifetimeSpec.swift | 9 +++-- .../Shared/NSLayoutConstraintSpec.swift | 3 +- ReactiveCocoaTests/TestError.swift | 5 ++- .../UIKit/ReusableComponentsSpec.swift | 1 - .../UIKit/UIActivityIndicatorViewSpec.swift | 3 +- .../UIKit/UIBarButtonItemSpec.swift | 17 +++++----- ReactiveCocoaTests/UIKit/UIButtonSpec.swift | 5 ++- .../UIKit/UICollectionViewSpec.swift | 11 +++--- ReactiveCocoaTests/UIKit/UIControlSpec.swift | 11 +++--- .../UIKit/UIGestureRecognizerSpec.swift | 1 - .../UIKit/UIImageViewSpec.swift | 5 ++- ReactiveCocoaTests/UIKit/UILabelSpec.swift | 7 ++-- .../UIKit/UINavigationItemSpec.swift | 27 +++++++-------- .../UIKit/UIPickerViewSpec.swift | 17 +++++----- .../UIKit/UIProgressViewSpec.swift | 3 +- .../UIKit/UIRefreshControlSpec.swift | 9 +++-- .../UIKit/UIScrollViewSpec.swift | 15 ++++---- .../UIKit/UISearchBarSpec.swift | 7 ++-- .../UIKit/UISegmentedControlSpec.swift | 3 +- ReactiveCocoaTests/UIKit/UISliderSpec.swift | 7 ++-- ReactiveCocoaTests/UIKit/UIStepperSpec.swift | 7 ++-- ReactiveCocoaTests/UIKit/UISwitchSpec.swift | 5 ++- .../UIKit/UITabBarItemSpec.swift | 5 ++- .../UIKit/UITableViewSpec.swift | 11 +++--- .../UIKit/UITextFieldSpec.swift | 9 +++-- ReactiveCocoaTests/UIKit/UITextViewSpec.swift | 3 +- .../UIKit/UIViewControllerSpec.swift | 3 +- ReactiveCocoaTests/UIKit/UIViewSpec.swift | 11 +++--- ReactiveMapKit/MKLocalSearchRequest.swift | 5 ++- ReactiveMapKitTests/MKMapViewSpec.swift | 11 +++--- 101 files changed, 260 insertions(+), 474 deletions(-) delete mode 160000 Carthage/Checkouts/Result diff --git a/.gitmodules b/.gitmodules index d3aed82746..cd4bff88f4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,9 +7,6 @@ [submodule "Carthage/Checkouts/xcconfigs"] path = Carthage/Checkouts/xcconfigs url = https://github.com/jspahrsummers/xcconfigs.git -[submodule "Carthage/Checkouts/Result"] - path = Carthage/Checkouts/Result - url = https://github.com/antitypical/Result.git [submodule "Carthage/Checkouts/ReactiveSwift"] path = Carthage/Checkouts/ReactiveSwift url = https://github.com/ReactiveCocoa/ReactiveSwift.git diff --git a/CHANGELOG.md b/CHANGELOG.md index 196bac2606..6d877c77ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # master *Please put new entries at the top. +1. Update ReactiveSwift to 6.0 +1. Remove dependency on antitypical/Result + # 9.0.0 1. Make UITextField and UITextView text and attributedText values non-optional. (#3591, kudos to @Marcocanc) 1. KVO observations can now be made with Smart Key Path in Swift 3.2+, using `producer(for:)` and `signal(for:)` available on `NSObject.reactive`. (#3491, kudos to @andersio) diff --git a/Cartfile b/Cartfile index e6233eb34c..a0fb0df979 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" ~> 5.0 +github "ReactiveCocoa/ReactiveSwift" ~> 6.0 diff --git a/Cartfile.resolved b/Cartfile.resolved index 9d55212049..a145b57e0a 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,5 +1,4 @@ github "Quick/Nimble" "v8.0.1" github "Quick/Quick" "v2.0.0" -github "ReactiveCocoa/ReactiveSwift" "5.0.0" -github "antitypical/Result" "4.1.0" +github "ReactiveCocoa/ReactiveSwift" "6.0.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index e285cea17c..3eba2b48ee 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit e285cea17cca8e23928d1e85a235638184e39ac9 +Subproject commit 3eba2b48ee80f87058de5ae8bb41e585bde5c997 diff --git a/Carthage/Checkouts/Result b/Carthage/Checkouts/Result deleted file mode 160000 index 2ca499ba45..0000000000 --- a/Carthage/Checkouts/Result +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2ca499ba456795616fbc471561ff1d963e6ae160 diff --git a/README.md b/README.md index a8d74f5bf6..29c5567604 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ ReactiveCocoa to your `Cartfile`: github "ReactiveCocoa/ReactiveCocoa" ~> 8.0 ``` -If you use Carthage to build your dependencies, make sure you have added `ReactiveCocoa.framework`, `ReactiveSwift.framework`, and `Result.framework` to the "_Linked Frameworks and Libraries_" section of your target, and have included them in your Carthage framework copying build phase. +If you use Carthage to build your dependencies, make sure you have added `ReactiveCocoa.framework` and `ReactiveSwift.framework` to the "_Linked Frameworks and Libraries_" section of your target, and have included them in your Carthage framework copying build phase. #### CocoaPods @@ -119,13 +119,10 @@ pod 'ReactiveCocoa', '~> 8.0' 1. Add the ReactiveCocoa repository as a [submodule][] of your application’s repository. 1. Run `git submodule update --init --recursive` from within the ReactiveCocoa folder. - 1. Drag and drop `ReactiveCocoa.xcodeproj`, - `Carthage/Checkouts/ReactiveSwift/ReactiveSwift.xcodeproj`, and - `Carthage/Checkouts/Result/Result.xcodeproj` into your application’s Xcode + 1. Drag and drop `ReactiveCocoa.xcodeproj` and `Carthage/Checkouts/ReactiveSwift/ReactiveSwift.xcodeproj` into your application’s Xcode project or workspace. 1. On the “General” tab of your application target’s settings, add - `ReactiveCocoa.framework`, `ReactiveSwift.framework`, and `Result.framework` - to the “Embedded Binaries” section. + `ReactiveCocoa.framework` and `ReactiveSwift.framework` to the “Embedded Binaries” section. 1. If your application target does not contain Swift code at all, you should also set the `EMBEDDED_CONTENT_CONTAINS_SWIFT` build setting to “Yes”. diff --git a/ReactiveCocoa-iOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift b/ReactiveCocoa-iOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift index 55082bfd30..5743e44ae4 100644 --- a/ReactiveCocoa-iOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift +++ b/ReactiveCocoa-iOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift @@ -6,14 +6,12 @@ **OR**, if you have [Carthage](https://github.com/Carthage/Carthage) installed - `carthage checkout --no-use-binaries` 1. Open `ReactiveCocoa.xcworkspace` -1. Build `Result-iOS` scheme 1. Build `ReactiveSwift-iOS` scheme 1. Build `ReactiveCocoa-iOS` scheme 1. Finally open the `ReactiveCocoa-iOS.playground` 1. Choose `View > Show Debug Area` */ -import Result import ReactiveCocoa import ReactiveSwift import UIKit diff --git a/ReactiveCocoa-macOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift b/ReactiveCocoa-macOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift index acbb9a577b..43f51659b4 100644 --- a/ReactiveCocoa-macOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift +++ b/ReactiveCocoa-macOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift @@ -6,14 +6,12 @@ **OR**, if you have [Carthage](https://github.com/Carthage/Carthage) installed - `carthage checkout --no-use-binaries` 1. Open `ReactiveCocoa.xcworkspace` -1. Build `Result-Mac` scheme 1. Build `ReactiveSwift-macOS` scheme 1. Build `ReactiveCocoa-macOS` scheme 1. Finally open the `ReactiveCocoa-macOS.playground` 1. Choose `View > Show Debug Area` */ -import Result import ReactiveCocoa import ReactiveSwift import AppKit diff --git a/ReactiveCocoa-tvOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift b/ReactiveCocoa-tvOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift index 455016c32f..238545fcbd 100644 --- a/ReactiveCocoa-tvOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift +++ b/ReactiveCocoa-tvOS.playground/Pages/Sandbox.xcplaygroundpage/Contents.swift @@ -6,14 +6,12 @@ **OR**, if you have [Carthage](https://github.com/Carthage/Carthage) installed - `carthage checkout --no-use-binaries` 1. Open `ReactiveCocoa.xcworkspace` -1. Build `Result-tvOS` scheme 1. Build `ReactiveSwift-tvOS` scheme 1. Build `ReactiveCocoa-tvOS` scheme 1. Finally open the `ReactiveCocoa-tvOS.playground` 1. Choose `View > Show Debug Area` */ -import Result import ReactiveCocoa import ReactiveSwift import UIKit diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 8b2baf781b..dc4e47cb8f 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -24,7 +24,7 @@ Pod::Spec.new do |s| s.watchos.source_files = "ReactiveCocoa/WatchKit/*.{swift}" s.module_name = 'ReactiveCocoa' - s.dependency 'ReactiveSwift', '~> 5.0' + s.dependency 'ReactiveSwift', '~> 6.0' s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "$(inherited) -suppress-warnings" } s.swift_version = '4.2' diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index f931c52c09..307d6bdf04 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -51,7 +51,6 @@ 538DCB7F1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB7C1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift */; }; 53AC46CC1DD6F97400C799E1 /* UISlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53AC46CB1DD6F97400C799E1 /* UISlider.swift */; }; 53AC46CF1DD6FC0000C799E1 /* UISliderSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53AC46CE1DD6FC0000C799E1 /* UISliderSpec.swift */; }; - 57A4D2081BA13D7A00F7D4B1 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; 57A4D20A1BA13D7A00F7D4B1 /* ReactiveCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; }; 654DE7B02205F9DE0048FE14 /* ReactiveCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D04725EA19E49ED7006002AA /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 654DE7B22205FA0A0048FE14 /* ReactiveCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D047260C19E49F82006002AA /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -62,7 +61,6 @@ 7DFBED1E1CDB8D7000EE435B /* ReactiveCocoa.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 7DFBED1F1CDB8D7800EE435B /* Quick.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 7DFBED201CDB8D7D00EE435B /* Nimble.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 7DFBED211CDB8D8300EE435B /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 7DFBED281CDB8DE300EE435B /* DynamicPropertySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */; }; 7DFBED6D1CDB8F7D00EE435B /* SignalProducerNimbleMatchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */; }; 834DE1011E4109750099F4E5 /* NSImageViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 834DE1001E4109750099F4E5 /* NSImageViewSpec.swift */; }; @@ -172,20 +170,15 @@ 9A73DFFE216D3CEE0069AD76 /* MKMapViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED51DD4BD2C0016C058 /* MKMapViewSpec.swift */; }; 9A73E015216D3E660069AD76 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D04725EA19E49ED7006002AA /* ReactiveCocoa.framework */; }; 9A73E01C216D3E700069AD76 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D047260C19E49F82006002AA /* ReactiveCocoa.framework */; }; - 9A73E030216D3FAF0069AD76 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; 9A73E036216D3FC50069AD76 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */; }; - 9A73E038216D3FC50069AD76 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E037216D3FC50069AD76 /* Result.framework */; }; 9A73E039216D3FEB0069AD76 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */; }; 9A73E03A216D3FF10069AD76 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */; }; 9A73E03B216D3FF50069AD76 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */; }; 9A73E03C216D3FFC0069AD76 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */; }; 9A73E03D216D40070069AD76 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */; }; 9A73E03E216D400D0069AD76 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */; }; - 9A73E03F216D400D0069AD76 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E037216D3FC50069AD76 /* Result.framework */; }; 9A73E040216D40170069AD76 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */; }; - 9A73E041216D40170069AD76 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E037216D3FC50069AD76 /* Result.framework */; }; 9A73E042216D40280069AD76 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */; }; - 9A73E043216D40280069AD76 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E037216D3FC50069AD76 /* Result.framework */; }; 9A73E049216D40800069AD76 /* ReactiveMapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73DFCC216D3C570069AD76 /* ReactiveMapKit.framework */; }; 9A73E04A216D40850069AD76 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */; }; 9A73E04C216D408D0069AD76 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E04B216D408D0069AD76 /* Nimble.framework */; }; @@ -197,13 +190,9 @@ 9A73E053216D40C80069AD76 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D04725EA19E49ED7006002AA /* ReactiveCocoa.framework */; }; 9A73E054216D40CD0069AD76 /* ReactiveMapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9AC03A571F7CC3BF00EC33C1 /* ReactiveMapKit.framework */; }; 9A73E055216D40D10069AD76 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */; }; - 9A73E056216D40D10069AD76 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E037216D3FC50069AD76 /* Result.framework */; }; 9A73E059216D40E30069AD76 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */; }; - 9A73E05A216D40E30069AD76 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E037216D3FC50069AD76 /* Result.framework */; }; 9A73E05B216D40EC0069AD76 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */; }; - 9A73E05C216D40EC0069AD76 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E037216D3FC50069AD76 /* Result.framework */; }; 9A73E05D216D40F70069AD76 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */; }; - 9A73E05E216D40F70069AD76 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E037216D3FC50069AD76 /* Result.framework */; }; 9A73E05F216D41FA0069AD76 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */; }; 9A7488481E3B8ACE00CD0317 /* DelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7488471E3B8ACE00CD0317 /* DelegateProxy.swift */; }; 9A7488491E3B8ACE00CD0317 /* DelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7488471E3B8ACE00CD0317 /* DelegateProxy.swift */; }; @@ -293,7 +282,6 @@ 9AFCBFE41EB1ABC0004B4C74 /* KVOKVCExtensionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AFCBFE21EB1ABC0004B4C74 /* KVOKVCExtensionSpec.swift */; }; 9AFCBFE51EB1ABC0004B4C74 /* KVOKVCExtensionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AFCBFE21EB1ABC0004B4C74 /* KVOKVCExtensionSpec.swift */; }; A91244E820389AEA0001BBCB /* MKLocalSearchRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91244E720389AEA0001BBCB /* MKLocalSearchRequest.swift */; }; - A9B315C91B3940980001CB9C /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; A9B315CA1B3940AB0001CB9C /* ReactiveCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; }; A9D8BA71207CD7090031733D /* UIResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D8BA70207CD7090031733D /* UIResponder.swift */; }; A9D8BA72207CD7090031733D /* UIResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D8BA70207CD7090031733D /* UIResponder.swift */; }; @@ -332,9 +320,6 @@ CD8401831CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8401821CEE8ED7009F0ABF /* CocoaActionSpec.swift */; }; CD8401841CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8401821CEE8ED7009F0ABF /* CocoaActionSpec.swift */; }; CD8401851CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8401821CEE8ED7009F0ABF /* CocoaActionSpec.swift */; }; - CDC42E2F1AE7AB8B00965373 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; - CDC42E311AE7AB8B00965373 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; }; - CDC42E331AE7AC6D00965373 /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CDC42E2E1AE7AB8B00965373 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; CDF066CA1CDC1CA200199626 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; }; CDF066CB1CDC1CA200199626 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D037672B19EDA75D00A782A9 /* Quick.framework */; }; D01B7B6219EDD8FE00D26E01 /* Nimble.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D05E662419EDD82000904ACA /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -438,7 +423,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 7DFBED211CDB8D8300EE435B /* Result.framework in Copy Frameworks */, 7DFBED201CDB8D7D00EE435B /* Nimble.framework in Copy Frameworks */, 7DFBED1F1CDB8D7800EE435B /* Quick.framework in Copy Frameworks */, 7DFBED1E1CDB8D7000EE435B /* ReactiveCocoa.framework in Copy Frameworks */, @@ -452,7 +436,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - CDC42E331AE7AC6D00965373 /* Result.framework in Copy Frameworks */, D01B7B6219EDD8FE00D26E01 /* Nimble.framework in Copy Frameworks */, D01B7B6319EDD8FE00D26E01 /* Quick.framework in Copy Frameworks */, D01B7B6419EDD94B00D26E01 /* ReactiveCocoa.framework in Copy Frameworks */, @@ -556,7 +539,6 @@ 9A73DFF8216D3CEB0069AD76 /* ReactiveMapKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveMapKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 9A73E013216D3CEE0069AD76 /* ReactiveMapKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveMapKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ReactiveSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 9A73E037216D3FC50069AD76 /* Result.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Result.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9A73E04B216D408D0069AD76 /* Nimble.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Nimble.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9A73E04D216D408D0069AD76 /* Quick.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Quick.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9A7488471E3B8ACE00CD0317 /* DelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DelegateProxy.swift; sourceTree = ""; }; @@ -619,7 +601,6 @@ CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DynamicProperty.swift; sourceTree = ""; }; CD42C69A1E951F6900AA9504 /* ReactiveCocoaTestsConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactiveCocoaTestsConfiguration.swift; sourceTree = ""; }; CD8401821CEE8ED7009F0ABF /* CocoaActionSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CocoaActionSpec.swift; sourceTree = ""; }; - CDC42E2E1AE7AB8B00965373 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D03766B119EDA60000A782A9 /* test-data.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "test-data.json"; sourceTree = ""; }; D037672B19EDA75D00A782A9 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Quick.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D04725EA19E49ED7006002AA /* ReactiveCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -660,7 +641,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 57A4D2081BA13D7A00F7D4B1 /* Result.framework in Frameworks */, 9A73E03C216D3FFC0069AD76 /* ReactiveSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -673,7 +653,6 @@ CDF066CB1CDC1CA200199626 /* Quick.framework in Frameworks */, 7DFBED081CDB8C9500EE435B /* ReactiveCocoa.framework in Frameworks */, 9A73E059216D40E30069AD76 /* ReactiveSwift.framework in Frameworks */, - 9A73E05A216D40E30069AD76 /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -681,7 +660,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9A73E043216D40280069AD76 /* Result.framework in Frameworks */, 9A73E042216D40280069AD76 /* ReactiveSwift.framework in Frameworks */, 9A73E052216D40AC0069AD76 /* ReactiveCocoa.framework in Frameworks */, 9A73E051216D40A20069AD76 /* ReactiveMapKit.framework in Frameworks */, @@ -696,7 +674,6 @@ files = ( 9A73E01C216D3E700069AD76 /* ReactiveCocoa.framework in Frameworks */, 9A73E03E216D400D0069AD76 /* ReactiveSwift.framework in Frameworks */, - 9A73E03F216D400D0069AD76 /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -706,7 +683,6 @@ files = ( 9A73E05F216D41FA0069AD76 /* ReactiveCocoa.framework in Frameworks */, 9A73E036216D3FC50069AD76 /* ReactiveSwift.framework in Frameworks */, - 9A73E038216D3FC50069AD76 /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -716,7 +692,6 @@ files = ( 9A3C54F121726BD200E98207 /* Nimble.framework in Frameworks */, 9A3C54F321726BD200E98207 /* Quick.framework in Frameworks */, - 9A73E056216D40D10069AD76 /* Result.framework in Frameworks */, 9A73E055216D40D10069AD76 /* ReactiveSwift.framework in Frameworks */, 9A73E053216D40C80069AD76 /* ReactiveCocoa.framework in Frameworks */, 9A73E054216D40CD0069AD76 /* ReactiveMapKit.framework in Frameworks */, @@ -727,7 +702,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9A73E041216D40170069AD76 /* Result.framework in Frameworks */, 9A73E040216D40170069AD76 /* ReactiveSwift.framework in Frameworks */, 9A73E04A216D40850069AD76 /* ReactiveCocoa.framework in Frameworks */, 9A73E049216D40800069AD76 /* ReactiveMapKit.framework in Frameworks */, @@ -740,7 +714,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9A73E030216D3FAF0069AD76 /* Result.framework in Frameworks */, 9A73E03D216D40070069AD76 /* ReactiveSwift.framework in Frameworks */, 9A73E015216D3E660069AD76 /* ReactiveCocoa.framework in Frameworks */, ); @@ -750,7 +723,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - A9B315C91B3940980001CB9C /* Result.framework in Frameworks */, 9A73E03B216D3FF50069AD76 /* ReactiveSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -759,7 +731,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - CDC42E2F1AE7AB8B00965373 /* Result.framework in Frameworks */, 9A73E039216D3FEB0069AD76 /* ReactiveSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -772,7 +743,6 @@ 9A3C54ED21726A1200E98207 /* Quick.framework in Frameworks */, D04725F619E49ED7006002AA /* ReactiveCocoa.framework in Frameworks */, 9A73E05D216D40F70069AD76 /* ReactiveSwift.framework in Frameworks */, - 9A73E05E216D40F70069AD76 /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -780,7 +750,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - CDC42E311AE7AB8B00965373 /* Result.framework in Frameworks */, 9A73E03A216D3FF10069AD76 /* ReactiveSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -793,7 +762,6 @@ D037672F19EDA78B00A782A9 /* Quick.framework in Frameworks */, D047261719E49F82006002AA /* ReactiveCocoa.framework in Frameworks */, 9A73E05B216D40EC0069AD76 /* ReactiveSwift.framework in Frameworks */, - 9A73E05C216D40EC0069AD76 /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -932,7 +900,6 @@ 9A73E04B216D408D0069AD76 /* Nimble.framework */, 9A73E04D216D408D0069AD76 /* Quick.framework */, 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */, - 9A73E037216D3FC50069AD76 /* Result.framework */, ); name = Frameworks; sourceTree = ""; @@ -1066,7 +1033,6 @@ 9A1D06751D9415FB00ACF44C /* ObjCRuntimeAliases.h */, 9AA0BD971DDE7A2200531FCF /* ObjCRuntimeAliases.m */, 9AE7C2A21DDD768500F7534C /* module.modulemap */, - CDC42E2E1AE7AB8B00965373 /* Result.framework */, D04725EE19E49ED7006002AA /* Info.plist */, ); name = "Supporting Files"; diff --git a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme index fd7a186857..ecd1e68d6b 100644 --- a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme +++ b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme @@ -6,20 +6,6 @@ parallelizeBuildables = "NO" buildImplicitDependencies = "NO"> - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ReactiveCocoa/AppKit/ActionProxy.swift b/ReactiveCocoa/AppKit/ActionProxy.swift index 6b95d7fe63..d3e34733c7 100644 --- a/ReactiveCocoa/AppKit/ActionProxy.swift +++ b/ReactiveCocoa/AppKit/ActionProxy.swift @@ -1,18 +1,17 @@ import AppKit import ReactiveSwift -import enum Result.NoError internal final class ActionProxy: NSObject { internal weak var target: AnyObject? internal var action: Selector? - internal let invoked: Signal + internal let invoked: Signal - private let observer: Signal.Observer + private let observer: Signal.Observer private unowned let owner: Owner internal init(owner: Owner, lifetime: Lifetime) { self.owner = owner - (invoked, observer) = Signal.pipe() + (invoked, observer) = Signal.pipe() lifetime.ended.observeCompleted(observer.sendCompleted) } diff --git a/ReactiveCocoa/AppKit/NSButton.swift b/ReactiveCocoa/AppKit/NSButton.swift index 33aa8e776e..613fb18013 100644 --- a/ReactiveCocoa/AppKit/NSButton.swift +++ b/ReactiveCocoa/AppKit/NSButton.swift @@ -1,5 +1,4 @@ import ReactiveSwift -import enum Result.NoError import AppKit extension Reactive where Base: NSButton { @@ -29,7 +28,7 @@ extension Reactive where Base: NSButton { #if swift(>=4.0) /// A signal of integer states (On, Off, Mixed), emitted by the button. - public var states: Signal { + public var states: Signal { return proxy.invoked.map { $0.state } } @@ -39,7 +38,7 @@ extension Reactive where Base: NSButton { } #else /// A signal of integer states (On, Off, Mixed), emitted by the button. - public var states: Signal { + public var states: Signal { return proxy.invoked.map { $0.state } } diff --git a/ReactiveCocoa/AppKit/NSControl.swift b/ReactiveCocoa/AppKit/NSControl.swift index f73f4393d2..bbd8a8da1c 100644 --- a/ReactiveCocoa/AppKit/NSControl.swift +++ b/ReactiveCocoa/AppKit/NSControl.swift @@ -1,5 +1,4 @@ import ReactiveSwift -import enum Result.NoError import AppKit extension NSControl: ActionMessageSending {} @@ -16,7 +15,7 @@ extension Reactive where Base: NSControl { } /// A signal of values in `NSAttributedString`, emitted by the control. - public var attributedStringValues: Signal { + public var attributedStringValues: Signal { return proxy.invoked.map { $0.attributedStringValue } } @@ -30,7 +29,7 @@ extension Reactive where Base: NSControl { } /// A signal of values in `Bool`, emitted by the control. - public var boolValues: Signal { + public var boolValues: Signal { #if swift(>=4.0) return proxy.invoked.map { $0.integerValue != NSControl.StateValue.off.rawValue } #else @@ -44,7 +43,7 @@ extension Reactive where Base: NSControl { } /// A signal of values in `Double`, emitted by the control. - public var doubleValues: Signal { + public var doubleValues: Signal { return proxy.invoked.map { $0.doubleValue } } @@ -54,7 +53,7 @@ extension Reactive where Base: NSControl { } /// A signal of values in `Float`, emitted by the control. - public var floatValues: Signal { + public var floatValues: Signal { return proxy.invoked.map { $0.floatValue } } @@ -64,7 +63,7 @@ extension Reactive where Base: NSControl { } /// A signal of values in `Int32`, emitted by the control. - public var intValues: Signal { + public var intValues: Signal { return proxy.invoked.map { $0.intValue } } @@ -74,7 +73,7 @@ extension Reactive where Base: NSControl { } /// A signal of values in `Int`, emitted by the control. - public var integerValues: Signal { + public var integerValues: Signal { return proxy.invoked.map { $0.integerValue } } @@ -84,7 +83,7 @@ extension Reactive where Base: NSControl { } /// A signal of values in `Any?`, emitted by the control. - public var objectValues: Signal { + public var objectValues: Signal { return proxy.invoked.map { $0.objectValue } } @@ -94,7 +93,7 @@ extension Reactive where Base: NSControl { } /// A signal of values in `String`, emitted by the control. - public var stringValues: Signal { + public var stringValues: Signal { return proxy.invoked.map { $0.stringValue } } } diff --git a/ReactiveCocoa/AppKit/NSImageView.swift b/ReactiveCocoa/AppKit/NSImageView.swift index f538720a6b..078e82eef3 100644 --- a/ReactiveCocoa/AppKit/NSImageView.swift +++ b/ReactiveCocoa/AppKit/NSImageView.swift @@ -1,5 +1,4 @@ import ReactiveSwift -import enum Result.NoError import AppKit extension Reactive where Base: NSImageView { diff --git a/ReactiveCocoa/AppKit/NSPopUpButton.swift b/ReactiveCocoa/AppKit/NSPopUpButton.swift index ef1186f356..4785f0060b 100644 --- a/ReactiveCocoa/AppKit/NSPopUpButton.swift +++ b/ReactiveCocoa/AppKit/NSPopUpButton.swift @@ -1,11 +1,10 @@ import ReactiveSwift import AppKit -import enum Result.NoError extension Reactive where Base: NSPopUpButton { /// A signal of selected indexes - public var selectedIndexes: Signal { + public var selectedIndexes: Signal { return proxy.invoked.map { $0.indexOfSelectedItem } } @@ -17,7 +16,7 @@ extension Reactive where Base: NSPopUpButton { } /// A signal of selected title - public var selectedTitles: Signal { + public var selectedTitles: Signal { return proxy.invoked.map { $0.titleOfSelectedItem }.skipNil() } @@ -29,13 +28,13 @@ extension Reactive where Base: NSPopUpButton { } } - public var selectedItems: Signal { + public var selectedItems: Signal { return proxy.invoked.map { $0.selectedItem }.skipNil() } /// A signal of selected tags - public var selectedTags: Signal { + public var selectedTags: Signal { return proxy.invoked.map { $0.selectedTag() } } diff --git a/ReactiveCocoa/AppKit/NSSegmentedControl.swift b/ReactiveCocoa/AppKit/NSSegmentedControl.swift index 2957e34196..ace144ce6f 100644 --- a/ReactiveCocoa/AppKit/NSSegmentedControl.swift +++ b/ReactiveCocoa/AppKit/NSSegmentedControl.swift @@ -1,5 +1,4 @@ import ReactiveSwift -import enum Result.NoError import AppKit extension Reactive where Base: NSSegmentedControl { @@ -9,7 +8,7 @@ extension Reactive where Base: NSSegmentedControl { } /// A signal of indexes of selections emitted by the segmented control. - public var selectedSegments: Signal { + public var selectedSegments: Signal { return proxy.invoked.map { $0.selectedSegment } } @@ -18,5 +17,5 @@ extension Reactive where Base: NSSegmentedControl { /// Changes the selected segment of the segmented control. public var selectedSegmentIndex: BindingTarget { return selectedSegment } /// A signal of indexes of selections emitted by the segmented control. - public var selectedSegmentIndexes: Signal { return selectedSegments } + public var selectedSegmentIndexes: Signal { return selectedSegments } } diff --git a/ReactiveCocoa/AppKit/NSSlider.swift b/ReactiveCocoa/AppKit/NSSlider.swift index 64f14735cf..32831104fd 100644 --- a/ReactiveCocoa/AppKit/NSSlider.swift +++ b/ReactiveCocoa/AppKit/NSSlider.swift @@ -1,5 +1,4 @@ import ReactiveSwift -import enum Result.NoError import AppKit extension Reactive where Base: NSSlider { @@ -7,5 +6,5 @@ extension Reactive where Base: NSSlider { // Provided for cross-platform compatibility public var value: BindingTarget { return floatValue } - public var values: Signal { return floatValues } + public var values: Signal { return floatValues } } diff --git a/ReactiveCocoa/AppKit/NSTextField.swift b/ReactiveCocoa/AppKit/NSTextField.swift index e8a27095be..9c57c705e8 100644 --- a/ReactiveCocoa/AppKit/NSTextField.swift +++ b/ReactiveCocoa/AppKit/NSTextField.swift @@ -1,9 +1,8 @@ import ReactiveSwift import AppKit -import enum Result.NoError extension Reactive where Base: NSTextField { - private var notifications: Signal { + private var notifications: Signal { #if swift(>=4.0) let name = NSControl.textDidChangeNotification #else @@ -17,14 +16,14 @@ extension Reactive where Base: NSTextField { } /// A signal of values in `String` from the text field upon any changes. - public var continuousStringValues: Signal { + public var continuousStringValues: Signal { return notifications .map { ($0.object as! NSTextField).stringValue } } /// A signal of values in `NSAttributedString` from the text field upon any /// changes. - public var continuousAttributedStringValues: Signal { + public var continuousAttributedStringValues: Signal { return notifications .map { ($0.object as! NSTextField).attributedStringValue } } diff --git a/ReactiveCocoa/AppKit/NSTextView.swift b/ReactiveCocoa/AppKit/NSTextView.swift index f1c5362ffb..b4ddd3ec5e 100644 --- a/ReactiveCocoa/AppKit/NSTextView.swift +++ b/ReactiveCocoa/AppKit/NSTextView.swift @@ -1,9 +1,8 @@ import ReactiveSwift import AppKit -import enum Result.NoError extension Reactive where Base: NSTextView { - private var notifications: Signal { + private var notifications: Signal { #if swift(>=4.0) let name = NSTextView.didChangeNotification #else @@ -17,7 +16,7 @@ extension Reactive where Base: NSTextView { } /// A signal of values in `String` from the text field upon any changes. - public var continuousStringValues: Signal { + public var continuousStringValues: Signal { return notifications .map { notification in let textView = notification.object as! NSTextView @@ -31,7 +30,7 @@ extension Reactive where Base: NSTextView { /// A signal of values in `NSAttributedString` from the text field upon any /// changes. - public var continuousAttributedStringValues: Signal { + public var continuousAttributedStringValues: Signal { return notifications .map { ($0.object as! NSTextView).attributedString() } } diff --git a/ReactiveCocoa/AppKit/NSView.swift b/ReactiveCocoa/AppKit/NSView.swift index 18a18eaaae..cb1916c2af 100644 --- a/ReactiveCocoa/AppKit/NSView.swift +++ b/ReactiveCocoa/AppKit/NSView.swift @@ -1,6 +1,5 @@ import ReactiveSwift import AppKit -import enum Result.NoError extension Reactive where Base: NSView { /// Sets the visibility of the view. diff --git a/ReactiveCocoa/AppKit/ReusableComponents.swift b/ReactiveCocoa/AppKit/ReusableComponents.swift index 3656d9de9f..9ba77b7a36 100644 --- a/ReactiveCocoa/AppKit/ReusableComponents.swift +++ b/ReactiveCocoa/AppKit/ReusableComponents.swift @@ -1,16 +1,15 @@ import AppKit import ReactiveSwift -import enum Result.NoError extension Reactive where Base: NSView { - public var prepareForReuse: Signal<(), NoError> { + public var prepareForReuse: Signal<(), Never> { return trigger(for: #selector(base.prepareForReuse)) } } extension Reactive where Base: NSObject, Base: NSCollectionViewElement { @available(macOS 10.11, *) - public var prepareForReuse: Signal<(), NoError> { + public var prepareForReuse: Signal<(), Never> { return trigger(for: #selector(base.prepareForReuse)) } } diff --git a/ReactiveCocoa/CocoaAction.swift b/ReactiveCocoa/CocoaAction.swift index 42135a601d..7efa82d8df 100644 --- a/ReactiveCocoa/CocoaAction.swift +++ b/ReactiveCocoa/CocoaAction.swift @@ -1,6 +1,5 @@ import Foundation import ReactiveSwift -import enum Result.NoError /// CocoaAction wraps an `Action` for use by a UI control (such as `NSControl` or /// `UIControl`). diff --git a/ReactiveCocoa/CocoaTarget.swift b/ReactiveCocoa/CocoaTarget.swift index cfa2d9bd8d..46d3f3490f 100644 --- a/ReactiveCocoa/CocoaTarget.swift +++ b/ReactiveCocoa/CocoaTarget.swift @@ -1,6 +1,5 @@ import Foundation import ReactiveSwift -import enum Result.NoError /// A target that accepts action messages. internal final class CocoaTarget: NSObject { @@ -9,12 +8,12 @@ internal final class CocoaTarget: NSObject { case sending(queue: [Value]) } - private let observer: Signal.Observer + private let observer: Signal.Observer private let transform: (Any?) -> Value private var state: State - internal init(_ observer: Signal.Observer, transform: @escaping (Any?) -> Value) { + internal init(_ observer: Signal.Observer, transform: @escaping (Any?) -> Value) { self.observer = observer self.transform = transform self.state = .idle @@ -53,7 +52,7 @@ internal final class CocoaTarget: NSObject { } extension CocoaTarget where Value == Void { - internal convenience init(_ observer: Signal<(), NoError>.Observer) { + internal convenience init(_ observer: Signal<(), Never>.Observer) { self.init(observer, transform: { _ in }) } } diff --git a/ReactiveCocoa/DelegateProxy.swift b/ReactiveCocoa/DelegateProxy.swift index c9ee6d6f73..37273c2184 100644 --- a/ReactiveCocoa/DelegateProxy.swift +++ b/ReactiveCocoa/DelegateProxy.swift @@ -1,5 +1,4 @@ import ReactiveSwift -import enum Result.NoError internal class DelegateProxy: NSObject { internal weak var forwardee: Delegate? { @@ -22,13 +21,13 @@ internal class DelegateProxy: NSObject { return interceptedSelectors.contains(selector) ? nil : forwardee } - func intercept(_ selector: Selector) -> Signal<(), NoError> { + func intercept(_ selector: Selector) -> Signal<(), Never> { interceptedSelectors.insert(selector) originalSetter(self) return self.reactive.trigger(for: selector).take(during: lifetime) } - func intercept(_ selector: Selector) -> Signal<[Any?], NoError> { + func intercept(_ selector: Selector) -> Signal<[Any?], Never> { interceptedSelectors.insert(selector) originalSetter(self) return self.reactive.signal(for: selector).take(during: lifetime) diff --git a/ReactiveCocoa/Deprecations+Removals.swift b/ReactiveCocoa/Deprecations+Removals.swift index dc685c598e..52687b8f44 100644 --- a/ReactiveCocoa/Deprecations+Removals.swift +++ b/ReactiveCocoa/Deprecations+Removals.swift @@ -1,5 +1,4 @@ import ReactiveSwift -import enum Result.NoError extension Action { @available(*, unavailable, message:"Use the `CocoaAction` initializers instead.") @@ -8,7 +7,7 @@ extension Action { extension Reactive where Base: NSObject { @available(*, deprecated, renamed: "producer(forKeyPath:)") - public func values(forKeyPath keyPath: String) -> SignalProducer { + public func values(forKeyPath keyPath: String) -> SignalProducer { return producer(forKeyPath: keyPath) } } diff --git a/ReactiveCocoa/DynamicProperty.swift b/ReactiveCocoa/DynamicProperty.swift index 08362c04d9..d102b85e80 100644 --- a/ReactiveCocoa/DynamicProperty.swift +++ b/ReactiveCocoa/DynamicProperty.swift @@ -1,6 +1,5 @@ import Foundation import ReactiveSwift -import enum Result.NoError /// A typed mutable property view to a certain key path of an Objective-C object using /// Key-Value Coding and Key-Value Observing. @@ -38,11 +37,11 @@ public final class DynamicProperty: MutablePropertyProtocol { /// /// - important: This only works if the object given to init() is KVO-compliant. /// Most UI controls are not! - public var producer: SignalProducer { + public var producer: SignalProducer { return cache.producer } - public var signal: Signal { + public var signal: Signal { return cache.signal } diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index df3a70e190..cde2d0f023 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -1,6 +1,5 @@ import Foundation import ReactiveSwift -import enum Result.NoError /// Whether the runtime subclass has already been prepared for method /// interception. @@ -27,7 +26,7 @@ extension Reactive where Base: NSObject { /// - selector: The selector to observe. /// /// - returns: A trigger signal. - public func trigger(for selector: Selector) -> Signal<(), NoError> { + public func trigger(for selector: Selector) -> Signal<(), Never> { return base.intercept(selector).map { _ in } } @@ -44,7 +43,7 @@ extension Reactive where Base: NSObject { /// - selector: The selector to observe. /// /// - returns: A signal that sends an array of bridged arguments. - public func signal(for selector: Selector) -> Signal<[Any?], NoError> { + public func signal(for selector: Selector) -> Signal<[Any?], Never> { return base.intercept(selector).map(unpackInvocation) } } @@ -58,7 +57,7 @@ extension NSObject { /// /// - returns: A signal that sends the corresponding `NSInvocation` after /// every invocation of the method. - @nonobjc fileprivate func intercept(_ selector: Selector) -> Signal { + @nonobjc fileprivate func intercept(_ selector: Selector) -> Signal { guard let method = class_getInstanceMethod(objcClass, selector) else { fatalError("Selector `\(selector)` does not exist in class `\(String(describing: objcClass))`.") } @@ -278,7 +277,7 @@ private func setupMethodSignatureCaching(_ realClass: AnyClass, _ signatureCache /// The state of an intercepted method specific to an instance. private final class InterceptingState { - let (signal, observer) = Signal.pipe() + let (signal, observer) = Signal.pipe() /// Initialize a state specific to an instance. /// diff --git a/ReactiveCocoa/NSObject+KeyValueObserving.swift b/ReactiveCocoa/NSObject+KeyValueObserving.swift index 5111d024ac..09d78684dd 100644 --- a/ReactiveCocoa/NSObject+KeyValueObserving.swift +++ b/ReactiveCocoa/NSObject+KeyValueObserving.swift @@ -1,6 +1,5 @@ import Foundation import ReactiveSwift -import enum Result.NoError extension Reactive where Base: NSObject { /// Create a producer which sends the current value and all the subsequent @@ -13,7 +12,7 @@ extension Reactive where Base: NSObject { /// /// - returns: A producer emitting values of the property specified by the /// key path. - public func producer(forKeyPath keyPath: String) -> SignalProducer { + public func producer(forKeyPath keyPath: String) -> SignalProducer { return SignalProducer { observer, lifetime in let disposable = KeyValueObserver.observe( self.base, @@ -42,7 +41,7 @@ extension Reactive where Base: NSObject { /// /// - returns: A producer emitting values of the property specified by the /// key path. - public func signal(forKeyPath keyPath: String) -> Signal { + public func signal(forKeyPath keyPath: String) -> Signal { return Signal { observer, signalLifetime in signalLifetime += KeyValueObserver.observe( self.base, @@ -57,7 +56,7 @@ extension Reactive where Base: NSObject { private func producer( for keyPath: KeyPath, transform: @escaping (Any?) -> U - ) -> SignalProducer { + ) -> SignalProducer { guard let kvcKeyPath = keyPath._kvcKeyPathString else { fatalError("Cannot use `producer(for:)` on a non Objective-C property.") } @@ -77,7 +76,7 @@ extension Reactive where Base: NSObject { private func signal( for keyPath: KeyPath, transform: @escaping (Any?) -> U - ) -> Signal { + ) -> Signal { guard let kvcKeyPath = keyPath._kvcKeyPathString else { fatalError("Cannot use `signal(for:)` on a non Objective-C property.") } @@ -103,7 +102,7 @@ extension Reactive where Base: NSObject { /// /// - returns: A producer emitting values of the property specified by the /// key path. - public func producer(for keyPath: KeyPath) -> SignalProducer { + public func producer(for keyPath: KeyPath) -> SignalProducer { return producer(for: keyPath) { $0 as! U? } } @@ -119,7 +118,7 @@ extension Reactive where Base: NSObject { /// /// - returns: A producer emitting values of the property specified by the /// key path. - public func signal(for keyPath: KeyPath) -> Signal { + public func signal(for keyPath: KeyPath) -> Signal { return signal(for: keyPath) { $0 as! U? } } @@ -133,7 +132,7 @@ extension Reactive where Base: NSObject { /// /// - returns: A producer emitting values of the property specified by the /// key path. - public func producer(for keyPath: KeyPath) -> SignalProducer { + public func producer(for keyPath: KeyPath) -> SignalProducer { return producer(for: keyPath) { $0 as! U } } @@ -149,7 +148,7 @@ extension Reactive where Base: NSObject { /// /// - returns: A producer emitting values of the property specified by the /// key path. - public func signal(for keyPath: KeyPath) -> Signal { + public func signal(for keyPath: KeyPath) -> Signal { return signal(for: keyPath) { $0 as! U } } } @@ -186,10 +185,10 @@ extension Property { // `Property(_:)` uses only the producer is explioted here to achieve the same effect. private final class UnsafeKVOProperty: PropertyProtocol { var value: Value { fatalError() } - var signal: Signal { fatalError() } - let producer: SignalProducer + var signal: Signal { fatalError() } + let producer: SignalProducer - init(producer: SignalProducer) { + init(producer: SignalProducer) { self.producer = producer } diff --git a/ReactiveCocoa/UIKit/ReusableComponents.swift b/ReactiveCocoa/UIKit/ReusableComponents.swift index 3beafd23cb..4c893a777f 100644 --- a/ReactiveCocoa/UIKit/ReusableComponents.swift +++ b/ReactiveCocoa/UIKit/ReusableComponents.swift @@ -1,13 +1,12 @@ import UIKit import ReactiveSwift -import enum Result.NoError @objc public protocol Reusable: class { func prepareForReuse() } extension Reactive where Base: NSObject, Base: Reusable { - public var prepareForReuse: Signal<(), NoError> { + public var prepareForReuse: Signal<(), Never> { return trigger(for: #selector(base.prepareForReuse)) } } diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index 7be2d7e919..f002157978 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -1,6 +1,5 @@ import ReactiveSwift import UIKit -import enum Result.NoError extension Reactive where Base: UIControl { /// The current associated action of `self`, with its registered event mask @@ -48,7 +47,7 @@ extension Reactive where Base: UIControl { /// - controlEvents: The control event mask. /// /// - returns: A signal that sends the control each time the control event occurs. - public func controlEvents(_ controlEvents: UIControl.Event) -> Signal { + public func controlEvents(_ controlEvents: UIControl.Event) -> Signal { return mapControlEvents(controlEvents, { $0 }) } @@ -71,7 +70,7 @@ extension Reactive where Base: UIControl { /// /// - returns: A signal that sends the reduced value from the control each time the /// control event occurs. - public func mapControlEvents(_ controlEvents: UIControl.Event, _ transform: @escaping (Base) -> Value) -> Signal { + public func mapControlEvents(_ controlEvents: UIControl.Event, _ transform: @escaping (Base) -> Value) -> Signal { return Signal { observer, signalLifetime in let receiver = CocoaTarget(observer) { transform($0 as! Base) } base.addTarget(receiver, @@ -91,7 +90,7 @@ extension Reactive where Base: UIControl { } @available(*, unavailable, renamed: "controlEvents(_:)") - public func trigger(for controlEvents: UIControl.Event) -> Signal<(), NoError> { + public func trigger(for controlEvents: UIControl.Event) -> Signal<(), Never> { fatalError() } diff --git a/ReactiveCocoa/UIKit/UIGestureRecognizer.swift b/ReactiveCocoa/UIKit/UIGestureRecognizer.swift index bfab11ad91..dbd59ff73d 100644 --- a/ReactiveCocoa/UIKit/UIGestureRecognizer.swift +++ b/ReactiveCocoa/UIKit/UIGestureRecognizer.swift @@ -1,12 +1,11 @@ import ReactiveSwift import UIKit -import enum Result.NoError extension Reactive where Base: UIGestureRecognizer { /// Create a signal which sends a `next` event for each gesture event /// /// - returns: A trigger signal. - public var stateChanged: Signal { + public var stateChanged: Signal { return Signal { observer, signalLifetime in let receiver = CocoaTarget(observer) { gestureRecognizer in return gestureRecognizer as! Base diff --git a/ReactiveCocoa/UIKit/UISegmentedControl.swift b/ReactiveCocoa/UIKit/UISegmentedControl.swift index 33c4e4055e..6c255a6750 100644 --- a/ReactiveCocoa/UIKit/UISegmentedControl.swift +++ b/ReactiveCocoa/UIKit/UISegmentedControl.swift @@ -1,5 +1,4 @@ import ReactiveSwift -import enum Result.NoError import UIKit extension Reactive where Base: UISegmentedControl { @@ -9,7 +8,7 @@ extension Reactive where Base: UISegmentedControl { } /// A signal of indexes of selections emitted by the segmented control. - public var selectedSegmentIndexes: Signal { + public var selectedSegmentIndexes: Signal { return mapControlEvents(.valueChanged) { $0.selectedSegmentIndex } } } diff --git a/ReactiveCocoa/UIKit/UITextField.swift b/ReactiveCocoa/UIKit/UITextField.swift index e6be8d9507..4a04b20a01 100644 --- a/ReactiveCocoa/UIKit/UITextField.swift +++ b/ReactiveCocoa/UIKit/UITextField.swift @@ -1,5 +1,4 @@ import ReactiveSwift -import enum Result.NoError import UIKit extension Reactive where Base: UITextField { @@ -12,14 +11,14 @@ extension Reactive where Base: UITextField { /// /// - note: To observe text values that change on all editing events, /// see `continuousTextValues`. - public var textValues: Signal { + public var textValues: Signal { return mapControlEvents([.editingDidEnd, .editingDidEndOnExit]) { $0.text ?? "" } } /// A signal of text values emitted by the text field upon any changes. /// /// - note: To observe text values only when editing ends, see `textValues`. - public var continuousTextValues: Signal { + public var continuousTextValues: Signal { return mapControlEvents(.allEditingEvents) { $0.text ?? "" } } @@ -42,14 +41,14 @@ extension Reactive where Base: UITextField { /// /// - note: To observe attributed text values that change on all editing events, /// see `continuousAttributedTextValues`. - public var attributedTextValues: Signal { + public var attributedTextValues: Signal { return mapControlEvents([.editingDidEnd, .editingDidEndOnExit]) { $0.attributedText ?? NSAttributedString() } } /// A signal of attributed text values emitted by the text field upon any changes. /// /// - note: To observe attributed text values only when editing ends, see `attributedTextValues`. - public var continuousAttributedTextValues: Signal { + public var continuousAttributedTextValues: Signal { return mapControlEvents(.allEditingEvents) { $0.attributedText ?? NSAttributedString() } } diff --git a/ReactiveCocoa/UIKit/UITextView.swift b/ReactiveCocoa/UIKit/UITextView.swift index bbb36d6c3b..6f7adf3d2a 100644 --- a/ReactiveCocoa/UIKit/UITextView.swift +++ b/ReactiveCocoa/UIKit/UITextView.swift @@ -1,6 +1,5 @@ import ReactiveSwift import UIKit -import enum Result.NoError private class TextViewDelegateProxy: DelegateProxy, UITextViewDelegate { @objc func textViewDidChangeSelection(_ textView: UITextView) { @@ -20,7 +19,7 @@ extension Reactive where Base: UITextView { return makeBindingTarget { $0.text = $1 } } - private func textValues(forName name: NSNotification.Name) -> Signal { + private func textValues(forName name: NSNotification.Name) -> Signal { return NotificationCenter.default .reactive .notifications(forName: name, object: base) @@ -32,14 +31,14 @@ extension Reactive where Base: UITextView { /// /// - note: To observe text values that change on all editing events, /// see `continuousTextValues`. - public var textValues: Signal { + public var textValues: Signal { return textValues(forName: UITextView.textDidEndEditingNotification) } /// A signal of text values emitted by the text view upon any changes. /// /// - note: To observe text values only when editing ends, see `textValues`. - public var continuousTextValues: Signal { + public var continuousTextValues: Signal { return textValues(forName: UITextView.textDidChangeNotification) } @@ -48,7 +47,7 @@ extension Reactive where Base: UITextView { return makeBindingTarget { $0.attributedText = $1 } } - private func attributedTextValues(forName name: NSNotification.Name) -> Signal { + private func attributedTextValues(forName name: NSNotification.Name) -> Signal { return NotificationCenter.default .reactive .notifications(forName: name, object: base) @@ -60,19 +59,19 @@ extension Reactive where Base: UITextView { /// /// - note: To observe attributed text values that change on all editing events, /// see `continuousAttributedTextValues`. - public var attributedTextValues: Signal { + public var attributedTextValues: Signal { return attributedTextValues(forName: UITextView.textDidEndEditingNotification) } /// A signal of attributed text values emitted by the text view upon any changes. /// /// - note: To observe text values only when editing ends, see `attributedTextValues`. - public var continuousAttributedTextValues: Signal { + public var continuousAttributedTextValues: Signal { return attributedTextValues(forName: UITextView.textDidChangeNotification) } /// A signal of range values emitted by the text view upon any selection change. - public var selectedRangeValues: Signal { + public var selectedRangeValues: Signal { return proxy.intercept(#selector(UITextViewDelegate.textViewDidChangeSelection)) .map { [unowned base] in base.selectedRange } } diff --git a/ReactiveCocoa/UIKit/UIViewController.swift b/ReactiveCocoa/UIKit/UIViewController.swift index 0b94554a1f..b9fd837a35 100644 --- a/ReactiveCocoa/UIKit/UIViewController.swift +++ b/ReactiveCocoa/UIKit/UIViewController.swift @@ -1,6 +1,5 @@ import ReactiveSwift import UIKit -import enum Result.NoError extension Reactive where Base: UIViewController { /// Set's the title of the view controller. @@ -9,32 +8,32 @@ extension Reactive where Base: UIViewController { } /// A signal that sends a value event every time `viewWillAppear` is invoked. - public var viewWillAppear: Signal { + public var viewWillAppear: Signal { return trigger(for: #selector(Base.viewWillAppear)) } /// A signal that sends a value event every time `viewDidAppear` is invoked. - public var viewDidAppear: Signal { + public var viewDidAppear: Signal { return trigger(for: #selector(Base.viewDidAppear)) } /// A signal that sends a value event every time `viewWillDisappear` is invoked. - public var viewWillDisappear: Signal { + public var viewWillDisappear: Signal { return trigger(for: #selector(Base.viewWillDisappear)) } /// A signal that sends a value event every time `viewDidDisappear` is invoked. - public var viewDidDisappear: Signal { + public var viewDidDisappear: Signal { return trigger(for: #selector(Base.viewDidDisappear)) } /// A signal that sends a value event every time `viewWillLayoutSubviews` is invoked. - public var viewWillLayoutSubviews: Signal { + public var viewWillLayoutSubviews: Signal { return trigger(for: #selector(Base.viewWillLayoutSubviews)) } /// A signal that sends a value event every time `viewDidLayoutSubviews` is invoked. - public var viewDidLayoutSubviews: Signal { + public var viewDidLayoutSubviews: Signal { return trigger(for: #selector(Base.viewDidLayoutSubviews)) } } diff --git a/ReactiveCocoa/UIKit/iOS/UIDatePicker.swift b/ReactiveCocoa/UIKit/iOS/UIDatePicker.swift index 356a1bf6bb..6cdde0c264 100644 --- a/ReactiveCocoa/UIKit/iOS/UIDatePicker.swift +++ b/ReactiveCocoa/UIKit/iOS/UIDatePicker.swift @@ -1,5 +1,4 @@ import ReactiveSwift -import enum Result.NoError import UIKit extension Reactive where Base: UIDatePicker { @@ -9,7 +8,7 @@ extension Reactive where Base: UIDatePicker { } /// A signal of dates emitted by the date picker. - public var dates: Signal { + public var dates: Signal { return mapControlEvents(.valueChanged) { $0.date } } } diff --git a/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift b/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift index 6d363c5994..573eb3a702 100644 --- a/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift +++ b/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift @@ -1,6 +1,5 @@ import UIKit import ReactiveSwift -import enum Result.NoError /// The type of system keyboard events. public enum KeyboardEvent { @@ -81,7 +80,7 @@ extension Reactive where Base: NotificationCenter { /// - event: The type of system keyboard event to observe. /// /// - returns: A `Signal` that emits the context of system keyboard event. - public func keyboard(_ event: KeyboardEvent) -> Signal { + public func keyboard(_ event: KeyboardEvent) -> Signal { return notifications(forName: event.notificationName) .map { notification in KeyboardChangeContext(userInfo: notification.userInfo!, event: event) } } @@ -94,7 +93,7 @@ extension Reactive where Base: NotificationCenter { /// - tail: Rest of the types of system keyboard events to observe. /// /// - returns: A `Signal` that emits the context of system keyboard events. - public func keyboard(_ first: KeyboardEvent, _ second: KeyboardEvent, _ tail: KeyboardEvent...) -> Signal { + public func keyboard(_ first: KeyboardEvent, _ second: KeyboardEvent, _ tail: KeyboardEvent...) -> Signal { let events = [first, second] + tail return .merge(events.map(keyboard)) } @@ -104,7 +103,7 @@ extension Reactive where Base: NotificationCenter { /// /// - returns: A `Signal` that emits the context of every change in the /// system keyboard's frame. - public var keyboardChange: Signal { + public var keyboardChange: Signal { return keyboard(.willChangeFrame) } } diff --git a/ReactiveCocoa/UIKit/iOS/UIPickerView.swift b/ReactiveCocoa/UIKit/iOS/UIPickerView.swift index 91e02a1514..d947fcb73b 100644 --- a/ReactiveCocoa/UIKit/iOS/UIPickerView.swift +++ b/ReactiveCocoa/UIKit/iOS/UIPickerView.swift @@ -1,5 +1,4 @@ import ReactiveSwift -import enum Result.NoError import UIKit private class PickerViewDelegateProxy: DelegateProxy, UIPickerViewDelegate { @@ -35,7 +34,7 @@ extension Reactive where Base: UIPickerView { /// Create a signal which sends a `value` event for each row selection /// /// - returns: A trigger signal. - public var selections: Signal<(row: Int, component: Int), NoError> { + public var selections: Signal<(row: Int, component: Int), Never> { return proxy.intercept(#selector(UIPickerViewDelegate.pickerView(_:didSelectRow:inComponent:))) .map { (row: $0[1] as! Int, component: $0[2] as! Int) } } diff --git a/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift b/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift index ae485851f8..d8b71aa7ca 100644 --- a/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift +++ b/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift @@ -1,5 +1,4 @@ import ReactiveSwift -import enum Result.NoError import UIKit extension Reactive where Base: UIRefreshControl { diff --git a/ReactiveCocoa/UIKit/iOS/UISearchBar.swift b/ReactiveCocoa/UIKit/iOS/UISearchBar.swift index 994be130c8..6d6a4aa56e 100644 --- a/ReactiveCocoa/UIKit/iOS/UISearchBar.swift +++ b/ReactiveCocoa/UIKit/iOS/UISearchBar.swift @@ -1,5 +1,4 @@ import ReactiveSwift -import enum Result.NoError import UIKit private class SearchBarDelegateProxy: DelegateProxy, UISearchBarDelegate { @@ -57,7 +56,7 @@ extension Reactive where Base: UISearchBar { /// /// - note: To observe text values that change on all editing events, /// see `continuousTextValues`. - public var textValues: Signal { + public var textValues: Signal { return proxy.intercept(#selector(UISearchBarDelegate.searchBarTextDidEndEditing)) .map { [unowned base] in base.text } } @@ -65,44 +64,44 @@ extension Reactive where Base: UISearchBar { /// A signal of text values emitted by the search bar upon any changes. /// /// - note: To observe text values only when editing ends, see `textValues`. - public var continuousTextValues: Signal { + public var continuousTextValues: Signal { return proxy.intercept(#selector(proxy.searchBar(_:textDidChange:))) .map { [unowned base] in base.text } } /// A signal of the latest selected scope button index upon any user selection. - public var selectedScopeButtonIndices: Signal { + public var selectedScopeButtonIndices: Signal { return proxy.intercept(#selector(proxy.searchBar(_:selectedScopeButtonIndexDidChange:))) .map { $0[1] as! Int } } /// A void signal emitted by the search bar upon any click on the cancel button - public var cancelButtonClicked: Signal { + public var cancelButtonClicked: Signal { return proxy.intercept(#selector(proxy.searchBarCancelButtonClicked)) } /// A void signal emitted by the search bar upon any click on the search button - public var searchButtonClicked: Signal { + public var searchButtonClicked: Signal { return proxy.intercept(#selector(proxy.searchBarSearchButtonClicked(_:))) } /// A void signal emitted by the search bar upon any click on the bookmark button - public var bookmarkButtonClicked: Signal { + public var bookmarkButtonClicked: Signal { return proxy.intercept(#selector(proxy.searchBarBookmarkButtonClicked)) } /// A void signal emitted by the search bar upon any click on the bookmark button - public var resultsListButtonClicked: Signal { + public var resultsListButtonClicked: Signal { return proxy.intercept(#selector(proxy.searchBarResultsListButtonClicked)) } /// A void signal emitted by the search bar upon start of editing - public var textDidBeginEditing: Signal { + public var textDidBeginEditing: Signal { return proxy.intercept(#selector(proxy.searchBarTextDidBeginEditing)) } /// A void signal emitted by the search bar upon end of editing - public var textDidEndEditing: Signal { + public var textDidEndEditing: Signal { return proxy.intercept(#selector(proxy.searchBarTextDidEndEditing)) } diff --git a/ReactiveCocoa/UIKit/iOS/UISlider.swift b/ReactiveCocoa/UIKit/iOS/UISlider.swift index 161fb59a74..ea030f3ae6 100644 --- a/ReactiveCocoa/UIKit/iOS/UISlider.swift +++ b/ReactiveCocoa/UIKit/iOS/UISlider.swift @@ -1,6 +1,5 @@ import UIKit import ReactiveSwift -import enum Result.NoError extension Reactive where Base: UISlider { @@ -24,7 +23,7 @@ extension Reactive where Base: UISlider { /// /// - note: If slider's `isContinuous` property is `false` then values are /// sent only when user releases the slider. - public var values: Signal { + public var values: Signal { return mapControlEvents(.valueChanged) { $0.value } } } diff --git a/ReactiveCocoa/UIKit/iOS/UIStepper.swift b/ReactiveCocoa/UIKit/iOS/UIStepper.swift index 44e9a108e3..b92b577589 100644 --- a/ReactiveCocoa/UIKit/iOS/UIStepper.swift +++ b/ReactiveCocoa/UIKit/iOS/UIStepper.swift @@ -1,6 +1,5 @@ import UIKit import ReactiveSwift -import enum Result.NoError extension Reactive where Base: UIStepper { @@ -21,7 +20,7 @@ extension Reactive where Base: UIStepper { /// A signal of double values emitted by the stepper upon each user's /// interaction. - public var values: Signal { + public var values: Signal { return mapControlEvents(.valueChanged) { $0.value } } } diff --git a/ReactiveCocoa/UIKit/iOS/UISwitch.swift b/ReactiveCocoa/UIKit/iOS/UISwitch.swift index 937e4e3a13..e714ec2044 100644 --- a/ReactiveCocoa/UIKit/iOS/UISwitch.swift +++ b/ReactiveCocoa/UIKit/iOS/UISwitch.swift @@ -1,5 +1,4 @@ import ReactiveSwift -import enum Result.NoError import UIKit extension Reactive where Base: UISwitch { @@ -24,7 +23,7 @@ extension Reactive where Base: UISwitch { } /// A signal of on-off states in `Bool` emitted by the switch. - public var isOnValues: Signal { + public var isOnValues: Signal { return mapControlEvents(.valueChanged) { $0.isOn } } } diff --git a/ReactiveCocoaTests/AppKit/ActionProxySpec.swift b/ReactiveCocoaTests/AppKit/ActionProxySpec.swift index f7b5c6f0d9..ff46edd4c4 100644 --- a/ReactiveCocoaTests/AppKit/ActionProxySpec.swift +++ b/ReactiveCocoaTests/AppKit/ActionProxySpec.swift @@ -1,6 +1,5 @@ import Quick import Nimble -import enum Result.NoError import ReactiveSwift @testable import ReactiveCocoa diff --git a/ReactiveCocoaTests/AppKit/NSButtonSpec.swift b/ReactiveCocoaTests/AppKit/NSButtonSpec.swift index 7bcab96947..c5b44f95af 100644 --- a/ReactiveCocoaTests/AppKit/NSButtonSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSButtonSpec.swift @@ -3,7 +3,6 @@ import Nimble import ReactiveSwift import ReactiveCocoa import AppKit -import enum Result.NoError class NSButtonSpec: QuickSpec { override func spec() { @@ -30,7 +29,7 @@ class NSButtonSpec: QuickSpec { it("should accept changes from bindings to its enabling state") { button.isEnabled = false - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() button.reactive.isEnabled <~ SignalProducer(pipeSignal) observer.send(value: true) @@ -44,7 +43,7 @@ class NSButtonSpec: QuickSpec { button.allowsMixedState = true button.state = RACNSOffState - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() button.reactive.state <~ SignalProducer(pipeSignal) observer.send(value: RACNSOffState) @@ -129,8 +128,8 @@ class NSButtonSpec: QuickSpec { let pressed = MutableProperty(false) - let (executionSignal, observer) = Signal.pipe() - let action = Action<(), Bool, NoError> { _ in + let (executionSignal, observer) = Signal.pipe() + let action = Action<(), Bool, Never> { _ in SignalProducer(executionSignal) } diff --git a/ReactiveCocoaTests/AppKit/NSCollectionViewSpec.swift b/ReactiveCocoaTests/AppKit/NSCollectionViewSpec.swift index b24313da68..e6bb162fec 100644 --- a/ReactiveCocoaTests/AppKit/NSCollectionViewSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSCollectionViewSpec.swift @@ -2,7 +2,6 @@ import Quick import Nimble import ReactiveCocoa import ReactiveSwift -import Result import AppKit @available(macOS 10.11, *) @@ -15,13 +14,13 @@ final class NSCollectionViewSpec: QuickSpec { } describe("reloadData") { - var bindingSignal: Signal<(), NoError>! - var bindingObserver: Signal<(), NoError>.Observer! + var bindingSignal: Signal<(), Never>! + var bindingObserver: Signal<(), Never>.Observer! var reloadDataCount = 0 beforeEach { - let (signal, observer) = Signal<(), NoError>.pipe() + let (signal, observer) = Signal<(), Never>.pipe() (bindingSignal, bindingObserver) = (signal, observer) reloadDataCount = 0 @@ -45,8 +44,8 @@ final class NSCollectionViewSpec: QuickSpec { @available(macOS 10.11, *) private final class TestCollectionView: NSCollectionView { - let reloadDataSignal: Signal<(), NoError> - private let reloadDataObserver: Signal<(), NoError>.Observer + let reloadDataSignal: Signal<(), Never> + private let reloadDataObserver: Signal<(), Never>.Observer override init(frame: CGRect) { (reloadDataSignal, reloadDataObserver) = Signal.pipe() diff --git a/ReactiveCocoaTests/AppKit/NSControlSpec.swift b/ReactiveCocoaTests/AppKit/NSControlSpec.swift index e95b95cdd9..240b909cd9 100644 --- a/ReactiveCocoaTests/AppKit/NSControlSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSControlSpec.swift @@ -1,6 +1,5 @@ import Quick import Nimble -import Result import ReactiveSwift import ReactiveCocoa import AppKit diff --git a/ReactiveCocoaTests/AppKit/NSImageViewSpec.swift b/ReactiveCocoaTests/AppKit/NSImageViewSpec.swift index 887f7868a9..db6d9e55da 100644 --- a/ReactiveCocoaTests/AppKit/NSImageViewSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSImageViewSpec.swift @@ -3,7 +3,6 @@ import Nimble import ReactiveSwift import ReactiveCocoa import AppKit -import enum Result.NoError class NSImageViewSpec: QuickSpec { override func spec() { @@ -23,7 +22,7 @@ class NSImageViewSpec: QuickSpec { it("should accept changes from bindings to its enabling state") { imageView.isEnabled = false - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() imageView.reactive.isEnabled <~ SignalProducer(pipeSignal) observer.send(value: true) @@ -35,7 +34,7 @@ class NSImageViewSpec: QuickSpec { it("should accept changes from bindings to its image") { - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() imageView.reactive.image <~ SignalProducer(pipeSignal) let theImage = NSImage(named: NSImage.userName) diff --git a/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift b/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift index 6a3cab0386..75eca540c4 100644 --- a/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift @@ -2,7 +2,6 @@ import Quick import Nimble import ReactiveCocoa import ReactiveSwift -import Result import AppKit final class NSPopUpButtonSpec: QuickSpec { @@ -56,7 +55,7 @@ final class NSPopUpButtonSpec: QuickSpec { } it("should accept changes from its bindings to its index values") { - let (signal, observer) = Signal.pipe() + let (signal, observer) = Signal.pipe() button.reactive.selectedIndex <~ SignalProducer(signal) observer.send(value: 1) @@ -71,7 +70,7 @@ final class NSPopUpButtonSpec: QuickSpec { } it("should accept changes from its bindings to its title values") { - let (signal, observer) = Signal.pipe() + let (signal, observer) = Signal.pipe() button.reactive.selectedTitle <~ SignalProducer(signal) observer.send(value: "1") @@ -107,7 +106,7 @@ final class NSPopUpButtonSpec: QuickSpec { } it("should accept changes from its bindings to its tag values") { - let (signal, observer) = Signal.pipe() + let (signal, observer) = Signal.pipe() button.reactive.selectedTag <~ SignalProducer(signal) observer.send(value: 1001) diff --git a/ReactiveCocoaTests/AppKit/NSTableViewSpec.swift b/ReactiveCocoaTests/AppKit/NSTableViewSpec.swift index 8fbb46fa95..15c74069b1 100644 --- a/ReactiveCocoaTests/AppKit/NSTableViewSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSTableViewSpec.swift @@ -2,7 +2,6 @@ import Quick import Nimble import ReactiveCocoa import ReactiveSwift -import Result import AppKit final class NSTableViewSpec: QuickSpec { @@ -14,13 +13,13 @@ final class NSTableViewSpec: QuickSpec { } describe("reloadData") { - var bindingSignal: Signal<(), NoError>! - var bindingObserver: Signal<(), NoError>.Observer! + var bindingSignal: Signal<(), Never>! + var bindingObserver: Signal<(), Never>.Observer! var reloadDataCount = 0 beforeEach { - let (signal, observer) = Signal<(), NoError>.pipe() + let (signal, observer) = Signal<(), Never>.pipe() (bindingSignal, bindingObserver) = (signal, observer) reloadDataCount = 0 @@ -43,8 +42,8 @@ final class NSTableViewSpec: QuickSpec { } private final class TestTableView: NSTableView { - let reloadDataSignal: Signal<(), NoError> - private let reloadDataObserver: Signal<(), NoError>.Observer + let reloadDataSignal: Signal<(), Never> + private let reloadDataObserver: Signal<(), Never>.Observer override init(frame: CGRect) { (reloadDataSignal, reloadDataObserver) = Signal.pipe() diff --git a/ReactiveCocoaTests/AppKit/NSViewSpec.swift b/ReactiveCocoaTests/AppKit/NSViewSpec.swift index e06af13f35..3e448ec71e 100644 --- a/ReactiveCocoaTests/AppKit/NSViewSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSViewSpec.swift @@ -1,6 +1,5 @@ import Quick import Nimble -import Result import ReactiveSwift import ReactiveCocoa import AppKit @@ -15,7 +14,7 @@ class NSViewSpec: QuickSpec { } it("should allow binding of `isHidden` property") { - let (hSignal, hSink) = Signal.pipe() + let (hSignal, hSink) = Signal.pipe() expect(view.isHidden) == false view.reactive.isHidden <~ hSignal @@ -25,7 +24,7 @@ class NSViewSpec: QuickSpec { } it("should allow binding of `alphaValue` property") { - let (avSignal, avSink) = Signal.pipe() + let (avSignal, avSink) = Signal.pipe() expect(view.alphaValue) == 1.0 view.reactive.alphaValue <~ avSignal diff --git a/ReactiveCocoaTests/AppKit/ReusableComponentsSpec.swift b/ReactiveCocoaTests/AppKit/ReusableComponentsSpec.swift index 359b395009..961a21da23 100644 --- a/ReactiveCocoaTests/AppKit/ReusableComponentsSpec.swift +++ b/ReactiveCocoaTests/AppKit/ReusableComponentsSpec.swift @@ -1,6 +1,5 @@ import Quick import Nimble -import Result import ReactiveSwift import ReactiveCocoa import AppKit diff --git a/ReactiveCocoaTests/AssociationSpec.swift b/ReactiveCocoaTests/AssociationSpec.swift index 54a1350db7..ca828720a2 100644 --- a/ReactiveCocoaTests/AssociationSpec.swift +++ b/ReactiveCocoaTests/AssociationSpec.swift @@ -1,5 +1,4 @@ import ReactiveSwift -import Result import Nimble import Quick @testable import ReactiveCocoa diff --git a/ReactiveCocoaTests/BindingTargetSpec.swift b/ReactiveCocoaTests/BindingTargetSpec.swift index 5baaf709d9..15ee6cd828 100644 --- a/ReactiveCocoaTests/BindingTargetSpec.swift +++ b/ReactiveCocoaTests/BindingTargetSpec.swift @@ -1,6 +1,5 @@ import ReactiveSwift import ReactiveCocoa -import Result import Quick import Nimble @@ -30,7 +29,7 @@ class BindingTargetSpec: QuickSpec { } expect(object.value) == 0 - let (signal, observer) = Signal<(), NoError>.pipe() + let (signal, observer) = Signal<(), Never>.pipe() target <~ signal expect(object.value) == 0 @@ -50,7 +49,7 @@ class BindingTargetSpec: QuickSpec { } expect(object.value) == 0 - let (signal, observer) = Signal<(), NoError>.pipe() + let (signal, observer) = Signal<(), Never>.pipe() target <~ signal observer.send(value: ()) expect(object.value) == 0 @@ -74,7 +73,7 @@ class BindingTargetSpec: QuickSpec { } expect(object.value) == 0 - let (signal, observer) = Signal<(), NoError>.pipe() + let (signal, observer) = Signal<(), Never>.pipe() target <~ signal expect(object.value) == 0 @@ -94,7 +93,7 @@ class BindingTargetSpec: QuickSpec { } expect(object.value) == 0 - let (signal, observer) = Signal<(), NoError>.pipe() + let (signal, observer) = Signal<(), Never>.pipe() target <~ signal observer.send(value: ()) expect(object.value) == 0 diff --git a/ReactiveCocoaTests/CocoaActionSpec.swift b/ReactiveCocoaTests/CocoaActionSpec.swift index 7ac03f9dfd..072b01f3e4 100644 --- a/ReactiveCocoaTests/CocoaActionSpec.swift +++ b/ReactiveCocoaTests/CocoaActionSpec.swift @@ -1,12 +1,11 @@ import ReactiveSwift -import Result import Nimble import Quick import ReactiveCocoa class CocoaActionSpec: QuickSpec { override func spec() { - var action: Action! + var action: Action! #if os(OSX) var cocoaAction: CocoaAction! #else diff --git a/ReactiveCocoaTests/DelegateProxySpec.swift b/ReactiveCocoaTests/DelegateProxySpec.swift index fa20e9cab2..174457d770 100644 --- a/ReactiveCocoaTests/DelegateProxySpec.swift +++ b/ReactiveCocoaTests/DelegateProxySpec.swift @@ -1,6 +1,5 @@ import Quick import Nimble -import enum Result.NoError import ReactiveSwift @testable import ReactiveCocoa @@ -106,7 +105,7 @@ class DelegateProxySpec: QuickSpec { it("should complete its signals when the object deinitializes") { var isCompleted = false - let foo: Signal<(), NoError> = proxy.intercept(#selector(ObjectDelegate.foo)) + let foo: Signal<(), Never> = proxy.intercept(#selector(ObjectDelegate.foo)) foo.observeCompleted { isCompleted = true } expect(isCompleted) == false @@ -120,7 +119,7 @@ class DelegateProxySpec: QuickSpec { object = nil - let foo: Signal<(), NoError> = proxy.intercept(#selector(ObjectDelegate.foo)) + let foo: Signal<(), Never> = proxy.intercept(#selector(ObjectDelegate.foo)) foo.observeInterrupted { isInterrupted = true } expect(isInterrupted) == true @@ -130,10 +129,10 @@ class DelegateProxySpec: QuickSpec { var fooCount = 0 var barCount = 0 - let foo: Signal<(), NoError> = proxy.intercept(#selector(ObjectDelegate.foo)) + let foo: Signal<(), Never> = proxy.intercept(#selector(ObjectDelegate.foo)) foo.observeValues { fooCount += 1 } - let bar: Signal<(), NoError> = proxy.intercept(#selector(ObjectDelegate.bar)) + let bar: Signal<(), Never> = proxy.intercept(#selector(ObjectDelegate.bar)) bar.observeValues { barCount += 1 } expect(fooCount) == 0 @@ -168,7 +167,7 @@ class DelegateProxySpec: QuickSpec { var fooCount = 0 - let foo: Signal<(), NoError> = proxy.intercept(#selector(ObjectDelegate.foo)) + let foo: Signal<(), Never> = proxy.intercept(#selector(ObjectDelegate.foo)) foo.observeValues { fooCount += 1 } expect(fooCount) == 0 @@ -189,7 +188,7 @@ class DelegateProxySpec: QuickSpec { var barCount = 0 - let bar: Signal<(), NoError> = proxy.intercept(#selector(ObjectDelegate.bar)) + let bar: Signal<(), Never> = proxy.intercept(#selector(ObjectDelegate.bar)) bar.observeValues { barCount += 1 } object.delegate?.bar?() @@ -238,7 +237,7 @@ class DelegateProxySpec: QuickSpec { setter: #selector(setter: object.delegate), getter: #selector(getter: object.delegate)) - let signal: Signal<(), NoError> = proxy.intercept(#selector(ObjectDelegate.foo)) + let signal: Signal<(), Never> = proxy.intercept(#selector(ObjectDelegate.foo)) signal.observeValues { fooCounter += 1 } } diff --git a/ReactiveCocoaTests/DeprecationsSpec.swift b/ReactiveCocoaTests/DeprecationsSpec.swift index 16f8abbe84..69b44009ee 100644 --- a/ReactiveCocoaTests/DeprecationsSpec.swift +++ b/ReactiveCocoaTests/DeprecationsSpec.swift @@ -1,7 +1,6 @@ import Foundation import ReactiveCocoa import ReactiveSwift -import enum Result.NoError import Quick import Nimble diff --git a/ReactiveCocoaTests/DynamicPropertySpec.swift b/ReactiveCocoaTests/DynamicPropertySpec.swift index 4e901b3184..88c2733f14 100644 --- a/ReactiveCocoaTests/DynamicPropertySpec.swift +++ b/ReactiveCocoaTests/DynamicPropertySpec.swift @@ -1,5 +1,4 @@ import ReactiveSwift -import Result import Nimble import Quick import ReactiveCocoa @@ -260,14 +259,14 @@ class DynamicPropertySpec: QuickSpec { } it("should bridge values sent on a signal to Objective-C") { - let (signal, observer) = Signal.pipe() + let (signal, observer) = Signal.pipe() property <~ signal observer.send(value: 1) expect(object.rac_value) == 1 } it("should bridge values sent on a signal producer to Objective-C") { - let producer = SignalProducer(value: 1) + let producer = SignalProducer(value: 1) property <~ producer expect(object.rac_value) == 1 } @@ -279,7 +278,7 @@ class DynamicPropertySpec: QuickSpec { } it("should bridge values sent on a signal to Objective-C, even if the view has deinitialized") { - let (signal, observer) = Signal.pipe() + let (signal, observer) = Signal.pipe() property <~ signal property = nil @@ -288,7 +287,7 @@ class DynamicPropertySpec: QuickSpec { } it("should bridge values sent on a signal producer to Objective-C, even if the view has deinitialized") { - let producer = SignalProducer(value: 1) + let producer = SignalProducer(value: 1) property <~ producer property = nil diff --git a/ReactiveCocoaTests/InterceptingSpec.swift b/ReactiveCocoaTests/InterceptingSpec.swift index 5d94567e11..edd8d6ab65 100644 --- a/ReactiveCocoaTests/InterceptingSpec.swift +++ b/ReactiveCocoaTests/InterceptingSpec.swift @@ -1,7 +1,6 @@ import Foundation @testable import ReactiveCocoa import ReactiveSwift -import enum Result.NoError import Quick import Nimble import CoreGraphics diff --git a/ReactiveCocoaTests/KVOKVCExtensionSpec.swift b/ReactiveCocoaTests/KVOKVCExtensionSpec.swift index dfe49f26c9..76eafb3772 100644 --- a/ReactiveCocoaTests/KVOKVCExtensionSpec.swift +++ b/ReactiveCocoaTests/KVOKVCExtensionSpec.swift @@ -1,5 +1,4 @@ import ReactiveSwift -import Result import Nimble import Quick import ReactiveCocoa diff --git a/ReactiveCocoaTests/KeyValueObservingSpec+Swift4.swift b/ReactiveCocoaTests/KeyValueObservingSpec+Swift4.swift index 613d7c506b..a93da30d64 100644 --- a/ReactiveCocoaTests/KeyValueObservingSpec+Swift4.swift +++ b/ReactiveCocoaTests/KeyValueObservingSpec+Swift4.swift @@ -1,7 +1,6 @@ import Foundation @testable import ReactiveCocoa import ReactiveSwift -import enum Result.NoError import Quick import Nimble @@ -82,7 +81,7 @@ fileprivate class KeyValueObservingSwift4SpecConfiguration: QuickConfiguration { self.context = context } - func observe(_ object: Object, _ keyPath: KeyPath) -> SignalProducer { + func observe(_ object: Object, _ keyPath: KeyPath) -> SignalProducer { switch context["observe"] { case let context as String where context == "signal": return SignalProducer(object.reactive.signal(for: keyPath)) @@ -95,7 +94,7 @@ fileprivate class KeyValueObservingSwift4SpecConfiguration: QuickConfiguration { } } - func observe(_ object: Object, _ keyPath: KeyPath) -> SignalProducer { + func observe(_ object: Object, _ keyPath: KeyPath) -> SignalProducer { switch context["observe"] { case let context as String where context == "signal": return SignalProducer(object.reactive.signal(for: keyPath)) @@ -108,31 +107,31 @@ fileprivate class KeyValueObservingSwift4SpecConfiguration: QuickConfiguration { } } - func isFinished(_ object: Operation) -> SignalProducer { + func isFinished(_ object: Operation) -> SignalProducer { return observe(object, \.isFinished) } - func changes(_ object: ObservableObject) -> SignalProducer { + func changes(_ object: ObservableObject) -> SignalProducer { return observe(object, \.rac_value) } - func nestedChanges(_ object: NestedObservableObject) -> SignalProducer { + func nestedChanges(_ object: NestedObservableObject) -> SignalProducer { return observe(object, \.rac_object.rac_value) } - func weakNestedChanges(_ object: NestedObservableObject) -> SignalProducer { + func weakNestedChanges(_ object: NestedObservableObject) -> SignalProducer { return observe(object, \.rac_weakObject?.rac_value) } - func strongReferenceChanges(_ object: ObservableObject) -> SignalProducer { + func strongReferenceChanges(_ object: ObservableObject) -> SignalProducer { return observe(object, \.target) } - func weakReferenceChanges(_ object: ObservableObject) -> SignalProducer { + func weakReferenceChanges(_ object: ObservableObject) -> SignalProducer { return observe(object, \.weakTarget) } - func dependentKeyChanges(_ object: ObservableObject) -> SignalProducer { + func dependentKeyChanges(_ object: ObservableObject) -> SignalProducer { return observe(object, \.rac_value_plusOne) } } diff --git a/ReactiveCocoaTests/KeyValueObservingSpec.swift b/ReactiveCocoaTests/KeyValueObservingSpec.swift index b1a89ac23f..eba2de54cd 100644 --- a/ReactiveCocoaTests/KeyValueObservingSpec.swift +++ b/ReactiveCocoaTests/KeyValueObservingSpec.swift @@ -1,7 +1,6 @@ import Foundation @testable import ReactiveCocoa @testable import ReactiveSwift -import enum Result.NoError import Quick import Nimble @@ -138,42 +137,42 @@ fileprivate class KeyValueObservingSpecConfiguration: QuickConfiguration { self.context = context } - func observe(_ object: NSObject, _ keyPath: String) -> SignalProducer { - if let block = context["observe"] as? (NSObject, String) -> Signal { + func observe(_ object: NSObject, _ keyPath: String) -> SignalProducer { + if let block = context["observe"] as? (NSObject, String) -> Signal { return SignalProducer(block(object, keyPath)) - } else if let block = context["observe"] as? (NSObject, String) -> SignalProducer { + } else if let block = context["observe"] as? (NSObject, String) -> SignalProducer { return block(object, keyPath).skip(first: 1) } else { fatalError("What is this?") } } - func isFinished(_ object: Operation) -> SignalProducer { + func isFinished(_ object: Operation) -> SignalProducer { return observe(object, #keyPath(Operation.isFinished)) } - func changes(_ object: NSObject) -> SignalProducer { + func changes(_ object: NSObject) -> SignalProducer { return observe(object, #keyPath(ObservableObject.rac_value)) } - func nestedChanges(_ object: NSObject) -> SignalProducer { + func nestedChanges(_ object: NSObject) -> SignalProducer { return observe(object, #keyPath(NestedObservableObject.rac_object.rac_value)) } - func weakNestedChanges(_ object: NSObject) -> SignalProducer { + func weakNestedChanges(_ object: NSObject) -> SignalProducer { // `#keyPath` does not work with weak relationships. return observe(object, "rac_weakObject.rac_value") } - func strongReferenceChanges(_ object: NSObject) -> SignalProducer { + func strongReferenceChanges(_ object: NSObject) -> SignalProducer { return observe(object, #keyPath(ObservableObject.target)) } - func weakReferenceChanges(_ object: NSObject) -> SignalProducer { + func weakReferenceChanges(_ object: NSObject) -> SignalProducer { return observe(object, #keyPath(ObservableObject.weakTarget)) } - func dependentKeyChanges(_ object: NSObject) -> SignalProducer { + func dependentKeyChanges(_ object: NSObject) -> SignalProducer { return observe(object, #keyPath(ObservableObject.rac_value_plusOne)) } } diff --git a/ReactiveCocoaTests/LifetimeSpec.swift b/ReactiveCocoaTests/LifetimeSpec.swift index b159284807..dc613119d9 100644 --- a/ReactiveCocoaTests/LifetimeSpec.swift +++ b/ReactiveCocoaTests/LifetimeSpec.swift @@ -1,7 +1,6 @@ import Foundation import ReactiveCocoa import ReactiveSwift -import enum Result.NoError import Quick import Nimble @@ -97,7 +96,7 @@ class LifetimeSpec: QuickSpec { weak var weakObject = object var isCompleted = false - let (signal, observer) = Signal<(), NoError>.pipe() + let (signal, observer) = Signal<(), Never>.pipe() withExtendedLifetime(observer) { signal @@ -119,7 +118,7 @@ class LifetimeSpec: QuickSpec { weak var weakObject = object var isCompleted = false - let (signal, observer) = Signal<(), NoError>.pipe() + let (signal, observer) = Signal<(), Never>.pipe() withExtendedLifetime(observer) { signal @@ -143,7 +142,7 @@ class LifetimeSpec: QuickSpec { weak var weakObject = object var isCompleted = false - let (signal, observer) = Signal<(), NoError>.pipe() + let (signal, observer) = Signal<(), Never>.pipe() withExtendedLifetime(observer) { SignalProducer(signal) @@ -165,7 +164,7 @@ class LifetimeSpec: QuickSpec { weak var weakObject = object var isCompleted = false - let (signal, observer) = Signal<(), NoError>.pipe() + let (signal, observer) = Signal<(), Never>.pipe() withExtendedLifetime(observer) { SignalProducer(signal) diff --git a/ReactiveCocoaTests/Shared/NSLayoutConstraintSpec.swift b/ReactiveCocoaTests/Shared/NSLayoutConstraintSpec.swift index 4c4bac5f7d..dcb55170d4 100644 --- a/ReactiveCocoaTests/Shared/NSLayoutConstraintSpec.swift +++ b/ReactiveCocoaTests/Shared/NSLayoutConstraintSpec.swift @@ -2,7 +2,6 @@ import ReactiveSwift import ReactiveCocoa import Quick import Nimble -import enum Result.NoError class NSLayoutConstraintSpec: QuickSpec { override func spec() { @@ -22,7 +21,7 @@ class NSLayoutConstraintSpec: QuickSpec { it("should accept changes from bindings to its constant") { expect(constraint.constant).to(equal(0.0)) - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() constraint.reactive.constant <~ pipeSignal diff --git a/ReactiveCocoaTests/TestError.swift b/ReactiveCocoaTests/TestError.swift index 2a89e4daba..e0a4660893 100644 --- a/ReactiveCocoaTests/TestError.swift +++ b/ReactiveCocoaTests/TestError.swift @@ -1,6 +1,5 @@ import ReactiveCocoa import ReactiveSwift -import Result internal enum TestError: Int { case `default` = 0 @@ -16,7 +15,7 @@ internal extension SignalProducer { /// Halts if an error is emitted in the receiver signal. /// This is useful in tests to be able to just use `startWithNext` /// in cases where we know that an error won't be emitted. - func assumeNoErrors() -> SignalProducer { + func assumeNoErrors() -> SignalProducer { return lift { $0.assumeNoErrors() } } } @@ -25,7 +24,7 @@ internal extension Signal { /// Halts if an error is emitted in the receiver signal. /// This is useful in tests to be able to just use `startWithNext` /// in cases where we know that an error won't be emitted. - func assumeNoErrors() -> Signal { + func assumeNoErrors() -> Signal { return mapError { error in fatalError("Unexpected error: \(error)") diff --git a/ReactiveCocoaTests/UIKit/ReusableComponentsSpec.swift b/ReactiveCocoaTests/UIKit/ReusableComponentsSpec.swift index 31c94a0827..e767043288 100644 --- a/ReactiveCocoaTests/UIKit/ReusableComponentsSpec.swift +++ b/ReactiveCocoaTests/UIKit/ReusableComponentsSpec.swift @@ -1,6 +1,5 @@ import Quick import Nimble -import Result import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewSpec.swift b/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewSpec.swift index 5ceea45bef..adeb139d24 100644 --- a/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewSpec.swift @@ -2,7 +2,6 @@ import Quick import Nimble import ReactiveSwift import ReactiveCocoa -import Result class UIActivityIndicatorSpec: QuickSpec { override func spec() { @@ -20,7 +19,7 @@ class UIActivityIndicatorSpec: QuickSpec { } it("should accept changes from bindings to its animating state") { - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() activityIndicatorView.reactive.isAnimating <~ SignalProducer(pipeSignal) observer.send(value: true) diff --git a/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift b/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift index eed8d6ce66..403c73cd1f 100644 --- a/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift @@ -3,7 +3,6 @@ import Nimble import ReactiveSwift import ReactiveCocoa import UIKit -import enum Result.NoError class UIBarButtonItemSpec: QuickSpec { override func spec() { @@ -21,12 +20,12 @@ class UIBarButtonItemSpec: QuickSpec { } it("should not be retained with the presence of a `pressed` action") { - let action = Action<(),(),NoError> { SignalProducer(value: ()) } + let action = Action<(),(),Never> { SignalProducer(value: ()) } barButtonItem.reactive.pressed = CocoaAction(action) } it("should accept changes from bindings to its enabling state") { - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() barButtonItem.reactive.isEnabled <~ SignalProducer(pipeSignal) observer.send(value: false) @@ -37,7 +36,7 @@ class UIBarButtonItemSpec: QuickSpec { } it("should accept changes from bindings to its title") { - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() barButtonItem.reactive.title <~ SignalProducer(pipeSignal) observer.send(value: "title") @@ -48,7 +47,7 @@ class UIBarButtonItemSpec: QuickSpec { } it("should accept changes from bindings to its image") { - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() barButtonItem.reactive.image <~ SignalProducer(pipeSignal) let image = UIImage() @@ -62,7 +61,7 @@ class UIBarButtonItemSpec: QuickSpec { } it("should accept changes from bindings to its style") { - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() barButtonItem.reactive.style <~ SignalProducer(pipeSignal) observer.send(value: .done) @@ -73,7 +72,7 @@ class UIBarButtonItemSpec: QuickSpec { } it("should accept changes from bindings to its width") { - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() barButtonItem.reactive.width <~ SignalProducer(pipeSignal) observer.send(value: 42.0) @@ -87,7 +86,7 @@ class UIBarButtonItemSpec: QuickSpec { } it("should accept changes from bindings to its possible titles") { - let (pipeSignal, observer) = Signal?, NoError>.pipe() + let (pipeSignal, observer) = Signal?, Never>.pipe() barButtonItem.reactive.possibleTitles <~ SignalProducer(pipeSignal) let possibleTitles = Set(["Unread (123,456,789)", "Unread"]) @@ -107,7 +106,7 @@ class UIBarButtonItemSpec: QuickSpec { barButtonItem.customView = nil - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() barButtonItem.reactive.customView <~ pipeSignal observer.send(value: firstChange) diff --git a/ReactiveCocoaTests/UIKit/UIButtonSpec.swift b/ReactiveCocoaTests/UIKit/UIButtonSpec.swift index bd61699d87..d2a373e6ef 100644 --- a/ReactiveCocoaTests/UIKit/UIButtonSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIButtonSpec.swift @@ -3,7 +3,6 @@ import Nimble import ReactiveSwift import ReactiveCocoa import UIKit -import enum Result.NoError class UIButtonSpec: QuickSpec { override func spec() { @@ -24,7 +23,7 @@ class UIButtonSpec: QuickSpec { let firstTitle = "First title" let secondTitle = "Second title" - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() button.reactive.title <~ SignalProducer(pipeSignal) button.setTitle("", for: .selected) button.setTitle("", for: .highlighted) @@ -45,7 +44,7 @@ class UIButtonSpec: QuickSpec { button.isUserInteractionEnabled = true let pressed = MutableProperty(false) - let action = Action<(), Bool, NoError> { _ in + let action = Action<(), Bool, Never> { _ in SignalProducer(value: true) } diff --git a/ReactiveCocoaTests/UIKit/UICollectionViewSpec.swift b/ReactiveCocoaTests/UIKit/UICollectionViewSpec.swift index 9b5d50e468..0f396d0d8c 100644 --- a/ReactiveCocoaTests/UIKit/UICollectionViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UICollectionViewSpec.swift @@ -2,7 +2,6 @@ import Quick import Nimble import ReactiveCocoa import ReactiveSwift -import Result import UIKit final class UICollectionViewSpec: QuickSpec { @@ -14,13 +13,13 @@ final class UICollectionViewSpec: QuickSpec { } describe("reloadData") { - var bindingSignal: Signal<(), NoError>! - var bindingObserver: Signal<(), NoError>.Observer! + var bindingSignal: Signal<(), Never>! + var bindingObserver: Signal<(), Never>.Observer! var reloadDataCount = 0 beforeEach { - let (signal, observer) = Signal<(), NoError>.pipe() + let (signal, observer) = Signal<(), Never>.pipe() (bindingSignal, bindingObserver) = (signal, observer) reloadDataCount = 0 @@ -43,8 +42,8 @@ final class UICollectionViewSpec: QuickSpec { } private final class TestCollectionView: UICollectionView { - let reloadDataSignal: Signal<(), NoError> - private let reloadDataObserver: Signal<(), NoError>.Observer + let reloadDataSignal: Signal<(), Never> + private let reloadDataObserver: Signal<(), Never>.Observer init() { (reloadDataSignal, reloadDataObserver) = Signal.pipe() diff --git a/ReactiveCocoaTests/UIKit/UIControlSpec.swift b/ReactiveCocoaTests/UIKit/UIControlSpec.swift index 353e45a893..24b27ea9c3 100644 --- a/ReactiveCocoaTests/UIKit/UIControlSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIControlSpec.swift @@ -3,7 +3,6 @@ import ReactiveCocoa import UIKit import Quick import Nimble -import enum Result.NoError class UIControlSpec: QuickSpec { override func spec() { @@ -22,7 +21,7 @@ class UIControlSpec: QuickSpec { it("should accept changes from bindings to its enabling state") { control.isEnabled = false - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() control.reactive.isEnabled <~ SignalProducer(pipeSignal) observer.send(value: true) @@ -35,7 +34,7 @@ class UIControlSpec: QuickSpec { it("should accept changes from bindings to its selecting state") { control.isSelected = false - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() control.reactive.isSelected <~ SignalProducer(pipeSignal) observer.send(value: true) @@ -48,7 +47,7 @@ class UIControlSpec: QuickSpec { it("should accept changes from bindings to its highlighting state") { control.isHighlighted = false - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() control.reactive.isHighlighted <~ SignalProducer(pipeSignal) observer.send(value: true) @@ -62,8 +61,8 @@ class UIControlSpec: QuickSpec { control.isSelected = false control.isEnabled = false - let (pipeSignalSelected, observerSelected) = Signal.pipe() - let (pipeSignalEnabled, observerEnabled) = Signal.pipe() + let (pipeSignalSelected, observerSelected) = Signal.pipe() + let (pipeSignalEnabled, observerEnabled) = Signal.pipe() control.reactive.isSelected <~ SignalProducer(pipeSignalSelected) control.reactive.isEnabled <~ SignalProducer(pipeSignalEnabled) diff --git a/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift b/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift index 33bc630ac6..2ccd230d44 100644 --- a/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift @@ -3,7 +3,6 @@ import Nimble import ReactiveSwift import ReactiveCocoa import UIKit -import enum Result.NoError class UIGestureRecognizerSpec: QuickSpec { override func spec() { diff --git a/ReactiveCocoaTests/UIKit/UIImageViewSpec.swift b/ReactiveCocoaTests/UIKit/UIImageViewSpec.swift index c311572afa..11f5783383 100644 --- a/ReactiveCocoaTests/UIKit/UIImageViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIImageViewSpec.swift @@ -3,7 +3,6 @@ import ReactiveCocoa import UIKit import Quick import Nimble -import enum Result.NoError class UIImageViewSpec: QuickSpec { override func spec() { @@ -24,7 +23,7 @@ class UIImageViewSpec: QuickSpec { let firstChange = UIImage() let secondChange = UIImage() - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() imageView.reactive.image <~ SignalProducer(pipeSignal) observer.send(value: firstChange) @@ -38,7 +37,7 @@ class UIImageViewSpec: QuickSpec { let firstChange = UIImage() let secondChange = UIImage() - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() imageView.reactive.highlightedImage <~ SignalProducer(pipeSignal) observer.send(value: firstChange) diff --git a/ReactiveCocoaTests/UIKit/UILabelSpec.swift b/ReactiveCocoaTests/UIKit/UILabelSpec.swift index 374549783b..d816ae805e 100644 --- a/ReactiveCocoaTests/UIKit/UILabelSpec.swift +++ b/ReactiveCocoaTests/UIKit/UILabelSpec.swift @@ -3,7 +3,6 @@ import ReactiveCocoa import UIKit import Quick import Nimble -import enum Result.NoError class UILabelSpec: QuickSpec { override func spec() { @@ -26,7 +25,7 @@ class UILabelSpec: QuickSpec { label.text = "" - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() label.reactive.text <~ SignalProducer(pipeSignal) observer.send(value: firstChange) @@ -45,7 +44,7 @@ class UILabelSpec: QuickSpec { label.attributedText = NSAttributedString(string: "") - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() label.reactive.attributedText <~ SignalProducer(pipeSignal) observer.send(value: firstChange) @@ -61,7 +60,7 @@ class UILabelSpec: QuickSpec { let label = UILabel(frame: .zero) - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() label.textColor = UIColor.black label.reactive.textColor <~ SignalProducer(pipeSignal) diff --git a/ReactiveCocoaTests/UIKit/UINavigationItemSpec.swift b/ReactiveCocoaTests/UIKit/UINavigationItemSpec.swift index b9f0b73a90..5579542989 100644 --- a/ReactiveCocoaTests/UIKit/UINavigationItemSpec.swift +++ b/ReactiveCocoaTests/UIKit/UINavigationItemSpec.swift @@ -3,7 +3,6 @@ import ReactiveCocoa import UIKit import Quick import Nimble -import enum Result.NoError class UINavigationItemSpec: QuickSpec { override func spec() { @@ -26,7 +25,7 @@ class UINavigationItemSpec: QuickSpec { navigationItem.title = "" - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() navigationItem.reactive.title <~ pipeSignal observer.send(value: firstChange) @@ -48,7 +47,7 @@ class UINavigationItemSpec: QuickSpec { navigationItem.titleView = nil - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() navigationItem.reactive.titleView <~ pipeSignal observer.send(value: firstChange) @@ -68,7 +67,7 @@ class UINavigationItemSpec: QuickSpec { navigationItem.prompt = "" - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() navigationItem.reactive.prompt <~ pipeSignal observer.send(value: firstChange) @@ -87,7 +86,7 @@ class UINavigationItemSpec: QuickSpec { navigationItem.backBarButtonItem = nil - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() navigationItem.reactive.backBarButtonItem <~ pipeSignal observer.send(value: firstChange) @@ -106,7 +105,7 @@ class UINavigationItemSpec: QuickSpec { navigationItem.hidesBackButton = false - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() navigationItem.reactive.hidesBackButton <~ pipeSignal observer.send(value: firstChange) @@ -130,7 +129,7 @@ class UINavigationItemSpec: QuickSpec { navigationItem.leftBarButtonItems = nil - let (pipeSignal, observer) = Signal<[UIBarButtonItem]?, NoError>.pipe() + let (pipeSignal, observer) = Signal<[UIBarButtonItem]?, Never>.pipe() navigationItem.reactive.leftBarButtonItems <~ pipeSignal observer.send(value: firstChange) @@ -156,7 +155,7 @@ class UINavigationItemSpec: QuickSpec { navigationItem.rightBarButtonItems = nil - let (pipeSignal, observer) = Signal<[UIBarButtonItem]?, NoError>.pipe() + let (pipeSignal, observer) = Signal<[UIBarButtonItem]?, Never>.pipe() navigationItem.reactive.rightBarButtonItems <~ pipeSignal observer.send(value: firstChange) @@ -175,7 +174,7 @@ class UINavigationItemSpec: QuickSpec { navigationItem.leftBarButtonItem = nil - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() navigationItem.reactive.leftBarButtonItem <~ pipeSignal observer.send(value: firstChange) @@ -194,7 +193,7 @@ class UINavigationItemSpec: QuickSpec { navigationItem.rightBarButtonItem = nil - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() navigationItem.reactive.rightBarButtonItem <~ pipeSignal observer.send(value: firstChange) @@ -214,7 +213,7 @@ class UINavigationItemSpec: QuickSpec { navigationItem.leftItemsSupplementBackButton = false - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() navigationItem.reactive.leftItemsSupplementBackButton <~ pipeSignal observer.send(value: firstChange) @@ -231,7 +230,7 @@ class UINavigationItemSpec: QuickSpec { navigationItem.largeTitleDisplayMode = .automatic - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() navigationItem.reactive.largeTitleDisplayMode <~ pipeSignal observer.send(value: firstChange) @@ -250,7 +249,7 @@ class UINavigationItemSpec: QuickSpec { navigationItem.searchController = nil - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() navigationItem.reactive.searchController <~ pipeSignal observer.send(value: firstChange) @@ -269,7 +268,7 @@ class UINavigationItemSpec: QuickSpec { navigationItem.hidesSearchBarWhenScrolling = false - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() navigationItem.reactive.hidesSearchBarWhenScrolling <~ pipeSignal observer.send(value: firstChange) diff --git a/ReactiveCocoaTests/UIKit/UIPickerViewSpec.swift b/ReactiveCocoaTests/UIKit/UIPickerViewSpec.swift index b6ecd0e195..544aa00b0f 100644 --- a/ReactiveCocoaTests/UIKit/UIPickerViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIPickerViewSpec.swift @@ -3,7 +3,6 @@ import ReactiveCocoa import UIKit import Quick import Nimble -import enum Result.NoError private final class PickerDataSource: NSObject, UIPickerViewDataSource { @objc func numberOfComponents(in pickerView: UIPickerView) -> Int { @@ -42,10 +41,10 @@ final class UIPickerViewSpec: QuickSpec { it("should accept changes from bindings to selected rows") { - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() pickerView.reactive.selectedRow(inComponent: 0) <~ SignalProducer(pipeSignal) - let (anotherPipeSignal, anotherObserver) = Signal.pipe() + let (anotherPipeSignal, anotherObserver) = Signal.pipe() pickerView.reactive.selectedRow(inComponent: 1) <~ SignalProducer(anotherPipeSignal) observer.send(value: 1) @@ -76,7 +75,7 @@ final class UIPickerViewSpec: QuickSpec { } it("invokes reloadAllComponents whenever the bound signal sends a value") { - let (signal, observer) = Signal<(), NoError>.pipe() + let (signal, observer) = Signal<(), Never>.pipe() var reloadAllComponentsCount = 0 @@ -93,7 +92,7 @@ final class UIPickerViewSpec: QuickSpec { } it("invokes reloadComponent whenever the bound signal sends a value") { - let (signal, observer) = Signal.pipe() + let (signal, observer) = Signal.pipe() var reloadFirstComponentCount = 0 var reloadSecondComponentCount = 0 @@ -123,11 +122,11 @@ final class UIPickerViewSpec: QuickSpec { } private final class TestPickerView: UIPickerView { - let reloadAllComponentsSignal: Signal<(), NoError> - private let reloadAllComponentsObserver: Signal<(), NoError>.Observer + let reloadAllComponentsSignal: Signal<(), Never> + private let reloadAllComponentsObserver: Signal<(), Never>.Observer - let reloadComponentSignal: Signal - private let reloadComponentObserver: Signal.Observer + let reloadComponentSignal: Signal + private let reloadComponentObserver: Signal.Observer init() { (reloadAllComponentsSignal, reloadAllComponentsObserver) = Signal.pipe() diff --git a/ReactiveCocoaTests/UIKit/UIProgressViewSpec.swift b/ReactiveCocoaTests/UIKit/UIProgressViewSpec.swift index 93f2d25765..97edd5736e 100644 --- a/ReactiveCocoaTests/UIKit/UIProgressViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIProgressViewSpec.swift @@ -3,7 +3,6 @@ import ReactiveCocoa import UIKit import Quick import Nimble -import enum Result.NoError class UIProgressViewSpec: QuickSpec { override func spec() { @@ -26,7 +25,7 @@ class UIProgressViewSpec: QuickSpec { progressView.progress = 1.0 - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() progressView.reactive.progress <~ SignalProducer(pipeSignal) observer.send(value: firstChange) diff --git a/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift b/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift index 35ae44d639..9ca1a9edab 100644 --- a/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift @@ -2,7 +2,6 @@ import Quick import Nimble import ReactiveSwift import ReactiveCocoa -import Result class UIRefreshControlSpec: QuickSpec { override func spec() { @@ -20,7 +19,7 @@ class UIRefreshControlSpec: QuickSpec { } it("should accept changes from bindings to its refreshing state") { - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() refreshControl.reactive.isRefreshing <~ SignalProducer(pipeSignal) observer.send(value: true) @@ -31,7 +30,7 @@ class UIRefreshControlSpec: QuickSpec { } it("should accept changes from bindings to its attributed title state") { - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() refreshControl.reactive.attributedTitle <~ SignalProducer(pipeSignal) let string = NSAttributedString(string: "test") @@ -51,7 +50,7 @@ class UIRefreshControlSpec: QuickSpec { refreshControl.isUserInteractionEnabled = true let refreshed = MutableProperty(false) - let action = Action<(), Bool, NoError> { _ in + let action = Action<(), Bool, Never> { _ in SignalProducer(value: true) } @@ -68,7 +67,7 @@ class UIRefreshControlSpec: QuickSpec { refreshControl.isEnabled = true refreshControl.isUserInteractionEnabled = true - let action = Action<(), Bool, NoError> { _ in + let action = Action<(), Bool, Never> { _ in SignalProducer(value: true).delay(1, on: QueueScheduler.main) } diff --git a/ReactiveCocoaTests/UIKit/UIScrollViewSpec.swift b/ReactiveCocoaTests/UIKit/UIScrollViewSpec.swift index 0117ba1d65..a208506467 100644 --- a/ReactiveCocoaTests/UIKit/UIScrollViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIScrollViewSpec.swift @@ -3,7 +3,6 @@ import ReactiveCocoa import UIKit import Quick import Nimble -import enum Result.NoError private final class UIScrollViewDelegateForZooming: NSObject, UIScrollViewDelegate { func viewForZooming(in scrollView: UIScrollView) -> UIView? { @@ -29,7 +28,7 @@ class UIScrollViewSpec: QuickSpec { it("should accept changes from bindings to its content inset value") { scrollView.contentInset = .zero - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() scrollView.reactive.contentInset <~ SignalProducer(pipeSignal) observer.send(value: UIEdgeInsets(top: 1, left: 2, bottom: 3, right: 4)) @@ -42,7 +41,7 @@ class UIScrollViewSpec: QuickSpec { it("should accept changes from bindings to its scroll indicator insets value") { scrollView.scrollIndicatorInsets = .zero - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() scrollView.reactive.scrollIndicatorInsets <~ SignalProducer(pipeSignal) observer.send(value: UIEdgeInsets(top: 1, left: 2, bottom: 3, right: 4)) @@ -55,7 +54,7 @@ class UIScrollViewSpec: QuickSpec { it("should accept changes from bindings to its scroll enabled state") { scrollView.isScrollEnabled = true - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() scrollView.reactive.isScrollEnabled <~ SignalProducer(pipeSignal) observer.send(value: true) @@ -75,7 +74,7 @@ class UIScrollViewSpec: QuickSpec { scrollView.maximumZoomScale = 5 scrollView.zoomScale = 1 - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() scrollView.reactive.zoomScale <~ SignalProducer(pipeSignal) observer.send(value: 3) @@ -87,7 +86,7 @@ class UIScrollViewSpec: QuickSpec { it("should accept changes from bindings to its minimum zoom scale value") { scrollView.minimumZoomScale = 0 - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() scrollView.reactive.minimumZoomScale <~ SignalProducer(pipeSignal) observer.send(value: 42) @@ -99,7 +98,7 @@ class UIScrollViewSpec: QuickSpec { it("should accept changes from bindings to its maximum zoom scale value") { scrollView.maximumZoomScale = 0 - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() scrollView.reactive.maximumZoomScale <~ SignalProducer(pipeSignal) observer.send(value: 42) @@ -111,7 +110,7 @@ class UIScrollViewSpec: QuickSpec { it("should accept changes from bindings to its scrolls to top state") { scrollView.scrollsToTop = true - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() scrollView.reactive.scrollsToTop <~ SignalProducer(pipeSignal) observer.send(value: true) diff --git a/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift b/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift index 60c8474a61..6f811ec9f9 100644 --- a/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift +++ b/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift @@ -3,7 +3,6 @@ import ReactiveCocoa import UIKit import Quick import Nimble -import enum Result.NoError class UISearchBarSpec: QuickSpec { override func spec() { @@ -37,7 +36,7 @@ class UISearchBarSpec: QuickSpec { searchBar.text = "" - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() searchBar.reactive.text <~ SignalProducer(pipeSignal) observer.send(value: firstChange) @@ -86,7 +85,7 @@ class UISearchBarSpec: QuickSpec { searchBar.scopeButtonTitles = ["First", "Second", "Third"] - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() searchBar.reactive.selectedScopeButtonIndex <~ SignalProducer(pipeSignal) observer.send(value: firstChange) @@ -204,7 +203,7 @@ class UISearchBarSpec: QuickSpec { it("should accept changes from bindings to its hidden state of the cancel button") { searchBar.showsCancelButton = false - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() searchBar.reactive.showsCancelButton <~ SignalProducer(pipeSignal) observer.send(value: true) diff --git a/ReactiveCocoaTests/UIKit/UISegmentedControlSpec.swift b/ReactiveCocoaTests/UIKit/UISegmentedControlSpec.swift index 51d4c5b21e..f0794b532d 100644 --- a/ReactiveCocoaTests/UIKit/UISegmentedControlSpec.swift +++ b/ReactiveCocoaTests/UIKit/UISegmentedControlSpec.swift @@ -2,7 +2,6 @@ import Quick import Nimble import ReactiveSwift import ReactiveCocoa -import Result class UISegmentedControlSpec: QuickSpec { override func spec() { @@ -11,7 +10,7 @@ class UISegmentedControlSpec: QuickSpec { s.selectedSegmentIndex = UISegmentedControl.noSegment expect(s.numberOfSegments) == 3 - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() s.reactive.selectedSegmentIndex <~ SignalProducer(pipeSignal) expect(s.selectedSegmentIndex) == UISegmentedControl.noSegment diff --git a/ReactiveCocoaTests/UIKit/UISliderSpec.swift b/ReactiveCocoaTests/UIKit/UISliderSpec.swift index d3b572ea94..0823549602 100644 --- a/ReactiveCocoaTests/UIKit/UISliderSpec.swift +++ b/ReactiveCocoaTests/UIKit/UISliderSpec.swift @@ -2,7 +2,6 @@ import Quick import Nimble import ReactiveCocoa import ReactiveSwift -import enum Result.NoError import UIKit class UISliderSpec: QuickSpec { @@ -23,7 +22,7 @@ class UISliderSpec: QuickSpec { it("should accept changes from bindings to its value") { expect(slider.value) == 0.0 - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() slider.reactive.value <~ pipeSignal @@ -34,7 +33,7 @@ class UISliderSpec: QuickSpec { it("should accept changes from bindings to its minimum value") { expect(slider.minimumValue) == 0.0 - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() slider.reactive.minimumValue <~ pipeSignal @@ -45,7 +44,7 @@ class UISliderSpec: QuickSpec { it("should accept changes from bindings to its maximum value") { expect(slider.maximumValue) == 1.0 - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() slider.reactive.maximumValue <~ pipeSignal diff --git a/ReactiveCocoaTests/UIKit/UIStepperSpec.swift b/ReactiveCocoaTests/UIKit/UIStepperSpec.swift index ac2f841d88..a886d74981 100644 --- a/ReactiveCocoaTests/UIKit/UIStepperSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIStepperSpec.swift @@ -2,7 +2,6 @@ import Quick import Nimble import ReactiveCocoa import ReactiveSwift -import enum Result.NoError import UIKit class UIStepperSpec: QuickSpec { @@ -23,7 +22,7 @@ class UIStepperSpec: QuickSpec { it("should accept changes from bindings to its value") { expect(stepper.value) == 0.0 - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() stepper.reactive.value <~ pipeSignal @@ -34,7 +33,7 @@ class UIStepperSpec: QuickSpec { it("should accept changes from bindings to its minimum value") { expect(stepper.minimumValue) == 0.0 - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() stepper.reactive.minimumValue <~ pipeSignal @@ -45,7 +44,7 @@ class UIStepperSpec: QuickSpec { it("should accept changes from bindings to its maximum value") { expect(stepper.maximumValue) == 100.0 - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() stepper.reactive.maximumValue <~ pipeSignal diff --git a/ReactiveCocoaTests/UIKit/UISwitchSpec.swift b/ReactiveCocoaTests/UIKit/UISwitchSpec.swift index a102a3ecdc..e1417183e3 100644 --- a/ReactiveCocoaTests/UIKit/UISwitchSpec.swift +++ b/ReactiveCocoaTests/UIKit/UISwitchSpec.swift @@ -2,7 +2,6 @@ import Quick import Nimble import ReactiveSwift import ReactiveCocoa -import Result class UISwitchSpec: QuickSpec { override func spec() { @@ -24,7 +23,7 @@ class UISwitchSpec: QuickSpec { it("should accept changes from bindings to its `isOn` state") { toggle.isOn = false - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() toggle.reactive.isOn <~ SignalProducer(pipeSignal) observer.send(value: true) @@ -49,7 +48,7 @@ class UISwitchSpec: QuickSpec { toggle.isUserInteractionEnabled = true let isOn = MutableProperty(false) - let action = Action { isOn in + let action = Action { isOn in return SignalProducer(value: isOn) } isOn <~ SignalProducer(action.values) diff --git a/ReactiveCocoaTests/UIKit/UITabBarItemSpec.swift b/ReactiveCocoaTests/UIKit/UITabBarItemSpec.swift index 8417ec9dcc..ab97fe261b 100644 --- a/ReactiveCocoaTests/UIKit/UITabBarItemSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITabBarItemSpec.swift @@ -3,7 +3,6 @@ import ReactiveCocoa import UIKit import Quick import Nimble -import enum Result.NoError class UITabBarSpec: QuickSpec { override func spec() { @@ -26,7 +25,7 @@ class UITabBarSpec: QuickSpec { tabBarItem.badgeValue = "" - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() tabBarItem.reactive.badgeValue <~ pipeSignal observer.send(value: firstChange) @@ -46,7 +45,7 @@ class UITabBarSpec: QuickSpec { tabBarItem.badgeColor = .blue - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() tabBarItem.reactive.badgeColor <~ pipeSignal observer.send(value: firstChange) diff --git a/ReactiveCocoaTests/UIKit/UITableViewSpec.swift b/ReactiveCocoaTests/UIKit/UITableViewSpec.swift index 9aa8c23259..fce22b3565 100644 --- a/ReactiveCocoaTests/UIKit/UITableViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITableViewSpec.swift @@ -2,7 +2,6 @@ import Quick import Nimble import ReactiveCocoa import ReactiveSwift -import Result import UIKit final class UITableViewSpec: QuickSpec { @@ -14,13 +13,13 @@ final class UITableViewSpec: QuickSpec { } describe("reloadData") { - var bindingSignal: Signal<(), NoError>! - var bindingObserver: Signal<(), NoError>.Observer! + var bindingSignal: Signal<(), Never>! + var bindingObserver: Signal<(), Never>.Observer! var reloadDataCount = 0 beforeEach { - let (signal, observer) = Signal<(), NoError>.pipe() + let (signal, observer) = Signal<(), Never>.pipe() (bindingSignal, bindingObserver) = (signal, observer) reloadDataCount = 0 @@ -43,8 +42,8 @@ final class UITableViewSpec: QuickSpec { } private final class TestTableView: UITableView { - let reloadDataSignal: Signal<(), NoError> - private let reloadDataObserver: Signal<(), NoError>.Observer + let reloadDataSignal: Signal<(), Never> + private let reloadDataObserver: Signal<(), Never>.Observer override init(frame: CGRect, style: UITableView.Style) { (reloadDataSignal, reloadDataObserver) = Signal.pipe() diff --git a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift index 06b03721ff..b9b3377f53 100644 --- a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift @@ -3,7 +3,6 @@ import ReactiveCocoa import UIKit import Quick import Nimble -import enum Result.NoError class UITextFieldSpec: QuickSpec { override func spec() { @@ -70,7 +69,7 @@ class UITextFieldSpec: QuickSpec { textField.attributedText = NSAttributedString(string: "") - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() textField.reactive.attributedText <~ SignalProducer(pipeSignal) observer.send(value: firstChange) @@ -119,7 +118,7 @@ class UITextFieldSpec: QuickSpec { } it("should accept changes from bindings to its placeholder attribute") { - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() textField.reactive.placeholder <~ pipeSignal observer.send(value: "A placeholder") @@ -133,7 +132,7 @@ class UITextFieldSpec: QuickSpec { } it("should accept changes from bindings to its secureTextEntry attribute") { - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() textField.reactive.isSecureTextEntry <~ pipeSignal observer.send(value: true) @@ -144,7 +143,7 @@ class UITextFieldSpec: QuickSpec { } it("should accept changes from bindings to its textColor attribute") { - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() textField.reactive.textColor <~ pipeSignal observer.send(value: UIColor.red) diff --git a/ReactiveCocoaTests/UIKit/UITextViewSpec.swift b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift index fb51107d4f..79c2427401 100644 --- a/ReactiveCocoaTests/UIKit/UITextViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift @@ -3,7 +3,6 @@ import ReactiveCocoa import UIKit import Quick import Nimble -import enum Result.NoError class UITextViewSpec: QuickSpec { override func spec() { @@ -66,7 +65,7 @@ class UITextViewSpec: QuickSpec { textView.attributedText = NSAttributedString(string: "") - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() textView.reactive.attributedText <~ SignalProducer(pipeSignal) observer.send(value: firstChange) diff --git a/ReactiveCocoaTests/UIKit/UIViewControllerSpec.swift b/ReactiveCocoaTests/UIKit/UIViewControllerSpec.swift index 0a2a128b04..b8db392c1c 100644 --- a/ReactiveCocoaTests/UIKit/UIViewControllerSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIViewControllerSpec.swift @@ -3,7 +3,6 @@ import ReactiveCocoa import UIKit import Quick import Nimble -import enum Result.NoError class UIViewControllerSpec: QuickSpec { override func spec() { @@ -26,7 +25,7 @@ class UIViewControllerSpec: QuickSpec { viewController.title = "" - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() viewController.reactive.title <~ pipeSignal observer.send(value: firstChange) diff --git a/ReactiveCocoaTests/UIKit/UIViewSpec.swift b/ReactiveCocoaTests/UIKit/UIViewSpec.swift index 7be1422df9..ac6fa6d519 100644 --- a/ReactiveCocoaTests/UIKit/UIViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIViewSpec.swift @@ -3,7 +3,6 @@ import ReactiveCocoa import UIKit import Quick import Nimble -import enum Result.NoError class UIViewSpec: QuickSpec { override func spec() { @@ -23,7 +22,7 @@ class UIViewSpec: QuickSpec { it("should accept changes from bindings to its hiding state") { view.isHidden = true - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() view.reactive.isHidden <~ SignalProducer(pipeSignal) observer.send(value: true) @@ -39,7 +38,7 @@ class UIViewSpec: QuickSpec { let firstChange = CGFloat(0.5) let secondChange = CGFloat(0.7) - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() view.reactive.alpha <~ SignalProducer(pipeSignal) observer.send(value: firstChange) @@ -52,7 +51,7 @@ class UIViewSpec: QuickSpec { it("should accept changes from bindings to its user interaction enabling state") { view.isUserInteractionEnabled = true - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() view.reactive.isUserInteractionEnabled <~ SignalProducer(pipeSignal) observer.send(value: true) @@ -65,7 +64,7 @@ class UIViewSpec: QuickSpec { it("should accept changes from bindings to its background color") { view.backgroundColor = .white - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() view.reactive.backgroundColor <~ SignalProducer(pipeSignal) observer.send(value: .yellow) @@ -81,7 +80,7 @@ class UIViewSpec: QuickSpec { it("should accept changes from bindings to its tint color") { view.tintColor = .white - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() view.reactive.tintColor <~ SignalProducer(pipeSignal) observer.send(value: .yellow) diff --git a/ReactiveMapKit/MKLocalSearchRequest.swift b/ReactiveMapKit/MKLocalSearchRequest.swift index 7a9650713e..b5b20201aa 100644 --- a/ReactiveMapKit/MKLocalSearchRequest.swift +++ b/ReactiveMapKit/MKLocalSearchRequest.swift @@ -1,6 +1,5 @@ import ReactiveSwift import ReactiveCocoa -import Result import MapKit private let defaultLocalSearchError = NSError(domain: "org.reactivecocoa.ReactiveCocoa.Reactivity.MKLocalSearchRequest", @@ -9,7 +8,7 @@ private let defaultLocalSearchError = NSError(domain: "org.reactivecocoa.Reactiv @available(tvOS 9.2, *) extension Reactive where Base: MKLocalSearch.Request { /// A SignalProducer which performs an `MKLocalSearch`. - public var search: SignalProducer { + public var search: SignalProducer { return SignalProducer {[base = self.base] observer, lifetime in let search = MKLocalSearch(request: base) search.start { response, error in @@ -17,7 +16,7 @@ extension Reactive where Base: MKLocalSearch.Request { observer.send(value: response) observer.sendCompleted() } else { - observer.send(error: AnyError(error ?? defaultLocalSearchError)) + observer.send(error: error ?? defaultLocalSearchError) } } lifetime.observeEnded(search.cancel) diff --git a/ReactiveMapKitTests/MKMapViewSpec.swift b/ReactiveMapKitTests/MKMapViewSpec.swift index 1039aae897..c71272a584 100644 --- a/ReactiveMapKitTests/MKMapViewSpec.swift +++ b/ReactiveMapKitTests/MKMapViewSpec.swift @@ -3,7 +3,6 @@ import ReactiveCocoa import ReactiveMapKit import Quick import Nimble -import enum Result.NoError import MapKit @available(tvOS 9.2, *) @@ -35,7 +34,7 @@ class MKMapViewSpec: QuickSpec { it("should accept changes from bindings to its map type") { expect(mapView.mapType) == MKMapType.standard - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() mapView.reactive.mapType <~ pipeSignal @@ -49,7 +48,7 @@ class MKMapViewSpec: QuickSpec { it("should accept changes from bindings to its zoom enabled state") { expect(mapView.isZoomEnabled) == true - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() mapView.reactive.isZoomEnabled <~ pipeSignal @@ -60,7 +59,7 @@ class MKMapViewSpec: QuickSpec { it("should accept changes from bindings to its scroll enabled state") { expect(mapView.isScrollEnabled) == true - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() mapView.reactive.isScrollEnabled <~ pipeSignal @@ -72,7 +71,7 @@ class MKMapViewSpec: QuickSpec { it("should accept changes from bindings to its pitch enabled state") { expect(mapView.isPitchEnabled) == true - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() mapView.reactive.isPitchEnabled <~ pipeSignal @@ -83,7 +82,7 @@ class MKMapViewSpec: QuickSpec { it("should accept changes from bindings to its rotate enabled state") { expect(mapView.isRotateEnabled) == true - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() mapView.reactive.isRotateEnabled <~ pipeSignal From d45848ae7532edcca828da59ca968a767f0c1380 Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Fri, 19 Apr 2019 11:37:00 +0200 Subject: [PATCH 0984/1028] Add Upgrade-Instructions Instructions for Upgrade to the next version where Result will be removed --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d877c77ec..6033a54895 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ 1. Update ReactiveSwift to 6.0 1. Remove dependency on antitypical/Result +**Upgrade to master** + +* If you have used `Result` only as dependency of `ReactiveSwift`, remove all instances of `import Result`, `import enum Result.NoError` or `import struct Result.AnyError` and remove the `Result` Framework from your project. +* Replace all cases where `NoError` was used in a `Signal` or `SignalProducer` with `Never` +* Replace all cases where `AnyError` was used in a `Signal` or `SignalProducer` with `Swift.Error` + # 9.0.0 1. Make UITextField and UITextView text and attributedText values non-optional. (#3591, kudos to @Marcocanc) 1. KVO observations can now be made with Smart Key Path in Swift 3.2+, using `producer(for:)` and `signal(for:)` available on `NSObject.reactive`. (#3491, kudos to @andersio) From 437de272a3a6c9bb985e2a01141b5a1cc1018741 Mon Sep 17 00:00:00 2001 From: Gordon Fontenot Date: Fri, 19 Apr 2019 19:36:31 -0500 Subject: [PATCH 0985/1028] Update version in README to 9.0 This is the newest release, so we should make it the version we talk about in the README. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 29c5567604..d3571cb4ea 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ If you use [Carthage][] to manage your dependencies, simply add ReactiveCocoa to your `Cartfile`: ``` -github "ReactiveCocoa/ReactiveCocoa" ~> 8.0 +github "ReactiveCocoa/ReactiveCocoa" ~> 9.0 ``` If you use Carthage to build your dependencies, make sure you have added `ReactiveCocoa.framework` and `ReactiveSwift.framework` to the "_Linked Frameworks and Libraries_" section of your target, and have included them in your Carthage framework copying build phase. @@ -111,7 +111,7 @@ If you use [CocoaPods][] to manage your dependencies, simply add ReactiveCocoa to your `Podfile`: ``` -pod 'ReactiveCocoa', '~> 8.0' +pod 'ReactiveCocoa', '~> 9.0' ``` #### Git submodule From 8e5e16f62e9703f5121d4809a0e3a1f6f6b91919 Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Sat, 27 Apr 2019 15:22:10 -0700 Subject: [PATCH 0986/1028] 10.0 release (#3665) * Swift 5.0 * 10.0 * Remove playground binary products. --- .gitignore | 1 + .swift-version | 2 +- CHANGELOG.md | 1 + ReactiveCocoa.podspec | 4 ++-- ReactiveCocoa.xcodeproj/project.pbxproj | 8 ++++---- ReactiveCocoaTests/InterceptingSpec.swift | 6 +++--- ReactiveMapKit.podspec | 4 ++-- 7 files changed, 14 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 28f5624a0b..048025513e 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ profile PlaygroundUtility.remap *.xctimeline *.xcscmblueprint +*.o # SwiftPM .build diff --git a/.swift-version b/.swift-version index bf77d54968..819e07a224 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -4.2 +5.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 6033a54895..e64fbf98e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master *Please put new entries at the top. +# 10.0.0 1. Update ReactiveSwift to 6.0 1. Remove dependency on antitypical/Result diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index dc4e47cb8f..a17b4cf6fb 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "9.0.0" + s.version = "10.0.0" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. @@ -27,5 +27,5 @@ Pod::Spec.new do |s| s.dependency 'ReactiveSwift', '~> 6.0' s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "$(inherited) -suppress-warnings" } - s.swift_version = '4.2' + s.swift_version = '5.0' end diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 307d6bdf04..1cab1e4231 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -2506,7 +2506,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -2533,7 +2533,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -2648,7 +2648,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -2718,7 +2718,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)-Tests"; PRODUCT_NAME = "$(PROJECT_NAME)"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; diff --git a/ReactiveCocoaTests/InterceptingSpec.swift b/ReactiveCocoaTests/InterceptingSpec.swift index edd8d6ab65..73fab9ef35 100644 --- a/ReactiveCocoaTests/InterceptingSpec.swift +++ b/ReactiveCocoaTests/InterceptingSpec.swift @@ -605,7 +605,7 @@ class InterceptingSpec: QuickSpec { it("should report the original class") { _ = object.reactive.trigger(for: #selector(object.lifeIsGood)) - expect((object as AnyObject).objcClass).to(beIdenticalTo(originalClass)) + expect((object as AnyObject).objcClass!).to(beIdenticalTo(originalClass)) } it("should report the original class when it's KVO'd after dynamically subclassing") { @@ -615,7 +615,7 @@ class InterceptingSpec: QuickSpec { .producer(forKeyPath: #keyPath(InterceptedObject.objectValue)) .start() - expect((object as AnyObject).objcClass).to(beIdenticalTo(originalClass)) + expect((object as AnyObject).objcClass!).to(beIdenticalTo(originalClass)) } it("should report the original class when it's KVO'd before dynamically subclassing") { @@ -624,7 +624,7 @@ class InterceptingSpec: QuickSpec { .start() _ = object.reactive.trigger(for: #selector(object.lifeIsGood)) - expect((object as AnyObject).objcClass).to(beIdenticalTo(originalClass)) + expect((object as AnyObject).objcClass!).to(beIdenticalTo(originalClass)) } } diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index c004e058c6..a851416e65 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveMapKit" - s.version = "9.0.0" + s.version = "10.0.0" s.summary = "MapKit bindings for ReactiveCocoa." s.description = <<-DESC Provide MapKit bindings for ReactiveCocoa. ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. @@ -19,5 +19,5 @@ Pod::Spec.new do |s| s.dependency 'ReactiveCocoa', "#{s.version}" s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "$(inherited) -suppress-warnings" } - s.swift_version = '4.2' + s.swift_version = '5.0' end From b933e2eafea87ddca6930c71a10aa444abe06ee5 Mon Sep 17 00:00:00 2001 From: Gordon Fontenot Date: Tue, 30 Apr 2019 15:20:14 -0500 Subject: [PATCH 0987/1028] Allow for observable enum values (#3667) * Fix method name in describe block * Allow for observable enum values Previously, when using the `NSObject.producer/signal(for:)` syntax with Swift 4 KeyPaths, we were force-casting the resulting value to the expected type. This works for native Objective-C types, but breaks for Objective-C enums, which are sent as their raw Integer types. This means the force-cast would result in a crash if you tried to observe an enum value. To fix this, we can add a special override for the KeyPath observation API that handles the RawRepresentable conversion automatically. This will then be used if the user specifies a KeyPath that points to a RawRepresentable type. --- .../NSObject+KeyValueObserving.swift | 64 +++++++++++++++++++ .../KeyValueObservingSpec+Swift4.swift | 39 ++++++++++- 2 files changed, 101 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoa/NSObject+KeyValueObserving.swift b/ReactiveCocoa/NSObject+KeyValueObserving.swift index 09d78684dd..2c306ffd92 100644 --- a/ReactiveCocoa/NSObject+KeyValueObserving.swift +++ b/ReactiveCocoa/NSObject+KeyValueObserving.swift @@ -106,6 +106,22 @@ extension Reactive where Base: NSObject { return producer(for: keyPath) { $0 as! U? } } + /// Create a producer which sends the current value and all the subsequent + /// changes of the property specified by the key path. + /// + /// The producer completes when the object deinitializes. + /// + /// - parameters: + /// - keyPath: The key path of the property to be observed. + /// + /// - returns: A producer emitting values of the property specified by the + /// key path. + public func producer(for keyPath: KeyPath) -> SignalProducer where U: RawRepresentable { + return producer(for: keyPath) { + $0.flatMap { value in U(rawValue: value as! U.RawValue)! } + } + } + /// Create a signal all changes of the property specified by the key path. /// /// The signal completes when the object deinitializes. @@ -122,6 +138,24 @@ extension Reactive where Base: NSObject { return signal(for: keyPath) { $0 as! U? } } + /// Create a signal all changes of the property specified by the key path. + /// + /// The signal completes when the object deinitializes. + /// + /// - note: + /// Does not send the initial value. See `producer(forKeyPath:)`. + /// + /// - parameters: + /// - keyPath: The key path of the property to be observed. + /// + /// - returns: A producer emitting values of the property specified by the + /// key path. + public func signal(for keyPath: KeyPath) -> Signal where U: RawRepresentable { + return signal(for: keyPath) { + $0.flatMap { value in U(rawValue: value as! U.RawValue) } + } + } + /// Create a producer which sends the current value and all the subsequent /// changes of the property specified by the key path. /// @@ -136,6 +170,20 @@ extension Reactive where Base: NSObject { return producer(for: keyPath) { $0 as! U } } + /// Create a producer which sends the current value and all the subsequent + /// changes of the property specified by the key path. + /// + /// The producer completes when the object deinitializes. + /// + /// - parameters: + /// - keyPath: The key path of the property to be observed. + /// + /// - returns: A producer emitting values of the property specified by the + /// key path. + public func producer(for keyPath: KeyPath) -> SignalProducer where U: RawRepresentable { + return producer(for: keyPath) { U(rawValue: $0 as! U.RawValue)! } + } + /// Create a signal all changes of the property specified by the key path. /// /// The signal completes when the object deinitializes. @@ -151,6 +199,22 @@ extension Reactive where Base: NSObject { public func signal(for keyPath: KeyPath) -> Signal { return signal(for: keyPath) { $0 as! U } } + + /// Create a signal all changes of the property specified by the key path. + /// + /// The signal completes when the object deinitializes. + /// + /// - note: + /// Does not send the initial value. See `producer(forKeyPath:)`. + /// + /// - parameters: + /// - keyPath: The key path of the property to be observed. + /// + /// - returns: A producer emitting values of the property specified by the + /// key path. + public func signal(for keyPath: KeyPath) -> Signal where U: RawRepresentable { + return signal(for: keyPath) { U(rawValue: $0 as! U.RawValue)! } + } } extension Property where Value: OptionalProtocol { diff --git a/ReactiveCocoaTests/KeyValueObservingSpec+Swift4.swift b/ReactiveCocoaTests/KeyValueObservingSpec+Swift4.swift index a93da30d64..4a6e643aa4 100644 --- a/ReactiveCocoaTests/KeyValueObservingSpec+Swift4.swift +++ b/ReactiveCocoaTests/KeyValueObservingSpec+Swift4.swift @@ -7,7 +7,7 @@ import Nimble #if swift(>=3.2) class KeyValueObservingSwift4Spec: QuickSpec { override func spec() { - describe("NSObject.signal(forKeyPath:)") { + describe("NSObject.signal(for:)") { it("should not send the initial value") { let object = ObservableObject() var values: [Int] = [] @@ -19,12 +19,25 @@ class KeyValueObservingSwift4Spec: QuickSpec { expect(values) == [] } + it("automatically converts enums") { + let object = ObservableObject() + var values: [ObservableEnum] = [] + + object.reactive + .signal(for: \.rac_enum) + .observeValues { values.append($0) } + + object.rac_enum = .two + + expect(values) == [.two] + } + itBehavesLike("a reactive key value observer using Swift 4 Smart Key Path") { ["observe": "signal"] } } - describe("NSObject.producer(forKeyPath:)") { + describe("NSObject.producer(for:)") { it("should send the initial value") { let object = ObservableObject() var values: [Int] = [] @@ -64,6 +77,21 @@ class KeyValueObservingSwift4Spec: QuickSpec { expect(values) == [0] } + it("should automatically convert enums") { + let object = ObservableObject() + var values: [ObservableEnum] = [] + + object.reactive + .producer(for: \.rac_enum) + .startWithValues { value in + values.append(value) + } + + object.rac_enum = .one + + expect(values) == [.zero, .one] + } + itBehavesLike("a reactive key value observer using Swift 4 Smart Key Path") { ["observe": "producer"] } @@ -584,8 +612,15 @@ fileprivate class KeyValueObservingSwift4SpecConfiguration: QuickConfiguration { private final class Token {} +@objc private enum ObservableEnum: Int { + case zero + case one + case two +} + private class ObservableObject: NSObject { @objc dynamic var rac_value: Int = 0 + @objc dynamic var rac_enum: ObservableEnum = .zero @objc dynamic var target: AnyObject? @objc dynamic weak var weakTarget: AnyObject? From 5473b73083608344a33056fb364b97a714f1ab9c Mon Sep 17 00:00:00 2001 From: Thomas Di Meco Date: Fri, 10 May 2019 08:47:48 +0200 Subject: [PATCH 0988/1028] Add new bindings for standard WatchKit components --- ReactiveCocoa.xcodeproj/project.pbxproj | 58 ++++++++++++++++++- .../WatchKit/WKInterfaceActivityRing.swift | 13 +++++ .../WatchKit/WKInterfaceController.swift | 9 +++ ReactiveCocoa/WatchKit/WKInterfaceDate.swift | 9 +++ ReactiveCocoa/WatchKit/WKInterfaceGroup.swift | 34 +++++++++++ ReactiveCocoa/WatchKit/WKInterfaceImage.swift | 24 ++++++++ .../WatchKit/WKInterfaceInlineMovie.swift | 30 ++++++++++ ReactiveCocoa/WatchKit/WKInterfaceMovie.swift | 24 ++++++++ .../WatchKit/WKInterfaceObject.swift | 14 +++++ .../WatchKit/WKInterfacePicker.swift | 19 ++++++ .../WatchKit/WKInterfaceSeparator.swift | 9 +++ .../WatchKit/WKInterfaceSlider.swift | 24 ++++++++ .../WatchKit/WKInterfaceSwitch.swift | 29 ++++++++++ ReactiveCocoa/WatchKit/WKInterfaceTimer.swift | 14 +++++ .../WatchKit/WKInterfaceVolumeControl.swift | 10 ++++ 15 files changed, 319 insertions(+), 1 deletion(-) create mode 100644 ReactiveCocoa/WatchKit/WKInterfaceActivityRing.swift create mode 100644 ReactiveCocoa/WatchKit/WKInterfaceController.swift create mode 100644 ReactiveCocoa/WatchKit/WKInterfaceDate.swift create mode 100644 ReactiveCocoa/WatchKit/WKInterfaceGroup.swift create mode 100644 ReactiveCocoa/WatchKit/WKInterfaceImage.swift create mode 100644 ReactiveCocoa/WatchKit/WKInterfaceInlineMovie.swift create mode 100644 ReactiveCocoa/WatchKit/WKInterfaceMovie.swift create mode 100644 ReactiveCocoa/WatchKit/WKInterfaceObject.swift create mode 100644 ReactiveCocoa/WatchKit/WKInterfacePicker.swift create mode 100644 ReactiveCocoa/WatchKit/WKInterfaceSeparator.swift create mode 100644 ReactiveCocoa/WatchKit/WKInterfaceSlider.swift create mode 100644 ReactiveCocoa/WatchKit/WKInterfaceSwitch.swift create mode 100644 ReactiveCocoa/WatchKit/WKInterfaceTimer.swift create mode 100644 ReactiveCocoa/WatchKit/WKInterfaceVolumeControl.swift diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 1cab1e4231..36bf839849 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -339,6 +339,20 @@ D9558AB91DFF86C0003254E1 /* NSPopUpButtonSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9558AB51DFF7B90003254E1 /* NSPopUpButtonSpec.swift */; }; E3AA54C12142918B0077B206 /* WKInterfaceLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3AA54C02142918B0077B206 /* WKInterfaceLabel.swift */; }; E3AA54C3214292550077B206 /* WKInterfaceButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3AA54C2214292540077B206 /* WKInterfaceButton.swift */; }; + E921428C2284A136007E412D /* WKInterfaceObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = E921428A2284A12B007E412D /* WKInterfaceObject.swift */; }; + E921428E2284A44F007E412D /* WKInterfaceDate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E921428D2284A44F007E412D /* WKInterfaceDate.swift */; }; + E92142902284A4EF007E412D /* WKInterfaceTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E921428F2284A4EF007E412D /* WKInterfaceTimer.swift */; }; + E92142922284AA64007E412D /* WKInterfaceSwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = E92142912284AA64007E412D /* WKInterfaceSwitch.swift */; }; + E92142942284ABDF007E412D /* WKInterfaceSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E92142932284ABDF007E412D /* WKInterfaceSlider.swift */; }; + E92142962284AD19007E412D /* WKInterfaceActivityRing.swift in Sources */ = {isa = PBXBuildFile; fileRef = E92142952284AD19007E412D /* WKInterfaceActivityRing.swift */; }; + E92142982284AEF0007E412D /* WKInterfaceGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = E92142972284AEF0007E412D /* WKInterfaceGroup.swift */; }; + E921429A2284AF99007E412D /* WKInterfaceSeparator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E92142992284AF99007E412D /* WKInterfaceSeparator.swift */; }; + E921429C2284B0A8007E412D /* WKInterfaceImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E921429B2284B0A8007E412D /* WKInterfaceImage.swift */; }; + E921429E2284B1C4007E412D /* WKInterfaceVolumeControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = E921429D2284B1C4007E412D /* WKInterfaceVolumeControl.swift */; }; + E92142A02284B4FF007E412D /* WKInterfacePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = E921429F2284B4FF007E412D /* WKInterfacePicker.swift */; }; + E92142A22284B64E007E412D /* WKInterfaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E92142A12284B64E007E412D /* WKInterfaceController.swift */; }; + E92142A9228552CC007E412D /* WKInterfaceMovie.swift in Sources */ = {isa = PBXBuildFile; fileRef = E92142A8228552CC007E412D /* WKInterfaceMovie.swift */; }; + E92142AB228553C5007E412D /* WKInterfaceInlineMovie.swift in Sources */ = {isa = PBXBuildFile; fileRef = E92142AA228553C5007E412D /* WKInterfaceInlineMovie.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -634,6 +648,20 @@ D9558AB71DFF805A003254E1 /* NSPopUpButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSPopUpButton.swift; sourceTree = ""; }; E3AA54C02142918B0077B206 /* WKInterfaceLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WKInterfaceLabel.swift; sourceTree = ""; }; E3AA54C2214292540077B206 /* WKInterfaceButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WKInterfaceButton.swift; sourceTree = ""; }; + E921428A2284A12B007E412D /* WKInterfaceObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKInterfaceObject.swift; sourceTree = ""; }; + E921428D2284A44F007E412D /* WKInterfaceDate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKInterfaceDate.swift; sourceTree = ""; }; + E921428F2284A4EF007E412D /* WKInterfaceTimer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKInterfaceTimer.swift; sourceTree = ""; }; + E92142912284AA64007E412D /* WKInterfaceSwitch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKInterfaceSwitch.swift; sourceTree = ""; }; + E92142932284ABDF007E412D /* WKInterfaceSlider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKInterfaceSlider.swift; sourceTree = ""; }; + E92142952284AD19007E412D /* WKInterfaceActivityRing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKInterfaceActivityRing.swift; sourceTree = ""; }; + E92142972284AEF0007E412D /* WKInterfaceGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKInterfaceGroup.swift; sourceTree = ""; }; + E92142992284AF99007E412D /* WKInterfaceSeparator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKInterfaceSeparator.swift; sourceTree = ""; }; + E921429B2284B0A8007E412D /* WKInterfaceImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKInterfaceImage.swift; sourceTree = ""; }; + E921429D2284B1C4007E412D /* WKInterfaceVolumeControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKInterfaceVolumeControl.swift; sourceTree = ""; }; + E921429F2284B4FF007E412D /* WKInterfacePicker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKInterfacePicker.swift; sourceTree = ""; }; + E92142A12284B64E007E412D /* WKInterfaceController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKInterfaceController.swift; sourceTree = ""; }; + E92142A8228552CC007E412D /* WKInterfaceMovie.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKInterfaceMovie.swift; sourceTree = ""; }; + E92142AA228553C5007E412D /* WKInterfaceInlineMovie.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKInterfaceInlineMovie.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1016,9 +1044,9 @@ 9AA0BD771DDE03DE00531FCF /* ObjC+Runtime.swift */, 9A90374E1ED61C6300345D62 /* ReactiveSwift+Lifetime.swift */, 9ADFE5A41DC0001C001E11F7 /* Synchronizing.swift */, - E3AA54BF214291590077B206 /* WatchKit */, 9A1D05E91D93E9F100ACF44C /* AppKit */, 9A1D05EB1D93E9F100ACF44C /* UIKit */, + E3AA54BF214291590077B206 /* WatchKit */, 538DCB761DCA5E1600332880 /* Shared */, D04725ED19E49ED7006002AA /* Supporting Files */, 9AB15C791E26CD9A00997378 /* Deprecations+Removals.swift */, @@ -1150,8 +1178,22 @@ E3AA54BF214291590077B206 /* WatchKit */ = { isa = PBXGroup; children = ( + E921428A2284A12B007E412D /* WKInterfaceObject.swift */, E3AA54C02142918B0077B206 /* WKInterfaceLabel.swift */, E3AA54C2214292540077B206 /* WKInterfaceButton.swift */, + E921428D2284A44F007E412D /* WKInterfaceDate.swift */, + E921428F2284A4EF007E412D /* WKInterfaceTimer.swift */, + E92142912284AA64007E412D /* WKInterfaceSwitch.swift */, + E92142932284ABDF007E412D /* WKInterfaceSlider.swift */, + E92142952284AD19007E412D /* WKInterfaceActivityRing.swift */, + E921429B2284B0A8007E412D /* WKInterfaceImage.swift */, + E92142A8228552CC007E412D /* WKInterfaceMovie.swift */, + E92142AA228553C5007E412D /* WKInterfaceInlineMovie.swift */, + E921429D2284B1C4007E412D /* WKInterfaceVolumeControl.swift */, + E92142972284AEF0007E412D /* WKInterfaceGroup.swift */, + E92142992284AF99007E412D /* WKInterfaceSeparator.swift */, + E921429F2284B4FF007E412D /* WKInterfacePicker.swift */, + E92142A12284B64E007E412D /* WKInterfaceController.swift */, ); path = WatchKit; sourceTree = ""; @@ -1797,26 +1839,40 @@ files = ( 9A2E42601DAA6737006D909F /* CocoaTarget.swift in Sources */, E3AA54C3214292550077B206 /* WKInterfaceButton.swift in Sources */, + E92142942284ABDF007E412D /* WKInterfaceSlider.swift in Sources */, 9A74884A1E3B8ACE00CD0317 /* DelegateProxy.swift in Sources */, + E92142A9228552CC007E412D /* WKInterfaceMovie.swift in Sources */, 9A9DFEE51DA7B5500039EE1B /* NSObject+Intercepting.swift in Sources */, + E921429E2284B1C4007E412D /* WKInterfaceVolumeControl.swift in Sources */, 9A54A21D1DE00D09001739B3 /* ObjC+Selector.swift in Sources */, 9AB15C7C1E26CD9A00997378 /* Deprecations+Removals.swift in Sources */, + E92142982284AEF0007E412D /* WKInterfaceGroup.swift in Sources */, 9AA0BD7E1DDE03DE00531FCF /* ObjC+Runtime.swift in Sources */, + E921428C2284A136007E412D /* WKInterfaceObject.swift in Sources */, 9AA0BD9A1DDE7A2200531FCF /* ObjCRuntimeAliases.m in Sources */, 9ADE4A931DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, + E92142A02284B4FF007E412D /* WKInterfacePicker.swift in Sources */, 9AA0BD8C1DDE153A00531FCF /* ObjC+Constants.swift in Sources */, 9ADFE5A71DC0001C001E11F7 /* Synchronizing.swift in Sources */, 9ADE4A7E1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, + E921429A2284AF99007E412D /* WKInterfaceSeparator.swift in Sources */, 9AA0BD831DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift in Sources */, E3AA54C12142918B0077B206 /* WKInterfaceLabel.swift in Sources */, + E92142922284AA64007E412D /* WKInterfaceSwitch.swift in Sources */, CD0C45E01CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 9AA0BD911DDE29F800531FCF /* NSObject+ObjCRuntime.swift in Sources */, + E92142A22284B64E007E412D /* WKInterfaceController.swift in Sources */, + E92142AB228553C5007E412D /* WKInterfaceInlineMovie.swift in Sources */, + E921428E2284A44F007E412D /* WKInterfaceDate.swift in Sources */, + E92142962284AD19007E412D /* WKInterfaceActivityRing.swift in Sources */, 9AF0EA771D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, 9A9037511ED61C6300345D62 /* ReactiveSwift+Lifetime.swift in Sources */, + E92142902284A4EF007E412D /* WKInterfaceTimer.swift in Sources */, 9A1D05E21D93E99100ACF44C /* NSObject+Association.swift in Sources */, 4A0E11011D2A92720065D310 /* AnyObject+Lifetime.swift in Sources */, 9AE7C2A61DDD7F5100F7534C /* ObjC+Messages.swift in Sources */, 9AD0F06C1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, + E921429C2284B0A8007E412D /* WKInterfaceImage.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ReactiveCocoa/WatchKit/WKInterfaceActivityRing.swift b/ReactiveCocoa/WatchKit/WKInterfaceActivityRing.swift new file mode 100644 index 0000000000..41ca1607a2 --- /dev/null +++ b/ReactiveCocoa/WatchKit/WKInterfaceActivityRing.swift @@ -0,0 +1,13 @@ +import ReactiveSwift +import WatchKit +import HealthKit + +@available(watchOSApplicationExtension 2.2, *) +extension Reactive where Base: WKInterfaceActivityRing { + /// Sets the summary of the activity ring. + /// + /// - Parameter animated: Whether updates are animated. + public func activitySummary(animated: Bool) -> BindingTarget { + return makeBindingTarget { $0.setActivitySummary($1, animated: animated) } + } +} diff --git a/ReactiveCocoa/WatchKit/WKInterfaceController.swift b/ReactiveCocoa/WatchKit/WKInterfaceController.swift new file mode 100644 index 0000000000..68e88eb9a0 --- /dev/null +++ b/ReactiveCocoa/WatchKit/WKInterfaceController.swift @@ -0,0 +1,9 @@ +import ReactiveSwift +import WatchKit + +extension Reactive where Base: WKInterfaceController { + /// Sets the title of the controller. + public var title: BindingTarget { + return makeBindingTarget { $0.setTitle($1) } + } +} diff --git a/ReactiveCocoa/WatchKit/WKInterfaceDate.swift b/ReactiveCocoa/WatchKit/WKInterfaceDate.swift new file mode 100644 index 0000000000..169054a916 --- /dev/null +++ b/ReactiveCocoa/WatchKit/WKInterfaceDate.swift @@ -0,0 +1,9 @@ +import ReactiveSwift +import WatchKit + +extension Reactive where Base: WKInterfaceDate { + /// Sets the color of the text of the date. + public var textColor: BindingTarget { + return makeBindingTarget { $0.setTextColor($1) } + } +} diff --git a/ReactiveCocoa/WatchKit/WKInterfaceGroup.swift b/ReactiveCocoa/WatchKit/WKInterfaceGroup.swift new file mode 100644 index 0000000000..12953fe780 --- /dev/null +++ b/ReactiveCocoa/WatchKit/WKInterfaceGroup.swift @@ -0,0 +1,34 @@ +import ReactiveSwift +import WatchKit + +extension Reactive where Base: WKInterfaceGroup { + /// Sets the background color of the group. + public var backgroundColor: BindingTarget { + return makeBindingTarget { $0.setBackgroundColor($1) } + } + + /// Sets the background image of the group. + public var backgroundImage: BindingTarget { + return makeBindingTarget { $0.setBackgroundImage($1) } + } + + /// Sets the background image data of the group. + public var backgroundImageData: BindingTarget { + return makeBindingTarget { $0.setBackgroundImageData($1) } + } + + /// Sets the background named image of the group. + public var backgroundImageNamed: BindingTarget { + return makeBindingTarget { $0.setBackgroundImageNamed($1) } + } + + /// Sets the corner radius of the group. + public var cornerRadius: BindingTarget { + return makeBindingTarget { $0.setCornerRadius($1) } + } + + /// Sets the content inset of the group. + public var contentInset: BindingTarget { + return makeBindingTarget { $0.setContentInset($1) } + } +} diff --git a/ReactiveCocoa/WatchKit/WKInterfaceImage.swift b/ReactiveCocoa/WatchKit/WKInterfaceImage.swift new file mode 100644 index 0000000000..15bc63328b --- /dev/null +++ b/ReactiveCocoa/WatchKit/WKInterfaceImage.swift @@ -0,0 +1,24 @@ +import ReactiveSwift +import WatchKit + +extension Reactive where Base: WKInterfaceImage { + /// Sets the image. + public var image: BindingTarget { + return makeBindingTarget { $0.setImage($1) } + } + + /// Sets the data of the image. + public var imageData: BindingTarget { + return makeBindingTarget { $0.setImageData($1) } + } + + /// Sets the name of the image. + public var imageNamed: BindingTarget { + return makeBindingTarget { $0.setImageNamed($1) } + } + + /// Sets the tint color of the template image. + public var tintColor: BindingTarget { + return makeBindingTarget { $0.setTintColor($1) } + } +} diff --git a/ReactiveCocoa/WatchKit/WKInterfaceInlineMovie.swift b/ReactiveCocoa/WatchKit/WKInterfaceInlineMovie.swift new file mode 100644 index 0000000000..52acaf5ae8 --- /dev/null +++ b/ReactiveCocoa/WatchKit/WKInterfaceInlineMovie.swift @@ -0,0 +1,30 @@ +import ReactiveSwift +import WatchKit + +@available(watchOSApplicationExtension 3.0, *) +extension Reactive where Base: WKInterfaceInlineMovie { + /// Sets the url of the movie. + public var movieURL: BindingTarget { + return makeBindingTarget { $0.setMovieURL($1) } + } + + /// Sets the video gravity of the movie. + public var videoGravity: BindingTarget { + return makeBindingTarget { $0.setVideoGravity($1) } + } + + /// Sets the poster image of the movie. + public var posterImage: BindingTarget { + return makeBindingTarget { $0.setPosterImage($1) } + } + + /// Whether the movie loops. + public var loops: BindingTarget { + return makeBindingTarget { $0.setLoops($1) } + } + + /// Whether the movie autoplays. + public var autoplays: BindingTarget { + return makeBindingTarget { $0.setAutoplays($1) } + } +} diff --git a/ReactiveCocoa/WatchKit/WKInterfaceMovie.swift b/ReactiveCocoa/WatchKit/WKInterfaceMovie.swift new file mode 100644 index 0000000000..6cedd849d9 --- /dev/null +++ b/ReactiveCocoa/WatchKit/WKInterfaceMovie.swift @@ -0,0 +1,24 @@ +import ReactiveSwift +import WatchKit + +extension Reactive where Base: WKInterfaceMovie { + /// Sets the url of the movie. + public var movieURL: BindingTarget { + return makeBindingTarget { $0.setMovieURL($1) } + } + + /// Sets the video gravity of the movie. + public var videoGravity: BindingTarget { + return makeBindingTarget { $0.setVideoGravity($1) } + } + + /// Sets the poster image of the movie. + public var posterImage: BindingTarget { + return makeBindingTarget { $0.setPosterImage($1) } + } + + /// Whether the movie loops. + public var loops: BindingTarget { + return makeBindingTarget { $0.setLoops($1) } + } +} diff --git a/ReactiveCocoa/WatchKit/WKInterfaceObject.swift b/ReactiveCocoa/WatchKit/WKInterfaceObject.swift new file mode 100644 index 0000000000..cc9e6f1548 --- /dev/null +++ b/ReactiveCocoa/WatchKit/WKInterfaceObject.swift @@ -0,0 +1,14 @@ +import ReactiveSwift +import WatchKit + +extension Reactive where Base: WKInterfaceObject { + /// Sets the alpha value of the object. + public var alpha: BindingTarget { + return makeBindingTarget { $0.setAlpha($1) } + } + + /// Sets whether the object is hidden. + public var isHidden: BindingTarget { + return makeBindingTarget { $0.setHidden($1) } + } +} diff --git a/ReactiveCocoa/WatchKit/WKInterfacePicker.swift b/ReactiveCocoa/WatchKit/WKInterfacePicker.swift new file mode 100644 index 0000000000..88abf4a5ba --- /dev/null +++ b/ReactiveCocoa/WatchKit/WKInterfacePicker.swift @@ -0,0 +1,19 @@ +import ReactiveSwift +import WatchKit + +extension Reactive where Base: WKInterfacePicker { + /// Sets the list of items of the picker. + public var items: BindingTarget<[WKPickerItem]?> { + return makeBindingTarget { $0.setItems($1) } + } + + /// Sets the selected item index of the picker. + public var selectedItemIndex: BindingTarget { + return makeBindingTarget { $0.setSelectedItemIndex($1) } + } + + /// Sets whether the picker is enabled. + public var isEnabled: BindingTarget { + return makeBindingTarget { $0.setEnabled($1) } + } +} diff --git a/ReactiveCocoa/WatchKit/WKInterfaceSeparator.swift b/ReactiveCocoa/WatchKit/WKInterfaceSeparator.swift new file mode 100644 index 0000000000..e8fb5015e9 --- /dev/null +++ b/ReactiveCocoa/WatchKit/WKInterfaceSeparator.swift @@ -0,0 +1,9 @@ +import ReactiveSwift +import WatchKit + +extension Reactive where Base: WKInterfaceSeparator { + /// Sets the color of the separator. + public var color: BindingTarget { + return makeBindingTarget { $0.setColor($1) } + } +} diff --git a/ReactiveCocoa/WatchKit/WKInterfaceSlider.swift b/ReactiveCocoa/WatchKit/WKInterfaceSlider.swift new file mode 100644 index 0000000000..faac72f80b --- /dev/null +++ b/ReactiveCocoa/WatchKit/WKInterfaceSlider.swift @@ -0,0 +1,24 @@ +import ReactiveSwift +import WatchKit + +extension Reactive where Base: WKInterfaceSlider { + /// Sets the value of the slider. + public var value: BindingTarget { + return makeBindingTarget { $0.setValue($1) } + } + + /// Sets the number of steps of the slider. + public var numberOfSteps: BindingTarget { + return makeBindingTarget { $0.setNumberOfSteps($1) } + } + + /// Sets the color of the slider. + public var color: BindingTarget { + return makeBindingTarget { $0.setColor($1) } + } + + /// Sets whether the slider is enabled. + public var isEnabled: BindingTarget { + return makeBindingTarget { $0.setEnabled($1) } + } +} diff --git a/ReactiveCocoa/WatchKit/WKInterfaceSwitch.swift b/ReactiveCocoa/WatchKit/WKInterfaceSwitch.swift new file mode 100644 index 0000000000..a12eaf987d --- /dev/null +++ b/ReactiveCocoa/WatchKit/WKInterfaceSwitch.swift @@ -0,0 +1,29 @@ +import ReactiveSwift +import WatchKit + +extension Reactive where Base: WKInterfaceSwitch { + /// Sets the title of the switch. + public var title: BindingTarget { + return makeBindingTarget { $0.setTitle($1) } + } + + /// Sets the attributed title of the switch. + public var attributedTitle: BindingTarget { + return makeBindingTarget { $0.setAttributedTitle($1) } + } + + /// Sets the color of the switch. + public var color: BindingTarget { + return makeBindingTarget { $0.setColor($1) } + } + + /// Sets whether the switch is on. + public var isOn: BindingTarget { + return makeBindingTarget { $0.setOn($1) } + } + + /// Sets whether the switch is enabled. + public var isEnabled: BindingTarget { + return makeBindingTarget { $0.setEnabled($1) } + } +} diff --git a/ReactiveCocoa/WatchKit/WKInterfaceTimer.swift b/ReactiveCocoa/WatchKit/WKInterfaceTimer.swift new file mode 100644 index 0000000000..452c07f8ef --- /dev/null +++ b/ReactiveCocoa/WatchKit/WKInterfaceTimer.swift @@ -0,0 +1,14 @@ +import ReactiveSwift +import WatchKit + +extension Reactive where Base: WKInterfaceTimer { + /// Sets the start date of the timer. + public var date: BindingTarget { + return makeBindingTarget { $0.setDate($1) } + } + + /// Sets the color of the text of the timer. + public var textColor: BindingTarget { + return makeBindingTarget { $0.setTextColor($1) } + } +} diff --git a/ReactiveCocoa/WatchKit/WKInterfaceVolumeControl.swift b/ReactiveCocoa/WatchKit/WKInterfaceVolumeControl.swift new file mode 100644 index 0000000000..ea34ac9205 --- /dev/null +++ b/ReactiveCocoa/WatchKit/WKInterfaceVolumeControl.swift @@ -0,0 +1,10 @@ +import ReactiveSwift +import WatchKit + +@available(watchOSApplicationExtension 5.0, *) +extension Reactive where Base: WKInterfaceVolumeControl { + /// Sets the tint color of the volume control. + public var tintColor: BindingTarget { + return makeBindingTarget { $0.setTintColor($1) } + } +} From ad6438054bb64dd1e5c4a15ad6242710a8d86a0f Mon Sep 17 00:00:00 2001 From: Thomas Di Meco Date: Fri, 10 May 2019 08:55:56 +0200 Subject: [PATCH 0989/1028] Fix some WatchKit binding mistakes --- ReactiveCocoa/Deprecations+Removals.swift | 10 ++++++++++ ReactiveCocoa/WatchKit/WKInterfaceButton.swift | 2 +- ReactiveCocoa/WatchKit/WKInterfaceLabel.swift | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoa/Deprecations+Removals.swift b/ReactiveCocoa/Deprecations+Removals.swift index 52687b8f44..65bf67a695 100644 --- a/ReactiveCocoa/Deprecations+Removals.swift +++ b/ReactiveCocoa/Deprecations+Removals.swift @@ -11,3 +11,13 @@ extension Reactive where Base: NSObject { return producer(forKeyPath: keyPath) } } + +#if os(watchOS) +import WatchKit +extension Reactive where Base: WKInterfaceButton { + @available(*, deprecated, renamed: "title") + public var text: BindingTarget { + return title + } +} +#endif diff --git a/ReactiveCocoa/WatchKit/WKInterfaceButton.swift b/ReactiveCocoa/WatchKit/WKInterfaceButton.swift index 2d1e4a7cd1..b9a551faa6 100644 --- a/ReactiveCocoa/WatchKit/WKInterfaceButton.swift +++ b/ReactiveCocoa/WatchKit/WKInterfaceButton.swift @@ -3,7 +3,7 @@ import WatchKit extension Reactive where Base: WKInterfaceButton { /// Sets the title of the button. - public var text: BindingTarget { + public var title: BindingTarget { return makeBindingTarget { $0.setTitle($1) } } diff --git a/ReactiveCocoa/WatchKit/WKInterfaceLabel.swift b/ReactiveCocoa/WatchKit/WKInterfaceLabel.swift index 61c5a54009..1c975f6a15 100644 --- a/ReactiveCocoa/WatchKit/WKInterfaceLabel.swift +++ b/ReactiveCocoa/WatchKit/WKInterfaceLabel.swift @@ -13,7 +13,7 @@ extension Reactive where Base: WKInterfaceLabel { } /// Sets the color of the text of the label. - public var textColor: BindingTarget { + public var textColor: BindingTarget { return makeBindingTarget { $0.setTextColor($1) } } } From 5b278abd1bd0cfd0a238d2e1bad904daba3e1017 Mon Sep 17 00:00:00 2001 From: Thomas Di Meco Date: Fri, 10 May 2019 09:06:02 +0200 Subject: [PATCH 0990/1028] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e64fbf98e7..6aba2005ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # master *Please put new entries at the top. +1. Add reactive extensions for standard WatchKit interface objects. # 10.0.0 1. Update ReactiveSwift to 6.0 From fb458d215742c39801c4d9f9115cb520fc49ae7f Mon Sep 17 00:00:00 2001 From: Rehat Kathuria Date: Sat, 15 Jun 2019 07:00:50 +0100 Subject: [PATCH 0991/1028] Add a binding target for the barTintColor of UINavigationBar --- ReactiveCocoa/UIKit/UINavigationBar.swift | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 ReactiveCocoa/UIKit/UINavigationBar.swift diff --git a/ReactiveCocoa/UIKit/UINavigationBar.swift b/ReactiveCocoa/UIKit/UINavigationBar.swift new file mode 100644 index 0000000000..e962bcdb7e --- /dev/null +++ b/ReactiveCocoa/UIKit/UINavigationBar.swift @@ -0,0 +1,9 @@ +import ReactiveSwift +import UIKit + +extension Reactive where Base: UINavigationBar { + /// Sets the barTintColor of the navigation bar. + public var barTintColor: BindingTarget { + return makeBindingTarget { $0.barTintColor = $1 } + } +} From 3c46f2573a79bf5e08c54507680e20c6411e1997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Olejni=CC=81k?= Date: Fri, 2 Aug 2019 20:19:43 +0200 Subject: [PATCH 0992/1028] Update dependencies --- Cartfile.resolved | 6 +++--- Carthage/Checkouts/Nimble | 2 +- Carthage/Checkouts/Quick | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cartfile.resolved b/Cartfile.resolved index a145b57e0a..57b09d884e 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,4 +1,4 @@ -github "Quick/Nimble" "v8.0.1" -github "Quick/Quick" "v2.0.0" -github "ReactiveCocoa/ReactiveSwift" "6.0.0" +github "Quick/Nimble" "v8.0.2" +github "Quick/Quick" "v2.1.0" +github "ReactiveCocoa/ReactiveSwift" "6.1.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" diff --git a/Carthage/Checkouts/Nimble b/Carthage/Checkouts/Nimble index 43304bf2b1..f8657642df 160000 --- a/Carthage/Checkouts/Nimble +++ b/Carthage/Checkouts/Nimble @@ -1 +1 @@ -Subproject commit 43304bf2b1579fd555f2fdd51742771c1e4f2b98 +Subproject commit f8657642dfdec9973efc79cc68bcef43a653a2bc diff --git a/Carthage/Checkouts/Quick b/Carthage/Checkouts/Quick index 0b4ed6c706..94df9b4495 160000 --- a/Carthage/Checkouts/Quick +++ b/Carthage/Checkouts/Quick @@ -1 +1 @@ -Subproject commit 0b4ed6c706dd0cce923b5019a605a9bcc6b1b600 +Subproject commit 94df9b449508344667e5afc7e80f8bcbff1e4c37 diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index 3eba2b48ee..b772fa0b62 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit 3eba2b48ee80f87058de5ae8bb41e585bde5c997 +Subproject commit b772fa0b624926e6e2f21acbb79297736a05c585 From 5d0673cdf218839369890afdd2a4e36559f37c43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Olejni=CC=81k?= Date: Fri, 2 Aug 2019 20:44:33 +0200 Subject: [PATCH 0993/1028] Update Cartfile --- Cartfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cartfile b/Cartfile index a0fb0df979..f358af4ff1 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" ~> 6.0 +github "ReactiveCocoa/ReactiveSwift" ~> 6.1 From 7efc563843ed383872b167a74e54c1e40e9ccf09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Olejn=C3=ADk?= Date: Fri, 2 Aug 2019 20:48:12 +0200 Subject: [PATCH 0994/1028] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6aba2005ec..fcb73d317b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master *Please put new entries at the top. 1. Add reactive extensions for standard WatchKit interface objects. +1. Update dependencies so ReactiveCocoa can be used with Xcode 11 (#3677, kudos to @olejnjak) # 10.0.0 1. Update ReactiveSwift to 6.0 From 387ff1736ac5d6b413b15d64ee7a0af0f31e81a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Olejni=CC=81k?= Date: Fri, 2 Aug 2019 21:27:22 +0200 Subject: [PATCH 0995/1028] Update tests for native Result --- ReactiveCocoaTests/CocoaActionSpec.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoaTests/CocoaActionSpec.swift b/ReactiveCocoaTests/CocoaActionSpec.swift index 072b01f3e4..6fdd6e78d7 100644 --- a/ReactiveCocoaTests/CocoaActionSpec.swift +++ b/ReactiveCocoaTests/CocoaActionSpec.swift @@ -44,7 +44,7 @@ class CocoaActionSpec: QuickSpec { expect(values) == [ true ] let result = action.apply(0).first() - expect(result?.value) == 1 + expect(try? result?.get()) == 1 expect(values).toEventually(equal([ true, false, true ])) _ = cocoaAction @@ -59,7 +59,7 @@ class CocoaActionSpec: QuickSpec { expect(values) == [ false ] let result = action.apply(0).first() - expect(result?.value) == 1 + expect(try? result?.get()) == 1 expect(values).toEventually(equal([ false, true, false ])) _ = cocoaAction From 8bc7796102b2f228590b22733904ae94791b089e Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Mon, 23 Sep 2019 20:20:15 +0900 Subject: [PATCH 0996/1028] 10.1.0 release --- CHANGELOG.md | 6 +++++- ReactiveCocoa.podspec | 4 ++-- ReactiveCocoa/Info.plist | 2 +- ReactiveMapKit.podspec | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fcb73d317b..5f5f3aff85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,11 @@ # master *Please put new entries at the top. -1. Add reactive extensions for standard WatchKit interface objects. + +# 10.1.0 1. Update dependencies so ReactiveCocoa can be used with Xcode 11 (#3677, kudos to @olejnjak) +1. Add a binding target for the `barTintColor` of `UINavigationBar` (#3675, kudos to @rehatkathuria) +1. Add reactive extensions for standard WatchKit interface objects. (#3670, kudos to @tdimeco) +1. Fix crashes of `NSObject.signal(for:)` and `NSObject.producer(for:)` with Objective-C enums (#3667, kudos to @gfontenot) # 10.0.0 1. Update ReactiveSwift to 6.0 diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index a17b4cf6fb..7ceee1d54b 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "10.0.0" + s.version = "10.1.0" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. @@ -24,7 +24,7 @@ Pod::Spec.new do |s| s.watchos.source_files = "ReactiveCocoa/WatchKit/*.{swift}" s.module_name = 'ReactiveCocoa' - s.dependency 'ReactiveSwift', '~> 6.0' + s.dependency 'ReactiveSwift', '~> 6.1' s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "$(inherited) -suppress-warnings" } s.swift_version = '5.0' diff --git a/ReactiveCocoa/Info.plist b/ReactiveCocoa/Info.plist index a18cd934e5..b22ee19865 100644 --- a/ReactiveCocoa/Info.plist +++ b/ReactiveCocoa/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 6.1.0 + 10.1.0 CFBundleSignature ???? CFBundleVersion diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index a851416e65..b8a719c221 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveMapKit" - s.version = "10.0.0" + s.version = "10.1.0" s.summary = "MapKit bindings for ReactiveCocoa." s.description = <<-DESC Provide MapKit bindings for ReactiveCocoa. ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. From 723f802a6eb76be9f112d74f26cbd39c49a45e66 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Tue, 24 Sep 2019 10:21:53 +0900 Subject: [PATCH 0997/1028] [CI] Fix podspec linting --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4386205141..b5c64884a2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,10 +39,11 @@ jobs: XCODE_DESTINATION="platform=watchOS Simulator,name=Apple Watch Series 3 - 38mm" script/build - stage: package manager tests + install: gem update cocoapods script: - pod repo update - pod lib lint ReactiveCocoa.podspec --use-libraries - - pod lib lint ReactiveMapKit.podspec --use-libraries + - pod lib lint ReactiveMapKit.podspec --use-libraries --include-podspecs=ReactiveCocoa.podspec env: - JOB=PODSPEC - script: carthage build --cache-builds --no-skip-current From 419b9e927c323275029cfb848c3af27a3ae5bc57 Mon Sep 17 00:00:00 2001 From: fabio cerdeiral <38343+fabio-cerdeiral-ck@users.noreply.github.com> Date: Wed, 20 Nov 2019 12:38:38 -0800 Subject: [PATCH 0998/1028] Preparation for Swift Package Manager (#3692) * Changes in prepation for Swift Package Manager * Move modulemap and header to the right folder * Update CHANGELOG.md * Update public header files config on podspec to reflect path changes in ObjC header --- CHANGELOG.md | 2 + ReactiveCocoa.podspec | 4 +- ReactiveCocoa.xcodeproj/project.pbxproj | 92 +++++++++++-------- ReactiveCocoa/AppKit/ActionProxy.swift | 2 + ...s.swift => AppKitReusableComponents.swift} | 2 + ReactiveCocoa/AppKit/NSButton.swift | 4 +- ReactiveCocoa/AppKit/NSCollectionView.swift | 2 + ReactiveCocoa/AppKit/NSControl.swift | 6 +- ReactiveCocoa/AppKit/NSImageView.swift | 4 +- ReactiveCocoa/AppKit/NSPopUpButton.swift | 4 +- ReactiveCocoa/AppKit/NSSegmentedControl.swift | 4 +- ReactiveCocoa/AppKit/NSSlider.swift | 4 +- ReactiveCocoa/AppKit/NSTableView.swift | 2 + ReactiveCocoa/AppKit/NSTextField.swift | 4 +- ReactiveCocoa/AppKit/NSTextView.swift | 4 +- ReactiveCocoa/AppKit/NSView.swift | 4 +- ReactiveCocoa/DelegateProxy.swift | 1 + ReactiveCocoa/Deprecations+Removals.swift | 1 + ReactiveCocoa/NSObject+Association.swift | 1 + ReactiveCocoa/NSObject+ObjCRuntime.swift | 2 + ReactiveCocoa/ObjC+Constants.swift | 2 + ReactiveCocoa/ObjC+Messages.swift | 2 + ReactiveCocoa/ObjC+Runtime.swift | 2 + ReactiveCocoa/ObjC+RuntimeSubclassing.swift | 1 + ReactiveCocoa/ObjC+Selector.swift | 2 + ReactiveCocoa/Shared/NSLayoutConstraint.swift | 4 +- ReactiveCocoa/Synchronizing.swift | 2 + .../UIKit/UIActivityIndicatorView.swift | 2 + ReactiveCocoa/UIKit/UIApplication.swift | 2 + ReactiveCocoa/UIKit/UIBarButtonItem.swift | 2 + ReactiveCocoa/UIKit/UIBarItem.swift | 2 + ReactiveCocoa/UIKit/UIButton.swift | 2 + ReactiveCocoa/UIKit/UICollectionView.swift | 2 + ReactiveCocoa/UIKit/UIControl.swift | 2 + ReactiveCocoa/UIKit/UIGestureRecognizer.swift | 2 + ReactiveCocoa/UIKit/UIImageView.swift | 2 + ...ts.swift => UIKitReusableComponents.swift} | 2 + ReactiveCocoa/UIKit/UILabel.swift | 2 + ReactiveCocoa/UIKit/UINavigationBar.swift | 2 + ReactiveCocoa/UIKit/UINavigationItem.swift | 2 + ReactiveCocoa/UIKit/UIProgressView.swift | 2 + ReactiveCocoa/UIKit/UIResponder.swift | 2 + ReactiveCocoa/UIKit/UIScrollView.swift | 2 + ReactiveCocoa/UIKit/UISegmentedControl.swift | 2 + ReactiveCocoa/UIKit/UITabBarItem.swift | 2 + ReactiveCocoa/UIKit/UITableView.swift | 2 + ReactiveCocoa/UIKit/UITextField.swift | 2 + ReactiveCocoa/UIKit/UITextView.swift | 2 + ReactiveCocoa/UIKit/UIView.swift | 2 + ReactiveCocoa/UIKit/UIViewController.swift | 2 + ReactiveCocoa/UIKit/iOS/UIDatePicker.swift | 2 + .../UIKit/iOS/UIFeedbackGenerator.swift | 2 + ...00\213Feedback\342\200\213Generator.swift" | 2 + ReactiveCocoa/UIKit/iOS/UIKeyboard.swift | 2 + ...00\213Feedback\342\200\213Generator.swift" | 2 + ReactiveCocoa/UIKit/iOS/UIPickerView.swift | 3 + .../UIKit/iOS/UIRefreshControl.swift | 2 + ReactiveCocoa/UIKit/iOS/UISearchBar.swift | 2 + ...00\213Feedback\342\200\213Generator.swift" | 2 + ReactiveCocoa/UIKit/iOS/UISlider.swift | 2 + ReactiveCocoa/UIKit/iOS/UIStepper.swift | 2 + ReactiveCocoa/UIKit/iOS/UISwitch.swift | 2 + .../WatchKit/WKInterfaceActivityRing.swift | 2 + .../WatchKit/WKInterfaceButton.swift | 2 + .../WatchKit/WKInterfaceController.swift | 2 + ReactiveCocoa/WatchKit/WKInterfaceDate.swift | 2 + ReactiveCocoa/WatchKit/WKInterfaceGroup.swift | 2 + ReactiveCocoa/WatchKit/WKInterfaceImage.swift | 2 + .../WatchKit/WKInterfaceInlineMovie.swift | 2 + ReactiveCocoa/WatchKit/WKInterfaceLabel.swift | 2 + ReactiveCocoa/WatchKit/WKInterfaceMovie.swift | 2 + .../WatchKit/WKInterfaceObject.swift | 2 + .../WatchKit/WKInterfacePicker.swift | 2 + .../WatchKit/WKInterfaceSeparator.swift | 2 + .../WatchKit/WKInterfaceSlider.swift | 2 + .../WatchKit/WKInterfaceSwitch.swift | 2 + ReactiveCocoa/WatchKit/WKInterfaceTimer.swift | 2 + .../WatchKit/WKInterfaceVolumeControl.swift | 2 + .../ObjCRuntimeAliases.h | 0 .../ObjCRuntimeAliases.m | 0 .../MessageForwardingEntity.h | 0 .../MessageForwardingEntity.m | 0 .../AppKit/ActionProxySpec.swift | 3 + ...ift => AppKitReusableComponentsSpec.swift} | 2 + ReactiveCocoaTests/AppKit/NSButtonSpec.swift | 3 +- .../AppKit/NSCollectionViewSpec.swift | 2 + ReactiveCocoaTests/AppKit/NSControlSpec.swift | 2 + .../AppKit/NSImageViewSpec.swift | 3 +- .../AppKit/NSPopUpButtonSpec.swift | 2 + .../AppKit/NSTableViewSpec.swift | 2 + ReactiveCocoaTests/AppKit/NSViewSpec.swift | 2 + .../AppKit/Swift4TestInteroperability.swift | 2 + ReactiveCocoaTests/AssociationSpec.swift | 1 + ReactiveCocoaTests/BindingTargetSpec.swift | 1 + ReactiveCocoaTests/CocoaActionSpec.swift | 11 ++- ReactiveCocoaTests/DelegateProxySpec.swift | 1 + ReactiveCocoaTests/DynamicPropertySpec.swift | 1 + ReactiveCocoaTests/KVOKVCExtensionSpec.swift | 1 + .../ReactiveCocoaTestsConfiguration.swift | 4 +- .../Shared/NSLayoutConstraintSpec.swift | 9 ++ ReactiveCocoaTests/SwizzlingSpec.swift | 1 + .../UIKit/UIActivityIndicatorViewSpec.swift | 3 + .../UIKit/UIBarButtonItemSpec.swift | 2 + ReactiveCocoaTests/UIKit/UIButtonSpec.swift | 3 +- .../UIKit/UICollectionViewSpec.swift | 2 + ...ol+EnableSendActionsForControlEvents.swift | 2 + ReactiveCocoaTests/UIKit/UIControlSpec.swift | 2 + .../UIKit/UIDatePickerSpec.swift | 2 + .../UIKit/UIGestureRecognizerSpec.swift | 6 +- .../UIKit/UIImageViewSpec.swift | 2 + ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift | 4 +- ...wift => UIKitReusableComponentsSpec.swift} | 2 + ReactiveCocoaTests/UIKit/UILabelSpec.swift | 2 + .../UIKit/UINavigationItemSpec.swift | 2 + .../UIKit/UIPickerViewSpec.swift | 2 + .../UIKit/UIProgressViewSpec.swift | 2 + .../UIKit/UIRefreshControlSpec.swift | 7 +- .../UIKit/UIResponderSpec.swift | 7 +- .../UIKit/UIScrollViewSpec.swift | 2 + .../UIKit/UISearchBarSpec.swift | 2 + .../UIKit/UISegmentedControlSpec.swift | 7 +- ReactiveCocoaTests/UIKit/UISliderSpec.swift | 10 +- ReactiveCocoaTests/UIKit/UIStepperSpec.swift | 8 +- ReactiveCocoaTests/UIKit/UISwitchSpec.swift | 7 +- .../UIKit/UITabBarItemSpec.swift | 2 + .../UIKit/UITableViewSpec.swift | 8 +- .../UIKit/UITextFieldSpec.swift | 2 + ReactiveCocoaTests/UIKit/UITextViewSpec.swift | 2 + .../UIKit/UIViewControllerSpec.swift | 2 + ReactiveCocoaTests/UIKit/UIViewSpec.swift | 2 + 130 files changed, 344 insertions(+), 82 deletions(-) rename ReactiveCocoa/AppKit/{ReusableComponents.swift => AppKitReusableComponents.swift} (93%) rename ReactiveCocoa/UIKit/{ReusableComponents.swift => UIKitReusableComponents.swift} (90%) rename {ReactiveCocoa => ReactiveCocoaObjC}/ObjCRuntimeAliases.h (100%) rename {ReactiveCocoa => ReactiveCocoaObjC}/ObjCRuntimeAliases.m (100%) rename {ReactiveCocoaTests => ReactiveCocoaObjCTestSupport}/MessageForwardingEntity.h (100%) rename {ReactiveCocoaTests => ReactiveCocoaObjCTestSupport}/MessageForwardingEntity.m (100%) rename ReactiveCocoaTests/AppKit/{ReusableComponentsSpec.swift => AppKitReusableComponentsSpec.swift} (97%) rename ReactiveCocoaTests/UIKit/{ReusableComponentsSpec.swift => UIKitReusableComponentsSpec.swift} (97%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f5f3aff85..0d17885af8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # master *Please put new entries at the top. +1. Add platform checks and update project's file organization in preparation of Swift Package Manager support [#3692](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/3692) + # 10.1.0 1. Update dependencies so ReactiveCocoa can be used with Xcode 11 (#3677, kudos to @olejnjak) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 7ceee1d54b..d4b275cd79 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -15,8 +15,8 @@ Pod::Spec.new do |s| s.watchos.deployment_target = "2.0" s.source = { :git => "https://github.com/ReactiveCocoa/ReactiveCocoa.git", :tag => "#{s.version}" } - s.source_files = "ReactiveCocoa/*.{swift,h,m}", "ReactiveCocoa/Shared/*.{swift}" - s.public_header_files = "ReactiveCocoa/ObjCRuntimeAliases.h" + s.source_files = "ReactiveCocoa/*.{swift,h,m}", "ReactiveCocoa/Shared/*.{swift}", "ReactiveCocoaObjC/*.{h,m}" + s.public_header_files = "ReactiveCocoaObjC/ObjCRuntimeAliases.h" s.osx.source_files = "ReactiveCocoa/AppKit/*.{swift}" s.ios.source_files = "ReactiveCocoa/UIKit/*.{swift}", "ReactiveCocoa/UIKit/iOS/*.{swift}" s.tvos.source_files = "ReactiveCocoa/UIKit/*.{swift}" diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 36bf839849..68e29080cc 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -156,12 +156,12 @@ 9A6AAA1A1DB808A80013AAEA /* ObjCRuntimeAliases.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A1D06751D9415FB00ACF44C /* ObjCRuntimeAliases.h */; settings = {ATTRIBUTES = (Private, ); }; }; 9A6AAA1B1DB808A90013AAEA /* ObjCRuntimeAliases.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A1D06751D9415FB00ACF44C /* ObjCRuntimeAliases.h */; settings = {ATTRIBUTES = (Private, ); }; }; 9A6AAA1C1DB808AA0013AAEA /* ObjCRuntimeAliases.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A1D06751D9415FB00ACF44C /* ObjCRuntimeAliases.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 9A6AAA231DB8F51C0013AAEA /* ReusableComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA221DB8F51C0013AAEA /* ReusableComponents.swift */; }; - 9A6AAA241DB8F51C0013AAEA /* ReusableComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA221DB8F51C0013AAEA /* ReusableComponents.swift */; }; - 9A6AAA261DB8F5280013AAEA /* ReusableComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA251DB8F5280013AAEA /* ReusableComponents.swift */; }; - 9A6AAA2B1DB8F85C0013AAEA /* ReusableComponentsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA271DB8F7EB0013AAEA /* ReusableComponentsSpec.swift */; }; - 9A6AAA2E1DB903A20013AAEA /* ReusableComponentsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA291DB8F7F10013AAEA /* ReusableComponentsSpec.swift */; }; - 9A6AAA2F1DB903A40013AAEA /* ReusableComponentsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA291DB8F7F10013AAEA /* ReusableComponentsSpec.swift */; }; + 9A6AAA231DB8F51C0013AAEA /* UIKitReusableComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA221DB8F51C0013AAEA /* UIKitReusableComponents.swift */; }; + 9A6AAA241DB8F51C0013AAEA /* UIKitReusableComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA221DB8F51C0013AAEA /* UIKitReusableComponents.swift */; }; + 9A6AAA261DB8F5280013AAEA /* AppKitReusableComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA251DB8F5280013AAEA /* AppKitReusableComponents.swift */; }; + 9A6AAA2B1DB8F85C0013AAEA /* AppKitReusableComponentsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA271DB8F7EB0013AAEA /* AppKitReusableComponentsSpec.swift */; }; + 9A6AAA2E1DB903A20013AAEA /* UIKitReusableComponentsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA291DB8F7F10013AAEA /* UIKitReusableComponentsSpec.swift */; }; + 9A6AAA2F1DB903A40013AAEA /* UIKitReusableComponentsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA291DB8F7F10013AAEA /* UIKitReusableComponentsSpec.swift */; }; 9A73DFAE216D3C550069AD76 /* MKMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED11DD4BCA90016C058 /* MKMapView.swift */; }; 9A73DFAF216D3C550069AD76 /* MKLocalSearchRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91244E720389AEA0001BBCB /* MKLocalSearchRequest.swift */; }; 9A73DFBF216D3C570069AD76 /* MKMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED11DD4BCA90016C058 /* MKMapView.swift */; }; @@ -544,10 +544,10 @@ 9A54A2101DDF5B4D001739B3 /* InterceptingPerformanceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterceptingPerformanceTests.swift; sourceTree = ""; }; 9A54A21A1DE00D09001739B3 /* ObjC+Selector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObjC+Selector.swift"; sourceTree = ""; }; 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterceptingSpec.swift; sourceTree = ""; }; - 9A6AAA221DB8F51C0013AAEA /* ReusableComponents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponents.swift; sourceTree = ""; }; - 9A6AAA251DB8F5280013AAEA /* ReusableComponents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponents.swift; sourceTree = ""; }; - 9A6AAA271DB8F7EB0013AAEA /* ReusableComponentsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponentsSpec.swift; sourceTree = ""; }; - 9A6AAA291DB8F7F10013AAEA /* ReusableComponentsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableComponentsSpec.swift; sourceTree = ""; }; + 9A6AAA221DB8F51C0013AAEA /* UIKitReusableComponents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIKitReusableComponents.swift; sourceTree = ""; }; + 9A6AAA251DB8F5280013AAEA /* AppKitReusableComponents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppKitReusableComponents.swift; sourceTree = ""; }; + 9A6AAA271DB8F7EB0013AAEA /* AppKitReusableComponentsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppKitReusableComponentsSpec.swift; sourceTree = ""; }; + 9A6AAA291DB8F7F10013AAEA /* UIKitReusableComponentsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIKitReusableComponentsSpec.swift; sourceTree = ""; }; 9A73DFBB216D3C550069AD76 /* ReactiveMapKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveMapKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9A73DFCC216D3C570069AD76 /* ReactiveMapKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveMapKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9A73DFF8216D3CEB0069AD76 /* ReactiveMapKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveMapKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -718,11 +718,11 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9A3C54F121726BD200E98207 /* Nimble.framework in Frameworks */, - 9A3C54F321726BD200E98207 /* Quick.framework in Frameworks */, 9A73E055216D40D10069AD76 /* ReactiveSwift.framework in Frameworks */, 9A73E053216D40C80069AD76 /* ReactiveCocoa.framework in Frameworks */, 9A73E054216D40CD0069AD76 /* ReactiveMapKit.framework in Frameworks */, + 9A3C54F121726BD200E98207 /* Nimble.framework in Frameworks */, + 9A3C54F321726BD200E98207 /* Quick.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -796,6 +796,24 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 056D088022C00A6A00291F50 /* ReactiveCocoaObjC */ = { + isa = PBXGroup; + children = ( + 9A1D06751D9415FB00ACF44C /* ObjCRuntimeAliases.h */, + 9AA0BD971DDE7A2200531FCF /* ObjCRuntimeAliases.m */, + ); + path = ReactiveCocoaObjC; + sourceTree = ""; + }; + 056D088222C00B2700291F50 /* ReactiveCocoaObjCTestSupport */ = { + isa = PBXGroup; + children = ( + 9AD841DA204C29B90040F0C0 /* MessageForwardingEntity.h */, + 9AD841DB204C29B90040F0C0 /* MessageForwardingEntity.m */, + ); + path = ReactiveCocoaObjCTestSupport; + sourceTree = ""; + }; 538DCB761DCA5E1600332880 /* Shared */ = { isa = PBXGroup; children = ( @@ -847,7 +865,7 @@ 9ADE4A951DA6F018005C2AC8 /* NSTextField.swift */, 7A8BA0F91FCC86FC003241C7 /* NSTextView.swift */, 537EC08021A950CA00D6EE18 /* NSView.swift */, - 9A6AAA251DB8F5280013AAEA /* ReusableComponents.swift */, + 9A6AAA251DB8F5280013AAEA /* AppKitReusableComponents.swift */, ); path = AppKit; sourceTree = ""; @@ -856,7 +874,7 @@ isa = PBXGroup; children = ( CD91E3D41DDAC67700FA70D0 /* iOS */, - 9A6AAA221DB8F51C0013AAEA /* ReusableComponents.swift */, + 9A6AAA221DB8F51C0013AAEA /* UIKitReusableComponents.swift */, 9A1D05EC1D93E9F100ACF44C /* UIActivityIndicatorView.swift */, 4EE637352090F92600ECD02A /* UIApplication.swift */, 9A1D05ED1D93E9F100ACF44C /* UIBarButtonItem.swift */, @@ -885,7 +903,7 @@ 9A1D06231D93EA7E00ACF44C /* UIKit */ = { isa = PBXGroup; children = ( - 9A6AAA291DB8F7F10013AAEA /* ReusableComponentsSpec.swift */, + 9A6AAA291DB8F7F10013AAEA /* UIKitReusableComponentsSpec.swift */, 9A1D06241D93EA7E00ACF44C /* UIActivityIndicatorViewSpec.swift */, 9A1D06251D93EA7E00ACF44C /* UIBarButtonItemSpec.swift */, 9A1D06261D93EA7E00ACF44C /* UIButtonSpec.swift */, @@ -953,7 +971,7 @@ D9558AB51DFF7B90003254E1 /* NSPopUpButtonSpec.swift */, 4ABEFE2C1DCFD0180066A8C2 /* NSTableViewSpec.swift */, 537EC08221A9557400D6EE18 /* NSViewSpec.swift */, - 9A6AAA271DB8F7EB0013AAEA /* ReusableComponentsSpec.swift */, + 9A6AAA271DB8F7EB0013AAEA /* AppKitReusableComponentsSpec.swift */, 9AADB6F31F84AECB00EFFD19 /* Swift4TestInteroperability.swift */, ); path = AppKit; @@ -993,7 +1011,9 @@ isa = PBXGroup; children = ( D04725EC19E49ED7006002AA /* ReactiveCocoa */, + 056D088022C00A6A00291F50 /* ReactiveCocoaObjC */, D04725F919E49ED7006002AA /* ReactiveCocoaTests */, + 056D088222C00B2700291F50 /* ReactiveCocoaObjCTestSupport */, D047262519E49FE8006002AA /* Configuration */, 9AC03A581F7CC3BF00EC33C1 /* ReactiveMapKit */, 9A16753E1F80C35100B63650 /* ReactiveMapKitTests */, @@ -1026,30 +1046,30 @@ D04725EC19E49ED7006002AA /* ReactiveCocoa */ = { isa = PBXGroup; children = ( + 4A0E10FE1D2A92720065D310 /* AnyObject+Lifetime.swift */, 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */, 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */, - CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */, 9A7488471E3B8ACE00CD0317 /* DelegateProxy.swift */, - 4A0E10FE1D2A92720065D310 /* AnyObject+Lifetime.swift */, - 9ADE4A901DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift */, - 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */, - 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */, - 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */, + 9AB15C791E26CD9A00997378 /* Deprecations+Removals.swift */, + CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */, 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */, + 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */, + 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */, + 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */, 9AA0BD8E1DDE29F800531FCF /* NSObject+ObjCRuntime.swift */, - 9AE7C2A31DDD7F5100F7534C /* ObjC+Messages.swift */, + 9ADE4A901DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift */, 9AA0BD891DDE153A00531FCF /* ObjC+Constants.swift */, - 9A54A21A1DE00D09001739B3 /* ObjC+Selector.swift */, - 9AA0BD801DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift */, + 9AE7C2A31DDD7F5100F7534C /* ObjC+Messages.swift */, 9AA0BD771DDE03DE00531FCF /* ObjC+Runtime.swift */, + 9AA0BD801DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift */, + 9A54A21A1DE00D09001739B3 /* ObjC+Selector.swift */, 9A90374E1ED61C6300345D62 /* ReactiveSwift+Lifetime.swift */, 9ADFE5A41DC0001C001E11F7 /* Synchronizing.swift */, 9A1D05E91D93E9F100ACF44C /* AppKit */, - 9A1D05EB1D93E9F100ACF44C /* UIKit */, - E3AA54BF214291590077B206 /* WatchKit */, 538DCB761DCA5E1600332880 /* Shared */, D04725ED19E49ED7006002AA /* Supporting Files */, - 9AB15C791E26CD9A00997378 /* Deprecations+Removals.swift */, + 9A1D05EB1D93E9F100ACF44C /* UIKit */, + E3AA54BF214291590077B206 /* WatchKit */, ); path = ReactiveCocoa; sourceTree = ""; @@ -1058,8 +1078,6 @@ isa = PBXGroup; children = ( D04725EF19E49ED7006002AA /* ReactiveCocoa.h */, - 9A1D06751D9415FB00ACF44C /* ObjCRuntimeAliases.h */, - 9AA0BD971DDE7A2200531FCF /* ObjCRuntimeAliases.m */, 9AE7C2A21DDD768500F7534C /* module.modulemap */, D04725EE19E49ED7006002AA /* Info.plist */, ); @@ -1088,8 +1106,6 @@ 3B30EE8B1E7BE529007CC8EF /* DeprecationsSpec.swift */, CD42C69A1E951F6900AA9504 /* ReactiveCocoaTestsConfiguration.swift */, D04725FA19E49ED7006002AA /* Supporting Files */, - 9AD841DA204C29B90040F0C0 /* MessageForwardingEntity.h */, - 9AD841DB204C29B90040F0C0 /* MessageForwardingEntity.m */, 9AD841D6204C29B90040F0C0 /* ReactiveCocoaTests-Bridging-Header.h */, ); path = ReactiveCocoaTests; @@ -1702,7 +1718,7 @@ 9AE7C2A71DDD7F5100F7534C /* ObjC+Messages.swift in Sources */, 4EE637372090F92600ECD02A /* UIApplication.swift in Sources */, 9A1D06201D93EA0100ACF44C /* UITextView.swift in Sources */, - 9A6AAA241DB8F51C0013AAEA /* ReusableComponents.swift in Sources */, + 9A6AAA241DB8F51C0013AAEA /* UIKitReusableComponents.swift in Sources */, 4ABEFE201DCFCEF80066A8C2 /* UITableView.swift in Sources */, 9A1D06181D93EA0100ACF44C /* UIImageView.swift in Sources */, 9AA0BD841DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift in Sources */, @@ -1760,7 +1776,7 @@ 9A1D063F1D93EA7E00ACF44C /* UIControl+EnableSendActionsForControlEvents.swift in Sources */, 9A1D06371D93EA7E00ACF44C /* UIActivityIndicatorViewSpec.swift in Sources */, 4EE637332090EFDD00ECD02A /* UIViewControllerSpec.swift in Sources */, - 9A6AAA2F1DB903A40013AAEA /* ReusableComponentsSpec.swift in Sources */, + 9A6AAA2F1DB903A40013AAEA /* UIKitReusableComponentsSpec.swift in Sources */, 538DCB7F1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift in Sources */, 9A892D911E8D19BE00EA35F3 /* DelegateProxySpec.swift in Sources */, 9A1D06471D93EA7E00ACF44C /* UILabelSpec.swift in Sources */, @@ -1894,7 +1910,7 @@ 9A54A21B1DE00D09001739B3 /* ObjC+Selector.swift in Sources */, 9AA0BD8A1DDE153A00531FCF /* ObjC+Constants.swift in Sources */, 9AED64C51E496A3700321004 /* ActionProxy.swift in Sources */, - 9A6AAA261DB8F5280013AAEA /* ReusableComponents.swift in Sources */, + 9A6AAA261DB8F5280013AAEA /* AppKitReusableComponents.swift in Sources */, 9A90374F1ED61C6300345D62 /* ReactiveSwift+Lifetime.swift in Sources */, 9AA0BD7C1DDE03DE00531FCF /* ObjC+Runtime.swift in Sources */, 9ADFE5A51DC0001C001E11F7 /* Synchronizing.swift in Sources */, @@ -1940,7 +1956,7 @@ D9558AB91DFF86C0003254E1 /* NSPopUpButtonSpec.swift in Sources */, BFA6B94D1A7604D400C846D1 /* SignalProducerNimbleMatchers.swift in Sources */, 9A24A8451DE142A400987AF9 /* SwizzlingSpec.swift in Sources */, - 9A6AAA2B1DB8F85C0013AAEA /* ReusableComponentsSpec.swift in Sources */, + 9A6AAA2B1DB8F85C0013AAEA /* AppKitReusableComponentsSpec.swift in Sources */, CD8401831CEE8ED7009F0ABF /* CocoaActionSpec.swift in Sources */, 9A9DFEE91DA7EFB60039EE1B /* AssociationSpec.swift in Sources */, 9AADB6F41F84AECB00EFFD19 /* Swift4TestInteroperability.swift in Sources */, @@ -1965,7 +1981,7 @@ 9A9037501ED61C6300345D62 /* ReactiveSwift+Lifetime.swift in Sources */, 9AB15C7B1E26CD9A00997378 /* Deprecations+Removals.swift in Sources */, A9EB3D831E955602002A9BCC /* UINotification​Feedback​Generator.swift in Sources */, - 9A6AAA231DB8F51C0013AAEA /* ReusableComponents.swift in Sources */, + 9A6AAA231DB8F51C0013AAEA /* UIKitReusableComponents.swift in Sources */, 9A7488491E3B8ACE00CD0317 /* DelegateProxy.swift in Sources */, 9A1D060E1D93EA0000ACF44C /* UITextView.swift in Sources */, BFCF775F1DFAD8A50058006E /* UISearchBar.swift in Sources */, @@ -2055,7 +2071,7 @@ 9A1D06381D93EA7E00ACF44C /* UIBarButtonItemSpec.swift in Sources */, 9AD841DD204C29B90040F0C0 /* MessageForwardingEntity.m in Sources */, 9A1E72BB1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, - 9A6AAA2E1DB903A20013AAEA /* ReusableComponentsSpec.swift in Sources */, + 9A6AAA2E1DB903A20013AAEA /* UIKitReusableComponentsSpec.swift in Sources */, 4ABEFE211DCFCF090066A8C2 /* UITableViewSpec.swift in Sources */, 9A1D06401D93EA7E00ACF44C /* UIControlSpec.swift in Sources */, 9A7990CF1F1085D8001493A3 /* BindingTargetSpec.swift in Sources */, diff --git a/ReactiveCocoa/AppKit/ActionProxy.swift b/ReactiveCocoa/AppKit/ActionProxy.swift index d3e34733c7..343adf09ab 100644 --- a/ReactiveCocoa/AppKit/ActionProxy.swift +++ b/ReactiveCocoa/AppKit/ActionProxy.swift @@ -1,3 +1,4 @@ +#if canImport(AppKit) import AppKit import ReactiveSwift @@ -91,3 +92,4 @@ extension Reactive where Base: NSObject, Base: ActionMessageSending { } } } +#endif diff --git a/ReactiveCocoa/AppKit/ReusableComponents.swift b/ReactiveCocoa/AppKit/AppKitReusableComponents.swift similarity index 93% rename from ReactiveCocoa/AppKit/ReusableComponents.swift rename to ReactiveCocoa/AppKit/AppKitReusableComponents.swift index 9ba77b7a36..6e040a56d1 100644 --- a/ReactiveCocoa/AppKit/ReusableComponents.swift +++ b/ReactiveCocoa/AppKit/AppKitReusableComponents.swift @@ -1,3 +1,4 @@ +#if canImport(AppKit) import AppKit import ReactiveSwift @@ -13,3 +14,4 @@ extension Reactive where Base: NSObject, Base: NSCollectionViewElement { return trigger(for: #selector(base.prepareForReuse)) } } +#endif diff --git a/ReactiveCocoa/AppKit/NSButton.swift b/ReactiveCocoa/AppKit/NSButton.swift index 613fb18013..1351446ae7 100644 --- a/ReactiveCocoa/AppKit/NSButton.swift +++ b/ReactiveCocoa/AppKit/NSButton.swift @@ -1,5 +1,6 @@ -import ReactiveSwift +#if canImport(AppKit) import AppKit +import ReactiveSwift extension Reactive where Base: NSButton { @@ -53,3 +54,4 @@ extension Reactive where Base: NSButton { return makeBindingTarget { $0.image = $1 } } } +#endif diff --git a/ReactiveCocoa/AppKit/NSCollectionView.swift b/ReactiveCocoa/AppKit/NSCollectionView.swift index 74741200e3..d8e07cf4ec 100644 --- a/ReactiveCocoa/AppKit/NSCollectionView.swift +++ b/ReactiveCocoa/AppKit/NSCollectionView.swift @@ -1,3 +1,4 @@ +#if canImport(AppKit) import AppKit import ReactiveSwift @@ -7,3 +8,4 @@ extension Reactive where Base: NSCollectionView { return makeBindingTarget { base, _ in base.reloadData() } } } +#endif diff --git a/ReactiveCocoa/AppKit/NSControl.swift b/ReactiveCocoa/AppKit/NSControl.swift index bbd8a8da1c..f6517e2fbb 100644 --- a/ReactiveCocoa/AppKit/NSControl.swift +++ b/ReactiveCocoa/AppKit/NSControl.swift @@ -1,5 +1,6 @@ -import ReactiveSwift +#if canImport(AppKit) import AppKit +import ReactiveSwift extension NSControl: ActionMessageSending {} @@ -97,5 +98,4 @@ extension Reactive where Base: NSControl { return proxy.invoked.map { $0.stringValue } } } - - +#endif diff --git a/ReactiveCocoa/AppKit/NSImageView.swift b/ReactiveCocoa/AppKit/NSImageView.swift index 078e82eef3..773f5db449 100644 --- a/ReactiveCocoa/AppKit/NSImageView.swift +++ b/ReactiveCocoa/AppKit/NSImageView.swift @@ -1,5 +1,6 @@ -import ReactiveSwift +#if canImport(AppKit) import AppKit +import ReactiveSwift extension Reactive where Base: NSImageView { /// Sets the currently displayed image @@ -7,3 +8,4 @@ extension Reactive where Base: NSImageView { return makeBindingTarget { $0.image = $1 } } } +#endif diff --git a/ReactiveCocoa/AppKit/NSPopUpButton.swift b/ReactiveCocoa/AppKit/NSPopUpButton.swift index 4785f0060b..959d842591 100644 --- a/ReactiveCocoa/AppKit/NSPopUpButton.swift +++ b/ReactiveCocoa/AppKit/NSPopUpButton.swift @@ -1,5 +1,6 @@ -import ReactiveSwift +#if canImport(AppKit) import AppKit +import ReactiveSwift extension Reactive where Base: NSPopUpButton { @@ -45,3 +46,4 @@ extension Reactive where Base: NSPopUpButton { } } } +#endif diff --git a/ReactiveCocoa/AppKit/NSSegmentedControl.swift b/ReactiveCocoa/AppKit/NSSegmentedControl.swift index ace144ce6f..e83376baf9 100644 --- a/ReactiveCocoa/AppKit/NSSegmentedControl.swift +++ b/ReactiveCocoa/AppKit/NSSegmentedControl.swift @@ -1,5 +1,6 @@ -import ReactiveSwift +#if canImport(AppKit) import AppKit +import ReactiveSwift extension Reactive where Base: NSSegmentedControl { /// Changes the selected segment of the segmented control. @@ -19,3 +20,4 @@ extension Reactive where Base: NSSegmentedControl { /// A signal of indexes of selections emitted by the segmented control. public var selectedSegmentIndexes: Signal { return selectedSegments } } +#endif diff --git a/ReactiveCocoa/AppKit/NSSlider.swift b/ReactiveCocoa/AppKit/NSSlider.swift index 32831104fd..ae7ee79160 100644 --- a/ReactiveCocoa/AppKit/NSSlider.swift +++ b/ReactiveCocoa/AppKit/NSSlider.swift @@ -1,5 +1,6 @@ -import ReactiveSwift +#if canImport(AppKit) import AppKit +import ReactiveSwift extension Reactive where Base: NSSlider { @@ -8,3 +9,4 @@ extension Reactive where Base: NSSlider { public var value: BindingTarget { return floatValue } public var values: Signal { return floatValues } } +#endif diff --git a/ReactiveCocoa/AppKit/NSTableView.swift b/ReactiveCocoa/AppKit/NSTableView.swift index 6cdc867a70..857520b9c1 100644 --- a/ReactiveCocoa/AppKit/NSTableView.swift +++ b/ReactiveCocoa/AppKit/NSTableView.swift @@ -1,3 +1,4 @@ +#if canImport(AppKit) import AppKit import ReactiveSwift @@ -6,3 +7,4 @@ extension Reactive where Base: NSTableView { return makeBindingTarget { base, _ in base.reloadData() } } } +#endif diff --git a/ReactiveCocoa/AppKit/NSTextField.swift b/ReactiveCocoa/AppKit/NSTextField.swift index 9c57c705e8..3401f57ea6 100644 --- a/ReactiveCocoa/AppKit/NSTextField.swift +++ b/ReactiveCocoa/AppKit/NSTextField.swift @@ -1,5 +1,6 @@ -import ReactiveSwift +#if canImport(AppKit) import AppKit +import ReactiveSwift extension Reactive where Base: NSTextField { private var notifications: Signal { @@ -45,3 +46,4 @@ extension Reactive where Base: NSTextField { return makeBindingTarget { $0.textColor = $1 } } } +#endif diff --git a/ReactiveCocoa/AppKit/NSTextView.swift b/ReactiveCocoa/AppKit/NSTextView.swift index b4ddd3ec5e..29298aa379 100644 --- a/ReactiveCocoa/AppKit/NSTextView.swift +++ b/ReactiveCocoa/AppKit/NSTextView.swift @@ -1,5 +1,6 @@ -import ReactiveSwift +#if canImport(AppKit) import AppKit +import ReactiveSwift extension Reactive where Base: NSTextView { private var notifications: Signal { @@ -36,3 +37,4 @@ extension Reactive where Base: NSTextView { } } +#endif diff --git a/ReactiveCocoa/AppKit/NSView.swift b/ReactiveCocoa/AppKit/NSView.swift index cb1916c2af..baa3e4606a 100644 --- a/ReactiveCocoa/AppKit/NSView.swift +++ b/ReactiveCocoa/AppKit/NSView.swift @@ -1,5 +1,6 @@ -import ReactiveSwift +#if canImport(AppKit) import AppKit +import ReactiveSwift extension Reactive where Base: NSView { /// Sets the visibility of the view. @@ -12,3 +13,4 @@ extension Reactive where Base: NSView { return makeBindingTarget { $0.alphaValue = $1 } } } +#endif diff --git a/ReactiveCocoa/DelegateProxy.swift b/ReactiveCocoa/DelegateProxy.swift index 37273c2184..4d484f33e9 100644 --- a/ReactiveCocoa/DelegateProxy.swift +++ b/ReactiveCocoa/DelegateProxy.swift @@ -1,3 +1,4 @@ +import Foundation import ReactiveSwift internal class DelegateProxy: NSObject { diff --git a/ReactiveCocoa/Deprecations+Removals.swift b/ReactiveCocoa/Deprecations+Removals.swift index 65bf67a695..6932121696 100644 --- a/ReactiveCocoa/Deprecations+Removals.swift +++ b/ReactiveCocoa/Deprecations+Removals.swift @@ -1,3 +1,4 @@ +import Foundation import ReactiveSwift extension Action { diff --git a/ReactiveCocoa/NSObject+Association.swift b/ReactiveCocoa/NSObject+Association.swift index 06d35344c1..df70f8ede9 100644 --- a/ReactiveCocoa/NSObject+Association.swift +++ b/ReactiveCocoa/NSObject+Association.swift @@ -1,3 +1,4 @@ +import Foundation import ReactiveSwift internal struct AssociationKey { diff --git a/ReactiveCocoa/NSObject+ObjCRuntime.swift b/ReactiveCocoa/NSObject+ObjCRuntime.swift index bd9b98150e..e4223a777a 100644 --- a/ReactiveCocoa/NSObject+ObjCRuntime.swift +++ b/ReactiveCocoa/NSObject+ObjCRuntime.swift @@ -1,3 +1,5 @@ +import Foundation + extension NSObject { /// The class of the instance reported by the ObjC `-class:` message. /// diff --git a/ReactiveCocoa/ObjC+Constants.swift b/ReactiveCocoa/ObjC+Constants.swift index e11b89e7bf..274cc3eb5c 100644 --- a/ReactiveCocoa/ObjC+Constants.swift +++ b/ReactiveCocoa/ObjC+Constants.swift @@ -1,3 +1,5 @@ +import Foundation + // Unavailable selectors in Swift. internal enum ObjCSelector { static let forwardInvocation = Selector((("forwardInvocation:"))) diff --git a/ReactiveCocoa/ObjC+Messages.swift b/ReactiveCocoa/ObjC+Messages.swift index d494a45883..2668b73948 100644 --- a/ReactiveCocoa/ObjC+Messages.swift +++ b/ReactiveCocoa/ObjC+Messages.swift @@ -5,6 +5,8 @@ // These `@objc` protocols host the method signatures so that they can be used // with `AnyObject`. +import Foundation + internal let NSInvocation: AnyClass = NSClassFromString("NSInvocation")! internal let NSMethodSignature: AnyClass = NSClassFromString("NSMethodSignature")! diff --git a/ReactiveCocoa/ObjC+Runtime.swift b/ReactiveCocoa/ObjC+Runtime.swift index 420f8332f6..71eef6b795 100644 --- a/ReactiveCocoa/ObjC+Runtime.swift +++ b/ReactiveCocoa/ObjC+Runtime.swift @@ -1,3 +1,5 @@ +import Foundation + /// Search in `class` for any method that matches the supplied selector without /// propagating to the ancestors. /// diff --git a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift index ed6e836f17..793dad2deb 100644 --- a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift +++ b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift @@ -1,3 +1,4 @@ +import Foundation import ReactiveSwift /// Whether the runtime subclass has already been swizzled. diff --git a/ReactiveCocoa/ObjC+Selector.swift b/ReactiveCocoa/ObjC+Selector.swift index b34dce9e71..78c3ad0f66 100644 --- a/ReactiveCocoa/ObjC+Selector.swift +++ b/ReactiveCocoa/ObjC+Selector.swift @@ -1,3 +1,5 @@ +import Foundation + extension Selector { /// `self` as a pointer. It is uniqued across instances, similar to /// `StaticString`. diff --git a/ReactiveCocoa/Shared/NSLayoutConstraint.swift b/ReactiveCocoa/Shared/NSLayoutConstraint.swift index 5fa78fb1d3..d4d7f470e4 100644 --- a/ReactiveCocoa/Shared/NSLayoutConstraint.swift +++ b/ReactiveCocoa/Shared/NSLayoutConstraint.swift @@ -1,6 +1,7 @@ +#if !os(watchOS) import ReactiveSwift -#if os(macOS) +#if canImport(AppKit) import AppKit #else import UIKit @@ -14,3 +15,4 @@ extension Reactive where Base: NSLayoutConstraint { } } +#endif diff --git a/ReactiveCocoa/Synchronizing.swift b/ReactiveCocoa/Synchronizing.swift index fc1dc21292..5302308b67 100644 --- a/ReactiveCocoa/Synchronizing.swift +++ b/ReactiveCocoa/Synchronizing.swift @@ -1,3 +1,5 @@ +import Foundation + internal func synchronized(_ token: AnyObject, execute: () throws -> Result) rethrows -> Result { objc_sync_enter(token) defer { objc_sync_exit(token) } diff --git a/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift b/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift index 6855318683..fa3ad35949 100644 --- a/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift +++ b/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(watchOS) import ReactiveSwift import UIKit @@ -7,3 +8,4 @@ extension Reactive where Base: UIActivityIndicatorView { return makeBindingTarget { $1 ? $0.startAnimating() : $0.stopAnimating() } } } +#endif diff --git a/ReactiveCocoa/UIKit/UIApplication.swift b/ReactiveCocoa/UIKit/UIApplication.swift index 57175a4c60..9a2cb1bac4 100644 --- a/ReactiveCocoa/UIKit/UIApplication.swift +++ b/ReactiveCocoa/UIKit/UIApplication.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(watchOS) import ReactiveSwift import UIKit @@ -7,3 +8,4 @@ extension Reactive where Base: UIApplication { return makeBindingTarget({ $0.applicationIconBadgeNumber = $1 }) } } +#endif diff --git a/ReactiveCocoa/UIKit/UIBarButtonItem.swift b/ReactiveCocoa/UIKit/UIBarButtonItem.swift index 6628b1576a..879d927bc6 100644 --- a/ReactiveCocoa/UIKit/UIBarButtonItem.swift +++ b/ReactiveCocoa/UIKit/UIBarButtonItem.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(watchOS) import ReactiveSwift import UIKit @@ -47,3 +48,4 @@ extension Reactive where Base: UIBarButtonItem { return makeBindingTarget { $0.customView = $1 } } } +#endif diff --git a/ReactiveCocoa/UIKit/UIBarItem.swift b/ReactiveCocoa/UIKit/UIBarItem.swift index 54a3f93e51..bfae00054d 100644 --- a/ReactiveCocoa/UIKit/UIBarItem.swift +++ b/ReactiveCocoa/UIKit/UIBarItem.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(watchOS) import ReactiveSwift import UIKit @@ -17,3 +18,4 @@ extension Reactive where Base: UIBarItem { return makeBindingTarget { $0.title = $1 } } } +#endif diff --git a/ReactiveCocoa/UIKit/UIButton.swift b/ReactiveCocoa/UIKit/UIButton.swift index f8f49964b5..d3a23f6d56 100644 --- a/ReactiveCocoa/UIKit/UIButton.swift +++ b/ReactiveCocoa/UIKit/UIButton.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(watchOS) import ReactiveSwift import UIKit @@ -46,3 +47,4 @@ extension Reactive where Base: UIButton { return image(for: .normal) } } +#endif diff --git a/ReactiveCocoa/UIKit/UICollectionView.swift b/ReactiveCocoa/UIKit/UICollectionView.swift index 67ea780693..18261905b0 100644 --- a/ReactiveCocoa/UIKit/UICollectionView.swift +++ b/ReactiveCocoa/UIKit/UICollectionView.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(watchOS) import ReactiveSwift import UIKit @@ -6,3 +7,4 @@ extension Reactive where Base: UICollectionView { return makeBindingTarget { base, _ in base.reloadData() } } } +#endif diff --git a/ReactiveCocoa/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift index f002157978..f25b728db1 100644 --- a/ReactiveCocoa/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(watchOS) import ReactiveSwift import UIKit @@ -109,3 +110,4 @@ extension Reactive where Base: UIControl { return makeBindingTarget { $0.isHighlighted = $1 } } } +#endif diff --git a/ReactiveCocoa/UIKit/UIGestureRecognizer.swift b/ReactiveCocoa/UIKit/UIGestureRecognizer.swift index dbd59ff73d..4d02b645d7 100644 --- a/ReactiveCocoa/UIKit/UIGestureRecognizer.swift +++ b/ReactiveCocoa/UIKit/UIGestureRecognizer.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(watchOS) import ReactiveSwift import UIKit @@ -21,3 +22,4 @@ extension Reactive where Base: UIGestureRecognizer { } } } +#endif diff --git a/ReactiveCocoa/UIKit/UIImageView.swift b/ReactiveCocoa/UIKit/UIImageView.swift index 4beedf6a1b..ca37327e2b 100644 --- a/ReactiveCocoa/UIKit/UIImageView.swift +++ b/ReactiveCocoa/UIKit/UIImageView.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(watchOS) import ReactiveSwift import UIKit @@ -12,3 +13,4 @@ extension Reactive where Base: UIImageView { return makeBindingTarget { $0.highlightedImage = $1 } } } +#endif diff --git a/ReactiveCocoa/UIKit/ReusableComponents.swift b/ReactiveCocoa/UIKit/UIKitReusableComponents.swift similarity index 90% rename from ReactiveCocoa/UIKit/ReusableComponents.swift rename to ReactiveCocoa/UIKit/UIKitReusableComponents.swift index 4c893a777f..7cb939e5fc 100644 --- a/ReactiveCocoa/UIKit/ReusableComponents.swift +++ b/ReactiveCocoa/UIKit/UIKitReusableComponents.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(watchOS) import UIKit import ReactiveSwift @@ -14,3 +15,4 @@ extension Reactive where Base: NSObject, Base: Reusable { extension UITableViewCell: Reusable {} extension UITableViewHeaderFooterView: Reusable {} extension UICollectionReusableView: Reusable {} +#endif diff --git a/ReactiveCocoa/UIKit/UILabel.swift b/ReactiveCocoa/UIKit/UILabel.swift index 5e21d19340..4877eff964 100644 --- a/ReactiveCocoa/UIKit/UILabel.swift +++ b/ReactiveCocoa/UIKit/UILabel.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(watchOS) import ReactiveSwift import UIKit @@ -17,3 +18,4 @@ extension Reactive where Base: UILabel { return makeBindingTarget { $0.textColor = $1 } } } +#endif diff --git a/ReactiveCocoa/UIKit/UINavigationBar.swift b/ReactiveCocoa/UIKit/UINavigationBar.swift index e962bcdb7e..9888216721 100644 --- a/ReactiveCocoa/UIKit/UINavigationBar.swift +++ b/ReactiveCocoa/UIKit/UINavigationBar.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(watchOS) import ReactiveSwift import UIKit @@ -7,3 +8,4 @@ extension Reactive where Base: UINavigationBar { return makeBindingTarget { $0.barTintColor = $1 } } } +#endif diff --git a/ReactiveCocoa/UIKit/UINavigationItem.swift b/ReactiveCocoa/UIKit/UINavigationItem.swift index 78e295c645..5df555ebc3 100644 --- a/ReactiveCocoa/UIKit/UINavigationItem.swift +++ b/ReactiveCocoa/UIKit/UINavigationItem.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(watchOS) import ReactiveSwift import UIKit @@ -75,3 +76,4 @@ extension Reactive where Base: UINavigationItem { } #endif } +#endif diff --git a/ReactiveCocoa/UIKit/UIProgressView.swift b/ReactiveCocoa/UIKit/UIProgressView.swift index 2d3bad01ab..2d175c58e3 100644 --- a/ReactiveCocoa/UIKit/UIProgressView.swift +++ b/ReactiveCocoa/UIKit/UIProgressView.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(watchOS) import ReactiveSwift import UIKit @@ -7,3 +8,4 @@ extension Reactive where Base: UIProgressView { return makeBindingTarget { $0.progress = $1 } } } +#endif diff --git a/ReactiveCocoa/UIKit/UIResponder.swift b/ReactiveCocoa/UIKit/UIResponder.swift index f085559e98..2b00709673 100644 --- a/ReactiveCocoa/UIKit/UIResponder.swift +++ b/ReactiveCocoa/UIKit/UIResponder.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(watchOS) import ReactiveSwift import UIKit @@ -12,3 +13,4 @@ extension Reactive where Base: UIResponder { return makeBindingTarget { base, _ in base.resignFirstResponder() } } } +#endif diff --git a/ReactiveCocoa/UIKit/UIScrollView.swift b/ReactiveCocoa/UIKit/UIScrollView.swift index cb087fd227..66ed1486e9 100644 --- a/ReactiveCocoa/UIKit/UIScrollView.swift +++ b/ReactiveCocoa/UIKit/UIScrollView.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(watchOS) import ReactiveSwift import UIKit @@ -39,3 +40,4 @@ extension Reactive where Base: UIScrollView { } #endif } +#endif diff --git a/ReactiveCocoa/UIKit/UISegmentedControl.swift b/ReactiveCocoa/UIKit/UISegmentedControl.swift index 6c255a6750..25e9c7919b 100644 --- a/ReactiveCocoa/UIKit/UISegmentedControl.swift +++ b/ReactiveCocoa/UIKit/UISegmentedControl.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(watchOS) import ReactiveSwift import UIKit @@ -12,3 +13,4 @@ extension Reactive where Base: UISegmentedControl { return mapControlEvents(.valueChanged) { $0.selectedSegmentIndex } } } +#endif diff --git a/ReactiveCocoa/UIKit/UITabBarItem.swift b/ReactiveCocoa/UIKit/UITabBarItem.swift index 415fa64f8c..9d34f58c7e 100644 --- a/ReactiveCocoa/UIKit/UITabBarItem.swift +++ b/ReactiveCocoa/UIKit/UITabBarItem.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(watchOS) import ReactiveSwift import UIKit @@ -15,3 +16,4 @@ extension Reactive where Base: UITabBarItem { return makeBindingTarget { $0.badgeColor = $1 } } } +#endif diff --git a/ReactiveCocoa/UIKit/UITableView.swift b/ReactiveCocoa/UIKit/UITableView.swift index 96f2bb952a..d6be837820 100644 --- a/ReactiveCocoa/UIKit/UITableView.swift +++ b/ReactiveCocoa/UIKit/UITableView.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(watchOS) import ReactiveSwift import UIKit @@ -6,3 +7,4 @@ extension Reactive where Base: UITableView { return makeBindingTarget { base, _ in base.reloadData() } } } +#endif diff --git a/ReactiveCocoa/UIKit/UITextField.swift b/ReactiveCocoa/UIKit/UITextField.swift index 4a04b20a01..0116cd6515 100644 --- a/ReactiveCocoa/UIKit/UITextField.swift +++ b/ReactiveCocoa/UIKit/UITextField.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(watchOS) import ReactiveSwift import UIKit @@ -57,3 +58,4 @@ extension Reactive where Base: UITextField { return makeBindingTarget { $0.isSecureTextEntry = $1 } } } +#endif diff --git a/ReactiveCocoa/UIKit/UITextView.swift b/ReactiveCocoa/UIKit/UITextView.swift index 6f7adf3d2a..b92dcc45e7 100644 --- a/ReactiveCocoa/UIKit/UITextView.swift +++ b/ReactiveCocoa/UIKit/UITextView.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(watchOS) import ReactiveSwift import UIKit @@ -76,3 +77,4 @@ extension Reactive where Base: UITextView { .map { [unowned base] in base.selectedRange } } } +#endif diff --git a/ReactiveCocoa/UIKit/UIView.swift b/ReactiveCocoa/UIKit/UIView.swift index 85f3046fd3..85628b866d 100644 --- a/ReactiveCocoa/UIKit/UIView.swift +++ b/ReactiveCocoa/UIKit/UIView.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(watchOS) import ReactiveSwift import UIKit @@ -27,3 +28,4 @@ extension Reactive where Base: UIView { return makeBindingTarget { $0.tintColor = $1 } } } +#endif diff --git a/ReactiveCocoa/UIKit/UIViewController.swift b/ReactiveCocoa/UIKit/UIViewController.swift index b9fd837a35..272e04f2d5 100644 --- a/ReactiveCocoa/UIKit/UIViewController.swift +++ b/ReactiveCocoa/UIKit/UIViewController.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(watchOS) import ReactiveSwift import UIKit @@ -37,3 +38,4 @@ extension Reactive where Base: UIViewController { return trigger(for: #selector(Base.viewDidLayoutSubviews)) } } +#endif diff --git a/ReactiveCocoa/UIKit/iOS/UIDatePicker.swift b/ReactiveCocoa/UIKit/iOS/UIDatePicker.swift index 6cdde0c264..13497588fc 100644 --- a/ReactiveCocoa/UIKit/iOS/UIDatePicker.swift +++ b/ReactiveCocoa/UIKit/iOS/UIDatePicker.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(tvOS) && !os(watchOS) import ReactiveSwift import UIKit @@ -12,3 +13,4 @@ extension Reactive where Base: UIDatePicker { return mapControlEvents(.valueChanged) { $0.date } } } +#endif diff --git a/ReactiveCocoa/UIKit/iOS/UIFeedbackGenerator.swift b/ReactiveCocoa/UIKit/iOS/UIFeedbackGenerator.swift index e64deda5da..e18e40c3a8 100644 --- a/ReactiveCocoa/UIKit/iOS/UIFeedbackGenerator.swift +++ b/ReactiveCocoa/UIKit/iOS/UIFeedbackGenerator.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(tvOS) && !os(watchOS) import ReactiveSwift import UIKit @@ -10,3 +11,4 @@ extension Reactive where Base: UIFeedbackGenerator { } } } +#endif diff --git "a/ReactiveCocoa/UIKit/iOS/UIImpact\342\200\213Feedback\342\200\213Generator.swift" "b/ReactiveCocoa/UIKit/iOS/UIImpact\342\200\213Feedback\342\200\213Generator.swift" index e3fe8959e4..b8ea887165 100644 --- "a/ReactiveCocoa/UIKit/iOS/UIImpact\342\200\213Feedback\342\200\213Generator.swift" +++ "b/ReactiveCocoa/UIKit/iOS/UIImpact\342\200\213Feedback\342\200\213Generator.swift" @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(tvOS) && !os(watchOS) import ReactiveSwift import UIKit @@ -10,3 +11,4 @@ extension Reactive where Base: UIImpactFeedbackGenerator { } } } +#endif diff --git a/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift b/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift index 573eb3a702..8148adb904 100644 --- a/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift +++ b/ReactiveCocoa/UIKit/iOS/UIKeyboard.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(tvOS) && !os(watchOS) import UIKit import ReactiveSwift @@ -107,3 +108,4 @@ extension Reactive where Base: NotificationCenter { return keyboard(.willChangeFrame) } } +#endif diff --git "a/ReactiveCocoa/UIKit/iOS/UINotification\342\200\213Feedback\342\200\213Generator.swift" "b/ReactiveCocoa/UIKit/iOS/UINotification\342\200\213Feedback\342\200\213Generator.swift" index 9b5cbd511c..aa6405529e 100644 --- "a/ReactiveCocoa/UIKit/iOS/UINotification\342\200\213Feedback\342\200\213Generator.swift" +++ "b/ReactiveCocoa/UIKit/iOS/UINotification\342\200\213Feedback\342\200\213Generator.swift" @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(tvOS) && !os(watchOS) import ReactiveSwift import UIKit @@ -8,3 +9,4 @@ extension Reactive where Base: UINotificationFeedbackGenerator { return makeBindingTarget { $0.notificationOccurred($1) } } } +#endif diff --git a/ReactiveCocoa/UIKit/iOS/UIPickerView.swift b/ReactiveCocoa/UIKit/iOS/UIPickerView.swift index d947fcb73b..b1431e0773 100644 --- a/ReactiveCocoa/UIKit/iOS/UIPickerView.swift +++ b/ReactiveCocoa/UIKit/iOS/UIPickerView.swift @@ -1,3 +1,5 @@ +#if canImport(UIKit) && !os(tvOS) && !os(watchOS) +import Foundation import ReactiveSwift import UIKit @@ -39,3 +41,4 @@ extension Reactive where Base: UIPickerView { .map { (row: $0[1] as! Int, component: $0[2] as! Int) } } } +#endif diff --git a/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift b/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift index d8b71aa7ca..8d180df8dc 100644 --- a/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift +++ b/ReactiveCocoa/UIKit/iOS/UIRefreshControl.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(tvOS) && !os(watchOS) import ReactiveSwift import UIKit @@ -29,3 +30,4 @@ extension Reactive where Base: UIRefreshControl { } } } +#endif diff --git a/ReactiveCocoa/UIKit/iOS/UISearchBar.swift b/ReactiveCocoa/UIKit/iOS/UISearchBar.swift index 6d6a4aa56e..81d7f37bfd 100644 --- a/ReactiveCocoa/UIKit/iOS/UISearchBar.swift +++ b/ReactiveCocoa/UIKit/iOS/UISearchBar.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(tvOS) && !os(watchOS) import ReactiveSwift import UIKit @@ -110,3 +111,4 @@ extension Reactive where Base: UISearchBar { return makeBindingTarget { $0.showsCancelButton = $1 } } } +#endif diff --git "a/ReactiveCocoa/UIKit/iOS/UISelection\342\200\213Feedback\342\200\213Generator.swift" "b/ReactiveCocoa/UIKit/iOS/UISelection\342\200\213Feedback\342\200\213Generator.swift" index 07ad9327a4..ffedaac423 100644 --- "a/ReactiveCocoa/UIKit/iOS/UISelection\342\200\213Feedback\342\200\213Generator.swift" +++ "b/ReactiveCocoa/UIKit/iOS/UISelection\342\200\213Feedback\342\200\213Generator.swift" @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(tvOS) && !os(watchOS) import ReactiveSwift import UIKit @@ -10,3 +11,4 @@ extension Reactive where Base: UISelectionFeedbackGenerator { } } } +#endif diff --git a/ReactiveCocoa/UIKit/iOS/UISlider.swift b/ReactiveCocoa/UIKit/iOS/UISlider.swift index ea030f3ae6..d6b6396cef 100644 --- a/ReactiveCocoa/UIKit/iOS/UISlider.swift +++ b/ReactiveCocoa/UIKit/iOS/UISlider.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(tvOS) && !os(watchOS) import UIKit import ReactiveSwift @@ -27,3 +28,4 @@ extension Reactive where Base: UISlider { return mapControlEvents(.valueChanged) { $0.value } } } +#endif diff --git a/ReactiveCocoa/UIKit/iOS/UIStepper.swift b/ReactiveCocoa/UIKit/iOS/UIStepper.swift index b92b577589..f89ebf121f 100644 --- a/ReactiveCocoa/UIKit/iOS/UIStepper.swift +++ b/ReactiveCocoa/UIKit/iOS/UIStepper.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(tvOS) && !os(watchOS) import UIKit import ReactiveSwift @@ -24,3 +25,4 @@ extension Reactive where Base: UIStepper { return mapControlEvents(.valueChanged) { $0.value } } } +#endif diff --git a/ReactiveCocoa/UIKit/iOS/UISwitch.swift b/ReactiveCocoa/UIKit/iOS/UISwitch.swift index e714ec2044..a2e9f679f5 100644 --- a/ReactiveCocoa/UIKit/iOS/UISwitch.swift +++ b/ReactiveCocoa/UIKit/iOS/UISwitch.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(tvOS) && !os(watchOS) import ReactiveSwift import UIKit @@ -27,3 +28,4 @@ extension Reactive where Base: UISwitch { return mapControlEvents(.valueChanged) { $0.isOn } } } +#endif diff --git a/ReactiveCocoa/WatchKit/WKInterfaceActivityRing.swift b/ReactiveCocoa/WatchKit/WKInterfaceActivityRing.swift index 41ca1607a2..aec47f71d1 100644 --- a/ReactiveCocoa/WatchKit/WKInterfaceActivityRing.swift +++ b/ReactiveCocoa/WatchKit/WKInterfaceActivityRing.swift @@ -1,3 +1,4 @@ +#if canImport(WatchKit) import ReactiveSwift import WatchKit import HealthKit @@ -11,3 +12,4 @@ extension Reactive where Base: WKInterfaceActivityRing { return makeBindingTarget { $0.setActivitySummary($1, animated: animated) } } } +#endif diff --git a/ReactiveCocoa/WatchKit/WKInterfaceButton.swift b/ReactiveCocoa/WatchKit/WKInterfaceButton.swift index b9a551faa6..582bc90fa0 100644 --- a/ReactiveCocoa/WatchKit/WKInterfaceButton.swift +++ b/ReactiveCocoa/WatchKit/WKInterfaceButton.swift @@ -1,3 +1,4 @@ +#if canImport(WatchKit) import ReactiveSwift import WatchKit @@ -37,3 +38,4 @@ extension Reactive where Base: WKInterfaceButton { return makeBindingTarget { $0.setEnabled($1) } } } +#endif diff --git a/ReactiveCocoa/WatchKit/WKInterfaceController.swift b/ReactiveCocoa/WatchKit/WKInterfaceController.swift index 68e88eb9a0..e8e0627768 100644 --- a/ReactiveCocoa/WatchKit/WKInterfaceController.swift +++ b/ReactiveCocoa/WatchKit/WKInterfaceController.swift @@ -1,3 +1,4 @@ +#if canImport(WatchKit) import ReactiveSwift import WatchKit @@ -7,3 +8,4 @@ extension Reactive where Base: WKInterfaceController { return makeBindingTarget { $0.setTitle($1) } } } +#endif diff --git a/ReactiveCocoa/WatchKit/WKInterfaceDate.swift b/ReactiveCocoa/WatchKit/WKInterfaceDate.swift index 169054a916..9b25a6a2b4 100644 --- a/ReactiveCocoa/WatchKit/WKInterfaceDate.swift +++ b/ReactiveCocoa/WatchKit/WKInterfaceDate.swift @@ -1,3 +1,4 @@ +#if canImport(WatchKit) import ReactiveSwift import WatchKit @@ -7,3 +8,4 @@ extension Reactive where Base: WKInterfaceDate { return makeBindingTarget { $0.setTextColor($1) } } } +#endif diff --git a/ReactiveCocoa/WatchKit/WKInterfaceGroup.swift b/ReactiveCocoa/WatchKit/WKInterfaceGroup.swift index 12953fe780..eaa78f1cb8 100644 --- a/ReactiveCocoa/WatchKit/WKInterfaceGroup.swift +++ b/ReactiveCocoa/WatchKit/WKInterfaceGroup.swift @@ -1,3 +1,4 @@ +#if canImport(WatchKit) import ReactiveSwift import WatchKit @@ -32,3 +33,4 @@ extension Reactive where Base: WKInterfaceGroup { return makeBindingTarget { $0.setContentInset($1) } } } +#endif diff --git a/ReactiveCocoa/WatchKit/WKInterfaceImage.swift b/ReactiveCocoa/WatchKit/WKInterfaceImage.swift index 15bc63328b..57367caf94 100644 --- a/ReactiveCocoa/WatchKit/WKInterfaceImage.swift +++ b/ReactiveCocoa/WatchKit/WKInterfaceImage.swift @@ -1,3 +1,4 @@ +#if canImport(WatchKit) import ReactiveSwift import WatchKit @@ -22,3 +23,4 @@ extension Reactive where Base: WKInterfaceImage { return makeBindingTarget { $0.setTintColor($1) } } } +#endif diff --git a/ReactiveCocoa/WatchKit/WKInterfaceInlineMovie.swift b/ReactiveCocoa/WatchKit/WKInterfaceInlineMovie.swift index 52acaf5ae8..ded534658d 100644 --- a/ReactiveCocoa/WatchKit/WKInterfaceInlineMovie.swift +++ b/ReactiveCocoa/WatchKit/WKInterfaceInlineMovie.swift @@ -1,3 +1,4 @@ +#if canImport(WatchKit) import ReactiveSwift import WatchKit @@ -28,3 +29,4 @@ extension Reactive where Base: WKInterfaceInlineMovie { return makeBindingTarget { $0.setAutoplays($1) } } } +#endif diff --git a/ReactiveCocoa/WatchKit/WKInterfaceLabel.swift b/ReactiveCocoa/WatchKit/WKInterfaceLabel.swift index 1c975f6a15..e2a259b847 100644 --- a/ReactiveCocoa/WatchKit/WKInterfaceLabel.swift +++ b/ReactiveCocoa/WatchKit/WKInterfaceLabel.swift @@ -1,3 +1,4 @@ +#if canImport(WatchKit) import ReactiveSwift import WatchKit @@ -17,3 +18,4 @@ extension Reactive where Base: WKInterfaceLabel { return makeBindingTarget { $0.setTextColor($1) } } } +#endif diff --git a/ReactiveCocoa/WatchKit/WKInterfaceMovie.swift b/ReactiveCocoa/WatchKit/WKInterfaceMovie.swift index 6cedd849d9..84325b56b8 100644 --- a/ReactiveCocoa/WatchKit/WKInterfaceMovie.swift +++ b/ReactiveCocoa/WatchKit/WKInterfaceMovie.swift @@ -1,3 +1,4 @@ +#if canImport(WatchKit) import ReactiveSwift import WatchKit @@ -22,3 +23,4 @@ extension Reactive where Base: WKInterfaceMovie { return makeBindingTarget { $0.setLoops($1) } } } +#endif diff --git a/ReactiveCocoa/WatchKit/WKInterfaceObject.swift b/ReactiveCocoa/WatchKit/WKInterfaceObject.swift index cc9e6f1548..8ff6d73a86 100644 --- a/ReactiveCocoa/WatchKit/WKInterfaceObject.swift +++ b/ReactiveCocoa/WatchKit/WKInterfaceObject.swift @@ -1,3 +1,4 @@ +#if canImport(WatchKit) import ReactiveSwift import WatchKit @@ -12,3 +13,4 @@ extension Reactive where Base: WKInterfaceObject { return makeBindingTarget { $0.setHidden($1) } } } +#endif diff --git a/ReactiveCocoa/WatchKit/WKInterfacePicker.swift b/ReactiveCocoa/WatchKit/WKInterfacePicker.swift index 88abf4a5ba..609a4ccd7d 100644 --- a/ReactiveCocoa/WatchKit/WKInterfacePicker.swift +++ b/ReactiveCocoa/WatchKit/WKInterfacePicker.swift @@ -1,3 +1,4 @@ +#if canImport(WatchKit) import ReactiveSwift import WatchKit @@ -17,3 +18,4 @@ extension Reactive where Base: WKInterfacePicker { return makeBindingTarget { $0.setEnabled($1) } } } +#endif diff --git a/ReactiveCocoa/WatchKit/WKInterfaceSeparator.swift b/ReactiveCocoa/WatchKit/WKInterfaceSeparator.swift index e8fb5015e9..675b136a35 100644 --- a/ReactiveCocoa/WatchKit/WKInterfaceSeparator.swift +++ b/ReactiveCocoa/WatchKit/WKInterfaceSeparator.swift @@ -1,3 +1,4 @@ +#if canImport(WatchKit) import ReactiveSwift import WatchKit @@ -7,3 +8,4 @@ extension Reactive where Base: WKInterfaceSeparator { return makeBindingTarget { $0.setColor($1) } } } +#endif diff --git a/ReactiveCocoa/WatchKit/WKInterfaceSlider.swift b/ReactiveCocoa/WatchKit/WKInterfaceSlider.swift index faac72f80b..39dbb1ea83 100644 --- a/ReactiveCocoa/WatchKit/WKInterfaceSlider.swift +++ b/ReactiveCocoa/WatchKit/WKInterfaceSlider.swift @@ -1,3 +1,4 @@ +#if canImport(WatchKit) import ReactiveSwift import WatchKit @@ -22,3 +23,4 @@ extension Reactive where Base: WKInterfaceSlider { return makeBindingTarget { $0.setEnabled($1) } } } +#endif diff --git a/ReactiveCocoa/WatchKit/WKInterfaceSwitch.swift b/ReactiveCocoa/WatchKit/WKInterfaceSwitch.swift index a12eaf987d..2e61d13340 100644 --- a/ReactiveCocoa/WatchKit/WKInterfaceSwitch.swift +++ b/ReactiveCocoa/WatchKit/WKInterfaceSwitch.swift @@ -1,3 +1,4 @@ +#if canImport(WatchKit) import ReactiveSwift import WatchKit @@ -27,3 +28,4 @@ extension Reactive where Base: WKInterfaceSwitch { return makeBindingTarget { $0.setEnabled($1) } } } +#endif diff --git a/ReactiveCocoa/WatchKit/WKInterfaceTimer.swift b/ReactiveCocoa/WatchKit/WKInterfaceTimer.swift index 452c07f8ef..21220da634 100644 --- a/ReactiveCocoa/WatchKit/WKInterfaceTimer.swift +++ b/ReactiveCocoa/WatchKit/WKInterfaceTimer.swift @@ -1,3 +1,4 @@ +#if canImport(WatchKit) import ReactiveSwift import WatchKit @@ -12,3 +13,4 @@ extension Reactive where Base: WKInterfaceTimer { return makeBindingTarget { $0.setTextColor($1) } } } +#endif diff --git a/ReactiveCocoa/WatchKit/WKInterfaceVolumeControl.swift b/ReactiveCocoa/WatchKit/WKInterfaceVolumeControl.swift index ea34ac9205..a85dcfb49c 100644 --- a/ReactiveCocoa/WatchKit/WKInterfaceVolumeControl.swift +++ b/ReactiveCocoa/WatchKit/WKInterfaceVolumeControl.swift @@ -1,3 +1,4 @@ +#if canImport(WatchKit) import ReactiveSwift import WatchKit @@ -8,3 +9,4 @@ extension Reactive where Base: WKInterfaceVolumeControl { return makeBindingTarget { $0.setTintColor($1) } } } +#endif diff --git a/ReactiveCocoa/ObjCRuntimeAliases.h b/ReactiveCocoaObjC/ObjCRuntimeAliases.h similarity index 100% rename from ReactiveCocoa/ObjCRuntimeAliases.h rename to ReactiveCocoaObjC/ObjCRuntimeAliases.h diff --git a/ReactiveCocoa/ObjCRuntimeAliases.m b/ReactiveCocoaObjC/ObjCRuntimeAliases.m similarity index 100% rename from ReactiveCocoa/ObjCRuntimeAliases.m rename to ReactiveCocoaObjC/ObjCRuntimeAliases.m diff --git a/ReactiveCocoaTests/MessageForwardingEntity.h b/ReactiveCocoaObjCTestSupport/MessageForwardingEntity.h similarity index 100% rename from ReactiveCocoaTests/MessageForwardingEntity.h rename to ReactiveCocoaObjCTestSupport/MessageForwardingEntity.h diff --git a/ReactiveCocoaTests/MessageForwardingEntity.m b/ReactiveCocoaObjCTestSupport/MessageForwardingEntity.m similarity index 100% rename from ReactiveCocoaTests/MessageForwardingEntity.m rename to ReactiveCocoaObjCTestSupport/MessageForwardingEntity.m diff --git a/ReactiveCocoaTests/AppKit/ActionProxySpec.swift b/ReactiveCocoaTests/AppKit/ActionProxySpec.swift index ff46edd4c4..82cc7f5682 100644 --- a/ReactiveCocoaTests/AppKit/ActionProxySpec.swift +++ b/ReactiveCocoaTests/AppKit/ActionProxySpec.swift @@ -1,3 +1,5 @@ +#if canImport(AppKit) +import Foundation import Quick import Nimble import ReactiveSwift @@ -298,3 +300,4 @@ class ActionProxySpec: QuickSpec { } } } +#endif diff --git a/ReactiveCocoaTests/AppKit/ReusableComponentsSpec.swift b/ReactiveCocoaTests/AppKit/AppKitReusableComponentsSpec.swift similarity index 97% rename from ReactiveCocoaTests/AppKit/ReusableComponentsSpec.swift rename to ReactiveCocoaTests/AppKit/AppKitReusableComponentsSpec.swift index 961a21da23..ca49c47d3b 100644 --- a/ReactiveCocoaTests/AppKit/ReusableComponentsSpec.swift +++ b/ReactiveCocoaTests/AppKit/AppKitReusableComponentsSpec.swift @@ -1,3 +1,4 @@ +#if canImport(AppKit) import Quick import Nimble import ReactiveSwift @@ -47,3 +48,4 @@ private class TestViewItem: NSCollectionViewItem { view = NSView() } } +#endif diff --git a/ReactiveCocoaTests/AppKit/NSButtonSpec.swift b/ReactiveCocoaTests/AppKit/NSButtonSpec.swift index c5b44f95af..154322f922 100644 --- a/ReactiveCocoaTests/AppKit/NSButtonSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSButtonSpec.swift @@ -1,3 +1,4 @@ +#if canImport(AppKit) import Quick import Nimble import ReactiveSwift @@ -148,4 +149,4 @@ class NSButtonSpec: QuickSpec { } } } - +#endif diff --git a/ReactiveCocoaTests/AppKit/NSCollectionViewSpec.swift b/ReactiveCocoaTests/AppKit/NSCollectionViewSpec.swift index e6bb162fec..56af12fcc9 100644 --- a/ReactiveCocoaTests/AppKit/NSCollectionViewSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSCollectionViewSpec.swift @@ -1,3 +1,4 @@ +#if canImport(AppKit) import Quick import Nimble import ReactiveCocoa @@ -62,3 +63,4 @@ private final class TestCollectionView: NSCollectionView { reloadDataObserver.send(value: ()) } } +#endif diff --git a/ReactiveCocoaTests/AppKit/NSControlSpec.swift b/ReactiveCocoaTests/AppKit/NSControlSpec.swift index 240b909cd9..4e89b6a679 100644 --- a/ReactiveCocoaTests/AppKit/NSControlSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSControlSpec.swift @@ -1,3 +1,4 @@ +#if canImport(AppKit) import Quick import Nimble import ReactiveSwift @@ -186,3 +187,4 @@ private final class TestTarget { counter += 1 } } +#endif diff --git a/ReactiveCocoaTests/AppKit/NSImageViewSpec.swift b/ReactiveCocoaTests/AppKit/NSImageViewSpec.swift index db6d9e55da..c3bd7d9fd7 100644 --- a/ReactiveCocoaTests/AppKit/NSImageViewSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSImageViewSpec.swift @@ -1,3 +1,4 @@ +#if canImport(AppKit) import Quick import Nimble import ReactiveSwift @@ -47,4 +48,4 @@ class NSImageViewSpec: QuickSpec { } } } - +#endif diff --git a/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift b/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift index 75eca540c4..03111d1122 100644 --- a/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift @@ -1,3 +1,4 @@ +#if canImport(AppKit) import Quick import Nimble import ReactiveCocoa @@ -129,3 +130,4 @@ final class NSPopUpButtonSpec: QuickSpec { } } } +#endif diff --git a/ReactiveCocoaTests/AppKit/NSTableViewSpec.swift b/ReactiveCocoaTests/AppKit/NSTableViewSpec.swift index 15c74069b1..f0a02fe74e 100644 --- a/ReactiveCocoaTests/AppKit/NSTableViewSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSTableViewSpec.swift @@ -1,3 +1,4 @@ +#if canImport(AppKit) import Quick import Nimble import ReactiveCocoa @@ -60,3 +61,4 @@ private final class TestTableView: NSTableView { reloadDataObserver.send(value: ()) } } +#endif diff --git a/ReactiveCocoaTests/AppKit/NSViewSpec.swift b/ReactiveCocoaTests/AppKit/NSViewSpec.swift index 3e448ec71e..baeddec7c5 100644 --- a/ReactiveCocoaTests/AppKit/NSViewSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSViewSpec.swift @@ -1,3 +1,4 @@ +#if canImport(AppKit) import Quick import Nimble import ReactiveSwift @@ -35,3 +36,4 @@ class NSViewSpec: QuickSpec { } } } +#endif diff --git a/ReactiveCocoaTests/AppKit/Swift4TestInteroperability.swift b/ReactiveCocoaTests/AppKit/Swift4TestInteroperability.swift index 4bc1a31b7e..cfce36d8a1 100644 --- a/ReactiveCocoaTests/AppKit/Swift4TestInteroperability.swift +++ b/ReactiveCocoaTests/AppKit/Swift4TestInteroperability.swift @@ -1,3 +1,4 @@ +#if canImport(AppKit) import AppKit #if swift(>=4.0) @@ -11,3 +12,4 @@ internal let RACNSOnState = NSOnState internal let RACNSOffState = NSOffState internal let RACNSMixedState = NSMixedState #endif +#endif diff --git a/ReactiveCocoaTests/AssociationSpec.swift b/ReactiveCocoaTests/AssociationSpec.swift index ca828720a2..773c66f829 100644 --- a/ReactiveCocoaTests/AssociationSpec.swift +++ b/ReactiveCocoaTests/AssociationSpec.swift @@ -1,3 +1,4 @@ +import Foundation import ReactiveSwift import Nimble import Quick diff --git a/ReactiveCocoaTests/BindingTargetSpec.swift b/ReactiveCocoaTests/BindingTargetSpec.swift index 15ee6cd828..9c6ad96b1b 100644 --- a/ReactiveCocoaTests/BindingTargetSpec.swift +++ b/ReactiveCocoaTests/BindingTargetSpec.swift @@ -1,3 +1,4 @@ +import Foundation import ReactiveSwift import ReactiveCocoa import Quick diff --git a/ReactiveCocoaTests/CocoaActionSpec.swift b/ReactiveCocoaTests/CocoaActionSpec.swift index 6fdd6e78d7..5983a3642b 100644 --- a/ReactiveCocoaTests/CocoaActionSpec.swift +++ b/ReactiveCocoaTests/CocoaActionSpec.swift @@ -1,3 +1,6 @@ +#if canImport(AppKit) +import AppKit +#endif import ReactiveSwift import Nimble import Quick @@ -6,7 +9,7 @@ import ReactiveCocoa class CocoaActionSpec: QuickSpec { override func spec() { var action: Action! - #if os(OSX) + #if canImport(AppKit) var cocoaAction: CocoaAction! #else var cocoaAction: CocoaAction! @@ -20,7 +23,7 @@ class CocoaActionSpec: QuickSpec { expect(cocoaAction.isEnabled.value).toEventually(beTruthy()) } - #if os(OSX) + #if canImport(AppKit) it("should be compatible with AppKit") { let control = NSControl(frame: NSZeroRect) control.target = cocoaAction @@ -44,7 +47,7 @@ class CocoaActionSpec: QuickSpec { expect(values) == [ true ] let result = action.apply(0).first() - expect(try? result?.get()) == 1 + expect { try result?.get() } == 1 expect(values).toEventually(equal([ true, false, true ])) _ = cocoaAction @@ -59,7 +62,7 @@ class CocoaActionSpec: QuickSpec { expect(values) == [ false ] let result = action.apply(0).first() - expect(try? result?.get()) == 1 + expect { try result?.get() } == 1 expect(values).toEventually(equal([ false, true, false ])) _ = cocoaAction diff --git a/ReactiveCocoaTests/DelegateProxySpec.swift b/ReactiveCocoaTests/DelegateProxySpec.swift index 174457d770..8c28c0e5fb 100644 --- a/ReactiveCocoaTests/DelegateProxySpec.swift +++ b/ReactiveCocoaTests/DelegateProxySpec.swift @@ -1,3 +1,4 @@ +import Foundation import Quick import Nimble import ReactiveSwift diff --git a/ReactiveCocoaTests/DynamicPropertySpec.swift b/ReactiveCocoaTests/DynamicPropertySpec.swift index 88c2733f14..a499a659c6 100644 --- a/ReactiveCocoaTests/DynamicPropertySpec.swift +++ b/ReactiveCocoaTests/DynamicPropertySpec.swift @@ -1,3 +1,4 @@ +import Foundation import ReactiveSwift import Nimble import Quick diff --git a/ReactiveCocoaTests/KVOKVCExtensionSpec.swift b/ReactiveCocoaTests/KVOKVCExtensionSpec.swift index 76eafb3772..1ba4cfeda4 100644 --- a/ReactiveCocoaTests/KVOKVCExtensionSpec.swift +++ b/ReactiveCocoaTests/KVOKVCExtensionSpec.swift @@ -1,3 +1,4 @@ +import Foundation import ReactiveSwift import Nimble import Quick diff --git a/ReactiveCocoaTests/ReactiveCocoaTestsConfiguration.swift b/ReactiveCocoaTests/ReactiveCocoaTestsConfiguration.swift index 5c46cc3cf3..6b32096f49 100644 --- a/ReactiveCocoaTests/ReactiveCocoaTestsConfiguration.swift +++ b/ReactiveCocoaTests/ReactiveCocoaTestsConfiguration.swift @@ -1,11 +1,11 @@ import Quick -#if os(iOS) || os(tvOS) +#if canImport(UIKit) import UIKit #endif class ReactiveCocoaTestsConfiguration: QuickConfiguration { override class func configure(_ configuration: Configuration) { - #if os(iOS) || os(tvOS) + #if canImport(UIKit) configuration.beforeSuite { UIControl._initialize() } diff --git a/ReactiveCocoaTests/Shared/NSLayoutConstraintSpec.swift b/ReactiveCocoaTests/Shared/NSLayoutConstraintSpec.swift index dcb55170d4..5966c90d57 100644 --- a/ReactiveCocoaTests/Shared/NSLayoutConstraintSpec.swift +++ b/ReactiveCocoaTests/Shared/NSLayoutConstraintSpec.swift @@ -1,3 +1,11 @@ +#if canImport(AppKit) || canImport(UIKit) + +#if canImport(AppKit) +import AppKit +#elseif canImport(UIKit) +import UIKit +#endif + import ReactiveSwift import ReactiveCocoa import Quick @@ -33,3 +41,4 @@ class NSLayoutConstraintSpec: QuickSpec { } } } +#endif diff --git a/ReactiveCocoaTests/SwizzlingSpec.swift b/ReactiveCocoaTests/SwizzlingSpec.swift index 9f47161f51..7de97f7464 100644 --- a/ReactiveCocoaTests/SwizzlingSpec.swift +++ b/ReactiveCocoaTests/SwizzlingSpec.swift @@ -1,4 +1,5 @@ @testable import ReactiveCocoa +import Foundation import Quick import Nimble diff --git a/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewSpec.swift b/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewSpec.swift index adeb139d24..33f1106c1d 100644 --- a/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewSpec.swift @@ -1,3 +1,5 @@ +#if canImport(UIKit) +import UIKit import Quick import Nimble import ReactiveSwift @@ -30,3 +32,4 @@ class UIActivityIndicatorSpec: QuickSpec { } } } +#endif diff --git a/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift b/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift index 403c73cd1f..ae47f85f22 100644 --- a/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIBarButtonItemSpec.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) import Quick import Nimble import ReactiveSwift @@ -120,3 +121,4 @@ class UIBarButtonItemSpec: QuickSpec { } } } +#endif diff --git a/ReactiveCocoaTests/UIKit/UIButtonSpec.swift b/ReactiveCocoaTests/UIKit/UIButtonSpec.swift index d2a373e6ef..b17093e030 100644 --- a/ReactiveCocoaTests/UIKit/UIButtonSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIButtonSpec.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) import Quick import Nimble import ReactiveSwift @@ -69,4 +70,4 @@ class UIButtonSpec: QuickSpec { } } } - +#endif diff --git a/ReactiveCocoaTests/UIKit/UICollectionViewSpec.swift b/ReactiveCocoaTests/UIKit/UICollectionViewSpec.swift index 0f396d0d8c..74270c5b54 100644 --- a/ReactiveCocoaTests/UIKit/UICollectionViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UICollectionViewSpec.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) import Quick import Nimble import ReactiveCocoa @@ -60,3 +61,4 @@ private final class TestCollectionView: UICollectionView { reloadDataObserver.send(value: ()) } } +#endif diff --git a/ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift b/ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift index c771d7a2f8..c2d5ad92fe 100644 --- a/ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift +++ b/ReactiveCocoaTests/UIKit/UIControl+EnableSendActionsForControlEvents.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) import UIKit private func rac_swizzle() { @@ -38,3 +39,4 @@ extension UIControl { _ = target?.perform(action, with: self) } } +#endif diff --git a/ReactiveCocoaTests/UIKit/UIControlSpec.swift b/ReactiveCocoaTests/UIKit/UIControlSpec.swift index 24b27ea9c3..3277b219a3 100644 --- a/ReactiveCocoaTests/UIKit/UIControlSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIControlSpec.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) import ReactiveSwift import ReactiveCocoa import UIKit @@ -81,3 +82,4 @@ class UIControlSpec: QuickSpec { } } } +#endif diff --git a/ReactiveCocoaTests/UIKit/UIDatePickerSpec.swift b/ReactiveCocoaTests/UIKit/UIDatePickerSpec.swift index 7904b1760d..53d693f868 100644 --- a/ReactiveCocoaTests/UIKit/UIDatePickerSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIDatePickerSpec.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(tvOS) import ReactiveSwift import ReactiveCocoa import UIKit @@ -45,3 +46,4 @@ class UIDatePickerSpec: QuickSpec { } } } +#endif diff --git a/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift b/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift index 2ccd230d44..1675c14117 100644 --- a/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIGestureRecognizerSpec.swift @@ -1,8 +1,9 @@ -import Quick -import Nimble +#if canImport(UIKit) import ReactiveSwift import ReactiveCocoa import UIKit +import Quick +import Nimble class UIGestureRecognizerSpec: QuickSpec { override func spec() { @@ -98,3 +99,4 @@ private final class TestTapGestureRecognizer: UITapGestureRecognizer { _ = targetAction.target.perform(targetAction.action, with: self) } } +#endif diff --git a/ReactiveCocoaTests/UIKit/UIImageViewSpec.swift b/ReactiveCocoaTests/UIKit/UIImageViewSpec.swift index 11f5783383..847bfbff3a 100644 --- a/ReactiveCocoaTests/UIKit/UIImageViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIImageViewSpec.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) import ReactiveSwift import ReactiveCocoa import UIKit @@ -48,3 +49,4 @@ class UIImageViewSpec: QuickSpec { } } } +#endif diff --git a/ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift b/ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift index 51bb6602cb..de8fd55bd9 100644 --- a/ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIKeyboardSpec.swift @@ -1,6 +1,7 @@ -import UIKit +#if canImport(UIKit) && !os(tvOS) import ReactiveSwift import ReactiveCocoa +import UIKit import Quick import Nimble @@ -193,3 +194,4 @@ class UIKeyboardSpec: QuickSpec { } } } +#endif diff --git a/ReactiveCocoaTests/UIKit/ReusableComponentsSpec.swift b/ReactiveCocoaTests/UIKit/UIKitReusableComponentsSpec.swift similarity index 97% rename from ReactiveCocoaTests/UIKit/ReusableComponentsSpec.swift rename to ReactiveCocoaTests/UIKit/UIKitReusableComponentsSpec.swift index e767043288..a72ff4a630 100644 --- a/ReactiveCocoaTests/UIKit/ReusableComponentsSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIKitReusableComponentsSpec.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) import Quick import Nimble import ReactiveSwift @@ -55,3 +56,4 @@ class ReusableComponentsSpec: QuickSpec { } } } +#endif diff --git a/ReactiveCocoaTests/UIKit/UILabelSpec.swift b/ReactiveCocoaTests/UIKit/UILabelSpec.swift index d816ae805e..9c30f30bbb 100644 --- a/ReactiveCocoaTests/UIKit/UILabelSpec.swift +++ b/ReactiveCocoaTests/UIKit/UILabelSpec.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) import ReactiveSwift import ReactiveCocoa import UIKit @@ -72,3 +73,4 @@ class UILabelSpec: QuickSpec { } } } +#endif diff --git a/ReactiveCocoaTests/UIKit/UINavigationItemSpec.swift b/ReactiveCocoaTests/UIKit/UINavigationItemSpec.swift index 5579542989..1f4cf45dad 100644 --- a/ReactiveCocoaTests/UIKit/UINavigationItemSpec.swift +++ b/ReactiveCocoaTests/UIKit/UINavigationItemSpec.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) import ReactiveSwift import ReactiveCocoa import UIKit @@ -281,3 +282,4 @@ class UINavigationItemSpec: QuickSpec { #endif } } +#endif diff --git a/ReactiveCocoaTests/UIKit/UIPickerViewSpec.swift b/ReactiveCocoaTests/UIKit/UIPickerViewSpec.swift index 544aa00b0f..e56d4d33eb 100644 --- a/ReactiveCocoaTests/UIKit/UIPickerViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIPickerViewSpec.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(tvOS) import ReactiveSwift import ReactiveCocoa import UIKit @@ -150,3 +151,4 @@ private final class TestPickerView: UIPickerView { reloadComponentObserver.send(value: component) } } +#endif diff --git a/ReactiveCocoaTests/UIKit/UIProgressViewSpec.swift b/ReactiveCocoaTests/UIKit/UIProgressViewSpec.swift index 97edd5736e..0e3e7ddb54 100644 --- a/ReactiveCocoaTests/UIKit/UIProgressViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIProgressViewSpec.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) import ReactiveSwift import ReactiveCocoa import UIKit @@ -36,3 +37,4 @@ class UIProgressViewSpec: QuickSpec { } } } +#endif diff --git a/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift b/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift index 9ca1a9edab..9740dce0f6 100644 --- a/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift @@ -1,7 +1,9 @@ -import Quick -import Nimble +#if canImport(UIKit) && !os(tvOS) import ReactiveSwift import ReactiveCocoa +import UIKit +import Quick +import Nimble class UIRefreshControlSpec: QuickSpec { override func spec() { @@ -81,3 +83,4 @@ class UIRefreshControlSpec: QuickSpec { } } } +#endif diff --git a/ReactiveCocoaTests/UIKit/UIResponderSpec.swift b/ReactiveCocoaTests/UIKit/UIResponderSpec.swift index 830fb5f0ee..d1c9c5e847 100644 --- a/ReactiveCocoaTests/UIKit/UIResponderSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIResponderSpec.swift @@ -1,7 +1,9 @@ -import Quick -import Nimble +#if canImport(UIKit) && !os(tvOS) import ReactiveSwift import ReactiveCocoa +import UIKit +import Quick +import Nimble class UIResponderSpec: QuickSpec { override func spec() { @@ -18,3 +20,4 @@ class UIResponderSpec: QuickSpec { } } } +#endif diff --git a/ReactiveCocoaTests/UIKit/UIScrollViewSpec.swift b/ReactiveCocoaTests/UIKit/UIScrollViewSpec.swift index a208506467..9f4f2203fd 100644 --- a/ReactiveCocoaTests/UIKit/UIScrollViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIScrollViewSpec.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(tvOS) import ReactiveSwift import ReactiveCocoa import UIKit @@ -121,3 +122,4 @@ class UIScrollViewSpec: QuickSpec { } } } +#endif diff --git a/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift b/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift index 6f811ec9f9..2aace1adc5 100644 --- a/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift +++ b/ReactiveCocoaTests/UIKit/UISearchBarSpec.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) && !os(tvOS) import ReactiveSwift import ReactiveCocoa import UIKit @@ -257,3 +258,4 @@ class SearchBarDelegateReceiver: NSObject, UISearchBarDelegate { selectedScopeButtonIndices.append(selectedScope) } } +#endif diff --git a/ReactiveCocoaTests/UIKit/UISegmentedControlSpec.swift b/ReactiveCocoaTests/UIKit/UISegmentedControlSpec.swift index f0794b532d..662eb6072b 100644 --- a/ReactiveCocoaTests/UIKit/UISegmentedControlSpec.swift +++ b/ReactiveCocoaTests/UIKit/UISegmentedControlSpec.swift @@ -1,7 +1,9 @@ -import Quick -import Nimble +#if canImport(UIKit) && !os(tvOS) import ReactiveSwift import ReactiveCocoa +import UIKit +import Quick +import Nimble class UISegmentedControlSpec: QuickSpec { override func spec() { @@ -23,3 +25,4 @@ class UISegmentedControlSpec: QuickSpec { } } } +#endif diff --git a/ReactiveCocoaTests/UIKit/UISliderSpec.swift b/ReactiveCocoaTests/UIKit/UISliderSpec.swift index 0823549602..143b6ce059 100644 --- a/ReactiveCocoaTests/UIKit/UISliderSpec.swift +++ b/ReactiveCocoaTests/UIKit/UISliderSpec.swift @@ -1,8 +1,9 @@ -import Quick -import Nimble -import ReactiveCocoa +#if canImport(UIKit) && !os(tvOS) import ReactiveSwift +import ReactiveCocoa import UIKit +import Quick +import Nimble class UISliderSpec: QuickSpec { override func spec() { @@ -64,5 +65,6 @@ class UISliderSpec: QuickSpec { slider.sendActions(for: .valueChanged) expect(updatedValue) ≈ 0.25 } - } + } } +#endif diff --git a/ReactiveCocoaTests/UIKit/UIStepperSpec.swift b/ReactiveCocoaTests/UIKit/UIStepperSpec.swift index a886d74981..5f1227503e 100644 --- a/ReactiveCocoaTests/UIKit/UIStepperSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIStepperSpec.swift @@ -1,8 +1,9 @@ -import Quick -import Nimble -import ReactiveCocoa +#if canImport(UIKit) && !os(tvOS) import ReactiveSwift +import ReactiveCocoa import UIKit +import Quick +import Nimble class UIStepperSpec: QuickSpec { override func spec() { @@ -66,3 +67,4 @@ class UIStepperSpec: QuickSpec { } } } +#endif diff --git a/ReactiveCocoaTests/UIKit/UISwitchSpec.swift b/ReactiveCocoaTests/UIKit/UISwitchSpec.swift index e1417183e3..5ad3baceae 100644 --- a/ReactiveCocoaTests/UIKit/UISwitchSpec.swift +++ b/ReactiveCocoaTests/UIKit/UISwitchSpec.swift @@ -1,7 +1,9 @@ -import Quick -import Nimble +#if canImport(UIKit) && !os(tvOS) import ReactiveSwift import ReactiveCocoa +import UIKit +import Quick +import Nimble class UISwitchSpec: QuickSpec { override func spec() { @@ -68,3 +70,4 @@ class UISwitchSpec: QuickSpec { } } } +#endif diff --git a/ReactiveCocoaTests/UIKit/UITabBarItemSpec.swift b/ReactiveCocoaTests/UIKit/UITabBarItemSpec.swift index ab97fe261b..16af36c5fb 100644 --- a/ReactiveCocoaTests/UIKit/UITabBarItemSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITabBarItemSpec.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) import ReactiveSwift import ReactiveCocoa import UIKit @@ -60,3 +61,4 @@ class UITabBarSpec: QuickSpec { } } } +#endif diff --git a/ReactiveCocoaTests/UIKit/UITableViewSpec.swift b/ReactiveCocoaTests/UIKit/UITableViewSpec.swift index fce22b3565..12ee8da778 100644 --- a/ReactiveCocoaTests/UIKit/UITableViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITableViewSpec.swift @@ -1,8 +1,9 @@ -import Quick -import Nimble -import ReactiveCocoa +#if canImport(UIKit) import ReactiveSwift +import ReactiveCocoa import UIKit +import Quick +import Nimble final class UITableViewSpec: QuickSpec { override func spec() { @@ -60,3 +61,4 @@ private final class TestTableView: UITableView { reloadDataObserver.send(value: ()) } } +#endif diff --git a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift index b9b3377f53..da5304c314 100644 --- a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) import ReactiveSwift import ReactiveCocoa import UIKit @@ -199,3 +200,4 @@ extension UIControl.Event { return [.allEditingEvents, .editingDidBegin, .editingChanged, .editingDidEndOnExit, .editingDidEnd] } } +#endif diff --git a/ReactiveCocoaTests/UIKit/UITextViewSpec.swift b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift index 79c2427401..faa5795773 100644 --- a/ReactiveCocoaTests/UIKit/UITextViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextViewSpec.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) import ReactiveSwift import ReactiveCocoa import UIKit @@ -114,3 +115,4 @@ class UITextViewSpec: QuickSpec { } } } +#endif diff --git a/ReactiveCocoaTests/UIKit/UIViewControllerSpec.swift b/ReactiveCocoaTests/UIKit/UIViewControllerSpec.swift index b8db392c1c..48513621ed 100644 --- a/ReactiveCocoaTests/UIKit/UIViewControllerSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIViewControllerSpec.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) import ReactiveSwift import ReactiveCocoa import UIKit @@ -111,3 +112,4 @@ class UIViewControllerSpec: QuickSpec { } } } +#endif diff --git a/ReactiveCocoaTests/UIKit/UIViewSpec.swift b/ReactiveCocoaTests/UIKit/UIViewSpec.swift index ac6fa6d519..669aa51876 100644 --- a/ReactiveCocoaTests/UIKit/UIViewSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIViewSpec.swift @@ -1,3 +1,4 @@ +#if canImport(UIKit) import ReactiveSwift import ReactiveCocoa import UIKit @@ -94,3 +95,4 @@ class UIViewSpec: QuickSpec { } } } +#endif From 71bebad8c7be1ce2acbd0ab0a4d1063b6c1533ea Mon Sep 17 00:00:00 2001 From: Simon Jarbrant Date: Tue, 10 Dec 2019 23:26:07 +0100 Subject: [PATCH 0999/1028] Add support for Swift Package Manager (#3693) * Add support for Swift Package Manager * Add swift build as a package manager test step in travis.yml * Fix Podspec after code restructure to support SwiftPM --- .travis.yml | 1 + CHANGELOG.md | 29 ++++---- Package.resolved | 52 +++++++++++++++ Package.swift | 43 ++++++++++++ README.md | 11 +++- ReactiveCocoa.podspec | 4 +- ReactiveCocoa.xcodeproj/project.pbxproj | 66 +++++++++++++------ ReactiveCocoa/NSObject+Association.swift | 3 + ReactiveCocoa/NSObject+Intercepting.swift | 3 + .../NSObject+KeyValueObserving.swift | 3 + ReactiveCocoa/ObjC+RuntimeSubclassing.swift | 3 + ReactiveCocoa/{ => include}/module.modulemap | 0 .../{ => include}/ObjCRuntimeAliases.h | 0 .../include}/ReactiveCocoa.h | 0 .../{ => include}/MessageForwardingEntity.h | 0 ReactiveCocoaTests/InterceptingSpec.swift | 4 ++ 16 files changed, 184 insertions(+), 38 deletions(-) create mode 100644 Package.resolved create mode 100644 Package.swift rename ReactiveCocoa/{ => include}/module.modulemap (100%) rename ReactiveCocoaObjC/{ => include}/ObjCRuntimeAliases.h (100%) rename {ReactiveCocoa => ReactiveCocoaObjC/include}/ReactiveCocoa.h (100%) rename ReactiveCocoaObjCTestSupport/{ => include}/MessageForwardingEntity.h (100%) diff --git a/.travis.yml b/.travis.yml index b5c64884a2..4e90f06adf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -47,3 +47,4 @@ jobs: env: - JOB=PODSPEC - script: carthage build --cache-builds --no-skip-current + - script: swift build diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d17885af8..46ba7f159f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # master *Please put new entries at the top. +1. Add support for Swift Package Manager 1. Add platform checks and update project's file organization in preparation of Swift Package Manager support [#3692](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/3692) @@ -99,7 +100,7 @@ Sources that use the MapKit bindings are now required to import ReactiveMapKit. - For all Xcode project users (including Carthage), targets need to be configured to link against ReactiveMapKit. For CocoaPods users, the framework is offered as a standalone podspec, so the Podfile needs to be updated with a new entry. + For all Xcode project users (including Carthage), targets need to be configured to link against ReactiveMapKit. For CocoaPods users, the framework is offered as a standalone podspec, so the Podfile needs to be updated with a new entry. # 6.1.0-alpha.2 # 6.1.0-alpha.1 @@ -205,16 +206,16 @@ Lots has changed, but if you're already migrating to Swift 3 then that should no #### Foundation: Object Interception RAC 5.0 includes a few object interception tools from ReactiveObjC, remastered for ReactiveSwift. - + 1. **Method Call Interception** Create signals that are sourced by intercepting Objective-C objects. - + ```swift // Notify after every time `viewWillAppear(_:)` is called. let appearing = viewController.reactive.trigger(for: #selector(UIViewController.viewWillAppear(_:))) ``` - + 1. **Object Lifetime** Obtain a `Lifetime` token for any `NSObject` to observe their deinitialization. @@ -228,7 +229,7 @@ RAC 5.0 includes a few object interception tools from ReactiveObjC, remastered f Establish key-value observations in the form of [`SignalProducer`][]s and strong-typed `DynamicProperty`s, and enjoy the inherited composability. - + ```swift // A producer that sends the current value of `keyPath`, followed by // subsequent changes. @@ -236,7 +237,7 @@ RAC 5.0 includes a few object interception tools from ReactiveObjC, remastered f // Terminate the KVO observation if the lifetime of `self` ends. let producer = object.reactive.values(forKeyPath: #keyPath(key)) .take(during: self.reactive.lifetime) - + // A parameterized property that represents the supplied key path of the // wrapped object. It holds a weak reference to the wrapped object. let property = DynamicProperty(object: person, @@ -263,19 +264,19 @@ UI components now expose a collection of binding targets to which can be bound f Interactive UI components expose [`Signal`][]s for control events and updates in the control value upon user interactions. - + A selected set of controls provide a convenience, expressive binding API for [`Action`][]s. - - + + ```swift // Update `allowsCookies` whenever the toggle is flipped. - preferences.allowsCookies <~ toggle.reactive.isOnValues - + preferences.allowsCookies <~ toggle.reactive.isOnValues + // Compute live character counts from the continuous stream of user initiated // changes in the text. textField.reactive.continuousTextValues.map { $0.characters.count } - + // Trigger `commit` whenever the button is pressed. button.reactive.pressed = CocoaAction(viewModel.commit) ``` @@ -330,7 +331,7 @@ let old = atomicCount.modify { value in The new `BindingTargetProtocol` protocol has been formally introduced to represent an entity to which can form a unidirectional binding using the `<~` operator. A new type `BindingTarget` has also been introduced to represent non-observable targets that are expected to only be written to. ```swift -// The `UIControl` exposes a `isEnabled` binding target. +// The `UIControl` exposes a `isEnabled` binding target. control.isEnabled <~ viewModel.isEnabled ``` @@ -342,7 +343,7 @@ control.isEnabled <~ viewModel.isEnabled public final class MyController { private let token = Lifetime.Token() public let lifetime: Lifetime - + public init() { lifetime = Lifetime(token) } diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000000..5afe1743d6 --- /dev/null +++ b/Package.resolved @@ -0,0 +1,52 @@ +{ + "object": { + "pins": [ + { + "package": "CwlCatchException", + "repositoryURL": "https://github.com/mattgallagher/CwlCatchException.git", + "state": { + "branch": null, + "revision": "7cd2f8cacc4d22f21bc0b2309c3b18acf7957b66", + "version": "1.2.0" + } + }, + { + "package": "CwlPreconditionTesting", + "repositoryURL": "https://github.com/mattgallagher/CwlPreconditionTesting.git", + "state": { + "branch": null, + "revision": "c228db5d2ad1b01ebc84435e823e6cca4e3db98b", + "version": "1.2.0" + } + }, + { + "package": "Nimble", + "repositoryURL": "https://github.com/Quick/Nimble.git", + "state": { + "branch": null, + "revision": "6abeb3f5c03beba2b9e4dbe20886e773b5b629b6", + "version": "8.0.4" + } + }, + { + "package": "Quick", + "repositoryURL": "https://github.com/Quick/Quick.git", + "state": { + "branch": null, + "revision": "33682c2f6230c60614861dfc61df267e11a1602f", + "version": "2.2.0" + } + }, + { + "package": "ReactiveSwift", + "repositoryURL": "https://github.com/ReactiveCocoa/ReactiveSwift", + "state": { + "branch": null, + "revision": "b772fa0b624926e6e2f21acbb79297736a05c585", + "version": "6.1.0" + } + } + ] + }, + "version": 1 +} diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000000..73f166a8a2 --- /dev/null +++ b/Package.swift @@ -0,0 +1,43 @@ +// swift-tools-version:5.0 +// The swift-tools-version declares the minimum version of Swift required to build this package. +import PackageDescription + +let package = Package( + name: "ReactiveCocoa", + platforms: [ + .macOS(.v10_10), .iOS(.v8), .tvOS(.v9), .watchOS(.v2) + ], + products: [ + .library(name: "ReactiveCocoa", targets: ["ReactiveCocoa"]) + ], + dependencies: [ + .package(url: "https://github.com/ReactiveCocoa/ReactiveSwift", from: "6.1.0"), + .package(url: "https://github.com/Quick/Quick.git", from: "2.0.0"), + .package(url: "https://github.com/Quick/Nimble.git", from: "8.0.0"), + ], + targets: [ + .target( + name: "ReactiveCocoaObjC", + dependencies: [], + path: "ReactiveCocoaObjC"), + + .target( + name: "ReactiveCocoa", + dependencies: ["ReactiveSwift", "ReactiveCocoaObjC"], + path: "ReactiveCocoa"), + + .target( + name: "ReactiveCocoaObjCTestSupport", + path: "ReactiveCocoaObjCTestSupport"), + + .testTarget( + name: "ReactiveCocoaTests", + dependencies: [ + "ReactiveCocoa", + "ReactiveCocoaObjCTestSupport", + "Quick", + "Nimble" + ], + path: "ReactiveCocoaTests"), + ] +) diff --git a/README.md b/README.md index d3571cb4ea..b318bd504f 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@


    -[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](#carthage) [![CocoaPods compatible](https://img.shields.io/cocoapods/v/ReactiveCocoa.svg)](#cocoapods) [![GitHub release](https://img.shields.io/github/release/ReactiveCocoa/ReactiveCocoa.svg)](https://github.com/ReactiveCocoa/ReactiveCocoa/releases) ![Swift 3.0.x](https://img.shields.io/badge/Swift-3.0.x-orange.svg) ![platforms](https://img.shields.io/badge/platforms-iOS%20%7C%20OS%20X%20%7C%20watchOS%20%7C%20tvOS%20-lightgrey.svg) +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](#carthage) [![CocoaPods compatible](https://img.shields.io/cocoapods/v/ReactiveCocoa.svg)](#cocoapods) [![SwiftPM compatible](https://img.shields.io/badge/SwiftPM-compatible-orange.svg)](#swift-package-manager) [![GitHub release](https://img.shields.io/github/release/ReactiveCocoa/ReactiveCocoa.svg)](https://github.com/ReactiveCocoa/ReactiveCocoa/releases) ![Swift 3.0.x](https://img.shields.io/badge/Swift-3.0.x-orange.svg) ![platforms](https://img.shields.io/badge/platforms-iOS%20%7C%20OS%20X%20%7C%20watchOS%20%7C%20tvOS%20-lightgrey.svg) ⚠️ [Looking for the Objective-C API?][] @@ -114,6 +114,15 @@ ReactiveCocoa to your `Podfile`: pod 'ReactiveCocoa', '~> 9.0' ``` +#### Swift Package Manager + +If you use Swift Package Manager, simply add ReactiveCocoa as a dependency +of your package in `Package.swift`: + +``` +.package(url: "https://github.com/ReactiveCocoa/ReactiveCocoa.git", branch: "master") +``` + #### Git submodule 1. Add the ReactiveCocoa repository as a [submodule][] of your diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index d4b275cd79..d86fcfaa9c 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -15,8 +15,8 @@ Pod::Spec.new do |s| s.watchos.deployment_target = "2.0" s.source = { :git => "https://github.com/ReactiveCocoa/ReactiveCocoa.git", :tag => "#{s.version}" } - s.source_files = "ReactiveCocoa/*.{swift,h,m}", "ReactiveCocoa/Shared/*.{swift}", "ReactiveCocoaObjC/*.{h,m}" - s.public_header_files = "ReactiveCocoaObjC/ObjCRuntimeAliases.h" + s.source_files = "ReactiveCocoa/*.{swift,h,m}", "ReactiveCocoa/Shared/*.{swift}", "ReactiveCocoaObjC/**/*.{h,m}" + s.public_header_files = "ReactiveCocoaObjC/include/ObjCRuntimeAliases.h" s.osx.source_files = "ReactiveCocoa/AppKit/*.{swift}" s.ios.source_files = "ReactiveCocoa/UIKit/*.{swift}", "ReactiveCocoa/UIKit/iOS/*.{swift}" s.tvos.source_files = "ReactiveCocoa/UIKit/*.{swift}" diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 68e29080cc..4e9ed97c3b 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -583,7 +583,6 @@ 9ADE4A951DA6F018005C2AC8 /* NSTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSTextField.swift; sourceTree = ""; }; 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LifetimeSpec.swift; sourceTree = ""; }; 9ADFE5A41DC0001C001E11F7 /* Synchronizing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Synchronizing.swift; sourceTree = ""; }; - 9AE7C2A21DDD768500F7534C /* module.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = ""; }; 9AE7C2A31DDD7F5100F7534C /* ObjC+Messages.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObjC+Messages.swift"; sourceTree = ""; }; 9AED64C41E496A3700321004 /* ActionProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionProxy.swift; sourceTree = ""; }; 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+BindingTarget.swift"; sourceTree = ""; }; @@ -662,6 +661,7 @@ E92142A12284B64E007E412D /* WKInterfaceController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKInterfaceController.swift; sourceTree = ""; }; E92142A8228552CC007E412D /* WKInterfaceMovie.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKInterfaceMovie.swift; sourceTree = ""; }; E92142AA228553C5007E412D /* WKInterfaceInlineMovie.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKInterfaceInlineMovie.swift; sourceTree = ""; }; + F440783A2371BE3D00F103E7 /* module.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -799,8 +799,8 @@ 056D088022C00A6A00291F50 /* ReactiveCocoaObjC */ = { isa = PBXGroup; children = ( - 9A1D06751D9415FB00ACF44C /* ObjCRuntimeAliases.h */, 9AA0BD971DDE7A2200531FCF /* ObjCRuntimeAliases.m */, + F44078372371B6E400F103E7 /* include */, ); path = ReactiveCocoaObjC; sourceTree = ""; @@ -808,8 +808,8 @@ 056D088222C00B2700291F50 /* ReactiveCocoaObjCTestSupport */ = { isa = PBXGroup; children = ( - 9AD841DA204C29B90040F0C0 /* MessageForwardingEntity.h */, 9AD841DB204C29B90040F0C0 /* MessageForwardingEntity.m */, + F440783B2371C00800F103E7 /* include */, ); path = ReactiveCocoaObjCTestSupport; sourceTree = ""; @@ -1066,6 +1066,7 @@ 9A90374E1ED61C6300345D62 /* ReactiveSwift+Lifetime.swift */, 9ADFE5A41DC0001C001E11F7 /* Synchronizing.swift */, 9A1D05E91D93E9F100ACF44C /* AppKit */, + F44078392371BE3D00F103E7 /* include */, 538DCB761DCA5E1600332880 /* Shared */, D04725ED19E49ED7006002AA /* Supporting Files */, 9A1D05EB1D93E9F100ACF44C /* UIKit */, @@ -1077,8 +1078,6 @@ D04725ED19E49ED7006002AA /* Supporting Files */ = { isa = PBXGroup; children = ( - D04725EF19E49ED7006002AA /* ReactiveCocoa.h */, - 9AE7C2A21DDD768500F7534C /* module.modulemap */, D04725EE19E49ED7006002AA /* Info.plist */, ); name = "Supporting Files"; @@ -1214,6 +1213,31 @@ path = WatchKit; sourceTree = ""; }; + F44078372371B6E400F103E7 /* include */ = { + isa = PBXGroup; + children = ( + 9A1D06751D9415FB00ACF44C /* ObjCRuntimeAliases.h */, + D04725EF19E49ED7006002AA /* ReactiveCocoa.h */, + ); + path = include; + sourceTree = ""; + }; + F44078392371BE3D00F103E7 /* include */ = { + isa = PBXGroup; + children = ( + F440783A2371BE3D00F103E7 /* module.modulemap */, + ); + path = include; + sourceTree = ""; + }; + F440783B2371C00800F103E7 /* include */ = { + isa = PBXGroup; + children = ( + 9AD841DA204C29B90040F0C0 /* MessageForwardingEntity.h */, + ); + path = include; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -2128,7 +2152,7 @@ DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; name = Debug; }; @@ -2140,7 +2164,7 @@ DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; name = Test; }; @@ -2152,7 +2176,7 @@ DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; name = Release; }; @@ -2164,7 +2188,7 @@ DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; name = Profile; }; @@ -2520,7 +2544,7 @@ DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; name = Debug; }; @@ -2532,7 +2556,7 @@ DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; name = Test; }; @@ -2544,7 +2568,7 @@ DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; name = Release; }; @@ -2556,7 +2580,7 @@ DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; name = Profile; }; @@ -2621,7 +2645,7 @@ DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; name = Debug; }; @@ -2633,7 +2657,7 @@ DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; name = Release; }; @@ -2667,7 +2691,7 @@ DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; name = Debug; }; @@ -2679,7 +2703,7 @@ DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; name = Release; }; @@ -2736,7 +2760,7 @@ DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; name = Profile; }; @@ -2759,7 +2783,7 @@ DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; name = Profile; }; @@ -2806,7 +2830,7 @@ DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; name = Test; }; @@ -2829,7 +2853,7 @@ DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/module.modulemap"; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; name = Test; }; diff --git a/ReactiveCocoa/NSObject+Association.swift b/ReactiveCocoa/NSObject+Association.swift index df70f8ede9..f145b00530 100644 --- a/ReactiveCocoa/NSObject+Association.swift +++ b/ReactiveCocoa/NSObject+Association.swift @@ -1,4 +1,7 @@ import Foundation +#if SWIFT_PACKAGE +import ReactiveCocoaObjC +#endif import ReactiveSwift internal struct AssociationKey { diff --git a/ReactiveCocoa/NSObject+Intercepting.swift b/ReactiveCocoa/NSObject+Intercepting.swift index cde2d0f023..f1fd9ba242 100644 --- a/ReactiveCocoa/NSObject+Intercepting.swift +++ b/ReactiveCocoa/NSObject+Intercepting.swift @@ -1,4 +1,7 @@ import Foundation +#if SWIFT_PACKAGE +import ReactiveCocoaObjC +#endif import ReactiveSwift /// Whether the runtime subclass has already been prepared for method diff --git a/ReactiveCocoa/NSObject+KeyValueObserving.swift b/ReactiveCocoa/NSObject+KeyValueObserving.swift index 2c306ffd92..c7fcc3a923 100644 --- a/ReactiveCocoa/NSObject+KeyValueObserving.swift +++ b/ReactiveCocoa/NSObject+KeyValueObserving.swift @@ -1,4 +1,7 @@ import Foundation +#if SWIFT_PACKAGE +import ReactiveCocoaObjC +#endif import ReactiveSwift extension Reactive where Base: NSObject { diff --git a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift index 793dad2deb..95c151ede6 100644 --- a/ReactiveCocoa/ObjC+RuntimeSubclassing.swift +++ b/ReactiveCocoa/ObjC+RuntimeSubclassing.swift @@ -1,4 +1,7 @@ import Foundation +#if SWIFT_PACKAGE +import ReactiveCocoaObjC +#endif import ReactiveSwift /// Whether the runtime subclass has already been swizzled. diff --git a/ReactiveCocoa/module.modulemap b/ReactiveCocoa/include/module.modulemap similarity index 100% rename from ReactiveCocoa/module.modulemap rename to ReactiveCocoa/include/module.modulemap diff --git a/ReactiveCocoaObjC/ObjCRuntimeAliases.h b/ReactiveCocoaObjC/include/ObjCRuntimeAliases.h similarity index 100% rename from ReactiveCocoaObjC/ObjCRuntimeAliases.h rename to ReactiveCocoaObjC/include/ObjCRuntimeAliases.h diff --git a/ReactiveCocoa/ReactiveCocoa.h b/ReactiveCocoaObjC/include/ReactiveCocoa.h similarity index 100% rename from ReactiveCocoa/ReactiveCocoa.h rename to ReactiveCocoaObjC/include/ReactiveCocoa.h diff --git a/ReactiveCocoaObjCTestSupport/MessageForwardingEntity.h b/ReactiveCocoaObjCTestSupport/include/MessageForwardingEntity.h similarity index 100% rename from ReactiveCocoaObjCTestSupport/MessageForwardingEntity.h rename to ReactiveCocoaObjCTestSupport/include/MessageForwardingEntity.h diff --git a/ReactiveCocoaTests/InterceptingSpec.swift b/ReactiveCocoaTests/InterceptingSpec.swift index 73fab9ef35..5f46ffada7 100644 --- a/ReactiveCocoaTests/InterceptingSpec.swift +++ b/ReactiveCocoaTests/InterceptingSpec.swift @@ -1,4 +1,8 @@ import Foundation +#if SWIFT_PACKAGE +import ReactiveCocoaObjC +import ReactiveCocoaObjCTestSupport +#endif @testable import ReactiveCocoa import ReactiveSwift import Quick From 2f27b840950c16e6db225c40989d525858cbf034 Mon Sep 17 00:00:00 2001 From: Marco Cancellieri Date: Tue, 7 Jan 2020 14:57:09 +0100 Subject: [PATCH 1000/1028] Update Readme (#3691) Co-authored-by: Anders Ha --- CHANGELOG.md | 4 ++-- README.md | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46ba7f159f..4f562efff4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ # master *Please put new entries at the top. -1. Add support for Swift Package Manager -1. Add platform checks and update project's file organization in preparation of Swift Package Manager support [#3692](https://github.com/ReactiveCocoa/ReactiveCocoa/pull/3692) +1. Updated `README.md` to reflect Swift 5.1 compatibility and point snippets to 10.1.0 (#3691, kudos to @Marcocanc) +1. Support for Swift Package Manager (#3692, #3676 & #3693, kudos to @fabio-cerdeiral-ck, @sharplet and @simba909) # 10.1.0 1. Update dependencies so ReactiveCocoa can be used with Xcode 11 (#3677, kudos to @olejnjak) diff --git a/README.md b/README.md index b318bd504f..140211c135 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@


    -[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](#carthage) [![CocoaPods compatible](https://img.shields.io/cocoapods/v/ReactiveCocoa.svg)](#cocoapods) [![SwiftPM compatible](https://img.shields.io/badge/SwiftPM-compatible-orange.svg)](#swift-package-manager) [![GitHub release](https://img.shields.io/github/release/ReactiveCocoa/ReactiveCocoa.svg)](https://github.com/ReactiveCocoa/ReactiveCocoa/releases) ![Swift 3.0.x](https://img.shields.io/badge/Swift-3.0.x-orange.svg) ![platforms](https://img.shields.io/badge/platforms-iOS%20%7C%20OS%20X%20%7C%20watchOS%20%7C%20tvOS%20-lightgrey.svg) +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](#carthage) [![CocoaPods compatible](https://img.shields.io/cocoapods/v/ReactiveCocoa.svg)](#cocoapods) [![SwiftPM compatible](https://img.shields.io/badge/SwiftPM-compatible-orange.svg)](#swift-package-manager) [![GitHub release](https://img.shields.io/github/release/ReactiveCocoa/ReactiveCocoa.svg)](https://github.com/ReactiveCocoa/ReactiveCocoa/releases) ![Swift 5.1](https://img.shields.io/badge/Swift-5.1-orange.svg) ![platforms](https://img.shields.io/badge/platforms-iOS%20%7C%20OS%20X%20%7C%20watchOS%20%7C%20tvOS%20-lightgrey.svg) ⚠️ [Looking for the Objective-C API?][] @@ -100,7 +100,7 @@ If you use [Carthage][] to manage your dependencies, simply add ReactiveCocoa to your `Cartfile`: ``` -github "ReactiveCocoa/ReactiveCocoa" ~> 9.0 +github "ReactiveCocoa/ReactiveCocoa" ~> 10.1 ``` If you use Carthage to build your dependencies, make sure you have added `ReactiveCocoa.framework` and `ReactiveSwift.framework` to the "_Linked Frameworks and Libraries_" section of your target, and have included them in your Carthage framework copying build phase. @@ -111,7 +111,7 @@ If you use [CocoaPods][] to manage your dependencies, simply add ReactiveCocoa to your `Podfile`: ``` -pod 'ReactiveCocoa', '~> 9.0' +pod 'ReactiveCocoa', '~> 10.1' ``` #### Swift Package Manager @@ -144,7 +144,7 @@ If you need any help, please visit our [GitHub issues][] or [Stack Overflow][]. ### In Development ### Plan of Record #### ABI stability release -ReactiveCocoa is expected to declare library ABI stability when Swift rolls out resilence support in Swift 5. Until then, ReactiveCocoa will incrementally adopt new language features. +ReactiveCocoa is expected to declare library ABI stability when Swift rolls out resilience support in Swift 5. Until then, ReactiveCocoa will incrementally adopt new language features. [ReactiveSwift]: https://github.com/ReactiveCocoa/ReactiveSwift [ReactiveObjC]: https://github.com/ReactiveCocoa/ReactiveObjC From ea74861333f3a8105f478bc4d94ee9f18c865937 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Tue, 7 Jan 2020 15:28:45 +0000 Subject: [PATCH 1001/1028] Update ReactiveSwift to 6.2.0. (#3696) --- CHANGELOG.md | 1 + Cartfile | 2 +- Cartfile.resolved | 6 +++--- Carthage/Checkouts/Nimble | 2 +- Carthage/Checkouts/Quick | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- Package.resolved | 8 ++++---- Package.swift | 2 +- ReactiveCocoa.podspec | 2 +- 9 files changed, 14 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f562efff4..c9a6b9f7b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master *Please put new entries at the top. +1. Update ReactiveSwift to 6.2. 1. Updated `README.md` to reflect Swift 5.1 compatibility and point snippets to 10.1.0 (#3691, kudos to @Marcocanc) 1. Support for Swift Package Manager (#3692, #3676 & #3693, kudos to @fabio-cerdeiral-ck, @sharplet and @simba909) diff --git a/Cartfile b/Cartfile index f358af4ff1..fe0cec3449 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" ~> 6.1 +github "ReactiveCocoa/ReactiveSwift" ~> 6.2 diff --git a/Cartfile.resolved b/Cartfile.resolved index 57b09d884e..034472dbc5 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,4 +1,4 @@ -github "Quick/Nimble" "v8.0.2" -github "Quick/Quick" "v2.1.0" -github "ReactiveCocoa/ReactiveSwift" "6.1.0" +github "Quick/Nimble" "v8.0.5" +github "Quick/Quick" "v2.2.0" +github "ReactiveCocoa/ReactiveSwift" "6.2.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" diff --git a/Carthage/Checkouts/Nimble b/Carthage/Checkouts/Nimble index f8657642df..b02b00b30b 160000 --- a/Carthage/Checkouts/Nimble +++ b/Carthage/Checkouts/Nimble @@ -1 +1 @@ -Subproject commit f8657642dfdec9973efc79cc68bcef43a653a2bc +Subproject commit b02b00b30b6353632aa4a5fb6124f8147f7140c0 diff --git a/Carthage/Checkouts/Quick b/Carthage/Checkouts/Quick index 94df9b4495..33682c2f62 160000 --- a/Carthage/Checkouts/Quick +++ b/Carthage/Checkouts/Quick @@ -1 +1 @@ -Subproject commit 94df9b449508344667e5afc7e80f8bcbff1e4c37 +Subproject commit 33682c2f6230c60614861dfc61df267e11a1602f diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index b772fa0b62..e8703715af 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit b772fa0b624926e6e2f21acbb79297736a05c585 +Subproject commit e8703715afe26a8efbb2ecfdb3454d648f58d691 diff --git a/Package.resolved b/Package.resolved index 5afe1743d6..a85c0f1d74 100644 --- a/Package.resolved +++ b/Package.resolved @@ -24,8 +24,8 @@ "repositoryURL": "https://github.com/Quick/Nimble.git", "state": { "branch": null, - "revision": "6abeb3f5c03beba2b9e4dbe20886e773b5b629b6", - "version": "8.0.4" + "revision": "b02b00b30b6353632aa4a5fb6124f8147f7140c0", + "version": "8.0.5" } }, { @@ -42,8 +42,8 @@ "repositoryURL": "https://github.com/ReactiveCocoa/ReactiveSwift", "state": { "branch": null, - "revision": "b772fa0b624926e6e2f21acbb79297736a05c585", - "version": "6.1.0" + "revision": "e8703715afe26a8efbb2ecfdb3454d648f58d691", + "version": "6.2.0" } } ] diff --git a/Package.swift b/Package.swift index 73f166a8a2..1e6e9a521b 100644 --- a/Package.swift +++ b/Package.swift @@ -11,7 +11,7 @@ let package = Package( .library(name: "ReactiveCocoa", targets: ["ReactiveCocoa"]) ], dependencies: [ - .package(url: "https://github.com/ReactiveCocoa/ReactiveSwift", from: "6.1.0"), + .package(url: "https://github.com/ReactiveCocoa/ReactiveSwift", from: "6.2.0"), .package(url: "https://github.com/Quick/Quick.git", from: "2.0.0"), .package(url: "https://github.com/Quick/Nimble.git", from: "8.0.0"), ], diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index d86fcfaa9c..cc57f12274 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -24,7 +24,7 @@ Pod::Spec.new do |s| s.watchos.source_files = "ReactiveCocoa/WatchKit/*.{swift}" s.module_name = 'ReactiveCocoa' - s.dependency 'ReactiveSwift', '~> 6.1' + s.dependency 'ReactiveSwift', '~> 6.2' s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "$(inherited) -suppress-warnings" } s.swift_version = '5.0' From 3937d9e64459b6cb8bf0ec1823251a25943c8eac Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Tue, 7 Jan 2020 15:29:49 +0000 Subject: [PATCH 1002/1028] 10.2.0 --- CHANGELOG.md | 1 + ReactiveCocoa.podspec | 2 +- ReactiveCocoa/Info.plist | 2 +- ReactiveMapKit.podspec | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9a6b9f7b8..ff985bc74d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master *Please put new entries at the top. +# 10.2.0 1. Update ReactiveSwift to 6.2. 1. Updated `README.md` to reflect Swift 5.1 compatibility and point snippets to 10.1.0 (#3691, kudos to @Marcocanc) 1. Support for Swift Package Manager (#3692, #3676 & #3693, kudos to @fabio-cerdeiral-ck, @sharplet and @simba909) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index cc57f12274..5dcc15aa8a 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "10.1.0" + s.version = "10.2.0" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveCocoa/Info.plist b/ReactiveCocoa/Info.plist index b22ee19865..be277c0fe7 100644 --- a/ReactiveCocoa/Info.plist +++ b/ReactiveCocoa/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 10.1.0 + 10.2.0 CFBundleSignature ???? CFBundleVersion diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index b8a719c221..680e3f084b 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveMapKit" - s.version = "10.1.0" + s.version = "10.2.0" s.summary = "MapKit bindings for ReactiveCocoa." s.description = <<-DESC Provide MapKit bindings for ReactiveCocoa. ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. From 6ee204dc2d1edc04d13671302fc392b9931455b2 Mon Sep 17 00:00:00 2001 From: Niclas Kristek Date: Fri, 17 Jan 2020 11:44:29 +0100 Subject: [PATCH 1003/1028] Add support for macOS Catalyst (#3698) * Add check if target environment is macOS Catalyst * Add changelog entry --- CHANGELOG.md | 2 +- ReactiveCocoa/AppKit/ActionProxy.swift | 2 +- ReactiveCocoa/AppKit/AppKitReusableComponents.swift | 2 +- ReactiveCocoa/AppKit/NSButton.swift | 2 +- ReactiveCocoa/AppKit/NSCollectionView.swift | 2 +- ReactiveCocoa/AppKit/NSControl.swift | 2 +- ReactiveCocoa/AppKit/NSImageView.swift | 2 +- ReactiveCocoa/AppKit/NSPopUpButton.swift | 2 +- ReactiveCocoa/AppKit/NSSegmentedControl.swift | 2 +- ReactiveCocoa/AppKit/NSSlider.swift | 2 +- ReactiveCocoa/AppKit/NSTableView.swift | 2 +- ReactiveCocoa/AppKit/NSTextField.swift | 2 +- ReactiveCocoa/AppKit/NSTextView.swift | 2 +- ReactiveCocoa/AppKit/NSView.swift | 2 +- ReactiveCocoa/Shared/NSLayoutConstraint.swift | 2 +- ReactiveCocoaTests/AppKit/ActionProxySpec.swift | 2 +- .../AppKit/AppKitReusableComponentsSpec.swift | 2 +- ReactiveCocoaTests/AppKit/NSButtonSpec.swift | 2 +- ReactiveCocoaTests/AppKit/NSCollectionViewSpec.swift | 2 +- ReactiveCocoaTests/AppKit/NSControlSpec.swift | 2 +- ReactiveCocoaTests/AppKit/NSImageViewSpec.swift | 2 +- ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift | 2 +- ReactiveCocoaTests/AppKit/NSTableViewSpec.swift | 2 +- ReactiveCocoaTests/AppKit/NSViewSpec.swift | 2 +- ReactiveCocoaTests/AppKit/Swift4TestInteroperability.swift | 2 +- ReactiveCocoaTests/CocoaActionSpec.swift | 6 +++--- ReactiveCocoaTests/Shared/NSLayoutConstraintSpec.swift | 2 +- 27 files changed, 29 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff985bc74d..ae1afe13b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ # master -*Please put new entries at the top. +1. Don't include code which uses unavailable classes (like `NSSlider`) when targeting macOS Catalyst (#3698, kudos to @nkristek) # 10.2.0 1. Update ReactiveSwift to 6.2. diff --git a/ReactiveCocoa/AppKit/ActionProxy.swift b/ReactiveCocoa/AppKit/ActionProxy.swift index 343adf09ab..81e1b34975 100644 --- a/ReactiveCocoa/AppKit/ActionProxy.swift +++ b/ReactiveCocoa/AppKit/ActionProxy.swift @@ -1,4 +1,4 @@ -#if canImport(AppKit) +#if canImport(AppKit) && !targetEnvironment(macCatalyst) import AppKit import ReactiveSwift diff --git a/ReactiveCocoa/AppKit/AppKitReusableComponents.swift b/ReactiveCocoa/AppKit/AppKitReusableComponents.swift index 6e040a56d1..f3a9bdbd8c 100644 --- a/ReactiveCocoa/AppKit/AppKitReusableComponents.swift +++ b/ReactiveCocoa/AppKit/AppKitReusableComponents.swift @@ -1,4 +1,4 @@ -#if canImport(AppKit) +#if canImport(AppKit) && !targetEnvironment(macCatalyst) import AppKit import ReactiveSwift diff --git a/ReactiveCocoa/AppKit/NSButton.swift b/ReactiveCocoa/AppKit/NSButton.swift index 1351446ae7..701d5c5d4d 100644 --- a/ReactiveCocoa/AppKit/NSButton.swift +++ b/ReactiveCocoa/AppKit/NSButton.swift @@ -1,4 +1,4 @@ -#if canImport(AppKit) +#if canImport(AppKit) && !targetEnvironment(macCatalyst) import AppKit import ReactiveSwift diff --git a/ReactiveCocoa/AppKit/NSCollectionView.swift b/ReactiveCocoa/AppKit/NSCollectionView.swift index d8e07cf4ec..021af808ba 100644 --- a/ReactiveCocoa/AppKit/NSCollectionView.swift +++ b/ReactiveCocoa/AppKit/NSCollectionView.swift @@ -1,4 +1,4 @@ -#if canImport(AppKit) +#if canImport(AppKit) && !targetEnvironment(macCatalyst) import AppKit import ReactiveSwift diff --git a/ReactiveCocoa/AppKit/NSControl.swift b/ReactiveCocoa/AppKit/NSControl.swift index f6517e2fbb..65e3fff521 100644 --- a/ReactiveCocoa/AppKit/NSControl.swift +++ b/ReactiveCocoa/AppKit/NSControl.swift @@ -1,4 +1,4 @@ -#if canImport(AppKit) +#if canImport(AppKit) && !targetEnvironment(macCatalyst) import AppKit import ReactiveSwift diff --git a/ReactiveCocoa/AppKit/NSImageView.swift b/ReactiveCocoa/AppKit/NSImageView.swift index 773f5db449..a3b24cd513 100644 --- a/ReactiveCocoa/AppKit/NSImageView.swift +++ b/ReactiveCocoa/AppKit/NSImageView.swift @@ -1,4 +1,4 @@ -#if canImport(AppKit) +#if canImport(AppKit) && !targetEnvironment(macCatalyst) import AppKit import ReactiveSwift diff --git a/ReactiveCocoa/AppKit/NSPopUpButton.swift b/ReactiveCocoa/AppKit/NSPopUpButton.swift index 959d842591..9e504028e0 100644 --- a/ReactiveCocoa/AppKit/NSPopUpButton.swift +++ b/ReactiveCocoa/AppKit/NSPopUpButton.swift @@ -1,4 +1,4 @@ -#if canImport(AppKit) +#if canImport(AppKit) && !targetEnvironment(macCatalyst) import AppKit import ReactiveSwift diff --git a/ReactiveCocoa/AppKit/NSSegmentedControl.swift b/ReactiveCocoa/AppKit/NSSegmentedControl.swift index e83376baf9..9ed515ec67 100644 --- a/ReactiveCocoa/AppKit/NSSegmentedControl.swift +++ b/ReactiveCocoa/AppKit/NSSegmentedControl.swift @@ -1,4 +1,4 @@ -#if canImport(AppKit) +#if canImport(AppKit) && !targetEnvironment(macCatalyst) import AppKit import ReactiveSwift diff --git a/ReactiveCocoa/AppKit/NSSlider.swift b/ReactiveCocoa/AppKit/NSSlider.swift index ae7ee79160..110181ce78 100644 --- a/ReactiveCocoa/AppKit/NSSlider.swift +++ b/ReactiveCocoa/AppKit/NSSlider.swift @@ -1,4 +1,4 @@ -#if canImport(AppKit) +#if canImport(AppKit) && !targetEnvironment(macCatalyst) import AppKit import ReactiveSwift diff --git a/ReactiveCocoa/AppKit/NSTableView.swift b/ReactiveCocoa/AppKit/NSTableView.swift index 857520b9c1..be0691675e 100644 --- a/ReactiveCocoa/AppKit/NSTableView.swift +++ b/ReactiveCocoa/AppKit/NSTableView.swift @@ -1,4 +1,4 @@ -#if canImport(AppKit) +#if canImport(AppKit) && !targetEnvironment(macCatalyst) import AppKit import ReactiveSwift diff --git a/ReactiveCocoa/AppKit/NSTextField.swift b/ReactiveCocoa/AppKit/NSTextField.swift index 3401f57ea6..e94a16a337 100644 --- a/ReactiveCocoa/AppKit/NSTextField.swift +++ b/ReactiveCocoa/AppKit/NSTextField.swift @@ -1,4 +1,4 @@ -#if canImport(AppKit) +#if canImport(AppKit) && !targetEnvironment(macCatalyst) import AppKit import ReactiveSwift diff --git a/ReactiveCocoa/AppKit/NSTextView.swift b/ReactiveCocoa/AppKit/NSTextView.swift index 29298aa379..b54ea5ca14 100644 --- a/ReactiveCocoa/AppKit/NSTextView.swift +++ b/ReactiveCocoa/AppKit/NSTextView.swift @@ -1,4 +1,4 @@ -#if canImport(AppKit) +#if canImport(AppKit) && !targetEnvironment(macCatalyst) import AppKit import ReactiveSwift diff --git a/ReactiveCocoa/AppKit/NSView.swift b/ReactiveCocoa/AppKit/NSView.swift index baa3e4606a..06e251cf93 100644 --- a/ReactiveCocoa/AppKit/NSView.swift +++ b/ReactiveCocoa/AppKit/NSView.swift @@ -1,4 +1,4 @@ -#if canImport(AppKit) +#if canImport(AppKit) && !targetEnvironment(macCatalyst) import AppKit import ReactiveSwift diff --git a/ReactiveCocoa/Shared/NSLayoutConstraint.swift b/ReactiveCocoa/Shared/NSLayoutConstraint.swift index d4d7f470e4..b62aeacd25 100644 --- a/ReactiveCocoa/Shared/NSLayoutConstraint.swift +++ b/ReactiveCocoa/Shared/NSLayoutConstraint.swift @@ -1,7 +1,7 @@ #if !os(watchOS) import ReactiveSwift -#if canImport(AppKit) +#if canImport(AppKit) && !targetEnvironment(macCatalyst) import AppKit #else import UIKit diff --git a/ReactiveCocoaTests/AppKit/ActionProxySpec.swift b/ReactiveCocoaTests/AppKit/ActionProxySpec.swift index 82cc7f5682..5c8d20d5b5 100644 --- a/ReactiveCocoaTests/AppKit/ActionProxySpec.swift +++ b/ReactiveCocoaTests/AppKit/ActionProxySpec.swift @@ -1,4 +1,4 @@ -#if canImport(AppKit) +#if canImport(AppKit) && !targetEnvironment(macCatalyst) import Foundation import Quick import Nimble diff --git a/ReactiveCocoaTests/AppKit/AppKitReusableComponentsSpec.swift b/ReactiveCocoaTests/AppKit/AppKitReusableComponentsSpec.swift index ca49c47d3b..187fa22015 100644 --- a/ReactiveCocoaTests/AppKit/AppKitReusableComponentsSpec.swift +++ b/ReactiveCocoaTests/AppKit/AppKitReusableComponentsSpec.swift @@ -1,4 +1,4 @@ -#if canImport(AppKit) +#if canImport(AppKit) && !targetEnvironment(macCatalyst) import Quick import Nimble import ReactiveSwift diff --git a/ReactiveCocoaTests/AppKit/NSButtonSpec.swift b/ReactiveCocoaTests/AppKit/NSButtonSpec.swift index 154322f922..badc0fc747 100644 --- a/ReactiveCocoaTests/AppKit/NSButtonSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSButtonSpec.swift @@ -1,4 +1,4 @@ -#if canImport(AppKit) +#if canImport(AppKit) && !targetEnvironment(macCatalyst) import Quick import Nimble import ReactiveSwift diff --git a/ReactiveCocoaTests/AppKit/NSCollectionViewSpec.swift b/ReactiveCocoaTests/AppKit/NSCollectionViewSpec.swift index 56af12fcc9..e68d5987d0 100644 --- a/ReactiveCocoaTests/AppKit/NSCollectionViewSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSCollectionViewSpec.swift @@ -1,4 +1,4 @@ -#if canImport(AppKit) +#if canImport(AppKit) && !targetEnvironment(macCatalyst) import Quick import Nimble import ReactiveCocoa diff --git a/ReactiveCocoaTests/AppKit/NSControlSpec.swift b/ReactiveCocoaTests/AppKit/NSControlSpec.swift index 4e89b6a679..e2b03757ea 100644 --- a/ReactiveCocoaTests/AppKit/NSControlSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSControlSpec.swift @@ -1,4 +1,4 @@ -#if canImport(AppKit) +#if canImport(AppKit) && !targetEnvironment(macCatalyst) import Quick import Nimble import ReactiveSwift diff --git a/ReactiveCocoaTests/AppKit/NSImageViewSpec.swift b/ReactiveCocoaTests/AppKit/NSImageViewSpec.swift index c3bd7d9fd7..f24fc66792 100644 --- a/ReactiveCocoaTests/AppKit/NSImageViewSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSImageViewSpec.swift @@ -1,4 +1,4 @@ -#if canImport(AppKit) +#if canImport(AppKit) && !targetEnvironment(macCatalyst) import Quick import Nimble import ReactiveSwift diff --git a/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift b/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift index 03111d1122..7cf9e58b3c 100644 --- a/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSPopUpButtonSpec.swift @@ -1,4 +1,4 @@ -#if canImport(AppKit) +#if canImport(AppKit) && !targetEnvironment(macCatalyst) import Quick import Nimble import ReactiveCocoa diff --git a/ReactiveCocoaTests/AppKit/NSTableViewSpec.swift b/ReactiveCocoaTests/AppKit/NSTableViewSpec.swift index f0a02fe74e..37b582b57f 100644 --- a/ReactiveCocoaTests/AppKit/NSTableViewSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSTableViewSpec.swift @@ -1,4 +1,4 @@ -#if canImport(AppKit) +#if canImport(AppKit) && !targetEnvironment(macCatalyst) import Quick import Nimble import ReactiveCocoa diff --git a/ReactiveCocoaTests/AppKit/NSViewSpec.swift b/ReactiveCocoaTests/AppKit/NSViewSpec.swift index baeddec7c5..6c53ca6919 100644 --- a/ReactiveCocoaTests/AppKit/NSViewSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSViewSpec.swift @@ -1,4 +1,4 @@ -#if canImport(AppKit) +#if canImport(AppKit) && !targetEnvironment(macCatalyst) import Quick import Nimble import ReactiveSwift diff --git a/ReactiveCocoaTests/AppKit/Swift4TestInteroperability.swift b/ReactiveCocoaTests/AppKit/Swift4TestInteroperability.swift index cfce36d8a1..58cb112ca9 100644 --- a/ReactiveCocoaTests/AppKit/Swift4TestInteroperability.swift +++ b/ReactiveCocoaTests/AppKit/Swift4TestInteroperability.swift @@ -1,4 +1,4 @@ -#if canImport(AppKit) +#if canImport(AppKit) && !targetEnvironment(macCatalyst) import AppKit #if swift(>=4.0) diff --git a/ReactiveCocoaTests/CocoaActionSpec.swift b/ReactiveCocoaTests/CocoaActionSpec.swift index 5983a3642b..f46fe2d0dc 100644 --- a/ReactiveCocoaTests/CocoaActionSpec.swift +++ b/ReactiveCocoaTests/CocoaActionSpec.swift @@ -1,4 +1,4 @@ -#if canImport(AppKit) +#if canImport(AppKit) && !targetEnvironment(macCatalyst) import AppKit #endif import ReactiveSwift @@ -9,7 +9,7 @@ import ReactiveCocoa class CocoaActionSpec: QuickSpec { override func spec() { var action: Action! - #if canImport(AppKit) + #if canImport(AppKit) && !targetEnvironment(macCatalyst) var cocoaAction: CocoaAction! #else var cocoaAction: CocoaAction! @@ -23,7 +23,7 @@ class CocoaActionSpec: QuickSpec { expect(cocoaAction.isEnabled.value).toEventually(beTruthy()) } - #if canImport(AppKit) + #if canImport(AppKit) && !targetEnvironment(macCatalyst) it("should be compatible with AppKit") { let control = NSControl(frame: NSZeroRect) control.target = cocoaAction diff --git a/ReactiveCocoaTests/Shared/NSLayoutConstraintSpec.swift b/ReactiveCocoaTests/Shared/NSLayoutConstraintSpec.swift index 5966c90d57..d0b629574c 100644 --- a/ReactiveCocoaTests/Shared/NSLayoutConstraintSpec.swift +++ b/ReactiveCocoaTests/Shared/NSLayoutConstraintSpec.swift @@ -1,6 +1,6 @@ #if canImport(AppKit) || canImport(UIKit) -#if canImport(AppKit) +#if canImport(AppKit) && !targetEnvironment(macCatalyst) import AppKit #elseif canImport(UIKit) import UIKit From 944ead086eb65fa850b22dfcab834fe96516921c Mon Sep 17 00:00:00 2001 From: Javier Soto Date: Mon, 11 May 2020 14:53:30 -0700 Subject: [PATCH 1004/1028] Fix watchOS build error (#3703) * Fix watchOS build error `WKInterfaceButton.title` is gone. * Mark as unavailable. Co-authored-by: Anders Ha --- ReactiveCocoa/Deprecations+Removals.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ReactiveCocoa/Deprecations+Removals.swift b/ReactiveCocoa/Deprecations+Removals.swift index 6932121696..2b9ed5dcd7 100644 --- a/ReactiveCocoa/Deprecations+Removals.swift +++ b/ReactiveCocoa/Deprecations+Removals.swift @@ -16,9 +16,9 @@ extension Reactive where Base: NSObject { #if os(watchOS) import WatchKit extension Reactive where Base: WKInterfaceButton { - @available(*, deprecated, renamed: "title") + @available(*, unavailable, renamed: "title") public var text: BindingTarget { - return title + fatalError() } } #endif From 8be399d8df3ca96e54f94c2a5091ce07c2cfe93c Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Tue, 12 May 2020 00:43:19 +0100 Subject: [PATCH 1005/1028] 10.3.0 --- CHANGELOG.md | 4 ++++ ReactiveCocoa.podspec | 2 +- ReactiveCocoa/Info.plist | 2 +- ReactiveMapKit.podspec | 2 +- ReactiveMapKit/Info.plist | 2 +- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae1afe13b9..09d5b0d32d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # master + +# 10.3.0 1. Don't include code which uses unavailable classes (like `NSSlider`) when targeting macOS Catalyst (#3698, kudos to @nkristek) +1. Fixed watchOS build issues. (#3703, kudos to @JaviSoto) # 10.2.0 1. Update ReactiveSwift to 6.2. @@ -12,6 +15,7 @@ 1. Add reactive extensions for standard WatchKit interface objects. (#3670, kudos to @tdimeco) 1. Fix crashes of `NSObject.signal(for:)` and `NSObject.producer(for:)` with Objective-C enums (#3667, kudos to @gfontenot) +# 10.3.0 # 10.0.0 1. Update ReactiveSwift to 6.0 1. Remove dependency on antitypical/Result diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 5dcc15aa8a..fc5650c185 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "10.2.0" + s.version = "10.3.0" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveCocoa/Info.plist b/ReactiveCocoa/Info.plist index be277c0fe7..a236379794 100644 --- a/ReactiveCocoa/Info.plist +++ b/ReactiveCocoa/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 10.2.0 + 10.3.0 CFBundleSignature ???? CFBundleVersion diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index 680e3f084b..aae31db664 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveMapKit" - s.version = "10.2.0" + s.version = "10.3.0" s.summary = "MapKit bindings for ReactiveCocoa." s.description = <<-DESC Provide MapKit bindings for ReactiveCocoa. ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveMapKit/Info.plist b/ReactiveMapKit/Info.plist index 63568f1c11..9e95939bcd 100644 --- a/ReactiveMapKit/Info.plist +++ b/ReactiveMapKit/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 6.0.1 + 10.3.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass From 261eebecf77583998da7670f011952875ff418e7 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Tue, 16 Jun 2020 14:00:23 +0100 Subject: [PATCH 1006/1028] Remove binding for WKInterfaceActivityRing + CI pipeline fixes (#3706) * Remove binding for WKInterfaceActivityRing * Update Changelog. * Remove WKInterfaceActivityRing.swift from build settings. * Move .swift-version to 5.1. [skip ci] * Move CI to Xcode 11.4. * Ditto * Update build destinations. * Disable the test case due to unrelated NSStackView KVO issue. * Adjust the flaky async tests so they don't block the main runloop. * Update podspecs on supported Swift versions. * PR tests in GitHub actions * Fix tests for Mac Catalyst. * Disable UISearchBarSpec for now. --- .github/workflows/master.yml | 53 ++++++++++++++++ .github/workflows/test.yml | 62 +++++++++++++++++++ .swift-version | 2 +- .travis.yml | 50 --------------- CHANGELOG.md | 4 ++ ReactiveCocoa.podspec | 2 +- ReactiveCocoa.xcodeproj/project.pbxproj | 20 ++++-- .../xcschemes/ReactiveCocoa-iOS.xcscheme | 27 ++++---- .../xcschemes/ReactiveCocoa-macOS.xcscheme | 27 ++++---- ReactiveCocoa/UIKit/iOS/UISearchBar.swift | 4 ++ .../WatchKit/WKInterfaceActivityRing.swift | 15 ----- .../KeyValueObservingSpec+Swift4.swift | 22 +++---- .../KeyValueObservingSpec.swift | 22 +++---- .../QueueScheduler+Factory.swift | 19 ++++++ ReactiveMapKit.podspec | 2 +- 15 files changed, 203 insertions(+), 128 deletions(-) create mode 100644 .github/workflows/master.yml create mode 100644 .github/workflows/test.yml delete mode 100644 .travis.yml delete mode 100644 ReactiveCocoa/WatchKit/WKInterfaceActivityRing.swift create mode 100644 ReactiveCocoaTests/QueueScheduler+Factory.swift diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml new file mode 100644 index 0000000000..51294cef57 --- /dev/null +++ b/.github/workflows/master.yml @@ -0,0 +1,53 @@ +on: + push: + branches: + - master + pull_request: + types: [labeled] + branches: + - master +name: Verification +jobs: + carthage: + if: ${{ github.event_name == 'push' || ( github.event_name == 'pull_request' && github.event.label.name == 'ci:verify' ) }} + runs-on: macos-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Recover cached dependencies + uses: actions/cache@v1 + id: dependency-cache + with: + path: ~/Library/Caches/org.carthage.CarthageKit + key: 4-carthage-verification-${{ runner.os }}-${{ hashFiles('Cartfile.resolved') }} + - name: Carthage verification + run: | + carthage checkout + carthage build --cache-builds --no-skip-current + swiftpm-macos: + if: ${{ github.event_name == 'push' || ( github.event_name == 'pull_request' && github.event.label.name == 'ci:verify' ) }} + name: SwiftPM macOS + runs-on: macos-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Pull dependencies + run: | + swift package resolve + - name: Test via SwiftPM + run: | + swift --version + swift build + cocoapods: + if: ${{ github.event_name == 'push' || ( github.event_name == 'pull_request' && github.event.label.name == 'ci:verify' ) }} + runs-on: macos-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: CocoaPods verification + run: | + # To work around the lint error: "ERROR | swift: Specification `ReactiveCocoa` specifies an inconsistent `swift_version` (`4.1`) compared to the one present in your `.swift-version` file (`4.1.2`). Please remove the `.swift-version` file which is now deprecated and only use the `swift_version` attribute within your podspec." + # `.swift-version` is for swiftenv, not for CocoaPods, so we can't remove the file as suggested. + rm .swift-version + pod repo update + pod lib lint --use-libraries \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000000..9a8cc61492 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,62 @@ +on: pull_request +name: Test +jobs: + test: + name: Test + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + destination: [macOS, macCatalyst, iOS, tvOS, watchOS] + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Recover cached dependencies + uses: actions/cache@v1 + id: dependency-cache + with: + path: Carthage/Checkouts + key: carthage-${{ hashFiles('Cartfile.resolved') }} + - name: Pull dependencies + if: steps.dependency-cache.outputs.cache-hit != 'true' + run: | + carthage checkout + - name: Test via xcodebuild + run: | + ACTION=test + DESTINATION=unknown + SCHEME=unknown + case "${{ matrix.destination }}" in + "iOS") + DESTINATION="platform=iOS Simulator,name=iPhone 11 Pro" + SCHEME=ReactiveCocoa-iOS + ;; + "tvOS") + DESTINATION="platform=tvOS Simulator,name=Apple TV 4K" + SCHEME=ReactiveCocoa-tvOS + ;; + "watchOS") + ACTION=build + DESTINATION="platform=watchOS Simulator,name=Apple Watch Series 5 - 44mm" + SCHEME=ReactiveCocoa-watchOS + ;; + "macCatalyst") + DESTINATION="platform=macOS,variant=Mac Catalyst" + SCHEME=ReactiveCocoa-iOS + ;; + "macOS") + DESTINATION="platform=macOS,arch=x86_64" + SCHEME=ReactiveCocoa-macOS + ;; + *) + echo "Unknown destination." + exit 1 + ;; + esac + xcodebuild clean ${ACTION} \ + -destination "${DESTINATION}" \ + -scheme ${SCHEME} \ + -workspace ReactiveCocoa.xcworkspace \ + CODE_SIGN_IDENTITY="" \ + CODE_SIGNING_REQUIRED=NO \ + ONLY_ACTIVE_ARCH=YES diff --git a/.swift-version b/.swift-version index 819e07a224..a75b92f1ed 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -5.0 +5.1 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 4e90f06adf..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,50 +0,0 @@ -language: objective-c -osx_image: xcode10.2 -before_install: true -install: true -branches: - only: - - master - - /^(\d+\.\d+\.\d+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/ - - /^hotfix-(\d+\.\d+\.\d+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/ -cache: - directories: - - $HOME/Library/Caches/org.carthage.CarthageKit/dependencies - - Carthage/Build -jobs: - include: - - stage: unit tests - osx_image: xcode10.2 - script: - - XCODE_SCHEME=ReactiveCocoa-macOS - XCODE_SDK=macosx - XCODE_ACTION="build-for-testing test-without-building" - XCODE_DESTINATION="arch=x86_64" - XCODE_PLAYGROUND_TARGET="x86_64-apple-macosx10.10" - PLAYGROUND="ReactiveCocoa-macOS.playground" - script/build - - XCODE_SCHEME=ReactiveCocoa-iOS - XCODE_SDK=iphonesimulator - XCODE_ACTION="build-for-testing test-without-building" - XCODE_DESTINATION="platform=iOS Simulator,name=iPhone 6s" - script/build - - XCODE_SCHEME=ReactiveCocoa-tvOS - XCODE_SDK=appletvsimulator - XCODE_ACTION="build-for-testing test-without-building" - XCODE_DESTINATION="platform=tvOS Simulator,name=Apple TV" - script/build - - XCODE_SCHEME=ReactiveCocoa-watchOS - XCODE_SDK=watchsimulator - XCODE_ACTION=build - XCODE_DESTINATION="platform=watchOS Simulator,name=Apple Watch Series 3 - 38mm" - script/build - - stage: package manager tests - install: gem update cocoapods - script: - - pod repo update - - pod lib lint ReactiveCocoa.podspec --use-libraries - - pod lib lint ReactiveMapKit.podspec --use-libraries --include-podspecs=ReactiveCocoa.podspec - env: - - JOB=PODSPEC - - script: carthage build --cache-builds --no-skip-current - - script: swift build diff --git a/CHANGELOG.md b/CHANGELOG.md index 09d5b0d32d..3ee42c7984 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # master +1. Binding for `WKInterfaceActivityRing` has been removed, since it causes watchOS builds to be linked with HealthKit, leading to potential App Store rejections for apps who do not use HealthKit. (#3706) + + Users who depend on the `WKInterfaceActivityRing` binding [should consider replicating them in their projects instead](https://github.com/ReactiveCocoa/ReactiveCocoa/blob/10.3.0/ReactiveCocoa/WatchKit/WKInterfaceActivityRing.swift). + # 10.3.0 1. Don't include code which uses unavailable classes (like `NSSlider`) when targeting macOS Catalyst (#3698, kudos to @nkristek) 1. Fixed watchOS build issues. (#3703, kudos to @JaviSoto) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index fc5650c185..d9b85ab7bb 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -27,5 +27,5 @@ Pod::Spec.new do |s| s.dependency 'ReactiveSwift', '~> 6.2' s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "$(inherited) -suppress-warnings" } - s.swift_version = '5.0' + s.swift_versions = ['5.0', '5.1', '5.2'] end diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 4e9ed97c3b..00027e6970 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -52,6 +52,9 @@ 53AC46CC1DD6F97400C799E1 /* UISlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53AC46CB1DD6F97400C799E1 /* UISlider.swift */; }; 53AC46CF1DD6FC0000C799E1 /* UISliderSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53AC46CE1DD6FC0000C799E1 /* UISliderSpec.swift */; }; 57A4D20A1BA13D7A00F7D4B1 /* ReactiveCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5B76DE402498EDDC00E8B4F3 /* QueueScheduler+Factory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B76DE3F2498EDDC00E8B4F3 /* QueueScheduler+Factory.swift */; }; + 5B76DE412498EDDC00E8B4F3 /* QueueScheduler+Factory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B76DE3F2498EDDC00E8B4F3 /* QueueScheduler+Factory.swift */; }; + 5B76DE422498EDDC00E8B4F3 /* QueueScheduler+Factory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B76DE3F2498EDDC00E8B4F3 /* QueueScheduler+Factory.swift */; }; 654DE7B02205F9DE0048FE14 /* ReactiveCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D04725EA19E49ED7006002AA /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 654DE7B22205FA0A0048FE14 /* ReactiveCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D047260C19E49F82006002AA /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 654DE7B32205FA200048FE14 /* ReactiveCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -344,7 +347,6 @@ E92142902284A4EF007E412D /* WKInterfaceTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E921428F2284A4EF007E412D /* WKInterfaceTimer.swift */; }; E92142922284AA64007E412D /* WKInterfaceSwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = E92142912284AA64007E412D /* WKInterfaceSwitch.swift */; }; E92142942284ABDF007E412D /* WKInterfaceSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E92142932284ABDF007E412D /* WKInterfaceSlider.swift */; }; - E92142962284AD19007E412D /* WKInterfaceActivityRing.swift in Sources */ = {isa = PBXBuildFile; fileRef = E92142952284AD19007E412D /* WKInterfaceActivityRing.swift */; }; E92142982284AEF0007E412D /* WKInterfaceGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = E92142972284AEF0007E412D /* WKInterfaceGroup.swift */; }; E921429A2284AF99007E412D /* WKInterfaceSeparator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E92142992284AF99007E412D /* WKInterfaceSeparator.swift */; }; E921429C2284B0A8007E412D /* WKInterfaceImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E921429B2284B0A8007E412D /* WKInterfaceImage.swift */; }; @@ -494,6 +496,7 @@ 57A4D2451BA13F9700F7D4B1 /* tvOS-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Base.xcconfig"; sourceTree = ""; }; 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Framework.xcconfig"; sourceTree = ""; }; 57A4D2471BA13F9700F7D4B1 /* tvOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-StaticLibrary.xcconfig"; sourceTree = ""; }; + 5B76DE3F2498EDDC00E8B4F3 /* QueueScheduler+Factory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "QueueScheduler+Factory.swift"; sourceTree = ""; }; 7A8BA0F91FCC86FC003241C7 /* NSTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSTextView.swift; sourceTree = ""; }; 7DFBED031CDB8C9500EE435B /* ReactiveCocoaTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveCocoaTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 834DE1001E4109750099F4E5 /* NSImageViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSImageViewSpec.swift; sourceTree = ""; }; @@ -652,7 +655,6 @@ E921428F2284A4EF007E412D /* WKInterfaceTimer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKInterfaceTimer.swift; sourceTree = ""; }; E92142912284AA64007E412D /* WKInterfaceSwitch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKInterfaceSwitch.swift; sourceTree = ""; }; E92142932284ABDF007E412D /* WKInterfaceSlider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKInterfaceSlider.swift; sourceTree = ""; }; - E92142952284AD19007E412D /* WKInterfaceActivityRing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKInterfaceActivityRing.swift; sourceTree = ""; }; E92142972284AEF0007E412D /* WKInterfaceGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKInterfaceGroup.swift; sourceTree = ""; }; E92142992284AF99007E412D /* WKInterfaceSeparator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKInterfaceSeparator.swift; sourceTree = ""; }; E921429B2284B0A8007E412D /* WKInterfaceImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKInterfaceImage.swift; sourceTree = ""; }; @@ -1118,6 +1120,7 @@ D03766B119EDA60000A782A9 /* test-data.json */, BFA6B94A1A76044800C846D1 /* SignalProducerNimbleMatchers.swift */, D04725FB19E49ED7006002AA /* Info.plist */, + 5B76DE3F2498EDDC00E8B4F3 /* QueueScheduler+Factory.swift */, ); name = "Supporting Files"; sourceTree = ""; @@ -1200,7 +1203,6 @@ E921428F2284A4EF007E412D /* WKInterfaceTimer.swift */, E92142912284AA64007E412D /* WKInterfaceSwitch.swift */, E92142932284ABDF007E412D /* WKInterfaceSlider.swift */, - E92142952284AD19007E412D /* WKInterfaceActivityRing.swift */, E921429B2284B0A8007E412D /* WKInterfaceImage.swift */, E92142A8228552CC007E412D /* WKInterfaceMovie.swift */, E92142AA228553C5007E412D /* WKInterfaceInlineMovie.swift */, @@ -1818,6 +1820,7 @@ 4ABEFE291DCFCFA90066A8C2 /* UICollectionViewSpec.swift in Sources */, 9A54A2131DDF5B4D001739B3 /* InterceptingPerformanceTests.swift in Sources */, BEE020661D637B0000DF261F /* TestError.swift in Sources */, + 5B76DE422498EDDC00E8B4F3 /* QueueScheduler+Factory.swift in Sources */, 9A1D06531D93EA7E00ACF44C /* UITextFieldSpec.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1904,7 +1907,6 @@ E92142A22284B64E007E412D /* WKInterfaceController.swift in Sources */, E92142AB228553C5007E412D /* WKInterfaceInlineMovie.swift in Sources */, E921428E2284A44F007E412D /* WKInterfaceDate.swift in Sources */, - E92142962284AD19007E412D /* WKInterfaceActivityRing.swift in Sources */, 9AF0EA771D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, 9A9037511ED61C6300345D62 /* ReactiveSwift+Lifetime.swift in Sources */, E92142902284A4EF007E412D /* WKInterfaceTimer.swift in Sources */, @@ -1987,6 +1989,7 @@ 3B30EE8C1E7BE529007CC8EF /* DeprecationsSpec.swift in Sources */, CD42C69B1E951F6900AA9504 /* ReactiveCocoaTestsConfiguration.swift in Sources */, 9AA6A1E51F11F9B000CA2257 /* KeyValueObservingSpec+Swift4.swift in Sources */, + 5B76DE402498EDDC00E8B4F3 /* QueueScheduler+Factory.swift in Sources */, 9A1E72BA1D4DE96500CC20C3 /* KeyValueObservingSpec.swift in Sources */, 4ABEFE2E1DCFD01F0066A8C2 /* NSTableViewSpec.swift in Sources */, ); @@ -2104,6 +2107,7 @@ A9EB3D291E94F3D3002A9BCC /* UITabBarItemSpec.swift in Sources */, 538DCB7E1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift in Sources */, 9A1D06521D93EA7E00ACF44C /* UITextFieldSpec.swift in Sources */, + 5B76DE412498EDDC00E8B4F3 /* QueueScheduler+Factory.swift in Sources */, 4EE637342090EFDF00ECD02A /* UIViewControllerSpec.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2236,6 +2240,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; buildSettings = { + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; @@ -2248,6 +2253,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; buildSettings = { + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; @@ -2260,6 +2266,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; buildSettings = { + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; @@ -2272,6 +2279,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; buildSettings = { + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; @@ -2711,6 +2719,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; buildSettings = { + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; SWIFT_OBJC_BRIDGING_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h"; @@ -2721,6 +2730,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; buildSettings = { + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; SWIFT_OBJC_BRIDGING_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h"; @@ -2791,6 +2801,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; buildSettings = { + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; SWIFT_OBJC_BRIDGING_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h"; @@ -2861,6 +2872,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; buildSettings = { + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; SWIFT_OBJC_BRIDGING_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h"; diff --git a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme index ecd1e68d6b..df877beeb8 100644 --- a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme +++ b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme @@ -97,6 +97,15 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> + + + + @@ -107,6 +116,11 @@ BlueprintName = "ReactiveCocoa-iOSTests" ReferencedContainer = "container:ReactiveCocoa.xcodeproj"> + + + + @@ -119,17 +133,6 @@ - - - - - -
    - - + + + + @@ -121,6 +130,11 @@ BlueprintName = "ReactiveCocoa-macOSTests" ReferencedContainer = "container:ReactiveCocoa.xcodeproj"> + + + + @@ -133,17 +147,6 @@ - - - - - - - - , UISear extension Reactive where Base: UISearchBar { private var proxy: SearchBarDelegateProxy { + // TODO: Mac Catalyst UISearchBarDelegate issue + // Related: https://github.com/ReactiveX/RxSwift/issues/2161 + _ = DelegateProxy.self + return .proxy(for: base, setter: #selector(setter: base.delegate), getter: #selector(getter: base.delegate)) diff --git a/ReactiveCocoa/WatchKit/WKInterfaceActivityRing.swift b/ReactiveCocoa/WatchKit/WKInterfaceActivityRing.swift deleted file mode 100644 index aec47f71d1..0000000000 --- a/ReactiveCocoa/WatchKit/WKInterfaceActivityRing.swift +++ /dev/null @@ -1,15 +0,0 @@ -#if canImport(WatchKit) -import ReactiveSwift -import WatchKit -import HealthKit - -@available(watchOSApplicationExtension 2.2, *) -extension Reactive where Base: WKInterfaceActivityRing { - /// Sets the summary of the activity ring. - /// - /// - Parameter animated: Whether updates are animated. - public func activitySummary(animated: Bool) -> BindingTarget { - return makeBindingTarget { $0.setActivitySummary($1, animated: animated) } - } -} -#endif diff --git a/ReactiveCocoaTests/KeyValueObservingSpec+Swift4.swift b/ReactiveCocoaTests/KeyValueObservingSpec+Swift4.swift index 4a6e643aa4..a22630fc8e 100644 --- a/ReactiveCocoaTests/KeyValueObservingSpec+Swift4.swift +++ b/ReactiveCocoaTests/KeyValueObservingSpec+Swift4.swift @@ -521,12 +521,7 @@ fileprivate class KeyValueObservingSwift4SpecConfiguration: QuickConfiguration { } it("attach observers") { - let deliveringObserver: QueueScheduler - if #available(*, OSX 10.10) { - deliveringObserver = QueueScheduler(name: "\(#file):\(#line)") - } else { - deliveringObserver = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) - } + let deliveringObserver = QueueScheduler.makeForTesting() var atomicCounter = Int64(0) @@ -566,17 +561,11 @@ fileprivate class KeyValueObservingSwift4SpecConfiguration: QuickConfiguration { } it("async disposal of signal with in-flight changes") { - let otherScheduler: QueueScheduler + let otherScheduler = QueueScheduler.makeForTesting() var token = Optional(Lifetime.Token()) let lifetime = Lifetime(token!) - if #available(*, OSX 10.10) { - otherScheduler = QueueScheduler(name: "\(#file):\(#line)") - } else { - otherScheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) - } - let replayProducer = context.changes(testObject) .map { $0 % 2 == 0 } .observe(on: otherScheduler) @@ -595,7 +584,7 @@ fileprivate class KeyValueObservingSwift4SpecConfiguration: QuickConfiguration { } if index == half { - iterationQueue.async(flags: .barrier) { + iterationQueue.async { token = nil expect(replayProducer.last()).toNot(beNil()) } @@ -603,7 +592,10 @@ fileprivate class KeyValueObservingSwift4SpecConfiguration: QuickConfiguration { } iterationQueue.resume() - iterationQueue.sync(flags: .barrier, execute: {}) + + waitUntil(timeout: 3.0) { done in + iterationQueue.async(flags: .barrier, execute: done) + } } } } diff --git a/ReactiveCocoaTests/KeyValueObservingSpec.swift b/ReactiveCocoaTests/KeyValueObservingSpec.swift index eba2de54cd..88b8c35a20 100644 --- a/ReactiveCocoaTests/KeyValueObservingSpec.swift +++ b/ReactiveCocoaTests/KeyValueObservingSpec.swift @@ -571,12 +571,7 @@ fileprivate class KeyValueObservingSpecConfiguration: QuickConfiguration { } it("attach observers") { - let deliveringObserver: QueueScheduler - if #available(*, OSX 10.10) { - deliveringObserver = QueueScheduler(name: "\(#file):\(#line)") - } else { - deliveringObserver = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) - } + let deliveringObserver = QueueScheduler.makeForTesting() var atomicCounter = Int64(0) @@ -624,17 +619,11 @@ fileprivate class KeyValueObservingSpecConfiguration: QuickConfiguration { // Direct port of https://github.com/ReactiveCocoa/ReactiveObjC/blob/3.1.0/ReactiveObjCTests/RACKVOProxySpec.m#L196 it("async disposal of signal with in-flight changes") { - let otherScheduler: QueueScheduler + let otherScheduler = QueueScheduler.makeForTesting() var token = Optional(Lifetime.Token()) let lifetime = Lifetime(token!) - if #available(*, OSX 10.10) { - otherScheduler = QueueScheduler(name: "\(#file):\(#line)") - } else { - otherScheduler = QueueScheduler(queue: DispatchQueue(label: "\(#file):\(#line)")) - } - let replayProducer = context.changes(testObject) .map { ($0 as! NSNumber).intValue } .map { $0 % 2 == 0 } @@ -654,7 +643,7 @@ fileprivate class KeyValueObservingSpecConfiguration: QuickConfiguration { } if index == half { - iterationQueue.async(flags: .barrier) { + iterationQueue.async { token = nil expect(replayProducer.last()).toNot(beNil()) } @@ -662,7 +651,10 @@ fileprivate class KeyValueObservingSpecConfiguration: QuickConfiguration { } iterationQueue.resume() - iterationQueue.sync(flags: .barrier, execute: {}) + + waitUntil(timeout: 3.0) { done in + iterationQueue.async(flags: .barrier, execute: done) + } } } } diff --git a/ReactiveCocoaTests/QueueScheduler+Factory.swift b/ReactiveCocoaTests/QueueScheduler+Factory.swift new file mode 100644 index 0000000000..c56a33242e --- /dev/null +++ b/ReactiveCocoaTests/QueueScheduler+Factory.swift @@ -0,0 +1,19 @@ +import ReactiveSwift +import Foundation + +extension QueueScheduler { + static func makeForTesting(file: String = #file, line: UInt = #line) -> QueueScheduler { + let file = URL(string: file)?.lastPathComponent ?? "" + let label = "reactiveswift:\(file):\(line)" + + #if targetEnvironment(macCatalyst) + return QueueScheduler() + #else + if #available(OSX 10.10, iOS 8.0, *) { + return QueueScheduler() + } else { + return QueueScheduler(queue: DispatchQueue(label: label)) + } + #endif + } +} diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index aae31db664..49ab9ff1a2 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -19,5 +19,5 @@ Pod::Spec.new do |s| s.dependency 'ReactiveCocoa', "#{s.version}" s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "$(inherited) -suppress-warnings" } - s.swift_version = '5.0' + s.swift_versions = ['5.0', '5.1', '5.2'] end From b3d48751cf06060de602f151a886def4c7f4c73b Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Tue, 16 Jun 2020 14:43:09 +0100 Subject: [PATCH 1007/1028] 11.0.0 --- CHANGELOG.md | 3 ++- ReactiveCocoa.podspec | 2 +- ReactiveCocoa/Info.plist | 2 +- ReactiveMapKit.podspec | 2 +- ReactiveMapKit/Info.plist | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ee42c7984..ecfaf48645 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master -1. Binding for `WKInterfaceActivityRing` has been removed, since it causes watchOS builds to be linked with HealthKit, leading to potential App Store rejections for apps who do not use HealthKit. (#3706) + +# 11.0.01. Binding for `WKInterfaceActivityRing` has been removed, since it causes watchOS builds to be linked with HealthKit, leading to potential App Store rejections for apps who do not use HealthKit. (#3706) Users who depend on the `WKInterfaceActivityRing` binding [should consider replicating them in their projects instead](https://github.com/ReactiveCocoa/ReactiveCocoa/blob/10.3.0/ReactiveCocoa/WatchKit/WKInterfaceActivityRing.swift). diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index d9b85ab7bb..d115061c1c 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "10.3.0" + s.version = "11.0.0" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveCocoa/Info.plist b/ReactiveCocoa/Info.plist index a236379794..e3a570baf3 100644 --- a/ReactiveCocoa/Info.plist +++ b/ReactiveCocoa/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 10.3.0 + 11.0.0 CFBundleSignature ???? CFBundleVersion diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index 49ab9ff1a2..9d4d67f817 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveMapKit" - s.version = "10.3.0" + s.version = "11.0.0" s.summary = "MapKit bindings for ReactiveCocoa." s.description = <<-DESC Provide MapKit bindings for ReactiveCocoa. ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveMapKit/Info.plist b/ReactiveMapKit/Info.plist index 9e95939bcd..c02e6d4bfa 100644 --- a/ReactiveMapKit/Info.plist +++ b/ReactiveMapKit/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 10.3.0 + 11.0.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass From 6d1b7f953604038707eca7204bd43c97bf102397 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 7 Aug 2020 16:48:15 +0100 Subject: [PATCH 1008/1028] Bump SPM swift-tools-version to 5.2 (#3710) * Add ReactiveCocoaObjC as a product * Bump swift-tools-version to 5.2 For the same reason as this was done in ReactiveSwift: so clients aren't forced to pull in test dependencies. * Add swiftLanguageVersions * Update .swift-version to 5.2 --- .swift-version | 2 +- Package.swift | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.swift-version b/.swift-version index a75b92f1ed..ef425ca982 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -5.1 +5.2 diff --git a/Package.swift b/Package.swift index 1e6e9a521b..bfa7931213 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.0 +// swift-tools-version:5.2 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -39,5 +39,6 @@ let package = Package( "Nimble" ], path: "ReactiveCocoaTests"), - ] + ], + swiftLanguageVersions: [.v5] ) From 0f5b94cce4ae4209f4b754f72f31f4d41b6cc141 Mon Sep 17 00:00:00 2001 From: Petr Pavlik Date: Tue, 29 Sep 2020 20:03:16 +0200 Subject: [PATCH 1009/1028] bump package target to iOS 9 and update testing packages (#3712) * Update swift package quick dependency to 3.0.0+ * bump min deployment target to iOS 9 and update Nimble --- Package.resolved | 30 ++++++------------------------ Package.swift | 4 ++-- 2 files changed, 8 insertions(+), 26 deletions(-) diff --git a/Package.resolved b/Package.resolved index a85c0f1d74..15399c269a 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,31 +1,13 @@ { "object": { "pins": [ - { - "package": "CwlCatchException", - "repositoryURL": "https://github.com/mattgallagher/CwlCatchException.git", - "state": { - "branch": null, - "revision": "7cd2f8cacc4d22f21bc0b2309c3b18acf7957b66", - "version": "1.2.0" - } - }, - { - "package": "CwlPreconditionTesting", - "repositoryURL": "https://github.com/mattgallagher/CwlPreconditionTesting.git", - "state": { - "branch": null, - "revision": "c228db5d2ad1b01ebc84435e823e6cca4e3db98b", - "version": "1.2.0" - } - }, { "package": "Nimble", "repositoryURL": "https://github.com/Quick/Nimble.git", "state": { "branch": null, - "revision": "b02b00b30b6353632aa4a5fb6124f8147f7140c0", - "version": "8.0.5" + "revision": "7a46a5fc86cb917f69e3daf79fcb045283d8f008", + "version": "8.1.2" } }, { @@ -33,8 +15,8 @@ "repositoryURL": "https://github.com/Quick/Quick.git", "state": { "branch": null, - "revision": "33682c2f6230c60614861dfc61df267e11a1602f", - "version": "2.2.0" + "revision": "0038bcbab4292f3b028632556507c124e5ba69f3", + "version": "3.0.0" } }, { @@ -42,8 +24,8 @@ "repositoryURL": "https://github.com/ReactiveCocoa/ReactiveSwift", "state": { "branch": null, - "revision": "e8703715afe26a8efbb2ecfdb3454d648f58d691", - "version": "6.2.0" + "revision": "3f4351d04115fd8797802d9b2d17b812cd761602", + "version": "6.3.0" } } ] diff --git a/Package.swift b/Package.swift index bfa7931213..0c66373b78 100644 --- a/Package.swift +++ b/Package.swift @@ -5,14 +5,14 @@ import PackageDescription let package = Package( name: "ReactiveCocoa", platforms: [ - .macOS(.v10_10), .iOS(.v8), .tvOS(.v9), .watchOS(.v2) + .macOS(.v10_10), .iOS(.v9), .tvOS(.v9), .watchOS(.v2) ], products: [ .library(name: "ReactiveCocoa", targets: ["ReactiveCocoa"]) ], dependencies: [ .package(url: "https://github.com/ReactiveCocoa/ReactiveSwift", from: "6.2.0"), - .package(url: "https://github.com/Quick/Quick.git", from: "2.0.0"), + .package(url: "https://github.com/Quick/Quick.git", from: "3.0.0"), .package(url: "https://github.com/Quick/Nimble.git", from: "8.0.0"), ], targets: [ From cca8bdc26d10a889b8bec3cc2167544df730953e Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Tue, 29 Sep 2020 19:04:51 +0100 Subject: [PATCH 1010/1028] 11.1.0 --- CHANGELOG.md | 1 + ReactiveCocoa.podspec | 2 +- ReactiveCocoa/Info.plist | 2 +- ReactiveMapKit.podspec | 2 +- ReactiveMapKit/Info.plist | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecfaf48645..778e5e77a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master +# 11.1.0 # 11.0.01. Binding for `WKInterfaceActivityRing` has been removed, since it causes watchOS builds to be linked with HealthKit, leading to potential App Store rejections for apps who do not use HealthKit. (#3706) Users who depend on the `WKInterfaceActivityRing` binding [should consider replicating them in their projects instead](https://github.com/ReactiveCocoa/ReactiveCocoa/blob/10.3.0/ReactiveCocoa/WatchKit/WKInterfaceActivityRing.swift). diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index d115061c1c..e4039bc44a 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "11.0.0" + s.version = "11.1.0" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveCocoa/Info.plist b/ReactiveCocoa/Info.plist index e3a570baf3..85935b63e1 100644 --- a/ReactiveCocoa/Info.plist +++ b/ReactiveCocoa/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 11.0.0 + 11.1.0 CFBundleSignature ???? CFBundleVersion diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index 9d4d67f817..ab63333ebc 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveMapKit" - s.version = "11.0.0" + s.version = "11.1.0" s.summary = "MapKit bindings for ReactiveCocoa." s.description = <<-DESC Provide MapKit bindings for ReactiveCocoa. ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveMapKit/Info.plist b/ReactiveMapKit/Info.plist index c02e6d4bfa..b45445606a 100644 --- a/ReactiveMapKit/Info.plist +++ b/ReactiveMapKit/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 11.0.0 + 11.1.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass From d45b06a6c2786e86b4287936ed935abe5bad88ed Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Tue, 16 Mar 2021 11:06:22 +0000 Subject: [PATCH 1011/1028] Requires RAS >= 6.6.0. Disable some test cases in Mac Catalyst. (#3722) * Requires RAS >= 6.6.0. Bump version to 11.2.0. * Disable first responder test cases for Mac Catalyst. * Update also min deployment target in the Xcode project. * Update changelog. * Update QueueScheduler factory for tests. * Run carthage tests with --use-xcframeworks * Upgrade to Nimble 9.0 and Quick 3.0. * Disable a check in MKMapView tests in Mac Catalyst. --- .github/workflows/master.yml | 2 +- CHANGELOG.md | 2 ++ Cartfile | 2 +- Cartfile.private | 4 +-- Cartfile.resolved | 6 ++--- Carthage/Checkouts/Nimble | 2 +- Carthage/Checkouts/Quick | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- Package.resolved | 26 ++++++++++++++++--- Package.swift | 4 +-- ReactiveCocoa.podspec | 8 +++--- ReactiveCocoa.xcodeproj/project.pbxproj | 12 ++++++--- .../KeyValueObservingSpec+Swift4.swift | 4 +-- .../KeyValueObservingSpec.swift | 4 +-- .../QueueScheduler+Factory.swift | 8 ++---- .../SignalProducerNimbleMatchers.swift | 4 +-- .../UIKit/UIRefreshControlSpec.swift | 2 +- .../UIKit/UIResponderSpec.swift | 4 ++- .../UIKit/UITextFieldSpec.swift | 2 ++ ReactiveMapKitTests/MKMapViewSpec.swift | 2 +- 20 files changed, 63 insertions(+), 39 deletions(-) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 51294cef57..ff6e4139c0 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -23,7 +23,7 @@ jobs: - name: Carthage verification run: | carthage checkout - carthage build --cache-builds --no-skip-current + carthage build --cache-builds --no-skip-current --use-xcframeworks swiftpm-macos: if: ${{ github.event_name == 'push' || ( github.event_name == 'pull_request' && github.event.label.name == 'ci:verify' ) }} name: SwiftPM macOS diff --git a/CHANGELOG.md b/CHANGELOG.md index 778e5e77a1..ecf1c3fbed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # master +1. Requires ReactiveSwift 6.6.0 or later. +1. The minimum deployment target for iOS has been raised consistently to 9.0 across all integration mediums. # 11.1.0 # 11.0.01. Binding for `WKInterfaceActivityRing` has been removed, since it causes watchOS builds to be linked with HealthKit, leading to potential App Store rejections for apps who do not use HealthKit. (#3706) diff --git a/Cartfile b/Cartfile index fe0cec3449..18d3112e54 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" ~> 6.2 +github "ReactiveCocoa/ReactiveSwift" ~> 6.6 diff --git a/Cartfile.private b/Cartfile.private index 7d29fbe4ec..9399a7123d 100644 --- a/Cartfile.private +++ b/Cartfile.private @@ -1,3 +1,3 @@ github "jspahrsummers/xcconfigs" "3d9d996" -github "Quick/Quick" ~> 2.0 -github "Quick/Nimble" ~> 8.0.1 +github "Quick/Quick" ~> 3.0 +github "Quick/Nimble" ~> 9.0 diff --git a/Cartfile.resolved b/Cartfile.resolved index 034472dbc5..b390301e12 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,4 +1,4 @@ -github "Quick/Nimble" "v8.0.5" -github "Quick/Quick" "v2.2.0" -github "ReactiveCocoa/ReactiveSwift" "6.2.0" +github "Quick/Nimble" "v9.0.0" +github "Quick/Quick" "v3.1.2" +github "ReactiveCocoa/ReactiveSwift" "6.6.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" diff --git a/Carthage/Checkouts/Nimble b/Carthage/Checkouts/Nimble index b02b00b30b..e491a67313 160000 --- a/Carthage/Checkouts/Nimble +++ b/Carthage/Checkouts/Nimble @@ -1 +1 @@ -Subproject commit b02b00b30b6353632aa4a5fb6124f8147f7140c0 +Subproject commit e491a6731307bb23783bf664d003be9b2fa59ab5 diff --git a/Carthage/Checkouts/Quick b/Carthage/Checkouts/Quick index 33682c2f62..8cce6acd38 160000 --- a/Carthage/Checkouts/Quick +++ b/Carthage/Checkouts/Quick @@ -1 +1 @@ -Subproject commit 33682c2f6230c60614861dfc61df267e11a1602f +Subproject commit 8cce6acd38f965f5baa3167b939f86500314022b diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index e8703715af..4a8e1e4a68 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit e8703715afe26a8efbb2ecfdb3454d648f58d691 +Subproject commit 4a8e1e4a683e96daf658f68132dae781738bc862 diff --git a/Package.resolved b/Package.resolved index 15399c269a..2e07d8d87e 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,13 +1,31 @@ { "object": { "pins": [ + { + "package": "CwlCatchException", + "repositoryURL": "https://github.com/mattgallagher/CwlCatchException.git", + "state": { + "branch": null, + "revision": "f809deb30dc5c9d9b78c872e553261a61177721a", + "version": "2.0.0" + } + }, + { + "package": "CwlPreconditionTesting", + "repositoryURL": "https://github.com/mattgallagher/CwlPreconditionTesting.git", + "state": { + "branch": null, + "revision": "02b7a39a99c4da27abe03cab2053a9034379639f", + "version": "2.0.0" + } + }, { "package": "Nimble", "repositoryURL": "https://github.com/Quick/Nimble.git", "state": { "branch": null, - "revision": "7a46a5fc86cb917f69e3daf79fcb045283d8f008", - "version": "8.1.2" + "revision": "e491a6731307bb23783bf664d003be9b2fa59ab5", + "version": "9.0.0" } }, { @@ -24,8 +42,8 @@ "repositoryURL": "https://github.com/ReactiveCocoa/ReactiveSwift", "state": { "branch": null, - "revision": "3f4351d04115fd8797802d9b2d17b812cd761602", - "version": "6.3.0" + "revision": "4a8e1e4a683e96daf658f68132dae781738bc862", + "version": "6.6.0" } } ] diff --git a/Package.swift b/Package.swift index 0c66373b78..ec07d485fd 100644 --- a/Package.swift +++ b/Package.swift @@ -11,9 +11,9 @@ let package = Package( .library(name: "ReactiveCocoa", targets: ["ReactiveCocoa"]) ], dependencies: [ - .package(url: "https://github.com/ReactiveCocoa/ReactiveSwift", from: "6.2.0"), + .package(url: "https://github.com/ReactiveCocoa/ReactiveSwift", from: "6.6.0"), .package(url: "https://github.com/Quick/Quick.git", from: "3.0.0"), - .package(url: "https://github.com/Quick/Nimble.git", from: "8.0.0"), + .package(url: "https://github.com/Quick/Nimble.git", from: "9.0.0"), ], targets: [ .target( diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index e4039bc44a..e797654b8f 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "11.1.0" + s.version = "11.2.0" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. @@ -10,7 +10,7 @@ Pod::Spec.new do |s| s.author = "ReactiveCocoa" s.osx.deployment_target = "10.9" - s.ios.deployment_target = "8.0" + s.ios.deployment_target = "9.0" s.tvos.deployment_target = "9.0" s.watchos.deployment_target = "2.0" @@ -24,8 +24,8 @@ Pod::Spec.new do |s| s.watchos.source_files = "ReactiveCocoa/WatchKit/*.{swift}" s.module_name = 'ReactiveCocoa' - s.dependency 'ReactiveSwift', '~> 6.2' + s.dependency 'ReactiveSwift', '~> 6.6' s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "$(inherited) -suppress-warnings" } - s.swift_versions = ['5.0', '5.1', '5.2'] + s.swift_versions = ['5.1', '5.2'] end diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 00027e6970..de3d435e5d 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -2606,7 +2606,7 @@ CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; ENABLE_TESTABILITY = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; @@ -2632,7 +2632,7 @@ CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; GCC_OPTIMIZATION_LEVEL = 0; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; @@ -2699,6 +2699,7 @@ DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; name = Debug; @@ -2711,6 +2712,7 @@ DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; name = Release; @@ -2750,7 +2752,7 @@ CLANG_WARN_STRICT_PROTOTYPES = YES; CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; @@ -2793,6 +2795,7 @@ DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; name = Profile; @@ -2821,7 +2824,7 @@ CLANG_WARN_STRICT_PROTOTYPES = YES; CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)-Tests"; PRODUCT_NAME = "$(PROJECT_NAME)"; @@ -2864,6 +2867,7 @@ DYLIB_CURRENT_VERSION = 1; ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; name = Test; diff --git a/ReactiveCocoaTests/KeyValueObservingSpec+Swift4.swift b/ReactiveCocoaTests/KeyValueObservingSpec+Swift4.swift index a22630fc8e..e9cc1eba5e 100644 --- a/ReactiveCocoaTests/KeyValueObservingSpec+Swift4.swift +++ b/ReactiveCocoaTests/KeyValueObservingSpec+Swift4.swift @@ -535,7 +535,7 @@ fileprivate class KeyValueObservingSwift4SpecConfiguration: QuickConfiguration { testObject.rac_value = 2 - expect(atomicCounter).toEventually(equal(Int64(numIterations * 2)), timeout: 30.0) + expect(atomicCounter).toEventually(equal(Int64(numIterations * 2)), timeout: .seconds(30)) } // ReactiveCocoa/ReactiveCocoa#1122 @@ -593,7 +593,7 @@ fileprivate class KeyValueObservingSwift4SpecConfiguration: QuickConfiguration { iterationQueue.resume() - waitUntil(timeout: 3.0) { done in + waitUntil(timeout: .seconds(3)) { done in iterationQueue.async(flags: .barrier, execute: done) } } diff --git a/ReactiveCocoaTests/KeyValueObservingSpec.swift b/ReactiveCocoaTests/KeyValueObservingSpec.swift index 88b8c35a20..07b9d9df47 100644 --- a/ReactiveCocoaTests/KeyValueObservingSpec.swift +++ b/ReactiveCocoaTests/KeyValueObservingSpec.swift @@ -587,7 +587,7 @@ fileprivate class KeyValueObservingSpecConfiguration: QuickConfiguration { testObject.rac_value = 2 - expect(atomicCounter).toEventually(equal(Int64(numIterations * 2)), timeout: 30.0) + expect(atomicCounter).toEventually(equal(Int64(numIterations * 2)), timeout: .seconds(30)) } // Direct port of https://github.com/ReactiveCocoa/ReactiveObjC/blob/3.1.0/ReactiveObjCTests/RACKVOProxySpec.m#L196 @@ -652,7 +652,7 @@ fileprivate class KeyValueObservingSpecConfiguration: QuickConfiguration { iterationQueue.resume() - waitUntil(timeout: 3.0) { done in + waitUntil(timeout: .seconds(3)) { done in iterationQueue.async(flags: .barrier, execute: done) } } diff --git a/ReactiveCocoaTests/QueueScheduler+Factory.swift b/ReactiveCocoaTests/QueueScheduler+Factory.swift index c56a33242e..8bdf6c8156 100644 --- a/ReactiveCocoaTests/QueueScheduler+Factory.swift +++ b/ReactiveCocoaTests/QueueScheduler+Factory.swift @@ -6,14 +6,10 @@ extension QueueScheduler { let file = URL(string: file)?.lastPathComponent ?? "" let label = "reactiveswift:\(file):\(line)" - #if targetEnvironment(macCatalyst) - return QueueScheduler() - #else if #available(OSX 10.10, iOS 8.0, *) { - return QueueScheduler() + return QueueScheduler(name: label) } else { - return QueueScheduler(queue: DispatchQueue(label: label)) + return QueueScheduler(name: label) } - #endif } } diff --git a/ReactiveCocoaTests/SignalProducerNimbleMatchers.swift b/ReactiveCocoaTests/SignalProducerNimbleMatchers.swift index 6fadb214ef..4d4b70aacd 100644 --- a/ReactiveCocoaTests/SignalProducerNimbleMatchers.swift +++ b/ReactiveCocoaTests/SignalProducerNimbleMatchers.swift @@ -44,7 +44,7 @@ public func sendValues(_ values: [T], sendError mayb if sentValues != values { let message = ExpectationMessage.expectedCustomValueTo( "send values <\(values)>", - "<\(sentValues)>" + actual: "<\(sentValues)>" ) return PredicateResult(status: .doesNotMatch, message: message) } @@ -52,7 +52,7 @@ public func sendValues(_ values: [T], sendError mayb if sentError != maybeSendError { let message = ExpectationMessage.expectedCustomValueTo( "send error <\(String(describing: maybeSendError))>", - "<\(String(describing: sentError))>" + actual: "<\(String(describing: sentError))>" ) return PredicateResult(status: .doesNotMatch, message: message) } diff --git a/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift b/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift index 9740dce0f6..85510fb34f 100644 --- a/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIRefreshControlSpec.swift @@ -79,7 +79,7 @@ class UIRefreshControlSpec: QuickSpec { refreshControl.sendActions(for: .valueChanged) expect(refreshControl.isRefreshing) == true - expect(refreshControl.isRefreshing).toEventually(equal(false), timeout: 2) + expect(refreshControl.isRefreshing).toEventually(equal(false), timeout: .seconds(2)) } } } diff --git a/ReactiveCocoaTests/UIKit/UIResponderSpec.swift b/ReactiveCocoaTests/UIKit/UIResponderSpec.swift index d1c9c5e847..20a3d1336e 100644 --- a/ReactiveCocoaTests/UIKit/UIResponderSpec.swift +++ b/ReactiveCocoaTests/UIKit/UIResponderSpec.swift @@ -7,17 +7,19 @@ import Nimble class UIResponderSpec: QuickSpec { override func spec() { + #if !targetEnvironment(macCatalyst) it("should become and resign first responder") { let window = UIWindow(frame: .zero) let textField = UITextField(frame: .zero) window.addSubview(textField) - + expect(textField.isFirstResponder).to(beFalse()) textField.reactive.becomeFirstResponder <~ SignalProducer(value: ()) expect(textField.isFirstResponder).to(beTrue()) textField.reactive.resignFirstResponder <~ SignalProducer(value: ()) expect(textField.isFirstResponder).to(beFalse()) } + #endif } } #endif diff --git a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift index da5304c314..bad595b323 100644 --- a/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift +++ b/ReactiveCocoaTests/UIKit/UITextFieldSpec.swift @@ -154,6 +154,7 @@ class UITextFieldSpec: QuickSpec { expect(textField.textColor == UIColor.red) == false } + #if !targetEnvironment(macCatalyst) it("should not deadlock when the text field is asked to resign first responder by any of its observers") { UIView.setAnimationsEnabled(false) defer { UIView.setAnimationsEnabled(true) } @@ -192,6 +193,7 @@ class UITextFieldSpec: QuickSpec { expect(values) == ["1", "2", "2"] } } + #endif } } diff --git a/ReactiveMapKitTests/MKMapViewSpec.swift b/ReactiveMapKitTests/MKMapViewSpec.swift index c71272a584..092767af29 100644 --- a/ReactiveMapKitTests/MKMapViewSpec.swift +++ b/ReactiveMapKitTests/MKMapViewSpec.swift @@ -24,7 +24,7 @@ class MKMapViewSpec: QuickSpec { // // Temporarily disabled since the expectation keeps failing with // Xcode 8.3 and macOS Sierra 10.12.4. - #if !os(macOS) + #if !os(macOS) && !targetEnvironment(macCatalyst) // using toEventually(beNil()) here // since it takes time to release MKMapView expect(_mapView).toEventually(beNil()) From 02580542900abb4527d96e1b6ef27bab6812278d Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Tue, 16 Mar 2021 12:35:31 +0000 Subject: [PATCH 1012/1028] 11.2.0 --- CHANGELOG.md | 3 +++ ReactiveCocoa/Info.plist | 2 +- ReactiveMapKit.podspec | 4 ++-- ReactiveMapKit/Info.plist | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecf1c3fbed..74e740b682 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # master + +# 11.2.0 + 1. Requires ReactiveSwift 6.6.0 or later. 1. The minimum deployment target for iOS has been raised consistently to 9.0 across all integration mediums. diff --git a/ReactiveCocoa/Info.plist b/ReactiveCocoa/Info.plist index 85935b63e1..0ea3e74fe6 100644 --- a/ReactiveCocoa/Info.plist +++ b/ReactiveCocoa/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 11.1.0 + 11.2.0 CFBundleSignature ???? CFBundleVersion diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index ab63333ebc..4596584204 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveMapKit" - s.version = "11.1.0" + s.version = "11.2.0" s.summary = "MapKit bindings for ReactiveCocoa." s.description = <<-DESC Provide MapKit bindings for ReactiveCocoa. ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. @@ -10,7 +10,7 @@ Pod::Spec.new do |s| s.author = "ReactiveCocoa" s.osx.deployment_target = "10.9" - s.ios.deployment_target = "8.0" + s.ios.deployment_target = "9.0" s.tvos.deployment_target = "9.0" s.source = { :git => "https://github.com/ReactiveCocoa/ReactiveCocoa.git", :tag => "#{s.version}" } diff --git a/ReactiveMapKit/Info.plist b/ReactiveMapKit/Info.plist index b45445606a..0314150124 100644 --- a/ReactiveMapKit/Info.plist +++ b/ReactiveMapKit/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 11.1.0 + 11.2.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass From f89a9f4a6eefcbaec42e45ddcde9a54ca63ea1b7 Mon Sep 17 00:00:00 2001 From: Timotej Papler <5990580+TimPapler@users.noreply.github.com> Date: Fri, 23 Apr 2021 12:57:22 +0200 Subject: [PATCH 1013/1028] Fix spm xcode 12.5 missing Foundation import (#3725) * Fix spm xcode 12.5 missing Foundation import * Update changelog --- CHANGELOG.md | 1 + ReactiveCocoaObjC/include/ObjCRuntimeAliases.h | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74e740b682..adea65aafd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # master +1. Adds support for Xcode 12.5 # 11.2.0 diff --git a/ReactiveCocoaObjC/include/ObjCRuntimeAliases.h b/ReactiveCocoaObjC/include/ObjCRuntimeAliases.h index 2dc6391c12..da296a3803 100644 --- a/ReactiveCocoaObjC/include/ObjCRuntimeAliases.h +++ b/ReactiveCocoaObjC/include/ObjCRuntimeAliases.h @@ -1,5 +1,6 @@ #import #import +#import NS_ASSUME_NONNULL_BEGIN From 376f98e9341bfe4daba01909d51acf47fde456d7 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Fri, 23 Apr 2021 11:59:53 +0100 Subject: [PATCH 1014/1028] 11.2.1 --- CHANGELOG.md | 5 ++++- ReactiveCocoa.podspec | 2 +- ReactiveCocoa/Info.plist | 2 +- ReactiveMapKit.podspec | 2 +- ReactiveMapKit/Info.plist | 2 +- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index adea65aafd..9fb9603ac5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # master -1. Adds support for Xcode 12.5 + +# 11.2.1 + +1. Fixed missing Foundation import when building with SPM in Xcode 12.5. (#3725, kudos to @TimPapler) # 11.2.0 diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index e797654b8f..b48dfcccbc 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "11.2.0" + s.version = "11.2.1" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveCocoa/Info.plist b/ReactiveCocoa/Info.plist index 0ea3e74fe6..cc02f94e92 100644 --- a/ReactiveCocoa/Info.plist +++ b/ReactiveCocoa/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 11.2.0 + 11.2.1 CFBundleSignature ???? CFBundleVersion diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index 4596584204..590cc860ad 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveMapKit" - s.version = "11.2.0" + s.version = "11.2.1" s.summary = "MapKit bindings for ReactiveCocoa." s.description = <<-DESC Provide MapKit bindings for ReactiveCocoa. ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveMapKit/Info.plist b/ReactiveMapKit/Info.plist index 0314150124..36c2d70b7c 100644 --- a/ReactiveMapKit/Info.plist +++ b/ReactiveMapKit/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 11.2.0 + 11.2.1 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass From 2e8de8855a2b365e5157a28dcb5b83515b96070b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C5=A0r=C5=AFtek?= <35694712+michalsrutek@users.noreply.github.com> Date: Wed, 23 Jun 2021 22:18:02 +0200 Subject: [PATCH 1015/1028] Use preferred AnyObject keyword (#3726) * Use preferred AnyObject keyword * Update CHANGELOG.md * Update CHANGELOG.md Co-authored-by: Anders Ha --- CHANGELOG.md | 1 + ReactiveCocoa/AppKit/ActionProxy.swift | 2 +- ReactiveCocoa/UIKit/UIKitReusableComponents.swift | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fb9603ac5..c2a1d2dc8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # master +1. Building from Xcode project no longer warns about use of deprecated `class` keyword usage. (#3726, kudos to @michalsrutek) # 11.2.1 diff --git a/ReactiveCocoa/AppKit/ActionProxy.swift b/ReactiveCocoa/AppKit/ActionProxy.swift index 81e1b34975..55f2091f57 100644 --- a/ReactiveCocoa/AppKit/ActionProxy.swift +++ b/ReactiveCocoa/AppKit/ActionProxy.swift @@ -32,7 +32,7 @@ internal final class ActionProxy: NSObject { private let hasSwizzledKey = AssociationKey(default: false) -@objc internal protocol ActionMessageSending: class { +@objc internal protocol ActionMessageSending: AnyObject { weak var target: AnyObject? { get set } var action: Selector? { get set } } diff --git a/ReactiveCocoa/UIKit/UIKitReusableComponents.swift b/ReactiveCocoa/UIKit/UIKitReusableComponents.swift index 7cb939e5fc..9108df6fa6 100644 --- a/ReactiveCocoa/UIKit/UIKitReusableComponents.swift +++ b/ReactiveCocoa/UIKit/UIKitReusableComponents.swift @@ -2,7 +2,7 @@ import UIKit import ReactiveSwift -@objc public protocol Reusable: class { +@objc public protocol Reusable: AnyObject { func prepareForReuse() } From 47672fa2b239a5f2f7c138378046b93393c05d3d Mon Sep 17 00:00:00 2001 From: Ian Bytchek <1086845+iby@users.noreply.github.com> Date: Sat, 4 Sep 2021 20:31:54 +0400 Subject: [PATCH 1016/1028] Update Carthage xcconfigs to 1.1 (#3728) * Update Carthage xcconfigs to 1.1 * Use macOS configurations directly over aliased Mac OS X * Update ReactiveSwift to 6.6.1 * Update Quick to 4.0.0 and Nimble to 9.2.0 * Disable treating Swift and C warnings as errors * Sort source files by type and name * Update deprecated global priority dispatch queue syntax * Remove redundant base SDK Xcode configuration Provided by Xcode. * Remove duplicating bitcode Xcode configuration This is already provided by Xcode defaults. * Clean up Xcode code signing configuration * Remove duplicating runpath search paths Xcode configuration This is already provided by xcconfigs. --- .gitmodules | 2 +- CHANGELOG.md | 1 + Cartfile.private | 4 +- Cartfile.resolved | 8 +- Carthage/Checkouts/Nimble | 2 +- Carthage/Checkouts/Quick | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- Carthage/Checkouts/xcconfigs | 2 +- Package.resolved | 16 +- Package.swift | 2 +- ReactiveCocoa.xcodeproj/project.pbxproj | 196 +++++++++------------- ReactiveCocoaTests/InterceptingSpec.swift | 5 +- 12 files changed, 99 insertions(+), 143 deletions(-) diff --git a/.gitmodules b/.gitmodules index cd4bff88f4..72a6626ffd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,7 +6,7 @@ url = https://github.com/Quick/Quick.git [submodule "Carthage/Checkouts/xcconfigs"] path = Carthage/Checkouts/xcconfigs - url = https://github.com/jspahrsummers/xcconfigs.git + url = https://github.com/xcconfigs/xcconfigs.git [submodule "Carthage/Checkouts/ReactiveSwift"] path = Carthage/Checkouts/ReactiveSwift url = https://github.com/ReactiveCocoa/ReactiveSwift.git diff --git a/CHANGELOG.md b/CHANGELOG.md index c2a1d2dc8f..43700d2bdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master 1. Building from Xcode project no longer warns about use of deprecated `class` keyword usage. (#3726, kudos to @michalsrutek) +2. Updated Carthage xcconfig dependency to 1.1 for proper building arm64 macOS variants. # 11.2.1 diff --git a/Cartfile.private b/Cartfile.private index 9399a7123d..2ff3b76213 100644 --- a/Cartfile.private +++ b/Cartfile.private @@ -1,3 +1,3 @@ -github "jspahrsummers/xcconfigs" "3d9d996" -github "Quick/Quick" ~> 3.0 +github "xcconfigs/xcconfigs" ~> 1.1 +github "Quick/Quick" ~> 4.0 github "Quick/Nimble" ~> 9.0 diff --git a/Cartfile.resolved b/Cartfile.resolved index b390301e12..9332ad0190 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,4 +1,4 @@ -github "Quick/Nimble" "v9.0.0" -github "Quick/Quick" "v3.1.2" -github "ReactiveCocoa/ReactiveSwift" "6.6.0" -github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" +github "Quick/Nimble" "v9.2.0" +github "Quick/Quick" "v4.0.0" +github "ReactiveCocoa/ReactiveSwift" "6.6.1" +github "xcconfigs/xcconfigs" "1.1" diff --git a/Carthage/Checkouts/Nimble b/Carthage/Checkouts/Nimble index e491a67313..af1730dde4 160000 --- a/Carthage/Checkouts/Nimble +++ b/Carthage/Checkouts/Nimble @@ -1 +1 @@ -Subproject commit e491a6731307bb23783bf664d003be9b2fa59ab5 +Subproject commit af1730dde4e6c0d45bf01b99f8a41713ce536790 diff --git a/Carthage/Checkouts/Quick b/Carthage/Checkouts/Quick index 8cce6acd38..bd86ca0141 160000 --- a/Carthage/Checkouts/Quick +++ b/Carthage/Checkouts/Quick @@ -1 +1 @@ -Subproject commit 8cce6acd38f965f5baa3167b939f86500314022b +Subproject commit bd86ca0141e3cfb333546de5a11ede63f0c4a0e6 diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index 4a8e1e4a68..2c06e94210 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit 4a8e1e4a683e96daf658f68132dae781738bc862 +Subproject commit 2c06e9421011a1a4cf57ea4bbcd8936843a6c9da diff --git a/Carthage/Checkouts/xcconfigs b/Carthage/Checkouts/xcconfigs index 3d9d99634c..4ced0ad5a9 160000 --- a/Carthage/Checkouts/xcconfigs +++ b/Carthage/Checkouts/xcconfigs @@ -1 +1 @@ -Subproject commit 3d9d99634cae6d586e272543d527681283b33eb0 +Subproject commit 4ced0ad5a971220917994a4edfa6abf9702e3818 diff --git a/Package.resolved b/Package.resolved index 2e07d8d87e..816482b01e 100644 --- a/Package.resolved +++ b/Package.resolved @@ -6,8 +6,8 @@ "repositoryURL": "https://github.com/mattgallagher/CwlCatchException.git", "state": { "branch": null, - "revision": "f809deb30dc5c9d9b78c872e553261a61177721a", - "version": "2.0.0" + "revision": "682841464136f8c66e04afe5dbd01ab51a3a56f2", + "version": "2.1.0" } }, { @@ -24,8 +24,8 @@ "repositoryURL": "https://github.com/Quick/Nimble.git", "state": { "branch": null, - "revision": "e491a6731307bb23783bf664d003be9b2fa59ab5", - "version": "9.0.0" + "revision": "af1730dde4e6c0d45bf01b99f8a41713ce536790", + "version": "9.2.0" } }, { @@ -33,8 +33,8 @@ "repositoryURL": "https://github.com/Quick/Quick.git", "state": { "branch": null, - "revision": "0038bcbab4292f3b028632556507c124e5ba69f3", - "version": "3.0.0" + "revision": "bd86ca0141e3cfb333546de5a11ede63f0c4a0e6", + "version": "4.0.0" } }, { @@ -42,8 +42,8 @@ "repositoryURL": "https://github.com/ReactiveCocoa/ReactiveSwift", "state": { "branch": null, - "revision": "4a8e1e4a683e96daf658f68132dae781738bc862", - "version": "6.6.0" + "revision": "2c06e9421011a1a4cf57ea4bbcd8936843a6c9da", + "version": "6.6.1" } } ] diff --git a/Package.swift b/Package.swift index ec07d485fd..8b74bbe04c 100644 --- a/Package.swift +++ b/Package.swift @@ -12,7 +12,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/ReactiveCocoa/ReactiveSwift", from: "6.6.0"), - .package(url: "https://github.com/Quick/Quick.git", from: "3.0.0"), + .package(url: "https://github.com/Quick/Quick.git", from: "4.0.0"), .package(url: "https://github.com/Quick/Nimble.git", from: "9.0.0"), ], targets: [ diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index de3d435e5d..a882d3ba1c 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -638,11 +638,11 @@ D047263319E49FE8006002AA /* iOS-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-Base.xcconfig"; sourceTree = ""; }; D047263419E49FE8006002AA /* iOS-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-Framework.xcconfig"; sourceTree = ""; }; D047263519E49FE8006002AA /* iOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-StaticLibrary.xcconfig"; sourceTree = ""; }; - D047263719E49FE8006002AA /* Mac-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Application.xcconfig"; sourceTree = ""; }; - D047263819E49FE8006002AA /* Mac-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Base.xcconfig"; sourceTree = ""; }; - D047263919E49FE8006002AA /* Mac-DynamicLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-DynamicLibrary.xcconfig"; sourceTree = ""; }; - D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Framework.xcconfig"; sourceTree = ""; }; - D047263B19E49FE8006002AA /* Mac-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-StaticLibrary.xcconfig"; sourceTree = ""; }; + D047263719E49FE8006002AA /* macOS-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "macOS-Application.xcconfig"; sourceTree = ""; }; + D047263819E49FE8006002AA /* macOS-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "macOS-Base.xcconfig"; sourceTree = ""; }; + D047263919E49FE8006002AA /* macOS-DynamicLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "macOS-DynamicLibrary.xcconfig"; sourceTree = ""; }; + D047263A19E49FE8006002AA /* macOS-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "macOS-Framework.xcconfig"; sourceTree = ""; }; + D047263B19E49FE8006002AA /* macOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "macOS-StaticLibrary.xcconfig"; sourceTree = ""; }; D047263C19E49FE8006002AA /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; D05E662419EDD82000904ACA /* Nimble.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Nimble.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = DynamicPropertySpec.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; @@ -650,6 +650,7 @@ D9558AB71DFF805A003254E1 /* NSPopUpButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSPopUpButton.swift; sourceTree = ""; }; E3AA54C02142918B0077B206 /* WKInterfaceLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WKInterfaceLabel.swift; sourceTree = ""; }; E3AA54C2214292540077B206 /* WKInterfaceButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WKInterfaceButton.swift; sourceTree = ""; }; + E6124BA8267DF3C0005A3490 /* macOS-XCTest.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "macOS-XCTest.xcconfig"; sourceTree = ""; }; E921428A2284A12B007E412D /* WKInterfaceObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKInterfaceObject.swift; sourceTree = ""; }; E921428D2284A44F007E412D /* WKInterfaceDate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKInterfaceDate.swift; sourceTree = ""; }; E921428F2284A4EF007E412D /* WKInterfaceTimer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKInterfaceTimer.swift; sourceTree = ""; }; @@ -801,8 +802,8 @@ 056D088022C00A6A00291F50 /* ReactiveCocoaObjC */ = { isa = PBXGroup; children = ( - 9AA0BD971DDE7A2200531FCF /* ObjCRuntimeAliases.m */, F44078372371B6E400F103E7 /* include */, + 9AA0BD971DDE7A2200531FCF /* ObjCRuntimeAliases.m */, ); path = ReactiveCocoaObjC; sourceTree = ""; @@ -810,8 +811,8 @@ 056D088222C00B2700291F50 /* ReactiveCocoaObjCTestSupport */ = { isa = PBXGroup; children = ( - 9AD841DB204C29B90040F0C0 /* MessageForwardingEntity.m */, F440783B2371C00800F103E7 /* include */, + 9AD841DB204C29B90040F0C0 /* MessageForwardingEntity.m */, ); path = ReactiveCocoaObjCTestSupport; sourceTree = ""; @@ -846,8 +847,8 @@ 9A16753E1F80C35100B63650 /* ReactiveMapKitTests */ = { isa = PBXGroup; children = ( - 53A6BED51DD4BD2C0016C058 /* MKMapViewSpec.swift */, 9A1675411F80C35100B63650 /* Info.plist */, + 53A6BED51DD4BD2C0016C058 /* MKMapViewSpec.swift */, ); path = ReactiveMapKitTests; sourceTree = ""; @@ -856,6 +857,7 @@ isa = PBXGroup; children = ( 9AED64C41E496A3700321004 /* ActionProxy.swift */, + 9A6AAA251DB8F5280013AAEA /* AppKitReusableComponents.swift */, 006518751E26865800C3139A /* NSButton.swift */, 4ABEFE2F1DCFD0530066A8C2 /* NSCollectionView.swift */, 9ADE4A881DA6D206005C2AC8 /* NSControl.swift */, @@ -867,7 +869,6 @@ 9ADE4A951DA6F018005C2AC8 /* NSTextField.swift */, 7A8BA0F91FCC86FC003241C7 /* NSTextView.swift */, 537EC08021A950CA00D6EE18 /* NSView.swift */, - 9A6AAA251DB8F5280013AAEA /* AppKitReusableComponents.swift */, ); path = AppKit; sourceTree = ""; @@ -876,7 +877,6 @@ isa = PBXGroup; children = ( CD91E3D41DDAC67700FA70D0 /* iOS */, - 9A6AAA221DB8F51C0013AAEA /* UIKitReusableComponents.swift */, 9A1D05EC1D93E9F100ACF44C /* UIActivityIndicatorView.swift */, 4EE637352090F92600ECD02A /* UIApplication.swift */, 9A1D05ED1D93E9F100ACF44C /* UIBarButtonItem.swift */, @@ -886,18 +886,19 @@ 9A1D05F11D93E9F100ACF44C /* UIControl.swift */, 419139431DB910570043C9D1 /* UIGestureRecognizer.swift */, 9A1D05F31D93E9F100ACF44C /* UIImageView.swift */, + 9A6AAA221DB8F51C0013AAEA /* UIKitReusableComponents.swift */, 9A1D05F41D93E9F100ACF44C /* UILabel.swift */, A9EB3D1C1E94ECAC002A9BCC /* UINavigationItem.swift */, 9A1D05F51D93E9F100ACF44C /* UIProgressView.swift */, + A9D8BA70207CD7090031733D /* UIResponder.swift */, BF4335641E02AC7600AC88DD /* UIScrollView.swift */, 9A1D05F61D93E9F100ACF44C /* UISegmentedControl.swift */, - 4ABEFDE11DCFC8560066A8C2 /* UITableView.swift */, A9EB3D241E94F335002A9BCC /* UITabBarItem.swift */, + 4ABEFDE11DCFC8560066A8C2 /* UITableView.swift */, 9A1D05FA1D93E9F100ACF44C /* UITextField.swift */, 9A1D05FB1D93E9F100ACF44C /* UITextView.swift */, 9A1D05FC1D93E9F100ACF44C /* UIView.swift */, 4EE6372D2090EEFA00ECD02A /* UIViewController.swift */, - A9D8BA70207CD7090031733D /* UIResponder.swift */, ); path = UIKit; sourceTree = ""; @@ -955,9 +956,9 @@ 9AC03A581F7CC3BF00EC33C1 /* ReactiveMapKit */ = { isa = PBXGroup; children = ( - 53A6BED11DD4BCA90016C058 /* MKMapView.swift */, - A91244E720389AEA0001BBCB /* MKLocalSearchRequest.swift */, 9AC03A5A1F7CC3BF00EC33C1 /* Info.plist */, + A91244E720389AEA0001BBCB /* MKLocalSearchRequest.swift */, + 53A6BED11DD4BCA90016C058 /* MKMapView.swift */, ); path = ReactiveMapKit; sourceTree = ""; @@ -996,12 +997,12 @@ 9A1D05F21D93E9F100ACF44C /* UIDatePicker.swift */, A9EB3D7D1E955602002A9BCC /* UIFeedbackGenerator.swift */, A9EB3D7E1E955602002A9BCC /* UIImpact​Feedback​Generator.swift */, - A9EB3D7F1E955602002A9BCC /* UINotification​Feedback​Generator.swift */, - A9EB3D801E955602002A9BCC /* UISelection​Feedback​Generator.swift */, 9AAD49871DED2C350068EC9B /* UIKeyboard.swift */, + A9EB3D7F1E955602002A9BCC /* UINotification​Feedback​Generator.swift */, BFE145881E43991A00208736 /* UIPickerView.swift */, 3BCAAC791DEE19BC00B30335 /* UIRefreshControl.swift */, BFCF775E1DFAD8A50058006E /* UISearchBar.swift */, + A9EB3D801E955602002A9BCC /* UISelection​Feedback​Generator.swift */, 53AC46CB1DD6F97400C799E1 /* UISlider.swift */, 531866F71DD7920400D1285F /* UIStepper.swift */, 9A1D05F71D93E9F100ACF44C /* UISwitch.swift */, @@ -1048,6 +1049,12 @@ D04725EC19E49ED7006002AA /* ReactiveCocoa */ = { isa = PBXGroup; children = ( + F44078392371BE3D00F103E7 /* include */, + 9A1D05E91D93E9F100ACF44C /* AppKit */, + 538DCB761DCA5E1600332880 /* Shared */, + D04725ED19E49ED7006002AA /* Supporting Files */, + 9A1D05EB1D93E9F100ACF44C /* UIKit */, + E3AA54BF214291590077B206 /* WatchKit */, 4A0E10FE1D2A92720065D310 /* AnyObject+Lifetime.swift */, 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */, 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */, @@ -1067,12 +1074,6 @@ 9A54A21A1DE00D09001739B3 /* ObjC+Selector.swift */, 9A90374E1ED61C6300345D62 /* ReactiveSwift+Lifetime.swift */, 9ADFE5A41DC0001C001E11F7 /* Synchronizing.swift */, - 9A1D05E91D93E9F100ACF44C /* AppKit */, - F44078392371BE3D00F103E7 /* include */, - 538DCB761DCA5E1600332880 /* Shared */, - D04725ED19E49ED7006002AA /* Supporting Files */, - 9A1D05EB1D93E9F100ACF44C /* UIKit */, - E3AA54BF214291590077B206 /* WatchKit */, ); path = ReactiveCocoa; sourceTree = ""; @@ -1089,25 +1090,25 @@ isa = PBXGroup; children = ( 9ADE4A8C1DA6D94C005C2AC8 /* AppKit */, - 9A1D06231D93EA7E00ACF44C /* UIKit */, 538DCB771DCA5E3200332880 /* Shared */, + D04725FA19E49ED7006002AA /* Supporting Files */, + 9A1D06231D93EA7E00ACF44C /* UIKit */, + 9AD841D6204C29B90040F0C0 /* ReactiveCocoaTests-Bridging-Header.h */, 9A9DFEE81DA7EFB60039EE1B /* AssociationSpec.swift */, 9A7990CD1F1085D8001493A3 /* BindingTargetSpec.swift */, CD8401821CEE8ED7009F0ABF /* CocoaActionSpec.swift */, 9A892D8E1E8D19BE00EA35F3 /* DelegateProxySpec.swift */, + 3B30EE8B1E7BE529007CC8EF /* DeprecationsSpec.swift */, D0A2260D1A72F16D00D33B74 /* DynamicPropertySpec.swift */, - B696FB801A7640C00075236D /* TestError.swift */, + 9A54A2101DDF5B4D001739B3 /* InterceptingPerformanceTests.swift */, + 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */, 9A1E72B91D4DE96500CC20C3 /* KeyValueObservingSpec.swift */, 9AA6A1E31F11F9A800CA2257 /* KeyValueObservingSpec+Swift4.swift */, 9AFCBFE21EB1ABC0004B4C74 /* KVOKVCExtensionSpec.swift */, - 9A6AAA0D1DB6A4CF0013AAEA /* InterceptingSpec.swift */, - 9A54A2101DDF5B4D001739B3 /* InterceptingPerformanceTests.swift */, 9ADFE5A01DBFFBCF001E11F7 /* LifetimeSpec.swift */, - 9A24A8431DE1429600987AF9 /* SwizzlingSpec.swift */, - 3B30EE8B1E7BE529007CC8EF /* DeprecationsSpec.swift */, CD42C69A1E951F6900AA9504 /* ReactiveCocoaTestsConfiguration.swift */, - D04725FA19E49ED7006002AA /* Supporting Files */, - 9AD841D6204C29B90040F0C0 /* ReactiveCocoaTests-Bridging-Header.h */, + 9A24A8431DE1429600987AF9 /* SwizzlingSpec.swift */, + B696FB801A7640C00075236D /* TestError.swift */, ); path = ReactiveCocoaTests; sourceTree = ""; @@ -1130,7 +1131,7 @@ children = ( D047262619E49FE8006002AA /* Base */, D047263119E49FE8006002AA /* iOS */, - D047263619E49FE8006002AA /* Mac OS X */, + D047263619E49FE8006002AA /* macOS */, A97451321B3A935E00F48E55 /* watchOS */, 57A4D2431BA13F9700F7D4B1 /* tvOS */, D047263C19E49FE8006002AA /* README.md */, @@ -1181,36 +1182,37 @@ path = iOS; sourceTree = ""; }; - D047263619E49FE8006002AA /* Mac OS X */ = { + D047263619E49FE8006002AA /* macOS */ = { isa = PBXGroup; children = ( - D047263719E49FE8006002AA /* Mac-Application.xcconfig */, - D047263819E49FE8006002AA /* Mac-Base.xcconfig */, - D047263919E49FE8006002AA /* Mac-DynamicLibrary.xcconfig */, - D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */, - D047263B19E49FE8006002AA /* Mac-StaticLibrary.xcconfig */, - ); - path = "Mac OS X"; + D047263719E49FE8006002AA /* macOS-Application.xcconfig */, + D047263819E49FE8006002AA /* macOS-Base.xcconfig */, + D047263919E49FE8006002AA /* macOS-DynamicLibrary.xcconfig */, + D047263A19E49FE8006002AA /* macOS-Framework.xcconfig */, + D047263B19E49FE8006002AA /* macOS-StaticLibrary.xcconfig */, + E6124BA8267DF3C0005A3490 /* macOS-XCTest.xcconfig */, + ); + path = macOS; sourceTree = ""; }; E3AA54BF214291590077B206 /* WatchKit */ = { isa = PBXGroup; children = ( - E921428A2284A12B007E412D /* WKInterfaceObject.swift */, - E3AA54C02142918B0077B206 /* WKInterfaceLabel.swift */, E3AA54C2214292540077B206 /* WKInterfaceButton.swift */, + E92142A12284B64E007E412D /* WKInterfaceController.swift */, E921428D2284A44F007E412D /* WKInterfaceDate.swift */, - E921428F2284A4EF007E412D /* WKInterfaceTimer.swift */, - E92142912284AA64007E412D /* WKInterfaceSwitch.swift */, - E92142932284ABDF007E412D /* WKInterfaceSlider.swift */, + E92142972284AEF0007E412D /* WKInterfaceGroup.swift */, E921429B2284B0A8007E412D /* WKInterfaceImage.swift */, - E92142A8228552CC007E412D /* WKInterfaceMovie.swift */, E92142AA228553C5007E412D /* WKInterfaceInlineMovie.swift */, - E921429D2284B1C4007E412D /* WKInterfaceVolumeControl.swift */, - E92142972284AEF0007E412D /* WKInterfaceGroup.swift */, - E92142992284AF99007E412D /* WKInterfaceSeparator.swift */, + E3AA54C02142918B0077B206 /* WKInterfaceLabel.swift */, + E92142A8228552CC007E412D /* WKInterfaceMovie.swift */, + E921428A2284A12B007E412D /* WKInterfaceObject.swift */, E921429F2284B4FF007E412D /* WKInterfacePicker.swift */, - E92142A12284B64E007E412D /* WKInterfaceController.swift */, + E92142992284AF99007E412D /* WKInterfaceSeparator.swift */, + E92142932284ABDF007E412D /* WKInterfaceSlider.swift */, + E92142912284AA64007E412D /* WKInterfaceSwitch.swift */, + E921428F2284A4EF007E412D /* WKInterfaceTimer.swift */, + E921429D2284B1C4007E412D /* WKInterfaceVolumeControl.swift */, ); path = WatchKit; sourceTree = ""; @@ -2154,7 +2156,6 @@ buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; @@ -2166,7 +2167,6 @@ buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; @@ -2178,7 +2178,6 @@ buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; @@ -2190,7 +2189,6 @@ buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; @@ -2240,12 +2238,9 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; buildSettings = { - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; PRODUCT_NAME = ReactiveMapKitTests; - SDKROOT = iphoneos; }; name = Debug; }; @@ -2253,12 +2248,9 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; buildSettings = { - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; PRODUCT_NAME = ReactiveMapKitTests; - SDKROOT = iphoneos; }; name = Test; }; @@ -2266,12 +2258,9 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; buildSettings = { - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; PRODUCT_NAME = ReactiveMapKitTests; - SDKROOT = iphoneos; }; name = Release; }; @@ -2279,12 +2268,9 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; buildSettings = { - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; PRODUCT_NAME = ReactiveMapKitTests; - SDKROOT = iphoneos; }; name = Profile; }; @@ -2297,7 +2283,6 @@ INFOPLIST_FILE = ReactiveMapKit/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; PRODUCT_NAME = ReactiveMapKit; - SDKROOT = iphoneos; }; name = Debug; }; @@ -2310,7 +2295,6 @@ INFOPLIST_FILE = ReactiveMapKit/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; PRODUCT_NAME = ReactiveMapKit; - SDKROOT = iphoneos; }; name = Test; }; @@ -2323,7 +2307,6 @@ INFOPLIST_FILE = ReactiveMapKit/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; PRODUCT_NAME = ReactiveMapKit; - SDKROOT = iphoneos; }; name = Release; }; @@ -2336,7 +2319,6 @@ INFOPLIST_FILE = ReactiveMapKit/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; PRODUCT_NAME = ReactiveMapKit; - SDKROOT = iphoneos; }; name = Profile; }; @@ -2349,7 +2331,6 @@ INFOPLIST_FILE = ReactiveMapKit/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; PRODUCT_NAME = ReactiveMapKit; - SDKROOT = appletvos; }; name = Debug; }; @@ -2362,7 +2343,6 @@ INFOPLIST_FILE = ReactiveMapKit/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; PRODUCT_NAME = ReactiveMapKit; - SDKROOT = appletvos; }; name = Test; }; @@ -2375,7 +2355,6 @@ INFOPLIST_FILE = ReactiveMapKit/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; PRODUCT_NAME = ReactiveMapKit; - SDKROOT = appletvos; }; name = Release; }; @@ -2388,59 +2367,50 @@ INFOPLIST_FILE = ReactiveMapKit/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; PRODUCT_NAME = ReactiveMapKit; - SDKROOT = appletvos; }; name = Profile; }; 9A73DFF4216D3CEB0069AD76 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; + baseConfigurationReference = D047263719E49FE8006002AA /* macOS-Application.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; PRODUCT_NAME = ReactiveMapKitTests; - SDKROOT = macosx; }; name = Debug; }; 9A73DFF5216D3CEB0069AD76 /* Test */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; + baseConfigurationReference = D047263719E49FE8006002AA /* macOS-Application.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; PRODUCT_NAME = ReactiveMapKitTests; - SDKROOT = macosx; }; name = Test; }; 9A73DFF6216D3CEB0069AD76 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; + baseConfigurationReference = D047263719E49FE8006002AA /* macOS-Application.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; PRODUCT_NAME = ReactiveMapKitTests; - SDKROOT = macosx; }; name = Release; }; 9A73DFF7216D3CEB0069AD76 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; + baseConfigurationReference = D047263719E49FE8006002AA /* macOS-Application.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; PRODUCT_NAME = ReactiveMapKitTests; - SDKROOT = macosx; }; name = Profile; }; @@ -2449,10 +2419,8 @@ baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; PRODUCT_NAME = ReactiveMapKitTests; - SDKROOT = appletvos; }; name = Debug; }; @@ -2461,10 +2429,8 @@ baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; PRODUCT_NAME = ReactiveMapKitTests; - SDKROOT = appletvos; }; name = Test; }; @@ -2473,10 +2439,8 @@ baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; PRODUCT_NAME = ReactiveMapKitTests; - SDKROOT = appletvos; }; name = Release; }; @@ -2485,62 +2449,56 @@ baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; PRODUCT_NAME = ReactiveMapKitTests; - SDKROOT = appletvos; }; name = Profile; }; 9AC03A5D1F7CC3BF00EC33C1 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; + baseConfigurationReference = D047263A19E49FE8006002AA /* macOS-Framework.xcconfig */; buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; INFOPLIST_FILE = ReactiveMapKit/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; PRODUCT_NAME = ReactiveMapKit; - SDKROOT = macosx; }; name = Debug; }; 9AC03A5E1F7CC3BF00EC33C1 /* Test */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; + baseConfigurationReference = D047263A19E49FE8006002AA /* macOS-Framework.xcconfig */; buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; INFOPLIST_FILE = ReactiveMapKit/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; PRODUCT_NAME = ReactiveMapKit; - SDKROOT = macosx; }; name = Test; }; 9AC03A5F1F7CC3BF00EC33C1 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; + baseConfigurationReference = D047263A19E49FE8006002AA /* macOS-Framework.xcconfig */; buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; INFOPLIST_FILE = ReactiveMapKit/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; PRODUCT_NAME = ReactiveMapKit; - SDKROOT = macosx; }; name = Release; }; 9AC03A601F7CC3BF00EC33C1 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; + baseConfigurationReference = D047263A19E49FE8006002AA /* macOS-Framework.xcconfig */; buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; INFOPLIST_FILE = ReactiveMapKit/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; PRODUCT_NAME = ReactiveMapKit; - SDKROOT = macosx; }; name = Profile; }; @@ -2550,7 +2508,6 @@ buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; @@ -2562,7 +2519,6 @@ buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; @@ -2574,7 +2530,6 @@ buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; @@ -2586,7 +2541,6 @@ buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; @@ -2606,10 +2560,12 @@ CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; ENABLE_TESTABILITY = YES; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; IPHONEOS_DEPLOYMENT_TARGET = 9.0; MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; + SWIFT_TREAT_WARNINGS_AS_ERRORS = NO; SWIFT_VERSION = 5.0; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; @@ -2632,11 +2588,13 @@ CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; GCC_OPTIMIZATION_LEVEL = 0; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; IPHONEOS_DEPLOYMENT_TARGET = 9.0; MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_TREAT_WARNINGS_AS_ERRORS = NO; SWIFT_VERSION = 5.0; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; @@ -2647,7 +2605,7 @@ }; D047260119E49ED7006002AA /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; + baseConfigurationReference = D047263A19E49FE8006002AA /* macOS-Framework.xcconfig */; buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -2659,7 +2617,7 @@ }; D047260219E49ED7006002AA /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; + baseConfigurationReference = D047263A19E49FE8006002AA /* macOS-Framework.xcconfig */; buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -2671,7 +2629,7 @@ }; D047260419E49ED7006002AA /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; + baseConfigurationReference = E6124BA8267DF3C0005A3490 /* macOS-XCTest.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; MACOSX_DEPLOYMENT_TARGET = 10.10; @@ -2682,7 +2640,7 @@ }; D047260519E49ED7006002AA /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; + baseConfigurationReference = E6124BA8267DF3C0005A3490 /* macOS-XCTest.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; MACOSX_DEPLOYMENT_TARGET = 10.10; @@ -2697,7 +2655,6 @@ buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.0; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; @@ -2710,7 +2667,6 @@ buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.0; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; @@ -2721,7 +2677,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; buildSettings = { - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; SWIFT_OBJC_BRIDGING_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h"; @@ -2732,7 +2687,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; buildSettings = { - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; SWIFT_OBJC_BRIDGING_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h"; @@ -2752,10 +2706,12 @@ CLANG_WARN_STRICT_PROTOTYPES = YES; CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; IPHONEOS_DEPLOYMENT_TARGET = 9.0; MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; + SWIFT_TREAT_WARNINGS_AS_ERRORS = NO; SWIFT_VERSION = 5.0; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; @@ -2766,7 +2722,7 @@ }; D047263E19E4A008006002AA /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; + baseConfigurationReference = D047263A19E49FE8006002AA /* macOS-Framework.xcconfig */; buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -2778,7 +2734,7 @@ }; D047263F19E4A008006002AA /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; + baseConfigurationReference = E6124BA8267DF3C0005A3490 /* macOS-XCTest.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; MACOSX_DEPLOYMENT_TARGET = 10.10; @@ -2793,7 +2749,6 @@ buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.0; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; @@ -2804,7 +2759,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; buildSettings = { - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; SWIFT_OBJC_BRIDGING_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h"; @@ -2824,10 +2778,12 @@ CLANG_WARN_STRICT_PROTOTYPES = YES; CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; IPHONEOS_DEPLOYMENT_TARGET = 9.0; MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)-Tests"; PRODUCT_NAME = "$(PROJECT_NAME)"; + SWIFT_TREAT_WARNINGS_AS_ERRORS = NO; SWIFT_VERSION = 5.0; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; @@ -2838,7 +2794,7 @@ }; D047264319E4A00B006002AA /* Test */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047263A19E49FE8006002AA /* Mac-Framework.xcconfig */; + baseConfigurationReference = D047263A19E49FE8006002AA /* macOS-Framework.xcconfig */; buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -2850,7 +2806,7 @@ }; D047264419E4A00B006002AA /* Test */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D047263719E49FE8006002AA /* Mac-Application.xcconfig */; + baseConfigurationReference = E6124BA8267DF3C0005A3490 /* macOS-XCTest.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; MACOSX_DEPLOYMENT_TARGET = 10.10; @@ -2865,7 +2821,6 @@ buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; - ENABLE_BITCODE = YES; INFOPLIST_FILE = ReactiveCocoa/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.0; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; @@ -2876,7 +2831,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = D047263219E49FE8006002AA /* iOS-Application.xcconfig */; buildSettings = { - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; SWIFT_OBJC_BRIDGING_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h"; diff --git a/ReactiveCocoaTests/InterceptingSpec.swift b/ReactiveCocoaTests/InterceptingSpec.swift index 5f46ffada7..b168fc3089 100644 --- a/ReactiveCocoaTests/InterceptingSpec.swift +++ b/ReactiveCocoaTests/InterceptingSpec.swift @@ -838,13 +838,14 @@ class InterceptingSpec: QuickSpec { } it("should not deadlock") { + let queue = DispatchQueue.global() for _ in 1 ... 10 { var isDeadlocked = true - DispatchQueue.global(priority: .high).async { + queue.async { _ = object.reactive.signal(for: #selector(object.increment)) - DispatchQueue.global(priority: .high).async { + queue.async { _ = object.reactive.signal(for: #selector(object.increment)) isDeadlocked = false From 579364393ad587352bcce133b7b3f73392cb585b Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Tue, 7 Sep 2021 13:59:24 +0100 Subject: [PATCH 1017/1028] 11.2.2 --- CHANGELOG.md | 3 +++ ReactiveCocoa.podspec | 2 +- ReactiveCocoa/Info.plist | 2 +- ReactiveMapKit.podspec | 2 +- ReactiveMapKit/Info.plist | 2 +- 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 43700d2bdd..d64e94bfb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # master + +# 11.2.2 + 1. Building from Xcode project no longer warns about use of deprecated `class` keyword usage. (#3726, kudos to @michalsrutek) 2. Updated Carthage xcconfig dependency to 1.1 for proper building arm64 macOS variants. diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index b48dfcccbc..47a56bb174 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "11.2.1" + s.version = "11.2.2" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveCocoa/Info.plist b/ReactiveCocoa/Info.plist index cc02f94e92..a37f7a6100 100644 --- a/ReactiveCocoa/Info.plist +++ b/ReactiveCocoa/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 11.2.1 + 11.2.2 CFBundleSignature ???? CFBundleVersion diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index 590cc860ad..83b59169ca 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveMapKit" - s.version = "11.2.1" + s.version = "11.2.2" s.summary = "MapKit bindings for ReactiveCocoa." s.description = <<-DESC Provide MapKit bindings for ReactiveCocoa. ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveMapKit/Info.plist b/ReactiveMapKit/Info.plist index 36c2d70b7c..2ff302122a 100644 --- a/ReactiveMapKit/Info.plist +++ b/ReactiveMapKit/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 11.2.1 + 11.2.2 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass From ea56cc7db441974ea126142350c0d00ae844fd8b Mon Sep 17 00:00:00 2001 From: NachoSoto Date: Tue, 5 Oct 2021 10:26:01 +0200 Subject: [PATCH 1018/1028] SUPPORTS_MACCATALYST --- ReactiveCocoa.xcodeproj/project.pbxproj | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index a882d3ba1c..1be96379dd 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -2463,6 +2463,7 @@ INFOPLIST_FILE = ReactiveMapKit/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; PRODUCT_NAME = ReactiveMapKit; + SUPPORTS_MACCATALYST = YES; }; name = Debug; }; @@ -2475,6 +2476,7 @@ INFOPLIST_FILE = ReactiveMapKit/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; PRODUCT_NAME = ReactiveMapKit; + SUPPORTS_MACCATALYST = YES; }; name = Test; }; @@ -2487,6 +2489,7 @@ INFOPLIST_FILE = ReactiveMapKit/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; PRODUCT_NAME = ReactiveMapKit; + SUPPORTS_MACCATALYST = YES; }; name = Release; }; @@ -2499,6 +2502,7 @@ INFOPLIST_FILE = ReactiveMapKit/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKit; PRODUCT_NAME = ReactiveMapKit; + SUPPORTS_MACCATALYST = YES; }; name = Profile; }; @@ -2612,6 +2616,7 @@ FRAMEWORK_VERSION = A; INFOPLIST_FILE = ReactiveCocoa/Info.plist; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; + SUPPORTS_MACCATALYST = YES; }; name = Debug; }; @@ -2624,6 +2629,7 @@ FRAMEWORK_VERSION = A; INFOPLIST_FILE = ReactiveCocoa/Info.plist; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; + SUPPORTS_MACCATALYST = YES; }; name = Release; }; @@ -2729,6 +2735,7 @@ FRAMEWORK_VERSION = A; INFOPLIST_FILE = ReactiveCocoa/Info.plist; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; + SUPPORTS_MACCATALYST = YES; }; name = Profile; }; @@ -2801,6 +2808,7 @@ FRAMEWORK_VERSION = A; INFOPLIST_FILE = ReactiveCocoa/Info.plist; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; + SUPPORTS_MACCATALYST = YES; }; name = Test; }; From fa0635820a45d5606e8c179e3a96107bef4f6379 Mon Sep 17 00:00:00 2001 From: NachoSoto Date: Tue, 5 Oct 2021 10:37:38 +0200 Subject: [PATCH 1019/1028] Compiling several files in macOS target These are useful when building for `macCatalyst`. Thanks to the existing `if canImport(UIKit)` it doesn't break the `macOS` build. --- ReactiveCocoa.xcodeproj/project.pbxproj | 32 +++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 1be96379dd..8efbceae44 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -51,6 +51,22 @@ 538DCB7F1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB7C1DCA5E9B00332880 /* NSLayoutConstraintSpec.swift */; }; 53AC46CC1DD6F97400C799E1 /* UISlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53AC46CB1DD6F97400C799E1 /* UISlider.swift */; }; 53AC46CF1DD6FC0000C799E1 /* UISliderSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53AC46CE1DD6FC0000C799E1 /* UISliderSpec.swift */; }; + 57911A0D270C43AE00E26A24 /* UISwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F71D93E9F100ACF44C /* UISwitch.swift */; }; + 57911A0E270C43B200E26A24 /* UIActivityIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05EC1D93E9F100ACF44C /* UIActivityIndicatorView.swift */; }; + 57911A0F270C43B600E26A24 /* UIBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05ED1D93E9F100ACF44C /* UIBarButtonItem.swift */; }; + 57911A10270C43BA00E26A24 /* UIBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05EE1D93E9F100ACF44C /* UIBarItem.swift */; }; + 57911A11270C43BC00E26A24 /* UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05EF1D93E9F100ACF44C /* UIButton.swift */; }; + 57911A12270C43BF00E26A24 /* UICollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE231DCFCF5C0066A8C2 /* UICollectionView.swift */; }; + 57911A13270C43C100E26A24 /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F11D93E9F100ACF44C /* UIControl.swift */; }; + 57911A14270C43C400E26A24 /* UIGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 419139431DB910570043C9D1 /* UIGestureRecognizer.swift */; }; + 57911A15270C43C600E26A24 /* UIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F31D93E9F100ACF44C /* UIImageView.swift */; }; + 57911A16270C43CB00E26A24 /* UILabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F41D93E9F100ACF44C /* UILabel.swift */; }; + 57911A17270C43CE00E26A24 /* UINavigationItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D1C1E94ECAC002A9BCC /* UINavigationItem.swift */; }; + 57911A18270C43D400E26A24 /* UIScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF4335641E02AC7600AC88DD /* UIScrollView.swift */; }; + 57911A19270C43D800E26A24 /* UISegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F61D93E9F100ACF44C /* UISegmentedControl.swift */; }; + 57911A1B270C43E000E26A24 /* UITableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFDE11DCFC8560066A8C2 /* UITableView.swift */; }; + 57911A1C270C43E700E26A24 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05FC1D93E9F100ACF44C /* UIView.swift */; }; + 57911A1D270C43EA00E26A24 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE6372D2090EEFA00ECD02A /* UIViewController.swift */; }; 57A4D20A1BA13D7A00F7D4B1 /* ReactiveCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; }; 5B76DE402498EDDC00E8B4F3 /* QueueScheduler+Factory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B76DE3F2498EDDC00E8B4F3 /* QueueScheduler+Factory.swift */; }; 5B76DE412498EDDC00E8B4F3 /* QueueScheduler+Factory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B76DE3F2498EDDC00E8B4F3 /* QueueScheduler+Factory.swift */; }; @@ -1927,34 +1943,50 @@ 4ABEFE2B1DCFD0030066A8C2 /* NSTableView.swift in Sources */, CD0C45DE1CC9A288009F5BF0 /* DynamicProperty.swift in Sources */, 4ABEFE301DCFD0530066A8C2 /* NSCollectionView.swift in Sources */, + 57911A1C270C43E700E26A24 /* UIView.swift in Sources */, + 57911A1D270C43EA00E26A24 /* UIViewController.swift in Sources */, + 57911A14270C43C400E26A24 /* UIGestureRecognizer.swift in Sources */, + 57911A13270C43C100E26A24 /* UIControl.swift in Sources */, 537EC08121A950CA00D6EE18 /* NSView.swift in Sources */, 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, 9AA0BD8F1DDE29F800531FCF /* NSObject+ObjCRuntime.swift in Sources */, + 57911A10270C43BA00E26A24 /* UIBarItem.swift in Sources */, + 57911A0D270C43AE00E26A24 /* UISwitch.swift in Sources */, 9A1D05E01D93E99100ACF44C /* NSObject+Association.swift in Sources */, 538DCB791DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */, 9A7488481E3B8ACE00CD0317 /* DelegateProxy.swift in Sources */, 9AE7C2A41DDD7F5100F7534C /* ObjC+Messages.swift in Sources */, + 57911A16270C43CB00E26A24 /* UILabel.swift in Sources */, 006518761E26865800C3139A /* NSButton.swift in Sources */, 9A54A21B1DE00D09001739B3 /* ObjC+Selector.swift in Sources */, + 57911A11270C43BC00E26A24 /* UIButton.swift in Sources */, + 57911A0F270C43B600E26A24 /* UIBarButtonItem.swift in Sources */, 9AA0BD8A1DDE153A00531FCF /* ObjC+Constants.swift in Sources */, 9AED64C51E496A3700321004 /* ActionProxy.swift in Sources */, 9A6AAA261DB8F5280013AAEA /* AppKitReusableComponents.swift in Sources */, 9A90374F1ED61C6300345D62 /* ReactiveSwift+Lifetime.swift in Sources */, 9AA0BD7C1DDE03DE00531FCF /* ObjC+Runtime.swift in Sources */, 9ADFE5A51DC0001C001E11F7 /* Synchronizing.swift in Sources */, + 57911A0E270C43B200E26A24 /* UIActivityIndicatorView.swift in Sources */, 9ADE4A7C1DA44A9E005C2AC8 /* CocoaAction.swift in Sources */, 9ADE4A961DA6F018005C2AC8 /* NSTextField.swift in Sources */, 9AA0BD981DDE7A2200531FCF /* ObjCRuntimeAliases.m in Sources */, + 57911A1B270C43E000E26A24 /* UITableView.swift in Sources */, + 57911A18270C43D400E26A24 /* UIScrollView.swift in Sources */, + 57911A19270C43D800E26A24 /* UISegmentedControl.swift in Sources */, 7A8BA0FA1FCC86FC003241C7 /* NSTextView.swift in Sources */, + 57911A17270C43CE00E26A24 /* UINavigationItem.swift in Sources */, 9A2E425E1DAA6737006D909F /* CocoaTarget.swift in Sources */, 9AB15C7A1E26CD9A00997378 /* Deprecations+Removals.swift in Sources */, 8392D8FD1DB93E5E00504ED4 /* NSImageView.swift in Sources */, + 57911A12270C43BF00E26A24 /* UICollectionView.swift in Sources */, 9ADE4A891DA6D206005C2AC8 /* NSControl.swift in Sources */, 9AF0EA751D9A7FF700F27DDF /* NSObject+BindingTarget.swift in Sources */, D9558AB81DFF805A003254E1 /* NSPopUpButton.swift in Sources */, 834DE1141E4122910099F4E5 /* NSSlider.swift in Sources */, 9ADE4A911DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift in Sources */, 834DE1121E4120340099F4E5 /* NSSegmentedControl.swift in Sources */, + 57911A15270C43C600E26A24 /* UIImageView.swift in Sources */, 9A1D065B1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, 4A0E10FF1D2A92720065D310 /* AnyObject+Lifetime.swift in Sources */, 9AA0BD811DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift in Sources */, From 9151737e66be56c4bcb5fdf063c10e029add961a Mon Sep 17 00:00:00 2001 From: NachoSoto Date: Wed, 3 Nov 2021 06:31:07 -0700 Subject: [PATCH 1020/1028] Added missing `UIKitReusableComponents` to macOS target (#3730) Follow up to #3729. --- ReactiveCocoa.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 8efbceae44..b2233b0759 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -67,6 +67,7 @@ 57911A1B270C43E000E26A24 /* UITableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFDE11DCFC8560066A8C2 /* UITableView.swift */; }; 57911A1C270C43E700E26A24 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05FC1D93E9F100ACF44C /* UIView.swift */; }; 57911A1D270C43EA00E26A24 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE6372D2090EEFA00ECD02A /* UIViewController.swift */; }; + 579C7D4E2731BC43009F8A2F /* UIKitReusableComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA221DB8F51C0013AAEA /* UIKitReusableComponents.swift */; }; 57A4D20A1BA13D7A00F7D4B1 /* ReactiveCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; }; 5B76DE402498EDDC00E8B4F3 /* QueueScheduler+Factory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B76DE3F2498EDDC00E8B4F3 /* QueueScheduler+Factory.swift */; }; 5B76DE412498EDDC00E8B4F3 /* QueueScheduler+Factory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B76DE3F2498EDDC00E8B4F3 /* QueueScheduler+Factory.swift */; }; @@ -1951,6 +1952,7 @@ 9AD0F06A1D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift in Sources */, 9AA0BD8F1DDE29F800531FCF /* NSObject+ObjCRuntime.swift in Sources */, 57911A10270C43BA00E26A24 /* UIBarItem.swift in Sources */, + 579C7D4E2731BC43009F8A2F /* UIKitReusableComponents.swift in Sources */, 57911A0D270C43AE00E26A24 /* UISwitch.swift in Sources */, 9A1D05E01D93E99100ACF44C /* NSObject+Association.swift in Sources */, 538DCB791DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */, From 74d1737f33e5dabf69b76c5f26f47ce30f1566c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thi=20Do=C3=A3n?= Date: Wed, 3 Nov 2021 23:53:05 +0900 Subject: [PATCH 1021/1028] Fix broken filenames (#3727) Some filenames contain invisible characters that make the project unable build with certain build system. Co-authored-by: Anders Ha --- ReactiveCocoa.xcodeproj/project.pbxproj | 24 +++++++++---------- .../UIKit/iOS/UIImpactFeedbackGenerator.swift | 0 .../iOS/UINotificationFeedbackGenerator.swift | 0 .../iOS/UISelectionFeedbackGenerator.swift | 0 4 files changed, 12 insertions(+), 12 deletions(-) rename "ReactiveCocoa/UIKit/iOS/UIImpact\342\200\213Feedback\342\200\213Generator.swift" => ReactiveCocoa/UIKit/iOS/UIImpactFeedbackGenerator.swift (100%) rename "ReactiveCocoa/UIKit/iOS/UINotification\342\200\213Feedback\342\200\213Generator.swift" => ReactiveCocoa/UIKit/iOS/UINotificationFeedbackGenerator.swift (100%) rename "ReactiveCocoa/UIKit/iOS/UISelection\342\200\213Feedback\342\200\213Generator.swift" => ReactiveCocoa/UIKit/iOS/UISelectionFeedbackGenerator.swift (100%) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index b2233b0759..ffe5629a04 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -87,6 +87,9 @@ 834DE1121E4120340099F4E5 /* NSSegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 834DE1111E4120340099F4E5 /* NSSegmentedControl.swift */; }; 834DE1141E4122910099F4E5 /* NSSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 834DE1131E4122910099F4E5 /* NSSlider.swift */; }; 8392D8FD1DB93E5E00504ED4 /* NSImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8392D8FC1DB93E5E00504ED4 /* NSImageView.swift */; }; + 84ADBDAB26660EA800ACE342 /* UIImpactFeedbackGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84ADBDA826660EA800ACE342 /* UIImpactFeedbackGenerator.swift */; }; + 84ADBDB026660EE800ACE342 /* UINotificationFeedbackGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84ADBDAE26660EE800ACE342 /* UINotificationFeedbackGenerator.swift */; }; + 84ADBDB126660EE800ACE342 /* UISelectionFeedbackGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84ADBDAF26660EE800ACE342 /* UISelectionFeedbackGenerator.swift */; }; 9A0726F31E912B610081F3F7 /* ActionProxySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A0726F21E912B610081F3F7 /* ActionProxySpec.swift */; }; 9A16755D1F80DE1C00B63650 /* MKMapViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6BED51DD4BD2C0016C058 /* MKMapViewSpec.swift */; }; 9A1D05E01D93E99100ACF44C /* NSObject+Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */; }; @@ -316,9 +319,6 @@ A9EB3D2C1E94F5A2002A9BCC /* UITabBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D241E94F335002A9BCC /* UITabBarItem.swift */; }; A9EB3D2D1E94F5A2002A9BCC /* UITabBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D241E94F335002A9BCC /* UITabBarItem.swift */; }; A9EB3D811E955602002A9BCC /* UIFeedbackGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D7D1E955602002A9BCC /* UIFeedbackGenerator.swift */; }; - A9EB3D821E955602002A9BCC /* UIImpact​Feedback​Generator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D7E1E955602002A9BCC /* UIImpact​Feedback​Generator.swift */; }; - A9EB3D831E955602002A9BCC /* UINotification​Feedback​Generator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D7F1E955602002A9BCC /* UINotification​Feedback​Generator.swift */; }; - A9EB3D841E955602002A9BCC /* UISelection​Feedback​Generator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D801E955602002A9BCC /* UISelection​Feedback​Generator.swift */; }; B696FB811A7640C00075236D /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B696FB801A7640C00075236D /* TestError.swift */; }; B696FB821A7640C00075236D /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B696FB801A7640C00075236D /* TestError.swift */; }; BEE020661D637B0000DF261F /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B696FB801A7640C00075236D /* TestError.swift */; }; @@ -520,6 +520,9 @@ 834DE1111E4120340099F4E5 /* NSSegmentedControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSSegmentedControl.swift; sourceTree = ""; }; 834DE1131E4122910099F4E5 /* NSSlider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSSlider.swift; sourceTree = ""; }; 8392D8FC1DB93E5E00504ED4 /* NSImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSImageView.swift; sourceTree = ""; }; + 84ADBDA826660EA800ACE342 /* UIImpactFeedbackGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImpactFeedbackGenerator.swift; sourceTree = ""; }; + 84ADBDAE26660EE800ACE342 /* UINotificationFeedbackGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UINotificationFeedbackGenerator.swift; sourceTree = ""; }; + 84ADBDAF26660EE800ACE342 /* UISelectionFeedbackGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISelectionFeedbackGenerator.swift; sourceTree = ""; }; 9A0726F21E912B610081F3F7 /* ActionProxySpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionProxySpec.swift; sourceTree = ""; }; 9A16753D1F80C35100B63650 /* ReactiveMapKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveMapKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 9A1675411F80C35100B63650 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -620,9 +623,6 @@ A9EB3D241E94F335002A9BCC /* UITabBarItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITabBarItem.swift; sourceTree = ""; }; A9EB3D261E94F3C7002A9BCC /* UITabBarItemSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITabBarItemSpec.swift; sourceTree = ""; }; A9EB3D7D1E955602002A9BCC /* UIFeedbackGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIFeedbackGenerator.swift; sourceTree = ""; }; - A9EB3D7E1E955602002A9BCC /* UIImpact​Feedback​Generator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImpact​Feedback​Generator.swift"; sourceTree = ""; }; - A9EB3D7F1E955602002A9BCC /* UINotification​Feedback​Generator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UINotification​Feedback​Generator.swift"; sourceTree = ""; }; - A9EB3D801E955602002A9BCC /* UISelection​Feedback​Generator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UISelection​Feedback​Generator.swift"; sourceTree = ""; }; B696FB801A7640C00075236D /* TestError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestError.swift; sourceTree = ""; }; BF4335641E02AC7600AC88DD /* UIScrollView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIScrollView.swift; sourceTree = ""; }; BF4335661E02EEDE00AC88DD /* UIScrollViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIScrollViewSpec.swift; sourceTree = ""; }; @@ -1013,13 +1013,13 @@ children = ( 9A1D05F21D93E9F100ACF44C /* UIDatePicker.swift */, A9EB3D7D1E955602002A9BCC /* UIFeedbackGenerator.swift */, - A9EB3D7E1E955602002A9BCC /* UIImpact​Feedback​Generator.swift */, + 84ADBDA826660EA800ACE342 /* UIImpactFeedbackGenerator.swift */, + 84ADBDAE26660EE800ACE342 /* UINotificationFeedbackGenerator.swift */, + 84ADBDAF26660EE800ACE342 /* UISelectionFeedbackGenerator.swift */, 9AAD49871DED2C350068EC9B /* UIKeyboard.swift */, - A9EB3D7F1E955602002A9BCC /* UINotification​Feedback​Generator.swift */, BFE145881E43991A00208736 /* UIPickerView.swift */, 3BCAAC791DEE19BC00B30335 /* UIRefreshControl.swift */, BFCF775E1DFAD8A50058006E /* UISearchBar.swift */, - A9EB3D801E955602002A9BCC /* UISelection​Feedback​Generator.swift */, 53AC46CB1DD6F97400C799E1 /* UISlider.swift */, 531866F71DD7920400D1285F /* UIStepper.swift */, 9A1D05F71D93E9F100ACF44C /* UISwitch.swift */, @@ -2036,14 +2036,12 @@ buildActionMask = 2147483647; files = ( A9EB3D201E94F08A002A9BCC /* UINavigationItem.swift in Sources */, - A9EB3D841E955602002A9BCC /* UISelection​Feedback​Generator.swift in Sources */, 4ABEFE251DCFCF630066A8C2 /* UICollectionView.swift in Sources */, 9A1D06011D93EA0000ACF44C /* UIBarItem.swift in Sources */, A9EB3D811E955602002A9BCC /* UIFeedbackGenerator.swift in Sources */, 9A1D060D1D93EA0000ACF44C /* UITextField.swift in Sources */, 9A9037501ED61C6300345D62 /* ReactiveSwift+Lifetime.swift in Sources */, 9AB15C7B1E26CD9A00997378 /* Deprecations+Removals.swift in Sources */, - A9EB3D831E955602002A9BCC /* UINotification​Feedback​Generator.swift in Sources */, 9A6AAA231DB8F51C0013AAEA /* UIKitReusableComponents.swift in Sources */, 9A7488491E3B8ACE00CD0317 /* DelegateProxy.swift in Sources */, 9A1D060E1D93EA0000ACF44C /* UITextView.swift in Sources */, @@ -2065,12 +2063,12 @@ 9A1D06021D93EA0000ACF44C /* UIButton.swift in Sources */, 4EE6372E2090EEFA00ECD02A /* UIViewController.swift in Sources */, 9AA0BD901DDE29F800531FCF /* NSObject+ObjCRuntime.swift in Sources */, - A9EB3D821E955602002A9BCC /* UIImpact​Feedback​Generator.swift in Sources */, 419139461DB910570043C9D1 /* UIGestureRecognizer.swift in Sources */, 9AA0BD7D1DDE03DE00531FCF /* ObjC+Runtime.swift in Sources */, BFE1458A1E439AB000208736 /* UIPickerView.swift in Sources */, 9A1D060A1D93EA0000ACF44C /* UISwitch.swift in Sources */, 9A1D065C1D93EC6E00ACF44C /* NSObject+Intercepting.swift in Sources */, + 84ADBDB026660EE800ACE342 /* UINotificationFeedbackGenerator.swift in Sources */, 9A1D06071D93EA0000ACF44C /* UILabel.swift in Sources */, BF4335651E02AC7600AC88DD /* UIScrollView.swift in Sources */, 4A0E11001D2A92720065D310 /* AnyObject+Lifetime.swift in Sources */, @@ -2082,9 +2080,11 @@ 531866F81DD7920400D1285F /* UIStepper.swift in Sources */, A9EB3D2C1E94F5A2002A9BCC /* UITabBarItem.swift in Sources */, 538DCB7A1DCA5E6C00332880 /* NSLayoutConstraint.swift in Sources */, + 84ADBDB126660EE800ACE342 /* UISelectionFeedbackGenerator.swift in Sources */, 9A1D06001D93EA0000ACF44C /* UIBarButtonItem.swift in Sources */, 9AA0BD991DDE7A2200531FCF /* ObjCRuntimeAliases.m in Sources */, 9A1D05FF1D93EA0000ACF44C /* UIActivityIndicatorView.swift in Sources */, + 84ADBDAB26660EA800ACE342 /* UIImpactFeedbackGenerator.swift in Sources */, 9A54A21C1DE00D09001739B3 /* ObjC+Selector.swift in Sources */, 9AA0BD8B1DDE153A00531FCF /* ObjC+Constants.swift in Sources */, 9A1D06091D93EA0000ACF44C /* UISegmentedControl.swift in Sources */, diff --git "a/ReactiveCocoa/UIKit/iOS/UIImpact\342\200\213Feedback\342\200\213Generator.swift" b/ReactiveCocoa/UIKit/iOS/UIImpactFeedbackGenerator.swift similarity index 100% rename from "ReactiveCocoa/UIKit/iOS/UIImpact\342\200\213Feedback\342\200\213Generator.swift" rename to ReactiveCocoa/UIKit/iOS/UIImpactFeedbackGenerator.swift diff --git "a/ReactiveCocoa/UIKit/iOS/UINotification\342\200\213Feedback\342\200\213Generator.swift" b/ReactiveCocoa/UIKit/iOS/UINotificationFeedbackGenerator.swift similarity index 100% rename from "ReactiveCocoa/UIKit/iOS/UINotification\342\200\213Feedback\342\200\213Generator.swift" rename to ReactiveCocoa/UIKit/iOS/UINotificationFeedbackGenerator.swift diff --git "a/ReactiveCocoa/UIKit/iOS/UISelection\342\200\213Feedback\342\200\213Generator.swift" b/ReactiveCocoa/UIKit/iOS/UISelectionFeedbackGenerator.swift similarity index 100% rename from "ReactiveCocoa/UIKit/iOS/UISelection\342\200\213Feedback\342\200\213Generator.swift" rename to ReactiveCocoa/UIKit/iOS/UISelectionFeedbackGenerator.swift From 791726533eb42f25b802c0b5596ae5703fcdb624 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Fri, 19 Nov 2021 23:01:24 +0000 Subject: [PATCH 1022/1028] Require ReactiveSwift 7.0. (#3731) --- CHANGELOG.md | 1 + Cartfile | 2 +- Cartfile.resolved | 4 ++-- Carthage/Checkouts/Nimble | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- Package.swift | 2 +- ReactiveCocoa.podspec | 2 +- 7 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d64e94bfb6..bb5e367fb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # master +1. Requires ReactiveSwift 7.0. # 11.2.2 diff --git a/Cartfile b/Cartfile index 18d3112e54..4220caa248 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" ~> 6.6 +github "ReactiveCocoa/ReactiveSwift" ~> 7.0 diff --git a/Cartfile.resolved b/Cartfile.resolved index 9332ad0190..926376da90 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,4 +1,4 @@ -github "Quick/Nimble" "v9.2.0" +github "Quick/Nimble" "v9.2.1" github "Quick/Quick" "v4.0.0" -github "ReactiveCocoa/ReactiveSwift" "6.6.1" +github "ReactiveCocoa/ReactiveSwift" "7.0.0" github "xcconfigs/xcconfigs" "1.1" diff --git a/Carthage/Checkouts/Nimble b/Carthage/Checkouts/Nimble index af1730dde4..c93f16c25a 160000 --- a/Carthage/Checkouts/Nimble +++ b/Carthage/Checkouts/Nimble @@ -1 +1 @@ -Subproject commit af1730dde4e6c0d45bf01b99f8a41713ce536790 +Subproject commit c93f16c25af5770f0d3e6af27c9634640946b068 diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index 2c06e94210..efb2f0a6f6 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit 2c06e9421011a1a4cf57ea4bbcd8936843a6c9da +Subproject commit efb2f0a6f6c8739cce8fb14148a5bd3c83f2f91d diff --git a/Package.swift b/Package.swift index 8b74bbe04c..9754d33d76 100644 --- a/Package.swift +++ b/Package.swift @@ -11,7 +11,7 @@ let package = Package( .library(name: "ReactiveCocoa", targets: ["ReactiveCocoa"]) ], dependencies: [ - .package(url: "https://github.com/ReactiveCocoa/ReactiveSwift", from: "6.6.0"), + .package(url: "https://github.com/ReactiveCocoa/ReactiveSwift", from: "7.0.0"), .package(url: "https://github.com/Quick/Quick.git", from: "4.0.0"), .package(url: "https://github.com/Quick/Nimble.git", from: "9.0.0"), ], diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 47a56bb174..7c0d67cef9 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -24,7 +24,7 @@ Pod::Spec.new do |s| s.watchos.source_files = "ReactiveCocoa/WatchKit/*.{swift}" s.module_name = 'ReactiveCocoa' - s.dependency 'ReactiveSwift', '~> 6.6' + s.dependency 'ReactiveSwift', '~> 7.0' s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS[config=Release]" => "$(inherited) -suppress-warnings" } s.swift_versions = ['5.1', '5.2'] From 8a5b07b18c6abb8e12fb845bf742675dc4914ed8 Mon Sep 17 00:00:00 2001 From: Anders Ha Date: Sat, 20 Nov 2021 14:18:05 +0000 Subject: [PATCH 1023/1028] 12.0 --- CHANGELOG.md | 3 ++- ReactiveCocoa.podspec | 2 +- ReactiveCocoa/Info.plist | 2 +- ReactiveMapKit.podspec | 2 +- ReactiveMapKit/Info.plist | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb5e367fb4..198cdb9f4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # master -1. Requires ReactiveSwift 7.0. + +# 12.0.01. Requires ReactiveSwift 7.0. # 11.2.2 diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 7c0d67cef9..874bd5255a 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveCocoa" - s.version = "11.2.2" + s.version = "12.0.0" s.summary = "Streams of values over time" s.description = <<-DESC ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveCocoa/Info.plist b/ReactiveCocoa/Info.plist index a37f7a6100..04e9494b8a 100644 --- a/ReactiveCocoa/Info.plist +++ b/ReactiveCocoa/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 11.2.2 + 12.0.0 CFBundleSignature ???? CFBundleVersion diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index 83b59169ca..ba48c74857 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ReactiveMapKit" - s.version = "11.2.2" + s.version = "12.0.0" s.summary = "MapKit bindings for ReactiveCocoa." s.description = <<-DESC Provide MapKit bindings for ReactiveCocoa. ReactiveCocoa (RAC) is a Cocoa framework built on top of ReactiveSwift. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. diff --git a/ReactiveMapKit/Info.plist b/ReactiveMapKit/Info.plist index 2ff302122a..db11367805 100644 --- a/ReactiveMapKit/Info.plist +++ b/ReactiveMapKit/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 11.2.2 + 12.0.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass From dd3c652ca1f731acc20f7b71632e58df2fbcaae7 Mon Sep 17 00:00:00 2001 From: NachoSoto Date: Mon, 18 Apr 2022 20:11:21 +0200 Subject: [PATCH 1024/1028] Fixed build phases order in target See [Xcode 10's known issues](https://developer.apple.com/documentation/xcode-release-notes/build-system-release-notes-for-xcode-10) in the release notes: > Targets with Copy Headers build phases ordered after Compile Sources build phases may fail to build and emit a diagnostic regarding build cycles. (39880168) >*Workaround*: Arrange any Copy Headers build phases before Compile Sources build phases. This has been an issue for nearly 4 years, and now with Xcode 13.3 it leads to `XCBBuildService` to crash consistently. --- ReactiveCocoa.xcodeproj/project.pbxproj | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index ffe5629a04..cdfd05d16b 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -1326,9 +1326,9 @@ isa = PBXNativeTarget; buildConfigurationList = 57A4D23C1BA13D7A00F7D4B1 /* Build configuration list for PBXNativeTarget "ReactiveCocoa-tvOS" */; buildPhases = ( + 57A4D2091BA13D7A00F7D4B1 /* Headers */, 57A4D1B01BA13D7A00F7D4B1 /* Sources */, 57A4D2071BA13D7A00F7D4B1 /* Frameworks */, - 57A4D2091BA13D7A00F7D4B1 /* Headers */, 57A4D23B1BA13D7A00F7D4B1 /* Resources */, ); buildRules = ( @@ -1382,9 +1382,9 @@ isa = PBXNativeTarget; buildConfigurationList = 9A73DFB6216D3C550069AD76 /* Build configuration list for PBXNativeTarget "ReactiveMapKit-iOS" */; buildPhases = ( + 9A73DFB4216D3C550069AD76 /* Headers */, 9A73DFAD216D3C550069AD76 /* Sources */, 9A73DFB0216D3C550069AD76 /* Frameworks */, - 9A73DFB4216D3C550069AD76 /* Headers */, 9A73DFB5216D3C550069AD76 /* Resources */, ); buildRules = ( @@ -1400,9 +1400,9 @@ isa = PBXNativeTarget; buildConfigurationList = 9A73DFC7216D3C570069AD76 /* Build configuration list for PBXNativeTarget "ReactiveMapKit-tvOS" */; buildPhases = ( + 9A73DFC5216D3C570069AD76 /* Headers */, 9A73DFBE216D3C570069AD76 /* Sources */, 9A73DFC1216D3C570069AD76 /* Frameworks */, - 9A73DFC5216D3C570069AD76 /* Headers */, 9A73DFC6216D3C570069AD76 /* Resources */, ); buildRules = ( @@ -1456,9 +1456,9 @@ isa = PBXNativeTarget; buildConfigurationList = 9AC03A5C1F7CC3BF00EC33C1 /* Build configuration list for PBXNativeTarget "ReactiveMapKit-macOS" */; buildPhases = ( + 9AC03A541F7CC3BF00EC33C1 /* Headers */, 9AC03A521F7CC3BF00EC33C1 /* Sources */, 9AC03A531F7CC3BF00EC33C1 /* Frameworks */, - 9AC03A541F7CC3BF00EC33C1 /* Headers */, 9AC03A551F7CC3BF00EC33C1 /* Resources */, ); buildRules = ( @@ -1474,9 +1474,9 @@ isa = PBXNativeTarget; buildConfigurationList = A9B3155D1B3940610001CB9C /* Build configuration list for PBXNativeTarget "ReactiveCocoa-watchOS" */; buildPhases = ( + A9B315511B3940610001CB9C /* Headers */, A9B3154F1B3940610001CB9C /* Sources */, A9B315501B3940610001CB9C /* Frameworks */, - A9B315511B3940610001CB9C /* Headers */, A9B315521B3940610001CB9C /* Resources */, ); buildRules = ( @@ -1492,9 +1492,9 @@ isa = PBXNativeTarget; buildConfigurationList = D047260019E49ED7006002AA /* Build configuration list for PBXNativeTarget "ReactiveCocoa-macOS" */; buildPhases = ( + D04725E719E49ED7006002AA /* Headers */, D04725E519E49ED7006002AA /* Sources */, D04725E619E49ED7006002AA /* Frameworks */, - D04725E719E49ED7006002AA /* Headers */, D04725E819E49ED7006002AA /* Resources */, ); buildRules = ( @@ -1528,9 +1528,9 @@ isa = PBXNativeTarget; buildConfigurationList = D047261F19E49F82006002AA /* Build configuration list for PBXNativeTarget "ReactiveCocoa-iOS" */; buildPhases = ( + D047260919E49F82006002AA /* Headers */, D047260719E49F82006002AA /* Sources */, D047260819E49F82006002AA /* Frameworks */, - D047260919E49F82006002AA /* Headers */, D047260A19E49F82006002AA /* Resources */, ); buildRules = ( From c1896eb81255f5b670fbb3c9f6a3059e00ef0dba Mon Sep 17 00:00:00 2001 From: Ian Bytchek Date: Thu, 2 Mar 2023 13:36:55 +0000 Subject: [PATCH 1025/1028] Update ReactiveSwift in Package.resoved --- Package.resolved | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Package.resolved b/Package.resolved index 816482b01e..7740300bb3 100644 --- a/Package.resolved +++ b/Package.resolved @@ -42,8 +42,8 @@ "repositoryURL": "https://github.com/ReactiveCocoa/ReactiveSwift", "state": { "branch": null, - "revision": "2c06e9421011a1a4cf57ea4bbcd8936843a6c9da", - "version": "6.6.1" + "revision": "efb2f0a6f6c8739cce8fb14148a5bd3c83f2f91d", + "version": "7.0.0" } } ] From 50f55563ad5c7718e2674557fea32cdff0056732 Mon Sep 17 00:00:00 2001 From: Ian Bytchek Date: Thu, 2 Mar 2023 14:26:23 +0000 Subject: [PATCH 1026/1028] Add support for ReactiveSwift 7.1.1 - Bump macOS 10.13, iOS 11.0, tvOS 11.0 and watchOS 4.0 as per ReactiveSwift 7.1.1. - Remove redundant conditional `@available` checks for earlier OS version. - Fix `NSButtonSpec` when buttons are used inside `NSStackView`. --- CHANGELOG.md | 5 +- Cartfile.resolved | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- Package.resolved | 16 ++-- Package.swift | 4 +- ReactiveCocoa.podspec | 8 +- ReactiveCocoa.xcodeproj/project.pbxproj | 44 ++++------- .../AppKit/AppKitReusableComponents.swift | 1 - ReactiveCocoa/AppKit/NSCollectionView.swift | 1 - .../AppKit/AppKitReusableComponentsSpec.swift | 22 +++--- ReactiveCocoaTests/AppKit/NSButtonSpec.swift | 73 +++++++++---------- .../AppKit/NSCollectionViewSpec.swift | 2 - ReactiveCocoaTests/InterceptingSpec.swift | 6 +- ReactiveCocoaTests/LifetimeSpec.swift | 12 +-- .../QueueScheduler+Factory.swift | 7 +- ReactiveMapKit.podspec | 6 +- 16 files changed, 89 insertions(+), 122 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 198cdb9f4f..f90f91ed41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,10 @@ # master +1. Add compatibility with ReactiveSwift 7.1.1 and bump minimum deployment targets to macOS 10.13, iOS 11.0, tvOS 11.0 and watchOS 4.0. -# 12.0.01. Requires ReactiveSwift 7.0. +# 12.0.0 + +1. Requires ReactiveSwift 7.0. # 11.2.2 diff --git a/Cartfile.resolved b/Cartfile.resolved index 926376da90..b2e8ee64ed 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,4 +1,4 @@ github "Quick/Nimble" "v9.2.1" github "Quick/Quick" "v4.0.0" -github "ReactiveCocoa/ReactiveSwift" "7.0.0" +github "ReactiveCocoa/ReactiveSwift" "7.1.1" github "xcconfigs/xcconfigs" "1.1" diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index efb2f0a6f6..40c465af19 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit efb2f0a6f6c8739cce8fb14148a5bd3c83f2f91d +Subproject commit 40c465af19b993344e84355c00669ba2022ca3cd diff --git a/Package.resolved b/Package.resolved index 7740300bb3..8d624fbdc5 100644 --- a/Package.resolved +++ b/Package.resolved @@ -6,8 +6,8 @@ "repositoryURL": "https://github.com/mattgallagher/CwlCatchException.git", "state": { "branch": null, - "revision": "682841464136f8c66e04afe5dbd01ab51a3a56f2", - "version": "2.1.0" + "revision": "35f9e770f54ce62dd8526470f14c6e137cef3eea", + "version": "2.1.1" } }, { @@ -15,8 +15,8 @@ "repositoryURL": "https://github.com/mattgallagher/CwlPreconditionTesting.git", "state": { "branch": null, - "revision": "02b7a39a99c4da27abe03cab2053a9034379639f", - "version": "2.0.0" + "revision": "c21f7bab5ca8eee0a9998bbd17ca1d0eb45d4688", + "version": "2.1.0" } }, { @@ -24,8 +24,8 @@ "repositoryURL": "https://github.com/Quick/Nimble.git", "state": { "branch": null, - "revision": "af1730dde4e6c0d45bf01b99f8a41713ce536790", - "version": "9.2.0" + "revision": "c93f16c25af5770f0d3e6af27c9634640946b068", + "version": "9.2.1" } }, { @@ -42,8 +42,8 @@ "repositoryURL": "https://github.com/ReactiveCocoa/ReactiveSwift", "state": { "branch": null, - "revision": "efb2f0a6f6c8739cce8fb14148a5bd3c83f2f91d", - "version": "7.0.0" + "revision": "40c465af19b993344e84355c00669ba2022ca3cd", + "version": "7.1.1" } } ] diff --git a/Package.swift b/Package.swift index 9754d33d76..8b60ce3233 100644 --- a/Package.swift +++ b/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "ReactiveCocoa", platforms: [ - .macOS(.v10_10), .iOS(.v9), .tvOS(.v9), .watchOS(.v2) + .macOS(.v10_13), .iOS(.v11), .tvOS(.v11), .watchOS(.v4) ], products: [ .library(name: "ReactiveCocoa", targets: ["ReactiveCocoa"]) @@ -40,5 +40,5 @@ let package = Package( ], path: "ReactiveCocoaTests"), ], - swiftLanguageVersions: [.v5] + swiftLanguageVersions: [.v5] ) diff --git a/ReactiveCocoa.podspec b/ReactiveCocoa.podspec index 874bd5255a..4106c676ac 100644 --- a/ReactiveCocoa.podspec +++ b/ReactiveCocoa.podspec @@ -9,10 +9,10 @@ Pod::Spec.new do |s| s.license = { :type => "MIT", :file => "LICENSE.md" } s.author = "ReactiveCocoa" - s.osx.deployment_target = "10.9" - s.ios.deployment_target = "9.0" - s.tvos.deployment_target = "9.0" - s.watchos.deployment_target = "2.0" + s.osx.deployment_target = "10.13" + s.ios.deployment_target = "11.0" + s.tvos.deployment_target = "11.0" + s.watchos.deployment_target = "4.0" s.source = { :git => "https://github.com/ReactiveCocoa/ReactiveCocoa.git", :tag => "#{s.version}" } s.source_files = "ReactiveCocoa/*.{swift,h,m}", "ReactiveCocoa/Shared/*.{swift}", "ReactiveCocoaObjC/**/*.{h,m}" diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index cdfd05d16b..2410f947c0 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -2409,7 +2409,6 @@ baseConfigurationReference = D047263719E49FE8006002AA /* macOS-Application.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; - MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; PRODUCT_NAME = ReactiveMapKitTests; }; @@ -2420,7 +2419,6 @@ baseConfigurationReference = D047263719E49FE8006002AA /* macOS-Application.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; - MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; PRODUCT_NAME = ReactiveMapKitTests; }; @@ -2431,7 +2429,6 @@ baseConfigurationReference = D047263719E49FE8006002AA /* macOS-Application.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; - MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; PRODUCT_NAME = ReactiveMapKitTests; }; @@ -2442,7 +2439,6 @@ baseConfigurationReference = D047263719E49FE8006002AA /* macOS-Application.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveMapKitTests/Info.plist; - MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_BUNDLE_IDENTIFIER = org.reactivecocoa.ReactiveMapKitTests; PRODUCT_NAME = ReactiveMapKitTests; }; @@ -2599,16 +2595,16 @@ CURRENT_PROJECT_VERSION = 1; ENABLE_TESTABILITY = YES; GCC_TREAT_WARNINGS_AS_ERRORS = NO; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MACOSX_DEPLOYMENT_TARGET = 10.13; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; SWIFT_TREAT_WARNINGS_AS_ERRORS = NO; SWIFT_VERSION = 5.0; - TVOS_DEPLOYMENT_TARGET = 9.0; + TVOS_DEPLOYMENT_TARGET = 11.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 2.0; + WATCHOS_DEPLOYMENT_TARGET = 4.0; }; name = Debug; }; @@ -2627,17 +2623,17 @@ CURRENT_PROJECT_VERSION = 1; GCC_OPTIMIZATION_LEVEL = 0; GCC_TREAT_WARNINGS_AS_ERRORS = NO; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MACOSX_DEPLOYMENT_TARGET = 10.13; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_TREAT_WARNINGS_AS_ERRORS = NO; SWIFT_VERSION = 5.0; - TVOS_DEPLOYMENT_TARGET = 9.0; + TVOS_DEPLOYMENT_TARGET = 11.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 2.0; + WATCHOS_DEPLOYMENT_TARGET = 4.0; }; name = Release; }; @@ -2672,7 +2668,6 @@ baseConfigurationReference = E6124BA8267DF3C0005A3490 /* macOS-XCTest.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; - MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; SWIFT_OBJC_BRIDGING_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h"; }; @@ -2683,7 +2678,6 @@ baseConfigurationReference = E6124BA8267DF3C0005A3490 /* macOS-XCTest.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; - MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; SWIFT_OBJC_BRIDGING_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h"; }; @@ -2696,7 +2690,6 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; name = Debug; @@ -2708,7 +2701,6 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; name = Release; @@ -2747,16 +2739,16 @@ CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; GCC_TREAT_WARNINGS_AS_ERRORS = NO; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MACOSX_DEPLOYMENT_TARGET = 10.13; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; SWIFT_TREAT_WARNINGS_AS_ERRORS = NO; SWIFT_VERSION = 5.0; - TVOS_DEPLOYMENT_TARGET = 9.0; + TVOS_DEPLOYMENT_TARGET = 11.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 2.0; + WATCHOS_DEPLOYMENT_TARGET = 4.0; }; name = Profile; }; @@ -2778,7 +2770,6 @@ baseConfigurationReference = E6124BA8267DF3C0005A3490 /* macOS-XCTest.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; - MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; SWIFT_OBJC_BRIDGING_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h"; }; @@ -2791,7 +2782,6 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; name = Profile; @@ -2820,16 +2810,16 @@ CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; GCC_TREAT_WARNINGS_AS_ERRORS = NO; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MACOSX_DEPLOYMENT_TARGET = 10.13; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)-Tests"; PRODUCT_NAME = "$(PROJECT_NAME)"; SWIFT_TREAT_WARNINGS_AS_ERRORS = NO; SWIFT_VERSION = 5.0; - TVOS_DEPLOYMENT_TARGET = 9.0; + TVOS_DEPLOYMENT_TARGET = 11.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 2.0; + WATCHOS_DEPLOYMENT_TARGET = 4.0; }; name = Test; }; @@ -2851,7 +2841,6 @@ baseConfigurationReference = E6124BA8267DF3C0005A3490 /* macOS-XCTest.xcconfig */; buildSettings = { INFOPLIST_FILE = ReactiveCocoaTests/Info.plist; - MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; SWIFT_OBJC_BRIDGING_HEADER = "ReactiveCocoaTests/ReactiveCocoaTests-Bridging-Header.h"; }; @@ -2864,7 +2853,6 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; INFOPLIST_FILE = ReactiveCocoa/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; }; name = Test; diff --git a/ReactiveCocoa/AppKit/AppKitReusableComponents.swift b/ReactiveCocoa/AppKit/AppKitReusableComponents.swift index f3a9bdbd8c..d2ff43f50d 100644 --- a/ReactiveCocoa/AppKit/AppKitReusableComponents.swift +++ b/ReactiveCocoa/AppKit/AppKitReusableComponents.swift @@ -9,7 +9,6 @@ extension Reactive where Base: NSView { } extension Reactive where Base: NSObject, Base: NSCollectionViewElement { - @available(macOS 10.11, *) public var prepareForReuse: Signal<(), Never> { return trigger(for: #selector(base.prepareForReuse)) } diff --git a/ReactiveCocoa/AppKit/NSCollectionView.swift b/ReactiveCocoa/AppKit/NSCollectionView.swift index 021af808ba..c10619beab 100644 --- a/ReactiveCocoa/AppKit/NSCollectionView.swift +++ b/ReactiveCocoa/AppKit/NSCollectionView.swift @@ -3,7 +3,6 @@ import AppKit import ReactiveSwift extension Reactive where Base: NSCollectionView { - @available(macOS 10.11, *) public var reloadData: BindingTarget<()> { return makeBindingTarget { base, _ in base.reloadData() } } diff --git a/ReactiveCocoaTests/AppKit/AppKitReusableComponentsSpec.swift b/ReactiveCocoaTests/AppKit/AppKitReusableComponentsSpec.swift index 187fa22015..6c153c8f14 100644 --- a/ReactiveCocoaTests/AppKit/AppKitReusableComponentsSpec.swift +++ b/ReactiveCocoaTests/AppKit/AppKitReusableComponentsSpec.swift @@ -23,21 +23,19 @@ class ReusableComponentsSpec: QuickSpec { } } - if #available(macOS 10.11, *) { - describe("NSCollectionViewItem") { - it("should send a `value` event when `prepareForReuse` is triggered") { - let item = TestViewItem() + describe("NSCollectionViewItem") { + it("should send a `value` event when `prepareForReuse` is triggered") { + let item = TestViewItem() - var isTriggered = false - item.reactive.prepareForReuse.observeValues { - isTriggered = true - } + var isTriggered = false + item.reactive.prepareForReuse.observeValues { + isTriggered = true + } - expect(isTriggered) == false + expect(isTriggered) == false - item.prepareForReuse() - expect(isTriggered) == true - } + item.prepareForReuse() + expect(isTriggered) == true } } } diff --git a/ReactiveCocoaTests/AppKit/NSButtonSpec.swift b/ReactiveCocoaTests/AppKit/NSButtonSpec.swift index badc0fc747..0029d0009d 100644 --- a/ReactiveCocoaTests/AppKit/NSButtonSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSButtonSpec.swift @@ -84,43 +84,42 @@ class NSButtonSpec: QuickSpec { } - if #available(OSX 10.11, *) { - it("should send along state changes embedded within NSStackView") { - - let window = NSWindow() - - let button1 = NSButton() - let button2 = NSButton() - - button1.setButtonType(.pushOnPushOff) - button1.allowsMixedState = false - button1.state = RACNSOffState - - button2.setButtonType(.pushOnPushOff) - button2.allowsMixedState = false - button2.state = RACNSOnState - - let stackView = NSStackView() - stackView.addArrangedSubview(button1) - stackView.addArrangedSubview(button2) - - window.contentView?.addSubview(stackView) - - let state = MutableProperty(RACNSOffState) - state <~ button1.reactive.states - state <~ button2.reactive.states - - button1.performClick(nil) - expect(state.value) == RACNSOnState - - button2.performClick(nil) - expect(state.value) == RACNSOffState - - autoreleasepool { - button1.removeFromSuperview() - button2.removeFromSuperview() - stackView.removeFromSuperview() - } + it("should send along state changes embedded within NSStackView") { + let window = NSWindow() + let button1 = NSButton() + let button2 = NSButton() + + button1.setButtonType(.pushOnPushOff) + button1.allowsMixedState = false + button1.state = RACNSOffState + + button2.setButtonType(.pushOnPushOff) + button2.allowsMixedState = false + button2.state = RACNSOnState + + let stackView = NSStackView() + stackView.addArrangedSubview(button1) + stackView.addArrangedSubview(button2) + + // This is required to avoid crashing as of 10.15, see https://github.com/ReactiveCocoa/ReactiveCocoa/issues/3690 + stackView.detachesHiddenViews = false + + window.contentView?.addSubview(stackView) + + let state = MutableProperty(RACNSOffState) + state <~ button1.reactive.states + state <~ button2.reactive.states + + button1.performClick(nil) + expect(state.value) == RACNSOnState + + button2.performClick(nil) + expect(state.value) == RACNSOffState + + autoreleasepool { + button1.removeFromSuperview() + button2.removeFromSuperview() + stackView.removeFromSuperview() } } diff --git a/ReactiveCocoaTests/AppKit/NSCollectionViewSpec.swift b/ReactiveCocoaTests/AppKit/NSCollectionViewSpec.swift index e68d5987d0..91e480edb8 100644 --- a/ReactiveCocoaTests/AppKit/NSCollectionViewSpec.swift +++ b/ReactiveCocoaTests/AppKit/NSCollectionViewSpec.swift @@ -5,7 +5,6 @@ import ReactiveCocoa import ReactiveSwift import AppKit -@available(macOS 10.11, *) final class NSCollectionViewSpec: QuickSpec { override func spec() { var collectionView: TestCollectionView! @@ -43,7 +42,6 @@ final class NSCollectionViewSpec: QuickSpec { } } -@available(macOS 10.11, *) private final class TestCollectionView: NSCollectionView { let reloadDataSignal: Signal<(), Never> private let reloadDataObserver: Signal<(), Never>.Observer diff --git a/ReactiveCocoaTests/InterceptingSpec.swift b/ReactiveCocoaTests/InterceptingSpec.swift index b168fc3089..b1ce061409 100644 --- a/ReactiveCocoaTests/InterceptingSpec.swift +++ b/ReactiveCocoaTests/InterceptingSpec.swift @@ -85,11 +85,7 @@ class InterceptingSpec: QuickSpec { var isDeadlocked = true func createQueue() -> DispatchQueue { - if #available(*, macOS 10.10) { - return .global(qos: .userInitiated) - } else { - return .global(priority: .high) - } + .global(qos: .userInitiated) } createQueue().async { diff --git a/ReactiveCocoaTests/LifetimeSpec.swift b/ReactiveCocoaTests/LifetimeSpec.swift index dc613119d9..34e08858a4 100644 --- a/ReactiveCocoaTests/LifetimeSpec.swift +++ b/ReactiveCocoaTests/LifetimeSpec.swift @@ -27,11 +27,7 @@ class LifetimeSpec: QuickSpec { var isDeadlocked = true func createQueue() -> DispatchQueue { - if #available(*, macOS 10.10) { - return .global(qos: .userInitiated) - } else { - return .global(priority: .high) - } + .global(qos: .userInitiated) } createQueue().async { @@ -68,11 +64,7 @@ class LifetimeSpec: QuickSpec { var isDeadlocked = true func createQueue() -> DispatchQueue { - if #available(*, macOS 10.10) { - return .global(qos: .userInitiated) - } else { - return .global(priority: .high) - } + return .global(qos: .userInitiated) } createQueue().async { diff --git a/ReactiveCocoaTests/QueueScheduler+Factory.swift b/ReactiveCocoaTests/QueueScheduler+Factory.swift index 8bdf6c8156..f988051c54 100644 --- a/ReactiveCocoaTests/QueueScheduler+Factory.swift +++ b/ReactiveCocoaTests/QueueScheduler+Factory.swift @@ -5,11 +5,6 @@ extension QueueScheduler { static func makeForTesting(file: String = #file, line: UInt = #line) -> QueueScheduler { let file = URL(string: file)?.lastPathComponent ?? "" let label = "reactiveswift:\(file):\(line)" - - if #available(OSX 10.10, iOS 8.0, *) { - return QueueScheduler(name: label) - } else { - return QueueScheduler(name: label) - } + return QueueScheduler(name: label) } } diff --git a/ReactiveMapKit.podspec b/ReactiveMapKit.podspec index ba48c74857..6109d4e670 100644 --- a/ReactiveMapKit.podspec +++ b/ReactiveMapKit.podspec @@ -9,9 +9,9 @@ Pod::Spec.new do |s| s.license = { :type => "MIT", :file => "LICENSE.md" } s.author = "ReactiveCocoa" - s.osx.deployment_target = "10.9" - s.ios.deployment_target = "9.0" - s.tvos.deployment_target = "9.0" + s.osx.deployment_target = "10.13" + s.ios.deployment_target = "11.0" + s.tvos.deployment_target = "11.0" s.source = { :git => "https://github.com/ReactiveCocoa/ReactiveCocoa.git", :tag => "#{s.version}" } s.source_files = "ReactiveMapKit/*.{swift,h,m}" From 23841b242f4d1ff373c51dbe7b744b04effbafee Mon Sep 17 00:00:00 2001 From: Ian Bytchek Date: Thu, 2 Mar 2023 14:39:27 +0000 Subject: [PATCH 1027/1028] Update GitHub test action destination names Seems like they changed in the latest Xcode. --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9a8cc61492..b5c80fc2cb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -32,12 +32,12 @@ jobs: SCHEME=ReactiveCocoa-iOS ;; "tvOS") - DESTINATION="platform=tvOS Simulator,name=Apple TV 4K" + DESTINATION="platform=tvOS Simulator,name=Apple TV" SCHEME=ReactiveCocoa-tvOS ;; "watchOS") ACTION=build - DESTINATION="platform=watchOS Simulator,name=Apple Watch Series 5 - 44mm" + DESTINATION="platform=watchOS Simulator,name=Apple Watch Series 5 (44mm)" SCHEME=ReactiveCocoa-watchOS ;; "macCatalyst") From 765a475e41e892827d74646254e1318ed57512ea Mon Sep 17 00:00:00 2001 From: NachoSoto Date: Sat, 24 Jun 2023 12:10:46 -0700 Subject: [PATCH 1028/1028] Add support for `xrOS` See also https://github.com/ReactiveCocoa/ReactiveSwift/pull/875 --- Cartfile | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- ReactiveCocoa.xcodeproj/project.pbxproj | 225 +++++++++++++++++- .../xcschemes/ReactiveCocoa-xrOS.xcscheme | 66 +++++ 5 files changed, 293 insertions(+), 4 deletions(-) create mode 100644 ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-xrOS.xcscheme diff --git a/Cartfile b/Cartfile index 4220caa248..d55c71844c 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" ~> 7.0 +github "ReactiveCocoa/ReactiveSwift" "master" diff --git a/Cartfile.resolved b/Cartfile.resolved index b2e8ee64ed..b090175891 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,4 +1,4 @@ github "Quick/Nimble" "v9.2.1" github "Quick/Quick" "v4.0.0" -github "ReactiveCocoa/ReactiveSwift" "7.1.1" +github "ReactiveCocoa/ReactiveSwift" "f4f3d4d7375ce26a797f7f0b4c246444c3afd43f" github "xcconfigs/xcconfigs" "1.1" diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index 40c465af19..f4f3d4d737 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit 40c465af19b993344e84355c00669ba2022ca3cd +Subproject commit f4f3d4d7375ce26a797f7f0b4c246444c3afd43f diff --git a/ReactiveCocoa.xcodeproj/project.pbxproj b/ReactiveCocoa.xcodeproj/project.pbxproj index 2410f947c0..89b157c07e 100644 --- a/ReactiveCocoa.xcodeproj/project.pbxproj +++ b/ReactiveCocoa.xcodeproj/project.pbxproj @@ -69,6 +69,60 @@ 57911A1D270C43EA00E26A24 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE6372D2090EEFA00ECD02A /* UIViewController.swift */; }; 579C7D4E2731BC43009F8A2F /* UIKitReusableComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA221DB8F51C0013AAEA /* UIKitReusableComponents.swift */; }; 57A4D20A1BA13D7A00F7D4B1 /* ReactiveCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57C885112A47756D00FC133C /* ReactiveCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = D04725EF19E49ED7006002AA /* ReactiveCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57C885122A47756D00FC133C /* ObjCRuntimeAliases.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A1D06751D9415FB00ACF44C /* ObjCRuntimeAliases.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 57C885142A47756D00FC133C /* UINavigationItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D1C1E94ECAC002A9BCC /* UINavigationItem.swift */; }; + 57C885152A47756D00FC133C /* UICollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFE231DCFCF5C0066A8C2 /* UICollectionView.swift */; }; + 57C885162A47756D00FC133C /* UIBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05EE1D93E9F100ACF44C /* UIBarItem.swift */; }; + 57C885182A47756D00FC133C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05FA1D93E9F100ACF44C /* UITextField.swift */; }; + 57C885192A47756D00FC133C /* ReactiveSwift+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A90374E1ED61C6300345D62 /* ReactiveSwift+Lifetime.swift */; }; + 57C8851A2A47756D00FC133C /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AB15C791E26CD9A00997378 /* Deprecations+Removals.swift */; }; + 57C8851B2A47756D00FC133C /* UIKitReusableComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6AAA221DB8F51C0013AAEA /* UIKitReusableComponents.swift */; }; + 57C8851C2A47756D00FC133C /* DelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7488471E3B8ACE00CD0317 /* DelegateProxy.swift */; }; + 57C8851D2A47756D00FC133C /* UITextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05FB1D93E9F100ACF44C /* UITextView.swift */; }; + 57C8851E2A47756D00FC133C /* UISearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCF775E1DFAD8A50058006E /* UISearchBar.swift */; }; + 57C8851F2A47756D00FC133C /* UIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F31D93E9F100ACF44C /* UIImageView.swift */; }; + 57C885202A47756D00FC133C /* NSObject+BindingTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0EA741D9A7FF700F27DDF /* NSObject+BindingTarget.swift */; }; + 57C885212A47756D00FC133C /* DynamicProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD0C45DD1CC9A288009F5BF0 /* DynamicProperty.swift */; }; + 57C885222A47756D00FC133C /* ObjC+Messages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE7C2A31DDD7F5100F7534C /* ObjC+Messages.swift */; }; + 57C885232A47756D00FC133C /* CocoaTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2E425D1DAA6737006D909F /* CocoaTarget.swift */; }; + 57C885242A47756D00FC133C /* UIKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AAD49871DED2C350068EC9B /* UIKeyboard.swift */; }; + 57C885252A47756D00FC133C /* UISlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53AC46CB1DD6F97400C799E1 /* UISlider.swift */; }; + 57C885262A47756D00FC133C /* UIApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE637352090F92600ECD02A /* UIApplication.swift */; }; + 57C885272A47756D00FC133C /* ObjC+RuntimeSubclassing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD801DDE03F500531FCF /* ObjC+RuntimeSubclassing.swift */; }; + 57C885282A47756D00FC133C /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05FC1D93E9F100ACF44C /* UIView.swift */; }; + 57C885292A47756D00FC133C /* UIDatePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F21D93E9F100ACF44C /* UIDatePicker.swift */; }; + 57C8852A2A47756D00FC133C /* UIResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D8BA70207CD7090031733D /* UIResponder.swift */; }; + 57C8852B2A47756D00FC133C /* NSObject+Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05DF1D93E99100ACF44C /* NSObject+Association.swift */; }; + 57C8852C2A47756D00FC133C /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F11D93E9F100ACF44C /* UIControl.swift */; }; + 57C8852D2A47756D00FC133C /* UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05EF1D93E9F100ACF44C /* UIButton.swift */; }; + 57C8852E2A47756D00FC133C /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE6372D2090EEFA00ECD02A /* UIViewController.swift */; }; + 57C8852F2A47756D00FC133C /* NSObject+ObjCRuntime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD8E1DDE29F800531FCF /* NSObject+ObjCRuntime.swift */; }; + 57C885302A47756D00FC133C /* UIGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 419139431DB910570043C9D1 /* UIGestureRecognizer.swift */; }; + 57C885312A47756D00FC133C /* ObjC+Runtime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD771DDE03DE00531FCF /* ObjC+Runtime.swift */; }; + 57C885322A47756D00FC133C /* UIPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE145881E43991A00208736 /* UIPickerView.swift */; }; + 57C885332A47756D00FC133C /* UISwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F71D93E9F100ACF44C /* UISwitch.swift */; }; + 57C885342A47756D00FC133C /* NSObject+Intercepting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D065A1D93EC6E00ACF44C /* NSObject+Intercepting.swift */; }; + 57C885362A47756D00FC133C /* UILabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F41D93E9F100ACF44C /* UILabel.swift */; }; + 57C885372A47756D00FC133C /* UIScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF4335641E02AC7600AC88DD /* UIScrollView.swift */; }; + 57C885382A47756D00FC133C /* AnyObject+Lifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E10FE1D2A92720065D310 /* AnyObject+Lifetime.swift */; }; + 57C885392A47756D00FC133C /* NSObject+ReactiveExtensionsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADE4A901DA6EA40005C2AC8 /* NSObject+ReactiveExtensionsProvider.swift */; }; + 57C8853A2A47756D00FC133C /* Synchronizing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADFE5A41DC0001C001E11F7 /* Synchronizing.swift */; }; + 57C8853B2A47756D00FC133C /* NSObject+KeyValueObserving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD0F0691D48BA4800ADFAB7 /* NSObject+KeyValueObserving.swift */; }; + 57C8853C2A47756D00FC133C /* UITableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ABEFDE11DCFC8560066A8C2 /* UITableView.swift */; }; + 57C8853D2A47756D00FC133C /* UIProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F51D93E9F100ACF44C /* UIProgressView.swift */; }; + 57C8853E2A47756D00FC133C /* UIStepper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531866F71DD7920400D1285F /* UIStepper.swift */; }; + 57C8853F2A47756D00FC133C /* UITabBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB3D241E94F335002A9BCC /* UITabBarItem.swift */; }; + 57C885402A47756D00FC133C /* NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538DCB781DCA5E6C00332880 /* NSLayoutConstraint.swift */; }; + 57C885422A47756D00FC133C /* UIBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05ED1D93E9F100ACF44C /* UIBarButtonItem.swift */; }; + 57C885432A47756D00FC133C /* ObjCRuntimeAliases.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD971DDE7A2200531FCF /* ObjCRuntimeAliases.m */; }; + 57C885442A47756D00FC133C /* UIActivityIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05EC1D93E9F100ACF44C /* UIActivityIndicatorView.swift */; }; + 57C885462A47756D00FC133C /* ObjC+Selector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A54A21A1DE00D09001739B3 /* ObjC+Selector.swift */; }; + 57C885472A47756D00FC133C /* ObjC+Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA0BD891DDE153A00531FCF /* ObjC+Constants.swift */; }; + 57C885482A47756D00FC133C /* UISegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1D05F61D93E9F100ACF44C /* UISegmentedControl.swift */; }; + 57C885492A47756D00FC133C /* CocoaAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADE4A7B1DA44A9E005C2AC8 /* CocoaAction.swift */; }; + 57C8854A2A47756D00FC133C /* UIRefreshControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BCAAC791DEE19BC00B30335 /* UIRefreshControl.swift */; }; + 57C8854C2A47756D00FC133C /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A73E035216D3FC50069AD76 /* ReactiveSwift.framework */; }; 5B76DE402498EDDC00E8B4F3 /* QueueScheduler+Factory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B76DE3F2498EDDC00E8B4F3 /* QueueScheduler+Factory.swift */; }; 5B76DE412498EDDC00E8B4F3 /* QueueScheduler+Factory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B76DE3F2498EDDC00E8B4F3 /* QueueScheduler+Factory.swift */; }; 5B76DE422498EDDC00E8B4F3 /* QueueScheduler+Factory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B76DE3F2498EDDC00E8B4F3 /* QueueScheduler+Factory.swift */; }; @@ -513,6 +567,7 @@ 57A4D2451BA13F9700F7D4B1 /* tvOS-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Base.xcconfig"; sourceTree = ""; }; 57A4D2461BA13F9700F7D4B1 /* tvOS-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-Framework.xcconfig"; sourceTree = ""; }; 57A4D2471BA13F9700F7D4B1 /* tvOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "tvOS-StaticLibrary.xcconfig"; sourceTree = ""; }; + 57C885532A47756D00FC133C /* ReactiveCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 5B76DE3F2498EDDC00E8B4F3 /* QueueScheduler+Factory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "QueueScheduler+Factory.swift"; sourceTree = ""; }; 7A8BA0F91FCC86FC003241C7 /* NSTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSTextView.swift; sourceTree = ""; }; 7DFBED031CDB8C9500EE435B /* ReactiveCocoaTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveCocoaTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -693,6 +748,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 57C8854B2A47756D00FC133C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 57C8854C2A47756D00FC133C /* ReactiveSwift.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 7DFBED001CDB8C9500EE435B /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -1059,6 +1122,7 @@ 9A73DFCC216D3C570069AD76 /* ReactiveMapKit.framework */, 9A73DFF8216D3CEB0069AD76 /* ReactiveMapKitTests.xctest */, 9A73E013216D3CEE0069AD76 /* ReactiveMapKitTests.xctest */, + 57C885532A47756D00FC133C /* ReactiveCocoa.framework */, ); name = Products; sourceTree = ""; @@ -1271,6 +1335,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 57C885102A47756D00FC133C /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 57C885112A47756D00FC133C /* ReactiveCocoa.h in Headers */, + 57C885122A47756D00FC133C /* ObjCRuntimeAliases.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 9A73DFB4216D3C550069AD76 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -1340,6 +1413,24 @@ productReference = 57A4D2411BA13D7A00F7D4B1 /* ReactiveCocoa.framework */; productType = "com.apple.product-type.framework"; }; + 57C8850F2A47756D00FC133C /* ReactiveCocoa-xrOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 57C8854E2A47756D00FC133C /* Build configuration list for PBXNativeTarget "ReactiveCocoa-xrOS" */; + buildPhases = ( + 57C885102A47756D00FC133C /* Headers */, + 57C885132A47756D00FC133C /* Sources */, + 57C8854B2A47756D00FC133C /* Frameworks */, + 57C8854D2A47756D00FC133C /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "ReactiveCocoa-xrOS"; + productName = "ReactiveCocoa-iOS"; + productReference = 57C885532A47756D00FC133C /* ReactiveCocoa.framework */; + productType = "com.apple.product-type.framework"; + }; 7DFBED021CDB8C9500EE435B /* ReactiveCocoa-tvOSTests */ = { isa = PBXNativeTarget; buildConfigurationList = 7DFBED0F1CDB8C9500EE435B /* Build configuration list for PBXNativeTarget "ReactiveCocoa-tvOSTests" */; @@ -1641,6 +1732,7 @@ A9B315531B3940610001CB9C /* ReactiveCocoa-watchOS */, 57A4D1AF1BA13D7A00F7D4B1 /* ReactiveCocoa-tvOS */, 7DFBED021CDB8C9500EE435B /* ReactiveCocoa-tvOSTests */, + 57C8850F2A47756D00FC133C /* ReactiveCocoa-xrOS */, 9AC03A561F7CC3BF00EC33C1 /* ReactiveMapKit-macOS */, 9A73DFDF216D3CEB0069AD76 /* ReactiveMapKitTests-macOS */, 9A73DFAC216D3C550069AD76 /* ReactiveMapKit-iOS */, @@ -1659,6 +1751,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 57C8854D2A47756D00FC133C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 7DFBED011CDB8C9500EE435B /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1798,6 +1897,64 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 57C885132A47756D00FC133C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 57C885142A47756D00FC133C /* UINavigationItem.swift in Sources */, + 57C885152A47756D00FC133C /* UICollectionView.swift in Sources */, + 57C885162A47756D00FC133C /* UIBarItem.swift in Sources */, + 57C885182A47756D00FC133C /* UITextField.swift in Sources */, + 57C885192A47756D00FC133C /* ReactiveSwift+Lifetime.swift in Sources */, + 57C8851A2A47756D00FC133C /* Deprecations+Removals.swift in Sources */, + 57C8851B2A47756D00FC133C /* UIKitReusableComponents.swift in Sources */, + 57C8851C2A47756D00FC133C /* DelegateProxy.swift in Sources */, + 57C8851D2A47756D00FC133C /* UITextView.swift in Sources */, + 57C8851E2A47756D00FC133C /* UISearchBar.swift in Sources */, + 57C8851F2A47756D00FC133C /* UIImageView.swift in Sources */, + 57C885202A47756D00FC133C /* NSObject+BindingTarget.swift in Sources */, + 57C885212A47756D00FC133C /* DynamicProperty.swift in Sources */, + 57C885222A47756D00FC133C /* ObjC+Messages.swift in Sources */, + 57C885232A47756D00FC133C /* CocoaTarget.swift in Sources */, + 57C885242A47756D00FC133C /* UIKeyboard.swift in Sources */, + 57C885252A47756D00FC133C /* UISlider.swift in Sources */, + 57C885262A47756D00FC133C /* UIApplication.swift in Sources */, + 57C885272A47756D00FC133C /* ObjC+RuntimeSubclassing.swift in Sources */, + 57C885282A47756D00FC133C /* UIView.swift in Sources */, + 57C885292A47756D00FC133C /* UIDatePicker.swift in Sources */, + 57C8852A2A47756D00FC133C /* UIResponder.swift in Sources */, + 57C8852B2A47756D00FC133C /* NSObject+Association.swift in Sources */, + 57C8852C2A47756D00FC133C /* UIControl.swift in Sources */, + 57C8852D2A47756D00FC133C /* UIButton.swift in Sources */, + 57C8852E2A47756D00FC133C /* UIViewController.swift in Sources */, + 57C8852F2A47756D00FC133C /* NSObject+ObjCRuntime.swift in Sources */, + 57C885302A47756D00FC133C /* UIGestureRecognizer.swift in Sources */, + 57C885312A47756D00FC133C /* ObjC+Runtime.swift in Sources */, + 57C885322A47756D00FC133C /* UIPickerView.swift in Sources */, + 57C885332A47756D00FC133C /* UISwitch.swift in Sources */, + 57C885342A47756D00FC133C /* NSObject+Intercepting.swift in Sources */, + 57C885362A47756D00FC133C /* UILabel.swift in Sources */, + 57C885372A47756D00FC133C /* UIScrollView.swift in Sources */, + 57C885382A47756D00FC133C /* AnyObject+Lifetime.swift in Sources */, + 57C885392A47756D00FC133C /* NSObject+ReactiveExtensionsProvider.swift in Sources */, + 57C8853A2A47756D00FC133C /* Synchronizing.swift in Sources */, + 57C8853B2A47756D00FC133C /* NSObject+KeyValueObserving.swift in Sources */, + 57C8853C2A47756D00FC133C /* UITableView.swift in Sources */, + 57C8853D2A47756D00FC133C /* UIProgressView.swift in Sources */, + 57C8853E2A47756D00FC133C /* UIStepper.swift in Sources */, + 57C8853F2A47756D00FC133C /* UITabBarItem.swift in Sources */, + 57C885402A47756D00FC133C /* NSLayoutConstraint.swift in Sources */, + 57C885422A47756D00FC133C /* UIBarButtonItem.swift in Sources */, + 57C885432A47756D00FC133C /* ObjCRuntimeAliases.m in Sources */, + 57C885442A47756D00FC133C /* UIActivityIndicatorView.swift in Sources */, + 57C885462A47756D00FC133C /* ObjC+Selector.swift in Sources */, + 57C885472A47756D00FC133C /* ObjC+Constants.swift in Sources */, + 57C885482A47756D00FC133C /* UISegmentedControl.swift in Sources */, + 57C885492A47756D00FC133C /* CocoaAction.swift in Sources */, + 57C8854A2A47756D00FC133C /* UIRefreshControl.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 7DFBECFF1CDB8C9500EE435B /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -2228,6 +2385,58 @@ }; name = Profile; }; + 57C8854F2A47756D00FC133C /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + INFOPLIST_FILE = ReactiveCocoa/Info.plist; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; + SDKROOT = xros; + TARGETED_DEVICE_FAMILY = 7; + }; + name = Debug; + }; + 57C885502A47756D00FC133C /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + INFOPLIST_FILE = ReactiveCocoa/Info.plist; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; + SDKROOT = xros; + TARGETED_DEVICE_FAMILY = 7; + }; + name = Test; + }; + 57C885512A47756D00FC133C /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + INFOPLIST_FILE = ReactiveCocoa/Info.plist; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; + SDKROOT = xros; + TARGETED_DEVICE_FAMILY = 7; + }; + name = Release; + }; + 57C885522A47756D00FC133C /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D047263419E49FE8006002AA /* iOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + INFOPLIST_FILE = ReactiveCocoa/Info.plist; + MODULEMAP_FILE = "$(SRCROOT)/ReactiveCocoa/include/module.modulemap"; + SDKROOT = xros; + TARGETED_DEVICE_FAMILY = 7; + }; + name = Profile; + }; 7DFBED0B1CDB8C9500EE435B /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 57A4D2441BA13F9700F7D4B1 /* tvOS-Application.xcconfig */; @@ -2605,6 +2814,7 @@ VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; WATCHOS_DEPLOYMENT_TARGET = 4.0; + XROS_DEPLOYMENT_TARGET = 1.0; }; name = Debug; }; @@ -2621,7 +2831,6 @@ CLANG_WARN_STRICT_PROTOTYPES = YES; CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; - GCC_OPTIMIZATION_LEVEL = 0; GCC_TREAT_WARNINGS_AS_ERRORS = NO; IPHONEOS_DEPLOYMENT_TARGET = 11.0; MACOSX_DEPLOYMENT_TARGET = 10.13; @@ -2634,6 +2843,7 @@ VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; WATCHOS_DEPLOYMENT_TARGET = 4.0; + XROS_DEPLOYMENT_TARGET = 1.0; }; name = Release; }; @@ -2749,6 +2959,7 @@ VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; WATCHOS_DEPLOYMENT_TARGET = 4.0; + XROS_DEPLOYMENT_TARGET = 1.0; }; name = Profile; }; @@ -2820,6 +3031,7 @@ VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; WATCHOS_DEPLOYMENT_TARGET = 4.0; + XROS_DEPLOYMENT_TARGET = 1.0; }; name = Test; }; @@ -2881,6 +3093,17 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 57C8854E2A47756D00FC133C /* Build configuration list for PBXNativeTarget "ReactiveCocoa-xrOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 57C8854F2A47756D00FC133C /* Debug */, + 57C885502A47756D00FC133C /* Test */, + 57C885512A47756D00FC133C /* Release */, + 57C885522A47756D00FC133C /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 7DFBED0F1CDB8C9500EE435B /* Build configuration list for PBXNativeTarget "ReactiveCocoa-tvOSTests" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-xrOS.xcscheme b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-xrOS.xcscheme new file mode 100644 index 0000000000..ea4b995bee --- /dev/null +++ b/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-xrOS.xcscheme @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + +

    + ReactiveCocoa

    + Reactive extensions to Cocoa frameworks, built on top of ReactiveSwift.

    + Join the ReactiveSwift Slack community. +

    $gH{q5tI+t_?C{%7bB+!Fv+pV9RZ|BTLu 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¢OxSy}5#{Ic}W7qmK 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 A+5i9m From dc8986e8a7b0259d994266d28633db941786c26c Mon Sep 17 00:00:00 2001 From: Zigii Wong Date: Mon, 19 Sep 2016 18:41:12 +0800 Subject: [PATCH 0383/1028] Remove invalid links after split up --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index b8dc9c0490..b16224d7c0 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,7 @@ ReactiveCocoa (RAC) is a Cocoa framework built on top of [ReactiveSwift][]. It provides APIs for using ReactiveSwift with Apple's Cocoa frameworks. 1. [Introduction](#introduction) - 1. [Example: online search](#example-online-search) 1. [Objective-C and Swift](#objective-c-and-swift) - 1. [How does ReactiveCocoa relate to Rx?](#how-does-reactivecocoa-relate-to-rx) 1. [Getting started](#getting-started) If you’re already familiar with functional reactive programming or what From 0d380afbb28167da1638a4b3219c8f1a179c9dd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20S=C4=99kara?= Date: Tue, 20 Sep 2016 12:21:25 +0200 Subject: [PATCH 0384/1028] Specified commit of ReactiveSwift in Cartfile to prevent conflicts with Cartfile.resolved; Updated renamed Observer methods in tests --- Cartfile | 2 +- Cartfile.resolved | 6 +++--- Carthage/Checkouts/Nimble | 2 +- Carthage/Checkouts/Quick | 2 +- Carthage/Checkouts/ReactiveSwift | 2 +- ReactiveCocoa/CocoaAction.swift | 4 ++-- ReactiveCocoa/NSObject+KeyValueObserving.swift | 2 +- ReactiveCocoaTests/CocoaActionSpec.swift | 4 ++-- ReactiveCocoaTests/DynamicPropertySpec.swift | 10 +++++----- ReactiveCocoaTests/KeyValueObservingSpec.swift | 18 +++++++++--------- .../SignalProducerNimbleMatchers.swift | 2 +- 11 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Cartfile b/Cartfile index d55c71844c..929ff6d5f0 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveSwift" "master" +github "ReactiveCocoa/ReactiveSwift" "228fd564561383a387f338558bd777cd14ca4291" diff --git a/Cartfile.resolved b/Cartfile.resolved index 0f60c16f05..069009d5b4 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,5 +1,5 @@ -github "Quick/Nimble" "db706fc1d7130f6ac96c56aaf0e635fa3217fe57" -github "Quick/Quick" "8f2bc636ecfa2cc20696f62548b38d4ab943e299" +github "Quick/Nimble" "0209419661b6acceff45cd63984596f2e9eea517" +github "Quick/Quick" "36ebbc75e093c7553a5145ed5dd21484364cd78d" github "antitypical/Result" "3.0.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" -github "ReactiveCocoa/ReactiveSwift" "d4de1ff81c12124d148f726b8b44dd6887b41ae0" +github "ReactiveCocoa/ReactiveSwift" "228fd564561383a387f338558bd777cd14ca4291" diff --git a/Carthage/Checkouts/Nimble b/Carthage/Checkouts/Nimble index db706fc1d7..0209419661 160000 --- a/Carthage/Checkouts/Nimble +++ b/Carthage/Checkouts/Nimble @@ -1 +1 @@ -Subproject commit db706fc1d7130f6ac96c56aaf0e635fa3217fe57 +Subproject commit 0209419661b6acceff45cd63984596f2e9eea517 diff --git a/Carthage/Checkouts/Quick b/Carthage/Checkouts/Quick index 8f2bc636ec..36ebbc75e0 160000 --- a/Carthage/Checkouts/Quick +++ b/Carthage/Checkouts/Quick @@ -1 +1 @@ -Subproject commit 8f2bc636ecfa2cc20696f62548b38d4ab943e299 +Subproject commit 36ebbc75e093c7553a5145ed5dd21484364cd78d diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift index d4de1ff81c..228fd56456 160000 --- a/Carthage/Checkouts/ReactiveSwift +++ b/Carthage/Checkouts/ReactiveSwift @@ -1 +1 @@ -Subproject commit d4de1ff81c12124d148f726b8b44dd6887b41ae0 +Subproject commit 228fd564561383a387f338558bd777cd14ca4291 diff --git a/ReactiveCocoa/CocoaAction.swift b/ReactiveCocoa/CocoaAction.swift index 32548cbeaa..f4e65b57ab 100644 --- a/ReactiveCocoa/CocoaAction.swift +++ b/ReactiveCocoa/CocoaAction.swift @@ -46,7 +46,7 @@ public final class CocoaAction: NSObject { disposable += action.isEnabled.producer .observe(on: UIScheduler()) - .startWithNext { [weak self] value in + .startWithValues { [weak self] value in self?.willChangeValue(forKey: #keyPath(CocoaAction.isEnabled)) self?.isEnabled = value self?.didChangeValue(forKey: #keyPath(CocoaAction.isEnabled)) @@ -54,7 +54,7 @@ public final class CocoaAction: NSObject { disposable += action.isExecuting.producer .observe(on: UIScheduler()) - .startWithNext { [weak self] value in + .startWithValues { [weak self] value in self?.willChangeValue(forKey: #keyPath(CocoaAction.isExecuting)) self?.isExecuting = value self?.didChangeValue(forKey: #keyPath(CocoaAction.isExecuting)) diff --git a/ReactiveCocoa/NSObject+KeyValueObserving.swift b/ReactiveCocoa/NSObject+KeyValueObserving.swift index be0d2cc03a..8114005973 100644 --- a/ReactiveCocoa/NSObject+KeyValueObserving.swift +++ b/ReactiveCocoa/NSObject+KeyValueObserving.swift @@ -19,7 +19,7 @@ extension NSObject { self, keyPath: keyPath, options: [.initial, .new], - action: observer.sendNext + action: observer.send ) disposable += self.rac_lifetime.ended.observeCompleted(observer.sendCompleted) } diff --git a/ReactiveCocoaTests/CocoaActionSpec.swift b/ReactiveCocoaTests/CocoaActionSpec.swift index 8eb27aec90..13b14dadaa 100644 --- a/ReactiveCocoaTests/CocoaActionSpec.swift +++ b/ReactiveCocoaTests/CocoaActionSpec.swift @@ -37,7 +37,7 @@ class CocoaActionSpec: QuickSpec { cocoaAction .values(forKeyPath: #keyPath(CocoaAction.isEnabled)) .map { $0! as! Bool } - .start(Observer(next: { values.append($0) })) + .start(Observer(value: { values.append($0) })) expect(values) == [ true ] @@ -55,7 +55,7 @@ class CocoaActionSpec: QuickSpec { cocoaAction .values(forKeyPath: #keyPath(CocoaAction.isExecuting)) .map { $0! as! Bool } - .start(Observer(next: { values.append($0) })) + .start(Observer(value: { values.append($0) })) expect(values) == [ false ] diff --git a/ReactiveCocoaTests/DynamicPropertySpec.swift b/ReactiveCocoaTests/DynamicPropertySpec.swift index c1aafc4168..4d4e9edcde 100644 --- a/ReactiveCocoaTests/DynamicPropertySpec.swift +++ b/ReactiveCocoaTests/DynamicPropertySpec.swift @@ -60,7 +60,7 @@ class DynamicPropertySpec: QuickSpec { it("should yield a producer that sends the current value and then the changes for the key path of the underlying object") { var values: [Int] = [] - property.producer.startWithNext { value in + property.producer.startWithValues { value in expect(value).notTo(beNil()) values.append(value!) } @@ -76,7 +76,7 @@ class DynamicPropertySpec: QuickSpec { it("should yield a producer that sends the current value and then the changes for the key path of the underlying object, even if the value actually remains unchanged") { var values: [Int] = [] - property.producer.startWithNext { value in + property.producer.startWithValues { value in expect(value).notTo(beNil()) values.append(value!) } @@ -92,7 +92,7 @@ class DynamicPropertySpec: QuickSpec { it("should yield a signal that emits subsequent values for the key path of the underlying object") { var values: [Int] = [] - property.signal.observeNext { value in + property.signal.observeValues { value in expect(value).notTo(beNil()) values.append(value!) } @@ -108,7 +108,7 @@ class DynamicPropertySpec: QuickSpec { it("should yield a signal that emits subsequent values for the key path of the underlying object, even if the value actually remains unchanged") { var values: [Int] = [] - property.signal.observeNext { value in + property.signal.observeValues { value in expect(value).notTo(beNil()) values.append(value!) } @@ -215,7 +215,7 @@ class DynamicPropertySpec: QuickSpec { it("should bridge values sent on a signal to Objective-C") { let (signal, observer) = Signal.pipe() property <~ signal - observer.sendNext(1) + observer.send(value: 1) expect(object.rac_value) == 1 } diff --git a/ReactiveCocoaTests/KeyValueObservingSpec.swift b/ReactiveCocoaTests/KeyValueObservingSpec.swift index 3c22ab7775..946e730f8e 100644 --- a/ReactiveCocoaTests/KeyValueObservingSpec.swift +++ b/ReactiveCocoaTests/KeyValueObservingSpec.swift @@ -11,7 +11,7 @@ class KeyValueObservingSpec: QuickSpec { it("should sends the current value and then the changes for the key path") { let object = ObservableObject() var values: [Int] = [] - object.values(forKeyPath: "rac_value").startWithNext { value in + object.values(forKeyPath: "rac_value").startWithValues { value in expect(value).notTo(beNil()) values.append(value as! Int) } @@ -28,7 +28,7 @@ class KeyValueObservingSpec: QuickSpec { it("should sends the current value and then the changes for the key path, even if the value actually remains unchanged") { let object = ObservableObject() var values: [Int] = [] - object.values(forKeyPath: "rac_value").startWithNext { value in + object.values(forKeyPath: "rac_value").startWithValues { value in expect(value).notTo(beNil()) values.append(value as! Int) } @@ -80,7 +80,7 @@ class KeyValueObservingSpec: QuickSpec { .values(forKeyPath: "rac_object.rac_value") .map { $0 as! NSNumber } .map { $0.intValue } - .startWithNext { + .startWithValues { values.append($0) } @@ -110,7 +110,7 @@ class KeyValueObservingSpec: QuickSpec { .values(forKeyPath: "rac_weakObject.rac_value") .map { $0 as! NSNumber } .map { $0.intValue } - .startWithNext { + .startWithValues { values.append($0) } @@ -161,7 +161,7 @@ class KeyValueObservingSpec: QuickSpec { .take(first: 1) .map { $0 as! NSNumber } .map { $0.intValue } - .startWithNext { + .startWithValues { observedValue = $0 } @@ -182,7 +182,7 @@ class KeyValueObservingSpec: QuickSpec { .observe(on: UIScheduler()) .map { $0 as! NSNumber } .map { $0.intValue } - .startWithNext { + .startWithValues { observedValue = $0 } @@ -201,7 +201,7 @@ class KeyValueObservingSpec: QuickSpec { .observe(on: UIScheduler()) .map { $0 as! NSNumber } .map { $0.intValue } - .startWithNext { + .startWithValues { observedValue = $0 } @@ -246,7 +246,7 @@ class KeyValueObservingSpec: QuickSpec { .observe(on: deliveringObserver) .map { $0 as! NSNumber } .map { $0.int64Value } - .startWithNext { value in + .startWithValues { value in OSAtomicAdd64(value, &atomicCounter) } } @@ -302,7 +302,7 @@ class KeyValueObservingSpec: QuickSpec { } iterationQueue.async(flags: .barrier) { - teardownObserver.sendNext() + teardownObserver.send(value: ()) } let event = replayProducer.last() diff --git a/ReactiveCocoaTests/SignalProducerNimbleMatchers.swift b/ReactiveCocoaTests/SignalProducerNimbleMatchers.swift index 5790549622..e7548b436c 100644 --- a/ReactiveCocoaTests/SignalProducerNimbleMatchers.swift +++ b/ReactiveCocoaTests/SignalProducerNimbleMatchers.swift @@ -30,7 +30,7 @@ public func sendValues(_ values: [T], sendError mayb signalProducer.start { event in switch event { - case let .next(value): + case let .value(value): sentValues.append(value) case .completed: signalCompleted = true From c5e136ec1ed88d05b12e45f5364697da191c40d0 Mon Sep 17 00:00:00 2001 From: Syo Ikeda Date: Thu, 22 Sep 2016 00:35:34 +0900 Subject: [PATCH 0385/1028] Update Nimble to v5.0.0 --- Cartfile.private | 2 +- Cartfile.resolved | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cartfile.private b/Cartfile.private index abf3a1a9ef..02554af5d9 100644 --- a/Cartfile.private +++ b/Cartfile.private @@ -1,3 +1,3 @@ github "jspahrsummers/xcconfigs" "3d9d996" github "Quick/Quick" "swift-3.0" -github "Quick/Nimble" "master" +github "Quick/Nimble" ~> 5.0 diff --git a/Cartfile.resolved b/Cartfile.resolved index 069009d5b4..1bea4cc2ca 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,4 +1,4 @@ -github "Quick/Nimble" "0209419661b6acceff45cd63984596f2e9eea517" +github "Quick/Nimble" "v5.0.0" github "Quick/Quick" "36ebbc75e093c7553a5145ed5dd21484364cd78d" github "antitypical/Result" "3.0.0" github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" From c7b22dc64f0a984d8ea90a11f35f046581e19973 Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 22 Sep 2016 00:02:22 +0200 Subject: [PATCH 0386/1028] Sorted for merging. --- .gitignore | 8 - .travis.yml | 37 - CONTRIBUTING.md | 23 - Cartfile | 1 - Cartfile.resolved | 3 - LICENSE | 21 - README.md | 67 - .../AppKit/NSTextField.swift | 1 - .../Association.swift | 0 ReactiveCocoa/CocoaAction+Extensions.swift | 29 + {Source => ReactiveCocoa}/Reusable.swift | 0 .../UIKit/UIActivityIndicatorView.swift | 1 - .../UIKit/UIBarButtonItem.swift | 1 - .../UIKit/UIBarItem.swift | 1 - .../UIKit/UIButton.swift | 1 - .../UIKit/UICollectionReusableView.swift | 0 .../UIKit/UIControl.swift | 1 - .../UIKit/UIDatePicker.swift | 1 - .../UIKit/UIImageView.swift | 1 - {Source => ReactiveCocoa}/UIKit/UILabel.swift | 1 - .../UIKit/UIProgressView.swift | 1 - .../UIKit/UISegmentedControl.swift | 1 - .../UIKit/UISwitch.swift | 1 - .../UIKit/UITableViewCell.swift | 1 - .../UIKit/UITableViewHeaderFooterView.swift | 0 .../UIKit/UITextField.swift | 2 - .../UIKit/UITextView.swift | 1 - {Source => ReactiveCocoa}/UIKit/UIView.swift | 1 - .../UIKit/UIViewController.swift | 1 - ...ol+EnableSendActionsForControlEvents.swift | 0 .../UIKit/UIActivityIndicatorViewTests.swift | 0 .../UIKit/UIBarButtonItemTests.swift | 0 .../UIKit/UIButtonTests.swift | 0 .../UIKit/UICollectionReusableViewTests.swift | 0 .../UIKit/UIControlTests.swift | 0 .../UIKit/UIDatePickerTests.swift | 0 .../UIKit/UIImageViewTests.swift | 0 .../UIKit/UILabelTests.swift | 0 .../UIKit/UIProgressViewTests.swift | 0 .../UIKit/UISegmentedControlTests.swift | 0 .../UIKit/UISwitchTests.swift | 0 .../UIKit/UITableViewCellTests.swift | 0 .../UITableViewHeaderFooterViewTests.swift | 0 .../UIKit/UITextFieldTests.swift | 0 .../UIKit/UITextViewTests.swift | 0 .../UIKit/UIViewControllerTests.swift | 0 .../UIKit/UIViewTests.swift | 0 .../RACSignal.swift | 2 +- Rex.podspec | 33 - Rex.xcodeproj/project.pbxproj | 1551 ----------------- .../contents.xcworkspacedata | 7 - .../xcshareddata/xcschemes/Rex-Mac.xcscheme | 113 -- .../xcshareddata/xcschemes/Rex-iOS.xcscheme | 113 -- .../xcshareddata/xcschemes/Rex-tvOS.xcscheme | 99 -- .../xcschemes/Rex-watchOS.xcscheme | 80 - Rex.xcworkspace/contents.xcworkspacedata | 10 - .../xcshareddata/WorkspaceSettings.xcsettings | 8 - Source/Foundation/NSObject.swift | 37 - Source/Info.plist | 28 - Source/Rex.h | 19 - .../Action+Extensions.swift | 19 - {Source => Sources}/Foundation/Data.swift | 0 {Source => Sources}/Foundation/NSData.swift | 0 .../Foundation/UserDefaults.swift | 0 .../Property+Extensions.swift | 0 .../Signal+Extensions.swift | 0 .../SignalProducer+Extensions.swift | 0 Tests/Foundation/NSObjectTests.swift | 70 - Tests/Info.plist | 24 - .../ActionExtensionTests.swift} | 2 - .../PropertyTests.swift | 2 - .../SignalProducerTests.swift | 2 - .../SignalTests.swift | 2 - script/ci | 40 - 74 files changed, 30 insertions(+), 2438 deletions(-) delete mode 100644 .gitignore delete mode 100644 .travis.yml delete mode 100644 CONTRIBUTING.md delete mode 100644 Cartfile delete mode 100644 Cartfile.resolved delete mode 100644 LICENSE delete mode 100644 README.md rename {Source => ReactiveCocoa}/AppKit/NSTextField.swift (96%) rename {Source/Foundation => ReactiveCocoa}/Association.swift (100%) create mode 100644 ReactiveCocoa/CocoaAction+Extensions.swift rename {Source => ReactiveCocoa}/Reusable.swift (100%) rename {Source => ReactiveCocoa}/UIKit/UIActivityIndicatorView.swift (97%) rename {Source => ReactiveCocoa}/UIKit/UIBarButtonItem.swift (98%) rename {Source => ReactiveCocoa}/UIKit/UIBarItem.swift (95%) rename {Source => ReactiveCocoa}/UIKit/UIButton.swift (98%) rename {Source => ReactiveCocoa}/UIKit/UICollectionReusableView.swift (100%) rename {Source => ReactiveCocoa}/UIKit/UIControl.swift (99%) rename {Source => ReactiveCocoa}/UIKit/UIDatePicker.swift (95%) rename {Source => ReactiveCocoa}/UIKit/UIImageView.swift (97%) rename {Source => ReactiveCocoa}/UIKit/UILabel.swift (98%) rename {Source => ReactiveCocoa}/UIKit/UIProgressView.swift (95%) rename {Source => ReactiveCocoa}/UIKit/UISegmentedControl.swift (97%) rename {Source => ReactiveCocoa}/UIKit/UISwitch.swift (94%) rename {Source => ReactiveCocoa}/UIKit/UITableViewCell.swift (91%) rename {Source => ReactiveCocoa}/UIKit/UITableViewHeaderFooterView.swift (100%) rename {Source => ReactiveCocoa}/UIKit/UITextField.swift (95%) rename {Source => ReactiveCocoa}/UIKit/UITextView.swift (96%) rename {Source => ReactiveCocoa}/UIKit/UIView.swift (98%) rename {Source => ReactiveCocoa}/UIKit/UIViewController.swift (99%) rename {Tests => ReactiveCocoaTests}/Helpers/UIControl+EnableSendActionsForControlEvents.swift (100%) rename {Tests => ReactiveCocoaTests}/UIKit/UIActivityIndicatorViewTests.swift (100%) rename {Tests => ReactiveCocoaTests}/UIKit/UIBarButtonItemTests.swift (100%) rename {Tests => ReactiveCocoaTests}/UIKit/UIButtonTests.swift (100%) rename {Tests => ReactiveCocoaTests}/UIKit/UICollectionReusableViewTests.swift (100%) rename {Tests => ReactiveCocoaTests}/UIKit/UIControlTests.swift (100%) rename {Tests => ReactiveCocoaTests}/UIKit/UIDatePickerTests.swift (100%) rename {Tests => ReactiveCocoaTests}/UIKit/UIImageViewTests.swift (100%) rename {Tests => ReactiveCocoaTests}/UIKit/UILabelTests.swift (100%) rename {Tests => ReactiveCocoaTests}/UIKit/UIProgressViewTests.swift (100%) rename {Tests => ReactiveCocoaTests}/UIKit/UISegmentedControlTests.swift (100%) rename {Tests => ReactiveCocoaTests}/UIKit/UISwitchTests.swift (100%) rename {Tests => ReactiveCocoaTests}/UIKit/UITableViewCellTests.swift (100%) rename {Tests => ReactiveCocoaTests}/UIKit/UITableViewHeaderFooterViewTests.swift (100%) rename {Tests => ReactiveCocoaTests}/UIKit/UITextFieldTests.swift (100%) rename {Tests => ReactiveCocoaTests}/UIKit/UITextViewTests.swift (100%) rename {Tests => ReactiveCocoaTests}/UIKit/UIViewControllerTests.swift (100%) rename {Tests => ReactiveCocoaTests}/UIKit/UIViewTests.swift (100%) rename {Source => ReactiveObjCBridge}/RACSignal.swift (97%) delete mode 100644 Rex.podspec delete mode 100644 Rex.xcodeproj/project.pbxproj delete mode 100644 Rex.xcodeproj/project.xcworkspace/contents.xcworkspacedata delete mode 100644 Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme delete mode 100644 Rex.xcodeproj/xcshareddata/xcschemes/Rex-iOS.xcscheme delete mode 100644 Rex.xcodeproj/xcshareddata/xcschemes/Rex-tvOS.xcscheme delete mode 100644 Rex.xcodeproj/xcshareddata/xcschemes/Rex-watchOS.xcscheme delete mode 100644 Rex.xcworkspace/contents.xcworkspacedata delete mode 100644 Rex.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings delete mode 100644 Source/Foundation/NSObject.swift delete mode 100644 Source/Info.plist delete mode 100644 Source/Rex.h rename Source/Action.swift => Sources/Action+Extensions.swift (55%) rename {Source => Sources}/Foundation/Data.swift (100%) rename {Source => Sources}/Foundation/NSData.swift (100%) rename {Source => Sources}/Foundation/UserDefaults.swift (100%) rename Source/Property.swift => Sources/Property+Extensions.swift (100%) rename Source/Signal.swift => Sources/Signal+Extensions.swift (100%) rename Source/SignalProducer.swift => Sources/SignalProducer+Extensions.swift (100%) delete mode 100644 Tests/Foundation/NSObjectTests.swift delete mode 100644 Tests/Info.plist rename Tests/{ActionTests.swift => ReactiveSwiftTests/ActionExtensionTests.swift} (97%) rename Tests/{ => ReactiveSwiftTests}/PropertyTests.swift (98%) rename Tests/{ => ReactiveSwiftTests}/SignalProducerTests.swift (99%) rename Tests/{ => ReactiveSwiftTests}/SignalTests.swift (99%) delete mode 100755 script/ci diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 6984e77129..0000000000 --- a/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -.DS_Store -xcuserdata -*.xcuserdatad -*.xccheckout -*.mode* -*.pbxuser - -/Carthage diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 5ca52c2874..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,37 +0,0 @@ -language: objective-c -matrix: - include: - - osx_image: xcode8 - env: PLATFORM=Mac TEST_ACTION="build-for-testing test-without-building" - - osx_image: xcode8 - env: PLATFORM=iOS TEST_ACTION="build-for-testing test-without-building" - - osx_image: xcode8 - env: PLATFORM=tvOS TEST_ACTION="build-for-testing test-without-building" - - osx_image: xcode8 - env: PLATFORM=watchOS TEST_ACTION="build-for-testing test-without-building" - - osx_image: xcode8 - env: PLATFORM=CocoaPods - -env: - global: - - LC_CTYPE=en_US.UTF-8 - -git: - submodules: false - -script: script/ci "$PLATFORM" - -notifications: - email: false -before_deploy: - - carthage build --no-skip-current - - carthage archive Rex -deploy: - provider: releases - api_key: - secure: lNuRUOxMrw2YSsibAAK+8GH35KfvgheiyBGsqD7Rqwjzf3orhTmNbWeRt38YiUxzmNZSzhQdcglwJnw9ymXuGyynOwWxRPbQnX0KE+fTIyoyZrxIwxkqyU6aGzgi1bGa/URKU83pDZsBrfPeLa89w5PYZ8UVPVs+alD796WTjNoXFhxvj4cZtT65Pqk4usSgq3l6GRGzVDmaOgiiDT509LdTi+x+BjRuQcP2wvxCKWGWtaR4COo+PH96mQ/vcykL97zyxScWOBRbVq5YEeqHC/OHV7kXMLRK6X0SBcpB8otV9ObxN76zqZjpxQ59/g0WN4bUogd5VYT11dxjSAQDNAtS/H0iHcw6ojDuAobQbD1W4Um6tHBPaT4ZVXDack8J2gSa2DhiFBt198XRSEWFczff5LevxFJaDwqLwEj5qtB6bkdvarsaZdlUzaPfmBfEjmLQdQmiEe82xYb+VcZK0SlgbNulvSt8J2FpLRcVQSs7Ef75zMKQECtxJCsOhSFGT+1Zal2YEk70HFdbkNE0DS57AX0hmgDFF0WhK1ZzpBgy432Hyo71srAJMyalMfF1zuc5kHSssezQ30p7ZdegDnkvbt0lhjGFgUlbKIoLS9e21uo3i96XZQagL5k/mZPxaq1hf1bsH+ow+Jcg3X7b8RJRqniHxASyffdzHYbmfyE= - file: Rex.framework.zip - skip_cleanup: true - on: - repo: RACCommunity/Rex - tags: true diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index f58c3fada3..0000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,23 +0,0 @@ -We love that you're interested in contributing to this project! - -To make the process as painless as possible, we have just a couple of guidelines -that should make life easier for everyone involved. - -## Prefer Pull Requests - -If you know exactly how to implement the feature being suggested or fix the bug -being reported, please open a pull request instead of an issue. Pull requests are easier than -patches or inline code blocks for discussing and merging the changes. - -If you can't make the change yourself, please open an issue after making sure -that one isn't already logged. - -## Contributing Code - -Fork this repository, make it awesomer (preferably in a branch named for the -topic), send a pull request! - -All code contributions should match our [coding -conventions](https://github.com/github/swift-style-guide). - -Thanks for contributing! :boom::camel: diff --git a/Cartfile b/Cartfile deleted file mode 100644 index 38f3bb3b35..0000000000 --- a/Cartfile +++ /dev/null @@ -1 +0,0 @@ -github "ReactiveCocoa/ReactiveCocoa" "master" diff --git a/Cartfile.resolved b/Cartfile.resolved deleted file mode 100644 index 118b9955d8..0000000000 --- a/Cartfile.resolved +++ /dev/null @@ -1,3 +0,0 @@ -github "antitypical/Result" "3.0.0" -github "ReactiveCocoa/ReactiveSwift" "598ce311be21911d3d365c6b9c5245fdc9e72394" -github "ReactiveCocoa/ReactiveCocoa" "26997cf6f0eda7d64b76ba82013525e6ab900e8e" diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 790040a0b5..0000000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Neil Pankey - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md deleted file mode 100644 index a7670db36d..0000000000 --- a/README.md +++ /dev/null @@ -1,67 +0,0 @@ -# Rex [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) -Extensions for [ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa) that may not fit in the core framework. - -New development targets RAC 4/Swift 2/Xcode 7. For RAC 3 support [see the 0.5 -release](https://github.com/RACCommunity/Rex/releases/tag/v0.5.0). - -## Signal -All `Signal` operators are available for `SignaProducer`s too via explicit `lift`ing. - -##### `filterMap` -Applies `transform` to values from `signal` with non-nil results unwrapped and forwared on the returned signal. This is equivalent to `map { … } .filter { $0 != nil } .map { $0! }` but without creating extra intermediate signals. - -```swift -func filterMap(transform: T -> U?) -> Signal -``` - -##### `ignoreError` -Wraps a `signal` in a version that drops `Error` events. By default errors are replaced with a `Completed` event but `Interrupted` can also be specified as `replacement`. - -```swift -func ignoreError(#replacement: Event = .Completed) -> Signal -``` - -##### `timeoutAfter` -Forwards events from `signal` until it terminates or until `interval` time passes. This is nearly identical to `timeoutWithError` from RAC except any terminating `event` can be used for the timeout. - -```swift -func timeoutAfter(interval: NSTimeInterval, withEvent event: Event, onScheduler scheduler: DateSchedulerType) -> Signal -``` - -##### `uncollect` - -Flattens batches of elements sent on `signal` into each individual element. The inverse of `collect`. Requires signal values to conform to `SequenceType`. - -```swift -func uncollect() -> Signal -``` - - -## SignalProducer -Operators specific to `SignalProducer`. - -##### `groupBy` -Partitions values from `producer` into new producer groups based on the key returned from `grouping`. Termination events on the original producer are forwarded to each inner producer group. - -```swift -func groupBy(grouping: T -> K) -> SignalProducer<(K, SignalProducer), E> -``` - -## UIKit Extensions - -##### `UIButton.rex_pressed` - -Flexible way to bind `CocoaAction` to the press of button. In addition the button will be disabled during the `Action` executing. Such behavior is convenient for tasks that require some time, like a download process in the example below. - -```swift -let downloadAction = Action { _ in - let url = NSURL(string: "https://github.com/RACCommunity/Rex/archive/master.zip") - let request = NSURLRequest(URL: url!) - return NSURLSession.sharedSession().rac_dataWithRequest(request).map { $0.0 } -} - -downloadButton.rex_pressed.value = downloadAction.unsafeCocoaAction -``` - -## License -Rex is released under the [MIT license](LICENSE) diff --git a/Source/AppKit/NSTextField.swift b/ReactiveCocoa/AppKit/NSTextField.swift similarity index 96% rename from Source/AppKit/NSTextField.swift rename to ReactiveCocoa/AppKit/NSTextField.swift index 67c33218d4..5f81f08c90 100644 --- a/Source/AppKit/NSTextField.swift +++ b/ReactiveCocoa/AppKit/NSTextField.swift @@ -7,7 +7,6 @@ // import ReactiveSwift -import ReactiveCocoa import AppKit import enum Result.NoError diff --git a/Source/Foundation/Association.swift b/ReactiveCocoa/Association.swift similarity index 100% rename from Source/Foundation/Association.swift rename to ReactiveCocoa/Association.swift diff --git a/ReactiveCocoa/CocoaAction+Extensions.swift b/ReactiveCocoa/CocoaAction+Extensions.swift new file mode 100644 index 0000000000..d9da1599a4 --- /dev/null +++ b/ReactiveCocoa/CocoaAction+Extensions.swift @@ -0,0 +1,29 @@ +// +// CocoaAction.swift +// Rex +// +// Created by Neil Pankey on 6/19/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import ReactiveSwift +import ReactiveCocoa +import enum Result.NoError + +extension CocoaAction { + /// Creates an always disabled action that can be used as a default for + /// things like `rac_pressed`. + public static var rex_disabled: CocoaAction { + return CocoaAction(Action.rex_disabled, input: nil) + } + + /// Creates a producer for the `enabled` state of a CocoaAction. + public var rex_enabledProducer: SignalProducer { + return rex_producer(forKeyPath: #keyPath(CocoaAction.isEnabled)) + } + + /// Creates a producer for the `executing` state of a CocoaAction. + public var rex_executingProducer: SignalProducer { + return rex_producer(forKeyPath: #keyPath(CocoaAction.isExecuting)) + } +} diff --git a/Source/Reusable.swift b/ReactiveCocoa/Reusable.swift similarity index 100% rename from Source/Reusable.swift rename to ReactiveCocoa/Reusable.swift diff --git a/Source/UIKit/UIActivityIndicatorView.swift b/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift similarity index 97% rename from Source/UIKit/UIActivityIndicatorView.swift rename to ReactiveCocoa/UIKit/UIActivityIndicatorView.swift index 62fc2903a6..5342819d36 100644 --- a/Source/UIKit/UIActivityIndicatorView.swift +++ b/ReactiveCocoa/UIKit/UIActivityIndicatorView.swift @@ -7,7 +7,6 @@ // import ReactiveSwift -import ReactiveCocoa import UIKit extension UIActivityIndicatorView { diff --git a/Source/UIKit/UIBarButtonItem.swift b/ReactiveCocoa/UIKit/UIBarButtonItem.swift similarity index 98% rename from Source/UIKit/UIBarButtonItem.swift rename to ReactiveCocoa/UIKit/UIBarButtonItem.swift index fd9239f17f..4841b65de9 100644 --- a/Source/UIKit/UIBarButtonItem.swift +++ b/ReactiveCocoa/UIKit/UIBarButtonItem.swift @@ -7,7 +7,6 @@ // import ReactiveSwift -import ReactiveCocoa import UIKit extension UIBarButtonItem { diff --git a/Source/UIKit/UIBarItem.swift b/ReactiveCocoa/UIKit/UIBarItem.swift similarity index 95% rename from Source/UIKit/UIBarItem.swift rename to ReactiveCocoa/UIKit/UIBarItem.swift index 710ee88976..345f7f0964 100644 --- a/Source/UIKit/UIBarItem.swift +++ b/ReactiveCocoa/UIKit/UIBarItem.swift @@ -7,7 +7,6 @@ // import ReactiveSwift -import ReactiveCocoa import UIKit extension UIBarItem { diff --git a/Source/UIKit/UIButton.swift b/ReactiveCocoa/UIKit/UIButton.swift similarity index 98% rename from Source/UIKit/UIButton.swift rename to ReactiveCocoa/UIKit/UIButton.swift index 82361eccc7..201f8e02e3 100644 --- a/Source/UIKit/UIButton.swift +++ b/ReactiveCocoa/UIKit/UIButton.swift @@ -7,7 +7,6 @@ // import ReactiveSwift -import ReactiveCocoa import UIKit extension UIButton { diff --git a/Source/UIKit/UICollectionReusableView.swift b/ReactiveCocoa/UIKit/UICollectionReusableView.swift similarity index 100% rename from Source/UIKit/UICollectionReusableView.swift rename to ReactiveCocoa/UIKit/UICollectionReusableView.swift diff --git a/Source/UIKit/UIControl.swift b/ReactiveCocoa/UIKit/UIControl.swift similarity index 99% rename from Source/UIKit/UIControl.swift rename to ReactiveCocoa/UIKit/UIControl.swift index c9b5053f3a..045b839974 100644 --- a/Source/UIKit/UIControl.swift +++ b/ReactiveCocoa/UIKit/UIControl.swift @@ -7,7 +7,6 @@ // import ReactiveSwift -import ReactiveCocoa import UIKit import enum Result.NoError diff --git a/Source/UIKit/UIDatePicker.swift b/ReactiveCocoa/UIKit/UIDatePicker.swift similarity index 95% rename from Source/UIKit/UIDatePicker.swift rename to ReactiveCocoa/UIKit/UIDatePicker.swift index 3b14724431..9a3293d6e8 100644 --- a/Source/UIKit/UIDatePicker.swift +++ b/ReactiveCocoa/UIKit/UIDatePicker.swift @@ -7,7 +7,6 @@ // import ReactiveSwift -import ReactiveCocoa import UIKit extension UIDatePicker { diff --git a/Source/UIKit/UIImageView.swift b/ReactiveCocoa/UIKit/UIImageView.swift similarity index 97% rename from Source/UIKit/UIImageView.swift rename to ReactiveCocoa/UIKit/UIImageView.swift index 75094a71d0..266d6bb2de 100644 --- a/Source/UIKit/UIImageView.swift +++ b/ReactiveCocoa/UIKit/UIImageView.swift @@ -7,7 +7,6 @@ // import ReactiveSwift -import ReactiveCocoa import UIKit extension UIImageView { diff --git a/Source/UIKit/UILabel.swift b/ReactiveCocoa/UIKit/UILabel.swift similarity index 98% rename from Source/UIKit/UILabel.swift rename to ReactiveCocoa/UIKit/UILabel.swift index a77bcafa61..bf3b0b2974 100644 --- a/Source/UIKit/UILabel.swift +++ b/ReactiveCocoa/UIKit/UILabel.swift @@ -7,7 +7,6 @@ // import ReactiveSwift -import ReactiveCocoa import UIKit extension UILabel { diff --git a/Source/UIKit/UIProgressView.swift b/ReactiveCocoa/UIKit/UIProgressView.swift similarity index 95% rename from Source/UIKit/UIProgressView.swift rename to ReactiveCocoa/UIKit/UIProgressView.swift index 871d2871d5..a635cae14e 100644 --- a/Source/UIKit/UIProgressView.swift +++ b/ReactiveCocoa/UIKit/UIProgressView.swift @@ -7,7 +7,6 @@ // import ReactiveSwift -import ReactiveCocoa import UIKit extension UIProgressView { diff --git a/Source/UIKit/UISegmentedControl.swift b/ReactiveCocoa/UIKit/UISegmentedControl.swift similarity index 97% rename from Source/UIKit/UISegmentedControl.swift rename to ReactiveCocoa/UIKit/UISegmentedControl.swift index 4a7f4a1566..c961952822 100644 --- a/Source/UIKit/UISegmentedControl.swift +++ b/ReactiveCocoa/UIKit/UISegmentedControl.swift @@ -7,7 +7,6 @@ // import ReactiveSwift -import ReactiveCocoa import UIKit extension UISegmentedControl { diff --git a/Source/UIKit/UISwitch.swift b/ReactiveCocoa/UIKit/UISwitch.swift similarity index 94% rename from Source/UIKit/UISwitch.swift rename to ReactiveCocoa/UIKit/UISwitch.swift index 9c06c48eab..73984aac89 100644 --- a/Source/UIKit/UISwitch.swift +++ b/ReactiveCocoa/UIKit/UISwitch.swift @@ -7,7 +7,6 @@ // import ReactiveSwift -import ReactiveCocoa import UIKit extension UISwitch { diff --git a/Source/UIKit/UITableViewCell.swift b/ReactiveCocoa/UIKit/UITableViewCell.swift similarity index 91% rename from Source/UIKit/UITableViewCell.swift rename to ReactiveCocoa/UIKit/UITableViewCell.swift index 122df0153f..87a1177e00 100644 --- a/Source/UIKit/UITableViewCell.swift +++ b/ReactiveCocoa/UIKit/UITableViewCell.swift @@ -7,7 +7,6 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // -import ReactiveCocoa import UIKit extension UITableViewCell: Reusable {} diff --git a/Source/UIKit/UITableViewHeaderFooterView.swift b/ReactiveCocoa/UIKit/UITableViewHeaderFooterView.swift similarity index 100% rename from Source/UIKit/UITableViewHeaderFooterView.swift rename to ReactiveCocoa/UIKit/UITableViewHeaderFooterView.swift diff --git a/Source/UIKit/UITextField.swift b/ReactiveCocoa/UIKit/UITextField.swift similarity index 95% rename from Source/UIKit/UITextField.swift rename to ReactiveCocoa/UIKit/UITextField.swift index 4beb5361ca..60c35e26a4 100644 --- a/Source/UIKit/UITextField.swift +++ b/ReactiveCocoa/UIKit/UITextField.swift @@ -7,8 +7,6 @@ // import ReactiveSwift -import ReactiveSwift -import ReactiveCocoa import UIKit extension UITextField { diff --git a/Source/UIKit/UITextView.swift b/ReactiveCocoa/UIKit/UITextView.swift similarity index 96% rename from Source/UIKit/UITextView.swift rename to ReactiveCocoa/UIKit/UITextView.swift index 7d02851fcb..86818c3d05 100644 --- a/Source/UIKit/UITextView.swift +++ b/ReactiveCocoa/UIKit/UITextView.swift @@ -7,7 +7,6 @@ // import ReactiveSwift -import ReactiveCocoa import UIKit import enum Result.NoError diff --git a/Source/UIKit/UIView.swift b/ReactiveCocoa/UIKit/UIView.swift similarity index 98% rename from Source/UIKit/UIView.swift rename to ReactiveCocoa/UIKit/UIView.swift index 51bb64725b..ba0ff4dc85 100644 --- a/Source/UIKit/UIView.swift +++ b/ReactiveCocoa/UIKit/UIView.swift @@ -7,7 +7,6 @@ // import ReactiveSwift -import ReactiveCocoa import UIKit extension UIView { diff --git a/Source/UIKit/UIViewController.swift b/ReactiveCocoa/UIKit/UIViewController.swift similarity index 99% rename from Source/UIKit/UIViewController.swift rename to ReactiveCocoa/UIKit/UIViewController.swift index add6634257..d96d486a66 100644 --- a/Source/UIKit/UIViewController.swift +++ b/ReactiveCocoa/UIKit/UIViewController.swift @@ -8,7 +8,6 @@ import ReactiveSwift import Result -import ReactiveCocoa import UIKit extension UIViewController { diff --git a/Tests/Helpers/UIControl+EnableSendActionsForControlEvents.swift b/ReactiveCocoaTests/Helpers/UIControl+EnableSendActionsForControlEvents.swift similarity index 100% rename from Tests/Helpers/UIControl+EnableSendActionsForControlEvents.swift rename to ReactiveCocoaTests/Helpers/UIControl+EnableSendActionsForControlEvents.swift diff --git a/Tests/UIKit/UIActivityIndicatorViewTests.swift b/ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift similarity index 100% rename from Tests/UIKit/UIActivityIndicatorViewTests.swift rename to ReactiveCocoaTests/UIKit/UIActivityIndicatorViewTests.swift diff --git a/Tests/UIKit/UIBarButtonItemTests.swift b/ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift similarity index 100% rename from Tests/UIKit/UIBarButtonItemTests.swift rename to ReactiveCocoaTests/UIKit/UIBarButtonItemTests.swift diff --git a/Tests/UIKit/UIButtonTests.swift b/ReactiveCocoaTests/UIKit/UIButtonTests.swift similarity index 100% rename from Tests/UIKit/UIButtonTests.swift rename to ReactiveCocoaTests/UIKit/UIButtonTests.swift diff --git a/Tests/UIKit/UICollectionReusableViewTests.swift b/ReactiveCocoaTests/UIKit/UICollectionReusableViewTests.swift similarity index 100% rename from Tests/UIKit/UICollectionReusableViewTests.swift rename to ReactiveCocoaTests/UIKit/UICollectionReusableViewTests.swift diff --git a/Tests/UIKit/UIControlTests.swift b/ReactiveCocoaTests/UIKit/UIControlTests.swift similarity index 100% rename from Tests/UIKit/UIControlTests.swift rename to ReactiveCocoaTests/UIKit/UIControlTests.swift diff --git a/Tests/UIKit/UIDatePickerTests.swift b/ReactiveCocoaTests/UIKit/UIDatePickerTests.swift similarity index 100% rename from Tests/UIKit/UIDatePickerTests.swift rename to ReactiveCocoaTests/UIKit/UIDatePickerTests.swift diff --git a/Tests/UIKit/UIImageViewTests.swift b/ReactiveCocoaTests/UIKit/UIImageViewTests.swift similarity index 100% rename from Tests/UIKit/UIImageViewTests.swift rename to ReactiveCocoaTests/UIKit/UIImageViewTests.swift diff --git a/Tests/UIKit/UILabelTests.swift b/ReactiveCocoaTests/UIKit/UILabelTests.swift similarity index 100% rename from Tests/UIKit/UILabelTests.swift rename to ReactiveCocoaTests/UIKit/UILabelTests.swift diff --git a/Tests/UIKit/UIProgressViewTests.swift b/ReactiveCocoaTests/UIKit/UIProgressViewTests.swift similarity index 100% rename from Tests/UIKit/UIProgressViewTests.swift rename to ReactiveCocoaTests/UIKit/UIProgressViewTests.swift diff --git a/Tests/UIKit/UISegmentedControlTests.swift b/ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift similarity index 100% rename from Tests/UIKit/UISegmentedControlTests.swift rename to ReactiveCocoaTests/UIKit/UISegmentedControlTests.swift diff --git a/Tests/UIKit/UISwitchTests.swift b/ReactiveCocoaTests/UIKit/UISwitchTests.swift similarity index 100% rename from Tests/UIKit/UISwitchTests.swift rename to ReactiveCocoaTests/UIKit/UISwitchTests.swift diff --git a/Tests/UIKit/UITableViewCellTests.swift b/ReactiveCocoaTests/UIKit/UITableViewCellTests.swift similarity index 100% rename from Tests/UIKit/UITableViewCellTests.swift rename to ReactiveCocoaTests/UIKit/UITableViewCellTests.swift diff --git a/Tests/UIKit/UITableViewHeaderFooterViewTests.swift b/ReactiveCocoaTests/UIKit/UITableViewHeaderFooterViewTests.swift similarity index 100% rename from Tests/UIKit/UITableViewHeaderFooterViewTests.swift rename to ReactiveCocoaTests/UIKit/UITableViewHeaderFooterViewTests.swift diff --git a/Tests/UIKit/UITextFieldTests.swift b/ReactiveCocoaTests/UIKit/UITextFieldTests.swift similarity index 100% rename from Tests/UIKit/UITextFieldTests.swift rename to ReactiveCocoaTests/UIKit/UITextFieldTests.swift diff --git a/Tests/UIKit/UITextViewTests.swift b/ReactiveCocoaTests/UIKit/UITextViewTests.swift similarity index 100% rename from Tests/UIKit/UITextViewTests.swift rename to ReactiveCocoaTests/UIKit/UITextViewTests.swift diff --git a/Tests/UIKit/UIViewControllerTests.swift b/ReactiveCocoaTests/UIKit/UIViewControllerTests.swift similarity index 100% rename from Tests/UIKit/UIViewControllerTests.swift rename to ReactiveCocoaTests/UIKit/UIViewControllerTests.swift diff --git a/Tests/UIKit/UIViewTests.swift b/ReactiveCocoaTests/UIKit/UIViewTests.swift similarity index 100% rename from Tests/UIKit/UIViewTests.swift rename to ReactiveCocoaTests/UIKit/UIViewTests.swift diff --git a/Source/RACSignal.swift b/ReactiveObjCBridge/RACSignal.swift similarity index 97% rename from Source/RACSignal.swift rename to ReactiveObjCBridge/RACSignal.swift index c1220bd4a9..7cfa075d8a 100644 --- a/Source/RACSignal.swift +++ b/ReactiveObjCBridge/RACSignal.swift @@ -6,8 +6,8 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveObjC import ReactiveSwift -import ReactiveCocoa import enum Result.NoError extension RACSignal { diff --git a/Rex.podspec b/Rex.podspec deleted file mode 100644 index 917d3e6e9a..0000000000 --- a/Rex.podspec +++ /dev/null @@ -1,33 +0,0 @@ -Pod::Spec.new do |s| - s.name = 'Rex' - s.module_name = 'Rex' - s.version = '0.10.0' - s.summary = 'ReactiveCocoa Extensions' - - s.description = <<-DESC - Extensions for ReactiveCocoa that may not fit in the core framework. - DESC - - s.homepage = 'https://github.com/neilpa/Rex' - s.license = 'MIT' - - s.author = { 'Neil Pankey' => 'npankey@gmail.com' } - - s.ios.deployment_target = '8.0' - s.osx.deployment_target = '10.10' - s.watchos.deployment_target = '2.0' - s.tvos.deployment_target = '9.0' - - s.source = { :git => 'https://github.com/neilpa/Rex.git', :tag => s.version } - s.dependency 'ReactiveCocoa', '~> 4.1' - s.ios.framework = 'UIKit' - s.tvos.framework = 'UIKit' - s.osx.framework = 'AppKit' - - s.source_files = 'Source/**/*.swift' - s.ios.exclude_files = 'Source/AppKit/*' - s.tvos.exclude_files = 'Source/AppKit/*', 'Source/UIKit/UIDatePicker.swift', 'Source/UIKit/UISwitch.swift', 'Source/UIKit/UISegmentedControl.swift' - s.watchos.exclude_files = 'Source/AppKit/*', 'Source/UIKit/*' - s.osx.exclude_files = 'Source/UIKit/*' - -end diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj deleted file mode 100644 index 33106c09a0..0000000000 --- a/Rex.xcodeproj/project.pbxproj +++ /dev/null @@ -1,1551 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 4238D5961B4D5950008534C0 /* NSTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4238D5951B4D5950008534C0 /* NSTextField.swift */; }; - 45CED46F1D27C1E300788BDC /* UIActivityIndicatorViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CED46D1D27C1D400788BDC /* UIActivityIndicatorViewTests.swift */; }; - 45CED4701D27C1E400788BDC /* UIActivityIndicatorViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CED46D1D27C1D400788BDC /* UIActivityIndicatorViewTests.swift */; }; - 45CED4711D27C1EB00788BDC /* UIActivityIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CED46B1D27BB8700788BDC /* UIActivityIndicatorView.swift */; }; - 45CED4721D27C1EC00788BDC /* UIActivityIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CED46B1D27BB8700788BDC /* UIActivityIndicatorView.swift */; }; - 4A8EDADB1D54D12400A1734C /* UIControl+EnableSendActionsForControlEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D0DABA91CCC381F00B6CD2B /* UIControl+EnableSendActionsForControlEvents.swift */; }; - 5B7F81E31D0842AD0014B06D /* UISegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1C882D1D0715CE000B888F /* UISegmentedControl.swift */; }; - 5B7F81E41D0842B50014B06D /* UISegmentedControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1C882F1D071639000B888F /* UISegmentedControlTests.swift */; }; - 7D2AA99B1CB6EFEB008AB5C9 /* UISwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D2AA99A1CB6EFEB008AB5C9 /* UISwitch.swift */; }; - 7D2AA99D1CB6F275008AB5C9 /* UISwitchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D2AA99C1CB6F275008AB5C9 /* UISwitchTests.swift */; }; - 7D5FE3081CD4B04E00834675 /* UITextFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E851C4B420A00086F3C /* UITextFieldTests.swift */; }; - 7DBD48F31CC8141D0077AD4F /* Reusable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DBD48F21CC8141D0077AD4F /* Reusable.swift */; }; - 7DBD48F41CC8141D0077AD4F /* Reusable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DBD48F21CC8141D0077AD4F /* Reusable.swift */; }; - 7DC325741CC6FCF100746D88 /* UITableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC325721CC6FCF100746D88 /* UITableViewCell.swift */; }; - 7DC325751CC6FCF100746D88 /* UITableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC325721CC6FCF100746D88 /* UITableViewCell.swift */; }; - 7DC325761CC6FCF100746D88 /* UITableViewHeaderFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC325731CC6FCF100746D88 /* UITableViewHeaderFooterView.swift */; }; - 7DC325771CC6FCF100746D88 /* UITableViewHeaderFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC325731CC6FCF100746D88 /* UITableViewHeaderFooterView.swift */; }; - 7DC3257E1CC6FD1C00746D88 /* UITableViewCellTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC325781CC6FD0A00746D88 /* UITableViewCellTests.swift */; }; - 7DC3257F1CC6FD1E00746D88 /* UITableViewCellTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC325781CC6FD0A00746D88 /* UITableViewCellTests.swift */; }; - 7DC325801CC6FD2100746D88 /* UITableViewHeaderFooterViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC325791CC6FD0A00746D88 /* UITableViewHeaderFooterViewTests.swift */; }; - 7DC325811CC6FD2300746D88 /* UITableViewHeaderFooterViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC325791CC6FD0A00746D88 /* UITableViewHeaderFooterViewTests.swift */; }; - 7DCF5B331CC80D77004AEE75 /* UICollectionReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DCF5B311CC80D0E004AEE75 /* UICollectionReusableView.swift */; }; - 7DCF5B341CC80D78004AEE75 /* UICollectionReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DCF5B311CC80D0E004AEE75 /* UICollectionReusableView.swift */; }; - 7DCF5B361CC80E8E004AEE75 /* UICollectionReusableViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DCF5B351CC80E8E004AEE75 /* UICollectionReusableViewTests.swift */; }; - 7DCF5B371CC80E8E004AEE75 /* UICollectionReusableViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DCF5B351CC80E8E004AEE75 /* UICollectionReusableViewTests.swift */; }; - 8289A2E11BD7EF1F0097FB60 /* UIImageViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */; }; - 8289A2E31BD7EF740097FB60 /* UIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E21BD7EF740097FB60 /* UIImageView.swift */; }; - 8289A2E51BD7F6DD0097FB60 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E41BD7F6DD0097FB60 /* UIView.swift */; }; - 8289A2E81BD7F7900097FB60 /* UIViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */; }; - 8295FD871B87309F007C9000 /* UIControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8295FD851B873081007C9000 /* UIControlTests.swift */; }; - 8295FD8A1B87352D007C9000 /* UIButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8295FD881B873490007C9000 /* UIButtonTests.swift */; }; - 8295FD8D1B87374A007C9000 /* UIBarButtonItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */; }; - 9A0501931D86D6D9006BBAE8 /* ReactiveSwift.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9A0501921D86D6D9006BBAE8 /* ReactiveSwift.framework */; }; - 9A0501941D86D6F0006BBAE8 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A0501921D86D6D9006BBAE8 /* ReactiveSwift.framework */; }; - 9A0501951D86D6FB006BBAE8 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A0501921D86D6D9006BBAE8 /* ReactiveSwift.framework */; }; - 9A0501971D86D713006BBAE8 /* ReactiveSwift.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = 9A0501961D86D713006BBAE8 /* ReactiveSwift.framework */; }; - 9A0501981D86D71D006BBAE8 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A0501961D86D713006BBAE8 /* ReactiveSwift.framework */; }; - 9A05019B1D86D737006BBAE8 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A05019A1D86D737006BBAE8 /* ReactiveSwift.framework */; }; - 9A05019D1D86D751006BBAE8 /* ReactiveSwift.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9A05019A1D86D737006BBAE8 /* ReactiveSwift.framework */; }; - 9A05019E1D86D759006BBAE8 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A05019A1D86D737006BBAE8 /* ReactiveSwift.framework */; }; - 9A0501A01D86D76C006BBAE8 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A05019F1D86D765006BBAE8 /* ReactiveSwift.framework */; }; - 9A4375B41D86DBA000F04A59 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A0501961D86D713006BBAE8 /* ReactiveSwift.framework */; }; - 9A4375B51D86DBA000F04A59 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; }; - 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 */; }; - C72CF3E61CBF188A00E19897 /* RACSignal.swift in Sources */ = {isa = PBXBuildFile; fileRef = C72CF3E41CBF188A00E19897 /* RACSignal.swift */; }; - C72CF3E71CBF188A00E19897 /* RACSignal.swift in Sources */ = {isa = PBXBuildFile; fileRef = C72CF3E41CBF188A00E19897 /* RACSignal.swift */; }; - C72CF3E81CBF188A00E19897 /* RACSignal.swift in Sources */ = {isa = PBXBuildFile; fileRef = C72CF3E41CBF188A00E19897 /* RACSignal.swift */; }; - C7932E831C4B3F3000086F3C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E811C4B3EDB00086F3C /* UITextField.swift */; }; - C7932E841C4B41E100086F3C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E811C4B3EDB00086F3C /* UITextField.swift */; }; - C7932E871C4B42F500086F3C /* UITextFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E851C4B420A00086F3C /* UITextFieldTests.swift */; }; - C7945F111CC192E800DC9E37 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7945F101CC192E800DC9E37 /* UIViewController.swift */; }; - C7945F141CC1DFBE00DC9E37 /* UIViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7945F121CC1DFB400DC9E37 /* UIViewControllerTests.swift */; }; - C7DCE2B41CB3C89A001217D8 /* UITextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DCE2B21CB3C872001217D8 /* UITextView.swift */; }; - C7DCE2B71CB3C9D6001217D8 /* UITextViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DCE2B51CB3C9C1001217D8 /* UITextViewTests.swift */; }; - CC02C18A1CCA704E0025CC04 /* ActionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC02C1881CCA704C0025CC04 /* ActionTests.swift */; }; - CC02C18B1CCA704F0025CC04 /* ActionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC02C1881CCA704C0025CC04 /* ActionTests.swift */; }; - CC02C18C1CCA704F0025CC04 /* ActionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC02C1881CCA704C0025CC04 /* ActionTests.swift */; }; - D8003E941AFEC3D400D7D3C5 /* Rex.h in Headers */ = {isa = PBXBuildFile; fileRef = D8003E931AFEC3D400D7D3C5 /* Rex.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (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 */; }; - D8003EC31AFED30F00D7D3C5 /* SignalProducerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EC01AFED30100D7D3C5 /* SignalProducerTests.swift */; }; - D8003EC51AFED36F00D7D3C5 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; }; - D8003EC61AFED36F00D7D3C5 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAE1AFEC68A00D7D3C5 /* Result.framework */; }; - 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 */; }; - D83457331AFEE4930070616A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAE1AFEC68A00D7D3C5 /* Result.framework */; }; - D83457351AFEE4B20070616A /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */; }; - D83457361AFEE4B20070616A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */; }; - D83457391AFEE4BE0070616A /* ReactiveCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */; }; - D834573A1AFEE4BE0070616A /* Result.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */; }; - D834573C1AFEE57E0070616A /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */; }; - D834573D1AFEE57E0070616A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */; }; - D83457411AFEE6050070616A /* SignalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */; }; - D86FFBD11B34AD6F001A89B3 /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD01B34AD6F001A89B3 /* Association.swift */; }; - D86FFBD21B34AD7A001A89B3 /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD01B34AD6F001A89B3 /* Association.swift */; }; - D86FFBD61B34B116001A89B3 /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD41B34B0FE001A89B3 /* UIControl.swift */; }; - D86FFBD81B34B242001A89B3 /* UILabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD71B34B242001A89B3 /* UILabel.swift */; }; - D86FFBDA1B34B3F0001A89B3 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD91B34B3F0001A89B3 /* Action.swift */; }; - D86FFBDB1B34B3F0001A89B3 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD91B34B3F0001A89B3 /* Action.swift */; }; - D86FFBDD1B34B691001A89B3 /* UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBDC1B34B691001A89B3 /* UIButton.swift */; }; - D8715D9D1C210FF9005F4191 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD91B34B3F0001A89B3 /* Action.swift */; }; - D8715D9E1C210FF9005F4191 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Property.swift */; }; - D8715D9F1C210FF9005F4191 /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBC1AFED01000D7D3C5 /* Signal.swift */; }; - D8715DA01C210FF9005F4191 /* SignalProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */; }; - 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 /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973A1B17F2F7002E15BA /* Data.swift */; }; - D8715DA51C21107F005F4191 /* NSObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097431B17F3C8002E15BA /* NSObject.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, ); }; }; - D8715DBA1C2112D1005F4191 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD91B34B3F0001A89B3 /* Action.swift */; }; - D8715DBB1C2112D1005F4191 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Property.swift */; }; - 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 /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973A1B17F2F7002E15BA /* Data.swift */; }; - D8715DC01C2112D6005F4191 /* NSObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097431B17F3C8002E15BA /* NSObject.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 */; }; - D8715DC71C211553005F4191 /* UIBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5173EBC51B625A2600C9B48E /* UIBarItem.swift */; }; - D8715DC81C211553005F4191 /* UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBDC1B34B691001A89B3 /* UIButton.swift */; }; - D8715DC91C211553005F4191 /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD41B34B0FE001A89B3 /* UIControl.swift */; }; - D8715DCA1C211553005F4191 /* UILabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD71B34B242001A89B3 /* UILabel.swift */; }; - D8715DCB1C211553005F4191 /* UIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E21BD7EF740097FB60 /* UIImageView.swift */; }; - D8715DCC1C211553005F4191 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E41BD7F6DD0097FB60 /* UIView.swift */; }; - D8715DDC1C211637005F4191 /* PropertyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454081BD2772700C9E790 /* PropertyTests.swift */; }; - D8715DDD1C211637005F4191 /* SignalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */; }; - D8715DDE1C211637005F4191 /* SignalProducerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EC01AFED30100D7D3C5 /* SignalProducerTests.swift */; }; - D8715DDF1C21163B005F4191 /* NSObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097471B17F5DD002E15BA /* NSObjectTests.swift */; }; - D8715DE01C211643005F4191 /* UIBarButtonItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */; }; - D8715DE11C211643005F4191 /* UIButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8295FD881B873490007C9000 /* UIButtonTests.swift */; }; - D8715DE21C211643005F4191 /* UIControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8295FD851B873081007C9000 /* UIControlTests.swift */; }; - D8715DE31C211643005F4191 /* UILabelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F073141B861B3A0047D546 /* UILabelTests.swift */; }; - D8715DE41C211643005F4191 /* UIImageViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */; }; - D8715DE51C211643005F4191 /* UIViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */; }; - D8715DE61C21170C005F4191 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8715DC21C211310005F4191 /* ReactiveCocoa.framework */; }; - D8715DE71C21170C005F4191 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8715DC31C211310005F4191 /* Result.framework */; }; - D8715DE91C211739005F4191 /* ReactiveCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8715DC21C211310005F4191 /* ReactiveCocoa.framework */; }; - D8715DEA1C211739005F4191 /* Result.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8715DC31C211310005F4191 /* Result.framework */; }; - D8A454061BD26A1A00C9E790 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Property.swift */; }; - D8A454071BD26A1A00C9E790 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Property.swift */; }; - D8A454091BD2772700C9E790 /* PropertyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454081BD2772700C9E790 /* PropertyTests.swift */; }; - D8A4540A1BD2772700C9E790 /* PropertyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454081BD2772700C9E790 /* PropertyTests.swift */; }; - 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 /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973A1B17F2F7002E15BA /* Data.swift */; }; - D8F0973D1B17F30D002E15BA /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973C1B17F30D002E15BA /* UserDefaults.swift */; }; - D8F0973E1B17F30D002E15BA /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973C1B17F30D002E15BA /* UserDefaults.swift */; }; - D8F0973F1B17F31E002E15BA /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973A1B17F2F7002E15BA /* Data.swift */; }; - D8F097441B17F3C8002E15BA /* NSObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097431B17F3C8002E15BA /* NSObject.swift */; }; - D8F097451B17F3C8002E15BA /* NSObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097431B17F3C8002E15BA /* NSObject.swift */; }; - D8F0974A1B17F5E1002E15BA /* NSObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097471B17F5DD002E15BA /* NSObjectTests.swift */; }; - D8F0974B1B17F5E2002E15BA /* NSObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097471B17F5DD002E15BA /* NSObjectTests.swift */; }; - E6933BEA1CD9C335006F7CE7 /* UIProgressViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6933BE81CD9C1F1006F7CE7 /* UIProgressViewTests.swift */; }; - E6933BEB1CD9C363006F7CE7 /* UIProgressViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6933BE81CD9C1F1006F7CE7 /* UIProgressViewTests.swift */; }; - E6933BEC1CD9C37D006F7CE7 /* UIProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6933BE61CD9C0B2006F7CE7 /* UIProgressView.swift */; }; - E6933BED1CD9C37D006F7CE7 /* UIProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6933BE61CD9C0B2006F7CE7 /* UIProgressView.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - D8003E9B1AFEC3D400D7D3C5 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D8003E851AFEC3D400D7D3C5 /* Project object */; - proxyType = 1; - remoteGlobalIDString = D8003E8D1AFEC3D400D7D3C5; - remoteInfo = Rex; - }; - D83457201AFEE44E0070616A /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D8003E851AFEC3D400D7D3C5 /* Project object */; - proxyType = 1; - remoteGlobalIDString = D83457131AFEE44E0070616A; - remoteInfo = "Rex-iOS"; - }; - D8715DD71C21160A005F4191 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D8003E851AFEC3D400D7D3C5 /* Project object */; - proxyType = 1; - remoteGlobalIDString = D8715DAF1C21123E005F4191; - remoteInfo = "Rex-tvOS"; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - D8003EB21AFEC6A800D7D3C5 /* Copy Files */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 16; - files = ( - 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 */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 16; - files = ( - D83457391AFEE4BE0070616A /* ReactiveCocoa.framework in CopyFiles */, - 9A0501931D86D6D9006BBAE8 /* ReactiveSwift.framework in CopyFiles */, - D834573A1AFEE4BE0070616A /* Result.framework in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D8715DE81C21172A005F4191 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 16; - files = ( - 9A05019D1D86D751006BBAE8 /* ReactiveSwift.framework in CopyFiles */, - D8715DE91C211739005F4191 /* ReactiveCocoa.framework in CopyFiles */, - D8715DEA1C211739005F4191 /* Result.framework in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 4238D5951B4D5950008534C0 /* NSTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; name = NSTextField.swift; path = AppKit/NSTextField.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 45CED46B1D27BB8700788BDC /* UIActivityIndicatorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorView.swift; sourceTree = ""; }; - 45CED46D1D27C1D400788BDC /* UIActivityIndicatorViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorViewTests.swift; sourceTree = ""; }; - 5173EBC51B625A2600C9B48E /* UIBarItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIBarItem.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIBarButtonItem.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 5B1C882D1D0715CE000B888F /* UISegmentedControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISegmentedControl.swift; sourceTree = ""; }; - 5B1C882F1D071639000B888F /* UISegmentedControlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISegmentedControlTests.swift; sourceTree = ""; }; - 7D0DABA91CCC381F00B6CD2B /* UIControl+EnableSendActionsForControlEvents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIControl+EnableSendActionsForControlEvents.swift"; sourceTree = ""; }; - 7D2AA99A1CB6EFEB008AB5C9 /* UISwitch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UISwitch.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 7D2AA99C1CB6F275008AB5C9 /* UISwitchTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UISwitchTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 7DBD48F21CC8141D0077AD4F /* Reusable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reusable.swift; sourceTree = ""; }; - 7DC325721CC6FCF100746D88 /* UITableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewCell.swift; sourceTree = ""; }; - 7DC325731CC6FCF100746D88 /* UITableViewHeaderFooterView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewHeaderFooterView.swift; sourceTree = ""; }; - 7DC325781CC6FD0A00746D88 /* UITableViewCellTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewCellTests.swift; sourceTree = ""; }; - 7DC325791CC6FD0A00746D88 /* UITableViewHeaderFooterViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITableViewHeaderFooterViewTests.swift; sourceTree = ""; }; - 7DCF5B311CC80D0E004AEE75 /* UICollectionReusableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UICollectionReusableView.swift; sourceTree = ""; }; - 7DCF5B351CC80E8E004AEE75 /* UICollectionReusableViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UICollectionReusableViewTests.swift; sourceTree = ""; }; - 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIImageViewTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 8289A2E21BD7EF740097FB60 /* UIImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIImageView.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 8289A2E41BD7F6DD0097FB60 /* UIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIView.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIViewTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 8295FD851B873081007C9000 /* UIControlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIControlTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 8295FD881B873490007C9000 /* UIButtonTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIButtonTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIBarButtonItemTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 9A0501921D86D6D9006BBAE8 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ReactiveSwift.framework; sourceTree = ""; }; - 9A0501961D86D713006BBAE8 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ReactiveSwift.framework; sourceTree = ""; }; - 9A05019A1D86D737006BBAE8 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = tvOS/ReactiveSwift.framework; sourceTree = ""; }; - 9A05019F1D86D765006BBAE8 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = watchOS/ReactiveSwift.framework; sourceTree = ""; }; - 9A5492111D33387D009A8D44 /* Deprecations+Removals.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deprecations+Removals.swift"; sourceTree = SOURCE_ROOT; }; - 9DA915A31CA6301C003723B9 /* UIDatePicker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIDatePicker.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 9DA915A51CA63046003723B9 /* UIDatePickerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIDatePickerTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 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; }; - 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; }; - C7DCE2B51CB3C9C1001217D8 /* UITextViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UITextViewTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - CC02C1881CCA704C0025CC04 /* ActionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionTests.swift; sourceTree = ""; }; - D8003E921AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - D8003E931AFEC3D400D7D3C5 /* Rex.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Rex.h; sourceTree = ""; }; - D8003E9F1AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ReactiveCocoa.framework; sourceTree = ""; }; - D8003EAE1AFEC68A00D7D3C5 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = ""; }; - D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SignalProducer.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - D8003EBC1AFED01000D7D3C5 /* Signal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Signal.swift; sourceTree = ""; }; - D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SignalTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - D8003EC01AFED30100D7D3C5 /* SignalProducerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SignalProducerTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 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-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; }; - D86FFBD41B34B0FE001A89B3 /* UIControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIControl.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - D86FFBD71B34B242001A89B3 /* UILabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UILabel.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - D86FFBD91B34B3F0001A89B3 /* Action.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Action.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - D86FFBDC1B34B691001A89B3 /* UIButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIButton.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - D8715D941C210F97005F4191 /* Rex.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Rex.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D8715DA71C2110DA005F4191 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveCocoa.framework; path = watchOS/ReactiveCocoa.framework; sourceTree = ""; }; - D8715DA81C2110DA005F4191 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = watchOS/Result.framework; sourceTree = ""; }; - D8715DB01C21123E005F4191 /* Rex.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Rex.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D8715DC21C211310005F4191 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveCocoa.framework; path = tvOS/ReactiveCocoa.framework; sourceTree = ""; }; - D8715DC31C211310005F4191 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = tvOS/Result.framework; sourceTree = ""; }; - D8715DD11C21160A005F4191 /* RexTests-tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RexTests-tvOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; - 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 /* Data.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Data.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - D8F0973C1B17F30D002E15BA /* UserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UserDefaults.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - D8F097431B17F3C8002E15BA /* NSObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = NSObject.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - D8F097471B17F5DD002E15BA /* NSObjectTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = NSObjectTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - E6933BE61CD9C0B2006F7CE7 /* UIProgressView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIProgressView.swift; sourceTree = ""; }; - E6933BE81CD9C1F1006F7CE7 /* UIProgressViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIProgressViewTests.swift; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - D8003E8A1AFEC3D400D7D3C5 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - D83457331AFEE4930070616A /* Result.framework in Frameworks */, - 9A4375B41D86DBA000F04A59 /* ReactiveSwift.framework in Frameworks */, - 9A4375B51D86DBA000F04A59 /* ReactiveCocoa.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D8003E961AFEC3D400D7D3C5 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - D8003EC51AFED36F00D7D3C5 /* ReactiveCocoa.framework in Frameworks */, - 9A0501981D86D71D006BBAE8 /* ReactiveSwift.framework in Frameworks */, - D8003EC61AFED36F00D7D3C5 /* Result.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D83457101AFEE44E0070616A /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - D83457361AFEE4B20070616A /* Result.framework in Frameworks */, - 9A0501951D86D6FB006BBAE8 /* ReactiveSwift.framework in Frameworks */, - D83457351AFEE4B20070616A /* ReactiveCocoa.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D834571B1AFEE44E0070616A /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - D834573C1AFEE57E0070616A /* ReactiveCocoa.framework in Frameworks */, - 9A0501941D86D6F0006BBAE8 /* ReactiveSwift.framework in Frameworks */, - D834573D1AFEE57E0070616A /* Result.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D8715D901C210F97005F4191 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - D8715DAA1C2110DA005F4191 /* Result.framework in Frameworks */, - 9A0501A01D86D76C006BBAE8 /* ReactiveSwift.framework in Frameworks */, - D8715DA91C2110DA005F4191 /* ReactiveCocoa.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D8715DAC1C21123E005F4191 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - D8715DC51C211310005F4191 /* Result.framework in Frameworks */, - 9A05019E1D86D759006BBAE8 /* ReactiveSwift.framework in Frameworks */, - D8715DC41C211310005F4191 /* ReactiveCocoa.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D8715DCE1C21160A005F4191 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - D8715DE61C21170C005F4191 /* ReactiveCocoa.framework in Frameworks */, - D8715DE71C21170C005F4191 /* Result.framework in Frameworks */, - 9A05019B1D86D737006BBAE8 /* ReactiveSwift.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 4238D5941B4D593E008534C0 /* AppKit */ = { - isa = PBXGroup; - children = ( - 4238D5951B4D5950008534C0 /* NSTextField.swift */, - ); - name = AppKit; - sourceTree = ""; - }; - 7D0DABA81CCC380700B6CD2B /* Helpers */ = { - isa = PBXGroup; - children = ( - 7D0DABA91CCC381F00B6CD2B /* UIControl+EnableSendActionsForControlEvents.swift */, - ); - path = Helpers; - sourceTree = ""; - }; - D8003E841AFEC3D400D7D3C5 = { - isa = PBXGroup; - children = ( - D8003E901AFEC3D400D7D3C5 /* Source */, - D8003E9D1AFEC3D400D7D3C5 /* Tests */, - D8003EAA1AFEC57200D7D3C5 /* Binaries */, - ); - indentWidth = 4; - sourceTree = ""; - tabWidth = 4; - usesTabs = 0; - }; - D8003E901AFEC3D400D7D3C5 /* Source */ = { - isa = PBXGroup; - children = ( - D86FFBD91B34B3F0001A89B3 /* Action.swift */, - D8A454051BD26A1A00C9E790 /* Property.swift */, - D8003EBC1AFED01000D7D3C5 /* Signal.swift */, - D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */, - C72CF3E41CBF188A00E19897 /* RACSignal.swift */, - 7DBD48F21CC8141D0077AD4F /* Reusable.swift */, - 4238D5941B4D593E008534C0 /* AppKit */, - D8F097391B17F2BF002E15BA /* Foundation */, - D86FFBD31B34B0E2001A89B3 /* UIKit */, - D8003E911AFEC3D400D7D3C5 /* Supporting Files */, - 9A5492111D33387D009A8D44 /* Deprecations+Removals.swift */, - ); - path = Source; - sourceTree = ""; - }; - D8003E911AFEC3D400D7D3C5 /* Supporting Files */ = { - isa = PBXGroup; - children = ( - D8003E931AFEC3D400D7D3C5 /* Rex.h */, - D8003E921AFEC3D400D7D3C5 /* Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - D8003E9D1AFEC3D400D7D3C5 /* Tests */ = { - isa = PBXGroup; - children = ( - CC02C1881CCA704C0025CC04 /* ActionTests.swift */, - D8A454081BD2772700C9E790 /* PropertyTests.swift */, - D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */, - D8003EC01AFED30100D7D3C5 /* SignalProducerTests.swift */, - D8F097461B17F5BF002E15BA /* Foundation */, - 7D0DABA81CCC380700B6CD2B /* Helpers */, - D8F073131B861B110047D546 /* UIKit */, - D8003E9E1AFEC3D400D7D3C5 /* Supporting Files */, - ); - path = Tests; - sourceTree = ""; - }; - D8003E9E1AFEC3D400D7D3C5 /* Supporting Files */ = { - isa = PBXGroup; - children = ( - D8003E9F1AFEC3D400D7D3C5 /* Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - D8003EAA1AFEC57200D7D3C5 /* Binaries */ = { - isa = PBXGroup; - children = ( - D8003EC71AFEE39000D7D3C5 /* iOS */, - D8003EAB1AFEC67700D7D3C5 /* macOS */, - D8715DB81C21124B005F4191 /* tvOS */, - D8715D9C1C210FA1005F4191 /* watchOS */, - ); - name = Binaries; - path = Carthage/Build; - sourceTree = ""; - }; - D8003EAB1AFEC67700D7D3C5 /* macOS */ = { - isa = PBXGroup; - children = ( - D86E77A91AFEE646004BF23D /* Rex.framework */, - D86E77AA1AFEE646004BF23D /* RexTests-macOS.xctest */, - 9A0501961D86D713006BBAE8 /* ReactiveSwift.framework */, - D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */, - D8003EAE1AFEC68A00D7D3C5 /* Result.framework */, - ); - name = macOS; - path = Mac; - sourceTree = ""; - }; - D8003EC71AFEE39000D7D3C5 /* iOS */ = { - isa = PBXGroup; - children = ( - D86E77AB1AFEE646004BF23D /* Rex.framework */, - D86E77AC1AFEE646004BF23D /* RexTests-iOS.xctest */, - 9A0501921D86D6D9006BBAE8 /* ReactiveSwift.framework */, - D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */, - D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */, - ); - path = iOS; - sourceTree = ""; - }; - D86FFBD31B34B0E2001A89B3 /* UIKit */ = { - isa = PBXGroup; - children = ( - 45CED46B1D27BB8700788BDC /* UIActivityIndicatorView.swift */, - 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */, - 5173EBC51B625A2600C9B48E /* UIBarItem.swift */, - D86FFBDC1B34B691001A89B3 /* UIButton.swift */, - 7DCF5B311CC80D0E004AEE75 /* UICollectionReusableView.swift */, - D86FFBD41B34B0FE001A89B3 /* UIControl.swift */, - 9DA915A31CA6301C003723B9 /* UIDatePicker.swift */, - 8289A2E21BD7EF740097FB60 /* UIImageView.swift */, - D86FFBD71B34B242001A89B3 /* UILabel.swift */, - E6933BE61CD9C0B2006F7CE7 /* UIProgressView.swift */, - 7D2AA99A1CB6EFEB008AB5C9 /* UISwitch.swift */, - 5B1C882D1D0715CE000B888F /* UISegmentedControl.swift */, - 7DC325721CC6FCF100746D88 /* UITableViewCell.swift */, - 7DC325731CC6FCF100746D88 /* UITableViewHeaderFooterView.swift */, - C7932E811C4B3EDB00086F3C /* UITextField.swift */, - C7DCE2B21CB3C872001217D8 /* UITextView.swift */, - 8289A2E41BD7F6DD0097FB60 /* UIView.swift */, - C7945F101CC192E800DC9E37 /* UIViewController.swift */, - ); - path = UIKit; - sourceTree = ""; - }; - D8715D9C1C210FA1005F4191 /* watchOS */ = { - isa = PBXGroup; - children = ( - D8715D941C210F97005F4191 /* Rex.framework */, - D8715DA71C2110DA005F4191 /* ReactiveCocoa.framework */, - 9A05019F1D86D765006BBAE8 /* ReactiveSwift.framework */, - D8715DA81C2110DA005F4191 /* Result.framework */, - ); - name = watchOS; - sourceTree = ""; - }; - D8715DB81C21124B005F4191 /* tvOS */ = { - isa = PBXGroup; - children = ( - D8715DB01C21123E005F4191 /* Rex.framework */, - D8715DD11C21160A005F4191 /* RexTests-tvOS.xctest */, - 9A05019A1D86D737006BBAE8 /* ReactiveSwift.framework */, - D8715DC21C211310005F4191 /* ReactiveCocoa.framework */, - D8715DC31C211310005F4191 /* Result.framework */, - ); - name = tvOS; - sourceTree = ""; - }; - D8F073131B861B110047D546 /* UIKit */ = { - isa = PBXGroup; - children = ( - 45CED46D1D27C1D400788BDC /* UIActivityIndicatorViewTests.swift */, - 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */, - 8295FD881B873490007C9000 /* UIButtonTests.swift */, - 8295FD851B873081007C9000 /* UIControlTests.swift */, - 7DCF5B351CC80E8E004AEE75 /* UICollectionReusableViewTests.swift */, - 9DA915A51CA63046003723B9 /* UIDatePickerTests.swift */, - 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */, - D8F073141B861B3A0047D546 /* UILabelTests.swift */, - E6933BE81CD9C1F1006F7CE7 /* UIProgressViewTests.swift */, - 7D2AA99C1CB6F275008AB5C9 /* UISwitchTests.swift */, - 5B1C882F1D071639000B888F /* UISegmentedControlTests.swift */, - 7DC325781CC6FD0A00746D88 /* UITableViewCellTests.swift */, - 7DC325791CC6FD0A00746D88 /* UITableViewHeaderFooterViewTests.swift */, - C7932E851C4B420A00086F3C /* UITextFieldTests.swift */, - C7DCE2B51CB3C9C1001217D8 /* UITextViewTests.swift */, - 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */, - C7945F121CC1DFB400DC9E37 /* UIViewControllerTests.swift */, - ); - path = UIKit; - sourceTree = ""; - }; - D8F097391B17F2BF002E15BA /* Foundation */ = { - isa = PBXGroup; - children = ( - D86FFBD01B34AD6F001A89B3 /* Association.swift */, - D8F0973A1B17F2F7002E15BA /* Data.swift */, - D8F097431B17F3C8002E15BA /* NSObject.swift */, - D8F0973C1B17F30D002E15BA /* UserDefaults.swift */, - ); - path = Foundation; - sourceTree = ""; - }; - D8F097461B17F5BF002E15BA /* Foundation */ = { - isa = PBXGroup; - children = ( - D8F097471B17F5DD002E15BA /* NSObjectTests.swift */, - ); - path = Foundation; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - D8003E8B1AFEC3D400D7D3C5 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - D8003E941AFEC3D400D7D3C5 /* Rex.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D83457111AFEE44E0070616A /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - D8715DA11C211011005F4191 /* Rex.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D8715D911C210F97005F4191 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - D8715DA21C211014005F4191 /* Rex.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D8715DAD1C21123E005F4191 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - D8715DB91C2112A9005F4191 /* Rex.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - D8003E8D1AFEC3D400D7D3C5 /* Rex-macOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = D8003EA41AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "Rex-macOS" */; - buildPhases = ( - D8003E891AFEC3D400D7D3C5 /* Sources */, - D8003E8A1AFEC3D400D7D3C5 /* Frameworks */, - D8003E8B1AFEC3D400D7D3C5 /* Headers */, - D8003E8C1AFEC3D400D7D3C5 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "Rex-macOS"; - productName = Rex; - productReference = D86E77A91AFEE646004BF23D /* Rex.framework */; - productType = "com.apple.product-type.framework"; - }; - D8003E981AFEC3D400D7D3C5 /* RexTests-macOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = D8003EA71AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "RexTests-macOS" */; - buildPhases = ( - D8003E951AFEC3D400D7D3C5 /* Sources */, - D8003E961AFEC3D400D7D3C5 /* Frameworks */, - D8003E971AFEC3D400D7D3C5 /* Resources */, - D8003EB21AFEC6A800D7D3C5 /* Copy Files */, - ); - buildRules = ( - ); - dependencies = ( - D8003E9C1AFEC3D400D7D3C5 /* PBXTargetDependency */, - ); - name = "RexTests-macOS"; - productName = RexTests; - productReference = D86E77AA1AFEE646004BF23D /* RexTests-macOS.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - D83457131AFEE44E0070616A /* Rex-iOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = D834572B1AFEE44E0070616A /* Build configuration list for PBXNativeTarget "Rex-iOS" */; - buildPhases = ( - D834570F1AFEE44E0070616A /* Sources */, - D83457101AFEE44E0070616A /* Frameworks */, - D83457111AFEE44E0070616A /* Headers */, - D83457121AFEE44E0070616A /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "Rex-iOS"; - productName = "Rex-iOS"; - productReference = D86E77AB1AFEE646004BF23D /* Rex.framework */; - productType = "com.apple.product-type.framework"; - }; - D834571D1AFEE44E0070616A /* RexTests-iOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = D834572C1AFEE44E0070616A /* Build configuration list for PBXNativeTarget "RexTests-iOS" */; - buildPhases = ( - D834571A1AFEE44E0070616A /* Sources */, - D834571B1AFEE44E0070616A /* Frameworks */, - D834571C1AFEE44E0070616A /* Resources */, - D83457371AFEE4B80070616A /* CopyFiles */, - ); - buildRules = ( - ); - dependencies = ( - D83457211AFEE44E0070616A /* PBXTargetDependency */, - ); - name = "RexTests-iOS"; - productName = "Rex-iOSTests"; - productReference = D86E77AC1AFEE646004BF23D /* RexTests-iOS.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - D8715D931C210F97005F4191 /* Rex-watchOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = D8715D9B1C210F97005F4191 /* Build configuration list for PBXNativeTarget "Rex-watchOS" */; - buildPhases = ( - D8715D8F1C210F97005F4191 /* Sources */, - D8715D901C210F97005F4191 /* Frameworks */, - D8715D911C210F97005F4191 /* Headers */, - D8715D921C210F97005F4191 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "Rex-watchOS"; - productName = Rex; - productReference = D8715D941C210F97005F4191 /* Rex.framework */; - productType = "com.apple.product-type.framework"; - }; - D8715DAF1C21123E005F4191 /* Rex-tvOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = D8715DB51C21123E005F4191 /* Build configuration list for PBXNativeTarget "Rex-tvOS" */; - buildPhases = ( - D8715DAB1C21123E005F4191 /* Sources */, - D8715DAC1C21123E005F4191 /* Frameworks */, - D8715DAD1C21123E005F4191 /* Headers */, - D8715DAE1C21123E005F4191 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "Rex-tvOS"; - productName = Rex; - productReference = D8715DB01C21123E005F4191 /* Rex.framework */; - productType = "com.apple.product-type.framework"; - }; - D8715DD01C21160A005F4191 /* RexTests-tvOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = D8715DD91C21160A005F4191 /* Build configuration list for PBXNativeTarget "RexTests-tvOS" */; - buildPhases = ( - D8715DCD1C21160A005F4191 /* Sources */, - D8715DCE1C21160A005F4191 /* Frameworks */, - D8715DCF1C21160A005F4191 /* Resources */, - D8715DE81C21172A005F4191 /* CopyFiles */, - ); - buildRules = ( - ); - dependencies = ( - D8715DD81C21160A005F4191 /* PBXTargetDependency */, - ); - name = "RexTests-tvOS"; - productName = RexTests; - productReference = D8715DD11C21160A005F4191 /* RexTests-tvOS.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - D8003E851AFEC3D400D7D3C5 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0720; - LastUpgradeCheck = 0800; - ORGANIZATIONNAME = "Neil Pankey"; - TargetAttributes = { - D8003E8D1AFEC3D400D7D3C5 = { - CreatedOnToolsVersion = 6.3; - LastSwiftMigration = 0800; - }; - D8003E981AFEC3D400D7D3C5 = { - CreatedOnToolsVersion = 6.3; - LastSwiftMigration = 0800; - }; - D83457131AFEE44E0070616A = { - CreatedOnToolsVersion = 6.3; - LastSwiftMigration = 0800; - }; - D834571D1AFEE44E0070616A = { - CreatedOnToolsVersion = 6.3; - LastSwiftMigration = 0800; - }; - D8715D931C210F97005F4191 = { - CreatedOnToolsVersion = 7.2; - LastSwiftMigration = 0800; - }; - D8715DAF1C21123E005F4191 = { - CreatedOnToolsVersion = 7.2; - LastSwiftMigration = 0800; - }; - D8715DD01C21160A005F4191 = { - CreatedOnToolsVersion = 7.2; - LastSwiftMigration = 0800; - }; - }; - }; - buildConfigurationList = D8003E881AFEC3D400D7D3C5 /* Build configuration list for PBXProject "Rex" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - ); - mainGroup = D8003E841AFEC3D400D7D3C5; - productRefGroup = D8003E841AFEC3D400D7D3C5; - projectDirPath = ""; - projectRoot = ""; - targets = ( - D8003E8D1AFEC3D400D7D3C5 /* Rex-macOS */, - D8003E981AFEC3D400D7D3C5 /* RexTests-macOS */, - D83457131AFEE44E0070616A /* Rex-iOS */, - D834571D1AFEE44E0070616A /* RexTests-iOS */, - D8715DAF1C21123E005F4191 /* Rex-tvOS */, - D8715DD01C21160A005F4191 /* RexTests-tvOS */, - D8715D931C210F97005F4191 /* Rex-watchOS */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - D8003E8C1AFEC3D400D7D3C5 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D8003E971AFEC3D400D7D3C5 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D83457121AFEE44E0070616A /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D834571C1AFEE44E0070616A /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D8715D921C210F97005F4191 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D8715DAE1C21123E005F4191 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D8715DCF1C21160A005F4191 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - D8003E891AFEC3D400D7D3C5 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - C72CF3E51CBF188A00E19897 /* RACSignal.swift in Sources */, - D8A454061BD26A1A00C9E790 /* Property.swift in Sources */, - D86FFBDA1B34B3F0001A89B3 /* Action.swift in Sources */, - 9A5492121D33387D009A8D44 /* Deprecations+Removals.swift in Sources */, - D86FFBD11B34AD6F001A89B3 /* Association.swift in Sources */, - D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */, - D8003EBD1AFED01000D7D3C5 /* Signal.swift in Sources */, - D8F097441B17F3C8002E15BA /* NSObject.swift in Sources */, - D8F0973B1B17F2F7002E15BA /* Data.swift in Sources */, - 4238D5961B4D5950008534C0 /* NSTextField.swift in Sources */, - D8F0973D1B17F30D002E15BA /* UserDefaults.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D8003E951AFEC3D400D7D3C5 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - CC02C18A1CCA704E0025CC04 /* ActionTests.swift in Sources */, - D8F0974A1B17F5E1002E15BA /* NSObjectTests.swift in Sources */, - D8003EC21AFED30F00D7D3C5 /* SignalTests.swift in Sources */, - D8003EC31AFED30F00D7D3C5 /* SignalProducerTests.swift in Sources */, - D8A454091BD2772700C9E790 /* PropertyTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D834570F1AFEE44E0070616A /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 9DA915A41CA6301C003723B9 /* UIDatePicker.swift in Sources */, - D86FFBD81B34B242001A89B3 /* UILabel.swift in Sources */, - 7DCF5B331CC80D77004AEE75 /* UICollectionReusableView.swift in Sources */, - D86FFBDB1B34B3F0001A89B3 /* Action.swift in Sources */, - D86FFBD21B34AD7A001A89B3 /* Association.swift in Sources */, - C7932E831C4B3F3000086F3C /* UITextField.swift in Sources */, - 7DC325761CC6FCF100746D88 /* UITableViewHeaderFooterView.swift in Sources */, - 7DC325741CC6FCF100746D88 /* UITableViewCell.swift in Sources */, - 5B7F81E31D0842AD0014B06D /* UISegmentedControl.swift in Sources */, - 8289A2E31BD7EF740097FB60 /* UIImageView.swift in Sources */, - D8F0973E1B17F30D002E15BA /* UserDefaults.swift in Sources */, - D834572D1AFEE45B0070616A /* Signal.swift in Sources */, - D8E4A6211B7BBB2100EAD8A8 /* UIBarItem.swift in Sources */, - 7D2AA99B1CB6EFEB008AB5C9 /* UISwitch.swift in Sources */, - 7DBD48F31CC8141D0077AD4F /* Reusable.swift in Sources */, - C72CF3E61CBF188A00E19897 /* RACSignal.swift in Sources */, - D8A454071BD26A1A00C9E790 /* Property.swift in Sources */, - D8E4A6201B7BBB1600EAD8A8 /* UIBarButtonItem.swift in Sources */, - D8F097451B17F3C8002E15BA /* NSObject.swift in Sources */, - C7945F111CC192E800DC9E37 /* UIViewController.swift in Sources */, - E6933BEC1CD9C37D006F7CE7 /* UIProgressView.swift in Sources */, - D834572E1AFEE45B0070616A /* SignalProducer.swift in Sources */, - C7DCE2B41CB3C89A001217D8 /* UITextView.swift in Sources */, - 8289A2E51BD7F6DD0097FB60 /* UIView.swift in Sources */, - D86FFBD61B34B116001A89B3 /* UIControl.swift in Sources */, - D8F0973F1B17F31E002E15BA /* Data.swift in Sources */, - D86FFBDD1B34B691001A89B3 /* UIButton.swift in Sources */, - 9A5492131D33387D009A8D44 /* Deprecations+Removals.swift in Sources */, - 45CED4711D27C1EB00788BDC /* UIActivityIndicatorView.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D834571A1AFEE44E0070616A /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - E6933BEA1CD9C335006F7CE7 /* UIProgressViewTests.swift in Sources */, - 45CED46F1D27C1E300788BDC /* UIActivityIndicatorViewTests.swift in Sources */, - CC02C18B1CCA704F0025CC04 /* ActionTests.swift in Sources */, - 7DC325801CC6FD2100746D88 /* UITableViewHeaderFooterViewTests.swift in Sources */, - 8289A2E11BD7EF1F0097FB60 /* UIImageViewTests.swift in Sources */, - D8F0974B1B17F5E2002E15BA /* NSObjectTests.swift in Sources */, - 7DCF5B361CC80E8E004AEE75 /* UICollectionReusableViewTests.swift in Sources */, - 8289A2E81BD7F7900097FB60 /* UIViewTests.swift in Sources */, - D8F073161B863CE70047D546 /* UILabelTests.swift in Sources */, - 7D2AA99D1CB6F275008AB5C9 /* UISwitchTests.swift in Sources */, - C7932E871C4B42F500086F3C /* UITextFieldTests.swift in Sources */, - 8295FD8A1B87352D007C9000 /* UIButtonTests.swift in Sources */, - D8A4540A1BD2772700C9E790 /* PropertyTests.swift in Sources */, - C7945F141CC1DFBE00DC9E37 /* UIViewControllerTests.swift in Sources */, - 7DC3257F1CC6FD1E00746D88 /* UITableViewCellTests.swift in Sources */, - 9DA915A61CA63046003723B9 /* UIDatePickerTests.swift in Sources */, - 4A8EDADB1D54D12400A1734C /* UIControl+EnableSendActionsForControlEvents.swift in Sources */, - D83457301AFEE45E0070616A /* SignalProducerTests.swift in Sources */, - D83457411AFEE6050070616A /* SignalTests.swift in Sources */, - 8295FD8D1B87374A007C9000 /* UIBarButtonItemTests.swift in Sources */, - 8295FD871B87309F007C9000 /* UIControlTests.swift in Sources */, - C7DCE2B71CB3C9D6001217D8 /* UITextViewTests.swift in Sources */, - 5B7F81E41D0842B50014B06D /* UISegmentedControlTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D8715D8F1C210F97005F4191 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D8715D9E1C210FF9005F4191 /* Property.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 /* Data.swift in Sources */, - 9A5492151D33387D009A8D44 /* Deprecations+Removals.swift in Sources */, - D8715D9D1C210FF9005F4191 /* Action.swift in Sources */, - D8715DA61C21107F005F4191 /* UserDefaults.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D8715DAB1C21123E005F4191 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 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 */, - D8715DC61C211553005F4191 /* UIBarButtonItem.swift in Sources */, - D8715DBD1C2112D1005F4191 /* SignalProducer.swift in Sources */, - D8715DBE1C2112D6005F4191 /* Association.swift in Sources */, - 7DC325771CC6FCF100746D88 /* UITableViewHeaderFooterView.swift in Sources */, - D8715DC01C2112D6005F4191 /* NSObject.swift in Sources */, - D8715DC91C211553005F4191 /* UIControl.swift in Sources */, - 7DC325751CC6FCF100746D88 /* UITableViewCell.swift in Sources */, - D8715DBC1C2112D1005F4191 /* Signal.swift in Sources */, - C72CF3E71CBF188A00E19897 /* RACSignal.swift in Sources */, - E6933BED1CD9C37D006F7CE7 /* UIProgressView.swift in Sources */, - D8715DBF1C2112D6005F4191 /* Data.swift in Sources */, - D8715DCC1C211553005F4191 /* UIView.swift in Sources */, - D8715DBA1C2112D1005F4191 /* Action.swift in Sources */, - D8715DC81C211553005F4191 /* UIButton.swift in Sources */, - D8715DC71C211553005F4191 /* UIBarItem.swift in Sources */, - D8715DC11C2112D6005F4191 /* UserDefaults.swift in Sources */, - 45CED4721D27C1EC00788BDC /* UIActivityIndicatorView.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D8715DCD1C21160A005F4191 /* Sources */ = { - 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 */, - D8715DDE1C211637005F4191 /* SignalProducerTests.swift in Sources */, - D8715DE11C211643005F4191 /* UIButtonTests.swift in Sources */, - 7D5FE3081CD4B04E00834675 /* UITextFieldTests.swift in Sources */, - D8715DE21C211643005F4191 /* UIControlTests.swift in Sources */, - CC02C18C1CCA704F0025CC04 /* ActionTests.swift in Sources */, - D8715DDF1C21163B005F4191 /* NSObjectTests.swift in Sources */, - 7DC3257E1CC6FD1C00746D88 /* UITableViewCellTests.swift in Sources */, - D8715DDC1C211637005F4191 /* PropertyTests.swift in Sources */, - D8715DDD1C211637005F4191 /* SignalTests.swift in Sources */, - 7DC325811CC6FD2300746D88 /* UITableViewHeaderFooterViewTests.swift in Sources */, - D8715DE41C211643005F4191 /* UIImageViewTests.swift in Sources */, - D8715DE31C211643005F4191 /* UILabelTests.swift in Sources */, - 45CED4701D27C1E400788BDC /* UIActivityIndicatorViewTests.swift in Sources */, - D8715DE01C211643005F4191 /* UIBarButtonItemTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - D8003E9C1AFEC3D400D7D3C5 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D8003E8D1AFEC3D400D7D3C5 /* Rex-macOS */; - targetProxy = D8003E9B1AFEC3D400D7D3C5 /* PBXContainerItemProxy */; - }; - D83457211AFEE44E0070616A /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D83457131AFEE44E0070616A /* Rex-iOS */; - targetProxy = D83457201AFEE44E0070616A /* PBXContainerItemProxy */; - }; - D8715DD81C21160A005F4191 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D8715DAF1C21123E005F4191 /* Rex-tvOS */; - targetProxy = D8715DD71C21160A005F4191 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - D8003EA21AFEC3D400D7D3C5 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - 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 = 3.0; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - D8003EA31AFEC3D400D7D3C5 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - 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 = 3.0; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - D8003EA51AFEC3D400D7D3C5 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - APPLICATION_EXTENSION_API_ONLY = YES; - CLANG_ENABLE_MODULES = YES; - COMBINE_HIDPI_IMAGES = YES; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/Mac", - ); - FRAMEWORK_VERSION = A; - INFOPLIST_FILE = Source/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "me.neilpa.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(PROJECT_NAME)"; - SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - D8003EA61AFEC3D400D7D3C5 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - APPLICATION_EXTENSION_API_ONLY = YES; - CLANG_ENABLE_MODULES = YES; - COMBINE_HIDPI_IMAGES = YES; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/Mac", - ); - FRAMEWORK_VERSION = A; - INFOPLIST_FILE = Source/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "me.neilpa.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(PROJECT_NAME)"; - SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - }; - name = Release; - }; - D8003EA81AFEC3D400D7D3C5 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - COMBINE_HIDPI_IMAGES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(DEVELOPER_FRAMEWORKS_DIR)", - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/Mac", - ); - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - INFOPLIST_FILE = Tests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "me.neilpa.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - D8003EA91AFEC3D400D7D3C5 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - COMBINE_HIDPI_IMAGES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(DEVELOPER_FRAMEWORKS_DIR)", - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/Mac", - ); - INFOPLIST_FILE = Tests/Info.plist; - 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; - }; - D83457271AFEE44E0070616A /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - APPLICATION_EXTENSION_API_ONLY = YES; - CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=iphonesimulator*]" = ""; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/iOS", - ); - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - INFOPLIST_FILE = Source/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "me.neilpa.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(PROJECT_NAME)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - D83457281AFEE44E0070616A /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - APPLICATION_EXTENSION_API_ONLY = YES; - CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=iphonesimulator*]" = ""; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/iOS", - ); - INFOPLIST_FILE = Source/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "me.neilpa.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(PROJECT_NAME)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - D83457291AFEE44E0070616A /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=iphonesimulator*]" = ""; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/iOS", - ); - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - INFOPLIST_FILE = Tests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.3; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "me.neilpa.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; - }; - name = Debug; - }; - D834572A1AFEE44E0070616A /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=iphonesimulator*]" = ""; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/iOS", - ); - INFOPLIST_FILE = Tests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.3; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "me.neilpa.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - D8715D991C210F97005F4191 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - APPLICATION_EXTENSION_API_ONLY = YES; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/watchOS", - ); - INFOPLIST_FILE = Source/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = me.neilpa.Rex; - PRODUCT_NAME = "$(PROJECT_NAME)"; - SDKROOT = watchos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = 4; - WATCHOS_DEPLOYMENT_TARGET = 2.0; - }; - name = Debug; - }; - D8715D9A1C210F97005F4191 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - APPLICATION_EXTENSION_API_ONLY = YES; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/watchOS", - ); - INFOPLIST_FILE = Source/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = me.neilpa.Rex; - 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; - }; - name = Release; - }; - D8715DB61C21123E005F4191 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - APPLICATION_EXTENSION_API_ONLY = YES; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/tvOS", - ); - INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = me.neilpa.Rex; - PRODUCT_NAME = "$(PROJECT_NAME)"; - SDKROOT = appletvos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 9.0; - }; - name = Debug; - }; - D8715DB71C21123E005F4191 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - APPLICATION_EXTENSION_API_ONLY = YES; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/tvOS", - ); - INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = me.neilpa.Rex; - 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; - }; - name = Release; - }; - D8715DDA1C21160A005F4191 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/tvOS", - ); - INFOPLIST_FILE = Tests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = me.neilpa.RexTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = appletvos; - TVOS_DEPLOYMENT_TARGET = 9.0; - }; - name = Debug; - }; - D8715DDB1C21160A005F4191 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/tvOS", - ); - INFOPLIST_FILE = Tests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = me.neilpa.RexTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = appletvos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - TVOS_DEPLOYMENT_TARGET = 9.0; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - D8003E881AFEC3D400D7D3C5 /* Build configuration list for PBXProject "Rex" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D8003EA21AFEC3D400D7D3C5 /* Debug */, - D8003EA31AFEC3D400D7D3C5 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D8003EA41AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "Rex-macOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D8003EA51AFEC3D400D7D3C5 /* Debug */, - D8003EA61AFEC3D400D7D3C5 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D8003EA71AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "RexTests-macOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D8003EA81AFEC3D400D7D3C5 /* Debug */, - D8003EA91AFEC3D400D7D3C5 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D834572B1AFEE44E0070616A /* Build configuration list for PBXNativeTarget "Rex-iOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D83457271AFEE44E0070616A /* Debug */, - D83457281AFEE44E0070616A /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D834572C1AFEE44E0070616A /* Build configuration list for PBXNativeTarget "RexTests-iOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D83457291AFEE44E0070616A /* Debug */, - D834572A1AFEE44E0070616A /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D8715D9B1C210F97005F4191 /* Build configuration list for PBXNativeTarget "Rex-watchOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D8715D991C210F97005F4191 /* Debug */, - D8715D9A1C210F97005F4191 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D8715DB51C21123E005F4191 /* Build configuration list for PBXNativeTarget "Rex-tvOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D8715DB61C21123E005F4191 /* Debug */, - D8715DB71C21123E005F4191 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D8715DD91C21160A005F4191 /* Build configuration list for PBXNativeTarget "RexTests-tvOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D8715DDA1C21160A005F4191 /* Debug */, - D8715DDB1C21160A005F4191 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = D8003E851AFEC3D400D7D3C5 /* Project object */; -} diff --git a/Rex.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Rex.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 9e10bd191a..0000000000 --- a/Rex.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme deleted file mode 100644 index 475a6e087e..0000000000 --- a/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Rex.xcodeproj/xcshareddata/xcschemes/Rex-iOS.xcscheme b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-iOS.xcscheme deleted file mode 100644 index 25982f120b..0000000000 --- a/Rex.xcodeproj/xcshareddata/xcschemes/Rex-iOS.xcscheme +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Rex.xcodeproj/xcshareddata/xcschemes/Rex-tvOS.xcscheme b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-tvOS.xcscheme deleted file mode 100644 index 18aa616496..0000000000 --- a/Rex.xcodeproj/xcshareddata/xcschemes/Rex-tvOS.xcscheme +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Rex.xcodeproj/xcshareddata/xcschemes/Rex-watchOS.xcscheme b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-watchOS.xcscheme deleted file mode 100644 index 1156886c04..0000000000 --- a/Rex.xcodeproj/xcshareddata/xcschemes/Rex-watchOS.xcscheme +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Rex.xcworkspace/contents.xcworkspacedata b/Rex.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 4b96bd7a5a..0000000000 --- a/Rex.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/Rex.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/Rex.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index 08de0be8d3..0000000000 --- a/Rex.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded - - - diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift deleted file mode 100644 index 68e118b4ac..0000000000 --- a/Source/Foundation/NSObject.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// NSObject.swift -// Rex -// -// Created by Neil Pankey on 5/28/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - -import ReactiveSwift -import ReactiveCocoa -import enum Result.NoError - -extension NSObject { - /// Creates a strongly-typed producer to monitor `keyPath` via KVO. The caller - /// is responsible for ensuring that the associated value is castable to `T`. - /// - /// Swift classes deriving `NSObject` must declare properties as `dynamic` for - /// them to work with KVO. However, this is not recommended practice. - 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 - /// is deallocated. - public var rex_willDealloc: Signal<(), NoError> { - return self - .rac_willDeallocSignal() - .rex_toTriggerSignal() - } -} diff --git a/Source/Info.plist b/Source/Info.plist deleted file mode 100644 index 80718e6547..0000000000 --- a/Source/Info.plist +++ /dev/null @@ -1,28 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - FMWK - CFBundleShortVersionString - 0.10.0 - CFBundleSignature - ???? - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - NSHumanReadableCopyright - Copyright © 2015 Neil Pankey. All rights reserved. - NSPrincipalClass - - - diff --git a/Source/Rex.h b/Source/Rex.h deleted file mode 100644 index 1338b3dd86..0000000000 --- a/Source/Rex.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// Rex.h -// Rex -// -// Created by Neil Pankey on 5/9/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - -#import - -//! Project version number for Rex. -FOUNDATION_EXPORT double RexVersionNumber; - -//! Project version string for Rex. -FOUNDATION_EXPORT const unsigned char RexVersionString[]; - -// In this header, you should import all the public headers of your framework using statements like #import - - diff --git a/Source/Action.swift b/Sources/Action+Extensions.swift similarity index 55% rename from Source/Action.swift rename to Sources/Action+Extensions.swift index e46eb987ff..07b4a43d39 100644 --- a/Source/Action.swift +++ b/Sources/Action+Extensions.swift @@ -7,7 +7,6 @@ // import ReactiveSwift -import ReactiveCocoa import enum Result.NoError extension Action { @@ -34,21 +33,3 @@ extension Action { } } } - -extension CocoaAction { - /// Creates an always disabled action that can be used as a default for - /// things like `rac_pressed`. - public static var rex_disabled: CocoaAction { - return CocoaAction(Action.rex_disabled, input: nil) - } - - /// Creates a producer for the `enabled` state of a CocoaAction. - public var rex_enabledProducer: SignalProducer { - return rex_producer(forKeyPath: #keyPath(CocoaAction.isEnabled)) - } - - /// Creates a producer for the `executing` state of a CocoaAction. - public var rex_executingProducer: SignalProducer { - return rex_producer(forKeyPath: #keyPath(CocoaAction.isExecuting)) - } -} diff --git a/Source/Foundation/Data.swift b/Sources/Foundation/Data.swift similarity index 100% rename from Source/Foundation/Data.swift rename to Sources/Foundation/Data.swift diff --git a/Source/Foundation/NSData.swift b/Sources/Foundation/NSData.swift similarity index 100% rename from Source/Foundation/NSData.swift rename to Sources/Foundation/NSData.swift diff --git a/Source/Foundation/UserDefaults.swift b/Sources/Foundation/UserDefaults.swift similarity index 100% rename from Source/Foundation/UserDefaults.swift rename to Sources/Foundation/UserDefaults.swift diff --git a/Source/Property.swift b/Sources/Property+Extensions.swift similarity index 100% rename from Source/Property.swift rename to Sources/Property+Extensions.swift diff --git a/Source/Signal.swift b/Sources/Signal+Extensions.swift similarity index 100% rename from Source/Signal.swift rename to Sources/Signal+Extensions.swift diff --git a/Source/SignalProducer.swift b/Sources/SignalProducer+Extensions.swift similarity index 100% rename from Source/SignalProducer.swift rename to Sources/SignalProducer+Extensions.swift diff --git a/Tests/Foundation/NSObjectTests.swift b/Tests/Foundation/NSObjectTests.swift deleted file mode 100644 index e04158f903..0000000000 --- a/Tests/Foundation/NSObjectTests.swift +++ /dev/null @@ -1,70 +0,0 @@ -// -// NSObjectTests.swift -// Rex -// -// Created by Neil Pankey on 5/28/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - -import Rex -import ReactiveSwift -import ReactiveCocoa -import XCTest - -final class NSObjectTests: XCTestCase { - - func testProducerForKeyPath() { - let object = Object() - var value: String = "" - - object.rex_producer(forKeyPath: "string").startWithNext { value = $0 } - XCTAssertEqual(value, "foo") - - object.string = "bar" - XCTAssertEqual(value, "bar") - } - - func testObjectsWillBeDeallocatedSignal() { - - 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(interval: 1, on: QueueScheduler(name: "test.queue")) - .take(until: object.rex_willDealloc) - .startWithCompleted { - expectation.fulfill() - } - } -} - -final class NSObjectDeallocTests: XCTestCase { - - weak var _object: Object? - - override func tearDown() { - XCTAssert(_object == nil, "Retain cycle detected") - super.tearDown() - } - - func testStringPropertyDoesntCreateRetainCycle() { - let object = Object() - _object = object - - associatedProperty(object, keyPath: "string") <~ SignalProducer(value: "Test") - XCTAssert(_object?.string == "Test") - } - - func testClassPropertyDoesntCreateRetainCycle() { - let object = Object() - _object = object - - associatedProperty(object, keyPath: "string", placeholder: { _ in "" as NSString }) <~ SignalProducer(value: "Test") - XCTAssert(_object?.string == "Test") - } -} - -class Object: NSObject { - dynamic var string = "foo" -} diff --git a/Tests/Info.plist b/Tests/Info.plist deleted file mode 100644 index ba72822e87..0000000000 --- a/Tests/Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - - diff --git a/Tests/ActionTests.swift b/Tests/ReactiveSwiftTests/ActionExtensionTests.swift similarity index 97% rename from Tests/ActionTests.swift rename to Tests/ReactiveSwiftTests/ActionExtensionTests.swift index 87f41356e3..cecd14f59d 100644 --- a/Tests/ActionTests.swift +++ b/Tests/ReactiveSwiftTests/ActionExtensionTests.swift @@ -6,9 +6,7 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // -@testable import Rex import ReactiveSwift -import ReactiveCocoa import XCTest import enum Result.NoError diff --git a/Tests/PropertyTests.swift b/Tests/ReactiveSwiftTests/PropertyTests.swift similarity index 98% rename from Tests/PropertyTests.swift rename to Tests/ReactiveSwiftTests/PropertyTests.swift index a5429c7261..e198f15f3f 100644 --- a/Tests/PropertyTests.swift +++ b/Tests/ReactiveSwiftTests/PropertyTests.swift @@ -6,9 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // -@testable import Rex import ReactiveSwift -import ReactiveCocoa import XCTest import enum Result.NoError diff --git a/Tests/SignalProducerTests.swift b/Tests/ReactiveSwiftTests/SignalProducerTests.swift similarity index 99% rename from Tests/SignalProducerTests.swift rename to Tests/ReactiveSwiftTests/SignalProducerTests.swift index 41094dc84f..97b1943b9d 100644 --- a/Tests/SignalProducerTests.swift +++ b/Tests/ReactiveSwiftTests/SignalProducerTests.swift @@ -6,9 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // -import Rex import ReactiveSwift -import ReactiveCocoa import XCTest import enum Result.NoError diff --git a/Tests/SignalTests.swift b/Tests/ReactiveSwiftTests/SignalTests.swift similarity index 99% rename from Tests/SignalTests.swift rename to Tests/ReactiveSwiftTests/SignalTests.swift index 54a4189010..b51b1a137d 100644 --- a/Tests/SignalTests.swift +++ b/Tests/ReactiveSwiftTests/SignalTests.swift @@ -6,9 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // -import Rex import ReactiveSwift -import ReactiveCocoa import XCTest import enum Result.NoError diff --git a/script/ci b/script/ci deleted file mode 100755 index 3ed40d744d..0000000000 --- a/script/ci +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/sh - -set -e -o pipefail - -bootstrap() { - case "$1" in - CocoaPods) - ;; - *) - carthage bootstrap --no-use-binaries --platform "$1" - ;; - esac -} - -build() { - case "$1" in - Mac) - xcodebuild ${TEST_ACTION:-test} -scheme Rex-Mac | xcpretty -c - ;; - iOS) - xcodebuild ${TEST_ACTION:-test} -scheme Rex-iOS -destination "platform=iOS Simulator,name=iPhone 6s" | xcpretty -c - ;; - tvOS) - xcodebuild ${TEST_ACTION:-test} -scheme Rex-tvOS -destination "platform=tvOS Simulator,name=Apple TV 1080p" | xcpretty -c - ;; - watchOS) - xcodebuild build -scheme Rex-watchOS -destination "platform=watchOS Simulator,name=Apple Watch - 42mm" | xcpretty -c - ;; - CocoaPods) - pod lib lint --quick --allow-warnings - ;; - *) - echo 2>&1 "Usage: $0 " - exit 1 - ;; - esac -} - -bootstrap "$@" -build "$@" From 683aeb8f0355c7c5092c7c04ace2758d5151bdac Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 22 Sep 2016 12:26:16 +0200 Subject: [PATCH 0387/1028] Removed unwanted implementations. --- Deprecations+Removals.swift | 46 ---- ReactiveCocoa/CocoaAction+Extensions.swift | 29 -- ReactiveObjCBridge/RACSignal.swift | 36 --- Sources/Action+Extensions.swift | 35 --- Sources/Foundation/Data.swift | 31 --- Sources/Foundation/NSData.swift | 25 -- Sources/Foundation/UserDefaults.swift | 38 --- Sources/Property+Extensions.swift | 140 ---------- Sources/Signal+Extensions.swift | 120 -------- Sources/SignalProducer+Extensions.swift | 256 ------------------ .../ActionExtensionTests.swift | 74 ----- Tests/ReactiveSwiftTests/PropertyTests.swift | 89 ------ .../SignalProducerTests.swift | 162 ----------- Tests/ReactiveSwiftTests/SignalTests.swift | 190 ------------- 14 files changed, 1271 deletions(-) delete mode 100644 Deprecations+Removals.swift delete mode 100644 ReactiveCocoa/CocoaAction+Extensions.swift delete mode 100644 ReactiveObjCBridge/RACSignal.swift delete mode 100644 Sources/Action+Extensions.swift delete mode 100644 Sources/Foundation/Data.swift delete mode 100644 Sources/Foundation/NSData.swift delete mode 100644 Sources/Foundation/UserDefaults.swift delete mode 100644 Sources/Property+Extensions.swift delete mode 100644 Sources/Signal+Extensions.swift delete mode 100644 Sources/SignalProducer+Extensions.swift delete mode 100644 Tests/ReactiveSwiftTests/ActionExtensionTests.swift delete mode 100644 Tests/ReactiveSwiftTests/PropertyTests.swift delete mode 100644 Tests/ReactiveSwiftTests/SignalProducerTests.swift delete mode 100644 Tests/ReactiveSwiftTests/SignalTests.swift diff --git a/Deprecations+Removals.swift b/Deprecations+Removals.swift deleted file mode 100644 index 5b62761a3c..0000000000 --- a/Deprecations+Removals.swift +++ /dev/null @@ -1,46 +0,0 @@ -// 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/ReactiveCocoa/CocoaAction+Extensions.swift b/ReactiveCocoa/CocoaAction+Extensions.swift deleted file mode 100644 index d9da1599a4..0000000000 --- a/ReactiveCocoa/CocoaAction+Extensions.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// CocoaAction.swift -// Rex -// -// Created by Neil Pankey on 6/19/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - -import ReactiveSwift -import ReactiveCocoa -import enum Result.NoError - -extension CocoaAction { - /// Creates an always disabled action that can be used as a default for - /// things like `rac_pressed`. - public static var rex_disabled: CocoaAction { - return CocoaAction(Action.rex_disabled, input: nil) - } - - /// Creates a producer for the `enabled` state of a CocoaAction. - public var rex_enabledProducer: SignalProducer { - return rex_producer(forKeyPath: #keyPath(CocoaAction.isEnabled)) - } - - /// Creates a producer for the `executing` state of a CocoaAction. - public var rex_executingProducer: SignalProducer { - return rex_producer(forKeyPath: #keyPath(CocoaAction.isExecuting)) - } -} diff --git a/ReactiveObjCBridge/RACSignal.swift b/ReactiveObjCBridge/RACSignal.swift deleted file mode 100644 index 7cfa075d8a..0000000000 --- a/ReactiveObjCBridge/RACSignal.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// RACSignal.swift -// Rex -// -// Created by Rui Peres on 14/04/2016. -// Copyright © 2016 Neil Pankey. All rights reserved. -// - -import ReactiveObjC -import ReactiveSwift -import enum Result.NoError - -extension RACSignal { - - /// Converts `self` into a `Signal`. - /// - /// Because the operator can't know whether `self` is hot or cold, - /// for certain things, like event streams (see `UIControl.signalForControlEvents`) - /// use this method to be able to expose these inherently hot streams - /// as `Signal`s. - public func rex_toSignal() -> Signal { - return Signal { observer in - return self.toSignalProducer().start(observer) - } - } - - /// Converts `self` into a `Signal`, that can be used - /// with the `takeUntil` operator, or as an "activation" signal. - /// (e.g. a button) - public final func rex_toTriggerSignal() -> Signal<(), NoError> { - return self - .rex_toSignal() - .map { _ in () } - .ignoreError() - } -} diff --git a/Sources/Action+Extensions.swift b/Sources/Action+Extensions.swift deleted file mode 100644 index 07b4a43d39..0000000000 --- a/Sources/Action+Extensions.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// Action.swift -// Rex -// -// Created by Neil Pankey on 6/19/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - -import ReactiveSwift -import enum Result.NoError - -extension Action { - /// Creates an always disabled action. - public static var rex_disabled: Action { - return Action(enabledIf: Property(value: false)) { _ in .empty } - } - - /// Whether the action execution was started. - public var rex_started: Signal { - return self.isExecuting.signal - .filterMap { $0 ? () : nil } - } - - /// Whether the action execution was completed successfully. - public var rex_completed: Signal { - return events - .filterMap { event -> Void? in - if case .completed = event { - return () - } else { - return nil - } - } - } -} diff --git a/Sources/Foundation/Data.swift b/Sources/Foundation/Data.swift deleted file mode 100644 index 4f77d0b8eb..0000000000 --- a/Sources/Foundation/Data.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// 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/Sources/Foundation/NSData.swift b/Sources/Foundation/NSData.swift deleted file mode 100644 index 1055125995..0000000000 --- a/Sources/Foundation/NSData.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// NSData.swift -// Rex -// -// Created by Ilya Laryionau on 10/05/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - -import ReactiveCocoa - -extension NSData { - /// Read the data at the URL, sending the result or an error. - @warn_unused_result(message="Did you forget to call `start` on the producer?") - public class func rex_dataWithContentsOfURL(url: NSURL, options: NSDataReadingOptions = NSDataReadingOptions()) -> SignalProducer { - return SignalProducer { observer, disposable in - do { - let data = try NSData(contentsOfURL: url, options: options) - observer.sendNext(data) - observer.sendCompleted() - } catch { - observer.sendFailed(error as NSError) - } - } - } -} diff --git a/Sources/Foundation/UserDefaults.swift b/Sources/Foundation/UserDefaults.swift deleted file mode 100644 index 061e0e2544..0000000000 --- a/Sources/Foundation/UserDefaults.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// NSUserDefaults.swift -// Rex -// -// Created by Neil Pankey on 5/28/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - -import ReactiveSwift -import ReactiveCocoa -import enum Result.NoError - -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. - public func rex_value(forKey key: String) -> SignalProducer { - let center = NotificationCenter.default - let initial = object(forKey: key) - - 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.object(forKey: key) - } - - return SignalProducer(value: initial) - .concat(changes) - .skipRepeats { previous, next in - if let previous = previous as? NSObject, let next = next as? NSObject { - return previous == next - } - return false - } - } -} diff --git a/Sources/Property+Extensions.swift b/Sources/Property+Extensions.swift deleted file mode 100644 index 067c629f79..0000000000 --- a/Sources/Property+Extensions.swift +++ /dev/null @@ -1,140 +0,0 @@ -// -// Property.swift -// Rex -// -// Created by Neil Pankey on 10/17/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - -import ReactiveSwift -import ReactiveCocoa -import enum Result.NoError - -extension PropertyProtocol where Value == Bool { - /// The conjunction of `self` and `other`. - public func and(_ other: P) -> AndProperty where P.Value == Bool { - return AndProperty(terms: [Property(self), Property(other)]) - } - - /// The conjunction of `self` and `other`. - public func and(_ other: Property) -> AndProperty { - return AndProperty(terms: [Property(self), other]) - } - - /// The disjunction of `self` and `other`. - public func or(_ other: P) -> OrProperty where P.Value == Bool { - return OrProperty(terms: [Property(self), Property(other)]) - } - - /// The disjunction of `self` and `other`. - public func or(_ other: Property) -> OrProperty { - return OrProperty(terms: [Property(self), other]) - } - - /// A negated property of `self`. - public func not() -> NotProperty { - return NotProperty(source: Property(self), invert: true) - } -} - -/// Specialized `PropertyType` for the conjuction of a set of boolean properties. -public class AndProperty: PropertyProtocol { - public let terms: [Property] - - public var value: Bool { - return terms.reduce(true) { $0 && $1.value } - } - - public var producer: SignalProducer { - let producers = terms.map { $0.producer } - return SignalProducer.combineLatest(producers).map { values in - return values.reduce(true) { $0 && $1 } - } - } - - public var signal: Signal { - let signals = terms.map { $0.signal } - return Signal.combineLatest(signals).map { values in - return values.reduce(true) { $0 && $1 } - } - } - - /// Creates a new property with an additional conjunctive term. - public func and

  • (other: P) -> OrProperty { return OrProperty(terms: terms + [AnyProperty(other)]) } @@ -82,6 +78,10 @@ public struct OrProperty: PropertyType { public func or(other: AnyProperty) -> OrProperty { return OrProperty(terms: terms + [other]) } + + private init(terms: [AnyProperty]) { + self.terms = terms + } } public struct NotProperty: PropertyType { @@ -95,7 +95,7 @@ public struct NotProperty: PropertyType { return source.producer.map { !$0 } } - public init(property: P) { + private init(property: P) { source = AnyProperty(property) } } From 0ae1e4aa60ddb27c30d734571d71df0ad6e5ec8f Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Wed, 25 Nov 2015 19:51:46 -0800 Subject: [PATCH 0206/1028] Avoid wrapping NotProperty too --- Source/Predicate.swift | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Source/Predicate.swift b/Source/Predicate.swift index bb6762c2c5..def3e1bf90 100644 --- a/Source/Predicate.swift +++ b/Source/Predicate.swift @@ -26,7 +26,7 @@ extension PropertyType where Value == Bool { } public func not() -> NotProperty { - return NotProperty(property: self) + return NotProperty(source: AnyProperty(self), invert: true) } } @@ -86,16 +86,22 @@ public struct OrProperty: PropertyType { public struct NotProperty: PropertyType { private let source: AnyProperty + private let invert: Bool public var value: Bool { - return !source.value + return source.value != invert } public var producer: SignalProducer { - return source.producer.map { !$0 } + return source.producer.map { $0 != self.invert } } - private init(property: P) { - source = AnyProperty(property) + public func not() -> NotProperty { + return NotProperty(source: source, invert: !invert) + } + + private init(source: AnyProperty, invert: Bool) { + self.source = source + self.invert = invert } } From 407b0a1c4af92010a1fb43a684b80544334c7164 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Tue, 1 Dec 2015 20:17:25 -0800 Subject: [PATCH 0207/1028] Remove unused arity code for now --- Rex.xcodeproj/project.pbxproj | 14 +++------ Source/Arity.swift | 58 ----------------------------------- 2 files changed, 4 insertions(+), 68 deletions(-) delete mode 100644 Source/Arity.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index c49c14ecad..7a2bd4ad7a 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -43,12 +43,10 @@ D86FFBDA1B34B3F0001A89B3 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD91B34B3F0001A89B3 /* Action.swift */; }; D86FFBDB1B34B3F0001A89B3 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD91B34B3F0001A89B3 /* Action.swift */; }; D86FFBDD1B34B691001A89B3 /* UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBDC1B34B691001A89B3 /* UIButton.swift */; }; - D8A454061BD26A1A00C9E790 /* Predicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Predicate.swift */; settings = {ASSET_TAGS = (); }; }; - D8A454071BD26A1A00C9E790 /* Predicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Predicate.swift */; settings = {ASSET_TAGS = (); }; }; - D8A454091BD2772700C9E790 /* PredicateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454081BD2772700C9E790 /* PredicateTests.swift */; settings = {ASSET_TAGS = (); }; }; - D8A4540A1BD2772700C9E790 /* PredicateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454081BD2772700C9E790 /* PredicateTests.swift */; settings = {ASSET_TAGS = (); }; }; - D8A4540C1BD29A2C00C9E790 /* Arity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A4540B1BD29A2C00C9E790 /* Arity.swift */; settings = {ASSET_TAGS = (); }; }; - D8A4540D1BD29A2C00C9E790 /* Arity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A4540B1BD29A2C00C9E790 /* Arity.swift */; settings = {ASSET_TAGS = (); }; }; + D8A454061BD26A1A00C9E790 /* Predicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Predicate.swift */; }; + D8A454071BD26A1A00C9E790 /* Predicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Predicate.swift */; }; + D8A454091BD2772700C9E790 /* PredicateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454081BD2772700C9E790 /* PredicateTests.swift */; }; + D8A4540A1BD2772700C9E790 /* PredicateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454081BD2772700C9E790 /* PredicateTests.swift */; }; 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 */; }; @@ -137,7 +135,6 @@ D86FFBDC1B34B691001A89B3 /* UIButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIButton.swift; sourceTree = ""; }; D8A454051BD26A1A00C9E790 /* Predicate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Predicate.swift; sourceTree = ""; }; D8A454081BD2772700C9E790 /* PredicateTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PredicateTests.swift; sourceTree = ""; }; - D8A4540B1BD29A2C00C9E790 /* Arity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Arity.swift; sourceTree = ""; }; D8F073141B861B3A0047D546 /* UILabelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabelTests.swift; sourceTree = ""; }; D8F0973A1B17F2F7002E15BA /* NSData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSData.swift; sourceTree = ""; }; D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSUserDefaults.swift; sourceTree = ""; }; @@ -206,7 +203,6 @@ isa = PBXGroup; children = ( D86FFBD91B34B3F0001A89B3 /* Action.swift */, - D8A4540B1BD29A2C00C9E790 /* Arity.swift */, D8A454051BD26A1A00C9E790 /* Predicate.swift */, D8003EBC1AFED01000D7D3C5 /* Signal.swift */, D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */, @@ -506,7 +502,6 @@ D86FFBD11B34AD6F001A89B3 /* Association.swift in Sources */, D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */, D8003EBD1AFED01000D7D3C5 /* Signal.swift in Sources */, - D8A4540C1BD29A2C00C9E790 /* Arity.swift in Sources */, D8F097441B17F3C8002E15BA /* NSObject.swift in Sources */, D8F0973B1B17F2F7002E15BA /* NSData.swift in Sources */, 4238D5961B4D5950008534C0 /* NSTextField.swift in Sources */, @@ -539,7 +534,6 @@ D8A454071BD26A1A00C9E790 /* Predicate.swift in Sources */, D8E4A6201B7BBB1600EAD8A8 /* UIBarButtonItem.swift in Sources */, D8F097451B17F3C8002E15BA /* NSObject.swift in Sources */, - D8A4540D1BD29A2C00C9E790 /* Arity.swift in Sources */, D834572E1AFEE45B0070616A /* SignalProducer.swift in Sources */, 8289A2E51BD7F6DD0097FB60 /* UIView.swift in Sources */, D86FFBD61B34B116001A89B3 /* UIControl.swift in Sources */, diff --git a/Source/Arity.swift b/Source/Arity.swift deleted file mode 100644 index 1f473d04cb..0000000000 --- a/Source/Arity.swift +++ /dev/null @@ -1,58 +0,0 @@ -// -// Tuple.swift -// Rex -// -// Created by Neil Pankey on 10/17/15. -// Copyright (c) 2015 Neil Pankey. All rights reserved. -// - -public protocol ArityType { - static var size: Int { get } - - // TODO Can this be guaranteed somehow? - typealias TupleType - var tuple: TupleType { get } -} - -public protocol NullaryType: ArityType { } - -extension NullaryType { - public static var size: Int { return 0 } -} - -public struct Nullary: NullaryType { - public let tuple: () = () -} - - -public protocol UnaryType: ArityType { } - -extension UnaryType { - public static var size: Int { return 1 } -} - -public struct Unary: UnaryType { - public let tuple: A -} - - -public protocol BinaryType: ArityType { } - -extension BinaryType { - public static var size: Int { return 2 } -} - -public struct Binary: BinaryType { - public let tuple: (A, B) -} - - -public protocol TernaryType: ArityType { } - -extension TernaryType { - public static var size: Int { return 3 } -} - -public struct Ternary: TernaryType { - public let tuple: (A, B, C) -} From 6fd377241a3633e089b3b6578dd2beadc5bfc058 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Tue, 1 Dec 2015 20:20:32 -0800 Subject: [PATCH 0208/1028] Predicate => Property --- Rex.xcodeproj/project.pbxproj | 24 +++++++++---------- Source/{Predicate.swift => Property.swift} | 2 +- ...edicateTests.swift => PropertyTests.swift} | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) rename Source/{Predicate.swift => Property.swift} (99%) rename Tests/{PredicateTests.swift => PropertyTests.swift} (98%) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 7a2bd4ad7a..1120059cd4 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -43,10 +43,10 @@ D86FFBDA1B34B3F0001A89B3 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD91B34B3F0001A89B3 /* Action.swift */; }; D86FFBDB1B34B3F0001A89B3 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD91B34B3F0001A89B3 /* Action.swift */; }; D86FFBDD1B34B691001A89B3 /* UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBDC1B34B691001A89B3 /* UIButton.swift */; }; - D8A454061BD26A1A00C9E790 /* Predicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Predicate.swift */; }; - D8A454071BD26A1A00C9E790 /* Predicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Predicate.swift */; }; - D8A454091BD2772700C9E790 /* PredicateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454081BD2772700C9E790 /* PredicateTests.swift */; }; - D8A4540A1BD2772700C9E790 /* PredicateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454081BD2772700C9E790 /* PredicateTests.swift */; }; + D8A454061BD26A1A00C9E790 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Property.swift */; }; + D8A454071BD26A1A00C9E790 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Property.swift */; }; + D8A454091BD2772700C9E790 /* PropertyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454081BD2772700C9E790 /* PropertyTests.swift */; }; + D8A4540A1BD2772700C9E790 /* PropertyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454081BD2772700C9E790 /* PropertyTests.swift */; }; 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 */; }; @@ -133,8 +133,8 @@ D86FFBD71B34B242001A89B3 /* UILabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabel.swift; sourceTree = ""; }; D86FFBD91B34B3F0001A89B3 /* Action.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Action.swift; sourceTree = ""; }; D86FFBDC1B34B691001A89B3 /* UIButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIButton.swift; sourceTree = ""; }; - D8A454051BD26A1A00C9E790 /* Predicate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Predicate.swift; sourceTree = ""; }; - D8A454081BD2772700C9E790 /* PredicateTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PredicateTests.swift; sourceTree = ""; }; + D8A454051BD26A1A00C9E790 /* Property.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Property.swift; sourceTree = ""; }; + D8A454081BD2772700C9E790 /* PropertyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PropertyTests.swift; sourceTree = ""; }; D8F073141B861B3A0047D546 /* UILabelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabelTests.swift; sourceTree = ""; }; D8F0973A1B17F2F7002E15BA /* NSData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSData.swift; sourceTree = ""; }; D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSUserDefaults.swift; sourceTree = ""; }; @@ -203,7 +203,7 @@ isa = PBXGroup; children = ( D86FFBD91B34B3F0001A89B3 /* Action.swift */, - D8A454051BD26A1A00C9E790 /* Predicate.swift */, + D8A454051BD26A1A00C9E790 /* Property.swift */, D8003EBC1AFED01000D7D3C5 /* Signal.swift */, D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */, 4238D5941B4D593E008534C0 /* AppKit */, @@ -226,7 +226,7 @@ D8003E9D1AFEC3D400D7D3C5 /* Tests */ = { isa = PBXGroup; children = ( - D8A454081BD2772700C9E790 /* PredicateTests.swift */, + D8A454081BD2772700C9E790 /* PropertyTests.swift */, D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */, D8003EC01AFED30100D7D3C5 /* SignalProducerTests.swift */, D8F097461B17F5BF002E15BA /* Foundation */, @@ -497,7 +497,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D8A454061BD26A1A00C9E790 /* Predicate.swift in Sources */, + D8A454061BD26A1A00C9E790 /* Property.swift in Sources */, D86FFBDA1B34B3F0001A89B3 /* Action.swift in Sources */, D86FFBD11B34AD6F001A89B3 /* Association.swift in Sources */, D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */, @@ -516,7 +516,7 @@ D8F0974A1B17F5E1002E15BA /* NSObjectTests.swift in Sources */, D8003EC21AFED30F00D7D3C5 /* SignalTests.swift in Sources */, D8003EC31AFED30F00D7D3C5 /* SignalProducerTests.swift in Sources */, - D8A454091BD2772700C9E790 /* PredicateTests.swift in Sources */, + D8A454091BD2772700C9E790 /* PropertyTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -531,7 +531,7 @@ D8F0973E1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, D834572D1AFEE45B0070616A /* Signal.swift in Sources */, D8E4A6211B7BBB2100EAD8A8 /* UIBarItem.swift in Sources */, - D8A454071BD26A1A00C9E790 /* Predicate.swift in Sources */, + D8A454071BD26A1A00C9E790 /* Property.swift in Sources */, D8E4A6201B7BBB1600EAD8A8 /* UIBarButtonItem.swift in Sources */, D8F097451B17F3C8002E15BA /* NSObject.swift in Sources */, D834572E1AFEE45B0070616A /* SignalProducer.swift in Sources */, @@ -551,7 +551,7 @@ 8289A2E81BD7F7900097FB60 /* UIViewTests.swift in Sources */, D8F073161B863CE70047D546 /* UILabelTests.swift in Sources */, 8295FD8A1B87352D007C9000 /* UIButtonTests.swift in Sources */, - D8A4540A1BD2772700C9E790 /* PredicateTests.swift in Sources */, + D8A4540A1BD2772700C9E790 /* PropertyTests.swift in Sources */, D83457301AFEE45E0070616A /* SignalProducerTests.swift in Sources */, D83457411AFEE6050070616A /* SignalTests.swift in Sources */, 8295FD8D1B87374A007C9000 /* UIBarButtonItemTests.swift in Sources */, diff --git a/Source/Predicate.swift b/Source/Property.swift similarity index 99% rename from Source/Predicate.swift rename to Source/Property.swift index def3e1bf90..1ff3b2e9db 100644 --- a/Source/Predicate.swift +++ b/Source/Property.swift @@ -1,5 +1,5 @@ // -// Predicate.swift +// Property.swift // Rex // // Created by Neil Pankey on 10/17/15. diff --git a/Tests/PredicateTests.swift b/Tests/PropertyTests.swift similarity index 98% rename from Tests/PredicateTests.swift rename to Tests/PropertyTests.swift index a7f35fcf65..c315ebe735 100644 --- a/Tests/PredicateTests.swift +++ b/Tests/PropertyTests.swift @@ -1,5 +1,5 @@ // -// PredicateTests.swift +// PropertyTests.swift // Rex // // Created by Neil Pankey on 10/17/15. From d49a6f93be45e49df2398db244d74c3735414271 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Tue, 1 Dec 2015 20:24:19 -0800 Subject: [PATCH 0209/1028] One last predicate --- Tests/PropertyTests.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Tests/PropertyTests.swift b/Tests/PropertyTests.swift index c315ebe735..a1fa20d2d6 100644 --- a/Tests/PropertyTests.swift +++ b/Tests/PropertyTests.swift @@ -10,7 +10,7 @@ import ReactiveCocoa import XCTest -final class PredicateTests: XCTestCase { +final class PropertyTests: XCTestCase { func testAndProperty() { let lhs = MutableProperty(false), rhs = MutableProperty(false) @@ -18,14 +18,14 @@ final class PredicateTests: XCTestCase { var current: Bool! and.producer.startWithNext { current = $0 } - + XCTAssertFalse(and.value) XCTAssertFalse(current!) lhs.value = true XCTAssertFalse(and.value) XCTAssertFalse(current!) - + rhs.value = true XCTAssertTrue(and.value) XCTAssertTrue(current!) @@ -48,7 +48,7 @@ final class PredicateTests: XCTestCase { var current: Bool! or.producer.startWithNext { current = $0 } - + XCTAssertTrue(or.value) XCTAssertTrue(current!) @@ -81,7 +81,7 @@ final class PredicateTests: XCTestCase { XCTAssertTrue(not.value) XCTAssertTrue(current!) - + source.value = true XCTAssertFalse(not.value) XCTAssertFalse(current!) From 6551cdb93534ca26b5d45b8b415149a821bc33ec Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Tue, 1 Dec 2015 20:49:50 -0800 Subject: [PATCH 0210/1028] Bump dependencies --- Cartfile | 2 +- Cartfile.resolved | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cartfile b/Cartfile index 822b36944e..fbc4ee3138 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveCocoa" "v4.0.0-alpha.3" +github "ReactiveCocoa/ReactiveCocoa" "v4.0.0-alpha.4" diff --git a/Cartfile.resolved b/Cartfile.resolved index 2c22f63c20..4c7b19d2c9 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ -github "antitypical/Result" "0.6.0-beta.5" -github "ReactiveCocoa/ReactiveCocoa" "v4.0.0-alpha.3" +github "antitypical/Result" "1.0.0" +github "ReactiveCocoa/ReactiveCocoa" "v4.0.0-alpha.4" From 375e5b46de61fa0c43313bedffd9d220514ed261 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Sat, 12 Dec 2015 14:39:55 -0800 Subject: [PATCH 0211/1028] Bump dependencies --- Cartfile | 2 +- Cartfile.resolved | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cartfile b/Cartfile index fbc4ee3138..ce85889a5b 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveCocoa" "v4.0.0-alpha.4" +github "ReactiveCocoa/ReactiveCocoa" "v4.0.0-RC.1" diff --git a/Cartfile.resolved b/Cartfile.resolved index 4c7b19d2c9..52670dde4d 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ -github "antitypical/Result" "1.0.0" -github "ReactiveCocoa/ReactiveCocoa" "v4.0.0-alpha.4" +github "antitypical/Result" "1.0.1" +github "ReactiveCocoa/ReactiveCocoa" "v4.0.0-RC.1" From 1a55815b151ea45226d3bb9bfc4baf4e4b3d26b0 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Tue, 15 Dec 2015 19:29:39 -0800 Subject: [PATCH 0212/1028] Add a watchOS target --- Rex.xcodeproj/project.pbxproj | 145 ++++++++++++++++++ .../xcschemes/Rex-watchOS.xcscheme | 80 ++++++++++ Source/Rex.h | 2 +- 3 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 Rex.xcodeproj/xcshareddata/xcschemes/Rex-watchOS.xcscheme diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 1120059cd4..ec3f5f932d 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -43,6 +43,18 @@ D86FFBDA1B34B3F0001A89B3 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD91B34B3F0001A89B3 /* Action.swift */; }; D86FFBDB1B34B3F0001A89B3 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD91B34B3F0001A89B3 /* Action.swift */; }; D86FFBDD1B34B691001A89B3 /* UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBDC1B34B691001A89B3 /* UIButton.swift */; }; + D8715D9D1C210FF9005F4191 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD91B34B3F0001A89B3 /* Action.swift */; }; + D8715D9E1C210FF9005F4191 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Property.swift */; }; + D8715D9F1C210FF9005F4191 /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBC1AFED01000D7D3C5 /* Signal.swift */; }; + D8715DA01C210FF9005F4191 /* SignalProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */; }; + 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 */; }; + D8715DA91C2110DA005F4191 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8715DA71C2110DA005F4191 /* ReactiveCocoa.framework */; }; + D8715DAA1C2110DA005F4191 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8715DA81C2110DA005F4191 /* Result.framework */; }; D8A454061BD26A1A00C9E790 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Property.swift */; }; D8A454071BD26A1A00C9E790 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Property.swift */; }; D8A454091BD2772700C9E790 /* PropertyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454081BD2772700C9E790 /* PropertyTests.swift */; }; @@ -133,6 +145,9 @@ D86FFBD71B34B242001A89B3 /* UILabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabel.swift; sourceTree = ""; }; D86FFBD91B34B3F0001A89B3 /* Action.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Action.swift; sourceTree = ""; }; D86FFBDC1B34B691001A89B3 /* UIButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIButton.swift; sourceTree = ""; }; + D8715D941C210F97005F4191 /* Rex.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Rex.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D8715DA71C2110DA005F4191 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveCocoa.framework; path = watchOS/ReactiveCocoa.framework; sourceTree = ""; }; + D8715DA81C2110DA005F4191 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = watchOS/Result.framework; sourceTree = ""; }; D8A454051BD26A1A00C9E790 /* Property.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Property.swift; sourceTree = ""; }; D8A454081BD2772700C9E790 /* PropertyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PropertyTests.swift; sourceTree = ""; }; D8F073141B861B3A0047D546 /* UILabelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabelTests.swift; sourceTree = ""; }; @@ -179,6 +194,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D8715D901C210F97005F4191 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D8715DA91C2110DA005F4191 /* ReactiveCocoa.framework in Frameworks */, + D8715DAA1C2110DA005F4191 /* Result.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -249,6 +273,7 @@ children = ( D8003EC71AFEE39000D7D3C5 /* iOS */, D8003EAB1AFEC67700D7D3C5 /* Mac */, + D8715D9C1C210FA1005F4191 /* watchOS */, ); name = Binaries; path = Carthage/Build; @@ -290,6 +315,16 @@ path = UIKit; sourceTree = ""; }; + D8715D9C1C210FA1005F4191 /* watchOS */ = { + isa = PBXGroup; + children = ( + D8715D941C210F97005F4191 /* Rex.framework */, + D8715DA71C2110DA005F4191 /* ReactiveCocoa.framework */, + D8715DA81C2110DA005F4191 /* Result.framework */, + ); + name = watchOS; + sourceTree = ""; + }; D8F073131B861B110047D546 /* UIKit */ = { isa = PBXGroup; children = ( @@ -337,6 +372,15 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + D8715DA11C211011005F4191 /* Rex.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D8715D911C210F97005F4191 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + D8715DA21C211014005F4191 /* Rex.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -417,6 +461,24 @@ productReference = D86E77AC1AFEE646004BF23D /* RexTests-iOS.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; + D8715D931C210F97005F4191 /* Rex-watchOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = D8715D9B1C210F97005F4191 /* Build configuration list for PBXNativeTarget "Rex-watchOS" */; + buildPhases = ( + D8715D8F1C210F97005F4191 /* Sources */, + D8715D901C210F97005F4191 /* Frameworks */, + D8715D911C210F97005F4191 /* Headers */, + D8715D921C210F97005F4191 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Rex-watchOS"; + productName = Rex; + productReference = D8715D941C210F97005F4191 /* Rex.framework */; + productType = "com.apple.product-type.framework"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -439,6 +501,9 @@ D834571D1AFEE44E0070616A = { CreatedOnToolsVersion = 6.3; }; + D8715D931C210F97005F4191 = { + CreatedOnToolsVersion = 7.2; + }; }; }; buildConfigurationList = D8003E881AFEC3D400D7D3C5 /* Build configuration list for PBXProject "Rex" */; @@ -457,6 +522,7 @@ D8003E981AFEC3D400D7D3C5 /* RexTests-Mac */, D83457131AFEE44E0070616A /* Rex-iOS */, D834571D1AFEE44E0070616A /* RexTests-iOS */, + D8715D931C210F97005F4191 /* Rex-watchOS */, ); }; /* End PBXProject section */ @@ -490,6 +556,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D8715D921C210F97005F4191 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -559,6 +632,21 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D8715D8F1C210F97005F4191 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D8715D9E1C210FF9005F4191 /* Property.swift in Sources */, + D8715DA01C210FF9005F4191 /* SignalProducer.swift in Sources */, + D8715DA31C21107F005F4191 /* Association.swift in Sources */, + D8715DA51C21107F005F4191 /* NSObject.swift in Sources */, + D8715D9F1C210FF9005F4191 /* Signal.swift in Sources */, + D8715DA41C21107F005F4191 /* NSData.swift in Sources */, + D8715D9D1C210FF9005F4191 /* Action.swift in Sources */, + D8715DA61C21107F005F4191 /* NSUserDefaults.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -849,6 +937,55 @@ }; name = Release; }; + D8715D991C210F97005F4191 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/watchOS", + ); + INFOPLIST_FILE = Source/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = me.neilpa.Rex; + PRODUCT_NAME = "$(PROJECT_NAME)"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 2.0; + }; + name = Debug; + }; + D8715D9A1C210F97005F4191 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/watchOS", + ); + INFOPLIST_FILE = Source/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = me.neilpa.Rex; + PRODUCT_NAME = "$(PROJECT_NAME)"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = 4; + VALIDATE_PRODUCT = YES; + WATCHOS_DEPLOYMENT_TARGET = 2.0; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -897,6 +1034,14 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + D8715D9B1C210F97005F4191 /* Build configuration list for PBXNativeTarget "Rex-watchOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D8715D991C210F97005F4191 /* Debug */, + D8715D9A1C210F97005F4191 /* Release */, + ); + defaultConfigurationIsVisible = 0; + }; /* End XCConfigurationList section */ }; rootObject = D8003E851AFEC3D400D7D3C5 /* Project object */; diff --git a/Rex.xcodeproj/xcshareddata/xcschemes/Rex-watchOS.xcscheme b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-watchOS.xcscheme new file mode 100644 index 0000000000..10d0727af4 --- /dev/null +++ b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-watchOS.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/Rex.h b/Source/Rex.h index 5aeccb98c1..1338b3dd86 100644 --- a/Source/Rex.h +++ b/Source/Rex.h @@ -6,7 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // -#import +#import //! Project version number for Rex. FOUNDATION_EXPORT double RexVersionNumber; From 4a312d97d7f9a7cfb451af38207c18ded4844c51 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Tue, 15 Dec 2015 19:34:47 -0800 Subject: [PATCH 0213/1028] Add tvOS target --- Rex.xcodeproj/project.pbxproj | 143 ++++++++++++++++++ .../xcshareddata/xcschemes/Rex-tvOS.xcscheme | 80 ++++++++++ 2 files changed, 223 insertions(+) create mode 100644 Rex.xcodeproj/xcshareddata/xcschemes/Rex-tvOS.xcscheme diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index ec3f5f932d..8025142a9e 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -55,6 +55,17 @@ D8715DA61C21107F005F4191 /* NSUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973C1B17F30D002E15BA /* NSUserDefaults.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, ); }; }; + D8715DBA1C2112D1005F4191 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD91B34B3F0001A89B3 /* Action.swift */; }; + D8715DBB1C2112D1005F4191 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Property.swift */; }; + 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 */; }; + D8715DC01C2112D6005F4191 /* NSObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097431B17F3C8002E15BA /* NSObject.swift */; }; + D8715DC11C2112D6005F4191 /* NSUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */; }; + D8715DC41C211310005F4191 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8715DC21C211310005F4191 /* ReactiveCocoa.framework */; }; + D8715DC51C211310005F4191 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8715DC31C211310005F4191 /* Result.framework */; }; D8A454061BD26A1A00C9E790 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Property.swift */; }; D8A454071BD26A1A00C9E790 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Property.swift */; }; D8A454091BD2772700C9E790 /* PropertyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454081BD2772700C9E790 /* PropertyTests.swift */; }; @@ -148,6 +159,9 @@ D8715D941C210F97005F4191 /* Rex.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Rex.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D8715DA71C2110DA005F4191 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveCocoa.framework; path = watchOS/ReactiveCocoa.framework; sourceTree = ""; }; D8715DA81C2110DA005F4191 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = watchOS/Result.framework; sourceTree = ""; }; + D8715DB01C21123E005F4191 /* Rex.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Rex.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D8715DC21C211310005F4191 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveCocoa.framework; path = tvOS/ReactiveCocoa.framework; sourceTree = ""; }; + D8715DC31C211310005F4191 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = tvOS/Result.framework; sourceTree = ""; }; D8A454051BD26A1A00C9E790 /* Property.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Property.swift; sourceTree = ""; }; D8A454081BD2772700C9E790 /* PropertyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PropertyTests.swift; sourceTree = ""; }; D8F073141B861B3A0047D546 /* UILabelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabelTests.swift; sourceTree = ""; }; @@ -203,6 +217,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D8715DAC1C21123E005F4191 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D8715DC41C211310005F4191 /* ReactiveCocoa.framework in Frameworks */, + D8715DC51C211310005F4191 /* Result.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -273,6 +296,7 @@ children = ( D8003EC71AFEE39000D7D3C5 /* iOS */, D8003EAB1AFEC67700D7D3C5 /* Mac */, + D8715DB81C21124B005F4191 /* tvOS */, D8715D9C1C210FA1005F4191 /* watchOS */, ); name = Binaries; @@ -325,6 +349,16 @@ name = watchOS; sourceTree = ""; }; + D8715DB81C21124B005F4191 /* tvOS */ = { + isa = PBXGroup; + children = ( + D8715DB01C21123E005F4191 /* Rex.framework */, + D8715DC21C211310005F4191 /* ReactiveCocoa.framework */, + D8715DC31C211310005F4191 /* Result.framework */, + ); + name = tvOS; + sourceTree = ""; + }; D8F073131B861B110047D546 /* UIKit */ = { isa = PBXGroup; children = ( @@ -384,6 +418,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D8715DAD1C21123E005F4191 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + D8715DB91C2112A9005F4191 /* Rex.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ @@ -479,6 +521,24 @@ productReference = D8715D941C210F97005F4191 /* Rex.framework */; productType = "com.apple.product-type.framework"; }; + D8715DAF1C21123E005F4191 /* Rex-tvOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = D8715DB51C21123E005F4191 /* Build configuration list for PBXNativeTarget "Rex-tvOS" */; + buildPhases = ( + D8715DAB1C21123E005F4191 /* Sources */, + D8715DAC1C21123E005F4191 /* Frameworks */, + D8715DAD1C21123E005F4191 /* Headers */, + D8715DAE1C21123E005F4191 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Rex-tvOS"; + productName = Rex; + productReference = D8715DB01C21123E005F4191 /* Rex.framework */; + productType = "com.apple.product-type.framework"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -504,6 +564,9 @@ D8715D931C210F97005F4191 = { CreatedOnToolsVersion = 7.2; }; + D8715DAF1C21123E005F4191 = { + CreatedOnToolsVersion = 7.2; + }; }; }; buildConfigurationList = D8003E881AFEC3D400D7D3C5 /* Build configuration list for PBXProject "Rex" */; @@ -522,6 +585,7 @@ D8003E981AFEC3D400D7D3C5 /* RexTests-Mac */, D83457131AFEE44E0070616A /* Rex-iOS */, D834571D1AFEE44E0070616A /* RexTests-iOS */, + D8715DAF1C21123E005F4191 /* Rex-tvOS */, D8715D931C210F97005F4191 /* Rex-watchOS */, ); }; @@ -563,6 +627,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D8715DAE1C21123E005F4191 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -647,6 +718,21 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D8715DAB1C21123E005F4191 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D8715DBB1C2112D1005F4191 /* Property.swift in Sources */, + D8715DBD1C2112D1005F4191 /* SignalProducer.swift in Sources */, + D8715DBE1C2112D6005F4191 /* Association.swift in Sources */, + D8715DC01C2112D6005F4191 /* NSObject.swift in Sources */, + D8715DBC1C2112D1005F4191 /* Signal.swift in Sources */, + D8715DBF1C2112D6005F4191 /* NSData.swift in Sources */, + D8715DBA1C2112D1005F4191 /* Action.swift in Sources */, + D8715DC11C2112D6005F4191 /* NSUserDefaults.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -986,6 +1072,55 @@ }; name = Release; }; + D8715DB61C21123E005F4191 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/tvOS", + ); + INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = me.neilpa.Rex; + PRODUCT_NAME = "$(PROJECT_NAME)"; + SDKROOT = appletvos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 9.0; + }; + name = Debug; + }; + D8715DB71C21123E005F4191 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/tvOS", + ); + INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = me.neilpa.Rex; + PRODUCT_NAME = "$(PROJECT_NAME)"; + SDKROOT = appletvos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 9.0; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -1042,6 +1177,14 @@ ); defaultConfigurationIsVisible = 0; }; + D8715DB51C21123E005F4191 /* Build configuration list for PBXNativeTarget "Rex-tvOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D8715DB61C21123E005F4191 /* Debug */, + D8715DB71C21123E005F4191 /* Release */, + ); + defaultConfigurationIsVisible = 0; + }; /* End XCConfigurationList section */ }; rootObject = D8003E851AFEC3D400D7D3C5 /* Project object */; diff --git a/Rex.xcodeproj/xcshareddata/xcschemes/Rex-tvOS.xcscheme b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-tvOS.xcscheme new file mode 100644 index 0000000000..6cc9957528 --- /dev/null +++ b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-tvOS.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From e4c4aa4594c732dc5c7d9535d26a88218e734eaf Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Tue, 15 Dec 2015 19:45:25 -0800 Subject: [PATCH 0214/1028] Add UIKit extensions to tvOS target --- Rex.xcodeproj/project.pbxproj | 14 ++++++++++++++ Source/UIKit/UIControl.swift | 2 ++ 2 files changed, 16 insertions(+) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 8025142a9e..29a982c471 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -66,6 +66,13 @@ D8715DC11C2112D6005F4191 /* NSUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973C1B17F30D002E15BA /* NSUserDefaults.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 */; }; + D8715DC71C211553005F4191 /* UIBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5173EBC51B625A2600C9B48E /* UIBarItem.swift */; }; + D8715DC81C211553005F4191 /* UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBDC1B34B691001A89B3 /* UIButton.swift */; }; + D8715DC91C211553005F4191 /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD41B34B0FE001A89B3 /* UIControl.swift */; }; + D8715DCA1C211553005F4191 /* UILabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD71B34B242001A89B3 /* UILabel.swift */; }; + D8715DCB1C211553005F4191 /* UIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E21BD7EF740097FB60 /* UIImageView.swift */; }; + D8715DCC1C211553005F4191 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E41BD7F6DD0097FB60 /* UIView.swift */; }; D8A454061BD26A1A00C9E790 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Property.swift */; }; D8A454071BD26A1A00C9E790 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Property.swift */; }; D8A454091BD2772700C9E790 /* PropertyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454081BD2772700C9E790 /* PropertyTests.swift */; }; @@ -723,12 +730,19 @@ buildActionMask = 2147483647; files = ( D8715DBB1C2112D1005F4191 /* Property.swift in Sources */, + D8715DCA1C211553005F4191 /* UILabel.swift in Sources */, + D8715DCB1C211553005F4191 /* UIImageView.swift in Sources */, + D8715DC61C211553005F4191 /* UIBarButtonItem.swift in Sources */, D8715DBD1C2112D1005F4191 /* SignalProducer.swift in Sources */, D8715DBE1C2112D6005F4191 /* Association.swift in Sources */, D8715DC01C2112D6005F4191 /* NSObject.swift in Sources */, + D8715DC91C211553005F4191 /* UIControl.swift in Sources */, D8715DBC1C2112D1005F4191 /* Signal.swift in Sources */, D8715DBF1C2112D6005F4191 /* NSData.swift in Sources */, + D8715DCC1C211553005F4191 /* UIView.swift in Sources */, D8715DBA1C2112D1005F4191 /* Action.swift in Sources */, + D8715DC81C211553005F4191 /* UIButton.swift in Sources */, + D8715DC71C211553005F4191 /* UIBarItem.swift in Sources */, D8715DC11C2112D6005F4191 /* NSUserDefaults.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Source/UIKit/UIControl.swift b/Source/UIKit/UIControl.swift index c1c2e3d2f7..f92da0ac22 100644 --- a/Source/UIKit/UIControl.swift +++ b/Source/UIKit/UIControl.swift @@ -10,6 +10,7 @@ import ReactiveCocoa import UIKit extension UIControl { +#if os(iOS) /// Creates a producer for the sender whenever a specified control event is triggered. public func rex_controlEvents(events: UIControlEvents) -> SignalProducer { return rac_signalForControlEvents(events) @@ -17,6 +18,7 @@ extension UIControl { .map { $0 as? UIControl } .flatMapError { _ in SignalProducer(value: nil) } } +#endif /// Wraps a control's `enabled` state in a bindable property. public var rex_enabled: MutableProperty { From c69567960173fe807fb02008c5026e9a6b9f8ca2 Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Tue, 15 Dec 2015 19:52:59 -0800 Subject: [PATCH 0215/1028] Test the tvOS target --- Rex.xcodeproj/project.pbxproj | 138 +++++++++++++++++- .../xcshareddata/xcschemes/Rex-Mac.xcscheme | 8 +- .../xcshareddata/xcschemes/Rex-iOS.xcscheme | 8 +- .../xcshareddata/xcschemes/Rex-tvOS.xcscheme | 19 +++ 4 files changed, 164 insertions(+), 9 deletions(-) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 29a982c471..0873ed6356 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -73,6 +73,20 @@ D8715DCA1C211553005F4191 /* UILabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD71B34B242001A89B3 /* UILabel.swift */; }; D8715DCB1C211553005F4191 /* UIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E21BD7EF740097FB60 /* UIImageView.swift */; }; D8715DCC1C211553005F4191 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E41BD7F6DD0097FB60 /* UIView.swift */; }; + D8715DDC1C211637005F4191 /* PropertyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454081BD2772700C9E790 /* PropertyTests.swift */; }; + D8715DDD1C211637005F4191 /* SignalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */; }; + D8715DDE1C211637005F4191 /* SignalProducerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EC01AFED30100D7D3C5 /* SignalProducerTests.swift */; }; + D8715DDF1C21163B005F4191 /* NSObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097471B17F5DD002E15BA /* NSObjectTests.swift */; }; + D8715DE01C211643005F4191 /* UIBarButtonItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */; }; + D8715DE11C211643005F4191 /* UIButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8295FD881B873490007C9000 /* UIButtonTests.swift */; }; + D8715DE21C211643005F4191 /* UIControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8295FD851B873081007C9000 /* UIControlTests.swift */; }; + D8715DE31C211643005F4191 /* UILabelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F073141B861B3A0047D546 /* UILabelTests.swift */; }; + D8715DE41C211643005F4191 /* UIImageViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */; }; + D8715DE51C211643005F4191 /* UIViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */; }; + D8715DE61C21170C005F4191 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8715DC21C211310005F4191 /* ReactiveCocoa.framework */; }; + D8715DE71C21170C005F4191 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8715DC31C211310005F4191 /* Result.framework */; }; + D8715DE91C211739005F4191 /* ReactiveCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8715DC21C211310005F4191 /* ReactiveCocoa.framework */; }; + D8715DEA1C211739005F4191 /* Result.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8715DC31C211310005F4191 /* Result.framework */; }; D8A454061BD26A1A00C9E790 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Property.swift */; }; D8A454071BD26A1A00C9E790 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Property.swift */; }; D8A454091BD2772700C9E790 /* PropertyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454081BD2772700C9E790 /* PropertyTests.swift */; }; @@ -105,6 +119,13 @@ remoteGlobalIDString = D83457131AFEE44E0070616A; remoteInfo = "Rex-iOS"; }; + D8715DD71C21160A005F4191 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D8003E851AFEC3D400D7D3C5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = D8715DAF1C21123E005F4191; + remoteInfo = "Rex-tvOS"; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -130,6 +151,17 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D8715DE81C21172A005F4191 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 16; + files = ( + D8715DE91C211739005F4191 /* ReactiveCocoa.framework in CopyFiles */, + D8715DEA1C211739005F4191 /* Result.framework in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -169,6 +201,7 @@ D8715DB01C21123E005F4191 /* Rex.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Rex.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D8715DC21C211310005F4191 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveCocoa.framework; path = tvOS/ReactiveCocoa.framework; sourceTree = ""; }; D8715DC31C211310005F4191 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = tvOS/Result.framework; sourceTree = ""; }; + D8715DD11C21160A005F4191 /* RexTests-tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RexTests-tvOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; D8A454051BD26A1A00C9E790 /* Property.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Property.swift; sourceTree = ""; }; D8A454081BD2772700C9E790 /* PropertyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PropertyTests.swift; sourceTree = ""; }; D8F073141B861B3A0047D546 /* UILabelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabelTests.swift; sourceTree = ""; }; @@ -233,6 +266,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D8715DCE1C21160A005F4191 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D8715DE61C21170C005F4191 /* ReactiveCocoa.framework in Frameworks */, + D8715DE71C21170C005F4191 /* Result.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -360,6 +402,7 @@ isa = PBXGroup; children = ( D8715DB01C21123E005F4191 /* Rex.framework */, + D8715DD11C21160A005F4191 /* RexTests-tvOS.xctest */, D8715DC21C211310005F4191 /* ReactiveCocoa.framework */, D8715DC31C211310005F4191 /* Result.framework */, ); @@ -546,13 +589,32 @@ productReference = D8715DB01C21123E005F4191 /* Rex.framework */; productType = "com.apple.product-type.framework"; }; + D8715DD01C21160A005F4191 /* RexTests-tvOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = D8715DD91C21160A005F4191 /* Build configuration list for PBXNativeTarget "RexTests-tvOS" */; + buildPhases = ( + D8715DCD1C21160A005F4191 /* Sources */, + D8715DCE1C21160A005F4191 /* Frameworks */, + D8715DCF1C21160A005F4191 /* Resources */, + D8715DE81C21172A005F4191 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + D8715DD81C21160A005F4191 /* PBXTargetDependency */, + ); + name = "RexTests-tvOS"; + productName = RexTests; + productReference = D8715DD11C21160A005F4191 /* RexTests-tvOS.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ D8003E851AFEC3D400D7D3C5 /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 0700; + LastSwiftUpdateCheck = 0720; LastUpgradeCheck = 0700; ORGANIZATIONNAME = "Neil Pankey"; TargetAttributes = { @@ -574,6 +636,9 @@ D8715DAF1C21123E005F4191 = { CreatedOnToolsVersion = 7.2; }; + D8715DD01C21160A005F4191 = { + CreatedOnToolsVersion = 7.2; + }; }; }; buildConfigurationList = D8003E881AFEC3D400D7D3C5 /* Build configuration list for PBXProject "Rex" */; @@ -593,6 +658,7 @@ D83457131AFEE44E0070616A /* Rex-iOS */, D834571D1AFEE44E0070616A /* RexTests-iOS */, D8715DAF1C21123E005F4191 /* Rex-tvOS */, + D8715DD01C21160A005F4191 /* RexTests-tvOS */, D8715D931C210F97005F4191 /* Rex-watchOS */, ); }; @@ -641,6 +707,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D8715DCF1C21160A005F4191 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -747,6 +820,23 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D8715DCD1C21160A005F4191 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D8715DE51C211643005F4191 /* UIViewTests.swift in Sources */, + D8715DDE1C211637005F4191 /* SignalProducerTests.swift in Sources */, + D8715DE11C211643005F4191 /* UIButtonTests.swift in Sources */, + D8715DE21C211643005F4191 /* UIControlTests.swift in Sources */, + D8715DDF1C21163B005F4191 /* NSObjectTests.swift in Sources */, + D8715DDC1C211637005F4191 /* PropertyTests.swift in Sources */, + D8715DDD1C211637005F4191 /* SignalTests.swift in Sources */, + D8715DE41C211643005F4191 /* UIImageViewTests.swift in Sources */, + D8715DE31C211643005F4191 /* UILabelTests.swift in Sources */, + D8715DE01C211643005F4191 /* UIBarButtonItemTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -760,6 +850,11 @@ target = D83457131AFEE44E0070616A /* Rex-iOS */; targetProxy = D83457201AFEE44E0070616A /* PBXContainerItemProxy */; }; + D8715DD81C21160A005F4191 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D8715DAF1C21123E005F4191 /* Rex-tvOS */; + targetProxy = D8715DD71C21160A005F4191 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ @@ -1135,6 +1230,39 @@ }; name = Release; }; + D8715DDA1C21160A005F4191 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/tvOS", + ); + INFOPLIST_FILE = Tests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = me.neilpa.RexTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + TVOS_DEPLOYMENT_TARGET = 9.0; + }; + name = Debug; + }; + D8715DDB1C21160A005F4191 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/tvOS", + ); + INFOPLIST_FILE = Tests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = me.neilpa.RexTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + TVOS_DEPLOYMENT_TARGET = 9.0; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -1199,6 +1327,14 @@ ); defaultConfigurationIsVisible = 0; }; + D8715DD91C21160A005F4191 /* Build configuration list for PBXNativeTarget "RexTests-tvOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D8715DDA1C21160A005F4191 /* Debug */, + D8715DDB1C21160A005F4191 /* Release */, + ); + defaultConfigurationIsVisible = 0; + }; /* End XCConfigurationList section */ }; rootObject = D8003E851AFEC3D400D7D3C5 /* Project object */; diff --git a/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme index 26421be816..cc58cdc730 100644 --- a/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme +++ b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme @@ -37,10 +37,10 @@ + shouldUseLaunchSchemeArgsEnv = "YES"> @@ -66,11 +66,11 @@ + shouldUseLaunchSchemeArgsEnv = "YES"> @@ -66,11 +66,11 @@ + + + + + + + + From d8c9ca2e64e017753430b4d9752b5ff51a4799fa Mon Sep 17 00:00:00 2001 From: Neil Pankey Date: Tue, 15 Dec 2015 20:09:28 -0800 Subject: [PATCH 0216/1028] Doc comments for property extensions --- Source/Property.swift | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Source/Property.swift b/Source/Property.swift index 1ff3b2e9db..5c2e21ad56 100644 --- a/Source/Property.swift +++ b/Source/Property.swift @@ -9,27 +9,33 @@ import ReactiveCocoa extension PropertyType where Value == Bool { + /// The conjunction of `self` and `other`. public func and(other: P) -> AndProperty { return AndProperty(terms: [AnyProperty(self), AnyProperty(other)]) } + /// The conjunction of `self` and `other`. public func and(other: AnyProperty) -> AndProperty { return AndProperty(terms: [AnyProperty(self), other]) } + /// The disjunction of `self` and `other`. public func or(other: P) -> OrProperty { return OrProperty(terms: [AnyProperty(self), AnyProperty(other)]) } + /// The disjunction of `self` and `other`. public func or(other: AnyProperty) -> OrProperty { return OrProperty(terms: [AnyProperty(self), other]) } + /// A negated property of `self`. public func not() -> NotProperty { return NotProperty(source: AnyProperty(self), invert: true) } } +/// Specialized `PropertyType` for the conjuction of a set of boolean properties. public struct AndProperty: PropertyType { public let terms: [AnyProperty] @@ -44,10 +50,12 @@ public struct AndProperty: PropertyType { } } + /// Creates a new property with an additional conjunctive term. public func and